@bpmn-io/properties-panel 0.8.1 → 0.10.2

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.
Files changed (175) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/assets/properties-panel.css +5 -17
  3. package/dist/index.esm.js +1610 -0
  4. package/dist/index.esm.js.map +1 -0
  5. package/dist/index.js +1651 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +17 -7
  8. package/preact/LICENSE +21 -0
  9. package/preact/README.md +185 -0
  10. package/preact/compat/dist/compat.js +2 -0
  11. package/preact/compat/dist/compat.js.map +1 -0
  12. package/preact/compat/dist/compat.mjs +2 -0
  13. package/preact/compat/dist/compat.module.js +2 -0
  14. package/preact/compat/dist/compat.module.js.map +1 -0
  15. package/preact/compat/dist/compat.umd.js +2 -0
  16. package/preact/compat/dist/compat.umd.js.map +1 -0
  17. package/preact/compat/jsx-dev-runtime.js +1 -0
  18. package/preact/compat/jsx-dev-runtime.mjs +1 -0
  19. package/preact/compat/jsx-runtime.js +1 -0
  20. package/preact/compat/jsx-runtime.mjs +1 -0
  21. package/preact/compat/package.json +19 -0
  22. package/preact/compat/server.js +15 -0
  23. package/preact/compat/server.mjs +4 -0
  24. package/preact/compat/src/Children.js +21 -0
  25. package/preact/compat/src/PureComponent.js +15 -0
  26. package/preact/compat/src/forwardRef.js +51 -0
  27. package/preact/compat/src/index.d.ts +140 -0
  28. package/preact/compat/src/index.js +175 -0
  29. package/preact/compat/src/internal.d.ts +47 -0
  30. package/preact/compat/src/memo.js +34 -0
  31. package/preact/compat/src/portals.js +80 -0
  32. package/preact/compat/src/render.js +219 -0
  33. package/preact/compat/src/scheduler.js +24 -0
  34. package/preact/compat/src/suspense-list.d.ts +14 -0
  35. package/preact/compat/src/suspense-list.js +126 -0
  36. package/preact/compat/src/suspense.d.ts +15 -0
  37. package/preact/compat/src/suspense.js +270 -0
  38. package/preact/compat/src/util.js +28 -0
  39. package/preact/compat/test-utils.js +1 -0
  40. package/preact/debug/dist/debug.js +2 -0
  41. package/preact/debug/dist/debug.js.map +1 -0
  42. package/preact/debug/dist/debug.mjs +2 -0
  43. package/preact/debug/dist/debug.module.js +2 -0
  44. package/preact/debug/dist/debug.module.js.map +1 -0
  45. package/preact/debug/dist/debug.umd.js +2 -0
  46. package/preact/debug/dist/debug.umd.js.map +1 -0
  47. package/preact/debug/package.json +18 -0
  48. package/preact/debug/src/check-props.js +54 -0
  49. package/preact/debug/src/component-stack.js +146 -0
  50. package/preact/debug/src/constants.js +3 -0
  51. package/preact/debug/src/debug.js +442 -0
  52. package/preact/debug/src/index.js +6 -0
  53. package/preact/debug/src/internal.d.ts +82 -0
  54. package/preact/debug/src/util.js +11 -0
  55. package/preact/devtools/dist/devtools.js +2 -0
  56. package/preact/devtools/dist/devtools.js.map +1 -0
  57. package/preact/devtools/dist/devtools.mjs +2 -0
  58. package/preact/devtools/dist/devtools.module.js +2 -0
  59. package/preact/devtools/dist/devtools.module.js.map +1 -0
  60. package/preact/devtools/dist/devtools.umd.js +2 -0
  61. package/preact/devtools/dist/devtools.umd.js.map +1 -0
  62. package/preact/devtools/package.json +16 -0
  63. package/preact/devtools/src/devtools.js +10 -0
  64. package/preact/devtools/src/index.d.ts +8 -0
  65. package/preact/devtools/src/index.js +15 -0
  66. package/preact/dist/preact.js +2 -0
  67. package/preact/dist/preact.js.map +1 -0
  68. package/preact/dist/preact.min.js +2 -0
  69. package/preact/dist/preact.min.js.map +1 -0
  70. package/preact/dist/preact.mjs +2 -0
  71. package/preact/dist/preact.module.js +2 -0
  72. package/preact/dist/preact.module.js.map +1 -0
  73. package/preact/dist/preact.umd.js +2 -0
  74. package/preact/dist/preact.umd.js.map +1 -0
  75. package/preact/hooks/dist/hooks.js +2 -0
  76. package/preact/hooks/dist/hooks.js.map +1 -0
  77. package/preact/hooks/dist/hooks.mjs +2 -0
  78. package/preact/hooks/dist/hooks.module.js +2 -0
  79. package/preact/hooks/dist/hooks.module.js.map +1 -0
  80. package/preact/hooks/dist/hooks.umd.js +2 -0
  81. package/preact/hooks/dist/hooks.umd.js.map +1 -0
  82. package/preact/hooks/package.json +26 -0
  83. package/preact/hooks/src/index.d.ts +130 -0
  84. package/preact/hooks/src/index.js +386 -0
  85. package/preact/hooks/src/internal.d.ts +75 -0
  86. package/preact/jsx-runtime/dist/jsxRuntime.js +2 -0
  87. package/preact/jsx-runtime/dist/jsxRuntime.js.map +1 -0
  88. package/preact/jsx-runtime/dist/jsxRuntime.mjs +2 -0
  89. package/preact/jsx-runtime/dist/jsxRuntime.module.js +2 -0
  90. package/preact/jsx-runtime/dist/jsxRuntime.module.js.map +1 -0
  91. package/preact/jsx-runtime/dist/jsxRuntime.umd.js +2 -0
  92. package/preact/jsx-runtime/dist/jsxRuntime.umd.js.map +1 -0
  93. package/preact/jsx-runtime/package.json +19 -0
  94. package/preact/jsx-runtime/src/index.d.ts +50 -0
  95. package/preact/jsx-runtime/src/index.js +72 -0
  96. package/preact/package.json +268 -0
  97. package/preact/src/cjs.js +3 -0
  98. package/preact/src/clone-element.js +39 -0
  99. package/preact/src/component.js +225 -0
  100. package/preact/src/constants.js +3 -0
  101. package/preact/src/create-context.js +68 -0
  102. package/preact/src/create-element.js +100 -0
  103. package/preact/src/diff/catch-error.js +38 -0
  104. package/preact/src/diff/children.js +347 -0
  105. package/preact/src/diff/index.js +516 -0
  106. package/preact/src/diff/props.js +158 -0
  107. package/preact/src/index.d.ts +310 -0
  108. package/preact/src/index.js +13 -0
  109. package/preact/src/internal.d.ts +147 -0
  110. package/preact/src/jsx.d.ts +955 -0
  111. package/preact/src/options.js +17 -0
  112. package/preact/src/render.js +74 -0
  113. package/preact/src/util.js +23 -0
  114. package/preact/test-utils/dist/testUtils.js +2 -0
  115. package/preact/test-utils/dist/testUtils.js.map +1 -0
  116. package/preact/test-utils/dist/testUtils.mjs +2 -0
  117. package/preact/test-utils/dist/testUtils.module.js +2 -0
  118. package/preact/test-utils/dist/testUtils.module.js.map +1 -0
  119. package/preact/test-utils/dist/testUtils.umd.js +2 -0
  120. package/preact/test-utils/dist/testUtils.umd.js.map +1 -0
  121. package/preact/test-utils/package.json +19 -0
  122. package/preact/test-utils/src/index.d.ts +3 -0
  123. package/preact/test-utils/src/index.js +117 -0
  124. package/lib/PropertiesPanel.js +0 -126
  125. package/lib/PropertiesPanel.js.map +0 -1
  126. package/lib/components/DropdownButton.js +0 -109
  127. package/lib/components/DropdownButton.js.map +0 -1
  128. package/lib/components/Group.js +0 -75
  129. package/lib/components/Group.js.map +0 -1
  130. package/lib/components/Header.js +0 -49
  131. package/lib/components/Header.js.map +0 -1
  132. package/lib/components/HeaderButton.js +0 -16
  133. package/lib/components/HeaderButton.js.map +0 -1
  134. package/lib/components/ListGroup.js +0 -167
  135. package/lib/components/ListGroup.js.map +0 -1
  136. package/lib/components/ListItem.js +0 -37
  137. package/lib/components/ListItem.js.map +0 -1
  138. package/lib/components/entries/Checkbox.js +0 -81
  139. package/lib/components/entries/Checkbox.js.map +0 -1
  140. package/lib/components/entries/Collapsible.js +0 -48
  141. package/lib/components/entries/Collapsible.js.map +0 -1
  142. package/lib/components/entries/List.js +0 -204
  143. package/lib/components/entries/List.js.map +0 -1
  144. package/lib/components/entries/NumberField.js +0 -108
  145. package/lib/components/entries/NumberField.js.map +0 -1
  146. package/lib/components/entries/Select.js +0 -107
  147. package/lib/components/entries/Select.js.map +0 -1
  148. package/lib/components/entries/Simple.js +0 -56
  149. package/lib/components/entries/Simple.js.map +0 -1
  150. package/lib/components/entries/TextArea.js +0 -96
  151. package/lib/components/entries/TextArea.js.map +0 -1
  152. package/lib/components/entries/TextField.js +0 -121
  153. package/lib/components/entries/TextField.js.map +0 -1
  154. package/lib/components/entries/ToggleSwitch.js +0 -87
  155. package/lib/components/entries/ToggleSwitch.js.map +0 -1
  156. package/lib/components/icons/index.js +0 -51
  157. package/lib/components/icons/index.js.map +0 -1
  158. package/lib/context/LayoutContext.js +0 -9
  159. package/lib/context/LayoutContext.js.map +0 -1
  160. package/lib/context/index.js +0 -2
  161. package/lib/context/index.js.map +0 -1
  162. package/lib/features/debounce-input/debounceInput.js +0 -14
  163. package/lib/features/debounce-input/debounceInput.js.map +0 -1
  164. package/lib/features/debounce-input/index.js +0 -5
  165. package/lib/features/debounce-input/index.js.map +0 -1
  166. package/lib/hooks/index.js +0 -4
  167. package/lib/hooks/index.js.map +0 -1
  168. package/lib/hooks/useKeyFactory.js +0 -39
  169. package/lib/hooks/useKeyFactory.js.map +0 -1
  170. package/lib/hooks/useLayoutState.js +0 -36
  171. package/lib/hooks/useLayoutState.js.map +0 -1
  172. package/lib/hooks/usePrevious.js +0 -16
  173. package/lib/hooks/usePrevious.js.map +0 -1
  174. package/lib/index.js +0 -2
  175. package/lib/index.js.map +0 -1
@@ -0,0 +1,1610 @@
1
+ import { useRef, useEffect, useMemo, useContext, useState } from '../preact/hooks';
2
+ import { isFunction, get, assign, set, sortBy, find, isNumber, debounce } from 'min-dash';
3
+ import classnames from 'classnames';
4
+ import { jsxs, jsx } from '../preact/jsx-runtime';
5
+ import { query } from 'min-dom';
6
+ import { createContext } from '../preact';
7
+ import '../preact/compat';
8
+
9
+ /**
10
+ * @typedef { { getElementLabel: Function, getTypeLabel: Function, getElementIcon: Function } } HeaderProvider
11
+ */
12
+
13
+ /**
14
+ * @param {Object} props
15
+ * @param {Object} props.element,
16
+ * @param {HeaderProvider} props.headerProvider
17
+ */
18
+ function Header(props) {
19
+ const {
20
+ element,
21
+ headerProvider
22
+ } = props;
23
+ const {
24
+ getElementLabel,
25
+ getTypeLabel,
26
+ getElementIcon
27
+ } = headerProvider;
28
+ const label = getElementLabel(element);
29
+ const type = getTypeLabel(element);
30
+ const ElementIcon = getElementIcon(element);
31
+ return jsxs("div", {
32
+ class: "bio-properties-panel-header",
33
+ children: [jsx("div", {
34
+ class: "bio-properties-panel-header-icon",
35
+ children: ElementIcon && jsx(ElementIcon, {
36
+ width: "32",
37
+ height: "32",
38
+ viewBox: "0 0 32 32"
39
+ })
40
+ }), jsxs("div", {
41
+ class: "bio-properties-panel-header-labels",
42
+ children: [jsx("div", {
43
+ title: type,
44
+ class: "bio-properties-panel-header-type",
45
+ children: type
46
+ }), getElementLabel(element) ? jsx("div", {
47
+ title: label,
48
+ class: "bio-properties-panel-header-label",
49
+ children: label
50
+ }) : null]
51
+ })]
52
+ });
53
+ }
54
+
55
+ /**
56
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
57
+ * state on updates.
58
+ *
59
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
60
+ */
61
+
62
+ function usePrevious(value) {
63
+ const ref = useRef();
64
+ useEffect(() => {
65
+ ref.current = value;
66
+ });
67
+ return ref.current;
68
+ }
69
+
70
+ const KEY_LENGTH = 6;
71
+ /**
72
+ * Create a persistent key factory for plain objects without id.
73
+ *
74
+ * @example
75
+ * ```jsx
76
+ * function List({ objects }) {
77
+ * const getKey = useKeyFactory();
78
+ * return (<ol>{
79
+ * objects.map(obj => {
80
+ * const key = getKey(obj);
81
+ * return <li key={key}>obj.name</li>
82
+ * })
83
+ * }</ol>);
84
+ * }
85
+ * ```
86
+ *
87
+ * @param {any[]} dependencies
88
+ * @returns {(element: object) => string}
89
+ */
90
+
91
+ function useKeyFactory(dependencies = []) {
92
+ const map = useMemo(() => new Map(), dependencies);
93
+
94
+ const getKey = el => {
95
+ let key = map.get(el);
96
+
97
+ if (!key) {
98
+ key = Math.random().toString().slice(-KEY_LENGTH);
99
+ map.set(el, key);
100
+ }
101
+
102
+ return key;
103
+ };
104
+
105
+ return getKey;
106
+ }
107
+
108
+ const DescriptionContext = createContext({
109
+ description: {},
110
+ getDescriptionForId: () => {}
111
+ });
112
+
113
+ const LayoutContext = createContext({
114
+ layout: {},
115
+ setLayout: () => {},
116
+ getLayoutForKey: () => {},
117
+ setLayoutForKey: () => {}
118
+ });
119
+
120
+ /**
121
+ * Creates a state that persists in the global LayoutContext.
122
+ *
123
+ * @example
124
+ * ```jsx
125
+ * function Group(props) {
126
+ * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
127
+ * }
128
+ * ```
129
+ *
130
+ * @param {(string|number)[]} path
131
+ * @param {any} [defaultValue]
132
+ *
133
+ * @returns {[ any, Function ]}
134
+ */
135
+
136
+ function useLayoutState(path, defaultValue) {
137
+ const {
138
+ getLayoutForKey,
139
+ setLayoutForKey
140
+ } = useContext(LayoutContext);
141
+ const layoutForKey = getLayoutForKey(path, defaultValue);
142
+ const [value, set] = useState(layoutForKey);
143
+
144
+ const setState = newValue => {
145
+ // (1) set component state
146
+ set(newValue); // (2) set context
147
+
148
+ setLayoutForKey(path, newValue);
149
+ };
150
+
151
+ return [value, setState];
152
+ }
153
+
154
+ /**
155
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
156
+ *
157
+ * @example
158
+ * ```jsx
159
+ * function TextField(props) {
160
+ * const description = useDescriptionContext('input1', element);
161
+ * }
162
+ * ```
163
+ *
164
+ * @param {string} id
165
+ * @param {djs.model.Base} element
166
+ *
167
+ * @returns {string}
168
+ */
169
+
170
+ function useDescriptionContext(id, element) {
171
+ const {
172
+ getDescriptionForId
173
+ } = useContext(DescriptionContext);
174
+ return getDescriptionForId(id, element);
175
+ }
176
+
177
+ var ArrowIcon = function ArrowIcon(props) {
178
+ return jsx("svg", { ...props,
179
+ children: jsx("path", {
180
+ fillRule: "evenodd",
181
+ d: "m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8z"
182
+ })
183
+ });
184
+ };
185
+
186
+ ArrowIcon.defaultProps = {
187
+ xmlns: "http://www.w3.org/2000/svg",
188
+ width: "16",
189
+ height: "16"
190
+ };
191
+
192
+ var CreateIcon = function CreateIcon(props) {
193
+ return jsx("svg", { ...props,
194
+ children: jsx("path", {
195
+ fillRule: "evenodd",
196
+ d: "M9 13V9h4a1 1 0 0 0 0-2H9V3a1 1 0 1 0-2 0v4H3a1 1 0 1 0 0 2h4v4a1 1 0 0 0 2 0z"
197
+ })
198
+ });
199
+ };
200
+
201
+ CreateIcon.defaultProps = {
202
+ xmlns: "http://www.w3.org/2000/svg",
203
+ width: "16",
204
+ height: "16"
205
+ };
206
+
207
+ var DeleteIcon = function DeleteIcon(props) {
208
+ return jsx("svg", { ...props,
209
+ children: jsx("path", {
210
+ fillRule: "evenodd",
211
+ d: "M12 6v7c0 1.1-.4 1.55-1.5 1.55h-5C4.4 14.55 4 14.1 4 13V6h8zm-1.5 1.5h-5v4.3c0 .66.5 1.2 1.111 1.2H9.39c.611 0 1.111-.54 1.111-1.2V7.5zM13 3h-2l-1-1H6L5 3H3v1.5h10V3z"
212
+ })
213
+ });
214
+ };
215
+
216
+ DeleteIcon.defaultProps = {
217
+ xmlns: "http://www.w3.org/2000/svg",
218
+ width: "16",
219
+ height: "16"
220
+ };
221
+
222
+ function Group(props) {
223
+ const {
224
+ id,
225
+ entries = [],
226
+ label
227
+ } = props;
228
+ const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
229
+
230
+ const toggleOpen = () => setOpen(!open);
231
+
232
+ const [edited, setEdited] = useState(false); // set edited state depending on all entries
233
+
234
+ useEffect(() => {
235
+ const hasOneEditedEntry = entries.find(entry => {
236
+ const {
237
+ id,
238
+ isEdited
239
+ } = entry;
240
+ const entryNode = query(`[data-entry-id="${id}"]`);
241
+
242
+ if (!isFunction(isEdited) || !entryNode) {
243
+ return false;
244
+ }
245
+
246
+ const inputNode = query('.bio-properties-panel-input', entryNode);
247
+ return isEdited(inputNode);
248
+ });
249
+ setEdited(hasOneEditedEntry);
250
+ }, [entries]);
251
+ return jsxs("div", {
252
+ class: "bio-properties-panel-group",
253
+ "data-group-id": 'group-' + id,
254
+ children: [jsxs("div", {
255
+ class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : ''),
256
+ onClick: toggleOpen,
257
+ children: [jsx("div", {
258
+ title: label,
259
+ class: "bio-properties-panel-group-header-title",
260
+ children: label
261
+ }), jsxs("div", {
262
+ class: "bio-properties-panel-group-header-buttons",
263
+ children: [edited && jsx(DataMarker, {}), jsx("button", {
264
+ title: "Toggle section",
265
+ class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
266
+ children: jsx(ArrowIcon, {
267
+ class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
268
+ })
269
+ })]
270
+ })]
271
+ }), jsx("div", {
272
+ class: classnames('bio-properties-panel-group-entries', open ? 'open' : ''),
273
+ children: entries.map(e => e.component)
274
+ })]
275
+ });
276
+ }
277
+
278
+ function DataMarker() {
279
+ return jsx("div", {
280
+ title: "Section contains data",
281
+ class: "bio-properties-panel-dot"
282
+ });
283
+ }
284
+
285
+ const DEFAULT_LAYOUT = {
286
+ open: true
287
+ };
288
+ const DEFAULT_DESCRIPTION = {};
289
+ /**
290
+ * @typedef { {
291
+ * component: import('preact').ComponentChild,
292
+ * id: String,
293
+ * isEdited?: Function
294
+ * } } EntryDefinition
295
+ *
296
+ * @typedef { {
297
+ * autoFocusEntry: String,
298
+ * autoOpen?: Boolean,
299
+ * entries: Array<EntryDefinition>,
300
+ * id: String,
301
+ * label: String,
302
+ * remove: (event: MouseEvent) => void
303
+ * } } ListItemDefinition
304
+ *
305
+ * @typedef { {
306
+ * add: (event: MouseEvent) => void,
307
+ * component: import('preact').Component,
308
+ * element: Object,
309
+ * id: String,
310
+ * items: Array<ListItemDefinition>,
311
+ * label: String,
312
+ * shouldSort?: Boolean,
313
+ * shouldOpen?: Boolean
314
+ * } } ListGroupDefinition
315
+ *
316
+ * @typedef { {
317
+ * component?: import('preact').Component,
318
+ * entries: Array<EntryDefinition>,
319
+ * id: String,
320
+ * label: String
321
+ * } } GroupDefinition
322
+ *
323
+ * @typedef { {
324
+ * [id: String]: GetDescriptionFunction
325
+ * } } DescriptionConfig
326
+ *
327
+ * @callback { {
328
+ * @param {string} id
329
+ * @param {djs.model.base} element
330
+ * @returns {string}
331
+ * } } GetDescriptionFunction
332
+ *
333
+ */
334
+
335
+ /**
336
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
337
+ * data from implementor to describe *what* will be rendered.
338
+ *
339
+ * @param {Object} props
340
+ * @param {Object} props.element
341
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
342
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
343
+ * @param {Object} [props.layoutConfig]
344
+ * @param {Function} [props.layoutChanged]
345
+ * @param {DescriptionConfig} [props.descriptionConfig]
346
+ * @param {Function} [props.descriptionLoaded]
347
+ */
348
+
349
+ function PropertiesPanel(props) {
350
+ const {
351
+ element,
352
+ headerProvider,
353
+ groups,
354
+ layoutConfig = {},
355
+ layoutChanged,
356
+ descriptionConfig = {},
357
+ descriptionLoaded
358
+ } = props; // set-up layout context
359
+
360
+ const [layout, setLayout] = useState(createLayout(layoutConfig));
361
+ useEffect(() => {
362
+ if (typeof layoutChanged === 'function') {
363
+ layoutChanged(layout);
364
+ }
365
+ }, [layout, layoutChanged]);
366
+
367
+ const getLayoutForKey = (key, defaultValue) => {
368
+ return get(layout, key, defaultValue);
369
+ };
370
+
371
+ const setLayoutForKey = (key, config) => {
372
+ const newLayout = assign({}, layout);
373
+ set(newLayout, key, config);
374
+ setLayout(newLayout);
375
+ };
376
+
377
+ const layoutContext = {
378
+ layout,
379
+ setLayout,
380
+ getLayoutForKey,
381
+ setLayoutForKey
382
+ }; // set-up description context
383
+
384
+ const description = createDescriptionContext(descriptionConfig);
385
+
386
+ if (typeof descriptionLoaded === 'function') {
387
+ descriptionLoaded(description);
388
+ }
389
+
390
+ const getDescriptionForId = (id, element) => {
391
+ return description[id] && description[id](element);
392
+ };
393
+
394
+ const descriptionContext = {
395
+ description,
396
+ getDescriptionForId
397
+ };
398
+
399
+ if (!element) {
400
+ return jsx("div", {
401
+ class: "bio-properties-panel-placeholder",
402
+ children: "Select an element to edit its properties."
403
+ });
404
+ }
405
+
406
+ return jsx(DescriptionContext.Provider, {
407
+ value: descriptionContext,
408
+ children: jsx(LayoutContext.Provider, {
409
+ value: layoutContext,
410
+ children: jsxs("div", {
411
+ class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
412
+ children: [jsx(Header, {
413
+ element: element,
414
+ headerProvider: headerProvider
415
+ }), jsx("div", {
416
+ class: "bio-properties-panel-scroll-container",
417
+ children: groups.map(group => {
418
+ const {
419
+ component: GroupComponent = Group,
420
+ id
421
+ } = group;
422
+ return jsx(GroupComponent, {
423
+ element: element,
424
+ ...group
425
+ }, id);
426
+ })
427
+ })]
428
+ })
429
+ })
430
+ });
431
+ } // helpers //////////////////
432
+
433
+ function createLayout(overrides) {
434
+ return { ...DEFAULT_LAYOUT,
435
+ ...overrides
436
+ };
437
+ }
438
+
439
+ function createDescriptionContext(overrides) {
440
+ return { ...DEFAULT_DESCRIPTION,
441
+ ...overrides
442
+ };
443
+ }
444
+
445
+ function DropdownButton(props) {
446
+ const {
447
+ class: className,
448
+ children,
449
+ menuItems = []
450
+ } = props;
451
+ const dropdownRef = useRef(null);
452
+ const menuRef = useRef(null);
453
+ const [open, setOpen] = useState(false);
454
+
455
+ const close = () => setOpen(false);
456
+
457
+ function onDropdownToggle(event) {
458
+ if (menuRef.current && menuRef.current.contains(event.target)) {
459
+ return;
460
+ }
461
+
462
+ event.stopPropagation();
463
+ setOpen(open => !open);
464
+ }
465
+
466
+ function onActionClick(event, action) {
467
+ event.stopPropagation();
468
+ close();
469
+ action();
470
+ }
471
+
472
+ useGlobalClick([dropdownRef.current], () => close());
473
+ return jsxs("div", {
474
+ class: classnames('bio-properties-panel-dropdown-button', {
475
+ open
476
+ }, className),
477
+ onClick: onDropdownToggle,
478
+ ref: dropdownRef,
479
+ children: [children, jsx("div", {
480
+ class: "bio-properties-panel-dropdown-button__menu",
481
+ ref: menuRef,
482
+ children: menuItems.map((item, index) => jsx(MenuItem, {
483
+ onClick: onActionClick,
484
+ item: item
485
+ }, index))
486
+ })]
487
+ });
488
+ }
489
+
490
+ function MenuItem({
491
+ item,
492
+ onClick
493
+ }) {
494
+ if (item.separator) {
495
+ return jsx("div", {
496
+ class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--separator"
497
+ });
498
+ }
499
+
500
+ if (item.action) {
501
+ return jsx("button", {
502
+ class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--actionable",
503
+ onClick: event => onClick(event, item.action),
504
+ children: item.entry
505
+ });
506
+ }
507
+
508
+ return jsx("div", {
509
+ class: "bio-properties-panel-dropdown-button__menu-item",
510
+ children: item.entry
511
+ });
512
+ }
513
+ /**
514
+ *
515
+ * @param {Array<null | Element>} ignoredElements
516
+ * @param {Function} callback
517
+ */
518
+
519
+
520
+ function useGlobalClick(ignoredElements, callback) {
521
+ useEffect(() => {
522
+ /**
523
+ * @param {MouseEvent} event
524
+ */
525
+ function listener(event) {
526
+ if (ignoredElements.some(element => element && element.contains(event.target))) {
527
+ return;
528
+ }
529
+
530
+ callback();
531
+ }
532
+
533
+ document.addEventListener('click', listener, {
534
+ capture: true
535
+ });
536
+ return () => document.removeEventListener('click', listener, {
537
+ capture: true
538
+ });
539
+ }, [...ignoredElements, callback]);
540
+ }
541
+
542
+ function HeaderButton(props) {
543
+ const {
544
+ children = null,
545
+ class: classname,
546
+ onClick = () => {},
547
+ ...otherProps
548
+ } = props;
549
+ return jsx("button", { ...otherProps,
550
+ onClick: onClick,
551
+ class: classnames('bio-properties-panel-group-header-button', classname),
552
+ children: children
553
+ });
554
+ }
555
+
556
+ function CollapsibleEntry(props) {
557
+ const {
558
+ id,
559
+ entries = [],
560
+ label,
561
+ remove,
562
+ open: shouldOpen
563
+ } = props;
564
+ const [open, setOpen] = useState(shouldOpen);
565
+
566
+ const toggleOpen = () => setOpen(!open); // todo(pinussilvestrus): translate once we have a translate mechanism for the core
567
+
568
+
569
+ const placeholderLabel = '<empty>';
570
+ return jsxs("div", {
571
+ "data-entry-id": id,
572
+ class: classnames('bio-properties-panel-collapsible-entry', open ? 'open' : ''),
573
+ children: [jsxs("div", {
574
+ class: "bio-properties-panel-collapsible-entry-header",
575
+ onClick: toggleOpen,
576
+ children: [jsx("div", {
577
+ title: label || placeholderLabel,
578
+ class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
579
+ children: label || placeholderLabel
580
+ }), jsx("button", {
581
+ title: "Toggle list item",
582
+ class: "bio-properties-panel-arrow bio-properties-panel-collapsible-entry-arrow",
583
+ children: jsx(ArrowIcon, {
584
+ class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
585
+ })
586
+ }), remove ? jsx("button", {
587
+ title: "Delete item",
588
+ class: "bio-properties-panel-remove-entry",
589
+ onClick: remove,
590
+ children: jsx(DeleteIcon, {})
591
+ }) : null]
592
+ }), jsx("div", {
593
+ class: classnames('bio-properties-panel-collapsible-entry-entries', open ? 'open' : ''),
594
+ children: entries.map(e => e.component)
595
+ })]
596
+ });
597
+ }
598
+
599
+ function ListItem(props) {
600
+ const {
601
+ autoOpen,
602
+ autoFocusEntry
603
+ } = props; // focus specified entry on auto open
604
+
605
+ useEffect(() => {
606
+ if (autoOpen && autoFocusEntry) {
607
+ const entry = query(`[data-entry-id="${autoFocusEntry}"]`);
608
+ const focusableInput = query('.bio-properties-panel-input', entry);
609
+
610
+ if (focusableInput) {
611
+ if (isFunction(focusableInput.select)) {
612
+ focusableInput.select();
613
+ } else if (isFunction(focusableInput.focus)) {
614
+ focusableInput.focus();
615
+ }
616
+ }
617
+ }
618
+ }, [autoOpen, autoFocusEntry]);
619
+ return jsx("div", {
620
+ class: "bio-properties-panel-list-item",
621
+ children: jsx(CollapsibleEntry, { ...props,
622
+ open: autoOpen
623
+ })
624
+ });
625
+ }
626
+
627
+ const noop = () => {};
628
+ /**
629
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
630
+ */
631
+
632
+
633
+ function ListGroup(props) {
634
+ const {
635
+ element,
636
+ id,
637
+ items,
638
+ label,
639
+ add,
640
+ shouldSort = true,
641
+ shouldOpen = true
642
+ } = props;
643
+ const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
644
+ const [ordering, setOrdering] = useState([]);
645
+ const [newItemAdded, setNewItemAdded] = useState(false);
646
+ const prevItems = usePrevious(items);
647
+ const prevElement = usePrevious(element);
648
+ const elementChanged = element !== prevElement;
649
+ const shouldHandleEffects = !elementChanged && (shouldSort || shouldOpen); // reset initial ordering when element changes (before first render)
650
+
651
+ if (elementChanged) {
652
+ setOrdering(createOrdering(shouldSort ? sortItems(items) : items));
653
+ } // keep ordering in sync to items - and open changes
654
+ // (0) set initial ordering from given items
655
+
656
+
657
+ useEffect(() => {
658
+ if (!prevItems || !shouldSort) {
659
+ setOrdering(createOrdering(items));
660
+ }
661
+ }, [items, element]); // (1) items were added
662
+
663
+ useEffect(() => {
664
+ if (shouldHandleEffects && prevItems && items.length > prevItems.length) {
665
+ let add = [];
666
+ items.forEach(item => {
667
+ if (!ordering.includes(item.id)) {
668
+ add.push(item.id);
669
+ }
670
+ });
671
+ let newOrdering = ordering; // open if not open and configured
672
+
673
+ if (!open && shouldOpen) {
674
+ toggleOpen(); // if I opened and I should sort, then sort items
675
+
676
+ if (shouldSort) {
677
+ newOrdering = createOrdering(sortItems(items));
678
+ }
679
+ } // add new items on top or bottom depending on sorting behavior
680
+
681
+
682
+ newOrdering = newOrdering.filter(item => !add.includes(item));
683
+
684
+ if (shouldSort) {
685
+ newOrdering.unshift(...add);
686
+ } else {
687
+ newOrdering.push(...add);
688
+ }
689
+
690
+ setOrdering(newOrdering);
691
+ setNewItemAdded(true);
692
+ } else {
693
+ setNewItemAdded(false);
694
+ }
695
+ }, [items, open, shouldHandleEffects]); // (2) sort items on open if shouldSort is set
696
+
697
+ useEffect(() => {
698
+ if (shouldSort && open && !newItemAdded) {
699
+ setOrdering(createOrdering(sortItems(items)));
700
+ }
701
+ }, [open, shouldSort]); // (3) items were deleted
702
+
703
+ useEffect(() => {
704
+ if (shouldHandleEffects && prevItems && items.length < prevItems.length) {
705
+ let keep = [];
706
+ ordering.forEach(o => {
707
+ if (getItem(items, o)) {
708
+ keep.push(o);
709
+ }
710
+ });
711
+ setOrdering(keep);
712
+ }
713
+ }, [items, shouldHandleEffects]);
714
+
715
+ const toggleOpen = () => setOpen(!open);
716
+
717
+ const hasItems = !!items.length;
718
+ return jsxs("div", {
719
+ class: "bio-properties-panel-group",
720
+ "data-group-id": 'group-' + id,
721
+ children: [jsxs("div", {
722
+ class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : ''),
723
+ onClick: hasItems ? toggleOpen : noop,
724
+ children: [jsx("div", {
725
+ title: label,
726
+ class: "bio-properties-panel-group-header-title",
727
+ children: label
728
+ }), jsxs("div", {
729
+ class: "bio-properties-panel-group-header-buttons",
730
+ children: [add ? jsxs("button", {
731
+ title: "Create new list item",
732
+ class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
733
+ onClick: add,
734
+ children: [jsx(CreateIcon, {}), !hasItems ? jsx("span", {
735
+ class: "bio-properties-panel-add-entry-label",
736
+ children: "Create"
737
+ }) : null]
738
+ }) : null, hasItems ? jsx("div", {
739
+ title: `List contains ${items.length} item${items.length != 1 ? 's' : ''}`,
740
+ class: "bio-properties-panel-list-badge",
741
+ children: items.length
742
+ }) : null, hasItems ? jsx("button", {
743
+ title: "Toggle section",
744
+ class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
745
+ children: jsx(ArrowIcon, {
746
+ class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
747
+ })
748
+ }) : null]
749
+ })]
750
+ }), jsx("div", {
751
+ class: classnames('bio-properties-panel-list', open && hasItems ? 'open' : ''),
752
+ children: ordering.map((o, index) => {
753
+ const item = getItem(items, o);
754
+
755
+ if (!item) {
756
+ return;
757
+ }
758
+
759
+ return jsx(ListItem, {
760
+ // if item was added, open first or last item based on ordering
761
+ autoOpen: newItemAdded && (shouldSort ? index === 0 : index === ordering.length - 1),
762
+ ...item
763
+ }, item.id);
764
+ })
765
+ })]
766
+ });
767
+ } // helpers ////////////////////
768
+
769
+ /**
770
+ * Sorts given items alphanumeric by label
771
+ */
772
+
773
+ function sortItems(items) {
774
+ return sortBy(items, i => i.label.toLowerCase());
775
+ }
776
+
777
+ function getItem(items, id) {
778
+ return find(items, i => i.id === id);
779
+ }
780
+
781
+ function createOrdering(items) {
782
+ return items.map(i => i.id);
783
+ }
784
+
785
+ function Description(props) {
786
+ const {
787
+ element,
788
+ forId,
789
+ value
790
+ } = props;
791
+ const contextDescription = useDescriptionContext(forId, element);
792
+ const description = value || contextDescription;
793
+
794
+ if (description) {
795
+ return jsx("div", {
796
+ class: "bio-properties-panel-description",
797
+ children: description
798
+ });
799
+ }
800
+ }
801
+
802
+ function Checkbox(props) {
803
+ const {
804
+ id,
805
+ label,
806
+ onChange,
807
+ disabled,
808
+ value = false
809
+ } = props;
810
+
811
+ const handleChange = ({
812
+ target
813
+ }) => {
814
+ onChange(target.checked);
815
+ };
816
+
817
+ return jsxs("div", {
818
+ class: "bio-properties-panel-checkbox",
819
+ children: [jsx("input", {
820
+ id: prefixId$6(id),
821
+ name: id,
822
+ type: "checkbox",
823
+ class: "bio-properties-panel-input",
824
+ onChange: handleChange,
825
+ checked: value,
826
+ disabled: disabled
827
+ }), jsx("label", {
828
+ for: prefixId$6(id),
829
+ class: "bio-properties-panel-label",
830
+ children: label
831
+ })]
832
+ });
833
+ }
834
+ /**
835
+ * @param {Object} props
836
+ * @param {Object} props.element
837
+ * @param {String} props.id
838
+ * @param {String} props.description
839
+ * @param {String} props.label
840
+ * @param {Function} props.getValue
841
+ * @param {Function} props.setValue
842
+ * @param {boolean} [props.disabled]
843
+ */
844
+
845
+
846
+ function CheckboxEntry(props) {
847
+ const {
848
+ element,
849
+ id,
850
+ description,
851
+ label,
852
+ getValue,
853
+ setValue,
854
+ disabled
855
+ } = props;
856
+ const value = getValue(element);
857
+ return jsxs("div", {
858
+ class: "bio-properties-panel-entry bio-properties-panel-checkbox-entry",
859
+ "data-entry-id": id,
860
+ children: [jsx(Checkbox, {
861
+ id: id,
862
+ label: label,
863
+ onChange: setValue,
864
+ value: value,
865
+ disabled: disabled
866
+ }), jsx(Description, {
867
+ forId: id,
868
+ element: element,
869
+ value: description
870
+ })]
871
+ });
872
+ }
873
+ function isEdited$6(node) {
874
+ return node && !!node.checked;
875
+ } // helpers /////////////////
876
+
877
+ function prefixId$6(id) {
878
+ return `bio-properties-panel-${id}`;
879
+ }
880
+
881
+ function List(props) {
882
+ const {
883
+ id,
884
+ element,
885
+ items = [],
886
+ renderItem,
887
+ label = '<empty>',
888
+ open: shouldOpen,
889
+ onAdd,
890
+ onRemove,
891
+ autoFocusEntry,
892
+ compareFn
893
+ } = props;
894
+ const [open, setOpen] = useState(!!shouldOpen);
895
+ const hasItems = !!items.length;
896
+
897
+ const toggleOpen = () => hasItems && setOpen(!open);
898
+
899
+ const opening = !usePrevious(open) && open;
900
+ const elementChanged = usePrevious(element) !== element;
901
+ const shouldReset = opening || elementChanged;
902
+ const sortedItems = useSortedItems(items, compareFn, shouldReset);
903
+ const newItems = useNewItems(items, elementChanged);
904
+ useEffect(() => {
905
+ if (open && !hasItems) {
906
+ setOpen(false);
907
+ }
908
+ }, [open, hasItems]);
909
+ /**
910
+ * @param {MouseEvent} event
911
+ */
912
+
913
+ function addItem(event) {
914
+ event.stopPropagation();
915
+ onAdd();
916
+
917
+ if (!open) {
918
+ setOpen(true);
919
+ }
920
+ }
921
+
922
+ return jsxs("div", {
923
+ "data-entry-id": id,
924
+ class: classnames('bio-properties-panel-entry', 'bio-properties-panel-list-entry', hasItems ? '' : 'empty', open ? 'open' : ''),
925
+ children: [jsxs("div", {
926
+ class: "bio-properties-panel-list-entry-header",
927
+ onClick: toggleOpen,
928
+ children: [jsx("div", {
929
+ title: label,
930
+ class: classnames('bio-properties-panel-list-entry-header-title', open && 'open'),
931
+ children: label
932
+ }), jsxs("div", {
933
+ class: "bio-properties-panel-list-entry-header-buttons",
934
+ children: [jsxs("button", {
935
+ title: "Create new list item",
936
+ onClick: addItem,
937
+ class: "bio-properties-panel-add-entry",
938
+ children: [jsx(CreateIcon, {}), !hasItems ? jsx("span", {
939
+ class: "bio-properties-panel-add-entry-label",
940
+ children: "Create"
941
+ }) : null]
942
+ }), hasItems && jsx("div", {
943
+ title: `List contains ${items.length} item${items.length != 1 ? 's' : ''}`,
944
+ class: "bio-properties-panel-list-badge",
945
+ children: items.length
946
+ }), hasItems && jsx("button", {
947
+ title: "Toggle list item",
948
+ class: "bio-properties-panel-arrow",
949
+ children: jsx(ArrowIcon, {
950
+ class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
951
+ })
952
+ })]
953
+ })]
954
+ }), hasItems && jsx(ItemsList, {
955
+ autoFocusEntry: autoFocusEntry,
956
+ id: id,
957
+ open: open,
958
+ items: sortedItems,
959
+ newItems: newItems,
960
+ onRemove: onRemove,
961
+ renderItem: renderItem
962
+ })]
963
+ });
964
+ }
965
+
966
+ function ItemsList(props) {
967
+ const {
968
+ autoFocusEntry,
969
+ id,
970
+ items,
971
+ newItems,
972
+ open,
973
+ onRemove,
974
+ renderItem
975
+ } = props;
976
+ const getKey = useKeyFactory();
977
+ const newItem = newItems[0];
978
+ useEffect(() => {
979
+ if (newItem && autoFocusEntry) {
980
+ // (0) select the parent entry (containing all list items)
981
+ const entry = query(`[data-entry-id="${id}"]`); // (1) select the first input or a custom element to be focussed
982
+
983
+ const selector = typeof autoFocusEntry === 'boolean' ? '.bio-properties-panel-input' : autoFocusEntry;
984
+ const focusableInput = query(selector, entry); // (2) set focus
985
+
986
+ if (focusableInput) {
987
+ if (isFunction(focusableInput.select)) {
988
+ focusableInput.select();
989
+ } else if (isFunction(focusableInput.focus)) {
990
+ focusableInput.focus();
991
+ }
992
+ }
993
+ }
994
+ }, [newItem, autoFocusEntry, id]);
995
+ return jsx("ol", {
996
+ class: classnames('bio-properties-panel-list-entry-items', open ? 'open' : ''),
997
+ children: items.map((item, index) => {
998
+ const key = getKey(item);
999
+ return jsxs("li", {
1000
+ class: "bio-properties-panel-list-entry-item",
1001
+ children: [renderItem(item, index, item === newItem), onRemove && jsx("button", {
1002
+ type: "button",
1003
+ title: "Delete item",
1004
+ class: "bio-properties-panel-remove-entry bio-properties-panel-remove-list-entry",
1005
+ onClick: () => onRemove && onRemove(item),
1006
+ children: jsx(DeleteIcon, {})
1007
+ })]
1008
+ }, key);
1009
+ })
1010
+ });
1011
+ }
1012
+ /**
1013
+ * Place new items in the beginning of the list and sort the rest with provided function.
1014
+ *
1015
+ * @template Item
1016
+ * @param {Item[]} currentItems
1017
+ * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
1018
+ * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
1019
+ * @returns {Item[]}
1020
+ */
1021
+
1022
+
1023
+ function useSortedItems(currentItems, compareFn, shouldReset = false) {
1024
+ const itemsRef = useRef(currentItems.slice()); // (1) Reset and optionally sort.
1025
+
1026
+ if (shouldReset) {
1027
+ itemsRef.current = currentItems.slice();
1028
+
1029
+ if (compareFn) {
1030
+ itemsRef.current.sort(compareFn);
1031
+ }
1032
+ } else {
1033
+ const items = itemsRef.current; // (2) Add new item to the list.
1034
+
1035
+ for (const item of currentItems) {
1036
+ if (!items.includes(item)) {
1037
+ // Unshift or push depending on whether we have a compareFn
1038
+ compareFn ? items.unshift(item) : items.push(item);
1039
+ }
1040
+ } // (3) Filter out removed items.
1041
+
1042
+
1043
+ itemsRef.current = items.filter(item => currentItems.includes(item));
1044
+ }
1045
+
1046
+ return itemsRef.current;
1047
+ }
1048
+
1049
+ function useNewItems(items = [], shouldReset) {
1050
+ const previousItems = usePrevious(items.slice()) || [];
1051
+
1052
+ if (shouldReset) {
1053
+ return [];
1054
+ }
1055
+
1056
+ return previousItems ? items.filter(item => !previousItems.includes(item)) : [];
1057
+ }
1058
+
1059
+ function NumberField(props) {
1060
+ const {
1061
+ debounce,
1062
+ disabled,
1063
+ id,
1064
+ label,
1065
+ max,
1066
+ min,
1067
+ onInput,
1068
+ step,
1069
+ value = ''
1070
+ } = props;
1071
+ const handleInput = useMemo(() => {
1072
+ return debounce(event => {
1073
+ const {
1074
+ validity,
1075
+ value
1076
+ } = event.target;
1077
+
1078
+ if (validity.valid) {
1079
+ onInput(value ? parseFloat(value) : undefined);
1080
+ }
1081
+ });
1082
+ }, [onInput, debounce]);
1083
+ return jsxs("div", {
1084
+ class: "bio-properties-panel-numberfield",
1085
+ children: [jsx("label", {
1086
+ for: prefixId$5(id),
1087
+ class: "bio-properties-panel-label",
1088
+ children: label
1089
+ }), jsx("input", {
1090
+ id: prefixId$5(id),
1091
+ type: "number",
1092
+ name: id,
1093
+ spellCheck: "false",
1094
+ autoComplete: "off",
1095
+ disabled: disabled,
1096
+ class: "bio-properties-panel-input",
1097
+ max: max,
1098
+ min: min,
1099
+ onInput: handleInput,
1100
+ step: step,
1101
+ value: value
1102
+ })]
1103
+ });
1104
+ }
1105
+ /**
1106
+ * @param {Object} props
1107
+ * @param {Boolean} props.debounce
1108
+ * @param {String} props.description
1109
+ * @param {Boolean} props.disabled
1110
+ * @param {Object} props.element
1111
+ * @param {Function} props.getValue
1112
+ * @param {String} props.id
1113
+ * @param {String} props.label
1114
+ * @param {String} props.max
1115
+ * @param {String} props.min
1116
+ * @param {Function} props.setValue
1117
+ * @param {String} props.step
1118
+ */
1119
+
1120
+
1121
+ function NumberFieldEntry(props) {
1122
+ const {
1123
+ debounce,
1124
+ description,
1125
+ disabled,
1126
+ element,
1127
+ getValue,
1128
+ id,
1129
+ label,
1130
+ max,
1131
+ min,
1132
+ setValue,
1133
+ step
1134
+ } = props;
1135
+ const value = getValue(element);
1136
+ return jsxs("div", {
1137
+ class: "bio-properties-panel-entry",
1138
+ "data-entry-id": id,
1139
+ children: [jsx(NumberField, {
1140
+ debounce: debounce,
1141
+ disabled: disabled,
1142
+ id: id,
1143
+ label: label,
1144
+ onInput: setValue,
1145
+ max: max,
1146
+ min: min,
1147
+ step: step,
1148
+ value: value
1149
+ }), jsx(Description, {
1150
+ forId: id,
1151
+ element: element,
1152
+ value: description
1153
+ })]
1154
+ });
1155
+ }
1156
+ function isEdited$5(node) {
1157
+ return node && !!node.value;
1158
+ } // helpers /////////////////
1159
+
1160
+ function prefixId$5(id) {
1161
+ return `bio-properties-panel-${id}`;
1162
+ }
1163
+
1164
+ function Select(props) {
1165
+ const {
1166
+ id,
1167
+ label,
1168
+ onChange,
1169
+ options = [],
1170
+ value,
1171
+ disabled
1172
+ } = props;
1173
+
1174
+ const handleChange = ({
1175
+ target
1176
+ }) => {
1177
+ onChange(target.value);
1178
+ };
1179
+
1180
+ return jsxs("div", {
1181
+ class: "bio-properties-panel-select",
1182
+ children: [jsx("label", {
1183
+ for: prefixId$4(id),
1184
+ class: "bio-properties-panel-label",
1185
+ children: label
1186
+ }), jsx("select", {
1187
+ id: prefixId$4(id),
1188
+ name: id,
1189
+ class: "bio-properties-panel-input",
1190
+ onInput: handleChange,
1191
+ value: value,
1192
+ disabled: disabled,
1193
+ children: options.map((option, idx) => {
1194
+ return jsx("option", {
1195
+ value: option.value,
1196
+ disabled: option.disabled,
1197
+ children: option.label
1198
+ }, idx);
1199
+ })
1200
+ })]
1201
+ });
1202
+ }
1203
+ /**
1204
+ * @param {object} props
1205
+ * @param {object} props.element
1206
+ * @param {string} props.id
1207
+ * @param {string} [props.description]
1208
+ * @param {string} props.label
1209
+ * @param {Function} props.getValue
1210
+ * @param {Function} props.setValue
1211
+ * @param {Function} props.getOptions
1212
+ * @param {boolean} [props.disabled]
1213
+ */
1214
+
1215
+
1216
+ function SelectEntry(props) {
1217
+ const {
1218
+ element,
1219
+ id,
1220
+ description,
1221
+ label,
1222
+ getValue,
1223
+ setValue,
1224
+ getOptions,
1225
+ disabled
1226
+ } = props;
1227
+ const value = getValue(element);
1228
+ const options = getOptions(element);
1229
+ return jsxs("div", {
1230
+ class: "bio-properties-panel-entry",
1231
+ "data-entry-id": id,
1232
+ children: [jsx(Select, {
1233
+ id: id,
1234
+ label: label,
1235
+ value: value,
1236
+ onChange: setValue,
1237
+ options: options,
1238
+ disabled: disabled
1239
+ }), jsx(Description, {
1240
+ forId: id,
1241
+ element: element,
1242
+ value: description
1243
+ })]
1244
+ });
1245
+ }
1246
+ function isEdited$4(node) {
1247
+ return node && !!node.value;
1248
+ } // helpers /////////////////
1249
+
1250
+ function prefixId$4(id) {
1251
+ return `bio-properties-panel-${id}`;
1252
+ }
1253
+
1254
+ function Simple(props) {
1255
+ const {
1256
+ debounce,
1257
+ disabled,
1258
+ element,
1259
+ getValue,
1260
+ id,
1261
+ onBlur,
1262
+ onFocus,
1263
+ setValue
1264
+ } = props;
1265
+ const handleInput = useMemo(() => {
1266
+ return debounce(({
1267
+ target
1268
+ }) => setValue(target.value.length ? target.value : undefined));
1269
+ }, [setValue, debounce]);
1270
+ const value = getValue(element);
1271
+ return jsx("div", {
1272
+ class: "bio-properties-panel-simple",
1273
+ children: jsx("input", {
1274
+ id: prefixId$3(id),
1275
+ type: "text",
1276
+ name: id,
1277
+ spellCheck: "false",
1278
+ autoComplete: "off",
1279
+ disabled: disabled,
1280
+ class: "bio-properties-panel-input",
1281
+ onInput: handleInput,
1282
+ "aria-label": value || '<empty>',
1283
+ onFocus: onFocus,
1284
+ onBlur: onBlur,
1285
+ value: value || ''
1286
+ })
1287
+ });
1288
+ }
1289
+ function isEdited$3(node) {
1290
+ return node && !!node.value;
1291
+ } // helpers /////////////////
1292
+
1293
+ function prefixId$3(id) {
1294
+ return `bio-properties-panel-${id}`;
1295
+ }
1296
+
1297
+ function TextArea(props) {
1298
+ const {
1299
+ id,
1300
+ label,
1301
+ rows = 2,
1302
+ debounce,
1303
+ onInput,
1304
+ value = '',
1305
+ disabled,
1306
+ monospace
1307
+ } = props;
1308
+ const handleInput = useMemo(() => {
1309
+ return debounce(({
1310
+ target
1311
+ }) => onInput(target.value.length ? target.value : undefined));
1312
+ }, [onInput, debounce]);
1313
+ return jsxs("div", {
1314
+ class: "bio-properties-panel-textarea",
1315
+ children: [jsx("label", {
1316
+ for: prefixId$2(id),
1317
+ class: "bio-properties-panel-label",
1318
+ children: label
1319
+ }), jsx("textarea", {
1320
+ id: prefixId$2(id),
1321
+ name: id,
1322
+ spellCheck: "false",
1323
+ class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : ''),
1324
+ onInput: handleInput,
1325
+ onFocus: props.onFocus,
1326
+ onBlur: props.onBlur,
1327
+ rows: rows,
1328
+ value: value,
1329
+ disabled: disabled
1330
+ })]
1331
+ });
1332
+ }
1333
+ /**
1334
+ * @param {object} props
1335
+ * @param {object} props.element
1336
+ * @param {string} props.id
1337
+ * @param {string} props.description
1338
+ * @param {boolean} props.debounce
1339
+ * @param {string} props.label
1340
+ * @param {Function} props.getValue
1341
+ * @param {Function} props.setValue
1342
+ * @param {number} props.rows
1343
+ * @param {boolean} props.monospace
1344
+ * @param {boolean} [props.disabled]
1345
+ */
1346
+
1347
+
1348
+ function TextAreaEntry(props) {
1349
+ const {
1350
+ element,
1351
+ id,
1352
+ description,
1353
+ debounce,
1354
+ label,
1355
+ getValue,
1356
+ setValue,
1357
+ rows,
1358
+ monospace,
1359
+ disabled
1360
+ } = props;
1361
+ const value = getValue(element);
1362
+ return jsxs("div", {
1363
+ class: "bio-properties-panel-entry",
1364
+ "data-entry-id": id,
1365
+ children: [jsx(TextArea, {
1366
+ id: id,
1367
+ label: label,
1368
+ value: value,
1369
+ onInput: setValue,
1370
+ rows: rows,
1371
+ debounce: debounce,
1372
+ monospace: monospace,
1373
+ disabled: disabled
1374
+ }), jsx(Description, {
1375
+ forId: id,
1376
+ element: element,
1377
+ value: description
1378
+ })]
1379
+ });
1380
+ }
1381
+ function isEdited$2(node) {
1382
+ return node && !!node.value;
1383
+ } // helpers /////////////////
1384
+
1385
+ function prefixId$2(id) {
1386
+ return `bio-properties-panel-${id}`;
1387
+ }
1388
+
1389
+ function Textfield(props) {
1390
+ const {
1391
+ debounce,
1392
+ disabled = false,
1393
+ id,
1394
+ label,
1395
+ onInput,
1396
+ value = ''
1397
+ } = props;
1398
+ const handleInput = useMemo(() => {
1399
+ return debounce(({
1400
+ target
1401
+ }) => onInput(target.value.length ? target.value : undefined));
1402
+ }, [onInput, debounce]);
1403
+ return jsxs("div", {
1404
+ class: "bio-properties-panel-textfield",
1405
+ children: [jsx("label", {
1406
+ for: prefixId$1(id),
1407
+ class: "bio-properties-panel-label",
1408
+ children: label
1409
+ }), jsx("input", {
1410
+ id: prefixId$1(id),
1411
+ type: "text",
1412
+ name: id,
1413
+ spellCheck: "false",
1414
+ autoComplete: "off",
1415
+ disabled: disabled,
1416
+ class: "bio-properties-panel-input",
1417
+ onInput: handleInput,
1418
+ onFocus: props.onFocus,
1419
+ onBlur: props.onBlur,
1420
+ value: value || ''
1421
+ })]
1422
+ });
1423
+ }
1424
+ /**
1425
+ * @param {Object} props
1426
+ * @param {Object} props.element
1427
+ * @param {String} props.id
1428
+ * @param {String} props.description
1429
+ * @param {Boolean} props.debounce
1430
+ * @param {Boolean} props.disabled
1431
+ * @param {String} props.label
1432
+ * @param {Function} props.getValue
1433
+ * @param {Function} props.setValue
1434
+ * @param {Function} props.validate
1435
+ */
1436
+
1437
+
1438
+ function TextfieldEntry(props) {
1439
+ const {
1440
+ element,
1441
+ id,
1442
+ description,
1443
+ debounce,
1444
+ disabled,
1445
+ label,
1446
+ getValue,
1447
+ setValue,
1448
+ validate
1449
+ } = props;
1450
+ const [error, setError] = useState(null);
1451
+ const [invalidValueCache, setInvalidValueCache] = useState(null);
1452
+ let value = getValue(element);
1453
+ const prevValue = usePrevious(value); // validate again when value prop changed
1454
+
1455
+ useEffect(() => {
1456
+ const err = validate ? validate(value) : null;
1457
+ setError(err);
1458
+ }, [value]); // validate on change
1459
+
1460
+ const handleChange = newValue => {
1461
+ const err = validate ? validate(newValue) : null;
1462
+
1463
+ if (err) {
1464
+ setInvalidValueCache(newValue);
1465
+ } else {
1466
+ setValue(newValue);
1467
+ }
1468
+
1469
+ setError(err);
1470
+ }; // keep showing invalid value on errors, although it was not set
1471
+
1472
+
1473
+ if (prevValue === value && error) {
1474
+ value = invalidValueCache;
1475
+ }
1476
+
1477
+ return jsxs("div", {
1478
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
1479
+ "data-entry-id": id,
1480
+ children: [jsx(Textfield, {
1481
+ id: id,
1482
+ label: label,
1483
+ value: value,
1484
+ onInput: handleChange,
1485
+ debounce: debounce,
1486
+ disabled: disabled
1487
+ }), jsx(Description, {
1488
+ forId: id,
1489
+ element: element,
1490
+ value: description
1491
+ }), error && jsx("div", {
1492
+ class: "bio-properties-panel-error",
1493
+ children: error
1494
+ })]
1495
+ });
1496
+ }
1497
+ function isEdited$1(node) {
1498
+ return node && !!node.value;
1499
+ } // helpers /////////////////
1500
+
1501
+ function prefixId$1(id) {
1502
+ return `bio-properties-panel-${id}`;
1503
+ }
1504
+
1505
+ function ToggleSwitch(props) {
1506
+ const {
1507
+ id,
1508
+ label,
1509
+ onInput,
1510
+ value,
1511
+ switcherLabel
1512
+ } = props;
1513
+
1514
+ const handleInput = async () => {
1515
+ onInput(!value);
1516
+ };
1517
+
1518
+ return jsxs("div", {
1519
+ class: "bio-properties-panel-toggle-switch",
1520
+ children: [jsx("label", {
1521
+ class: "bio-properties-panel-label",
1522
+ for: prefixId(id),
1523
+ children: label
1524
+ }), jsxs("div", {
1525
+ class: "bio-properties-panel-field-wrapper",
1526
+ children: [jsxs("label", {
1527
+ class: "bio-properties-panel-toggle-switch__switcher",
1528
+ children: [jsx("input", {
1529
+ id: prefixId(id),
1530
+ class: "bio-properties-panel-input",
1531
+ type: "checkbox",
1532
+ name: id,
1533
+ onInput: handleInput,
1534
+ checked: value
1535
+ }), jsx("span", {
1536
+ class: "bio-properties-panel-toggle-switch__slider"
1537
+ })]
1538
+ }), jsx("p", {
1539
+ class: "bio-properties-panel-toggle-switch__label",
1540
+ children: switcherLabel
1541
+ })]
1542
+ })]
1543
+ });
1544
+ }
1545
+ /**
1546
+ * @param {Object} props
1547
+ * @param {Object} props.element
1548
+ * @param {String} props.id
1549
+ * @param {String} props.description
1550
+ * @param {String} props.label
1551
+ * @param {String} props.switcherLabel
1552
+ * @param {Function} props.getValue
1553
+ * @param {Function} props.setValue
1554
+ */
1555
+
1556
+
1557
+ function ToggleSwitchEntry(props) {
1558
+ const {
1559
+ element,
1560
+ id,
1561
+ description,
1562
+ label,
1563
+ switcherLabel,
1564
+ getValue,
1565
+ setValue
1566
+ } = props;
1567
+ const value = getValue(element);
1568
+ return jsxs("div", {
1569
+ class: "bio-properties-panel-entry bio-properties-panel-toggle-switch-entry",
1570
+ "data-entry-id": id,
1571
+ children: [jsx(ToggleSwitch, {
1572
+ id: id,
1573
+ label: label,
1574
+ value: value,
1575
+ onInput: setValue,
1576
+ switcherLabel: switcherLabel
1577
+ }), jsx(Description, {
1578
+ forId: id,
1579
+ element: element,
1580
+ value: description
1581
+ })]
1582
+ });
1583
+ }
1584
+ function isEdited(node) {
1585
+ return node && !!node.checked;
1586
+ } // helpers /////////////////
1587
+
1588
+ function prefixId(id) {
1589
+ return `bio-properties-panel-${id}`;
1590
+ }
1591
+
1592
+ const DEFAULT_DEBOUNCE_TIME = 300;
1593
+ function debounceInput(debounceDelay) {
1594
+ return function _debounceInput(fn) {
1595
+ if (debounceDelay !== false) {
1596
+ var debounceTime = isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
1597
+ return debounce(fn, debounceTime);
1598
+ } else {
1599
+ return fn;
1600
+ }
1601
+ };
1602
+ }
1603
+ debounceInput.$inject = ['config.debounceInput'];
1604
+
1605
+ var index = {
1606
+ debounceInput: ['factory', debounceInput]
1607
+ };
1608
+
1609
+ export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, PropertiesPanel, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$6 as isCheckboxEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useKeyFactory, useLayoutState, usePrevious };
1610
+ //# sourceMappingURL=index.esm.js.map