From 3d43058578afebaedb9ae95fa29eb546d20a6769 Mon Sep 17 00:00:00 2001 From: Do Duc Quan <99700700+ducquando@users.noreply.github.com> Date: Thu, 18 Apr 2024 15:54:23 +0700 Subject: [PATCH] Add walkthrough and documentation --- src/App.tsx | 97 +++++-- src/const/texts.ts | 3 + src/style/icomoon/demo-files/demo.css | 7 +- src/style/icomoon/demo.html | 267 ++++++++++-------- src/style/icomoon/fonts/icomoon.eot | Bin 7852 -> 8236 bytes src/style/icomoon/fonts/icomoon.svg | 2 + src/style/icomoon/fonts/icomoon.ttf | Bin 7688 -> 8072 bytes src/style/icomoon/fonts/icomoon.woff | Bin 7764 -> 8148 bytes src/style/icomoon/selection.json | 2 +- src/style/icomoon/style.css | 51 ++-- src/wireframes/components/HeaderView.tsx | 18 +- .../components/headers/FileHeader.tsx | 25 +- src/wireframes/model/actions/ui.ts | 8 + src/wireframes/model/ui-state.ts | 4 + 14 files changed, 311 insertions(+), 173 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index eb791bc..bda85d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,16 +6,17 @@ * Copyright (c) Do Duc Quan. All rights reserved. */ -import { ConfigProvider, Layout } from 'antd'; +import { ConfigProvider, Layout, Tour, TourProps } from 'antd'; import * as React from 'react'; import { useDispatch } from 'react-redux'; import { useRouteMatch } from 'react-router'; import { ClipboardContainer } from '@app/core'; import { EditorView, ShapeView, PagesView, HeaderView, AnimationView, ToolView } from '@app/wireframes/components'; -import { getSelectedItems, getSelectedShape, loadDiagramFromServer, newDiagram, useStore } from '@app/wireframes/model'; +import { getSelectedItems, getSelectedShape, loadDiagramFromServer, newDiagram, setIsTourOpen, useStore } from '@app/wireframes/model'; import { vogues } from './const'; import { CustomDragLayer } from './wireframes/components/CustomDragLayer'; import { OverlayContainer } from './wireframes/contexts/OverlayContext'; +import { useEffect, useRef } from 'react'; export const App = () => { const dispatch = useDispatch(); @@ -27,16 +28,10 @@ export const App = () => { const sidebarWidth = useStore(s => s.ui.sidebarSize); const footerHeight = useStore(s => s.ui.footerSize); const applicationMode = useStore(s => s.ui.selectedMode); + const isTourOpen = useStore(s => s.ui.isTourOpen); + const tourRefs = Array(7).fill(0).map(() => useRef(null)); - const margin = { - tool: `${vogues.common.editorPad}px 0`, - sideLeft: `${vogues.common.editorPad}px ${vogues.common.editorPad}px ${vogues.common.editorPad}px 0`, - sideMid: `0 ${vogues.common.editorPad}px 0 0`, - sideRight: `0 0 ${vogues.common.editorPad}px ${vogues.common.editorPad}px`, - editor: `0 ${vogues.common.editorMargin}px`, - } - - React.useEffect(() => { + useEffect(() => { const token = routeTokenSnapshot.current; if (token && token.length > 0) { @@ -46,15 +41,61 @@ export const App = () => { } }, [dispatch]); - React.useEffect(() => { + useEffect(() => { const handleContextmenu = (e: MouseEvent) => { e.preventDefault() }; document.addEventListener('contextmenu', handleContextmenu); - + return function cleanup() { document.removeEventListener('contextmenu', handleContextmenu) } }, []) + const margin = { + tool: `${vogues.common.editorPad}px 0`, + sideLeft: `${vogues.common.editorPad}px ${vogues.common.editorPad}px ${vogues.common.editorPad}px 0`, + sideMid: `0 ${vogues.common.editorPad}px 0 0`, + sideRight: `0 0 ${vogues.common.editorPad}px ${vogues.common.editorPad}px`, + editor: `0 ${vogues.common.editorMargin}px`, + } + + const walkthroughTour: TourProps['steps'] = [ + { + title: 'Step 1: Starting Your Project', + description: 'Start a fresh project, open an existing one, or save your work to your local machine. The documentation for coding syntax is also here.', + target: () => tourRefs[0].current, + }, + { + title: 'Step 2: Designing Your Presentation', + description: 'Use the canvas to layout and customize your presentation\'s structure.', + target: () => tourRefs[1].current, + }, + { + title: 'Step 3: Adding Shapes', + description: 'Add visual elements to your canvas by clicking on any shape.', + target: () => tourRefs[2].current, + }, + { + title: 'Step 4: Modifying Appearance', + description: 'Edit your objects\'s appearance, including color, font size, stroke, and more, to match your vision.', + target: () => tourRefs[3].current, + }, + { + title: 'Step 5: Writing Code', + description: 'Switch to coding mode to set object\'s occurrences. If you\'re stuck on syntax, the documentation is under the button at the top left corner.', + target: () => tourRefs[4].current, + }, + { + title: 'Step 6: Managing Pages', + description: 'Add new pages and continue unfolding your presentation.', + target: () => tourRefs[5].current, + }, + { + title: 'Step 7: Launching Your Presentation', + description: 'Start your presentation. If you need to make changes, you can always come back and edit.', + target: () => tourRefs[6].current, + }, + ]; + return ( { - + - - + - - + + - + - @@ -109,10 +153,17 @@ export const App = () => { - - + + + dispatch(setIsTourOpen(false))} + steps={walkthroughTour} + /> diff --git a/src/const/texts.ts b/src/const/texts.ts index 41078bb..28a3f00 100644 --- a/src/const/texts.ts +++ b/src/const/texts.ts @@ -32,6 +32,7 @@ export const texts = { diagram: 'Canvas', distributeHorizontally: 'Distribute Horizontally', distributeVertically: 'Distribute Vertically', + documentation: 'Documentation', duplicate: 'Duplicate', error: 'Output', findIcon: 'Find Icon', @@ -42,6 +43,7 @@ export const texts = { group: 'Group', groupTooltip: 'Group Items', height: 'Height', + help: 'Help', id: 'ID', icons: 'Icons', imageChangeURL: 'Change Image by URL', @@ -104,6 +106,7 @@ export const texts = { ungroupTooltip: 'Ungroup Items', unsaved: 'unsaved', visual: 'Visual', + walkthrough: 'Walkthrough', width: 'Width', zoomIn: 'Zoom In', zoomOut: 'Zoom Out', diff --git a/src/style/icomoon/demo-files/demo.css b/src/style/icomoon/demo-files/demo.css index 218fc89..79b58e2 100644 --- a/src/style/icomoon/demo-files/demo.css +++ b/src/style/icomoon/demo-files/demo.css @@ -147,15 +147,18 @@ p { font-size: 16px; } .fs1 { - font-size: 24px; + font-size: 20px; } .fs2 { font-size: 32px; } .fs3 { - font-size: 32px; + font-size: 24px; } .fs4 { + font-size: 32px; +} +.fs5 { font-size: 28px; } diff --git a/src/style/icomoon/demo.html b/src/style/icomoon/demo.html index 52bbbc7..8c7e3c3 100644 --- a/src/style/icomoon/demo.html +++ b/src/style/icomoon/demo.html @@ -9,11 +9,115 @@
-

Font Name: icomoon (Glyphs: 42)

+

Font Name: icomoon (Glyphs: 44)

-

Grid Size: 24

+

Grid Size: 20

+
+ + icon-graduation-cap +
+
+ + +
+
+ liga: + +
+
+
+
+

Grid Size: Unknown

+
+
+ + icon-sidebar-right +
+
+ + +
+
+ liga: + +
+
+
+
+ + icon-sidebar-left +
+
+ + +
+
+ liga: + +
+
+
+
+ + icon-distribute-h2 +
+
+ + +
+
+ liga: + +
+
+
+
+ + icon-distribute-v2 +
+
+ + +
+
+ liga: + +
+
+
+
+ + icon-distribute-h +
+
+ + +
+
+ liga: + +
+
+
+
+ + icon-distribute-v +
+
+ + +
+
+ liga: + +
+
+
+
+

Grid Size: 24

+
icon-flip_to_front @@ -27,7 +131,7 @@

Grid Size: 24

-
+
icon-to_front @@ -41,7 +145,7 @@

Grid Size: 24

-
+
icon-flip_to_back @@ -55,7 +159,7 @@

Grid Size: 24

-
+
icon-to_back @@ -69,7 +173,7 @@

Grid Size: 24

-
+
icon-align_vertical_top @@ -83,7 +187,7 @@

Grid Size: 24

-
+
icon-align_vertical_center @@ -97,7 +201,7 @@

Grid Size: 24

-
+
icon-align_vertical_bottom @@ -111,7 +215,7 @@

Grid Size: 24

-
+
icon-align_horizontal_left @@ -125,7 +229,7 @@

Grid Size: 24

-
+
icon-align_horizontal_right @@ -139,7 +243,7 @@

Grid Size: 24

-
+
icon-align_horizontal_center @@ -153,7 +257,7 @@

Grid Size: 24

-
+
icon-format_align_right @@ -167,7 +271,7 @@

Grid Size: 24

-
+
icon-format_align_justify @@ -181,7 +285,7 @@

Grid Size: 24

-
+
icon-format_align_left @@ -195,7 +299,7 @@

Grid Size: 24

-
+
icon-format_align_center @@ -209,7 +313,7 @@

Grid Size: 24

-
+
icon-file_rename @@ -223,7 +327,7 @@

Grid Size: 24

-
+
icon-format_bold @@ -237,7 +341,7 @@

Grid Size: 24

-
+
icon-redo @@ -251,7 +355,7 @@

Grid Size: 24

-
+
icon-undo @@ -265,87 +369,14 @@

Grid Size: 24

-
-
-

Grid Size: Unknown

-
-
- - icon-sidebar-right -
-
- - -
-
- liga: - -
-
-
-
- - icon-sidebar-left -
-
- - -
-
- liga: - -
-
-
-
- - icon-distribute-h2 -
-
- - -
-
- liga: - -
-
-
-
- - icon-distribute-v2 -
-
- - -
-
- liga: - -
-
-
+
- - icon-distribute-h + + icon-help_outline
- - -
-
- liga: - -
-
-
-
- - icon-distribute-v -
-
- - + +
liga: @@ -355,7 +386,7 @@

Grid Size: Unknown

Grid Size: 16

-
+
icon-new @@ -369,7 +400,7 @@

Grid Size: 16

-
+
icon-save @@ -383,7 +414,7 @@

Grid Size: 16

-
+
icon-share @@ -397,7 +428,7 @@

Grid Size: 16

-
+
icon-cog @@ -414,7 +445,7 @@

Grid Size: 16

Grid Size: 14

-
+
icon-floppy-o @@ -428,7 +459,7 @@

Grid Size: 14

-
+
icon-folder-open @@ -442,7 +473,7 @@

Grid Size: 14

-
+
icon-search @@ -456,7 +487,7 @@

Grid Size: 14

-
+
icon-search-plus @@ -470,7 +501,7 @@

Grid Size: 14

-
+
icon-search-minus @@ -484,7 +515,7 @@

Grid Size: 14

-
+
icon-delete @@ -498,7 +529,7 @@

Grid Size: 14

-
+
icon-plus @@ -512,7 +543,7 @@

Grid Size: 14

-
+
icon-minus @@ -526,7 +557,7 @@

Grid Size: 14

-
+
icon-cut @@ -540,7 +571,7 @@

Grid Size: 14

-
+
icon-scissors @@ -554,7 +585,7 @@

Grid Size: 14

-
+
icon-copy @@ -568,7 +599,7 @@

Grid Size: 14

-
+
icon-files-o @@ -582,7 +613,7 @@

Grid Size: 14

-
+
icon-arrange @@ -596,7 +627,7 @@

Grid Size: 14

-
+
icon-clipboard @@ -610,7 +641,7 @@

Grid Size: 14

-
+
icon-paste @@ -624,7 +655,7 @@

Grid Size: 14

-
+
icon-group @@ -638,7 +669,7 @@

Grid Size: 14

-
+
icon-ungroup diff --git a/src/style/icomoon/fonts/icomoon.eot b/src/style/icomoon/fonts/icomoon.eot index 2227bc0d3d11948649ce2c969c7a235eb4ee5190..071f6629e3597bc8a55f6dbd6de31a4ae91f8b9a 100644 GIT binary patch delta 787 zcmYk4Ur1AN6vxkR_s{+~cZb{V{y3+*+FYs2uG^g>C+iPusZr1xAx5t09O2SX$}Agb zNQ6-`3CaqR2s9t0z4#Ea>eYuH>ZKb?>`?2Jc{ ze*joQiOy(;^KEPbz#723lt4l8i77%JK(0t6`v)>{+=2BWx@>n(EIRUHNCi;RhI}p= z9q5Hd7d%5Di$d9jXtMKulX4Sz312eW+tb$%6lS0_k+BVKb7xD67gmlHFiZ*!gFc zTBEX{0T8-+xL6y_S&PjW;Zu9nhlLpv(9l3f-*`lnFND2QH@HjfiqUU8FOC3vGW(7 z!Hbtb4fx>zek{?*8}xG4Q113h`N!j~bd(E!1)cbxpf~cm{OJ@PFBd9tB(J3N)EQ(D z6vaRwtk;`NdcDOW2o_6N6oX@(Xk&l;+z{H_Z$BEAJ;$u%^SNAhJIC?7RWPFo*kya> zUj^1tuv3$-+>S)>4+3B^f+C``P7QLCXSs%NP=HgRtpxAf-HGXmVlSypFBYFle<@a^ z?beYd%0gYEo>Gf6OCO}$>Fe}56J~~)$IJq=%Bop6+s4kx1M>6od4*bWPsu75lv^rW ZfvI4$U`ZWR&lXl-bAqcj^V#~E`~n17z_0)S delta 405 zcmZ4Eu*Q~cjT{35hulOqGnVd_H>?vK%Im`!7#Q{daYAx#VnL9t%Nqs;Mj0UgPI_W- z0gx5|@_ztnj`W<$H2((o#|#WiK(54@jMT&wjibImj?@{TfLR7mfa3)#P`xw@kgt-F zTT-z)4aj1U)&LSaa`KZCOLpYt0QnDq{J7l2iUI~3J%(ir3^EQt0foH8+|-#~oWFtm z1fZpD1^LA#49q|o2H6)Nc?M?Yr;}3{tHlohc{)Ho3j+@WBZI=jsE28fEMBrt{=%rw z$TeAsNlQVTRqU5ow^+33d;tXkQ2`MFLH_;xDg252j=WESMlwz=WKx;jz{I(EDpL#V z=Bb?F!ZNIHTDs%;ZN4&agIv$R@W_$H9Y#;)kTqx2o9rMPI(e>avm4VhW + + diff --git a/src/style/icomoon/fonts/icomoon.ttf b/src/style/icomoon/fonts/icomoon.ttf index d1c29b122e152702b707fb9011deba8ab45ad1be..8d339674e3eca8df7976f2bfff38289e7008ce56 100644 GIT binary patch delta 801 zcmYjQUr1A76hGhY?%mvUckFI=f1J}@ZQe3<-EH?eQkxaaQZaIk5Tn*|j?f$^VSm(6 zFbSh#5%hjWsmbZy~5f2$l_4f5+qln^UE)Ep6ayN6;GLK{rQZ?vso|2{Z zrNnYg`UI&?kCNRkev7}wpSC7a* zDxnS<@LNe%-eT4;mQueW=bnJS+*2z26*%cX!EEKZ-0orAUMiI1NQ%q_d=?diB`Fk& zn9Vku+3au#g2NG!r0_&1V&adT9YoCjwqp_1ON`3FU{#gd&9Xf26zm8At87pI%fLJe zc5-r=+tDaqAOL!9NE79p&#thK&ak!LK#x-)mczI2?D+JCLm}R@Sa>qsQm9Q27LG23Nk f{bGJNf2N=ei|2j;FHAx~?jTU%jo{|u&gbM8X)(XJ delta 419 zcmeCM@35(7U}Rum;AUuGU}iAz57sy07vc{GitGX6gyh`Bf*@O$Hw+AnGC=;F^u*!< zAT0po{{Ye)={c2Y{tfPr85o#=T!}Losfj5XM}2`DsWU(UvkagB#|u`VdTACQUnL{A zq+)d%ki{Ua0VHsK_jCz>%$l@jY{7#=yYxWnklDKh4adXwkKgiih`(;UF`jG2$wgSmxy5%U2S z1D0tlM_3-PGO%i}`mvU=&SU+=*2K1k?H9Wr`wb2cP6kdp&IGO~t`po!+#THSct8OH YiUd7|B@DbE3mBMyt`Xk+SavBR0I|(wVE_OC diff --git a/src/style/icomoon/fonts/icomoon.woff b/src/style/icomoon/fonts/icomoon.woff index 5e8b5e2990ded1bac9287fe3e555e2f3b73ace92..f382c013914d84e111c4c28b5d4ad26ad9e58182 100644 GIT binary patch delta 847 zcmYjPO-vI}5Pq-Q-Tu;T*zNY0Lbq6|{8+btG!{g~S_Q?#+8CnIS}Bd8LIP3z!6smc zF(qo&n5YSf28|$z#CqUFFnZCL=%HRb35mc7PM(13ECO*h-@KV`XXeek_X^>{KK5!` zB!VykDK8++#(GC|$V?AvnC$6fCeecsP6J(#(0|zfqchP9oGc8!aufQ_u4^ganh~-@ zB{aKJ@pMPxB0{zt!~`U`za<~0fduX&_>~frOJr#})7KB&68N(c@=&R(I|*^e%U%I- zLBi160d*$P4;$OH;8$#*5xu;e$aDZ#15FM~czjf~+SA?J2h+UUjw8S#hy_uLZy2V* z-Uaz`q=u<0=Gx~{^Y!8=-YtH`JMG*WH^QAYJk#@fqn^_<^m{r*C+UMFi-W;_7~W{TgHjM^Ov zxyuDLZfG-`2eU+kl519*@HnA~OG35oc`AuhUP@(c#}n15<&_NABIPng$+?LI3=E7JK;b(e9AxYACOxqj zD8}RhUm { +interface HeaderViewProps { + refs: React.MutableRefObject[]; +} + +export const HeaderView = React.memo((props: HeaderViewProps) => { const forLoading = useLoading(); const editor = useStore(s => s.editor); const tokenToWrite = useStore(s => s.loading.tokenToWrite); @@ -58,14 +62,20 @@ export const HeaderView = React.memo(() => {
- + + +
- + + + - + + +
); diff --git a/src/wireframes/components/headers/FileHeader.tsx b/src/wireframes/components/headers/FileHeader.tsx index 187fcb9..7cb8e94 100644 --- a/src/wireframes/components/headers/FileHeader.tsx +++ b/src/wireframes/components/headers/FileHeader.tsx @@ -6,7 +6,7 @@ * Copyright (c) Do Duc Quan. All rights reserved. */ -import { changeName, getEditor, useStore } from '@app/wireframes/model'; +import { changeName, getEditor, setIsTourOpen, useStore } from '@app/wireframes/model'; import { Button, Dropdown, Form, Input, message } from 'antd'; import { useEffect, useState } from 'react'; import { useLoading, useServer } from '../actions'; @@ -91,6 +91,21 @@ export const FileHeader = () => { className: 'loading-action-item', disabled: forLoading.downloadDiagram.disabled, }, + { + type: 'divider', + }, + { + key: texts.common.walkthrough, + label: texts.common.walkthrough, + icon: , + className: 'loading-action-item', + }, + { + key: texts.common.documentation, + label: texts.common.documentation, + icon: , + className: 'loading-action-item', + }, ]; const menuEvt: MenuProps['onClick'] = ({key}) => { @@ -104,9 +119,13 @@ export const FileHeader = () => { dispatch(forLoading.openDiagramAction.onAction); } else if (key == forLoading.downloadDiagram.label) { dispatch(forLoading.downloadDiagram.onAction); - } else if (texts.common.saveDiagramToFileTooltip) { + } else if (key == texts.common.saveDiagramToFileTooltip) { dispatch(forServer.pdf(messageApi, messageKey)); - } + } else if (key == texts.common.documentation) { + window.open('https://github.com/code-slide/ui/wiki'); + } else if (key == texts.common.walkthrough) { + dispatch(setIsTourOpen(true)); + } } return ( diff --git a/src/wireframes/model/actions/ui.ts b/src/wireframes/model/actions/ui.ts index b39abee..09af955 100644 --- a/src/wireframes/model/actions/ui.ts +++ b/src/wireframes/model/actions/ui.ts @@ -53,6 +53,11 @@ export const filterDiagrams = return { payload: { filter } }; }); +export const setIsTourOpen = + createAction('ui/isTourOpen', (isOpen: boolean) => { + return { payload: { isOpen } }; + }); + export function toastMiddleware() { const middleware: Middleware = () => (next: Dispatch) => (action: any) => { if (showToast.match(action)) { @@ -91,5 +96,8 @@ export function ui(initialState: UIState): Reducer { }) .addCase(selectColorTab, (state, action) => { state.selectedColor = action.payload.tab; + }) + .addCase(setIsTourOpen, (state, action) => { + state.isTourOpen = action.payload.isOpen; })); } diff --git a/src/wireframes/model/ui-state.ts b/src/wireframes/model/ui-state.ts index ac48596..73878d3 100644 --- a/src/wireframes/model/ui-state.ts +++ b/src/wireframes/model/ui-state.ts @@ -34,6 +34,9 @@ export interface UIState { // The color tab. selectedColor: string; + // The tour step. + isTourOpen: boolean; + // The filter for the diagram. diagramsFilter?: string; } @@ -50,5 +53,6 @@ export const createInitialUIState: () => UIState = () => { selectedMode: 'design', selectedAnimation: 'script', sidebarSize: vogues.common.close, + isTourOpen: false, }; };