@bpmn-io/properties-panel 0.9.0 → 0.10.0

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