浏览代码

Adds menu panel

main
Steve Ruiz 3 年前
父节点
当前提交
6fb1822467

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

2
 import styled from 'styles'
2
 import styled from 'styles'
3
 import {
3
 import {
4
   IconWrapper,
4
   IconWrapper,
5
-  IconButton as _IconButton,
6
-  RowButton,
7
   breakpoints,
5
   breakpoints,
6
+  RowButton,
7
+  ContextMenuArrow,
8
+  ContextMenuDivider,
9
+  ContextMenuButton,
10
+  ContextMenuSubMenu,
11
+  ContextMenuIconButton,
12
+  ContextMenuRoot,
13
+  MenuContent,
8
 } from 'components/shared'
14
 } from 'components/shared'
9
 import { commandKey, deepCompareArrays, isMobile } from 'utils'
15
 import { commandKey, deepCompareArrays, isMobile } from 'utils'
10
 import state, { useSelector } from 'state'
16
 import state, { useSelector } from 'state'
93
   const hasThreeOrMore = selectedShapeIds.length > 2
99
   const hasThreeOrMore = selectedShapeIds.length > 2
94
 
100
 
95
   return (
101
   return (
96
-    <_ContextMenu.Root dir="ltr">
102
+    <ContextMenuRoot>
97
       <_ContextMenu.Trigger>{children}</_ContextMenu.Trigger>
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
         {selectedShapeIds.length ? (
109
         {selectedShapeIds.length ? (
100
           <>
110
           <>
101
-            {/* <Button onSelect={() => state.send('COPIED')}>
111
+            {/* <ContextMenuButton onSelect={() => state.send('COPIED')}>
102
               <span>Copy</span>
112
               <span>Copy</span>
103
               <kbd>
113
               <kbd>
104
                 <span>{commandKey()}</span>
114
                 <span>{commandKey()}</span>
105
                 <span>C</span>
115
                 <span>C</span>
106
               </kbd>
116
               </kbd>
107
-            </Button>
108
-            <Button onSelect={() => state.send('CUT')}>
117
+            </ContextMenuButton>
118
+            <ContextMenuButton onSelect={() => state.send('CUT')}>
109
               <span>Cut</span>
119
               <span>Cut</span>
110
               <kbd>
120
               <kbd>
111
                 <span>{commandKey()}</span>
121
                 <span>{commandKey()}</span>
112
                 <span>X</span>
122
                 <span>X</span>
113
               </kbd>
123
               </kbd>
114
-            </Button>
124
+            </ContextMenuButton>
115
              */}
125
              */}
116
-            <Button onSelect={() => state.send('DUPLICATED')}>
126
+            <ContextMenuButton onSelect={() => state.send('DUPLICATED')}>
117
               <span>Duplicate</span>
127
               <span>Duplicate</span>
118
               <kbd>
128
               <kbd>
119
                 <span>{commandKey()}</span>
129
                 <span>{commandKey()}</span>
120
                 <span>D</span>
130
                 <span>D</span>
121
               </kbd>
131
               </kbd>
122
-            </Button>
123
-            <StyledDivider />
132
+            </ContextMenuButton>
133
+            <ContextMenuDivider />
124
             {hasGroupSelected ||
134
             {hasGroupSelected ||
125
               (hasTwoOrMore && (
135
               (hasTwoOrMore && (
126
                 <>
136
                 <>
127
                   {hasGroupSelected && (
137
                   {hasGroupSelected && (
128
-                    <Button onSelect={() => state.send('UNGROUPED')}>
138
+                    <ContextMenuButton onSelect={() => state.send('UNGROUPED')}>
129
                       <span>Ungroup</span>
139
                       <span>Ungroup</span>
130
                       <kbd>
140
                       <kbd>
131
                         <span>{commandKey()}</span>
141
                         <span>{commandKey()}</span>
132
                         <span>⇧</span>
142
                         <span>⇧</span>
133
                         <span>G</span>
143
                         <span>G</span>
134
                       </kbd>
144
                       </kbd>
135
-                    </Button>
145
+                    </ContextMenuButton>
136
                   )}
146
                   )}
137
                   {hasTwoOrMore && (
147
                   {hasTwoOrMore && (
138
-                    <Button onSelect={() => state.send('GROUPED')}>
148
+                    <ContextMenuButton onSelect={() => state.send('GROUPED')}>
139
                       <span>Group</span>
149
                       <span>Group</span>
140
                       <kbd>
150
                       <kbd>
141
                         <span>{commandKey()}</span>
151
                         <span>{commandKey()}</span>
142
                         <span>G</span>
152
                         <span>G</span>
143
                       </kbd>
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
                 onSelect={() =>
160
                 onSelect={() =>
151
                   state.send('MOVED', {
161
                   state.send('MOVED', {
152
                     type: MoveType.ToFront,
162
                     type: MoveType.ToFront,
159
                   <span>⇧</span>
169
                   <span>⇧</span>
160
                   <span>]</span>
170
                   <span>]</span>
161
                 </kbd>
171
                 </kbd>
162
-              </Button>
172
+              </ContextMenuButton>
163
 
173
 
164
-              <Button
174
+              <ContextMenuButton
165
                 onSelect={() =>
175
                 onSelect={() =>
166
                   state.send('MOVED', {
176
                   state.send('MOVED', {
167
                     type: MoveType.Forward,
177
                     type: MoveType.Forward,
173
                   <span>{commandKey()}</span>
183
                   <span>{commandKey()}</span>
174
                   <span>]</span>
184
                   <span>]</span>
175
                 </kbd>
185
                 </kbd>
176
-              </Button>
177
-              <Button
186
+              </ContextMenuButton>
187
+              <ContextMenuButton
178
                 onSelect={() =>
188
                 onSelect={() =>
179
                   state.send('MOVED', {
189
                   state.send('MOVED', {
180
                     type: MoveType.Backward,
190
                     type: MoveType.Backward,
186
                   <span>{commandKey()}</span>
196
                   <span>{commandKey()}</span>
187
                   <span>[</span>
197
                   <span>[</span>
188
                 </kbd>
198
                 </kbd>
189
-              </Button>
190
-              <Button
199
+              </ContextMenuButton>
200
+              <ContextMenuButton
191
                 onSelect={() =>
201
                 onSelect={() =>
192
                   state.send('MOVED', {
202
                   state.send('MOVED', {
193
                     type: MoveType.ToBack,
203
                     type: MoveType.ToBack,
200
                   <span>⇧</span>
210
                   <span>⇧</span>
201
                   <span>[</span>
211
                   <span>[</span>
202
                 </kbd>
212
                 </kbd>
203
-              </Button>
204
-            </SubMenu>
213
+              </ContextMenuButton>
214
+            </ContextMenuSubMenu>
205
             {hasTwoOrMore && (
215
             {hasTwoOrMore && (
206
               <AlignDistributeSubMenu
216
               <AlignDistributeSubMenu
207
                 hasTwoOrMore={hasTwoOrMore}
217
                 hasTwoOrMore={hasTwoOrMore}
209
               />
219
               />
210
             )}
220
             )}
211
             <MoveToPageMenu />
221
             <MoveToPageMenu />
212
-            <Button onSelect={() => state.send('COPIED_TO_SVG')}>
222
+            <ContextMenuButton onSelect={() => state.send('COPIED_TO_SVG')}>
213
               <span>Copy to SVG</span>
223
               <span>Copy to SVG</span>
214
               <kbd>
224
               <kbd>
215
                 <span>{commandKey()}</span>
225
                 <span>{commandKey()}</span>
216
                 <span>⇧</span>
226
                 <span>⇧</span>
217
                 <span>C</span>
227
                 <span>C</span>
218
               </kbd>
228
               </kbd>
219
-            </Button>
220
-            <StyledDivider />
221
-            <Button onSelect={() => state.send('DELETED')}>
229
+            </ContextMenuButton>
230
+            <ContextMenuDivider />
231
+            <ContextMenuButton onSelect={() => state.send('DELETED')}>
222
               <span>Delete</span>
232
               <span>Delete</span>
223
               <kbd>
233
               <kbd>
224
                 <span>⌫</span>
234
                 <span>⌫</span>
225
               </kbd>
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
               <span>Undo</span>
241
               <span>Undo</span>
232
               <kbd>
242
               <kbd>
233
                 <span>{commandKey()}</span>
243
                 <span>{commandKey()}</span>
234
                 <span>Z</span>
244
                 <span>Z</span>
235
               </kbd>
245
               </kbd>
236
-            </Button>
237
-            <Button onSelect={() => state.send('REDO')}>
246
+            </ContextMenuButton>
247
+            <ContextMenuButton onSelect={() => state.send('REDO')}>
238
               <span>Redo</span>
248
               <span>Redo</span>
239
               <kbd>
249
               <kbd>
240
                 <span>{commandKey()}</span>
250
                 <span>{commandKey()}</span>
241
                 <span>⇧</span>
251
                 <span>⇧</span>
242
                 <span>Z</span>
252
                 <span>Z</span>
243
               </kbd>
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
   hasThreeOrMore: boolean
266
   hasThreeOrMore: boolean
364
 }) {
267
 }) {
365
   return (
268
   return (
366
-    <_ContextMenu.Root dir="ltr">
269
+    <ContextMenuRoot>
367
       <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
270
       <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
368
         <span>Align / Distribute</span>
271
         <span>Align / Distribute</span>
369
         <IconWrapper size="small">
272
         <IconWrapper size="small">
371
         </IconWrapper>
274
         </IconWrapper>
372
       </_ContextMenu.TriggerItem>
275
       </_ContextMenu.TriggerItem>
373
       <StyledGrid
276
       <StyledGrid
277
+        as={_ContextMenu.Content}
374
         sideOffset={2}
278
         sideOffset={2}
375
         alignOffset={-2}
279
         alignOffset={-2}
376
         isMobile={isMobile()}
280
         isMobile={isMobile()}
377
         selectedStyle={hasThreeOrMore ? 'threeOrMore' : 'twoOrMore'}
281
         selectedStyle={hasThreeOrMore ? 'threeOrMore' : 'twoOrMore'}
378
       >
282
       >
379
-        <IconButton onSelect={alignLeft}>
283
+        <ContextMenuIconButton onSelect={alignLeft}>
380
           <AlignLeftIcon />
284
           <AlignLeftIcon />
381
-        </IconButton>
382
-        <IconButton onSelect={alignCenterHorizontal}>
285
+        </ContextMenuIconButton>
286
+        <ContextMenuIconButton onSelect={alignCenterHorizontal}>
383
           <AlignCenterHorizontallyIcon />
287
           <AlignCenterHorizontallyIcon />
384
-        </IconButton>
385
-        <IconButton onSelect={alignRight}>
288
+        </ContextMenuIconButton>
289
+        <ContextMenuIconButton onSelect={alignRight}>
386
           <AlignRightIcon />
290
           <AlignRightIcon />
387
-        </IconButton>
388
-        <IconButton onSelect={stretchHorizontally}>
291
+        </ContextMenuIconButton>
292
+        <ContextMenuIconButton onSelect={stretchHorizontally}>
389
           <StretchHorizontallyIcon />
293
           <StretchHorizontallyIcon />
390
-        </IconButton>
294
+        </ContextMenuIconButton>
391
         {hasThreeOrMore && (
295
         {hasThreeOrMore && (
392
-          <IconButton onSelect={distributeHorizontally}>
296
+          <ContextMenuIconButton onSelect={distributeHorizontally}>
393
             <SpaceEvenlyHorizontallyIcon />
297
             <SpaceEvenlyHorizontallyIcon />
394
-          </IconButton>
298
+          </ContextMenuIconButton>
395
         )}
299
         )}
396
 
300
 
397
-        <IconButton onSelect={alignTop}>
301
+        <ContextMenuIconButton onSelect={alignTop}>
398
           <AlignTopIcon />
302
           <AlignTopIcon />
399
-        </IconButton>
400
-        <IconButton onSelect={alignCenterVertical}>
303
+        </ContextMenuIconButton>
304
+        <ContextMenuIconButton onSelect={alignCenterVertical}>
401
           <AlignCenterVerticallyIcon />
305
           <AlignCenterVerticallyIcon />
402
-        </IconButton>
403
-        <IconButton onSelect={alignBottom}>
306
+        </ContextMenuIconButton>
307
+        <ContextMenuIconButton onSelect={alignBottom}>
404
           <AlignBottomIcon />
308
           <AlignBottomIcon />
405
-        </IconButton>
406
-        <IconButton onSelect={stretchVertically}>
309
+        </ContextMenuIconButton>
310
+        <ContextMenuIconButton onSelect={stretchVertically}>
407
           <StretchVerticallyIcon />
311
           <StretchVerticallyIcon />
408
-        </IconButton>
312
+        </ContextMenuIconButton>
409
         {hasThreeOrMore && (
313
         {hasThreeOrMore && (
410
-          <IconButton onSelect={distributeVertically}>
314
+          <ContextMenuIconButton onSelect={distributeVertically}>
411
             <SpaceEvenlyVerticallyIcon />
315
             <SpaceEvenlyVerticallyIcon />
412
-          </IconButton>
316
+          </ContextMenuIconButton>
413
         )}
317
         )}
414
-        <StyledArrow offset={13} />
318
+        <ContextMenuArrow offset={13} />
415
       </StyledGrid>
319
       </StyledGrid>
416
-    </_ContextMenu.Root>
320
+    </ContextMenuRoot>
417
   )
321
   )
418
 }
322
 }
419
 
323
 
420
-const StyledGrid = styled(StyledContent, {
324
+const StyledGrid = styled(MenuContent, {
421
   display: 'grid',
325
   display: 'grid',
422
   variants: {
326
   variants: {
423
     selectedStyle: {
327
     selectedStyle: {
444
   if (sorted.length === 0) return null
348
   if (sorted.length === 0) return null
445
 
349
 
446
   return (
350
   return (
447
-    <_ContextMenu.Root dir="ltr">
448
-      <_ContextMenu.TriggerItem as={RowButton} bp={breakpoints}>
351
+    <ContextMenuRoot>
352
+      <ContextMenuButton>
449
         <span>Move To Page</span>
353
         <span>Move To Page</span>
450
         <IconWrapper size="small">
354
         <IconWrapper size="small">
451
           <ChevronRightIcon />
355
           <ChevronRightIcon />
452
         </IconWrapper>
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
         {sorted.map(({ id, name }) => (
364
         {sorted.map(({ id, name }) => (
456
-          <Button
365
+          <ContextMenuButton
457
             key={id}
366
             key={id}
458
             disabled={id === currentPageId}
367
             disabled={id === currentPageId}
459
             onSelect={() => state.send('MOVED_TO_PAGE', { id })}
368
             onSelect={() => state.send('MOVED_TO_PAGE', { id })}
460
           >
369
           >
461
             <span>{name}</span>
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
   onKey,
40
   onKey,
41
 }: Props): JSX.Element {
41
 }: Props): JSX.Element {
42
   const { theme } = useTheme()
42
   const { theme } = useTheme()
43
+
43
   const rEditor = useRef<IMonacoEditor>(null)
44
   const rEditor = useRef<IMonacoEditor>(null)
44
   const rMonaco = useRef<IMonaco>(null)
45
   const rMonaco = useRef<IMonaco>(null)
45
 
46
 

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

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

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

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

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

4
   return (
4
   return (
5
     <svg
5
     <svg
6
       viewBox="0 0 15 15"
6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8
       xmlns="http://www.w3.org/2000/svg"
8
       xmlns="http://www.w3.org/2000/svg"
9
       {...props}
9
       {...props}
10
     >
10
     >
12
         fillRule="evenodd"
12
         fillRule="evenodd"
13
         clipRule="evenodd"
13
         clipRule="evenodd"
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"
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
       <path
16
       <path
18
         fillRule="evenodd"
17
         fillRule="evenodd"
19
         clipRule="evenodd"
18
         clipRule="evenodd"
20
         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"
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
     </svg>
21
     </svg>
24
   )
22
   )

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

4
   return (
4
   return (
5
     <svg
5
     <svg
6
       viewBox="0 0 15 15"
6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8
       xmlns="http://www.w3.org/2000/svg"
8
       xmlns="http://www.w3.org/2000/svg"
9
       {...props}
9
       {...props}
10
     >
10
     >
12
         fillRule="evenodd"
12
         fillRule="evenodd"
13
         clipRule="evenodd"
13
         clipRule="evenodd"
14
         d="M2 4.656a.5.5 0 01.5-.5h9.7a.5.5 0 010 1H2.5a.5.5 0 01-.5-.5z"
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
       <path
16
       <path
18
         fillRule="evenodd"
17
         fillRule="evenodd"
19
         clipRule="evenodd"
18
         clipRule="evenodd"
20
         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"
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
       <path
21
       <path
24
         fillRule="evenodd"
22
         fillRule="evenodd"
25
         clipRule="evenodd"
23
         clipRule="evenodd"
26
         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"
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
     </svg>
26
     </svg>
30
   )
27
   )

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

4
   return (
4
   return (
5
     <svg
5
     <svg
6
       viewBox="0 0 15 15"
6
       viewBox="0 0 15 15"
7
-      fill="none"
7
+      fill="currentColor"
8
       xmlns="http://www.w3.org/2000/svg"
8
       xmlns="http://www.w3.org/2000/svg"
9
       {...props}
9
       {...props}
10
     >
10
     >
12
         fillRule="evenodd"
12
         fillRule="evenodd"
13
         clipRule="evenodd"
13
         clipRule="evenodd"
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"
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
       <path
16
       <path
18
         fillRule="evenodd"
17
         fillRule="evenodd"
19
         clipRule="evenodd"
18
         clipRule="evenodd"
20
         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"
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
     </svg>
21
     </svg>
24
   )
22
   )

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

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

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

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

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

1
+import * as ContextMenu from '@radix-ui/react-context-menu'
1
 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
2
 import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
2
 import * as RadioGroup from '@radix-ui/react-radio-group'
3
 import * as RadioGroup from '@radix-ui/react-radio-group'
3
 import * as Panel from './panel'
4
 import * as Panel from './panel'
4
 import styled from 'styles'
5
 import styled from 'styles'
5
 import { forwardRef } from 'react'
6
 import { forwardRef } from 'react'
7
+import { ChevronRightIcon } from '@radix-ui/react-icons'
8
+import { isMobile } from 'utils'
6
 
9
 
7
 export const breakpoints: any = { '@initial': 'mobile', '@sm': 'small' }
10
 export const breakpoints: any = { '@initial': 'mobile', '@sm': 'small' }
8
 
11
 
146
   overflow: 'hidden',
149
   overflow: 'hidden',
147
   position: 'relative',
150
   position: 'relative',
148
   border: '1px solid $panel',
151
   border: '1px solid $panel',
149
-  boxShadow: '0px 2px 4px rgba(0,0,0,.12)',
152
+  boxShadow: '$4',
150
 
153
 
151
   variants: {
154
   variants: {
152
     isOpen: {
155
     isOpen: {
153
       true: {},
156
       true: {},
154
       false: {
157
       false: {
155
-        padding: 2,
158
+        padding: '$0',
156
         height: 38,
159
         height: 38,
157
         width: 38,
160
         width: 38,
158
       },
161
       },
258
   backgroundColor: '$panel',
261
   backgroundColor: '$panel',
259
   borderRadius: 4,
262
   borderRadius: 4,
260
   border: '1px solid $panel',
263
   border: '1px solid $panel',
261
-  boxShadow: '0px 2px 4px rgba(0,0,0,.28)',
264
+  boxShadow: '$4',
262
 
265
 
263
   variants: {
266
   variants: {
264
     direction: {
267
     direction: {
415
   border: 'none',
418
   border: 'none',
416
   backgroundColor: '$brushFill',
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
 const handleCopy = () => state.send('COPIED')
29
 const handleCopy = () => state.send('COPIED')
30
 const handlePaste = () => state.send('PASTED')
30
 const handlePaste = () => state.send('PASTED')
31
 const handleCopyToSvg = () => state.send('COPIED_TO_SVG')
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
 export default function StylePanel(): JSX.Element {
33
 export default function StylePanel(): JSX.Element {
36
   const rContainer = useRef<HTMLDivElement>(null)
34
   const rContainer = useRef<HTMLDivElement>(null)
99
           <Share2Icon />
97
           <Share2Icon />
100
         </IconWrapper>
98
         </IconWrapper>
101
       </RowButton>
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
   overflow: 'hidden',
108
   overflow: 'hidden',
118
   position: 'relative',
109
   position: 'relative',
119
   border: '1px solid $panel',
110
   border: '1px solid $panel',
120
-  boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
111
+  boxShadow: '$4',
121
   display: 'flex',
112
   display: 'flex',
122
   flexDirection: 'column',
113
   flexDirection: 'column',
123
   alignItems: 'center',
114
   alignItems: 'center',
124
   pointerEvents: 'all',
115
   pointerEvents: 'all',
125
-  padding: 2,
116
+  padding: '$0',
126
   zIndex: 300,
117
   zIndex: 300,
127
 
118
 
128
   '& hr': {
119
   '& hr': {
129
     marginTop: 2,
120
     marginTop: 2,
130
     marginBottom: 2,
121
     marginBottom: 2,
131
-    marginLeft: '-2px',
122
+    marginLeft: '-$0',
132
     border: 'none',
123
     border: 'none',
133
     height: 1,
124
     height: 1,
134
     backgroundColor: '$brushFill',
125
     backgroundColor: '$brushFill',

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

1
+import { FloatingContainer } from 'components/shared'
1
 import Tooltip from 'components/tooltip'
2
 import Tooltip from 'components/tooltip'
2
 import styled from 'styles'
3
 import styled from 'styles'
3
 
4
 
5
   position: 'relative',
6
   position: 'relative',
6
   height: '32px',
7
   height: '32px',
7
   width: '32px',
8
   width: '32px',
9
+  color: '$text',
8
   backgroundColor: '$panel',
10
   backgroundColor: '$panel',
9
   borderRadius: '4px',
11
   borderRadius: '4px',
10
   padding: '0',
12
   padding: '0',
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
   variants: {
226
   variants: {
238
     bp: {
227
     bp: {
239
       mobile: {
228
       mobile: {

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

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

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

80
   backgroundColor: '$panel',
80
   backgroundColor: '$panel',
81
   margin: '32px auto',
81
   margin: '32px auto',
82
   borderRadius: '0px',
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
   padding: '16px',
84
   padding: '16px',
85
   overflow: 'hidden',
85
   overflow: 'hidden',
86
   color: '$text',
86
   color: '$text',
154
         fontWeight: 'bold',
154
         fontWeight: 'bold',
155
         background: '$bounds',
155
         background: '$bounds',
156
         color: '$panel',
156
         color: '$panel',
157
-        boxShadow: '0px 2px 4px rgba(0,0,0,.2)',
157
+        boxShadow: '$4',
158
       },
158
       },
159
       secondary: {
159
       secondary: {
160
         border: '1px solid $overlay',
160
         border: '1px solid $overlay',

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

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

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

26
       inputBorder: '#ddd',
26
       inputBorder: '#ddd',
27
       lineError: 'rgba(255, 0, 0, .1)',
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
     fontSizes: {
43
     fontSizes: {
31
       0: '10px',
44
       0: '10px',
32
       1: '12px',
45
       1: '12px',
43
     lineHeights: {},
56
     lineHeights: {},
44
     letterSpacings: {},
57
     letterSpacings: {},
45
     sizes: {},
58
     sizes: {},
46
-    borderWidths: {},
59
+    borderWidths: {
60
+      0: '$1',
61
+    },
47
     borderStyles: {},
62
     borderStyles: {},
48
-    radii: {},
49
-    shadows: {},
63
+    radii: {
64
+      0: '2px',
65
+      1: '4px',
66
+      2: '8px',
67
+    },
50
     zIndices: {},
68
     zIndices: {},
51
     transitions: {},
69
     transitions: {},
52
   },
70
   },
76
 
94
 
77
 const light = theme({})
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
 const globalStyles = global({
121
 const globalStyles = global({
82
   '*': { boxSizing: 'border-box' },
122
   '*': { boxSizing: 'border-box' },

正在加载...
取消
保存