@bpmn-io/properties-panel 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ All notable changes to [`@bpmn-io/properties-panel`](https://github.com/bpmn-io/
6
6
 
7
7
  ___Note:__ Yet to be released changes appear here._
8
8
 
9
+ ## 0.4.1
10
+
11
+ * `CHORE`: unbuild custom checkbox component ([#97](https://github.com/bpmn-io/properties-panel/pull/97))
12
+
13
+ ## 0.4.0
14
+
15
+ * `FEAT`: add list entry ([#92](https://github.com/bpmn-io/properties-panel/issues/92))
16
+ * `FEAT`: add simple text entry ([#94](https://github.com/bpmn-io/properties-panel/pull/94))
17
+ * `FIX`: use default cursor when hovering collapsible headers ([#95](https://github.com/bpmn-io/properties-panel/pull/95))
18
+
19
+ ## 0.3.0
20
+
21
+ * `FEAT`: use semantic HTML for buttons ([#45](https://github.com/bpmn-io/properties-panel/issues/45))
22
+ * `FIX`: prevent list ordering effects on element changes ([#89](https://github.com/bpmn-io/properties-panel/pull/89))
23
+
24
+ ## 0.2.1
25
+
26
+ * `FIX`: do not use monospace as default font for text areas ([#87](https://github.com/bpmn-io/properties-panel/pull/87))
27
+
9
28
  ## 0.2.0
10
29
 
11
30
  * `FEAT`: add number field entry ([#76](https://github.com/bpmn-io/properties-panel/pull/76))
@@ -10,6 +10,8 @@
10
10
  --color-red-360-100-97: #fff0f0;
11
11
  --color-red-360-100-45: #e60000;
12
12
 
13
+ --color-grey-225-10-35: hsl(225, 10%, 35%);
14
+
13
15
  --color-cccccc: #cccccc;
14
16
  --color-aaaaaa: #aaaaaa;
15
17
  --color-white: white;
@@ -173,14 +175,6 @@
173
175
  margin-right: 0;
174
176
  }
175
177
 
176
- .bio-properties-panel-group-header-buttons .bio-properties-panel-add-entry {
177
- width: 20px;
178
- height: 20px;
179
- display: inherit;
180
- justify-content: center;
181
- align-items: center;
182
- }
183
-
184
178
  .bio-properties-panel-group-header:not(.empty) .bio-properties-panel-add-entry {
185
179
  visibility: hidden;
186
180
  }
@@ -225,11 +219,6 @@
225
219
  margin-bottom: 6px;
226
220
  }
227
221
 
228
- .bio-properties-panel-list-item .bio-properties-panel-collapsible-entry-entries {
229
- padding-left: 15px;
230
- margin-bottom: 10px;
231
- }
232
-
233
222
  .bio-properties-panel-list-badge {
234
223
  color: var(--color-white);
235
224
  background-color: var(--color-black);
@@ -278,6 +267,9 @@
278
267
  border-radius: 3px;
279
268
  background-color: var(--color-input-background);
280
269
  font-size: 14px;
270
+ font-style: normal;
271
+ font-weight: 400;
272
+ font-family: inherit;
281
273
  }
282
274
 
283
275
  .bio-properties-panel-input[type=number],
@@ -294,65 +286,19 @@ textarea.bio-properties-panel-input,
294
286
  border: 1px solid var(--color-input-border-focus);
295
287
  }
296
288
 
297
- .bio-properties-panel-input[type="checkbox"], .bio-properties-panel-input[type="radio"] {
298
- margin: 0;
299
- }
300
-
301
- .bio-properties-panel-input[type="checkbox"] + label::after,
302
- .bio-properties-panel-input[type="radio"] + label::after {
303
- -webkit-transform: scale(0);
304
- -ms-transform: scale(0);
305
- -o-transform: scale(0);
306
- transform: scale(0);
289
+ .bio-properties-panel-input-monospace {
290
+ font-family: monospace;
307
291
  }
308
292
 
309
- .bio-properties-panel-input[type="checkbox"]:checked + label::after,
310
- .bio-properties-panel-input[type="radio"]:checked + label::after {
311
- -webkit-transform: scale(1);
312
- -ms-transform: scale(1);
313
- -o-transform: scale(1);
314
- transform: scale(1);
315
- }
316
-
317
- .bio-properties-panel-checkbox {
318
- margin-top: 12px;
319
- margin-bottom: 6px;
320
- }
321
-
322
- .bio-properties-panel-checkbox .bio-properties-panel-input {
323
- position: absolute;
324
- opacity: 0;
325
- z-index: -1;
293
+ .bio-properties-panel-input[type="checkbox"], .bio-properties-panel-input[type="radio"] {
294
+ margin: 0;
295
+ vertical-align: middle;
326
296
  }
327
297
 
328
- .bio-properties-panel-checkbox .bio-properties-panel-label {
329
- position: relative;
298
+ .bio-properties-panel-checkbox > .bio-properties-panel-label {
330
299
  display: inline-block;
331
- padding: 0 0 0 24px;
332
- }
333
-
334
- .bio-properties-panel-checkbox .bio-properties-panel-label::before,
335
- .bio-properties-panel-checkbox .bio-properties-panel-label::after {
336
- position: absolute;
337
- top: 0;
338
- left: 0;
339
- display: block;
340
- width: 16px;
341
- height: 16px;
342
- }
343
-
344
- .bio-properties-panel-checkbox .bio-properties-panel-label::before {
345
- content: " ";
346
- border: 1px solid var(--color-input-border);
347
- border-radius: 3px;
348
- background-color:var(--color-000000-opacity-5);
349
- }
350
-
351
- .bio-properties-panel-checkbox .bio-properties-panel-label::after {
352
- content: "\2714";
353
- color: var(--color-text-base);
354
- line-height: 1.5;
355
- text-align: center;
300
+ margin-left: 4px;
301
+ vertical-align: middle;
356
302
  }
357
303
 
358
304
  textarea.bio-properties-panel-input {
@@ -374,6 +320,15 @@ textarea.bio-properties-panel-input {
374
320
  font-size: 13px;
375
321
  }
376
322
 
323
+ .bio-properties-panel-simple {
324
+ width: 100%;
325
+ margin-right: 12px;
326
+ }
327
+
328
+ .bio-properties-panel-simple + .bio-properties-panel-remove-entry {
329
+ margin: auto;
330
+ }
331
+
377
332
  /**
378
333
  * Toggle Switch
379
334
  */
@@ -454,6 +409,8 @@ textarea.bio-properties-panel-input {
454
409
  }
455
410
 
456
411
  .bio-properties-panel-collapsible-entry-entries {
412
+ padding-left: 15px;
413
+ margin-bottom: 10px;
457
414
  position: relative;
458
415
  display: none;
459
416
  }
@@ -472,6 +429,7 @@ textarea.bio-properties-panel-input {
472
429
  white-space: nowrap;
473
430
  overflow: hidden;
474
431
  text-overflow: ellipsis;
432
+ user-select: none;
475
433
  }
476
434
 
477
435
  .bio-properties-panel-collapsible-entry-arrow {
@@ -484,14 +442,21 @@ textarea.bio-properties-panel-input {
484
442
  }
485
443
 
486
444
  .bio-properties-panel-remove-entry {
487
- position: absolute;
488
- transform: translateY(calc(-50% - 1px));
489
- right: 11px;
445
+ display: flex;
446
+ align-items: center;
447
+ justify-content: center;
490
448
  width: 16px;
491
449
  height: 16px;
450
+
451
+ margin-right: 14px;
452
+ padding: 0;
453
+
454
+ border: none;
455
+ background: none;
492
456
  visibility: hidden;
493
457
  }
494
458
 
459
+ .bio-properties-panel-list-entry-item:hover .bio-properties-panel-remove-entry,
495
460
  .bio-properties-panel-collapsible-entry-header:hover .bio-properties-panel-remove-entry {
496
461
  visibility: visible;
497
462
  }
@@ -502,6 +467,102 @@ textarea.bio-properties-panel-input {
502
467
  left: 10px;
503
468
  width: 2px;
504
469
  top: 0;
470
+ bottom: 12px;
471
+ background-color: var(--color-grey-225-10-35);
472
+ }
473
+
474
+ /* Side line extension for non-list entry or open list entry positioned as the last one. */
475
+ .bio-properties-panel-collapsible-entry-entries .bio-properties-panel-entry:not(.bio-properties-panel-list-entry):last-child,
476
+ .bio-properties-panel-list-entry.open:last-child {
477
+ position: relative;
478
+ }
479
+
480
+ .bio-properties-panel-collapsible-entry-entries .bio-properties-panel-entry:not(.bio-properties-panel-list-entry):last-child::after,
481
+ .bio-properties-panel-list-entry.open:last-child::after {
482
+ content: "";
483
+ position: absolute;
484
+ left: -17px;
485
+ width: 2px;
486
+ top: 0;
505
487
  bottom: 0;
506
- background-color: var(--color-000000-opacity-25);
488
+ background-color: var(--color-grey-225-10-35);
489
+ }
490
+
491
+ /* List entry */
492
+
493
+ .bio-properties-panel-list-entry {
494
+ position: relative;
495
+ }
496
+
497
+ .bio-properties-panel-list-entry-header {
498
+ display: flex;
499
+ justify-content: space-between;
500
+
501
+ height: 32px;
502
+ }
503
+
504
+ .bio-properties-panel-list-entry::before {
505
+ content: "";
506
+ width: 8px;
507
+ height: 8px;
508
+ position: absolute;
509
+ left: -20px;
510
+ top: 13px;
511
+ border-radius: 50%;
512
+ background-color: var(--color-grey-225-10-35);
513
+ }
514
+
515
+ .bio-properties-panel-list-entry-header-title {
516
+ padding: 2px 0;
517
+ font-size: var(--text-size-base);
518
+ white-space: nowrap;
519
+ overflow: hidden;
520
+ text-overflow: ellipsis;
521
+ }
522
+
523
+ .bio-properties-panel-list-entry-header-title.open {
524
+ font-weight: 600;
525
+ }
526
+
527
+ .bio-properties-panel-list-entry-header-title,
528
+ .bio-properties-panel-list-entry-header-buttons {
529
+ display: flex;
530
+ align-items: center;
531
+ }
532
+
533
+ .bio-properties-panel-list-entry-header-buttons > button {
534
+ display: inline-flex;
535
+ justify-content: center;
536
+ align-items: center;
537
+ align-self: center;
538
+ width: 20px;
539
+ height: 20px;
540
+ margin-right: 4px;
541
+ padding: 0;
542
+ border: none;
543
+ background: none;
544
+ }
545
+
546
+ .bio-properties-panel-list-entry-header-buttons > :last-child {
547
+ margin-right: 0;
548
+ }
549
+
550
+ .bio-properties-panel-list-entry-items {
551
+ padding: 0;
552
+ margin: 0;
553
+
554
+ list-style: none;
555
+ }
556
+
557
+ .bio-properties-panel-list-entry-items:not(.open) {
558
+ display: none;
559
+ }
560
+
561
+ .bio-properties-panel-list-entry-item {
562
+ display: flex;
563
+ justify-content: space-between;
564
+ }
565
+
566
+ .bio-properties-panel-list-entry-item > .bio-properties-panel-remove-entry {
567
+ margin-right: 2px;
507
568
  }
@@ -27,6 +27,7 @@ const DEFAULT_LAYOUT = {
27
27
  * @typedef { {
28
28
  * add: import('preact').Component,
29
29
  * component: import('preact').Component,
30
+ * element: Object,
30
31
  * id: String,
31
32
  * items: Array<ListItemDefinition>,
32
33
  * label: String,
@@ -102,7 +103,9 @@ export default function PropertiesPanel(props) {
102
103
  component: GroupComponent = Group,
103
104
  id
104
105
  } = group;
105
- return _jsx(GroupComponent, { ...group
106
+ return _jsx(GroupComponent, {
107
+ element: element,
108
+ ...group
106
109
  }, id);
107
110
  })
108
111
  })]
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/PropertiesPanel.js"],"names":["useState","useEffect","classnames","Header","Group","LayoutContext","DEFAULT_LAYOUT","open","PropertiesPanel","props","element","headerProvider","groups","layoutConfig","layoutChanged","layout","setLayout","createLayoutContext","setLayoutForKey","key","config","layoutContext","map","group","component","GroupComponent","id","overrides"],"mappings":"AAAA,SACEA,QADF,EAEEC,SAFF,QAGO,cAHP;AAKA,OAAOC,UAAP,MAAuB,YAAvB;AAEA,OAAOC,MAAP,MAAmB,qBAAnB;AAEA,OAAOC,KAAP,MAAkB,oBAAlB;AAEA,SACEC,aADF,QAEO,WAFP;;;AAIA,MAAMC,cAAc,GAAG;AACrBC,EAAAA,IAAI,EAAE;AADe,CAAvB;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,eAAe,SAASC,eAAT,CAAyBC,KAAzB,EAAgC;AAC7C,QAAM;AACJC,IAAAA,OADI;AAEJC,IAAAA,cAFI;AAGJC,IAAAA,MAHI;AAIJC,IAAAA,YAAY,GAAG,EAJX;AAKJC,IAAAA;AALI,MAMFL,KANJ;AAQA,QAAM,CAAEM,MAAF,EAAUC,SAAV,IAAwBhB,QAAQ,CAACiB,mBAAmB,CAACJ,YAAD,CAApB,CAAtC;AAEAZ,EAAAA,SAAS,CAAC,MAAM;AACd,QAAI,OAAOa,aAAP,KAAyB,UAA7B,EAAyC;AACvCA,MAAAA,aAAa,CAACC,MAAD,CAAb;AACD;AACF,GAJQ,EAIN,CAAEA,MAAF,EAAUD,aAAV,CAJM,CAAT;;AAMA,QAAMI,eAAe,GAAG,CAACC,GAAD,EAAMC,MAAN,KAAiB;AACvCJ,IAAAA,SAAS,CAAC,EACR,GAAGD,MADK;AAER,OAACI,GAAD,GAAOC;AAFC,KAAD,CAAT;AAID,GALD;;AAOA,QAAMC,aAAa,GAAG;AACpBN,IAAAA,MADoB;AAEpBC,IAAAA,SAFoB;AAGpBE,IAAAA;AAHoB,GAAtB;;AAMA,MAAI,CAACR,OAAL,EAAc;AACZ,WAAO;AAAK,MAAA,KAAK,EAAC,kCAAX;AAAA;AAAA,MAAP;AACD;;AAED,SAAO,KAAC,aAAD,CAAe,QAAf;AAAwB,IAAA,KAAK,EAAGW,aAAhC;AAAA,cACL;AACE,MAAA,KAAK,EAAGnB,UAAU,CAChB,sBADgB,EAEhBa,MAAM,CAACR,IAAP,GAAc,MAAd,GAAuB,EAFP,CADpB;AAAA,iBAKE,KAAC,MAAD;AACE,QAAA,OAAO,EAAGG,OADZ;AAEE,QAAA,cAAc,EAAGC;AAFnB,QALF,EAQE;AAAK,QAAA,KAAK,EAAC,uCAAX;AAAA,kBAEIC,MAAM,CAACU,GAAP,CAAWC,KAAK,IAAI;AAElB,gBAAM;AACJC,YAAAA,SAAS,EAAEC,cAAc,GAAGrB,KADxB;AAEJsB,YAAAA;AAFI,cAGFH,KAHJ;AAKA,iBAAO,KAAC,cAAD,OAAgCA;AAAhC,aAAsBG,EAAtB,CAAP;AACD,SARD;AAFJ,QARF;AAAA;AADK,IAAP;AAwBD,C,CAGD;;AAEA,SAAST,mBAAT,CAA6BU,SAA7B,EAAwC;AACtC,SAAO,EACL,GAAGrB,cADE;AAEL,OAAGqB;AAFE,GAAP;AAID","sourcesContent":["import {\n useState,\n useEffect\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport Header from './components/Header';\n\nimport Group from './components/Group';\n\nimport {\n LayoutContext\n} from './context';\n\nconst DEFAULT_LAYOUT = {\n open: true\n};\n\n\n/**\n * @typedef { {\n * component: import('preact').ComponentChild,\n * id: String,\n * isEdited?: Function\n * } } EntryDefinition\n *\n * @typedef { {\n * autoFocusEntry: String,\n * autoOpen?: Boolean,\n * entries: Array<EntryDefinition>,\n * id: String,\n * label: String,\n * remove: import('preact').Component\n * } } ListItemDefinition\n *\n * @typedef { {\n * add: import('preact').Component,\n * component: import('preact').Component,\n * id: String,\n * items: Array<ListItemDefinition>,\n * label: String,\n * shouldSort?: Boolean\n * } } ListGroupDefinition\n *\n * @typedef { {\n * component?: import('preact').Component,\n * entries: Array<EntryDefinition>,\n * id: String,\n * label: String\n * } } GroupDefinition\n *\n */\n\n\n/**\n * A basic properties panel component. Describes *how* content will be rendered, accepts\n * data from implementor to describe *what* will be rendered.\n *\n * @param {Object} props\n * @param {Object} props.element\n * @param {import('./components/Header').HeaderProvider} props.headerProvider\n * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups\n * @param {Object} [props.layoutConfig]\n * @param {Function} [props.layoutChanged]\n */\nexport default function PropertiesPanel(props) {\n const {\n element,\n headerProvider,\n groups,\n layoutConfig = {},\n layoutChanged\n } = props;\n\n const [ layout, setLayout ] = useState(createLayoutContext(layoutConfig));\n\n useEffect(() => {\n if (typeof layoutChanged === 'function') {\n layoutChanged(layout);\n }\n }, [ layout, layoutChanged ]);\n\n const setLayoutForKey = (key, config) => {\n setLayout({\n ...layout,\n [key]: config\n });\n };\n\n const layoutContext = {\n layout,\n setLayout,\n setLayoutForKey\n };\n\n if (!element) {\n return <div class=\"bio-properties-panel-placeholder\">Select an element to edit its properties.</div>;\n }\n\n return <LayoutContext.Provider value={ layoutContext }>\n <div\n class={ classnames(\n 'bio-properties-panel',\n layout.open ? 'open' : '')\n }>\n <Header\n element={ element }\n headerProvider={ headerProvider } />\n <div class=\"bio-properties-panel-scroll-container\">\n {\n groups.map(group => {\n\n const {\n component: GroupComponent = Group,\n id\n } = group;\n\n return <GroupComponent key={ id } { ...group } />;\n })\n }\n </div>\n </div>\n </LayoutContext.Provider>;\n}\n\n\n// helpers //////////////////\n\nfunction createLayoutContext(overrides) {\n return {\n ...DEFAULT_LAYOUT,\n ...overrides\n };\n}\n"],"file":"PropertiesPanel.js"}
1
+ {"version":3,"sources":["../src/PropertiesPanel.js"],"names":["useState","useEffect","classnames","Header","Group","LayoutContext","DEFAULT_LAYOUT","open","PropertiesPanel","props","element","headerProvider","groups","layoutConfig","layoutChanged","layout","setLayout","createLayoutContext","setLayoutForKey","key","config","layoutContext","map","group","component","GroupComponent","id","overrides"],"mappings":"AAAA,SACEA,QADF,EAEEC,SAFF,QAGO,cAHP;AAKA,OAAOC,UAAP,MAAuB,YAAvB;AAEA,OAAOC,MAAP,MAAmB,qBAAnB;AAEA,OAAOC,KAAP,MAAkB,oBAAlB;AAEA,SACEC,aADF,QAEO,WAFP;;;AAIA,MAAMC,cAAc,GAAG;AACrBC,EAAAA,IAAI,EAAE;AADe,CAAvB;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,eAAe,SAASC,eAAT,CAAyBC,KAAzB,EAAgC;AAC7C,QAAM;AACJC,IAAAA,OADI;AAEJC,IAAAA,cAFI;AAGJC,IAAAA,MAHI;AAIJC,IAAAA,YAAY,GAAG,EAJX;AAKJC,IAAAA;AALI,MAMFL,KANJ;AAQA,QAAM,CAAEM,MAAF,EAAUC,SAAV,IAAwBhB,QAAQ,CAACiB,mBAAmB,CAACJ,YAAD,CAApB,CAAtC;AAEAZ,EAAAA,SAAS,CAAC,MAAM;AACd,QAAI,OAAOa,aAAP,KAAyB,UAA7B,EAAyC;AACvCA,MAAAA,aAAa,CAACC,MAAD,CAAb;AACD;AACF,GAJQ,EAIN,CAAEA,MAAF,EAAUD,aAAV,CAJM,CAAT;;AAMA,QAAMI,eAAe,GAAG,CAACC,GAAD,EAAMC,MAAN,KAAiB;AACvCJ,IAAAA,SAAS,CAAC,EACR,GAAGD,MADK;AAER,OAACI,GAAD,GAAOC;AAFC,KAAD,CAAT;AAID,GALD;;AAOA,QAAMC,aAAa,GAAG;AACpBN,IAAAA,MADoB;AAEpBC,IAAAA,SAFoB;AAGpBE,IAAAA;AAHoB,GAAtB;;AAMA,MAAI,CAACR,OAAL,EAAc;AACZ,WAAO;AAAK,MAAA,KAAK,EAAC,kCAAX;AAAA;AAAA,MAAP;AACD;;AAED,SAAO,KAAC,aAAD,CAAe,QAAf;AAAwB,IAAA,KAAK,EAAGW,aAAhC;AAAA,cACL;AACE,MAAA,KAAK,EAAGnB,UAAU,CAChB,sBADgB,EAEhBa,MAAM,CAACR,IAAP,GAAc,MAAd,GAAuB,EAFP,CADpB;AAAA,iBAKE,KAAC,MAAD;AACE,QAAA,OAAO,EAAGG,OADZ;AAEE,QAAA,cAAc,EAAGC;AAFnB,QALF,EAQE;AAAK,QAAA,KAAK,EAAC,uCAAX;AAAA,kBAEIC,MAAM,CAACU,GAAP,CAAWC,KAAK,IAAI;AAElB,gBAAM;AACJC,YAAAA,SAAS,EAAEC,cAAc,GAAGrB,KADxB;AAEJsB,YAAAA;AAFI,cAGFH,KAHJ;AAKA,iBAAO,KAAC,cAAD;AAEL,YAAA,OAAO,EAAGb,OAFL;AAAA,eAGAa;AAHA,aACCG,EADD,CAAP;AAID,SAXD;AAFJ,QARF;AAAA;AADK,IAAP;AA2BD,C,CAGD;;AAEA,SAAST,mBAAT,CAA6BU,SAA7B,EAAwC;AACtC,SAAO,EACL,GAAGrB,cADE;AAEL,OAAGqB;AAFE,GAAP;AAID","sourcesContent":["import {\n useState,\n useEffect\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport Header from './components/Header';\n\nimport Group from './components/Group';\n\nimport {\n LayoutContext\n} from './context';\n\nconst DEFAULT_LAYOUT = {\n open: true\n};\n\n\n/**\n * @typedef { {\n * component: import('preact').ComponentChild,\n * id: String,\n * isEdited?: Function\n * } } EntryDefinition\n *\n * @typedef { {\n * autoFocusEntry: String,\n * autoOpen?: Boolean,\n * entries: Array<EntryDefinition>,\n * id: String,\n * label: String,\n * remove: import('preact').Component\n * } } ListItemDefinition\n *\n * @typedef { {\n * add: import('preact').Component,\n * component: import('preact').Component,\n * element: Object,\n * id: String,\n * items: Array<ListItemDefinition>,\n * label: String,\n * shouldSort?: Boolean\n * } } ListGroupDefinition\n *\n * @typedef { {\n * component?: import('preact').Component,\n * entries: Array<EntryDefinition>,\n * id: String,\n * label: String\n * } } GroupDefinition\n *\n */\n\n\n/**\n * A basic properties panel component. Describes *how* content will be rendered, accepts\n * data from implementor to describe *what* will be rendered.\n *\n * @param {Object} props\n * @param {Object} props.element\n * @param {import('./components/Header').HeaderProvider} props.headerProvider\n * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups\n * @param {Object} [props.layoutConfig]\n * @param {Function} [props.layoutChanged]\n */\nexport default function PropertiesPanel(props) {\n const {\n element,\n headerProvider,\n groups,\n layoutConfig = {},\n layoutChanged\n } = props;\n\n const [ layout, setLayout ] = useState(createLayoutContext(layoutConfig));\n\n useEffect(() => {\n if (typeof layoutChanged === 'function') {\n layoutChanged(layout);\n }\n }, [ layout, layoutChanged ]);\n\n const setLayoutForKey = (key, config) => {\n setLayout({\n ...layout,\n [key]: config\n });\n };\n\n const layoutContext = {\n layout,\n setLayout,\n setLayoutForKey\n };\n\n if (!element) {\n return <div class=\"bio-properties-panel-placeholder\">Select an element to edit its properties.</div>;\n }\n\n return <LayoutContext.Provider value={ layoutContext }>\n <div\n class={ classnames(\n 'bio-properties-panel',\n layout.open ? 'open' : '')\n }>\n <Header\n element={ element }\n headerProvider={ headerProvider } />\n <div class=\"bio-properties-panel-scroll-container\">\n {\n groups.map(group => {\n\n const {\n component: GroupComponent = Group,\n id\n } = group;\n\n return <GroupComponent\n key={ id }\n element={ element }\n { ...group } />;\n })\n }\n </div>\n </div>\n </LayoutContext.Provider>;\n}\n\n\n// helpers //////////////////\n\nfunction createLayoutContext(overrides) {\n return {\n ...DEFAULT_LAYOUT,\n ...overrides\n };\n}\n"],"file":"PropertiesPanel.js"}
@@ -50,7 +50,7 @@ export default function Group(props) {
50
50
  children: label
51
51
  }), _jsxs("div", {
52
52
  class: "bio-properties-panel-group-header-buttons",
53
- children: [edited && _jsx(DataMarker, {}), _jsx("div", {
53
+ children: [edited && _jsx(DataMarker, {}), _jsx("button", {
54
54
  class: "bio-properties-panel-group-header-button",
55
55
  children: _jsx(GroupArrowIcon, {
56
56
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/Group.js"],"names":["useEffect","useState","classnames","query","domQuery","isFunction","GroupArrowIcon","Group","props","id","entries","label","open","setOpen","toggleOpen","edited","setEdited","hasOneEditedEntry","find","entry","isEdited","entryNode","inputNode","getTitleAttribute","map","e","component","DataMarker"],"mappings":"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","sourcesContent":["import {\n useEffect,\n useState\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n query as domQuery\n} from 'min-dom';\n\nimport {\n isFunction\n} from 'min-dash';\n\nimport { GroupArrowIcon } from './icons';\n\n/**\n * @param {import('../PropertiesPanel').GroupDefinition} props\n */\nexport default function Group(props) {\n const {\n id,\n entries = [],\n label\n } = props;\n\n const [ open, setOpen ] = useState(false);\n\n const toggleOpen = () => setOpen(!open);\n\n const [ edited, setEdited ] = useState(false);\n\n // set edited state depending on all entries\n useEffect(() => {\n const hasOneEditedEntry = entries.find(entry => {\n const {\n id,\n isEdited\n } = entry;\n\n const entryNode = domQuery(`[data-entry-id=\"${id}\"]`);\n\n if (!isFunction(isEdited) || !entryNode) {\n return false;\n }\n\n const inputNode = domQuery('.bio-properties-panel-input', entryNode);\n\n return isEdited(inputNode);\n });\n\n setEdited(hasOneEditedEntry);\n }, [ entries ]);\n\n return <div class=\"bio-properties-panel-group\" data-group-id={ 'group-' + id }>\n <div class={ classnames(\n 'bio-properties-panel-group-header',\n edited ? '' : 'empty'\n ) } onClick={ toggleOpen }>\n <div title={ getTitleAttribute(label, edited) } class=\"bio-properties-panel-group-header-title\">\n { label }\n </div>\n <div class=\"bio-properties-panel-group-header-buttons\">\n {\n edited && <DataMarker />\n }\n <div class=\"bio-properties-panel-group-header-button\">\n <GroupArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </div>\n </div>\n </div>\n <div class={ classnames(\n 'bio-properties-panel-group-entries',\n open ? 'open' : ''\n ) }>\n {\n entries.map(e => e.component)\n }\n </div>\n </div>;\n}\n\nfunction DataMarker() {\n return (\n <div class=\"bio-properties-panel-dot\">\n <svg\n aria-label=\"edited\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 100 100\"\n >\n <circle fill=\"currentColor\" cx=\"50\" cy=\"50\" r=\"50\" />\n </svg>\n </div>\n );\n}\n\n\n// helper //////////////\n\nfunction getTitleAttribute(label, edited) {\n return label + (edited ? ' (edited)' : '');\n}"],"file":"Group.js"}
1
+ {"version":3,"sources":["../../src/components/Group.js"],"names":["useEffect","useState","classnames","query","domQuery","isFunction","GroupArrowIcon","Group","props","id","entries","label","open","setOpen","toggleOpen","edited","setEdited","hasOneEditedEntry","find","entry","isEdited","entryNode","inputNode","getTitleAttribute","map","e","component","DataMarker"],"mappings":"AAAA,SACEA,SADF,EAEEC,QAFF,QAGO,cAHP;AAKA,OAAOC,UAAP,MAAuB,YAAvB;AAEA,SACEC,KAAK,IAAIC,QADX,QAEO,SAFP;AAIA,SACEC,UADF,QAEO,UAFP;AAIA,SAASC,cAAT,QAA+B,SAA/B;AAEA;AACA;AACA;;;;AACA,eAAe,SAASC,KAAT,CAAeC,KAAf,EAAsB;AACnC,QAAM;AACJC,IAAAA,EADI;AAEJC,IAAAA,OAAO,GAAG,EAFN;AAGJC,IAAAA;AAHI,MAIFH,KAJJ;AAMA,QAAM,CAAEI,IAAF,EAAQC,OAAR,IAAoBZ,QAAQ,CAAC,KAAD,CAAlC;;AAEA,QAAMa,UAAU,GAAG,MAAMD,OAAO,CAAC,CAACD,IAAF,CAAhC;;AAEA,QAAM,CAAEG,MAAF,EAAUC,SAAV,IAAwBf,QAAQ,CAAC,KAAD,CAAtC,CAXmC,CAanC;;AACAD,EAAAA,SAAS,CAAC,MAAM;AACd,UAAMiB,iBAAiB,GAAGP,OAAO,CAACQ,IAAR,CAAaC,KAAK,IAAI;AAC9C,YAAM;AACJV,QAAAA,EADI;AAEJW,QAAAA;AAFI,UAGFD,KAHJ;AAKA,YAAME,SAAS,GAAGjB,QAAQ,CAAE,mBAAkBK,EAAG,IAAvB,CAA1B;;AAEA,UAAI,CAACJ,UAAU,CAACe,QAAD,CAAX,IAAyB,CAACC,SAA9B,EAAyC;AACvC,eAAO,KAAP;AACD;;AAED,YAAMC,SAAS,GAAGlB,QAAQ,CAAC,6BAAD,EAAgCiB,SAAhC,CAA1B;AAEA,aAAOD,QAAQ,CAACE,SAAD,CAAf;AACD,KAfyB,CAA1B;AAiBAN,IAAAA,SAAS,CAACC,iBAAD,CAAT;AACD,GAnBQ,EAmBN,CAAEP,OAAF,CAnBM,CAAT;AAqBA,SAAO;AAAK,IAAA,KAAK,EAAC,4BAAX;AAAwC,qBAAgB,WAAWD,EAAnE;AAAA,eACL;AAAK,MAAA,KAAK,EAAGP,UAAU,CACrB,mCADqB,EAErBa,MAAM,GAAG,EAAH,GAAQ,OAFO,CAAvB;AAGI,MAAA,OAAO,EAAGD,UAHd;AAAA,iBAIE;AAAK,QAAA,KAAK,EAAGS,iBAAiB,CAACZ,KAAD,EAAQI,MAAR,CAA9B;AAAgD,QAAA,KAAK,EAAC,yCAAtD;AAAA,kBACIJ;AADJ,QAJF,EAOE;AAAK,QAAA,KAAK,EAAC,2CAAX;AAAA,mBAEII,MAAM,IAAI,KAAC,UAAD,KAFd,EAIE;AAAQ,UAAA,KAAK,EAAC,0CAAd;AAAA,oBACE,KAAC,cAAD;AAAgB,YAAA,KAAK,EAAGH,IAAI,GAAG,iCAAH,GAAuC;AAAnE;AADF,UAJF;AAAA,QAPF;AAAA,MADK,EAiBL;AAAK,MAAA,KAAK,EAAGV,UAAU,CACrB,oCADqB,EAErBU,IAAI,GAAG,MAAH,GAAY,EAFK,CAAvB;AAAA,gBAKIF,OAAO,CAACc,GAAR,CAAYC,CAAC,IAAIA,CAAC,CAACC,SAAnB;AALJ,MAjBK;AAAA,IAAP;AA0BD;;AAED,SAASC,UAAT,GAAsB;AACpB,SACE;AAAK,IAAA,KAAK,EAAC,0BAAX;AAAA,cACE;AACE,oBAAW,QADb;AACsB,MAAA,IAAI,EAAC,KAD3B;AACiC,MAAA,KAAK,EAAC,4BADvC;AAEE,MAAA,OAAO,EAAC,aAFV;AAAA,gBAIE;AAAQ,QAAA,IAAI,EAAC,cAAb;AAA4B,QAAA,EAAE,EAAC,IAA/B;AAAoC,QAAA,EAAE,EAAC,IAAvC;AAA4C,QAAA,CAAC,EAAC;AAA9C;AAJF;AADF,IADF;AAUD,C,CAGD;;;AAEA,SAASJ,iBAAT,CAA2BZ,KAA3B,EAAkCI,MAAlC,EAA0C;AACxC,SAAOJ,KAAK,IAAII,MAAM,GAAG,WAAH,GAAiB,EAA3B,CAAZ;AACD","sourcesContent":["import {\n useEffect,\n useState\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n query as domQuery\n} from 'min-dom';\n\nimport {\n isFunction\n} from 'min-dash';\n\nimport { GroupArrowIcon } from './icons';\n\n/**\n * @param {import('../PropertiesPanel').GroupDefinition} props\n */\nexport default function Group(props) {\n const {\n id,\n entries = [],\n label\n } = props;\n\n const [ open, setOpen ] = useState(false);\n\n const toggleOpen = () => setOpen(!open);\n\n const [ edited, setEdited ] = useState(false);\n\n // set edited state depending on all entries\n useEffect(() => {\n const hasOneEditedEntry = entries.find(entry => {\n const {\n id,\n isEdited\n } = entry;\n\n const entryNode = domQuery(`[data-entry-id=\"${id}\"]`);\n\n if (!isFunction(isEdited) || !entryNode) {\n return false;\n }\n\n const inputNode = domQuery('.bio-properties-panel-input', entryNode);\n\n return isEdited(inputNode);\n });\n\n setEdited(hasOneEditedEntry);\n }, [ entries ]);\n\n return <div class=\"bio-properties-panel-group\" data-group-id={ 'group-' + id }>\n <div class={ classnames(\n 'bio-properties-panel-group-header',\n edited ? '' : 'empty'\n ) } onClick={ toggleOpen }>\n <div title={ getTitleAttribute(label, edited) } class=\"bio-properties-panel-group-header-title\">\n { label }\n </div>\n <div class=\"bio-properties-panel-group-header-buttons\">\n {\n edited && <DataMarker />\n }\n <button class=\"bio-properties-panel-group-header-button\">\n <GroupArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </button>\n </div>\n </div>\n <div class={ classnames(\n 'bio-properties-panel-group-entries',\n open ? 'open' : ''\n ) }>\n {\n entries.map(e => e.component)\n }\n </div>\n </div>;\n}\n\nfunction DataMarker() {\n return (\n <div class=\"bio-properties-panel-dot\">\n <svg\n aria-label=\"edited\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 100 100\"\n >\n <circle fill=\"currentColor\" cx=\"50\" cy=\"50\" r=\"50\" />\n </svg>\n </div>\n );\n}\n\n\n// helper //////////////\n\nfunction getTitleAttribute(label, edited) {\n return label + (edited ? ' (edited)' : '');\n}"],"file":"Group.js"}
@@ -15,6 +15,7 @@ const noop = () => {};
15
15
 
16
16
  export default function ListGroup(props) {
17
17
  const {
18
+ element,
18
19
  id,
19
20
  items,
20
21
  label,
@@ -24,17 +25,25 @@ export default function ListGroup(props) {
24
25
  const [open, setOpen] = useState(false);
25
26
  const [ordering, setOrdering] = useState([]);
26
27
  const [newItemAdded, setNewItemAdded] = useState(false);
27
- const prevItems = usePrevious(items); // keep ordering in sync to items and open changes
28
+ const prevItems = usePrevious(items);
29
+ const prevElement = usePrevious(element);
30
+ const elementChanged = element !== prevElement;
31
+ const shouldHandleEffects = !elementChanged && shouldSort; // reset initial ordering when element changes (before first render)
32
+
33
+ if (elementChanged) {
34
+ setOrdering(createOrdering(shouldSort ? sortItems(items) : items));
35
+ } // keep ordering in sync to items - and open changes
28
36
  // (0) set initial ordering from given items
29
37
 
38
+
30
39
  useEffect(() => {
31
40
  if (!prevItems || !shouldSort) {
32
41
  setOrdering(createOrdering(items));
33
42
  }
34
- }, [items]); // (1) items were added
43
+ }, [items, element]); // (1) items were added
35
44
 
36
45
  useEffect(() => {
37
- if (shouldSort && prevItems && items.length > prevItems.length) {
46
+ if (shouldHandleEffects && prevItems && items.length > prevItems.length) {
38
47
  let add = [];
39
48
  items.forEach(item => {
40
49
  if (!ordering.includes(item.id)) {
@@ -55,17 +64,17 @@ export default function ListGroup(props) {
55
64
  } else {
56
65
  setNewItemAdded(false);
57
66
  }
58
- }, [items, open]); // (2) sort items on open
67
+ }, [items, open, shouldHandleEffects]); // (2) sort items on open
59
68
 
60
69
  useEffect(() => {
61
70
  // we already sorted as items were added
62
- if (shouldSort && open && !newItemAdded) {
71
+ if (shouldHandleEffects && open && !newItemAdded) {
63
72
  setOrdering(createOrdering(sortItems(items)));
64
73
  }
65
- }, [open]); // (3) items were deleted
74
+ }, [open, shouldHandleEffects]); // (3) items were deleted
66
75
 
67
76
  useEffect(() => {
68
- if (shouldSort && prevItems && items.length < prevItems.length) {
77
+ if (shouldHandleEffects && prevItems && items.length < prevItems.length) {
69
78
  let keep = [];
70
79
  ordering.forEach(o => {
71
80
  if (getItem(items, o)) {
@@ -74,7 +83,7 @@ export default function ListGroup(props) {
74
83
  });
75
84
  setOrdering(keep);
76
85
  }
77
- }, [items]);
86
+ }, [items, shouldHandleEffects]);
78
87
 
79
88
  const toggleOpen = () => setOpen(!open);
80
89
 
@@ -92,14 +101,14 @@ export default function ListGroup(props) {
92
101
  }), _jsxs("div", {
93
102
  class: "bio-properties-panel-group-header-buttons",
94
103
  children: [AddContainer ? _jsx(AddContainer, {
95
- children: _jsx("div", {
96
- class: "bio-properties-panel-add-entry",
104
+ children: _jsx("button", {
105
+ class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
97
106
  children: _jsx(CreateIcon, {})
98
107
  })
99
108
  }) : null, hasItems ? _jsx("div", {
100
109
  class: "bio-properties-panel-list-badge",
101
110
  children: items.length
102
- }) : null, hasItems ? _jsx("div", {
111
+ }) : null, hasItems ? _jsx("button", {
103
112
  class: "bio-properties-panel-group-header-button",
104
113
  children: _jsx(GroupArrowIcon, {
105
114
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/ListGroup.js"],"names":["useState","useEffect","classnames","find","sortBy","usePrevious","ListItem","CreateIcon","GroupArrowIcon","noop","ListGroup","props","id","items","label","add","AddContainer","shouldSort","open","setOpen","ordering","setOrdering","newItemAdded","setNewItemAdded","prevItems","createOrdering","length","forEach","item","includes","push","newOrdering","sortItems","removeDuplicates","keep","o","getItem","toggleOpen","hasItems","getTitleAttribute","map","index","i","toLowerCase","filter","indexOf","count"],"mappings":"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","sourcesContent":["import {\n useState,\n useEffect\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n find,\n sortBy\n} from 'min-dash';\n\nimport {\n usePrevious\n} from '../hooks';\n\nimport ListItem from './ListItem';\n\nimport {\n CreateIcon,\n GroupArrowIcon\n} from './icons';\n\nconst noop = () => {};\n\n/**\n * @param {import('../PropertiesPanel').ListGroupDefinition} props\n */\nexport default function ListGroup(props) {\n const {\n id,\n items,\n label,\n add: AddContainer,\n shouldSort = true\n } = props;\n\n const [ open, setOpen ] = useState(false);\n const [ ordering, setOrdering ] = useState([]);\n const [ newItemAdded, setNewItemAdded ] = useState(false);\n\n const prevItems = usePrevious(items);\n\n\n // keep ordering in sync to items and open changes\n\n // (0) set initial ordering from given items\n useEffect(() => {\n if (!prevItems || !shouldSort) {\n setOrdering(createOrdering(items));\n }\n }, [ items ]);\n\n // (1) items were added\n useEffect(() => {\n if (shouldSort && prevItems && items.length > prevItems.length) {\n\n let add = [];\n\n items.forEach(item => {\n if (!ordering.includes(item.id)) {\n add.push(item.id);\n }\n });\n\n let newOrdering = ordering;\n\n // sort + open if closed\n if (!open) {\n newOrdering = createOrdering(sortItems(items));\n setOpen(true);\n }\n\n // add new items on top\n newOrdering = removeDuplicates([\n ...add,\n ...newOrdering\n ]);\n\n setOrdering(newOrdering);\n setNewItemAdded(true);\n } else {\n setNewItemAdded(false);\n }\n }, [ items, open ]);\n\n // (2) sort items on open\n useEffect(() => {\n\n // we already sorted as items were added\n if (shouldSort && open && !newItemAdded) {\n setOrdering(createOrdering(sortItems(items)));\n }\n }, [ open ]);\n\n // (3) items were deleted\n useEffect(() => {\n if (shouldSort && prevItems && items.length < prevItems.length) {\n let keep = [];\n\n ordering.forEach(o => {\n if (getItem(items, o)) {\n keep.push(o);\n }\n });\n\n setOrdering(keep);\n }\n }, [ items ]);\n\n const toggleOpen = () => setOpen(!open);\n\n const hasItems = !!items.length;\n\n\n return <div class=\"bio-properties-panel-group\" data-group-id={ 'group-' + id }>\n <div\n class={ classnames(\n 'bio-properties-panel-group-header',\n hasItems ? '' : 'empty'\n ) }\n onClick={ hasItems ? toggleOpen : noop }>\n <div title={ getTitleAttribute(label, items) } class=\"bio-properties-panel-group-header-title\">\n { label }\n </div>\n <div class=\"bio-properties-panel-group-header-buttons\">\n {\n AddContainer\n ? (\n <AddContainer>\n <div class=\"bio-properties-panel-add-entry\">\n <CreateIcon />\n </div>\n </AddContainer>\n )\n : null\n }\n {\n hasItems\n ? (\n <div class=\"bio-properties-panel-list-badge\">\n { items.length }\n </div>\n )\n : null\n }\n {\n hasItems\n ? (\n <div class=\"bio-properties-panel-group-header-button\">\n <GroupArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </div>\n )\n : null\n }\n </div>\n </div>\n <div class={ classnames(\n 'bio-properties-panel-list',\n open && hasItems ? 'open' : ''\n ) }>\n {\n ordering.map((o, index) => {\n const item = getItem(items, o);\n\n if (!item) {\n return;\n }\n\n return (\n <ListItem\n key={ item.id }\n autoOpen={ index === 0 && newItemAdded } // open first item when recently added\n { ...item } />\n );\n })\n }\n </div>\n </div>;\n}\n\n\n// helpers ////////////////////\n\n/**\n * Sorts given items alphanumeric by label\n */\nfunction sortItems(items) {\n return sortBy(items, i => i.label.toLowerCase());\n}\n\nfunction getItem(items, id) {\n return find(items, i => i.id === id);\n}\n\nfunction createOrdering(items) {\n return items.map(i => i.id);\n}\n\nfunction removeDuplicates(items) {\n return items.filter((i, index) => items.indexOf(i) === index);\n}\n\nfunction getTitleAttribute(label, items) {\n const count = items.length;\n\n return label + (count ? ` (${count} item${count != 1 ? 's' : ''})` : '');\n}"],"file":"ListGroup.js"}
1
+ {"version":3,"sources":["../../src/components/ListGroup.js"],"names":["useState","useEffect","classnames","find","sortBy","usePrevious","ListItem","CreateIcon","GroupArrowIcon","noop","ListGroup","props","element","id","items","label","add","AddContainer","shouldSort","open","setOpen","ordering","setOrdering","newItemAdded","setNewItemAdded","prevItems","prevElement","elementChanged","shouldHandleEffects","createOrdering","sortItems","length","forEach","item","includes","push","newOrdering","removeDuplicates","keep","o","getItem","toggleOpen","hasItems","getTitleAttribute","map","index","i","toLowerCase","filter","indexOf","count"],"mappings":"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","sourcesContent":["import {\n useState,\n useEffect\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n find,\n sortBy\n} from 'min-dash';\n\nimport {\n usePrevious\n} from '../hooks';\n\nimport ListItem from './ListItem';\n\nimport {\n CreateIcon,\n GroupArrowIcon\n} from './icons';\n\nconst noop = () => {};\n\n/**\n * @param {import('../PropertiesPanel').ListGroupDefinition} props\n */\nexport default function ListGroup(props) {\n const {\n element,\n id,\n items,\n label,\n add: AddContainer,\n shouldSort = true\n } = props;\n\n const [ open, setOpen ] = useState(false);\n const [ ordering, setOrdering ] = useState([]);\n const [ newItemAdded, setNewItemAdded ] = useState(false);\n\n const prevItems = usePrevious(items);\n const prevElement = usePrevious(element);\n\n const elementChanged = element !== prevElement;\n const shouldHandleEffects = !elementChanged && shouldSort;\n\n // reset initial ordering when element changes (before first render)\n if (elementChanged) {\n setOrdering(createOrdering(shouldSort ? sortItems(items) : items));\n }\n\n // keep ordering in sync to items - and open changes\n\n // (0) set initial ordering from given items\n useEffect(() => {\n if (!prevItems || !shouldSort) {\n setOrdering(createOrdering(items));\n }\n }, [ items, element ]);\n\n // (1) items were added\n useEffect(() => {\n if (shouldHandleEffects && prevItems && items.length > prevItems.length) {\n\n let add = [];\n\n items.forEach(item => {\n if (!ordering.includes(item.id)) {\n add.push(item.id);\n }\n });\n\n let newOrdering = ordering;\n\n // sort + open if closed\n if (!open) {\n newOrdering = createOrdering(sortItems(items));\n setOpen(true);\n }\n\n // add new items on top\n newOrdering = removeDuplicates([\n ...add,\n ...newOrdering\n ]);\n\n setOrdering(newOrdering);\n setNewItemAdded(true);\n } else {\n setNewItemAdded(false);\n }\n }, [ items, open, shouldHandleEffects ]);\n\n // (2) sort items on open\n useEffect(() => {\n\n // we already sorted as items were added\n if (shouldHandleEffects && open && !newItemAdded) {\n setOrdering(createOrdering(sortItems(items)));\n }\n }, [ open, shouldHandleEffects ]);\n\n // (3) items were deleted\n useEffect(() => {\n if (shouldHandleEffects && prevItems && items.length < prevItems.length) {\n let keep = [];\n\n ordering.forEach(o => {\n if (getItem(items, o)) {\n keep.push(o);\n }\n });\n\n setOrdering(keep);\n }\n }, [ items, shouldHandleEffects ]);\n\n const toggleOpen = () => setOpen(!open);\n\n const hasItems = !!items.length;\n\n return <div class=\"bio-properties-panel-group\" data-group-id={ 'group-' + id }>\n <div\n class={ classnames(\n 'bio-properties-panel-group-header',\n hasItems ? '' : 'empty'\n ) }\n onClick={ hasItems ? toggleOpen : noop }>\n <div title={ getTitleAttribute(label, items) } class=\"bio-properties-panel-group-header-title\">\n { label }\n </div>\n <div class=\"bio-properties-panel-group-header-buttons\">\n {\n AddContainer\n ? (\n <AddContainer>\n <button class=\"bio-properties-panel-group-header-button bio-properties-panel-add-entry\">\n <CreateIcon />\n </button>\n </AddContainer>\n )\n : null\n }\n {\n hasItems\n ? (\n <div class=\"bio-properties-panel-list-badge\">\n { items.length }\n </div>\n )\n : null\n }\n {\n hasItems\n ? (\n <button class=\"bio-properties-panel-group-header-button\">\n <GroupArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </button>\n )\n : null\n }\n </div>\n </div>\n <div class={ classnames(\n 'bio-properties-panel-list',\n open && hasItems ? 'open' : ''\n ) }>\n {\n ordering.map((o, index) => {\n const item = getItem(items, o);\n\n if (!item) {\n return;\n }\n\n return (\n <ListItem\n key={ item.id }\n autoOpen={ index === 0 && newItemAdded } // open first item when recently added\n { ...item } />\n );\n })\n }\n </div>\n </div>;\n}\n\n\n// helpers ////////////////////\n\n/**\n * Sorts given items alphanumeric by label\n */\nfunction sortItems(items) {\n return sortBy(items, i => i.label.toLowerCase());\n}\n\nfunction getItem(items, id) {\n return find(items, i => i.id === id);\n}\n\nfunction createOrdering(items) {\n return items.map(i => i.id);\n}\n\nfunction removeDuplicates(items) {\n return items.filter((i, index) => items.indexOf(i) === index);\n}\n\nfunction getTitleAttribute(label, items) {\n const count = items.length;\n\n return label + (count ? ` (${count} item${count != 1 ? 's' : ''})` : '');\n}"],"file":"ListGroup.js"}
@@ -32,8 +32,9 @@ export default function CollapsibleEntry(props) {
32
32
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
33
33
  })
34
34
  }), RemoveContainer ? _jsx(RemoveContainer, {
35
- children: _jsx(ListDeleteIcon, {
36
- class: "bio-properties-panel-remove-entry"
35
+ children: _jsx("button", {
36
+ class: "bio-properties-panel-remove-entry",
37
+ children: _jsx(ListDeleteIcon, {})
37
38
  })
38
39
  }) : null]
39
40
  }), _jsx("div", {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/entries/Collapsible.js"],"names":["useState","classnames","ListArrowIcon","ListDeleteIcon","CollapsibleEntry","props","id","entries","label","remove","RemoveContainer","open","shouldOpen","setOpen","toggleOpen","placeholderLabel","map","e","component"],"mappings":"AAAA,SACEA,QADF,QAEO,cAFP;AAIA,OAAOC,UAAP,MAAuB,YAAvB;AAEA,SACEC,aADF,EAEEC,cAFF,QAGO,UAHP;;;AAMA,eAAe,SAASC,gBAAT,CAA0BC,KAA1B,EAAiC;AAC9C,QAAM;AACJC,IAAAA,EADI;AAEJC,IAAAA,OAAO,GAAG,EAFN;AAGJC,IAAAA,KAHI;AAIJC,IAAAA,MAAM,EAAEC,eAJJ;AAKJC,IAAAA,IAAI,EAAEC;AALF,MAMFP,KANJ;AAQA,QAAM,CAAEM,IAAF,EAAQE,OAAR,IAAoBb,QAAQ,CAACY,UAAD,CAAlC;;AAEA,QAAME,UAAU,GAAG,MAAMD,OAAO,CAAC,CAACF,IAAF,CAAhC,CAX8C,CAa9C;;;AACA,QAAMI,gBAAgB,GAAG,SAAzB;AAEA,SACE;AACE,qBAAgBT,EADlB;AAEE,IAAA,KAAK,EAAGL,UAAU,CAChB,wCADgB,EAEhBU,IAAI,GAAG,MAAH,GAAY,EAFA,CAFpB;AAAA,eAME;AAAK,MAAA,KAAK,EAAC,+CAAX;AAA2D,MAAA,OAAO,EAAGG,UAArE;AAAA,iBACE;AACE,QAAA,KAAK,EAAGb,UAAU,CAChB,qDADgB,EAEhB,CAACO,KAAD,IAAU,OAFM,CADpB;AAAA,kBAKIA,KAAK,IAAIO;AALb,QADF,EAQE;AAAK,QAAA,KAAK,EAAC,8CAAX;AAAA,kBACE,KAAC,aAAD;AAAe,UAAA,KAAK,EAAGJ,IAAI,GAAG,iCAAH,GAAuC;AAAlE;AADF,QARF,EAYID,eAAe,GAGX,KAAC,eAAD;AAAA,kBACE,KAAC,cAAD;AAAgB,UAAA,KAAK,EAAC;AAAtB;AADF,QAHW,GAOX,IAnBR;AAAA,MANF,EA4BE;AAAK,MAAA,KAAK,EAAGT,UAAU,CACrB,gDADqB,EAErBU,IAAI,GAAG,MAAH,GAAY,EAFK,CAAvB;AAAA,gBAKIJ,OAAO,CAACS,GAAR,CAAYC,CAAC,IAAIA,CAAC,CAACC,SAAnB;AALJ,MA5BF;AAAA,IADF;AAuCD","sourcesContent":["import {\n useState\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n ListArrowIcon,\n ListDeleteIcon,\n} from '../icons';\n\n\nexport default function CollapsibleEntry(props) {\n const {\n id,\n entries = [],\n label,\n remove: RemoveContainer,\n open: shouldOpen\n } = props;\n\n const [ open, setOpen ] = useState(shouldOpen);\n\n const toggleOpen = () => setOpen(!open);\n\n // todo(pinussilvestrus): translate once we have a translate mechanism for the core\n const placeholderLabel = '<empty>';\n\n return (\n <div\n data-entry-id={ id }\n class={ classnames(\n 'bio-properties-panel-collapsible-entry',\n open ? 'open' : ''\n ) }>\n <div class=\"bio-properties-panel-collapsible-entry-header\" onClick={ toggleOpen }>\n <div\n class={ classnames(\n 'bio-properties-panel-collapsible-entry-header-title',\n !label && 'empty'\n ) }>\n { label || placeholderLabel }\n </div>\n <div class=\"bio-properties-panel-collapsible-entry-arrow\">\n <ListArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </div>\n {\n RemoveContainer\n ?\n (\n <RemoveContainer>\n <ListDeleteIcon class=\"bio-properties-panel-remove-entry\" />\n </RemoveContainer>\n )\n : null\n }\n </div>\n <div class={ classnames(\n 'bio-properties-panel-collapsible-entry-entries',\n open ? 'open' : ''\n ) }>\n {\n entries.map(e => e.component)\n }\n </div>\n </div>\n );\n}"],"file":"Collapsible.js"}
1
+ {"version":3,"sources":["../../../src/components/entries/Collapsible.js"],"names":["useState","classnames","ListArrowIcon","ListDeleteIcon","CollapsibleEntry","props","id","entries","label","remove","RemoveContainer","open","shouldOpen","setOpen","toggleOpen","placeholderLabel","map","e","component"],"mappings":"AAAA,SACEA,QADF,QAEO,cAFP;AAIA,OAAOC,UAAP,MAAuB,YAAvB;AAEA,SACEC,aADF,EAEEC,cAFF,QAGO,UAHP;;;AAMA,eAAe,SAASC,gBAAT,CAA0BC,KAA1B,EAAiC;AAC9C,QAAM;AACJC,IAAAA,EADI;AAEJC,IAAAA,OAAO,GAAG,EAFN;AAGJC,IAAAA,KAHI;AAIJC,IAAAA,MAAM,EAAEC,eAJJ;AAKJC,IAAAA,IAAI,EAAEC;AALF,MAMFP,KANJ;AAQA,QAAM,CAAEM,IAAF,EAAQE,OAAR,IAAoBb,QAAQ,CAACY,UAAD,CAAlC;;AAEA,QAAME,UAAU,GAAG,MAAMD,OAAO,CAAC,CAACF,IAAF,CAAhC,CAX8C,CAa9C;;;AACA,QAAMI,gBAAgB,GAAG,SAAzB;AAEA,SACE;AACE,qBAAgBT,EADlB;AAEE,IAAA,KAAK,EAAGL,UAAU,CAChB,wCADgB,EAEhBU,IAAI,GAAG,MAAH,GAAY,EAFA,CAFpB;AAAA,eAME;AAAK,MAAA,KAAK,EAAC,+CAAX;AAA2D,MAAA,OAAO,EAAGG,UAArE;AAAA,iBACE;AACE,QAAA,KAAK,EAAGb,UAAU,CAChB,qDADgB,EAEhB,CAACO,KAAD,IAAU,OAFM,CADpB;AAAA,kBAKIA,KAAK,IAAIO;AALb,QADF,EAQE;AAAK,QAAA,KAAK,EAAC,8CAAX;AAAA,kBACE,KAAC,aAAD;AAAe,UAAA,KAAK,EAAGJ,IAAI,GAAG,iCAAH,GAAuC;AAAlE;AADF,QARF,EAYID,eAAe,GAGX,KAAC,eAAD;AAAA,kBACE;AAAQ,UAAA,KAAK,EAAC,mCAAd;AAAA,oBACE,KAAC,cAAD;AADF;AADF,QAHW,GASX,IArBR;AAAA,MANF,EA8BE;AAAK,MAAA,KAAK,EAAGT,UAAU,CACrB,gDADqB,EAErBU,IAAI,GAAG,MAAH,GAAY,EAFK,CAAvB;AAAA,gBAKIJ,OAAO,CAACS,GAAR,CAAYC,CAAC,IAAIA,CAAC,CAACC,SAAnB;AALJ,MA9BF;AAAA,IADF;AAyCD","sourcesContent":["import {\n useState\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nimport {\n ListArrowIcon,\n ListDeleteIcon,\n} from '../icons';\n\n\nexport default function CollapsibleEntry(props) {\n const {\n id,\n entries = [],\n label,\n remove: RemoveContainer,\n open: shouldOpen\n } = props;\n\n const [ open, setOpen ] = useState(shouldOpen);\n\n const toggleOpen = () => setOpen(!open);\n\n // todo(pinussilvestrus): translate once we have a translate mechanism for the core\n const placeholderLabel = '<empty>';\n\n return (\n <div\n data-entry-id={ id }\n class={ classnames(\n 'bio-properties-panel-collapsible-entry',\n open ? 'open' : ''\n ) }>\n <div class=\"bio-properties-panel-collapsible-entry-header\" onClick={ toggleOpen }>\n <div\n class={ classnames(\n 'bio-properties-panel-collapsible-entry-header-title',\n !label && 'empty'\n ) }>\n { label || placeholderLabel }\n </div>\n <div class=\"bio-properties-panel-collapsible-entry-arrow\">\n <ListArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </div>\n {\n RemoveContainer\n ?\n (\n <RemoveContainer>\n <button class=\"bio-properties-panel-remove-entry\">\n <ListDeleteIcon />\n </button>\n </RemoveContainer>\n )\n : null\n }\n </div>\n <div class={ classnames(\n 'bio-properties-panel-collapsible-entry-entries',\n open ? 'open' : ''\n ) }>\n {\n entries.map(e => e.component)\n }\n </div>\n </div>\n );\n}"],"file":"Collapsible.js"}
@@ -0,0 +1,196 @@
1
+ import { useEffect, useRef, useState } from 'preact/hooks';
2
+ import { query as domQuery } from 'min-dom';
3
+ import { useKeyFactory, usePrevious } from '../../hooks';
4
+ import classnames from 'classnames';
5
+ import { CreateIcon, GroupArrowIcon, ListDeleteIcon } from '../icons';
6
+ /**
7
+ * Entry for handling lists represented as nested entries.
8
+ *
9
+ * @template Item
10
+ * @param {object} props
11
+ * @param {string} props.id
12
+ * @param {*} props.element
13
+ * @param {Function} props.onAdd
14
+ * @param {(item: Item, index: number, isNew: boolean) => JSX.Element} props.renderItem
15
+ * @param {string} [props.label='<empty>']
16
+ * @param {Function} [props.onRemove]
17
+ * @param {Item[]} [props.items]
18
+ * @param {boolean} [props.open]
19
+ * @param {string} [props.autoFocusEntry]
20
+ * @param {(a: Item, b: Item) => -1 | 0 | 1} [props.compareFn]
21
+ * @returns
22
+ */
23
+
24
+ import { jsx as _jsx } from "preact/jsx-runtime";
25
+ import { jsxs as _jsxs } from "preact/jsx-runtime";
26
+ export default function List(props) {
27
+ const {
28
+ id,
29
+ element,
30
+ items = [],
31
+ renderItem,
32
+ label = '<empty>',
33
+ open: shouldOpen,
34
+ onAdd,
35
+ onRemove,
36
+ autoFocusEntry,
37
+ compareFn
38
+ } = props;
39
+ const [open, setOpen] = useState(!!shouldOpen);
40
+ const hasItems = !!items.length;
41
+
42
+ const toggleOpen = () => hasItems && setOpen(!open);
43
+
44
+ const opening = !usePrevious(open) && open;
45
+ const elementChanged = usePrevious(element) !== element;
46
+ const shouldReset = opening || elementChanged;
47
+ const sortedItems = useSortedItems(items, compareFn, shouldReset);
48
+ const newItems = useNewItems(items, elementChanged);
49
+ useEffect(() => {
50
+ if (open && !hasItems) {
51
+ setOpen(false);
52
+ }
53
+ }, [open, hasItems]);
54
+ /**
55
+ * @param {MouseEvent} event
56
+ */
57
+
58
+ function addItem(event) {
59
+ event.stopPropagation();
60
+ onAdd();
61
+
62
+ if (!open) {
63
+ setOpen(true);
64
+ }
65
+ }
66
+
67
+ return _jsxs("div", {
68
+ "data-entry-id": id,
69
+ class: classnames('bio-properties-panel-entry', 'bio-properties-panel-list-entry', open ? 'open' : ''),
70
+ children: [_jsxs("div", {
71
+ class: "bio-properties-panel-list-entry-header",
72
+ onClick: toggleOpen,
73
+ children: [_jsx("div", {
74
+ title: getTitle(label, items),
75
+ class: classnames('bio-properties-panel-list-entry-header-title', open && 'open'),
76
+ children: label
77
+ }), _jsxs("div", {
78
+ class: "bio-properties-panel-list-entry-header-buttons",
79
+ children: [_jsx("button", {
80
+ onClick: addItem,
81
+ class: "bio-properties-panel-add-entry",
82
+ children: _jsx(CreateIcon, {})
83
+ }), hasItems && _jsx("div", {
84
+ class: "bio-properties-panel-list-badge",
85
+ children: items.length
86
+ }), hasItems && _jsx("button", {
87
+ class: "bio-properties-panel-list-entry-arrow",
88
+ children: _jsx(GroupArrowIcon, {
89
+ class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
90
+ })
91
+ })]
92
+ })]
93
+ }), hasItems && _jsx(ItemsList, {
94
+ autoFocusEntry: autoFocusEntry,
95
+ id: id,
96
+ open: open,
97
+ items: sortedItems,
98
+ newItems: newItems,
99
+ onRemove: onRemove,
100
+ renderItem: renderItem
101
+ })]
102
+ });
103
+ }
104
+
105
+ function ItemsList(props) {
106
+ const {
107
+ autoFocusEntry,
108
+ id,
109
+ items,
110
+ newItems,
111
+ open,
112
+ onRemove,
113
+ renderItem
114
+ } = props;
115
+ const getKey = useKeyFactory();
116
+ const newItem = newItems[0];
117
+ useEffect(() => {
118
+ if (newItem && autoFocusEntry) {
119
+ const entry = domQuery(`[data-entry-id="${id}"]`);
120
+ const focusableInput = domQuery('.bio-properties-panel-input', entry);
121
+
122
+ if (focusableInput) {
123
+ focusableInput.focus();
124
+ }
125
+ }
126
+ }, [newItem, autoFocusEntry, id]);
127
+ return _jsx("ol", {
128
+ class: classnames('bio-properties-panel-list-entry-items', open ? 'open' : ''),
129
+ children: items.map((item, index) => {
130
+ const key = getKey(item);
131
+ return _jsxs("li", {
132
+ class: "bio-properties-panel-list-entry-item",
133
+ children: [renderItem(item, index, item === newItem), onRemove && _jsx("button", {
134
+ type: "button",
135
+ class: "bio-properties-panel-remove-entry",
136
+ onClick: () => onRemove && onRemove(item),
137
+ children: _jsx(ListDeleteIcon, {})
138
+ })]
139
+ }, key);
140
+ })
141
+ });
142
+ }
143
+
144
+ function getTitle(label, items) {
145
+ if (!items.length) {
146
+ return label;
147
+ }
148
+
149
+ return `${label} (${items.length} items)`;
150
+ }
151
+ /**
152
+ * Place new items in the beginning of the list and sort the rest with provided function.
153
+ *
154
+ * @template Item
155
+ * @param {Item[]} currentItems
156
+ * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
157
+ * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
158
+ * @returns {Item[]}
159
+ */
160
+
161
+
162
+ function useSortedItems(currentItems, compareFn, shouldReset = false) {
163
+ const itemsRef = useRef(currentItems.slice()); // (1) Reset and optionally sort.
164
+
165
+ if (shouldReset) {
166
+ itemsRef.current = currentItems.slice();
167
+
168
+ if (compareFn) {
169
+ itemsRef.current.sort(compareFn);
170
+ }
171
+ } else {
172
+ const items = itemsRef.current; // (2) Move new items to the beginning of the list.
173
+
174
+ for (const item of currentItems) {
175
+ if (!items.includes(item)) {
176
+ items.unshift(item);
177
+ }
178
+ } // (3) Filter out removed items.
179
+
180
+
181
+ itemsRef.current = items.filter(item => currentItems.includes(item));
182
+ }
183
+
184
+ return itemsRef.current;
185
+ }
186
+
187
+ function useNewItems(items = [], shouldReset) {
188
+ const previousItems = usePrevious(items.slice()) || [];
189
+
190
+ if (shouldReset) {
191
+ return [];
192
+ }
193
+
194
+ return previousItems ? items.filter(item => !previousItems.includes(item)) : [];
195
+ }
196
+ //# sourceMappingURL=List.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/entries/List.js"],"names":["useEffect","useRef","useState","query","domQuery","useKeyFactory","usePrevious","classnames","CreateIcon","GroupArrowIcon","ListDeleteIcon","List","props","id","element","items","renderItem","label","open","shouldOpen","onAdd","onRemove","autoFocusEntry","compareFn","setOpen","hasItems","length","toggleOpen","opening","elementChanged","shouldReset","sortedItems","useSortedItems","newItems","useNewItems","addItem","event","stopPropagation","getTitle","ItemsList","getKey","newItem","entry","focusableInput","focus","map","item","index","key","currentItems","itemsRef","slice","current","sort","includes","unshift","filter","previousItems"],"mappings":"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","sourcesContent":["import {\n useEffect,\n useRef,\n useState\n} from 'preact/hooks';\n\nimport {\n query as domQuery\n} from 'min-dom';\n\nimport {\n useKeyFactory,\n usePrevious\n} from '../../hooks';\n\nimport classnames from 'classnames';\n\nimport {\n CreateIcon,\n GroupArrowIcon,\n ListDeleteIcon\n} from '../icons';\n\n/**\n * Entry for handling lists represented as nested entries.\n *\n * @template Item\n * @param {object} props\n * @param {string} props.id\n * @param {*} props.element\n * @param {Function} props.onAdd\n * @param {(item: Item, index: number, isNew: boolean) => JSX.Element} props.renderItem\n * @param {string} [props.label='<empty>']\n * @param {Function} [props.onRemove]\n * @param {Item[]} [props.items]\n * @param {boolean} [props.open]\n * @param {string} [props.autoFocusEntry]\n * @param {(a: Item, b: Item) => -1 | 0 | 1} [props.compareFn]\n * @returns\n */\nexport default function List(props) {\n const {\n id,\n element,\n items = [],\n renderItem,\n label = '<empty>',\n open: shouldOpen,\n onAdd,\n onRemove,\n autoFocusEntry,\n compareFn\n } = props;\n\n const [ open, setOpen ] = useState(!!shouldOpen);\n\n const hasItems = !!items.length;\n const toggleOpen = () => hasItems && setOpen(!open);\n\n const opening = !usePrevious(open) && open;\n const elementChanged = usePrevious(element) !== element;\n const shouldReset = opening || elementChanged;\n const sortedItems = useSortedItems(items, compareFn, shouldReset);\n\n const newItems = useNewItems(items, elementChanged);\n\n useEffect(() => {\n if (open && !hasItems) {\n setOpen(false);\n }\n }, [ open, hasItems ]);\n\n /**\n * @param {MouseEvent} event\n */\n function addItem(event) {\n event.stopPropagation();\n onAdd();\n\n if (!open) {\n setOpen(true);\n }\n }\n\n return (\n <div\n data-entry-id={ id }\n class={ classnames(\n 'bio-properties-panel-entry',\n 'bio-properties-panel-list-entry',\n open ? 'open' : ''\n ) }>\n <div class=\"bio-properties-panel-list-entry-header\" onClick={ toggleOpen }>\n <div\n title={ getTitle(label, items) }\n class={ classnames(\n 'bio-properties-panel-list-entry-header-title',\n open && 'open'\n ) }>\n { label }\n </div>\n <div\n class=\"bio-properties-panel-list-entry-header-buttons\"\n >\n <button onClick={ addItem } class=\"bio-properties-panel-add-entry\">\n <CreateIcon />\n </button>\n {\n hasItems && (\n <div class=\"bio-properties-panel-list-badge\">\n { items.length }\n </div>\n )\n }\n {\n hasItems && (\n <button class=\"bio-properties-panel-list-entry-arrow\">\n <GroupArrowIcon class={ open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right' } />\n </button>\n )\n }\n </div>\n </div>\n {\n hasItems && (\n <ItemsList\n autoFocusEntry={ autoFocusEntry }\n id={ id }\n open={ open }\n items={ sortedItems }\n newItems={ newItems }\n onRemove={ onRemove }\n renderItem={ renderItem }\n />\n )\n }\n </div>\n );\n}\n\nfunction ItemsList(props) {\n const {\n autoFocusEntry,\n id,\n items,\n newItems,\n open,\n onRemove,\n renderItem\n } = props;\n\n const getKey = useKeyFactory();\n\n const newItem = newItems[0];\n\n useEffect(() => {\n if (newItem && autoFocusEntry) {\n const entry = domQuery(`[data-entry-id=\"${id}\"]`);\n const focusableInput = domQuery('.bio-properties-panel-input', entry);\n\n if (focusableInput) {\n focusableInput.focus();\n }\n }\n }, [ newItem, autoFocusEntry, id ]);\n\n return (\n <ol class={ classnames(\n 'bio-properties-panel-list-entry-items',\n open ? 'open' : ''\n ) }>\n {\n items.map((item, index) => {\n const key = getKey(item);\n\n return (<li class=\"bio-properties-panel-list-entry-item\" key={ key }>\n {renderItem(item, index, item === newItem)}\n {\n onRemove && (\n <button\n type=\"button\"\n class=\"bio-properties-panel-remove-entry\"\n onClick={ () => onRemove && onRemove(item) }\n ><ListDeleteIcon /></button>\n )\n }\n </li>);\n })\n }\n </ol>);\n}\n\nfunction getTitle(label, items) {\n if (!items.length) {\n return label;\n }\n\n return `${label} (${items.length} items)`;\n}\n\n/**\n * Place new items in the beginning of the list and sort the rest with provided function.\n *\n * @template Item\n * @param {Item[]} currentItems\n * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items\n * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook\n * @returns {Item[]}\n */\nfunction useSortedItems(currentItems, compareFn, shouldReset = false) {\n const itemsRef = useRef(currentItems.slice());\n\n // (1) Reset and optionally sort.\n if (shouldReset) {\n itemsRef.current = currentItems.slice();\n\n if (compareFn) {\n itemsRef.current.sort(compareFn);\n }\n } else {\n const items = itemsRef.current;\n\n // (2) Move new items to the beginning of the list.\n for (const item of currentItems) {\n if (!items.includes(item)) {\n items.unshift(item);\n }\n }\n\n // (3) Filter out removed items.\n itemsRef.current = items.filter(item => currentItems.includes(item));\n }\n\n return itemsRef.current;\n}\n\nfunction useNewItems(items = [], shouldReset) {\n const previousItems = usePrevious(items.slice()) || [];\n\n if (shouldReset) {\n return [];\n }\n\n return previousItems ? items.filter(item => !previousItems.includes(item)) : [];\n}\n"],"file":"List.js"}
@@ -0,0 +1,56 @@
1
+ import { useMemo } from 'preact/hooks';
2
+ /**
3
+ * @param {Object} props
4
+ * @param {Function} props.debounce
5
+ * @param {Boolean} [props.disabled]
6
+ * @param {Object} props.element
7
+ * @param {Function} props.getValue
8
+ * @param {String} props.id
9
+ * @param {Function} [props.onBlur]
10
+ * @param {Function} [props.onFocus]
11
+ * @param {Function} props.setValue
12
+ */
13
+
14
+ import { jsx as _jsx } from "preact/jsx-runtime";
15
+ export default function Simple(props) {
16
+ const {
17
+ debounce,
18
+ disabled,
19
+ element,
20
+ getValue,
21
+ id,
22
+ onBlur,
23
+ onFocus,
24
+ setValue
25
+ } = props;
26
+ const handleInput = useMemo(() => {
27
+ return debounce(({
28
+ target
29
+ }) => setValue(target.value.length ? target.value : undefined));
30
+ }, [setValue, debounce]);
31
+ const value = getValue(element);
32
+ return _jsx("div", {
33
+ class: "bio-properties-panel-simple",
34
+ children: _jsx("input", {
35
+ id: prefixId(id),
36
+ type: "text",
37
+ name: id,
38
+ spellCheck: "false",
39
+ autoComplete: "off",
40
+ disabled: disabled,
41
+ class: "bio-properties-panel-input",
42
+ onInput: handleInput,
43
+ onFocus: onFocus,
44
+ onBlur: onBlur,
45
+ value: value || ''
46
+ })
47
+ });
48
+ }
49
+ export function isEdited(node) {
50
+ return node && !!node.value;
51
+ } // helpers /////////////////
52
+
53
+ function prefixId(id) {
54
+ return `bio-properties-panel-${id}`;
55
+ }
56
+ //# sourceMappingURL=Simple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/entries/Simple.js"],"names":["useMemo","Simple","props","debounce","disabled","element","getValue","id","onBlur","onFocus","setValue","handleInput","target","value","length","undefined","prefixId","isEdited","node"],"mappings":"AAAA,SACEA,OADF,QAEO,cAFP;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACA,eAAe,SAASC,MAAT,CAAgBC,KAAhB,EAAuB;AACpC,QAAM;AACJC,IAAAA,QADI;AAEJC,IAAAA,QAFI;AAGJC,IAAAA,OAHI;AAIJC,IAAAA,QAJI;AAKJC,IAAAA,EALI;AAMJC,IAAAA,MANI;AAOJC,IAAAA,OAPI;AAQJC,IAAAA;AARI,MASFR,KATJ;AAWA,QAAMS,WAAW,GAAGX,OAAO,CAAC,MAAM;AAChC,WAAOG,QAAQ,CAAC,CAAC;AAAES,MAAAA;AAAF,KAAD,KAAgBF,QAAQ,CAACE,MAAM,CAACC,KAAP,CAAaC,MAAb,GAAsBF,MAAM,CAACC,KAA7B,GAAqCE,SAAtC,CAAzB,CAAf;AACD,GAF0B,EAExB,CAAEL,QAAF,EAAYP,QAAZ,CAFwB,CAA3B;AAIA,QAAMU,KAAK,GAAGP,QAAQ,CAACD,OAAD,CAAtB;AAEA,SACE;AAAK,IAAA,KAAK,EAAC,6BAAX;AAAA,cACE;AACE,MAAA,EAAE,EAAGW,QAAQ,CAACT,EAAD,CADf;AAEE,MAAA,IAAI,EAAC,MAFP;AAGE,MAAA,IAAI,EAAGA,EAHT;AAIE,MAAA,UAAU,EAAC,OAJb;AAKE,MAAA,YAAY,EAAC,KALf;AAME,MAAA,QAAQ,EAAGH,QANb;AAOE,MAAA,KAAK,EAAC,4BAPR;AAQE,MAAA,OAAO,EAAGO,WARZ;AASE,MAAA,OAAO,EAAGF,OATZ;AAUE,MAAA,MAAM,EAAGD,MAVX;AAWE,MAAA,KAAK,EAAGK,KAAK,IAAI;AAXnB;AADF,IADF;AAgBD;AAED,OAAO,SAASI,QAAT,CAAkBC,IAAlB,EAAwB;AAC7B,SAAOA,IAAI,IAAI,CAAC,CAACA,IAAI,CAACL,KAAtB;AACD,C,CAGD;;AAEA,SAASG,QAAT,CAAkBT,EAAlB,EAAsB;AACpB,SAAQ,wBAAwBA,EAAI,EAApC;AACD","sourcesContent":["import {\n useMemo\n} from 'preact/hooks';\n\n/**\n * @param {Object} props\n * @param {Function} props.debounce\n * @param {Boolean} [props.disabled]\n * @param {Object} props.element\n * @param {Function} props.getValue\n * @param {String} props.id\n * @param {Function} [props.onBlur]\n * @param {Function} [props.onFocus]\n * @param {Function} props.setValue\n */\nexport default function Simple(props) {\n const {\n debounce,\n disabled,\n element,\n getValue,\n id,\n onBlur,\n onFocus,\n setValue\n } = props;\n\n const handleInput = useMemo(() => {\n return debounce(({ target }) => setValue(target.value.length ? target.value : undefined));\n }, [ setValue, debounce ]);\n\n const value = getValue(element);\n\n return (\n <div class=\"bio-properties-panel-simple\">\n <input\n id={ prefixId(id) }\n type=\"text\"\n name={ id }\n spellCheck=\"false\"\n autoComplete=\"off\"\n disabled={ disabled }\n class=\"bio-properties-panel-input\"\n onInput={ handleInput }\n onFocus={ onFocus }\n onBlur={ onBlur }\n value={ value || '' } />\n </div>\n );\n}\n\nexport function isEdited(node) {\n return node && !!node.value;\n}\n\n\n// helpers /////////////////\n\nfunction prefixId(id) {\n return `bio-properties-panel-${ id }`;\n}\n"],"file":"Simple.js"}
@@ -1,4 +1,5 @@
1
1
  import { useMemo } from 'preact/hooks';
2
+ import classnames from 'classnames';
2
3
  import { jsx as _jsx } from "preact/jsx-runtime";
3
4
  import { jsxs as _jsxs } from "preact/jsx-runtime";
4
5
 
@@ -9,7 +10,8 @@ function TextArea(props) {
9
10
  rows = 2,
10
11
  debounce,
11
12
  onInput,
12
- value = ''
13
+ value = '',
14
+ monospace
13
15
  } = props;
14
16
  const handleInput = useMemo(() => {
15
17
  return debounce(({
@@ -26,7 +28,7 @@ function TextArea(props) {
26
28
  id: prefixId(id),
27
29
  name: id,
28
30
  spellCheck: "false",
29
- class: "bio-properties-panel-input",
31
+ class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : ''),
30
32
  onInput: handleInput,
31
33
  onFocus: props.onFocus,
32
34
  onBlur: props.onBlur,
@@ -45,6 +47,7 @@ function TextArea(props) {
45
47
  * @param {Function} props.getValue
46
48
  * @param {Function} props.setValue
47
49
  * @param {Number} props.rows
50
+ * @param {Boolean} props.monospace
48
51
  */
49
52
 
50
53
 
@@ -57,7 +60,8 @@ export default function TextAreaEntry(props) {
57
60
  label,
58
61
  getValue,
59
62
  setValue,
60
- rows
63
+ rows,
64
+ monospace
61
65
  } = props;
62
66
  const value = getValue(element);
63
67
  return _jsxs("div", {
@@ -69,7 +73,8 @@ export default function TextAreaEntry(props) {
69
73
  value: value,
70
74
  onInput: setValue,
71
75
  rows: rows,
72
- debounce: debounce
76
+ debounce: debounce,
77
+ monospace: monospace
73
78
  }), description && _jsx("div", {
74
79
  class: "bio-properties-panel-description",
75
80
  children: description
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/entries/TextArea.js"],"names":["useMemo","TextArea","props","id","label","rows","debounce","onInput","value","handleInput","target","length","undefined","prefixId","onFocus","onBlur","TextAreaEntry","element","description","getValue","setValue","isEdited","node"],"mappings":"AAAA,SACEA,OADF,QAEO,cAFP;;;;AAIA,SAASC,QAAT,CAAkBC,KAAlB,EAAyB;AAEvB,QAAM;AACJC,IAAAA,EADI;AAEJC,IAAAA,KAFI;AAGJC,IAAAA,IAAI,GAAG,CAHH;AAIJC,IAAAA,QAJI;AAKJC,IAAAA,OALI;AAMJC,IAAAA,KAAK,GAAG;AANJ,MAOFN,KAPJ;AASA,QAAMO,WAAW,GAAGT,OAAO,CAAC,MAAM;AAChC,WAAOM,QAAQ,CAAC,CAAC;AAAEI,MAAAA;AAAF,KAAD,KAAgBH,OAAO,CAACG,MAAM,CAACF,KAAP,CAAaG,MAAb,GAAsBD,MAAM,CAACF,KAA7B,GAAqCI,SAAtC,CAAxB,CAAf;AACD,GAF0B,EAExB,CAAEL,OAAF,EAAWD,QAAX,CAFwB,CAA3B;AAIA,SACE;AAAK,IAAA,KAAK,EAAC,+BAAX;AAAA,eACE;AAAO,MAAA,GAAG,EAAGO,QAAQ,CAACV,EAAD,CAArB;AAA4B,MAAA,KAAK,EAAC,4BAAlC;AAAA,gBAAiEC;AAAjE,MADF,EAEE;AACE,MAAA,EAAE,EAAGS,QAAQ,CAACV,EAAD,CADf;AAEE,MAAA,IAAI,EAAGA,EAFT;AAGE,MAAA,UAAU,EAAC,OAHb;AAIE,MAAA,KAAK,EAAC,4BAJR;AAKE,MAAA,OAAO,EAAGM,WALZ;AAME,MAAA,OAAO,EAAGP,KAAK,CAACY,OANlB;AAOE,MAAA,MAAM,EAAGZ,KAAK,CAACa,MAPjB;AAQE,MAAA,IAAI,EAAGV,IART;AASE,MAAA,KAAK,EAAGG;AATV,MAFF;AAAA,IADF;AAeD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACA,eAAe,SAASQ,aAAT,CAAuBd,KAAvB,EAA8B;AAC3C,QAAM;AACJe,IAAAA,OADI;AAEJd,IAAAA,EAFI;AAGJe,IAAAA,WAHI;AAIJZ,IAAAA,QAJI;AAKJF,IAAAA,KALI;AAMJe,IAAAA,QANI;AAOJC,IAAAA,QAPI;AAQJf,IAAAA;AARI,MASFH,KATJ;AAWA,QAAMM,KAAK,GAAGW,QAAQ,CAACF,OAAD,CAAtB;AACA,SACE;AAAK,IAAA,KAAK,EAAC,4BAAX;AAAwC,qBAAgBd,EAAxD;AAAA,eACE,KAAC,QAAD;AACE,MAAA,EAAE,EAAGA,EADP;AAEE,MAAA,KAAK,EAAGC,KAFV;AAGE,MAAA,KAAK,EAAGI,KAHV;AAIE,MAAA,OAAO,EAAGY,QAJZ;AAKE,MAAA,IAAI,EAAGf,IALT;AAME,MAAA,QAAQ,EAAGC;AANb,MADF,EAQIY,WAAW,IAAI;AAAK,MAAA,KAAK,EAAC,kCAAX;AAAA,gBAAgDA;AAAhD,MARnB;AAAA,IADF;AAYD;AAED,OAAO,SAASG,QAAT,CAAkBC,IAAlB,EAAwB;AAC7B,SAAOA,IAAI,IAAI,CAAC,CAACA,IAAI,CAACd,KAAtB;AACD,C,CAGD;;AAEA,SAASK,QAAT,CAAkBV,EAAlB,EAAsB;AACpB,SAAQ,wBAAwBA,EAAI,EAApC;AACD","sourcesContent":["import {\n useMemo\n} from 'preact/hooks';\n\nfunction TextArea(props) {\n\n const {\n id,\n label,\n rows = 2,\n debounce,\n onInput,\n value = ''\n } = props;\n\n const handleInput = useMemo(() => {\n return debounce(({ target }) => onInput(target.value.length ? target.value : undefined));\n }, [ onInput, debounce ]);\n\n return (\n <div class=\"bio-properties-panel-textarea\">\n <label for={ prefixId(id) } class=\"bio-properties-panel-label\">{ label }</label>\n <textarea\n id={ prefixId(id) }\n name={ id }\n spellCheck=\"false\"\n class=\"bio-properties-panel-input\"\n onInput={ handleInput }\n onFocus={ props.onFocus }\n onBlur={ props.onBlur }\n rows={ rows }\n value={ value } />\n </div>\n );\n}\n\n/**\n * @param {Object} props\n * @param {Object} props.element\n * @param {String} props.id\n * @param {String} props.description\n * @param {Boolean} props.debounce\n * @param {String} props.label\n * @param {Function} props.getValue\n * @param {Function} props.setValue\n * @param {Number} props.rows\n */\nexport default function TextAreaEntry(props) {\n const {\n element,\n id,\n description,\n debounce,\n label,\n getValue,\n setValue,\n rows\n } = props;\n\n const value = getValue(element);\n return (\n <div class=\"bio-properties-panel-entry\" data-entry-id={ id }>\n <TextArea\n id={ id }\n label={ label }\n value={ value }\n onInput={ setValue }\n rows={ rows }\n debounce={ debounce } />\n { description && <div class=\"bio-properties-panel-description\">{ description }</div> }\n </div>\n );\n}\n\nexport function isEdited(node) {\n return node && !!node.value;\n}\n\n\n// helpers /////////////////\n\nfunction prefixId(id) {\n return `bio-properties-panel-${ id }`;\n}"],"file":"TextArea.js"}
1
+ {"version":3,"sources":["../../../src/components/entries/TextArea.js"],"names":["useMemo","classnames","TextArea","props","id","label","rows","debounce","onInput","value","monospace","handleInput","target","length","undefined","prefixId","onFocus","onBlur","TextAreaEntry","element","description","getValue","setValue","isEdited","node"],"mappings":"AAAA,SACEA,OADF,QAEO,cAFP;AAIA,OAAOC,UAAP,MAAuB,YAAvB;;;;AAEA,SAASC,QAAT,CAAkBC,KAAlB,EAAyB;AAEvB,QAAM;AACJC,IAAAA,EADI;AAEJC,IAAAA,KAFI;AAGJC,IAAAA,IAAI,GAAG,CAHH;AAIJC,IAAAA,QAJI;AAKJC,IAAAA,OALI;AAMJC,IAAAA,KAAK,GAAG,EANJ;AAOJC,IAAAA;AAPI,MAQFP,KARJ;AAUA,QAAMQ,WAAW,GAAGX,OAAO,CAAC,MAAM;AAChC,WAAOO,QAAQ,CAAC,CAAC;AAAEK,MAAAA;AAAF,KAAD,KAAgBJ,OAAO,CAACI,MAAM,CAACH,KAAP,CAAaI,MAAb,GAAsBD,MAAM,CAACH,KAA7B,GAAqCK,SAAtC,CAAxB,CAAf;AACD,GAF0B,EAExB,CAAEN,OAAF,EAAWD,QAAX,CAFwB,CAA3B;AAIA,SACE;AAAK,IAAA,KAAK,EAAC,+BAAX;AAAA,eACE;AAAO,MAAA,GAAG,EAAGQ,QAAQ,CAACX,EAAD,CAArB;AAA4B,MAAA,KAAK,EAAC,4BAAlC;AAAA,gBAAiEC;AAAjE,MADF,EAEE;AACE,MAAA,EAAE,EAAGU,QAAQ,CAACX,EAAD,CADf;AAEE,MAAA,IAAI,EAAGA,EAFT;AAGE,MAAA,UAAU,EAAC,OAHb;AAIE,MAAA,KAAK,EAAGH,UAAU,CAChB,4BADgB,EAEhBS,SAAS,GAAG,sCAAH,GAA4C,EAFrC,CAJpB;AAQE,MAAA,OAAO,EAAGC,WARZ;AASE,MAAA,OAAO,EAAGR,KAAK,CAACa,OATlB;AAUE,MAAA,MAAM,EAAGb,KAAK,CAACc,MAVjB;AAWE,MAAA,IAAI,EAAGX,IAXT;AAYE,MAAA,KAAK,EAAGG;AAZV,MAFF;AAAA,IADF;AAkBD;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AACA,eAAe,SAASS,aAAT,CAAuBf,KAAvB,EAA8B;AAC3C,QAAM;AACJgB,IAAAA,OADI;AAEJf,IAAAA,EAFI;AAGJgB,IAAAA,WAHI;AAIJb,IAAAA,QAJI;AAKJF,IAAAA,KALI;AAMJgB,IAAAA,QANI;AAOJC,IAAAA,QAPI;AAQJhB,IAAAA,IARI;AASJI,IAAAA;AATI,MAUFP,KAVJ;AAYA,QAAMM,KAAK,GAAGY,QAAQ,CAACF,OAAD,CAAtB;AACA,SACE;AAAK,IAAA,KAAK,EAAC,4BAAX;AAAwC,qBAAgBf,EAAxD;AAAA,eACE,KAAC,QAAD;AACE,MAAA,EAAE,EAAGA,EADP;AAEE,MAAA,KAAK,EAAGC,KAFV;AAGE,MAAA,KAAK,EAAGI,KAHV;AAIE,MAAA,OAAO,EAAGa,QAJZ;AAKE,MAAA,IAAI,EAAGhB,IALT;AAME,MAAA,QAAQ,EAAGC,QANb;AAOE,MAAA,SAAS,EAAGG;AAPd,MADF,EASIU,WAAW,IAAI;AAAK,MAAA,KAAK,EAAC,kCAAX;AAAA,gBAAgDA;AAAhD,MATnB;AAAA,IADF;AAaD;AAED,OAAO,SAASG,QAAT,CAAkBC,IAAlB,EAAwB;AAC7B,SAAOA,IAAI,IAAI,CAAC,CAACA,IAAI,CAACf,KAAtB;AACD,C,CAGD;;AAEA,SAASM,QAAT,CAAkBX,EAAlB,EAAsB;AACpB,SAAQ,wBAAwBA,EAAI,EAApC;AACD","sourcesContent":["import {\n useMemo\n} from 'preact/hooks';\n\nimport classnames from 'classnames';\n\nfunction TextArea(props) {\n\n const {\n id,\n label,\n rows = 2,\n debounce,\n onInput,\n value = '',\n monospace\n } = props;\n\n const handleInput = useMemo(() => {\n return debounce(({ target }) => onInput(target.value.length ? target.value : undefined));\n }, [ onInput, debounce ]);\n\n return (\n <div class=\"bio-properties-panel-textarea\">\n <label for={ prefixId(id) } class=\"bio-properties-panel-label\">{ label }</label>\n <textarea\n id={ prefixId(id) }\n name={ id }\n spellCheck=\"false\"\n class={ classnames(\n 'bio-properties-panel-input',\n monospace ? 'bio-properties-panel-input-monospace' : '')\n }\n onInput={ handleInput }\n onFocus={ props.onFocus }\n onBlur={ props.onBlur }\n rows={ rows }\n value={ value } />\n </div>\n );\n}\n\n/**\n * @param {Object} props\n * @param {Object} props.element\n * @param {String} props.id\n * @param {String} props.description\n * @param {Boolean} props.debounce\n * @param {String} props.label\n * @param {Function} props.getValue\n * @param {Function} props.setValue\n * @param {Number} props.rows\n * @param {Boolean} props.monospace\n */\nexport default function TextAreaEntry(props) {\n const {\n element,\n id,\n description,\n debounce,\n label,\n getValue,\n setValue,\n rows,\n monospace\n } = props;\n\n const value = getValue(element);\n return (\n <div class=\"bio-properties-panel-entry\" data-entry-id={ id }>\n <TextArea\n id={ id }\n label={ label }\n value={ value }\n onInput={ setValue }\n rows={ rows }\n debounce={ debounce }\n monospace={ monospace } />\n { description && <div class=\"bio-properties-panel-description\">{ description }</div> }\n </div>\n );\n}\n\nexport function isEdited(node) {\n return node && !!node.value;\n}\n\n\n// helpers /////////////////\n\nfunction prefixId(id) {\n return `bio-properties-panel-${ id }`;\n}"],"file":"TextArea.js"}
@@ -1,2 +1,3 @@
1
1
  export { default as usePrevious } from './usePrevious';
2
+ export { useKeyFactory } from './useKeyFactory';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/index.js"],"names":["default","usePrevious"],"mappings":"AAAA,SAASA,OAAO,IAAIC,WAApB,QAAuC,eAAvC","sourcesContent":["export { default as usePrevious } from './usePrevious';"],"file":"index.js"}
1
+ {"version":3,"sources":["../../src/hooks/index.js"],"names":["default","usePrevious","useKeyFactory"],"mappings":"AAAA,SAASA,OAAO,IAAIC,WAApB,QAAuC,eAAvC;AACA,SAASC,aAAT,QAA8B,iBAA9B","sourcesContent":["export { default as usePrevious } from './usePrevious';\nexport { useKeyFactory } from './useKeyFactory';"],"file":"index.js"}
@@ -0,0 +1,39 @@
1
+ import { useMemo } from 'preact/hooks';
2
+ const KEY_LENGTH = 6;
3
+ /**
4
+ * Create a persistent key factory for plain objects without id.
5
+ *
6
+ * @example
7
+ * ```jsx
8
+ * function List({ objects }) {
9
+ * const getKey = useKeyFactory();
10
+ * return (<ol>{
11
+ * objects.map(obj => {
12
+ * const key = getKey(obj);
13
+ * return <li key={key}>obj.name</li>
14
+ * })
15
+ * }</ol>);
16
+ * }
17
+ * ```
18
+ *
19
+ * @param {any[]} dependencies
20
+ * @returns {(element: object) => string}
21
+ */
22
+
23
+ export function useKeyFactory(dependencies = []) {
24
+ const map = useMemo(() => new Map(), dependencies);
25
+
26
+ const getKey = el => {
27
+ let key = map.get(el);
28
+
29
+ if (!key) {
30
+ key = Math.random().toString().slice(-KEY_LENGTH);
31
+ map.set(el, key);
32
+ }
33
+
34
+ return key;
35
+ };
36
+
37
+ return getKey;
38
+ }
39
+ //# sourceMappingURL=useKeyFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/useKeyFactory.js"],"names":["useMemo","KEY_LENGTH","useKeyFactory","dependencies","map","Map","getKey","el","key","get","Math","random","toString","slice","set"],"mappings":"AAAA,SAASA,OAAT,QAAwB,cAAxB;AAEA,MAAMC,UAAU,GAAG,CAAnB;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,OAAO,SAASC,aAAT,CAAuBC,YAAY,GAAG,EAAtC,EAA0C;AAC/C,QAAMC,GAAG,GAAGJ,OAAO,CAAC,MAAM,IAAIK,GAAJ,EAAP,EAAkBF,YAAlB,CAAnB;;AAEA,QAAMG,MAAM,GAAGC,EAAE,IAAI;AACnB,QAAIC,GAAG,GAAGJ,GAAG,CAACK,GAAJ,CAAQF,EAAR,CAAV;;AAEA,QAAI,CAACC,GAAL,EAAU;AACRA,MAAAA,GAAG,GAAGE,IAAI,CAACC,MAAL,GAAcC,QAAd,GAAyBC,KAAzB,CAA+B,CAACZ,UAAhC,CAAN;AACAG,MAAAA,GAAG,CAACU,GAAJ,CAAQP,EAAR,EAAYC,GAAZ;AACD;;AAED,WAAOA,GAAP;AACD,GATD;;AAWA,SAAOF,MAAP;AACD","sourcesContent":["import { useMemo } from 'preact/hooks';\n\nconst KEY_LENGTH = 6;\n\n/**\n * Create a persistent key factory for plain objects without id.\n *\n * @example\n * ```jsx\n * function List({ objects }) {\n * const getKey = useKeyFactory();\n * return (<ol>{\n * objects.map(obj => {\n * const key = getKey(obj);\n * return <li key={key}>obj.name</li>\n * })\n * }</ol>);\n * }\n * ```\n *\n * @param {any[]} dependencies\n * @returns {(element: object) => string}\n */\nexport function useKeyFactory(dependencies = []) {\n const map = useMemo(() => new Map(), dependencies);\n\n const getKey = el => {\n let key = map.get(el);\n\n if (!key) {\n key = Math.random().toString().slice(-KEY_LENGTH);\n map.set(el, key);\n }\n\n return key;\n };\n\n return getKey;\n}\n"],"file":"useKeyFactory.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bpmn-io/properties-panel",
3
- "version": "0.2.0",
3
+ "version": "0.4.1",
4
4
  "description": "Library for creating bpmn-io properties panels.",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -10,6 +10,7 @@
10
10
  "scripts": {
11
11
  "all": "run-s lint test",
12
12
  "build": "del-cli lib && babel -s -d lib src",
13
+ "build:watch": "babel -w -s -d lib src",
13
14
  "lint": "eslint .",
14
15
  "dev": "npm test -- --auto-watch --no-single-run",
15
16
  "test": "karma start karma.config.js",
@@ -41,6 +42,7 @@
41
42
  "@babel/core": "^7.14.3",
42
43
  "@babel/plugin-transform-react-jsx": "^7.14.3",
43
44
  "@testing-library/preact": "^2.0.1",
45
+ "@testing-library/preact-hooks": "^1.1.0",
44
46
  "babel-loader": "^8.2.2",
45
47
  "babel-plugin-inline-react-svg": "^2.0.1",
46
48
  "chai": "^4.3.4",