瀏覽代碼

feat: Add shortcuts for stroke and background color picker (#3318)

* feat: Add shortcuts for opening stroke and background color picker

* Use App.tsx keydown handler

* only get selectedElements if applicable (perf)

* fix tests and snaps

* reuse `appState.openMenu`

Co-authored-by: dwelle <luzar.david@gmail.com>
vanilla_orig
Arun 3 年之前
父節點
當前提交
6c3e4417e1
沒有連結到貢獻者的電子郵件帳戶。

+ 7
- 3
src/actions/actionCanvas.tsx 查看文件

@@ -22,8 +22,8 @@ export const actionChangeViewBackgroundColor = register({
22 22
   name: "changeViewBackgroundColor",
23 23
   perform: (_, appState, value) => {
24 24
     return {
25
-      appState: { ...appState, viewBackgroundColor: value },
26
-      commitToHistory: true,
25
+      appState: { ...appState, ...value },
26
+      commitToHistory: !!value.viewBackgroundColor,
27 27
     };
28 28
   },
29 29
   PanelComponent: ({ appState, updateData }) => {
@@ -33,7 +33,11 @@ export const actionChangeViewBackgroundColor = register({
33 33
           label={t("labels.canvasBackground")}
34 34
           type="canvasBackground"
35 35
           color={appState.viewBackgroundColor}
36
-          onChange={(color) => updateData(color)}
36
+          onChange={(color) => updateData({ viewBackgroundColor: color })}
37
+          isActive={appState.openMenu === "canvasColorPicker"}
38
+          setActive={(active) =>
39
+            updateData({ openMenu: active ? "canvasColorPicker" : null })
40
+          }
37 41
           data-testid="canvas-background-picker"
38 42
         />
39 43
       </div>

+ 34
- 16
src/actions/actionProperties.tsx 查看文件

@@ -99,13 +99,18 @@ export const actionChangeStrokeColor = register({
99 99
   name: "changeStrokeColor",
100 100
   perform: (elements, appState, value) => {
101 101
     return {
102
-      elements: changeProperty(elements, appState, (el) =>
103
-        newElementWith(el, {
104
-          strokeColor: value,
105
-        }),
106
-      ),
107
-      appState: { ...appState, currentItemStrokeColor: value },
108
-      commitToHistory: true,
102
+      ...(value.currentItemStrokeColor && {
103
+        elements: changeProperty(elements, appState, (el) =>
104
+          newElementWith(el, {
105
+            strokeColor: value.currentItemStrokeColor,
106
+          }),
107
+        ),
108
+      }),
109
+      appState: {
110
+        ...appState,
111
+        ...value,
112
+      },
113
+      commitToHistory: !!value.currentItemStrokeColor,
109 114
     };
110 115
   },
111 116
   PanelComponent: ({ elements, appState, updateData }) => (
@@ -120,7 +125,11 @@ export const actionChangeStrokeColor = register({
120 125
           (element) => element.strokeColor,
121 126
           appState.currentItemStrokeColor,
122 127
         )}
123
-        onChange={updateData}
128
+        onChange={(color) => updateData({ currentItemStrokeColor: color })}
129
+        isActive={appState.openMenu === "strokeColorPicker"}
130
+        setActive={(active) =>
131
+          updateData({ openMenu: active ? "strokeColorPicker" : null })
132
+        }
124 133
       />
125 134
     </>
126 135
   ),
@@ -130,13 +139,18 @@ export const actionChangeBackgroundColor = register({
130 139
   name: "changeBackgroundColor",
131 140
   perform: (elements, appState, value) => {
132 141
     return {
133
-      elements: changeProperty(elements, appState, (el) =>
134
-        newElementWith(el, {
135
-          backgroundColor: value,
136
-        }),
137
-      ),
138
-      appState: { ...appState, currentItemBackgroundColor: value },
139
-      commitToHistory: true,
142
+      ...(value.currentItemBackgroundColor && {
143
+        elements: changeProperty(elements, appState, (el) =>
144
+          newElementWith(el, {
145
+            backgroundColor: value.currentItemBackgroundColor,
146
+          }),
147
+        ),
148
+      }),
149
+      appState: {
150
+        ...appState,
151
+        ...value,
152
+      },
153
+      commitToHistory: !!value.currentItemBackgroundColor,
140 154
     };
141 155
   },
142 156
   PanelComponent: ({ elements, appState, updateData }) => (
@@ -151,7 +165,11 @@ export const actionChangeBackgroundColor = register({
151 165
           (element) => element.backgroundColor,
152 166
           appState.currentItemBackgroundColor,
153 167
         )}
154
-        onChange={updateData}
168
+        onChange={(color) => updateData({ currentItemBackgroundColor: color })}
169
+        isActive={appState.openMenu === "backgroundColorPicker"}
170
+        setActive={(active) =>
171
+          updateData({ openMenu: active ? "backgroundColorPicker" : null })
172
+        }
155 173
       />
156 174
     </>
157 175
   ),

+ 15
- 0
src/components/App.tsx 查看文件

@@ -1645,6 +1645,21 @@ class App extends React.Component<AppProps, AppState> {
1645 1645
         isHoldingSpace = true;
1646 1646
         setCursor(this.canvas, CURSOR_TYPE.GRABBING);
1647 1647
       }
1648
+
1649
+      if (event.key === KEYS.G || event.key === KEYS.S) {
1650
+        const selectedElements = getSelectedElements(
1651
+          this.scene.getElements(),
1652
+          this.state,
1653
+        );
1654
+        if (selectedElements.length) {
1655
+          if (event.key === KEYS.G) {
1656
+            this.setState({ openMenu: "backgroundColorPicker" });
1657
+          }
1658
+          if (event.key === KEYS.S) {
1659
+            this.setState({ openMenu: "strokeColorPicker" });
1660
+          }
1661
+        }
1662
+      }
1648 1663
     },
1649 1664
   );
1650 1665
 

+ 4
- 1
src/components/ColorPicker.tsx 查看文件

@@ -238,13 +238,16 @@ export const ColorPicker = ({
238 238
   color,
239 239
   onChange,
240 240
   label,
241
+  isActive,
242
+  setActive,
241 243
 }: {
242 244
   type: "canvasBackground" | "elementBackground" | "elementStroke";
243 245
   color: string | null;
244 246
   onChange: (color: string) => void;
245 247
   label: string;
248
+  isActive: boolean;
249
+  setActive: (active: boolean) => void;
246 250
 }) => {
247
-  const [isActive, setActive] = React.useState(false);
248 251
   const pickerButton = React.useRef<HTMLButtonElement>(null);
249 252
 
250 253
   return (

+ 8
- 0
src/components/HelpDialog.tsx 查看文件

@@ -365,6 +365,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
365 365
                   label={t("labels.flipVertical")}
366 366
                   shortcuts={[getShortcutKey("Shift+V")]}
367 367
                 />
368
+                <Shortcut
369
+                  label={t("labels.showStroke")}
370
+                  shortcuts={[getShortcutKey("S")]}
371
+                />
372
+                <Shortcut
373
+                  label={t("labels.showBackground")}
374
+                  shortcuts={[getShortcutKey("G")]}
375
+                />
368 376
               </ShortcutIsland>
369 377
             </Column>
370 378
           </Columns>

+ 1
- 0
src/keys.ts 查看文件

@@ -44,6 +44,7 @@ export const KEYS = {
44 44
   A: "a",
45 45
   D: "d",
46 46
   E: "e",
47
+  G: "g",
47 48
   L: "l",
48 49
   O: "o",
49 50
   P: "p",

+ 2
- 0
src/locales/en.json 查看文件

@@ -101,6 +101,8 @@
101 101
     "viewMode": "View mode",
102 102
     "toggleExportColorScheme": "Toggle export color scheme",
103 103
     "share": "Share",
104
+    "showStroke": "Show stroke color picker",
105
+    "showBackground": "Show background color picker",
104 106
     "toggleTheme": "Toggle theme"
105 107
   },
106 108
   "buttons": {

+ 2
- 2
src/tests/__snapshots__/contextmenu.test.tsx.snap 查看文件

@@ -1748,7 +1748,7 @@ Object {
1748 1748
   "name": "Untitled-201933152653",
1749 1749
   "offsetLeft": 20,
1750 1750
   "offsetTop": 10,
1751
-  "openMenu": null,
1751
+  "openMenu": "backgroundColorPicker",
1752 1752
   "pasteDialog": Object {
1753 1753
     "data": null,
1754 1754
     "shown": false,
@@ -2424,7 +2424,7 @@ Object {
2424 2424
 
2425 2425
 exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] number of elements 1`] = `2`;
2426 2426
 
2427
-exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] number of renders 1`] = `25`;
2427
+exports[`contextMenu element selecting 'Paste styles' in context menu pastes styles: [end of test] number of renders 1`] = `27`;
2428 2428
 
2429 2429
 exports[`contextMenu element selecting 'Send backward' in context menu sends element backward: [end of test] appState 1`] = `
2430 2430
 Object {

+ 77
- 152
src/tests/__snapshots__/regressionTests.test.tsx.snap 查看文件

@@ -3260,7 +3260,7 @@ Object {
3260 3260
   "name": "Untitled-201933152653",
3261 3261
   "offsetLeft": 0,
3262 3262
   "offsetTop": 0,
3263
-  "openMenu": null,
3263
+  "openMenu": "strokeColorPicker",
3264 3264
   "pasteDialog": Object {
3265 3265
     "data": null,
3266 3266
     "shown": false,
@@ -3451,7 +3451,7 @@ Object {
3451 3451
 
3452 3452
 exports[`regression tests change the properties of a shape: [end of test] number of elements 1`] = `1`;
3453 3453
 
3454
-exports[`regression tests change the properties of a shape: [end of test] number of renders 1`] = `12`;
3454
+exports[`regression tests change the properties of a shape: [end of test] number of renders 1`] = `14`;
3455 3455
 
3456 3456
 exports[`regression tests click on an element and drag it: [dragged] appState 1`] = `
3457 3457
 Object {
@@ -8734,7 +8734,7 @@ Object {
8734 8734
   "boundElementIds": null,
8735 8735
   "fillStyle": "hachure",
8736 8736
   "groupIds": Array [],
8737
-  "height": 1000,
8737
+  "height": 0,
8738 8738
   "id": "id0",
8739 8739
   "isDeleted": false,
8740 8740
   "opacity": 100,
@@ -8745,9 +8745,9 @@ Object {
8745 8745
   "strokeStyle": "solid",
8746 8746
   "strokeWidth": 1,
8747 8747
   "type": "rectangle",
8748
-  "version": 2,
8749
-  "versionNonce": 1278240551,
8750
-  "width": 1000,
8748
+  "version": 1,
8749
+  "versionNonce": 0,
8750
+  "width": 0,
8751 8751
   "x": 0,
8752 8752
   "y": 0,
8753 8753
 }
@@ -8765,14 +8765,14 @@ Object {
8765 8765
   "isDeleted": false,
8766 8766
   "opacity": 100,
8767 8767
   "roughness": 1,
8768
-  "seed": 449462985,
8768
+  "seed": 1278240551,
8769 8769
   "strokeColor": "#000000",
8770 8770
   "strokeSharpness": "sharp",
8771 8771
   "strokeStyle": "solid",
8772 8772
   "strokeWidth": 1,
8773 8773
   "type": "ellipse",
8774 8774
   "version": 2,
8775
-  "versionNonce": 453191,
8775
+  "versionNonce": 449462985,
8776 8776
   "width": 1000,
8777 8777
   "x": 500,
8778 8778
   "y": 500,
@@ -8799,9 +8799,7 @@ Object {
8799 8799
         "editingGroupId": null,
8800 8800
         "editingLinearElement": null,
8801 8801
         "name": "Untitled-201933152653",
8802
-        "selectedElementIds": Object {
8803
-          "id0": true,
8804
-        },
8802
+        "selectedElementIds": Object {},
8805 8803
         "viewBackgroundColor": "#ffffff",
8806 8804
       },
8807 8805
       "elements": Array [
@@ -8811,7 +8809,7 @@ Object {
8811 8809
           "boundElementIds": null,
8812 8810
           "fillStyle": "hachure",
8813 8811
           "groupIds": Array [],
8814
-          "height": 1000,
8812
+          "height": 0,
8815 8813
           "id": "id0",
8816 8814
           "isDeleted": false,
8817 8815
           "opacity": 100,
@@ -8822,9 +8820,9 @@ Object {
8822 8820
           "strokeStyle": "solid",
8823 8821
           "strokeWidth": 1,
8824 8822
           "type": "rectangle",
8825
-          "version": 2,
8826
-          "versionNonce": 1278240551,
8827
-          "width": 1000,
8823
+          "version": 1,
8824
+          "versionNonce": 0,
8825
+          "width": 0,
8828 8826
           "x": 0,
8829 8827
           "y": 0,
8830 8828
         },
@@ -8847,7 +8845,7 @@ Object {
8847 8845
           "boundElementIds": null,
8848 8846
           "fillStyle": "hachure",
8849 8847
           "groupIds": Array [],
8850
-          "height": 1000,
8848
+          "height": 0,
8851 8849
           "id": "id0",
8852 8850
           "isDeleted": false,
8853 8851
           "opacity": 100,
@@ -8858,9 +8856,9 @@ Object {
8858 8856
           "strokeStyle": "solid",
8859 8857
           "strokeWidth": 1,
8860 8858
           "type": "rectangle",
8861
-          "version": 2,
8862
-          "versionNonce": 1278240551,
8863
-          "width": 1000,
8859
+          "version": 1,
8860
+          "versionNonce": 0,
8861
+          "width": 0,
8864 8862
           "x": 0,
8865 8863
           "y": 0,
8866 8864
         },
@@ -8875,14 +8873,14 @@ Object {
8875 8873
           "isDeleted": false,
8876 8874
           "opacity": 100,
8877 8875
           "roughness": 1,
8878
-          "seed": 449462985,
8876
+          "seed": 1278240551,
8879 8877
           "strokeColor": "#000000",
8880 8878
           "strokeSharpness": "sharp",
8881 8879
           "strokeStyle": "solid",
8882 8880
           "strokeWidth": 1,
8883 8881
           "type": "ellipse",
8884 8882
           "version": 2,
8885
-          "versionNonce": 453191,
8883
+          "versionNonce": 449462985,
8886 8884
           "width": 1000,
8887 8885
           "x": 500,
8888 8886
           "y": 500,
@@ -8901,7 +8899,7 @@ exports[`regression tests given selected element A with lower z-index than unsel
8901 8899
 Object {
8902 8900
   "collaborators": Map {},
8903 8901
   "currentChartType": "bar",
8904
-  "currentItemBackgroundColor": "#fa5252",
8902
+  "currentItemBackgroundColor": "transparent",
8905 8903
   "currentItemEndArrowhead": "arrow",
8906 8904
   "currentItemFillStyle": "hachure",
8907 8905
   "currentItemFontFamily": 1,
@@ -8982,7 +8980,7 @@ Object {
8982 8980
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] element 0 1`] = `
8983 8981
 Object {
8984 8982
   "angle": 0,
8985
-  "backgroundColor": "#fa5252",
8983
+  "backgroundColor": "red",
8986 8984
   "boundElementIds": null,
8987 8985
   "fillStyle": "hachure",
8988 8986
   "groupIds": Array [],
@@ -8997,8 +8995,8 @@ Object {
8997 8995
   "strokeStyle": "solid",
8998 8996
   "strokeWidth": 1,
8999 8997
   "type": "rectangle",
9000
-  "version": 2,
9001
-  "versionNonce": 1278240551,
8998
+  "version": 1,
8999
+  "versionNonce": 0,
9002 9000
   "width": 1000,
9003 9001
   "x": 0,
9004 9002
   "y": 0,
@@ -9008,24 +9006,24 @@ Object {
9008 9006
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] element 1 1`] = `
9009 9007
 Object {
9010 9008
   "angle": 0,
9011
-  "backgroundColor": "#fa5252",
9009
+  "backgroundColor": "red",
9012 9010
   "boundElementIds": null,
9013 9011
   "fillStyle": "hachure",
9014 9012
   "groupIds": Array [],
9015
-  "height": 1000,
9013
+  "height": 500,
9016 9014
   "id": "id1",
9017 9015
   "isDeleted": false,
9018 9016
   "opacity": 100,
9019 9017
   "roughness": 1,
9020
-  "seed": 449462985,
9018
+  "seed": 1278240551,
9021 9019
   "strokeColor": "#000000",
9022 9020
   "strokeSharpness": "sharp",
9023 9021
   "strokeStyle": "solid",
9024 9022
   "strokeWidth": 1,
9025
-  "type": "ellipse",
9026
-  "version": 2,
9027
-  "versionNonce": 453191,
9028
-  "width": 1000,
9023
+  "type": "rectangle",
9024
+  "version": 1,
9025
+  "versionNonce": 0,
9026
+  "width": 500,
9029 9027
   "x": 500,
9030 9028
   "y": 500,
9031 9029
 }
@@ -9053,49 +9051,14 @@ Object {
9053 9051
         "name": "Untitled-201933152653",
9054 9052
         "selectedElementIds": Object {
9055 9053
           "id0": true,
9054
+          "id2": true,
9056 9055
         },
9057 9056
         "viewBackgroundColor": "#ffffff",
9058 9057
       },
9059 9058
       "elements": Array [
9060 9059
         Object {
9061 9060
           "angle": 0,
9062
-          "backgroundColor": "#fa5252",
9063
-          "boundElementIds": null,
9064
-          "fillStyle": "hachure",
9065
-          "groupIds": Array [],
9066
-          "height": 1000,
9067
-          "id": "id0",
9068
-          "isDeleted": false,
9069
-          "opacity": 100,
9070
-          "roughness": 1,
9071
-          "seed": 337897,
9072
-          "strokeColor": "#000000",
9073
-          "strokeSharpness": "sharp",
9074
-          "strokeStyle": "solid",
9075
-          "strokeWidth": 1,
9076
-          "type": "rectangle",
9077
-          "version": 2,
9078
-          "versionNonce": 1278240551,
9079
-          "width": 1000,
9080
-          "x": 0,
9081
-          "y": 0,
9082
-        },
9083
-      ],
9084
-    },
9085
-    Object {
9086
-      "appState": Object {
9087
-        "editingGroupId": null,
9088
-        "editingLinearElement": null,
9089
-        "name": "Untitled-201933152653",
9090
-        "selectedElementIds": Object {
9091
-          "id1": true,
9092
-        },
9093
-        "viewBackgroundColor": "#ffffff",
9094
-      },
9095
-      "elements": Array [
9096
-        Object {
9097
-          "angle": 0,
9098
-          "backgroundColor": "#fa5252",
9061
+          "backgroundColor": "red",
9099 9062
           "boundElementIds": null,
9100 9063
           "fillStyle": "hachure",
9101 9064
           "groupIds": Array [],
@@ -9110,32 +9073,32 @@ Object {
9110 9073
           "strokeStyle": "solid",
9111 9074
           "strokeWidth": 1,
9112 9075
           "type": "rectangle",
9113
-          "version": 2,
9114
-          "versionNonce": 1278240551,
9076
+          "version": 1,
9077
+          "versionNonce": 0,
9115 9078
           "width": 1000,
9116 9079
           "x": 0,
9117 9080
           "y": 0,
9118 9081
         },
9119 9082
         Object {
9120 9083
           "angle": 0,
9121
-          "backgroundColor": "#fa5252",
9084
+          "backgroundColor": "red",
9122 9085
           "boundElementIds": null,
9123 9086
           "fillStyle": "hachure",
9124 9087
           "groupIds": Array [],
9125
-          "height": 1000,
9088
+          "height": 500,
9126 9089
           "id": "id1",
9127 9090
           "isDeleted": false,
9128 9091
           "opacity": 100,
9129 9092
           "roughness": 1,
9130
-          "seed": 449462985,
9093
+          "seed": 1278240551,
9131 9094
           "strokeColor": "#000000",
9132 9095
           "strokeSharpness": "sharp",
9133 9096
           "strokeStyle": "solid",
9134 9097
           "strokeWidth": 1,
9135
-          "type": "ellipse",
9136
-          "version": 2,
9137
-          "versionNonce": 453191,
9138
-          "width": 1000,
9098
+          "type": "rectangle",
9099
+          "version": 1,
9100
+          "versionNonce": 0,
9101
+          "width": 500,
9139 9102
           "x": 500,
9140 9103
           "y": 500,
9141 9104
         },
@@ -9147,13 +9110,13 @@ Object {
9147 9110
 
9148 9111
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of elements 1`] = `2`;
9149 9112
 
9150
-exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of renders 1`] = `18`;
9113
+exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of renders 1`] = `11`;
9151 9114
 
9152 9115
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] appState 1`] = `
9153 9116
 Object {
9154 9117
   "collaborators": Map {},
9155 9118
   "currentChartType": "bar",
9156
-  "currentItemBackgroundColor": "#fa5252",
9119
+  "currentItemBackgroundColor": "transparent",
9157 9120
   "currentItemEndArrowhead": "arrow",
9158 9121
   "currentItemFillStyle": "hachure",
9159 9122
   "currentItemFontFamily": 1,
@@ -9235,7 +9198,7 @@ Object {
9235 9198
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] element 0 1`] = `
9236 9199
 Object {
9237 9200
   "angle": 0,
9238
-  "backgroundColor": "#fa5252",
9201
+  "backgroundColor": "red",
9239 9202
   "boundElementIds": null,
9240 9203
   "fillStyle": "hachure",
9241 9204
   "groupIds": Array [],
@@ -9250,8 +9213,8 @@ Object {
9250 9213
   "strokeStyle": "solid",
9251 9214
   "strokeWidth": 1,
9252 9215
   "type": "rectangle",
9253
-  "version": 3,
9254
-  "versionNonce": 1150084233,
9216
+  "version": 2,
9217
+  "versionNonce": 401146281,
9255 9218
   "width": 1000,
9256 9219
   "x": 100,
9257 9220
   "y": 100,
@@ -9261,24 +9224,24 @@ Object {
9261 9224
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] element 1 1`] = `
9262 9225
 Object {
9263 9226
   "angle": 0,
9264
-  "backgroundColor": "#fa5252",
9227
+  "backgroundColor": "red",
9265 9228
   "boundElementIds": null,
9266 9229
   "fillStyle": "hachure",
9267 9230
   "groupIds": Array [],
9268
-  "height": 1000,
9231
+  "height": 500,
9269 9232
   "id": "id1",
9270 9233
   "isDeleted": false,
9271 9234
   "opacity": 100,
9272 9235
   "roughness": 1,
9273
-  "seed": 449462985,
9236
+  "seed": 1278240551,
9274 9237
   "strokeColor": "#000000",
9275 9238
   "strokeSharpness": "sharp",
9276 9239
   "strokeStyle": "solid",
9277 9240
   "strokeWidth": 1,
9278
-  "type": "ellipse",
9279
-  "version": 2,
9280
-  "versionNonce": 453191,
9281
-  "width": 1000,
9241
+  "type": "rectangle",
9242
+  "version": 1,
9243
+  "versionNonce": 0,
9244
+  "width": 500,
9282 9245
   "x": 500,
9283 9246
   "y": 500,
9284 9247
 }
@@ -9306,49 +9269,14 @@ Object {
9306 9269
         "name": "Untitled-201933152653",
9307 9270
         "selectedElementIds": Object {
9308 9271
           "id0": true,
9272
+          "id2": true,
9309 9273
         },
9310 9274
         "viewBackgroundColor": "#ffffff",
9311 9275
       },
9312 9276
       "elements": Array [
9313 9277
         Object {
9314 9278
           "angle": 0,
9315
-          "backgroundColor": "#fa5252",
9316
-          "boundElementIds": null,
9317
-          "fillStyle": "hachure",
9318
-          "groupIds": Array [],
9319
-          "height": 1000,
9320
-          "id": "id0",
9321
-          "isDeleted": false,
9322
-          "opacity": 100,
9323
-          "roughness": 1,
9324
-          "seed": 337897,
9325
-          "strokeColor": "#000000",
9326
-          "strokeSharpness": "sharp",
9327
-          "strokeStyle": "solid",
9328
-          "strokeWidth": 1,
9329
-          "type": "rectangle",
9330
-          "version": 2,
9331
-          "versionNonce": 1278240551,
9332
-          "width": 1000,
9333
-          "x": 0,
9334
-          "y": 0,
9335
-        },
9336
-      ],
9337
-    },
9338
-    Object {
9339
-      "appState": Object {
9340
-        "editingGroupId": null,
9341
-        "editingLinearElement": null,
9342
-        "name": "Untitled-201933152653",
9343
-        "selectedElementIds": Object {
9344
-          "id1": true,
9345
-        },
9346
-        "viewBackgroundColor": "#ffffff",
9347
-      },
9348
-      "elements": Array [
9349
-        Object {
9350
-          "angle": 0,
9351
-          "backgroundColor": "#fa5252",
9279
+          "backgroundColor": "red",
9352 9280
           "boundElementIds": null,
9353 9281
           "fillStyle": "hachure",
9354 9282
           "groupIds": Array [],
@@ -9363,32 +9291,32 @@ Object {
9363 9291
           "strokeStyle": "solid",
9364 9292
           "strokeWidth": 1,
9365 9293
           "type": "rectangle",
9366
-          "version": 2,
9367
-          "versionNonce": 1278240551,
9294
+          "version": 1,
9295
+          "versionNonce": 0,
9368 9296
           "width": 1000,
9369 9297
           "x": 0,
9370 9298
           "y": 0,
9371 9299
         },
9372 9300
         Object {
9373 9301
           "angle": 0,
9374
-          "backgroundColor": "#fa5252",
9302
+          "backgroundColor": "red",
9375 9303
           "boundElementIds": null,
9376 9304
           "fillStyle": "hachure",
9377 9305
           "groupIds": Array [],
9378
-          "height": 1000,
9306
+          "height": 500,
9379 9307
           "id": "id1",
9380 9308
           "isDeleted": false,
9381 9309
           "opacity": 100,
9382 9310
           "roughness": 1,
9383
-          "seed": 449462985,
9311
+          "seed": 1278240551,
9384 9312
           "strokeColor": "#000000",
9385 9313
           "strokeSharpness": "sharp",
9386 9314
           "strokeStyle": "solid",
9387 9315
           "strokeWidth": 1,
9388
-          "type": "ellipse",
9389
-          "version": 2,
9390
-          "versionNonce": 453191,
9391
-          "width": 1000,
9316
+          "type": "rectangle",
9317
+          "version": 1,
9318
+          "versionNonce": 0,
9319
+          "width": 500,
9392 9320
           "x": 500,
9393 9321
           "y": 500,
9394 9322
         },
@@ -9409,7 +9337,7 @@ Object {
9409 9337
       "elements": Array [
9410 9338
         Object {
9411 9339
           "angle": 0,
9412
-          "backgroundColor": "#fa5252",
9340
+          "backgroundColor": "red",
9413 9341
           "boundElementIds": null,
9414 9342
           "fillStyle": "hachure",
9415 9343
           "groupIds": Array [],
@@ -9424,32 +9352,32 @@ Object {
9424 9352
           "strokeStyle": "solid",
9425 9353
           "strokeWidth": 1,
9426 9354
           "type": "rectangle",
9427
-          "version": 3,
9428
-          "versionNonce": 1150084233,
9355
+          "version": 2,
9356
+          "versionNonce": 401146281,
9429 9357
           "width": 1000,
9430 9358
           "x": 100,
9431 9359
           "y": 100,
9432 9360
         },
9433 9361
         Object {
9434 9362
           "angle": 0,
9435
-          "backgroundColor": "#fa5252",
9363
+          "backgroundColor": "red",
9436 9364
           "boundElementIds": null,
9437 9365
           "fillStyle": "hachure",
9438 9366
           "groupIds": Array [],
9439
-          "height": 1000,
9367
+          "height": 500,
9440 9368
           "id": "id1",
9441 9369
           "isDeleted": false,
9442 9370
           "opacity": 100,
9443 9371
           "roughness": 1,
9444
-          "seed": 449462985,
9372
+          "seed": 1278240551,
9445 9373
           "strokeColor": "#000000",
9446 9374
           "strokeSharpness": "sharp",
9447 9375
           "strokeStyle": "solid",
9448 9376
           "strokeWidth": 1,
9449
-          "type": "ellipse",
9450
-          "version": 2,
9451
-          "versionNonce": 453191,
9452
-          "width": 1000,
9377
+          "type": "rectangle",
9378
+          "version": 1,
9379
+          "versionNonce": 0,
9380
+          "width": 500,
9453 9381
           "x": 500,
9454 9382
           "y": 500,
9455 9383
         },
@@ -9461,7 +9389,7 @@ Object {
9461 9389
 
9462 9390
 exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of elements 1`] = `2`;
9463 9391
 
9464
-exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `19`;
9392
+exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `12`;
9465 9393
 
9466 9394
 exports[`regression tests key 2 selects rectangle tool: [end of test] appState 1`] = `
9467 9395
 Object {
@@ -13323,16 +13251,13 @@ Object {
13323 13251
     "data": null,
13324 13252
     "shown": false,
13325 13253
   },
13326
-  "previousSelectedElementIds": Object {
13327
-    "id0": true,
13328
-  },
13254
+  "previousSelectedElementIds": Object {},
13329 13255
   "resizingElement": null,
13330 13256
   "scrollX": 0,
13331 13257
   "scrollY": 0,
13332 13258
   "scrolledOutside": false,
13333 13259
   "selectedElementIds": Object {
13334 13260
     "id0": true,
13335
-    "id1": true,
13336 13261
   },
13337 13262
   "selectedGroupIds": Object {},
13338 13263
   "selectionElement": null,
@@ -13476,7 +13401,7 @@ Object {
13476 13401
 
13477 13402
 exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of elements 1`] = `1`;
13478 13403
 
13479
-exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of renders 1`] = `12`;
13404
+exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of renders 1`] = `13`;
13480 13405
 
13481 13406
 exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] appState 1`] = `
13482 13407
 Object {

+ 44
- 40
src/tests/regressionTests.test.tsx 查看文件

@@ -702,33 +702,36 @@ describe("regression tests", () => {
702 702
       "when clicking intersection between A and B " +
703 703
       "B should be selected on pointer up",
704 704
     () => {
705
-      UI.clickTool("rectangle");
706
-      // change background color since default is transparent
705
+      // set background color since default is transparent
707 706
       // and transparent elements can't be selected by clicking inside of them
708
-      clickLabeledElement("Background");
709
-      clickLabeledElement("#fa5252");
710
-      mouse.down();
711
-      mouse.up(1000, 1000);
712
-
713
-      // draw ellipse partially over rectangle.
714
-      // since ellipse was created after rectangle it has an higher z-index.
715
-      // we don't need to change background color again since change above
716
-      // affects next drawn elements.
717
-      UI.clickTool("ellipse");
718
-      mouse.reset();
719
-      mouse.down(500, 500);
720
-      mouse.up(1000, 1000);
707
+      const rect1 = API.createElement({
708
+        type: "rectangle",
709
+        backgroundColor: "red",
710
+        x: 0,
711
+        y: 0,
712
+        width: 1000,
713
+        height: 1000,
714
+      });
715
+      const rect2 = API.createElement({
716
+        type: "rectangle",
717
+        backgroundColor: "red",
718
+        x: 500,
719
+        y: 500,
720
+        width: 500,
721
+        height: 500,
722
+      });
723
+      h.elements = [rect1, rect2];
721 724
 
722
-      // select rectangle
723
-      mouse.reset();
724
-      mouse.click();
725
+      mouse.select(rect1);
725 726
 
726
-      // pointer down on intersection between ellipse and rectangle
727
+      // pointerdown on rect2 covering rect1 while rect1 is selected should
728
+      // retain rect1 selection
727 729
       mouse.down(900, 900);
728
-      expect(API.getSelectedElement().type).toBe("rectangle");
730
+      expect(API.getSelectedElement().id).toBe(rect1.id);
729 731
 
732
+      // pointerup should select rect2
730 733
       mouse.up();
731
-      expect(API.getSelectedElement().type).toBe("ellipse");
734
+      expect(API.getSelectedElement().id).toBe(rect2.id);
732 735
     },
733 736
   );
734 737
 
@@ -737,26 +740,27 @@ describe("regression tests", () => {
737 740
       "when dragging on intersection between A and B " +
738 741
       "A should be dragged and keep being selected",
739 742
     () => {
740
-      UI.clickTool("rectangle");
741
-      // change background color since default is transparent
742
-      // and transparent elements can't be selected by clicking inside of them
743
-      clickLabeledElement("Background");
744
-      clickLabeledElement("#fa5252");
745
-      mouse.down();
746
-      mouse.up(1000, 1000);
743
+      const rect1 = API.createElement({
744
+        type: "rectangle",
745
+        backgroundColor: "red",
746
+        x: 0,
747
+        y: 0,
748
+        width: 1000,
749
+        height: 1000,
750
+      });
751
+      const rect2 = API.createElement({
752
+        type: "rectangle",
753
+        backgroundColor: "red",
754
+        x: 500,
755
+        y: 500,
756
+        width: 500,
757
+        height: 500,
758
+      });
759
+      h.elements = [rect1, rect2];
747 760
 
748
-      // draw ellipse partially over rectangle.
749
-      // since ellipse was created after rectangle it has an higher z-index.
750
-      // we don't need to change background color again since change above
751
-      // affects next drawn elements.
752
-      UI.clickTool("ellipse");
753
-      mouse.reset();
754
-      mouse.down(500, 500);
755
-      mouse.up(1000, 1000);
761
+      mouse.select(rect1);
756 762
 
757
-      // select rectangle
758
-      mouse.reset();
759
-      mouse.click();
763
+      expect(API.getSelectedElement().id).toBe(rect1.id);
760 764
 
761 765
       const { x: prevX, y: prevY } = API.getSelectedElement();
762 766
 
@@ -764,7 +768,7 @@ describe("regression tests", () => {
764 768
       mouse.down(900, 900);
765 769
       mouse.up(100, 100);
766 770
 
767
-      expect(API.getSelectedElement().type).toBe("rectangle");
771
+      expect(API.getSelectedElement().id).toBe(rect1.id);
768 772
       expect(API.getSelectedElement().x).toEqual(prevX + 100);
769 773
       expect(API.getSelectedElement().y).toEqual(prevY + 100);
770 774
     },

+ 7
- 1
src/types.ts 查看文件

@@ -81,7 +81,13 @@ export type AppState = {
81 81
   isResizing: boolean;
82 82
   isRotating: boolean;
83 83
   zoom: Zoom;
84
-  openMenu: "canvas" | "shape" | null;
84
+  openMenu:
85
+    | "canvas"
86
+    | "shape"
87
+    | "canvasColorPicker"
88
+    | "backgroundColorPicker"
89
+    | "strokeColorPicker"
90
+    | null;
85 91
   lastPointerDownWith: PointerType;
86 92
   selectedElementIds: { [id: string]: boolean };
87 93
   previousSelectedElementIds: { [id: string]: boolean };

Loading…
取消
儲存