瀏覽代碼

Adds menu panel

main
Steve Ruiz 3 年之前
父節點
當前提交
6fb1822467

+ 85
- 180
components/canvas/context-menu/context-menu.tsx 查看文件

@@ -2,9 +2,15 @@ import * as _ContextMenu from '@radix-ui/react-context-menu'
2 2
 import styled from 'styles'
3 3
 import {
4 4
   IconWrapper,
5
-  IconButton as _IconButton,
6
-  RowButton,
7 5
   breakpoints,
6
+  RowButton,
7
+  ContextMenuArrow,
8
+  ContextMenuDivider,
9
+  ContextMenuButton,
10
+  ContextMenuSubMenu,
11
+  ContextMenuIconButton,
12
+  ContextMenuRoot,
13
+  MenuContent,
8 14
 } from 'components/shared'
9 15
 import { commandKey, deepCompareArrays, isMobile } from 'utils'
10 16
 import state, { useSelector } from 'state'
@@ -93,60 +99,64 @@ export default function ContextMenu({
93 99
   const hasThreeOrMore = selectedShapeIds.length > 2
94 100
 
95 101
   return (
96
-    <_ContextMenu.Root dir="ltr">
102
+    <ContextMenuRoot>
97 103
       <_ContextMenu.Trigger>{children}</_ContextMenu.Trigger>
98
-      <StyledContent ref={rContent} isMobile={isMobile()}>
104
+      <MenuContent
105
+        as={_ContextMenu.Content}
106
+        ref={rContent}
107
+        isMobile={isMobile()}
108
+      >
99 109
         {selectedShapeIds.length ? (
100 110
           <>
101
-            {/* <Button onSelect={() => state.send('COPIED')}>
111
+            {/* <ContextMenuButton onSelect={() => state.send('COPIED')}>
102 112
               <span>Copy</span>
103 113
               <kbd>
104 114
                 <span>{commandKey()}</span>
105 115
                 <span>C</span>
106 116
               </kbd>
107
-            </Button>
108
-            <Button onSelect={() => state.send('CUT')}>
117
+            </ContextMenuButton>
118
+            <ContextMenuButton onSelect={() => state.send('CUT')}>
109 119
               <span>Cut</span>
110 120
               <kbd>
111 121
                 <span>{commandKey()}</span>
112 122
                 <span>X</span>
113 123
               </kbd>
114
-            </Button>
124
+            </ContextMenuButton>
115 125
              */}
116
-            <Button onSelect={() => state.send('DUPLICATED')}>
126
+            <ContextMenuButton onSelect={() => state.send('DUPLICATED')}>
117 127
               <span>Duplicate</span>
118 128
               <kbd>
119 129
                 <span>{commandKey()}</span>
120 130
                 <span>D</span>
121 131
               </kbd>
122
-            </Button>
123
-            <StyledDivider />
132
+            </ContextMenuButton>
133
+            <ContextMenuDivider />
124 134
             {hasGroupSelected ||
125 135
               (hasTwoOrMore && (
126 136
                 <>
127 137
                   {hasGroupSelected && (
128
-                    <Button onSelect={() => state.send('UNGROUPED')}>
138
+                    <ContextMenuButton onSelect={() => state.send('UNGROUPED')}>
129 139
                       <span>Ungroup</span>
130 140
                       <kbd>
131 141
                         <span>{commandKey()}</span>
132 142
                         <span>⇧</span>
133 143
                         <span>G</span>
134 144
                       </kbd>
135
-                    </Button>
145
+                    </ContextMenuButton>
136 146
                   )}
137 147
                   {hasTwoOrMore && (
138
-                    <Button onSelect={() => state.send('GROUPED')}>
148
+                    <ContextMenuButton onSelect={() => state.send('GROUPED')}>
139 149
                       <span>Group</span>
140 150
                       <kbd>
141 151
                         <span>{commandKey()}</span>
142 152
                         <span>G</span>
143 153
                       </kbd>
144
-                    </Button>
154
+                    </ContextMenuButton>
145 155
                   )}
146 156
                 </>
147 157
               ))}
148
-            <SubMenu label="Move">
149
-              <Button
158
+            <ContextMenuSubMenu label="Move">
159
+              <ContextMenuButton
150 160
                 onSelect={() =>
151 161
                   state.send('MOVED', {
152 162
                     type: MoveType.ToFront,
@@ -159,9 +169,9 @@ export default function ContextMenu({
159 169
                   <span>⇧</span>
160 170
                   <span>]</span>
161 171
                 </kbd>
162
-              </Button>
172
+              </ContextMenuButton>
163 173
 
164
-              <Button
174
+              <ContextMenuButton
165 175
                 onSelect={() =>
166 176
                   state.send('MOVED', {
167 177
                     type: MoveType.Forward,
@@ -173,8 +183,8 @@ export default function ContextMenu({
173 183
                   <span>{commandKey()}</span>
174 184
                   <span>]</span>
175 185
                 </kbd>
176
-              </Button>
177
-              <Button
186
+              </ContextMenuButton>
187
+              <ContextMenuButton
178 188
                 onSelect={() =>
179 189
                   state.send('MOVED', {
180 190
                     type: MoveType.Backward,
@@ -186,8 +196,8 @@ export default function ContextMenu({
186 196
                   <span>{commandKey()}</span>
187 197
                   <span>[</span>
188 198
                 </kbd>
189
-              </Button>
190
-              <Button
199
+              </ContextMenuButton>
200
+              <ContextMenuButton
191 201
                 onSelect={() =>
192 202
                   state.send('MOVED', {
193 203
                     type: MoveType.ToBack,
@@ -200,8 +210,8 @@ export default function ContextMenu({
200 210
                   <span>⇧</span>
201 211
                   <span>[</span>
202 212
                 </kbd>
203
-              </Button>
204
-            </SubMenu>
213
+              </ContextMenuButton>
214
+            </ContextMenuSubMenu>
205 215
             {hasTwoOrMore && (
206 216
               <AlignDistributeSubMenu
207 217
                 hasTwoOrMore={hasTwoOrMore}
@@ -209,150 +219,43 @@ export default function ContextMenu({
209 219
               />
210 220
             )}
211 221
             <MoveToPageMenu />
212
-            <Button onSelect={() => state.send('COPIED_TO_SVG')}>
222
+            <ContextMenuButton onSelect={() => state.send('COPIED_TO_SVG')}>
213 223
               <span>Copy to SVG</span>
214 224
               <kbd>
215 225
                 <span>{commandKey()}</span>
216 226
                 <span>⇧</span>
217 227
                 <span>C</span>
218 228
               </kbd>
219
-            </Button>
220
-            <StyledDivider />
221
-            <Button onSelect={() => state.send('DELETED')}>
229
+            </ContextMenuButton>
230
+            <ContextMenuDivider />
231
+            <ContextMenuButton onSelect={() => state.send('DELETED')}>
222 232
               <span>Delete</span>
223 233
               <kbd>
224 234
                 <span>⌫</span>
225 235
               </kbd>
226
-            </Button>
236
+            </ContextMenuButton>
227 237
           </>
228 238
         ) : (
229 239
           <>
230
-            <Button onSelect={() => state.send('UNDO')}>
240
+            <ContextMenuButton onSelect={() => state.send('UNDO')}>
231 241
               <span>Undo</span>
232 242
               <kbd>
233 243
                 <span>{commandKey()}</span>
234 244
                 <span>Z</span>
235 245
               </kbd>
236
-            </Button>
237
-            <Button onSelect={() => state.send('REDO')}>
246
+            </ContextMenuButton>
247
+            <ContextMenuButton onSelect={() => state.send('REDO')}>
238 248
               <span>Redo</span>
239 249
               <kbd>
240 250
                 <span>{commandKey()}</span>
241 251
                 <span>⇧</span>
242 252
                 <span>Z</span>
243 253
               </kbd>
244
-            </Button>
254
+            </ContextMenuButton>
245 255
           </>
246 256
         )}
247
-      </StyledContent>
248
-    </_ContextMenu.Root>
249
-  )
250
-}
251
-
252
-const StyledContent = styled(_ContextMenu.Content, {
253
-  position: 'relative',
254
-  backgroundColor: '$panel',
255
-  borderRadius: '4px',
256
-  overflow: 'hidden',
257
-  pointerEvents: 'all',
258
-  userSelect: 'none',
259
-  zIndex: 200,
260
-  padding: 3,
261
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
262
-  minWidth: 128,
263
-  font: '$ui',
264
-
265
-  '& kbd': {
266
-    marginLeft: '32px',
267
-    fontSize: '$1',
268
-    fontFamily: '$ui',
269
-    fontWeight: 400,
270
-  },
271
-
272
-  '& kbd > span': {
273
-    display: 'inline-block',
274
-    width: '12px',
275
-  },
276
-
277
-  variants: {
278
-    isMobile: {
279
-      true: {
280
-        '& kbd': {
281
-          display: 'none',
282
-        },
283
-      },
284
-    },
285
-  },
286
-})
287
-
288
-const StyledDivider = styled(_ContextMenu.Separator, {
289
-  backgroundColor: '$hover',
290
-  height: 1,
291
-  margin: '3px -3px',
292
-})
293
-
294
-function Button({
295
-  onSelect,
296
-  children,
297
-  disabled = false,
298
-}: {
299
-  onSelect: () => void
300
-  disabled?: boolean
301
-  children: React.ReactNode
302
-}) {
303
-  return (
304
-    <_ContextMenu.Item
305
-      as={RowButton}
306
-      disabled={disabled}
307
-      bp={breakpoints}
308
-      onSelect={onSelect}
309
-    >
310
-      {children}
311
-    </_ContextMenu.Item>
312
-  )
313
-}
314
-
315
-function IconButton({
316
-  onSelect,
317
-  children,
318
-  disabled = false,
319
-}: {
320
-  onSelect: () => void
321
-  disabled?: boolean
322
-  children: React.ReactNode
323
-}) {
324
-  return (
325
-    <_ContextMenu.Item
326
-      as={_IconButton}
327
-      bp={breakpoints}
328
-      disabled={disabled}
329
-      onSelect={onSelect}
330
-    >
331
-      {children}
332
-    </_ContextMenu.Item>
333
-  )
334
-}
335
-
336
-function SubMenu({
337
-  children,
338
-  label,
339
-}: {
340
-  label: string
341
-  children: React.ReactNode
342
-}) {
343
-  return (
344
-    <_ContextMenu.Root dir="ltr">
345
-      <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
346
-        <span>{label}</span>
347
-        <IconWrapper size="small">
348
-          <ChevronRightIcon />
349
-        </IconWrapper>
350
-      </_ContextMenu.TriggerItem>
351
-      <StyledContent sideOffset={2} alignOffset={-2} isMobile={isMobile()}>
352
-        {children}
353
-        <StyledArrow offset={13} />
354
-      </StyledContent>
355
-    </_ContextMenu.Root>
257
+      </MenuContent>
258
+    </ContextMenuRoot>
356 259
   )
357 260
 }
358 261
 
@@ -363,7 +266,7 @@ function AlignDistributeSubMenu({
363 266
   hasThreeOrMore: boolean
364 267
 }) {
365 268
   return (
366
-    <_ContextMenu.Root dir="ltr">
269
+    <ContextMenuRoot>
367 270
       <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
368 271
         <span>Align / Distribute</span>
369 272
         <IconWrapper size="small">
@@ -371,53 +274,54 @@ function AlignDistributeSubMenu({
371 274
         </IconWrapper>
372 275
       </_ContextMenu.TriggerItem>
373 276
       <StyledGrid
277
+        as={_ContextMenu.Content}
374 278
         sideOffset={2}
375 279
         alignOffset={-2}
376 280
         isMobile={isMobile()}
377 281
         selectedStyle={hasThreeOrMore ? 'threeOrMore' : 'twoOrMore'}
378 282
       >
379
-        <IconButton onSelect={alignLeft}>
283
+        <ContextMenuIconButton onSelect={alignLeft}>
380 284
           <AlignLeftIcon />
381
-        </IconButton>
382
-        <IconButton onSelect={alignCenterHorizontal}>
285
+        </ContextMenuIconButton>
286
+        <ContextMenuIconButton onSelect={alignCenterHorizontal}>
383 287
           <AlignCenterHorizontallyIcon />
384
-        </IconButton>
385
-        <IconButton onSelect={alignRight}>
288
+        </ContextMenuIconButton>
289
+        <ContextMenuIconButton onSelect={alignRight}>
386 290
           <AlignRightIcon />
387
-        </IconButton>
388
-        <IconButton onSelect={stretchHorizontally}>
291
+        </ContextMenuIconButton>
292
+        <ContextMenuIconButton onSelect={stretchHorizontally}>
389 293
           <StretchHorizontallyIcon />
390
-        </IconButton>
294
+        </ContextMenuIconButton>
391 295
         {hasThreeOrMore && (
392
-          <IconButton onSelect={distributeHorizontally}>
296
+          <ContextMenuIconButton onSelect={distributeHorizontally}>
393 297
             <SpaceEvenlyHorizontallyIcon />
394
-          </IconButton>
298
+          </ContextMenuIconButton>
395 299
         )}
396 300
 
397
-        <IconButton onSelect={alignTop}>
301
+        <ContextMenuIconButton onSelect={alignTop}>
398 302
           <AlignTopIcon />
399
-        </IconButton>
400
-        <IconButton onSelect={alignCenterVertical}>
303
+        </ContextMenuIconButton>
304
+        <ContextMenuIconButton onSelect={alignCenterVertical}>
401 305
           <AlignCenterVerticallyIcon />
402
-        </IconButton>
403
-        <IconButton onSelect={alignBottom}>
306
+        </ContextMenuIconButton>
307
+        <ContextMenuIconButton onSelect={alignBottom}>
404 308
           <AlignBottomIcon />
405
-        </IconButton>
406
-        <IconButton onSelect={stretchVertically}>
309
+        </ContextMenuIconButton>
310
+        <ContextMenuIconButton onSelect={stretchVertically}>
407 311
           <StretchVerticallyIcon />
408
-        </IconButton>
312
+        </ContextMenuIconButton>
409 313
         {hasThreeOrMore && (
410
-          <IconButton onSelect={distributeVertically}>
314
+          <ContextMenuIconButton onSelect={distributeVertically}>
411 315
             <SpaceEvenlyVerticallyIcon />
412
-          </IconButton>
316
+          </ContextMenuIconButton>
413 317
         )}
414
-        <StyledArrow offset={13} />
318
+        <ContextMenuArrow offset={13} />
415 319
       </StyledGrid>
416
-    </_ContextMenu.Root>
320
+    </ContextMenuRoot>
417 321
   )
418 322
 }
419 323
 
420
-const StyledGrid = styled(StyledContent, {
324
+const StyledGrid = styled(MenuContent, {
421 325
   display: 'grid',
422 326
   variants: {
423 327
     selectedStyle: {
@@ -444,29 +348,30 @@ function MoveToPageMenu() {
444 348
   if (sorted.length === 0) return null
445 349
 
446 350
   return (
447
-    <_ContextMenu.Root dir="ltr">
448
-      <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
351
+    <ContextMenuRoot>
352
+      <ContextMenuButton>
449 353
         <span>Move To Page</span>
450 354
         <IconWrapper size="small">
451 355
           <ChevronRightIcon />
452 356
         </IconWrapper>
453
-      </_ContextMenu.TriggerItem>
454
-      <StyledContent sideOffset={2} alignOffset={-2} isMobile={isMobile()}>
357
+      </ContextMenuButton>
358
+      <MenuContent
359
+        as={_ContextMenu.Content}
360
+        sideOffset={2}
361
+        alignOffset={-2}
362
+        isMobile={isMobile()}
363
+      >
455 364
         {sorted.map(({ id, name }) => (
456
-          <Button
365
+          <ContextMenuButton
457 366
             key={id}
458 367
             disabled={id === currentPageId}
459 368
             onSelect={() => state.send('MOVED_TO_PAGE', { id })}
460 369
           >
461 370
             <span>{name}</span>
462
-          </Button>
371
+          </ContextMenuButton>
463 372
         ))}
464
-        <StyledArrow offset={13} />
465
-      </StyledContent>
466
-    </_ContextMenu.Root>
373
+        <ContextMenuArrow offset={13} />
374
+      </MenuContent>
375
+    </ContextMenuRoot>
467 376
   )
468 377
 }
469
-
470
-const StyledArrow = styled(_ContextMenu.Arrow, {
471
-  fill: 'white',
472
-})

+ 1
- 0
components/code-panel/code-editor.tsx 查看文件

@@ -40,6 +40,7 @@ export default function CodeEditor({
40 40
   onKey,
41 41
 }: Props): JSX.Element {
42 42
   const { theme } = useTheme()
43
+
43 44
   const rEditor = useRef<IMonacoEditor>(null)
44 45
   const rMonaco = useRef<IMonaco>(null)
45 46
 

+ 3
- 3
components/debug-panel/debug-panel.tsx 查看文件

@@ -185,17 +185,17 @@ const StylePanelRoot = styled(Panel.Root, {
185 185
   overflow: 'hidden',
186 186
   position: 'relative',
187 187
   border: '1px solid $panel',
188
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
188
+  boxShadow: '$4',
189 189
   display: 'flex',
190 190
   flexDirection: 'column',
191 191
   alignItems: 'center',
192 192
   pointerEvents: 'all',
193
-  padding: 2,
193
+  padding: '$0',
194 194
 
195 195
   '& hr': {
196 196
     marginTop: 2,
197 197
     marginBottom: 2,
198
-    marginLeft: '-2px',
198
+    marginLeft: '-$0',
199 199
     border: 'none',
200 200
     height: 1,
201 201
     backgroundColor: '$brushFill',

+ 12
- 3
components/editor.tsx 查看文件

@@ -1,5 +1,6 @@
1 1
 import useKeyboardEvents from 'hooks/useKeyboardEvents'
2 2
 import useLoadOnMount from 'hooks/useLoadOnMount'
3
+import Menu from './menu/menu'
3 4
 import Canvas from './canvas/canvas'
4 5
 import StatusBar from './status-bar'
5 6
 import ToolsPanel from './tools-panel/tools-panel'
@@ -16,9 +17,12 @@ export default function Editor({ roomId }: { roomId?: string }): JSX.Element {
16 17
 
17 18
   return (
18 19
     <Layout>
19
-      <DebugPanel />
20
-      <CodePanel />
21
-      <PagePanel />
20
+      <MenuButtons>
21
+        <Menu />
22
+        <DebugPanel />
23
+        <CodePanel />
24
+        <PagePanel />
25
+      </MenuButtons>
22 26
       <ControlsPanel />
23 27
       <Spacer />
24 28
       <StylePanel />
@@ -33,6 +37,11 @@ const Spacer = styled('div', {
33 37
   flexGrow: 2,
34 38
 })
35 39
 
40
+const MenuButtons = styled('div', {
41
+  display: 'flex',
42
+  gap: 8,
43
+})
44
+
36 45
 const Layout = styled('main', {
37 46
   position: 'fixed',
38 47
   overflow: 'hidden',

+ 1
- 3
components/icons/redo.tsx 查看文件

@@ -4,7 +4,7 @@ function SvgRedo(props: React.SVGProps<SVGSVGElement>): JSX.Element {
4 4
   return (
5 5
     <svg
6 6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8 8
       xmlns="http://www.w3.org/2000/svg"
9 9
       {...props}
10 10
     >
@@ -12,13 +12,11 @@ function SvgRedo(props: React.SVGProps<SVGSVGElement>): JSX.Element {
12 12
         fillRule="evenodd"
13 13
         clipRule="evenodd"
14 14
         d="M12.5 2.495a.5.5 0 00-.5.5v2.5H9.5a.5.5 0 100 1h3a.5.5 0 00.5-.5v-3a.5.5 0 00-.5-.5z"
15
-        fill="#000"
16 15
       />
17 16
       <path
18 17
         fillRule="evenodd"
19 18
         clipRule="evenodd"
20 19
         d="M7.697 2.049a5 5 0 104.02 6.613.5.5 0 10-.944-.332 4 4 0 11-.946-4.16l.01.01 2.32 2.18a.5.5 0 00.685-.729l-2.314-2.175A5 5 0 007.697 2.05z"
21
-        fill="#000"
22 20
       />
23 21
     </svg>
24 22
   )

+ 1
- 4
components/icons/trash.tsx 查看文件

@@ -4,7 +4,7 @@ function SvgTrash(props: React.SVGProps<SVGSVGElement>): JSX.Element {
4 4
   return (
5 5
     <svg
6 6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8 8
       xmlns="http://www.w3.org/2000/svg"
9 9
       {...props}
10 10
     >
@@ -12,19 +12,16 @@ function SvgTrash(props: React.SVGProps<SVGSVGElement>): JSX.Element {
12 12
         fillRule="evenodd"
13 13
         clipRule="evenodd"
14 14
         d="M2 4.656a.5.5 0 01.5-.5h9.7a.5.5 0 010 1H2.5a.5.5 0 01-.5-.5z"
15
-        fill="#000"
16 15
       />
17 16
       <path
18 17
         fillRule="evenodd"
19 18
         clipRule="evenodd"
20 19
         d="M6.272 3a.578.578 0 00-.578.578v.578h3.311v-.578A.578.578 0 008.428 3H6.272zm3.733 1.156v-.578A1.578 1.578 0 008.428 2H6.272a1.578 1.578 0 00-1.578 1.578v.578H3.578a.5.5 0 00-.5.5V12.2a1.578 1.578 0 001.577 1.578h5.39a1.578 1.578 0 001.577-1.578V4.656a.5.5 0 00-.5-.5h-1.117zm-5.927 1V12.2a.578.578 0 00.577.578h5.39a.578.578 0 00.577-.578V5.156H4.078z"
21
-        fill="#000"
22 20
       />
23 21
       <path
24 22
         fillRule="evenodd"
25 23
         clipRule="evenodd"
26 24
         d="M6.272 6.85a.5.5 0 01.5.5v3.233a.5.5 0 11-1 0V7.35a.5.5 0 01.5-.5zM8.428 6.85a.5.5 0 01.5.5v3.233a.5.5 0 11-1 0V7.35a.5.5 0 01.5-.5z"
27
-        fill="#000"
28 25
       />
29 26
     </svg>
30 27
   )

+ 1
- 3
components/icons/undo.tsx 查看文件

@@ -4,7 +4,7 @@ function SvgUndo(props: React.SVGProps<SVGSVGElement>): JSX.Element {
4 4
   return (
5 5
     <svg
6 6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8 8
       xmlns="http://www.w3.org/2000/svg"
9 9
       {...props}
10 10
     >
@@ -12,13 +12,11 @@ function SvgUndo(props: React.SVGProps<SVGSVGElement>): JSX.Element {
12 12
         fillRule="evenodd"
13 13
         clipRule="evenodd"
14 14
         d="M2.5 2.495a.5.5 0 01.5.5v2.5h2.5a.5.5 0 110 1h-3a.5.5 0 01-.5-.5v-3a.5.5 0 01.5-.5z"
15
-        fill="#000"
16 15
       />
17 16
       <path
18 17
         fillRule="evenodd"
19 18
         clipRule="evenodd"
20 19
         d="M7.303 2.049a5 5 0 11-4.02 6.613.5.5 0 01.944-.332 4 4 0 10.946-4.16l-.01.01-2.32 2.18a.5.5 0 01-.685-.729l2.314-2.175A5 5 0 017.303 2.05z"
21
-        fill="#000"
22 20
       />
23 21
     </svg>
24 22
   )

+ 102
- 0
components/menu/menu.tsx 查看文件

@@ -0,0 +1,102 @@
1
+import * as React from 'react'
2
+import { HamburgerMenuIcon } from '@radix-ui/react-icons'
3
+import { Trigger, Content } from '@radix-ui/react-dropdown-menu'
4
+import { memo } from 'react'
5
+import {
6
+  FloatingContainer,
7
+  DropdownMenuRoot,
8
+  MenuContent,
9
+  IconButton,
10
+  breakpoints,
11
+  DropdownMenuButton,
12
+  DropdownMenuSubMenu,
13
+  DropdownMenuDivider,
14
+} from '../shared'
15
+import state from 'state'
16
+import { commandKey } from 'utils'
17
+
18
+const handleNew = () => state.send('CREATED_NEW_PROJECT')
19
+const handleSave = () => state.send('SAVED')
20
+const handleLoad = () => state.send('LOADED_FROM_FILE_STSTEM')
21
+const toggleDarkMode = () => state.send('TOGGLED_DARK_MODE')
22
+
23
+function Menu() {
24
+  return (
25
+    <FloatingContainer>
26
+      <DropdownMenuRoot>
27
+        <IconButton as={Trigger} bp={breakpoints}>
28
+          <HamburgerMenuIcon />
29
+        </IconButton>
30
+        <Content as={MenuContent} sideOffset={8}>
31
+          <DropdownMenuButton onSelect={handleNew}>
32
+            <span>New Project</span>
33
+            <kbd>
34
+              <span>{commandKey()}</span>
35
+              <span>N</span>
36
+            </kbd>
37
+          </DropdownMenuButton>
38
+          <DropdownMenuDivider />
39
+          <DropdownMenuButton onSelect={handleLoad}>
40
+            <span>Open...</span>
41
+            <kbd>
42
+              <span>{commandKey()}</span>
43
+              <span>L</span>
44
+            </kbd>
45
+          </DropdownMenuButton>
46
+          <RecentFiles />
47
+          <DropdownMenuDivider />
48
+          <DropdownMenuButton onSelect={handleSave}>
49
+            <span>Save</span>
50
+            <kbd>
51
+              <span>{commandKey()}</span>
52
+              <span>S</span>
53
+            </kbd>
54
+          </DropdownMenuButton>
55
+          <DropdownMenuButton onSelect={handleSave}>
56
+            <span>Save As...</span>
57
+            <kbd>
58
+              <span>⇧</span>
59
+              <span>{commandKey()}</span>
60
+              <span>S</span>
61
+            </kbd>
62
+          </DropdownMenuButton>
63
+          <DropdownMenuDivider />
64
+          <Preferences />
65
+        </Content>
66
+      </DropdownMenuRoot>
67
+    </FloatingContainer>
68
+  )
69
+}
70
+
71
+export default memo(Menu)
72
+
73
+function RecentFiles() {
74
+  return (
75
+    <DropdownMenuSubMenu label="Open Recent...">
76
+      <DropdownMenuButton>
77
+        <span>Project A</span>
78
+      </DropdownMenuButton>
79
+      <DropdownMenuButton>
80
+        <span>Project B</span>
81
+      </DropdownMenuButton>
82
+      <DropdownMenuButton>
83
+        <span>Project C</span>
84
+      </DropdownMenuButton>
85
+    </DropdownMenuSubMenu>
86
+  )
87
+}
88
+
89
+function Preferences() {
90
+  return (
91
+    <DropdownMenuSubMenu label="Preferences">
92
+      <DropdownMenuButton onSelect={toggleDarkMode}>
93
+        <span>Toggle Dark Mode</span>
94
+        <kbd>
95
+          <span>⇧</span>
96
+          <span>{commandKey()}</span>
97
+          <span>D</span>
98
+        </kbd>
99
+      </DropdownMenuButton>
100
+    </DropdownMenuSubMenu>
101
+  )
102
+}

+ 4
- 5
components/page-panel/page-panel.tsx 查看文件

@@ -106,7 +106,6 @@ export default function PagePanel(): JSX.Element {
106 106
 }
107 107
 
108 108
 const PanelRoot = styled('div', {
109
-  marginLeft: 8,
110 109
   zIndex: 200,
111 110
   overflow: 'hidden',
112 111
   position: 'relative',
@@ -114,11 +113,11 @@ const PanelRoot = styled('div', {
114 113
   flexDirection: 'column',
115 114
   alignItems: 'center',
116 115
   pointerEvents: 'all',
117
-  padding: '2px',
116
+  padding: '$0',
118 117
   borderRadius: '4px',
119 118
   backgroundColor: '$panel',
120 119
   border: '1px solid $panel',
121
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
120
+  boxShadow: '$4',
122 121
   userSelect: 'none',
123 122
 })
124 123
 
@@ -157,11 +156,11 @@ const StyledRadioItem = styled(DropdownMenu.RadioItem, {
157 156
 })
158 157
 
159 158
 const StyledContextMenuContent = styled(ContextMenu.Content, {
160
-  padding: '2px',
159
+  padding: '$0',
161 160
   borderRadius: '4px',
162 161
   backgroundColor: '$panel',
163 162
   border: '1px solid $panel',
164
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
163
+  boxShadow: '$4',
165 164
 })
166 165
 
167 166
 const StyledContextMenuItem = styled(ContextMenu.Item, {

+ 3
- 3
components/panel.tsx 查看文件

@@ -9,7 +9,7 @@ export const Root = styled('div', {
9 9
   userSelect: 'none',
10 10
   zIndex: 200,
11 11
   border: '1px solid $panel',
12
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
12
+  boxShadow: '$4',
13 13
   font: '$ui',
14 14
 
15 15
   variants: {
@@ -27,7 +27,7 @@ export const Root = styled('div', {
27 27
     isOpen: {
28 28
       true: {},
29 29
       false: {
30
-        padding: 2,
30
+        padding: '$0',
31 31
         height: 38,
32 32
         width: 38,
33 33
       },
@@ -84,7 +84,7 @@ export const Header = styled('div', {
84 84
   width: '100%',
85 85
   alignItems: 'center',
86 86
   justifyContent: 'space-between',
87
-  padding: 2,
87
+  padding: '$0',
88 88
   position: 'relative',
89 89
 
90 90
   '& h3': {

+ 259
- 3
components/shared.tsx 查看文件

@@ -1,8 +1,11 @@
1
+import * as ContextMenu from '@radix-ui/react-context-menu'
1 2
 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
2 3
 import * as RadioGroup from '@radix-ui/react-radio-group'
3 4
 import * as Panel from './panel'
4 5
 import styled from 'styles'
5 6
 import { forwardRef } from 'react'
7
+import { ChevronRightIcon } from '@radix-ui/react-icons'
8
+import { isMobile } from 'utils'
6 9
 
7 10
 export const breakpoints: any = { '@initial': 'mobile', '@sm': 'small' }
8 11
 
@@ -146,13 +149,13 @@ export const StylePanelRoot = styled(Panel.Root, {
146 149
   overflow: 'hidden',
147 150
   position: 'relative',
148 151
   border: '1px solid $panel',
149
-  boxShadow: '0px 2px 4px rgba(0,0,0,.12)',
152
+  boxShadow: '$4',
150 153
 
151 154
   variants: {
152 155
     isOpen: {
153 156
       true: {},
154 157
       false: {
155
-        padding: 2,
158
+        padding: '$0',
156 159
         height: 38,
157 160
         width: 38,
158 161
       },
@@ -258,7 +261,7 @@ export const DropdownContent = styled(DropdownMenu.Content, {
258 261
   backgroundColor: '$panel',
259 262
   borderRadius: 4,
260 263
   border: '1px solid $panel',
261
-  boxShadow: '0px 2px 4px rgba(0,0,0,.28)',
264
+  boxShadow: '$4',
262 265
 
263 266
   variants: {
264 267
     direction: {
@@ -415,3 +418,256 @@ export const VerticalDivider = styled('hr', {
415 418
   border: 'none',
416 419
   backgroundColor: '$brushFill',
417 420
 })
421
+
422
+export const FloatingContainer = styled('div', {
423
+  backgroundColor: '$panel',
424
+  border: '1px solid $panel',
425
+  borderRadius: '4px',
426
+  boxShadow: '$4',
427
+  display: 'flex',
428
+  height: 'fit-content',
429
+  padding: '$0',
430
+  pointerEvents: 'all',
431
+  position: 'relative',
432
+  userSelect: 'none',
433
+  zIndex: 200,
434
+})
435
+
436
+/* -------------------------------------------------- */
437
+/*                        Menus                       */
438
+/* -------------------------------------------------- */
439
+
440
+export const MenuContent = styled('div', {
441
+  position: 'relative',
442
+  backgroundColor: '$panel',
443
+  borderRadius: '4px',
444
+  overflow: 'hidden',
445
+  pointerEvents: 'all',
446
+  userSelect: 'none',
447
+  zIndex: 180,
448
+  border: '1px solid $panel',
449
+  padding: '$0',
450
+  boxShadow: '$4',
451
+  minWidth: 200,
452
+  font: '$ui',
453
+
454
+  '& kbd': {
455
+    marginLeft: '32px',
456
+    fontSize: '$1',
457
+    fontFamily: '$ui',
458
+    fontWeight: 400,
459
+  },
460
+
461
+  '& kbd > span': {
462
+    display: 'inline-block',
463
+    width: '12px',
464
+  },
465
+
466
+  variants: {
467
+    isMobile: {
468
+      true: {
469
+        '& kbd': {
470
+          display: 'none',
471
+        },
472
+      },
473
+    },
474
+  },
475
+})
476
+
477
+/* -------------------------------------------------- */
478
+/*                    Dropdown Menu                   */
479
+/* -------------------------------------------------- */
480
+
481
+export function DropdownMenuRoot({
482
+  isOpen,
483
+  onOpenChange,
484
+  children,
485
+}: {
486
+  isOpen?: boolean
487
+  onOpenChange?: (isOpen: boolean) => void
488
+  children: React.ReactNode
489
+}): JSX.Element {
490
+  return (
491
+    <DropdownMenu.Root dir="ltr" open={isOpen} onOpenChange={onOpenChange}>
492
+      {children}
493
+    </DropdownMenu.Root>
494
+  )
495
+}
496
+
497
+export function DropdownMenuSubMenu({
498
+  children,
499
+  label,
500
+}: {
501
+  label: string
502
+  children: React.ReactNode
503
+}): JSX.Element {
504
+  return (
505
+    <DropdownMenu.Root dir="ltr">
506
+      <DropdownMenu.TriggerItem as={RowButton} bp={breakpoints}>
507
+        <span>{label}</span>
508
+        <IconWrapper size="small">
509
+          <ChevronRightIcon />
510
+        </IconWrapper>
511
+      </DropdownMenu.TriggerItem>
512
+      <DropdownMenu.Content
513
+        as={MenuContent}
514
+        sideOffset={2}
515
+        alignOffset={-2}
516
+        isMobile={isMobile()}
517
+      >
518
+        {children}
519
+        <DropdownMenuArrow offset={13} />
520
+      </DropdownMenu.Content>
521
+    </DropdownMenu.Root>
522
+  )
523
+}
524
+
525
+export const DropdownMenuDivider = styled(DropdownMenu.Separator, {
526
+  backgroundColor: '$hover',
527
+  height: 1,
528
+  margin: '$2 -$2',
529
+})
530
+
531
+export const DropdownMenuArrow = styled(DropdownMenu.Arrow, {
532
+  fill: '$panel',
533
+})
534
+
535
+export function DropdownMenuButton({
536
+  onSelect,
537
+  children,
538
+  disabled = false,
539
+}: {
540
+  onSelect?: () => void
541
+  disabled?: boolean
542
+  children: React.ReactNode
543
+}): JSX.Element {
544
+  return (
545
+    <DropdownMenu.Item
546
+      as={RowButton}
547
+      bp={breakpoints}
548
+      disabled={disabled}
549
+      onSelect={onSelect}
550
+    >
551
+      {children}
552
+    </DropdownMenu.Item>
553
+  )
554
+}
555
+
556
+export function DropdownMenuIconButton({
557
+  onSelect,
558
+  children,
559
+  disabled = false,
560
+}: {
561
+  onSelect: () => void
562
+  disabled?: boolean
563
+  children: React.ReactNode
564
+}): JSX.Element {
565
+  return (
566
+    <DropdownMenu.Item
567
+      as={IconButton}
568
+      bp={breakpoints}
569
+      disabled={disabled}
570
+      onSelect={onSelect}
571
+    >
572
+      {children}
573
+    </DropdownMenu.Item>
574
+  )
575
+}
576
+
577
+/* -------------------------------------------------- */
578
+/*                    Context Menu                   */
579
+/* -------------------------------------------------- */
580
+
581
+export function ContextMenuRoot({
582
+  onOpenChange,
583
+  children,
584
+}: {
585
+  onOpenChange?: (isOpen: boolean) => void
586
+  children: React.ReactNode
587
+}): JSX.Element {
588
+  return (
589
+    <ContextMenu.Root dir="ltr" onOpenChange={onOpenChange}>
590
+      {children}
591
+    </ContextMenu.Root>
592
+  )
593
+}
594
+
595
+export function ContextMenuSubMenu({
596
+  children,
597
+  label,
598
+}: {
599
+  label: string
600
+  children: React.ReactNode
601
+}): JSX.Element {
602
+  return (
603
+    <ContextMenu.Root dir="ltr">
604
+      <ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
605
+        <span>{label}</span>
606
+        <IconWrapper size="small">
607
+          <ChevronRightIcon />
608
+        </IconWrapper>
609
+      </ContextMenu.TriggerItem>
610
+      <ContextMenu.Content
611
+        as={MenuContent}
612
+        sideOffset={2}
613
+        alignOffset={-2}
614
+        isMobile={isMobile()}
615
+      >
616
+        {children}
617
+        <ContextMenuArrow offset={13} />
618
+      </ContextMenu.Content>
619
+    </ContextMenu.Root>
620
+  )
621
+}
622
+
623
+export const ContextMenuDivider = styled(ContextMenu.Separator, {
624
+  backgroundColor: '$hover',
625
+  height: 1,
626
+  margin: '$2 -$2',
627
+})
628
+
629
+export const ContextMenuArrow = styled(ContextMenu.Arrow, {
630
+  fill: '$panel',
631
+})
632
+
633
+export function ContextMenuButton({
634
+  onSelect,
635
+  children,
636
+  disabled = false,
637
+}: {
638
+  onSelect?: () => void
639
+  disabled?: boolean
640
+  children: React.ReactNode
641
+}): JSX.Element {
642
+  return (
643
+    <ContextMenu.Item
644
+      as={RowButton}
645
+      bp={breakpoints}
646
+      disabled={disabled}
647
+      onSelect={onSelect}
648
+    >
649
+      {children}
650
+    </ContextMenu.Item>
651
+  )
652
+}
653
+
654
+export function ContextMenuIconButton({
655
+  onSelect,
656
+  children,
657
+  disabled = false,
658
+}: {
659
+  onSelect: () => void
660
+  disabled?: boolean
661
+  children: React.ReactNode
662
+}): JSX.Element {
663
+  return (
664
+    <ContextMenu.Item
665
+      as={IconButton}
666
+      bp={breakpoints}
667
+      disabled={disabled}
668
+      onSelect={onSelect}
669
+    >
670
+      {children}
671
+    </ContextMenu.Item>
672
+  )
673
+}

+ 3
- 12
components/style-panel/style-panel.tsx 查看文件

@@ -29,8 +29,6 @@ const handleStylePanelOpen = () => state.send('TOGGLED_STYLE_PANEL_OPEN')
29 29
 const handleCopy = () => state.send('COPIED')
30 30
 const handlePaste = () => state.send('PASTED')
31 31
 const handleCopyToSvg = () => state.send('COPIED_TO_SVG')
32
-const handleSave = () => state.send('SAVED')
33
-const handleLoad = () => state.send('LOADED_FROM_FILE_STSTEM')
34 32
 
35 33
 export default function StylePanel(): JSX.Element {
36 34
   const rContainer = useRef<HTMLDivElement>(null)
@@ -99,13 +97,6 @@ function SelectedShapeContent(): JSX.Element {
99 97
           <Share2Icon />
100 98
         </IconWrapper>
101 99
       </RowButton>
102
-      <hr />
103
-      <RowButton bp={breakpoints} onClick={handleSave}>
104
-        <span>Save</span>
105
-      </RowButton>
106
-      <RowButton bp={breakpoints} onClick={handleLoad}>
107
-        <span>Load</span>
108
-      </RowButton>
109 100
     </>
110 101
   )
111 102
 }
@@ -117,18 +108,18 @@ const StylePanelRoot = styled(motion(Panel.Root), {
117 108
   overflow: 'hidden',
118 109
   position: 'relative',
119 110
   border: '1px solid $panel',
120
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
111
+  boxShadow: '$4',
121 112
   display: 'flex',
122 113
   flexDirection: 'column',
123 114
   alignItems: 'center',
124 115
   pointerEvents: 'all',
125
-  padding: 2,
116
+  padding: '$0',
126 117
   zIndex: 300,
127 118
 
128 119
   '& hr': {
129 120
     marginTop: 2,
130 121
     marginBottom: 2,
131
-    marginLeft: '-2px',
122
+    marginLeft: '-$0',
132 123
     border: 'none',
133 124
     height: 1,
134 125
     backgroundColor: '$brushFill',

+ 4
- 15
components/tools-panel/shared.tsx 查看文件

@@ -1,3 +1,4 @@
1
+import { FloatingContainer } from 'components/shared'
1 2
 import Tooltip from 'components/tooltip'
2 3
 import styled from 'styles'
3 4
 
@@ -5,6 +6,7 @@ export const ToolButton = styled('button', {
5 6
   position: 'relative',
6 7
   height: '32px',
7 8
   width: '32px',
9
+  color: '$text',
8 10
   backgroundColor: '$panel',
9 11
   borderRadius: '4px',
10 12
   padding: '0',
@@ -219,21 +221,8 @@ export function TertiaryButton({
219 221
   )
220 222
 }
221 223
 
222
-export const Container = styled('div', {
223
-  backgroundColor: '$panel',
224
-  border: '1px solid $panel',
225
-  borderRadius: '4px',
226
-  boxShadow: '0px 2px 4px rgba(0,0,0,.16)',
227
-  display: 'flex',
228
-  height: 'fit-content',
229
-  padding: 2,
230
-  pointerEvents: 'all',
231
-  position: 'relative',
232
-  userSelect: 'none',
233
-  zIndex: 200,
234
-})
235
-
236
-export const TertiaryButtonsContainer = styled(Container, {
224
+export const TertiaryButtonsContainer = styled(FloatingContainer, {
225
+  boxShadow: '$3',
237 226
   variants: {
238 227
     bp: {
239 228
       mobile: {

+ 8
- 7
components/tools-panel/tools-panel.tsx 查看文件

@@ -8,7 +8,8 @@ import {
8 8
   SquareIcon,
9 9
   TextIcon,
10 10
 } from '@radix-ui/react-icons'
11
-import { PrimaryButton, SecondaryButton, Container } from './shared'
11
+import { PrimaryButton, SecondaryButton } from './shared'
12
+import { FloatingContainer } from '../shared'
12 13
 import React from 'react'
13 14
 import state, { useSelector } from 'state'
14 15
 import styled from 'styles'
@@ -33,7 +34,7 @@ export default function ToolsPanel(): JSX.Element {
33 34
     <ToolsPanelContainer>
34 35
       <LeftWrap size={{ '@initial': 'mobile', '@sm': 'small' }}>
35 36
         <Zoom />
36
-        <Container>
37
+        <FloatingContainer>
37 38
           <SecondaryButton
38 39
             label={'Select'}
39 40
             onClick={selectSelectTool}
@@ -41,10 +42,10 @@ export default function ToolsPanel(): JSX.Element {
41 42
           >
42 43
             <CursorArrowIcon />
43 44
           </SecondaryButton>
44
-        </Container>
45
+        </FloatingContainer>
45 46
       </LeftWrap>
46 47
       <CenterWrap>
47
-        <Container>
48
+        <FloatingContainer>
48 49
           <PrimaryButton
49 50
             label={ShapeType.Draw}
50 51
             onClick={selectDrawTool}
@@ -80,10 +81,10 @@ export default function ToolsPanel(): JSX.Element {
80 81
           >
81 82
             <TextIcon />
82 83
           </PrimaryButton>
83
-        </Container>
84
+        </FloatingContainer>
84 85
       </CenterWrap>
85 86
       <RightWrap size={{ '@initial': 'mobile', '@sm': 'small' }}>
86
-        <Container>
87
+        <FloatingContainer>
87 88
           <SecondaryButton
88 89
             label={'Lock Tool'}
89 90
             onClick={toggleToolLock}
@@ -91,7 +92,7 @@ export default function ToolsPanel(): JSX.Element {
91 92
           >
92 93
             {isToolLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
93 94
           </SecondaryButton>
94
-        </Container>
95
+        </FloatingContainer>
95 96
         <UndoRedo />
96 97
       </RightWrap>
97 98
     </ToolsPanelContainer>

+ 2
- 2
pages/sponsorware.tsx 查看文件

@@ -80,7 +80,7 @@ const Content = styled('div', {
80 80
   backgroundColor: '$panel',
81 81
   margin: '32px auto',
82 82
   borderRadius: '0px',
83
-  boxShadow: '0px 2px 24px rgba(0,0,0,.08), 0px 2px 4px rgba(0,0,0,.16)',
83
+  boxShadow: '$12',
84 84
   padding: '16px',
85 85
   overflow: 'hidden',
86 86
   color: '$text',
@@ -154,7 +154,7 @@ const Button = styled('button', {
154 154
         fontWeight: 'bold',
155 155
         background: '$bounds',
156 156
         color: '$panel',
157
-        boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
157
+        boxShadow: '$4',
158 158
       },
159 159
       secondary: {
160 160
         border: '1px solid $overlay',

+ 10
- 0
state/state.ts 查看文件

@@ -203,6 +203,9 @@ const state = createState({
203 203
           unlessAny: ['isReadOnly', 'isInSession'],
204 204
           do: 'pasteShapesFromClipboard',
205 205
         },
206
+        TOGGLED_DARK_MODE: {
207
+          do: 'toggleDarkMode',
208
+        },
206 209
         TOGGLED_SHAPE_LOCK: {
207 210
           unlessAny: ['isReadOnly', 'isInSession'],
208 211
           if: 'hasSelection',
@@ -1263,6 +1266,7 @@ const state = createState({
1263 1266
     copyDebugLog() {
1264 1267
       logger.copyToJson()
1265 1268
     },
1269
+
1266 1270
     // Networked Room
1267 1271
     addRtShape(data, payload: { pageId: string; shape: Shape }) {
1268 1272
       const { pageId, shape } = payload
@@ -1962,6 +1966,12 @@ const state = createState({
1962 1966
       history.reset()
1963 1967
     },
1964 1968
 
1969
+    /* ------------------- Preferences ------------------ */
1970
+
1971
+    toggleDarkMode(data) {
1972
+      data.settings.isDarkMode = !data.settings.isDarkMode
1973
+    },
1974
+
1965 1975
     /* --------------------- Styles --------------------- */
1966 1976
 
1967 1977
     toggleStylePanel(data) {

+ 45
- 5
styles/stitches.config.ts 查看文件

@@ -26,7 +26,20 @@ const { styled, global, css, theme, getCssString } = createCss({
26 26
       inputBorder: '#ddd',
27 27
       lineError: 'rgba(255, 0, 0, .1)',
28 28
     },
29
-    space: {},
29
+    shadows: {
30
+      2: '0px 1px 1px rgba(0, 0, 0, 0.14)',
31
+      3: '0px 2px 3px rgba(0, 0, 0, 0.14)',
32
+      4: '0px 4px 5px -1px rgba(0, 0, 0, 0.14)',
33
+      8: '0px 12px 17px rgba(0, 0, 0, 0.14)',
34
+      12: '0px 12px 17px rgba(0, 0, 0, 0.14)',
35
+      24: '0px 24px 38px rgba(0, 0, 0, 0.14)',
36
+    },
37
+    space: {
38
+      0: '2px',
39
+      1: '3px',
40
+      2: '4px',
41
+      3: '8px',
42
+    },
30 43
     fontSizes: {
31 44
       0: '10px',
32 45
       1: '12px',
@@ -43,10 +56,15 @@ const { styled, global, css, theme, getCssString } = createCss({
43 56
     lineHeights: {},
44 57
     letterSpacings: {},
45 58
     sizes: {},
46
-    borderWidths: {},
59
+    borderWidths: {
60
+      0: '$1',
61
+    },
47 62
     borderStyles: {},
48
-    radii: {},
49
-    shadows: {},
63
+    radii: {
64
+      0: '2px',
65
+      1: '4px',
66
+      2: '8px',
67
+    },
50 68
     zIndices: {},
51 69
     transitions: {},
52 70
   },
@@ -76,7 +94,29 @@ const { styled, global, css, theme, getCssString } = createCss({
76 94
 
77 95
 const light = theme({})
78 96
 
79
-const dark = theme({})
97
+const dark = theme({
98
+  colors: {
99
+    codeHl: 'rgba(144, 144, 144, .15)',
100
+    brushFill: 'rgba(0,0,0,.05)',
101
+    brushStroke: 'rgba(0,0,0,.25)',
102
+    hint: 'rgba(216, 226, 249, 1.000)',
103
+    selected: 'rgba(66, 133, 244, 1.000)',
104
+    bounds: 'rgba(65, 132, 244, 1.000)',
105
+    boundsBg: 'rgba(65, 132, 244, 0.05)',
106
+    highlight: 'rgba(65, 132, 244, 0.15)',
107
+    overlay: 'rgba(0, 0, 0, 0.15)',
108
+    border: '#aaa',
109
+    canvas: '#fafafa',
110
+    panel: '#fefefe',
111
+    inactive: '#cccccf',
112
+    hover: '#efefef',
113
+    text: '#333',
114
+    muted: '#777',
115
+    input: '#f3f3f3',
116
+    inputBorder: '#ddd',
117
+    lineError: 'rgba(255, 0, 0, .1)',
118
+  },
119
+})
80 120
 
81 121
 const globalStyles = global({
82 122
   '*': { boxSizing: 'border-box' },

Loading…
取消
儲存