@kunpeng-js/properties-panel 0.0.0-stage → 2026.0.0-alpha2

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -2
  3. package/assets/properties-panel.css +4 -0
  4. package/dist/assets/properties-panel.css +1539 -0
  5. package/dist/index.esm.js +3958 -0
  6. package/dist/index.esm.js.map +1 -0
  7. package/dist/index.js +4053 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +113 -3
  10. package/preact/LICENSE +21 -0
  11. package/preact/README.md +188 -0
  12. package/preact/compat/client.d.ts +13 -0
  13. package/preact/compat/client.js +21 -0
  14. package/preact/compat/client.mjs +24 -0
  15. package/preact/compat/dist/compat.js +2 -0
  16. package/preact/compat/dist/compat.js.map +1 -0
  17. package/preact/compat/dist/compat.mjs +2 -0
  18. package/preact/compat/dist/compat.module.js +2 -0
  19. package/preact/compat/dist/compat.module.js.map +1 -0
  20. package/preact/compat/dist/compat.umd.js +2 -0
  21. package/preact/compat/dist/compat.umd.js.map +1 -0
  22. package/preact/compat/jsx-dev-runtime.js +3 -0
  23. package/preact/compat/jsx-dev-runtime.mjs +3 -0
  24. package/preact/compat/jsx-runtime.js +3 -0
  25. package/preact/compat/jsx-runtime.mjs +3 -0
  26. package/preact/compat/package.json +55 -0
  27. package/preact/compat/scheduler.js +15 -0
  28. package/preact/compat/scheduler.mjs +23 -0
  29. package/preact/compat/server.browser.js +11 -0
  30. package/preact/compat/server.js +36 -0
  31. package/preact/compat/server.mjs +17 -0
  32. package/preact/compat/src/Children.js +21 -0
  33. package/preact/compat/src/PureComponent.js +16 -0
  34. package/preact/compat/src/forwardRef.js +44 -0
  35. package/preact/compat/src/hooks.js +67 -0
  36. package/preact/compat/src/index.d.ts +353 -0
  37. package/preact/compat/src/index.js +243 -0
  38. package/preact/compat/src/internal.d.ts +51 -0
  39. package/preact/compat/src/memo.js +31 -0
  40. package/preact/compat/src/portals.js +78 -0
  41. package/preact/compat/src/render.js +302 -0
  42. package/preact/compat/src/suspense-list.d.ts +16 -0
  43. package/preact/compat/src/suspense-list.js +127 -0
  44. package/preact/compat/src/suspense.d.ts +19 -0
  45. package/preact/compat/src/suspense.js +289 -0
  46. package/preact/compat/src/util.js +33 -0
  47. package/preact/compat/test-utils.js +1 -0
  48. package/preact/compat/test-utils.mjs +1 -0
  49. package/preact/debug/dist/debug.js +2 -0
  50. package/preact/debug/dist/debug.js.map +1 -0
  51. package/preact/debug/dist/debug.mjs +2 -0
  52. package/preact/debug/dist/debug.module.js +2 -0
  53. package/preact/debug/dist/debug.module.js.map +1 -0
  54. package/preact/debug/dist/debug.umd.js +2 -0
  55. package/preact/debug/dist/debug.umd.js.map +1 -0
  56. package/preact/debug/package.json +27 -0
  57. package/preact/debug/src/check-props.js +54 -0
  58. package/preact/debug/src/component-stack.js +146 -0
  59. package/preact/debug/src/constants.js +3 -0
  60. package/preact/debug/src/debug.js +593 -0
  61. package/preact/debug/src/index.d.ts +23 -0
  62. package/preact/debug/src/index.js +12 -0
  63. package/preact/debug/src/internal.d.ts +82 -0
  64. package/preact/debug/src/util.js +15 -0
  65. package/preact/devtools/dist/devtools.js +2 -0
  66. package/preact/devtools/dist/devtools.js.map +1 -0
  67. package/preact/devtools/dist/devtools.mjs +2 -0
  68. package/preact/devtools/dist/devtools.module.js +2 -0
  69. package/preact/devtools/dist/devtools.module.js.map +1 -0
  70. package/preact/devtools/dist/devtools.umd.js +2 -0
  71. package/preact/devtools/dist/devtools.umd.js.map +1 -0
  72. package/preact/devtools/package.json +25 -0
  73. package/preact/devtools/src/devtools.js +21 -0
  74. package/preact/devtools/src/index.d.ts +8 -0
  75. package/preact/devtools/src/index.js +15 -0
  76. package/preact/dist/preact.js +2 -0
  77. package/preact/dist/preact.js.map +1 -0
  78. package/preact/dist/preact.min.js +2 -0
  79. package/preact/dist/preact.min.js.map +1 -0
  80. package/preact/dist/preact.min.module.js +2 -0
  81. package/preact/dist/preact.min.module.js.map +1 -0
  82. package/preact/dist/preact.min.umd.js +2 -0
  83. package/preact/dist/preact.min.umd.js.map +1 -0
  84. package/preact/dist/preact.mjs +2 -0
  85. package/preact/dist/preact.module.js +2 -0
  86. package/preact/dist/preact.module.js.map +1 -0
  87. package/preact/dist/preact.umd.js +2 -0
  88. package/preact/dist/preact.umd.js.map +1 -0
  89. package/preact/hooks/dist/hooks.js +2 -0
  90. package/preact/hooks/dist/hooks.js.map +1 -0
  91. package/preact/hooks/dist/hooks.mjs +2 -0
  92. package/preact/hooks/dist/hooks.module.js +2 -0
  93. package/preact/hooks/dist/hooks.module.js.map +1 -0
  94. package/preact/hooks/dist/hooks.umd.js +2 -0
  95. package/preact/hooks/dist/hooks.umd.js.map +1 -0
  96. package/preact/hooks/package.json +35 -0
  97. package/preact/hooks/src/index.d.ts +145 -0
  98. package/preact/hooks/src/index.js +549 -0
  99. package/preact/hooks/src/internal.d.ts +108 -0
  100. package/preact/jsx-runtime/dist/jsxRuntime.js +2 -0
  101. package/preact/jsx-runtime/dist/jsxRuntime.js.map +1 -0
  102. package/preact/jsx-runtime/dist/jsxRuntime.mjs +2 -0
  103. package/preact/jsx-runtime/dist/jsxRuntime.module.js +2 -0
  104. package/preact/jsx-runtime/dist/jsxRuntime.module.js.map +1 -0
  105. package/preact/jsx-runtime/dist/jsxRuntime.umd.js +2 -0
  106. package/preact/jsx-runtime/dist/jsxRuntime.umd.js.map +1 -0
  107. package/preact/jsx-runtime/package.json +28 -0
  108. package/preact/jsx-runtime/src/index.d.ts +62 -0
  109. package/preact/jsx-runtime/src/index.js +206 -0
  110. package/preact/jsx-runtime/src/utils.js +36 -0
  111. package/preact/package.json +273 -0
  112. package/preact/src/cjs.js +3 -0
  113. package/preact/src/clone-element.js +48 -0
  114. package/preact/src/component.js +247 -0
  115. package/preact/src/constants.js +22 -0
  116. package/preact/src/create-context.js +60 -0
  117. package/preact/src/create-element.js +97 -0
  118. package/preact/src/diff/catch-error.js +46 -0
  119. package/preact/src/diff/children.js +451 -0
  120. package/preact/src/diff/index.js +693 -0
  121. package/preact/src/diff/props.js +184 -0
  122. package/preact/src/dom.d.ts +2548 -0
  123. package/preact/src/index-5.d.ts +399 -0
  124. package/preact/src/index.d.ts +373 -0
  125. package/preact/src/index.js +13 -0
  126. package/preact/src/internal.d.ts +194 -0
  127. package/preact/src/jsx.d.ts +2958 -0
  128. package/preact/src/options.js +16 -0
  129. package/preact/src/render.js +81 -0
  130. package/preact/src/util.js +28 -0
  131. package/preact/test-utils/dist/testUtils.js +2 -0
  132. package/preact/test-utils/dist/testUtils.js.map +1 -0
  133. package/preact/test-utils/dist/testUtils.mjs +2 -0
  134. package/preact/test-utils/dist/testUtils.module.js +2 -0
  135. package/preact/test-utils/dist/testUtils.module.js.map +1 -0
  136. package/preact/test-utils/dist/testUtils.umd.js +2 -0
  137. package/preact/test-utils/dist/testUtils.umd.js.map +1 -0
  138. package/preact/test-utils/package.json +28 -0
  139. package/preact/test-utils/src/index.d.ts +3 -0
  140. package/preact/test-utils/src/index.js +130 -0
@@ -0,0 +1,3958 @@
1
+ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "../preact/hooks";
2
+ import { assign, debounce, get, isArray, isFunction, isNumber, isObject, isString, set } from "min-dash";
3
+ import { createPortal, forwardRef } from "../preact/compat";
4
+ import { Fragment, jsx, jsxs } from "../preact/jsx-runtime";
5
+ import { createContext, createElement, render } from "../preact";
6
+ import classnames from "classnames";
7
+ import { domify, query } from "min-dom";
8
+ import { EditorView, drawSelection, highlightSpecialChars, keymap, lineNumbers, placeholder } from "@codemirror/view";
9
+ import { Annotation, EditorState } from "@codemirror/state";
10
+ import { bracketMatching, defaultHighlightStyle, foldGutter, foldKeymap, syntaxHighlighting } from "@codemirror/language";
11
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
12
+ import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
13
+ import { json, jsonParseLinter } from "@codemirror/lang-json";
14
+ import { linter } from "@codemirror/lint";
15
+ import Editor, { ExpressionEditor } from "@kunpeng-js/expression-editor";
16
+ import * as focusTrap from "focus-trap";
17
+ //#region src/components/icons/index.js
18
+ var ArrowIcon = function ArrowIcon(props) {
19
+ return jsx("svg", {
20
+ ...props,
21
+ children: jsx("path", {
22
+ fillRule: "evenodd",
23
+ 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"
24
+ })
25
+ });
26
+ };
27
+ ArrowIcon.defaultProps = {
28
+ xmlns: "http://www.w3.org/2000/svg",
29
+ width: "16",
30
+ height: "16"
31
+ };
32
+ var CloseIcon = function CloseIcon(props) {
33
+ return jsx("svg", {
34
+ ...props,
35
+ children: jsx("path", {
36
+ fillRule: "evenodd",
37
+ d: "m12 4.7-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7Z",
38
+ fill: "currentColor"
39
+ })
40
+ });
41
+ };
42
+ CloseIcon.defaultProps = {
43
+ xmlns: "http://www.w3.org/2000/svg",
44
+ width: "16",
45
+ height: "16"
46
+ };
47
+ var CreateIcon = function CreateIcon(props) {
48
+ return jsx("svg", {
49
+ ...props,
50
+ children: jsx("path", {
51
+ fillRule: "evenodd",
52
+ 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"
53
+ })
54
+ });
55
+ };
56
+ CreateIcon.defaultProps = {
57
+ xmlns: "http://www.w3.org/2000/svg",
58
+ width: "16",
59
+ height: "16"
60
+ };
61
+ var DeleteIcon = function DeleteIcon(props) {
62
+ return jsx("svg", {
63
+ ...props,
64
+ children: jsx("path", {
65
+ fillRule: "evenodd",
66
+ 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"
67
+ })
68
+ });
69
+ };
70
+ DeleteIcon.defaultProps = {
71
+ xmlns: "http://www.w3.org/2000/svg",
72
+ width: "16",
73
+ height: "16"
74
+ };
75
+ var DragIcon = function DragIcon(props) {
76
+ return jsx("svg", {
77
+ ...props,
78
+ children: jsx("path", {
79
+ d: "M7 3H5v2h2V3zm4 0H9v2h2V3zM7 7H5v2h2V7zm4 0H9v2h2V7zm-4 4H5v2h2v-2zm4 0H9v2h2v-2z",
80
+ fill: "currentColor"
81
+ })
82
+ });
83
+ };
84
+ DragIcon.defaultProps = {
85
+ width: "16",
86
+ height: "16",
87
+ fill: "none",
88
+ xmlns: "http://www.w3.org/2000/svg"
89
+ };
90
+ var ExternalLinkIcon = function ExternalLinkIcon(props) {
91
+ return jsx("svg", {
92
+ ...props,
93
+ children: jsx("path", {
94
+ fillRule: "evenodd",
95
+ clipRule: "evenodd",
96
+ d: "M12.637 12.637v-4.72h1.362v4.721c0 .36-.137.676-.411.95-.275.275-.591.412-.95.412H3.362c-.38 0-.703-.132-.967-.396A1.315 1.315 0 0 1 2 12.638V3.362c0-.38.132-.703.396-.967S2.982 2 3.363 2h4.553v1.363H3.363v9.274h9.274ZM14 2H9.28l-.001 1.362h2.408L5.065 9.984l.95.95 6.622-6.622v2.409H14V2Z",
97
+ fill: "currentcolor"
98
+ })
99
+ });
100
+ };
101
+ ExternalLinkIcon.defaultProps = {
102
+ width: "16",
103
+ height: "16",
104
+ fill: "none",
105
+ xmlns: "http://www.w3.org/2000/svg"
106
+ };
107
+ var FeelIcon = function FeelIcon(props) {
108
+ return jsx("svg", {
109
+ ...props,
110
+ children: jsx("path", {
111
+ d: "M3.617 11.99c-.137.684-.392 1.19-.765 1.518-.362.328-.882.492-1.558.492H0l.309-1.579h1.264l1.515-7.64h-.912l.309-1.579h.911l.236-1.191c.137-.685.387-1.192.75-1.52C4.753.164 5.277 0 5.953 0h1.294L6.94 1.579H5.675l-.323 1.623h1.264l-.309 1.579H5.043l-1.426 7.208ZM5.605 11.021l3.029-4.155L7.28 3.202h2.073l.706 2.547h.176l1.691-2.547H14l-3.014 4.051 1.338 3.768H10.25l-.706-2.606H9.37L7.678 11.02H5.605Z",
112
+ fill: "currentcolor"
113
+ })
114
+ });
115
+ };
116
+ FeelIcon.defaultProps = {
117
+ width: "14",
118
+ height: "14",
119
+ fill: "none",
120
+ xmlns: "http://www.w3.org/2000/svg"
121
+ };
122
+ var LaunchIcon = function LaunchIcon(props) {
123
+ return jsxs("svg", {
124
+ ...props,
125
+ children: [jsx("path", { d: "M26 28H6a2.003 2.003 0 0 1-2-2V6a2.003 2.003 0 0 1 2-2h10v2H6v20h20V16h2v10a2.003 2.003 0 0 1-2 2Z" }), jsx("path", { d: "M20 2v2h6.586L18 12.586 19.414 14 28 5.414V12h2V2H20z" })]
126
+ });
127
+ };
128
+ LaunchIcon.defaultProps = {
129
+ xmlns: "http://www.w3.org/2000/svg",
130
+ viewBox: "0 0 32 32"
131
+ };
132
+ var OpenPopupIcon = function OpenPopupIcon(props) {
133
+ return jsx("svg", {
134
+ ...props,
135
+ children: jsx("path", { d: "M6 15v-1H2.7L7 9.7 6.3 9 2 13.3V10H1v5zM10 1v1h3.3L9 6.3l.7.7L14 2.7V6h1V1z" })
136
+ });
137
+ };
138
+ OpenPopupIcon.defaultProps = {
139
+ xmlns: "http://www.w3.org/2000/svg",
140
+ viewBox: "0 0 16 16"
141
+ };
142
+ //#endregion
143
+ //#region src/components/Header.js
144
+ /**
145
+ * @typedef { {
146
+ * getElementLabel: (element: object) => string,
147
+ * getTypeLabel: (element: object) => string,
148
+ * getElementIcon: (element: object) => import('preact').Component,
149
+ * getDocumentationRef: (element: object) => string
150
+ * } } HeaderProvider
151
+ */
152
+ /**
153
+ * @param {Object} props
154
+ * @param {Object} props.element,
155
+ * @param {HeaderProvider} props.headerProvider
156
+ */
157
+ function Header(props) {
158
+ const { element, headerProvider } = props;
159
+ const { getElementIcon, getDocumentationRef, getElementLabel, getTypeLabel } = headerProvider;
160
+ const label = getElementLabel(element);
161
+ const type = getTypeLabel(element);
162
+ const documentationRef = getDocumentationRef && getDocumentationRef(element);
163
+ const ElementIcon = getElementIcon(element);
164
+ return jsxs("div", {
165
+ class: "bio-properties-panel-header",
166
+ children: [
167
+ jsx("div", {
168
+ class: "bio-properties-panel-header-icon",
169
+ children: ElementIcon && jsx(ElementIcon, {
170
+ width: "32",
171
+ height: "32",
172
+ viewBox: "0 0 32 32"
173
+ })
174
+ }),
175
+ jsxs("div", {
176
+ class: "bio-properties-panel-header-labels",
177
+ children: [jsx("div", {
178
+ class: "bio-properties-panel-header-type",
179
+ children: type
180
+ }), label ? jsx("div", {
181
+ class: "bio-properties-panel-header-label",
182
+ children: label
183
+ }) : null]
184
+ }),
185
+ jsx("div", {
186
+ class: "bio-properties-panel-header-actions",
187
+ children: documentationRef ? jsx("a", {
188
+ rel: "noreferrer",
189
+ class: "bio-properties-panel-header-link",
190
+ href: documentationRef,
191
+ title: "Open documentation",
192
+ target: "_blank",
193
+ children: jsx(ExternalLinkIcon, {})
194
+ }) : null
195
+ })
196
+ ]
197
+ });
198
+ }
199
+ //#endregion
200
+ //#region src/context/DescriptionContext.js
201
+ var DescriptionContext = createContext({
202
+ description: {},
203
+ getDescriptionForId: () => {}
204
+ });
205
+ //#endregion
206
+ //#region src/context/ErrorsContext.js
207
+ var ErrorsContext = createContext({ errors: {} });
208
+ //#endregion
209
+ //#region src/context/EventContext.js
210
+ /**
211
+ * @typedef {Function} <propertiesPanel.showEntry> callback
212
+ *
213
+ * @example
214
+ *
215
+ * useEvent('propertiesPanel.showEntry', ({ focus = false, ...rest }) => {
216
+ * // ...
217
+ * });
218
+ *
219
+ * @param {Object} context
220
+ * @param {boolean} [context.focus]
221
+ *
222
+ * @returns void
223
+ */
224
+ /**
225
+ * @typedef {Function} <propertiesPanel.showError> callback
226
+ *
227
+ * @example
228
+ *
229
+ * useEvent('propertiesPanel.showError', ({ focus = false, message, ...rest }) => {
230
+ * // ...
231
+ * });
232
+ *
233
+ * @param {Object} context
234
+ * @param {string} context.message
235
+ * @param {boolean} [context.focus]
236
+ *
237
+ * @returns void
238
+ */
239
+ var EventContext = createContext({ eventBus: null });
240
+ //#endregion
241
+ //#region src/context/FeelLanguageContext.js
242
+ /**
243
+ * @typedef { {
244
+ * parserDialect?: import('@kunpeng-js/expression-editor').ParserDialect,
245
+ * builtins?: import('@kunpeng-js/expression-editor').Variable[],
246
+ * dialect?: import('@kunpeng-js/expression-editor').Dialect
247
+ * } } FeelLanguageContextType
248
+ */
249
+ /**
250
+ * @type {import('preact').Context<FeelLanguageContextType>}
251
+ */
252
+ var FeelLanguageContext = createContext({});
253
+ //#endregion
254
+ //#region src/context/LayoutContext.js
255
+ var LayoutContext = createContext({
256
+ layout: {},
257
+ setLayout: () => {},
258
+ getLayoutForKey: () => {},
259
+ setLayoutForKey: () => {}
260
+ });
261
+ //#endregion
262
+ //#region src/context/TooltipContext.js
263
+ var TooltipContext = createContext({
264
+ tooltip: {},
265
+ getTooltipForId: () => {}
266
+ });
267
+ //#endregion
268
+ //#region src/hooks/useTooltipContext.js
269
+ /**
270
+ * Accesses the global TooltipContext and returns a tooltip for a given id and element.
271
+ *
272
+ * @example
273
+ * ```jsx
274
+ * function TextField(props) {
275
+ * const tooltip = useTooltipContext('input1', element);
276
+ * }
277
+ * ```
278
+ *
279
+ * @param {string} id
280
+ * @param {object} element
281
+ *
282
+ * @returns {string}
283
+ */
284
+ function useTooltipContext(id, element) {
285
+ const { getTooltipForId } = useContext(TooltipContext);
286
+ return getTooltipForId(id, element);
287
+ }
288
+ //#endregion
289
+ //#region src/components/entries/Tooltip.js
290
+ /**
291
+ * @typedef {Object} TooltipProps
292
+ * @property {Object} [parent] - Parent element ref for portal rendering
293
+ * @property {String} [direction='right'] - Tooltip direction ( 'right', 'top')
294
+ * @property {String} [position] - Custom CSS position override
295
+ * @property {Number} [showDelay=250] - Delay in ms before showing tooltip on hover
296
+ * @property {Number} [hideDelay=250] - Delay in ms before hiding tooltip when mouse leaves, to avoid multiple tooltips from being opened, this should be the same as showDelay
297
+ * @property {*} [children] - Child elements to render inside the tooltip wrapper
298
+ */
299
+ /**
300
+ * Tooltip wrapper that provides context-based tooltip content lookup.
301
+ * All props are forwarded to the underlying Tooltip component.
302
+ *
303
+ * @param {TooltipProps & {
304
+ * forId: String,
305
+ * value?: String|Object,
306
+ * element?: Object
307
+ * }} props - Shared tooltip props plus wrapper-specific ones
308
+ */
309
+ function TooltipWrapper(props) {
310
+ const { forId, element } = props;
311
+ const contextDescription = useTooltipContext(forId, element);
312
+ const value = props.value || contextDescription;
313
+ if (!value) return props.children;
314
+ return jsx(Tooltip, {
315
+ ...props,
316
+ value,
317
+ forId: `bio-properties-panel-${forId}`
318
+ });
319
+ }
320
+ /**
321
+ * @param {TooltipProps & {
322
+ * forId: String,
323
+ * value: String|Object
324
+ * }} props
325
+ */
326
+ function Tooltip(props) {
327
+ const { forId, value, parent, direction = "right", position, showDelay = 250, hideDelay = 250 } = props;
328
+ const [visible, setVisible] = useState(false);
329
+ const showTimeoutRef = useRef(null);
330
+ const hideTimeoutRef = useRef(null);
331
+ const wrapperRef = useRef(null);
332
+ const tooltipRef = useRef(null);
333
+ const show = (_, delay) => {
334
+ clearTimeout(showTimeoutRef.current);
335
+ clearTimeout(hideTimeoutRef.current);
336
+ if (visible) return;
337
+ if (delay) showTimeoutRef.current = setTimeout(() => {
338
+ setVisible(true);
339
+ }, showDelay);
340
+ else setVisible(true);
341
+ };
342
+ const handleWrapperMouseEnter = (e) => {
343
+ show(e, true);
344
+ };
345
+ const hide = (delay = false) => {
346
+ clearTimeout(showTimeoutRef.current);
347
+ clearTimeout(hideTimeoutRef.current);
348
+ if (delay) hideTimeoutRef.current = setTimeout(() => {
349
+ setVisible(false);
350
+ }, hideDelay);
351
+ else setVisible(false);
352
+ };
353
+ useEffect(() => {
354
+ return () => {
355
+ clearTimeout(showTimeoutRef.current);
356
+ clearTimeout(hideTimeoutRef.current);
357
+ };
358
+ }, []);
359
+ useEffect(() => {
360
+ if (!visible) return;
361
+ const handleClickOutside = (e) => {
362
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target) && tooltipRef.current && !tooltipRef.current.contains(e.target)) hide(false);
363
+ };
364
+ document.addEventListener("mousedown", handleClickOutside);
365
+ return () => {
366
+ document.removeEventListener("mousedown", handleClickOutside);
367
+ };
368
+ }, [visible, hide]);
369
+ const handleMouseLeave = ({ relatedTarget }) => {
370
+ if (relatedTarget === wrapperRef.current || relatedTarget === tooltipRef.current || relatedTarget?.parentElement === tooltipRef.current) return;
371
+ const selection = window.getSelection();
372
+ if (selection && selection.toString().length > 0) {
373
+ const selectionRange = selection.getRangeAt(0);
374
+ if (tooltipRef.current?.contains(selectionRange.commonAncestorContainer) || tooltipRef.current?.contains(selection.anchorNode) || tooltipRef.current?.contains(selection.focusNode)) return;
375
+ }
376
+ hide(true);
377
+ };
378
+ const handleTooltipMouseEnter = () => {
379
+ clearTimeout(hideTimeoutRef.current);
380
+ };
381
+ const handleFocusOut = (e) => {
382
+ const { relatedTarget } = e;
383
+ if (tooltipRef.current?.contains(relatedTarget) || wrapperRef.current?.contains(relatedTarget)) return;
384
+ hide(false);
385
+ };
386
+ const hideTooltipViaEscape = (e) => {
387
+ e.code === "Escape" && hide(false);
388
+ };
389
+ const renderTooltip = () => {
390
+ return jsxs("div", {
391
+ class: `bio-properties-panel-tooltip ${direction}`,
392
+ role: "tooltip",
393
+ id: "bio-properties-panel-tooltip",
394
+ "aria-labelledby": forId,
395
+ style: position || getTooltipPosition(wrapperRef.current),
396
+ ref: tooltipRef,
397
+ onClick: (e) => e.stopPropagation(),
398
+ onMouseEnter: handleTooltipMouseEnter,
399
+ onMouseLeave: handleMouseLeave,
400
+ children: [jsx("div", {
401
+ class: "bio-properties-panel-tooltip-content",
402
+ children: value
403
+ }), jsx("div", { class: "bio-properties-panel-tooltip-arrow" })]
404
+ });
405
+ };
406
+ return jsxs("div", {
407
+ class: "bio-properties-panel-tooltip-wrapper",
408
+ tabIndex: "0",
409
+ ref: wrapperRef,
410
+ onMouseEnter: handleWrapperMouseEnter,
411
+ onMouseLeave: handleMouseLeave,
412
+ onFocus: show,
413
+ onBlur: handleFocusOut,
414
+ onKeyDown: hideTooltipViaEscape,
415
+ children: [props.children, visible ? parent ? createPortal(renderTooltip(), parent.current) : renderTooltip() : null]
416
+ });
417
+ }
418
+ function getTooltipPosition(refElement) {
419
+ const refPosition = refElement.getBoundingClientRect();
420
+ return `right: ${`calc(100% - ${refPosition.x}px)`}; top: ${`${refPosition.top - 10}px`};`;
421
+ }
422
+ //#endregion
423
+ //#region src/hooks/useDescriptionContext.js
424
+ /**
425
+ * Accesses the global DescriptionContext and returns a description for a given id and element.
426
+ *
427
+ * @example
428
+ * ```jsx
429
+ * function TextField(props) {
430
+ * const description = useDescriptionContext('input1', element);
431
+ * }
432
+ * ```
433
+ *
434
+ * @param {string} id
435
+ * @param {object} element
436
+ *
437
+ * @returns {string}
438
+ */
439
+ function useDescriptionContext(id, element) {
440
+ const { getDescriptionForId } = useContext(DescriptionContext);
441
+ return getDescriptionForId(id, element);
442
+ }
443
+ //#endregion
444
+ //#region src/hooks/useDebounce.js
445
+ function useDebounce(callback, debounceFn) {
446
+ const debouncedCallback = useCallback(debounceFn(callback), [callback, debounceFn]);
447
+ useEffect(() => {
448
+ return () => {
449
+ debouncedCallback.cancel?.();
450
+ };
451
+ }, [debouncedCallback]);
452
+ return debouncedCallback;
453
+ }
454
+ //#endregion
455
+ //#region src/hooks/useError.js
456
+ function useError(id) {
457
+ const { errors } = useContext(ErrorsContext);
458
+ return errors[id];
459
+ }
460
+ function useErrors() {
461
+ const { errors } = useContext(ErrorsContext);
462
+ return errors;
463
+ }
464
+ //#endregion
465
+ //#region src/hooks/useEvent.js
466
+ /**
467
+ * Subscribe to an event immediately. Update subscription after inputs changed.
468
+ *
469
+ * @param {string} event
470
+ * @param {Function} callback
471
+ */
472
+ function useEvent(event, callback, eventBus) {
473
+ const eventContext = useContext(EventContext);
474
+ if (!eventBus) ({eventBus} = eventContext);
475
+ const didMount = useRef(false);
476
+ if (eventBus && !didMount.current) eventBus.on(event, callback);
477
+ useEffect(() => {
478
+ if (eventBus && didMount.current) eventBus.on(event, callback);
479
+ didMount.current = true;
480
+ return () => {
481
+ if (eventBus) eventBus.off(event, callback);
482
+ };
483
+ }, [
484
+ callback,
485
+ event,
486
+ eventBus
487
+ ]);
488
+ }
489
+ //#endregion
490
+ //#region src/hooks/useKeyFactory.js
491
+ /**
492
+ * Create a persistent key factory for plain objects without id.
493
+ *
494
+ * @example
495
+ * ```jsx
496
+ * function List({ objects }) {
497
+ * const getKey = useKeyFactory();
498
+ * return (<ol>{
499
+ * objects.map(obj => {
500
+ * const key = getKey(obj);
501
+ * return <li key={key}>obj.name</li>
502
+ * })
503
+ * }</ol>);
504
+ * }
505
+ * ```
506
+ *
507
+ * @param {any[]} dependencies
508
+ * @returns {(element: object) => string}
509
+ */
510
+ function useKeyFactory(dependencies = []) {
511
+ const map = useMemo(() => /* @__PURE__ */ new Map(), dependencies);
512
+ const getKey = (el) => {
513
+ let key = map.get(el);
514
+ if (!key) {
515
+ key = Math.random().toString().slice(-6);
516
+ map.set(el, key);
517
+ }
518
+ return key;
519
+ };
520
+ return getKey;
521
+ }
522
+ //#endregion
523
+ //#region src/hooks/useLayoutState.js
524
+ /**
525
+ * Creates a state that persists in the global LayoutContext.
526
+ *
527
+ * @example
528
+ * ```jsx
529
+ * function Group(props) {
530
+ * const [ open, setOpen ] = useLayoutState([ 'groups', 'foo', 'open' ], false);
531
+ * }
532
+ * ```
533
+ *
534
+ * @param {(string|number)[]} path
535
+ * @param {any} [defaultValue]
536
+ *
537
+ * @returns {[ any, Function ]}
538
+ */
539
+ function useLayoutState(path, defaultValue) {
540
+ const { getLayoutForKey, setLayoutForKey } = useContext(LayoutContext);
541
+ return [getLayoutForKey(path, defaultValue), useCallback((newValue) => {
542
+ setLayoutForKey(path, newValue);
543
+ }, [setLayoutForKey])];
544
+ }
545
+ //#endregion
546
+ //#region src/hooks/usePrevious.js
547
+ /**
548
+ * @pinussilvestrus: we need to introduce our own hook to persist the previous
549
+ * state on updates.
550
+ *
551
+ * cf. https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state
552
+ */
553
+ function usePrevious(value) {
554
+ const ref = useRef();
555
+ useEffect(() => {
556
+ ref.current = value;
557
+ });
558
+ return ref.current;
559
+ }
560
+ //#endregion
561
+ //#region src/hooks/useShowEntryEvent.js
562
+ /**
563
+ * Subscribe to `propertiesPanel.showEntry`.
564
+ *
565
+ * @param {string} id
566
+ *
567
+ * @returns {import('preact').Ref}
568
+ */
569
+ function useShowEntryEvent(id) {
570
+ const { onShow } = useContext(LayoutContext);
571
+ const ref = useRef();
572
+ const focus = useRef(false);
573
+ const onShowEntry = useCallback((event) => {
574
+ if (event.id === id) {
575
+ onShow();
576
+ if (!focus.current) focus.current = true;
577
+ }
578
+ }, [id]);
579
+ useEffect(() => {
580
+ if (focus.current && ref.current) {
581
+ if (isFunction(ref.current.focus)) ref.current.focus();
582
+ if (isFunction(ref.current.select)) ref.current.select();
583
+ focus.current = false;
584
+ }
585
+ });
586
+ useEvent("propertiesPanel.showEntry", onShowEntry);
587
+ return ref;
588
+ }
589
+ //#endregion
590
+ //#region src/hooks/useStickyIntersectionObserver.js
591
+ /**
592
+ * @callback setSticky
593
+ * @param {boolean} value
594
+ */
595
+ /**
596
+ * Use IntersectionObserver to identify when DOM element is in sticky mode.
597
+ * If sticky is observered setSticky(true) will be called.
598
+ * If sticky mode is left, setSticky(false) will be called.
599
+ *
600
+ *
601
+ * @param {Object} ref
602
+ * @param {string} scrollContainerSelector
603
+ * @param {setSticky} setSticky
604
+ */
605
+ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
606
+ const [scrollContainer, setScrollContainer] = useState(query(scrollContainerSelector));
607
+ const updateScrollContainer = useCallback(() => {
608
+ const newScrollContainer = query(scrollContainerSelector);
609
+ if (newScrollContainer !== scrollContainer) setScrollContainer(newScrollContainer);
610
+ }, [scrollContainerSelector, scrollContainer]);
611
+ useEffect(() => {
612
+ updateScrollContainer();
613
+ }, [updateScrollContainer]);
614
+ useEvent("propertiesPanel.attach", updateScrollContainer);
615
+ useEvent("propertiesPanel.detach", updateScrollContainer);
616
+ useEffect(() => {
617
+ const Observer = IntersectionObserver;
618
+ if (!Observer) return;
619
+ if (!ref.current || !scrollContainer) return;
620
+ const observer = new Observer((entries) => {
621
+ if (scrollContainer.scrollHeight === 0) return;
622
+ entries.forEach((entry) => {
623
+ if (entry.intersectionRatio < 1) setSticky(true);
624
+ else if (entry.intersectionRatio === 1) setSticky(false);
625
+ });
626
+ }, {
627
+ root: scrollContainer,
628
+ rootMargin: "0px 0px 999999% 0px",
629
+ threshold: [1]
630
+ });
631
+ observer.observe(ref.current);
632
+ return () => {
633
+ observer.unobserve(ref.current);
634
+ };
635
+ }, [
636
+ ref.current,
637
+ scrollContainer,
638
+ setSticky
639
+ ]);
640
+ }
641
+ //#endregion
642
+ //#region src/hooks/useStaticCallback.js
643
+ /**
644
+ * Creates a static function reference with changing body.
645
+ * This is necessary when external libraries require a callback function
646
+ * that has references to state variables.
647
+ *
648
+ * Usage:
649
+ * const callback = useStaticCallback((val) => {val === currentState});
650
+ *
651
+ * The `callback` reference is static and can be safely used in external
652
+ * libraries or as a prop that does not cause rerendering of children.
653
+ *
654
+ * @param {Function} callback function with changing reference
655
+ * @returns {Function} static function reference
656
+ */
657
+ function useStaticCallback(callback) {
658
+ const callbackRef = useRef(callback);
659
+ callbackRef.current = callback;
660
+ return useCallback((...args) => callbackRef.current(...args), []);
661
+ }
662
+ //#endregion
663
+ //#region src/hooks/useElementVisible.js
664
+ function useElementVisible(element) {
665
+ const [visible, setVisible] = useState(!!element && !!element.clientHeight);
666
+ useLayoutEffect(() => {
667
+ if (!element) return;
668
+ const resizeObserver = new ResizeObserver(([entry]) => {
669
+ requestAnimationFrame(() => {
670
+ const newVisible = !!entry.contentRect.height;
671
+ if (newVisible !== visible) setVisible(newVisible);
672
+ });
673
+ });
674
+ resizeObserver.observe(element);
675
+ return () => resizeObserver.disconnect();
676
+ }, [element, visible]);
677
+ return visible;
678
+ }
679
+ //#endregion
680
+ //#region src/components/Group.js
681
+ /**
682
+ * @param {import('../PropertiesPanel').GroupDefinition} props
683
+ */
684
+ function Group(props) {
685
+ const { element, entries = [], id, label, shouldOpen = false } = props;
686
+ const groupRef = useRef(null);
687
+ const [open, setOpen] = useLayoutState([
688
+ "groups",
689
+ id,
690
+ "open"
691
+ ], shouldOpen);
692
+ const onShow = useCallback(() => setOpen(true), [setOpen]);
693
+ const toggleOpen = () => setOpen(!open);
694
+ const [edited, setEdited] = useState(false);
695
+ const [sticky, setSticky] = useState(false);
696
+ useEffect(() => {
697
+ const scheduled = requestAnimationFrame(() => {
698
+ const hasOneEditedEntry = entries.find((entry) => {
699
+ const { id, isEdited } = entry;
700
+ const entryNode = query(`[data-entry-id="${id}"]`);
701
+ if (!isFunction(isEdited) || !entryNode) return false;
702
+ return isEdited(query(".bio-properties-panel-input", entryNode));
703
+ });
704
+ setEdited(hasOneEditedEntry);
705
+ });
706
+ return () => cancelAnimationFrame(scheduled);
707
+ }, [entries, setEdited]);
708
+ const allErrors = useErrors();
709
+ const hasErrors = entries.some((entry) => allErrors[entry.id]);
710
+ useStickyIntersectionObserver(groupRef, "div.bio-properties-panel-scroll-container", setSticky);
711
+ const propertiesPanelContext = {
712
+ ...useContext(LayoutContext),
713
+ onShow
714
+ };
715
+ return jsxs("div", {
716
+ class: "bio-properties-panel-group",
717
+ "data-group-id": "group-" + id,
718
+ ref: groupRef,
719
+ children: [jsxs("div", {
720
+ class: classnames("bio-properties-panel-group-header", edited ? "" : "empty", open ? "open" : "", sticky && open ? "sticky" : ""),
721
+ onClick: toggleOpen,
722
+ children: [jsx("div", {
723
+ class: "bio-properties-panel-group-header-title",
724
+ children: jsx(TooltipWrapper, {
725
+ value: props.tooltip,
726
+ forId: "group-" + id,
727
+ element,
728
+ parent: groupRef,
729
+ children: label
730
+ })
731
+ }), jsxs("div", {
732
+ class: "bio-properties-panel-group-header-buttons",
733
+ children: [jsx(DataMarker, {
734
+ edited,
735
+ hasErrors
736
+ }), jsx("button", {
737
+ type: "button",
738
+ title: "Toggle section",
739
+ class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
740
+ children: jsx(ArrowIcon, { class: open ? "bio-properties-panel-arrow-down" : "bio-properties-panel-arrow-right" })
741
+ })]
742
+ })]
743
+ }), jsx("div", {
744
+ class: classnames("bio-properties-panel-group-entries", open ? "open" : ""),
745
+ children: jsx(LayoutContext.Provider, {
746
+ value: propertiesPanelContext,
747
+ children: entries.map((entry) => {
748
+ const { component: Component, id } = entry;
749
+ return createElement(Component, {
750
+ ...entry,
751
+ element,
752
+ key: id
753
+ });
754
+ })
755
+ })
756
+ })]
757
+ });
758
+ }
759
+ function DataMarker(props) {
760
+ const { edited, hasErrors } = props;
761
+ if (hasErrors) return jsx("div", {
762
+ title: "Section contains an error",
763
+ class: "bio-properties-panel-dot bio-properties-panel-dot--error"
764
+ });
765
+ if (edited) return jsx("div", {
766
+ title: "Section contains edits",
767
+ class: "bio-properties-panel-dot"
768
+ });
769
+ return null;
770
+ }
771
+ //#endregion
772
+ //#region src/components/Placeholder.js
773
+ /**
774
+ * @typedef { {
775
+ * text: (element: object) => string,
776
+ * icon?: (element: Object) => import('preact').Component
777
+ * } } PlaceholderDefinition
778
+ *
779
+ * @param { PlaceholderDefinition } props
780
+ */
781
+ function Placeholder(props) {
782
+ const { text, icon: Icon } = props;
783
+ return jsx("div", {
784
+ class: "bio-properties-panel open",
785
+ children: jsxs("section", {
786
+ class: "bio-properties-panel-placeholder",
787
+ children: [Icon && jsx(Icon, { class: "bio-properties-panel-placeholder-icon" }), jsx("p", {
788
+ class: "bio-properties-panel-placeholder-text",
789
+ children: text
790
+ })]
791
+ })
792
+ });
793
+ }
794
+ //#endregion
795
+ //#region src/PropertiesPanel.js
796
+ var DEFAULT_LAYOUT = {};
797
+ var DEFAULT_DESCRIPTION = {};
798
+ var DEFAULT_TOOLTIP = {};
799
+ /**
800
+ * @typedef { {
801
+ * component: import('preact').Component,
802
+ * id: String,
803
+ * isEdited?: Function
804
+ * } } EntryDefinition
805
+ *
806
+ * @typedef { {
807
+ * autoFocusEntry: String,
808
+ * autoOpen?: Boolean,
809
+ * entries: Array<EntryDefinition>,
810
+ * id: String,
811
+ * label: String,
812
+ * remove: (event: MouseEvent) => void
813
+ * } } ListItemDefinition
814
+ *
815
+ * @typedef { {
816
+ * add: (event: MouseEvent) => void,
817
+ * component: import('preact').Component,
818
+ * element: Object,
819
+ * id: String,
820
+ * items: Array<ListItemDefinition>,
821
+ * label: String,
822
+ * shouldOpen?: Boolean
823
+ * } } ListGroupDefinition
824
+ *
825
+ * @typedef { {
826
+ * component?: import('preact').Component,
827
+ * entries: Array<EntryDefinition>,
828
+ * id: String,
829
+ * label: String,
830
+ * shouldOpen?: Boolean
831
+ * } } GroupDefinition
832
+ *
833
+ * @typedef { {
834
+ * [id: String]: GetDescriptionFunction
835
+ * } } DescriptionConfig
836
+ *
837
+ * @typedef { {
838
+ * [id: String]: GetTooltipFunction
839
+ * } } TooltipConfig
840
+ *
841
+ * @callback { {
842
+ * @param {string} id
843
+ * @param {Object} element
844
+ * @returns {string}
845
+ * } } GetDescriptionFunction
846
+ *
847
+ * @callback { {
848
+ * @param {string} id
849
+ * @param {Object} element
850
+ * @returns {string}
851
+ * } } GetTooltipFunction
852
+ *
853
+ * @typedef { {
854
+ * getEmpty: (element: object) => import('./components/Placeholder').PlaceholderDefinition,
855
+ * getMultiple: (element: Object) => import('./components/Placeholder').PlaceholderDefinition
856
+ * } } PlaceholderProvider
857
+ *
858
+ */
859
+ /**
860
+ * A basic properties panel component. Describes *how* content will be rendered, accepts
861
+ * data from implementor to describe *what* will be rendered.
862
+ *
863
+ * @param {Object} props
864
+ * @param {Object|Array} props.element
865
+ * @param {import('./components/Header').HeaderProvider} props.headerProvider
866
+ * @param {PlaceholderProvider} [props.placeholderProvider]
867
+ * @param {Array<GroupDefinition|ListGroupDefinition>} props.groups
868
+ * @param {Object} [props.layoutConfig]
869
+ * @param {Function} [props.layoutChanged]
870
+ * @param {DescriptionConfig} [props.descriptionConfig]
871
+ * @param {Function} [props.descriptionLoaded]
872
+ * @param {TooltipConfig} [props.tooltipConfig]
873
+ * @param {Function} [props.tooltipLoaded]
874
+ * @param {HTMLElement} [props.feelPopupContainer]
875
+ * @param {Function} [props.getFeelPopupLinks]
876
+ * @param {Object} [props.eventBus]
877
+ */
878
+ function PropertiesPanel(props) {
879
+ const { element, headerProvider, placeholderProvider, groups, layoutConfig, layoutChanged, descriptionConfig, descriptionLoaded, tooltipConfig, tooltipLoaded, eventBus } = props;
880
+ const [layout, setLayout] = useState(createLayout(layoutConfig));
881
+ useUpdateLayoutEffect(() => {
882
+ const newLayout = createLayout(layoutConfig);
883
+ setLayout(newLayout);
884
+ }, [layoutConfig]);
885
+ useEffect(() => {
886
+ if (typeof layoutChanged === "function") layoutChanged(layout);
887
+ }, [layout, layoutChanged]);
888
+ const getLayoutForKey = (key, defaultValue) => {
889
+ return get(layout, key, defaultValue);
890
+ };
891
+ const setLayoutForKey = (key, config) => {
892
+ const newLayout = assign({}, layout);
893
+ set(newLayout, key, config);
894
+ setLayout(newLayout);
895
+ };
896
+ const layoutContext = {
897
+ layout,
898
+ setLayout,
899
+ getLayoutForKey,
900
+ setLayoutForKey
901
+ };
902
+ const description = useMemo(() => createDescriptionContext(descriptionConfig), [descriptionConfig]);
903
+ useEffect(() => {
904
+ if (typeof descriptionLoaded === "function") descriptionLoaded(description);
905
+ }, [description, descriptionLoaded]);
906
+ const getDescriptionForId = (id, element) => {
907
+ return description[id] && description[id](element);
908
+ };
909
+ const descriptionContext = {
910
+ description,
911
+ getDescriptionForId
912
+ };
913
+ const tooltip = useMemo(() => createTooltipContext(tooltipConfig), [tooltipConfig]);
914
+ useEffect(() => {
915
+ if (typeof tooltipLoaded === "function") tooltipLoaded(tooltip);
916
+ }, [tooltip, tooltipLoaded]);
917
+ const getTooltipForId = (id, element) => {
918
+ return tooltip[id] && tooltip[id](element);
919
+ };
920
+ const tooltipContext = {
921
+ tooltip,
922
+ getTooltipForId
923
+ };
924
+ const [errors, setErrors] = useState({});
925
+ const onSetErrors = ({ errors }) => setErrors(errors);
926
+ useEvent("propertiesPanel.setErrors", onSetErrors, eventBus);
927
+ const errorsContext = { errors };
928
+ const eventContext = { eventBus };
929
+ const propertiesPanelContext = { element };
930
+ if (placeholderProvider && !element) return jsx(Placeholder, { ...placeholderProvider.getEmpty() });
931
+ if (placeholderProvider && isArray(element)) return jsx(Placeholder, { ...placeholderProvider.getMultiple() });
932
+ return jsx(LayoutContext.Provider, {
933
+ value: propertiesPanelContext,
934
+ children: jsx(ErrorsContext.Provider, {
935
+ value: errorsContext,
936
+ children: jsx(DescriptionContext.Provider, {
937
+ value: descriptionContext,
938
+ children: jsx(TooltipContext.Provider, {
939
+ value: tooltipContext,
940
+ children: jsx(LayoutContext.Provider, {
941
+ value: layoutContext,
942
+ children: jsx(EventContext.Provider, {
943
+ value: eventContext,
944
+ children: jsxs("div", {
945
+ class: "bio-properties-panel",
946
+ children: [jsx(Header, {
947
+ element,
948
+ headerProvider
949
+ }), jsx("div", {
950
+ class: "bio-properties-panel-scroll-container",
951
+ children: groups.map((group) => {
952
+ const { component: Component = Group, id } = group;
953
+ return createElement(Component, {
954
+ ...group,
955
+ key: id,
956
+ element
957
+ });
958
+ })
959
+ })]
960
+ })
961
+ })
962
+ })
963
+ })
964
+ })
965
+ })
966
+ });
967
+ }
968
+ function createLayout(overrides = {}, defaults = DEFAULT_LAYOUT) {
969
+ return {
970
+ ...defaults,
971
+ ...overrides
972
+ };
973
+ }
974
+ function createDescriptionContext(overrides = {}) {
975
+ return {
976
+ ...DEFAULT_DESCRIPTION,
977
+ ...overrides
978
+ };
979
+ }
980
+ function createTooltipContext(overrides = {}) {
981
+ return {
982
+ ...DEFAULT_TOOLTIP,
983
+ ...overrides
984
+ };
985
+ }
986
+ /**
987
+ * This hook behaves like useLayoutEffect, but does not trigger on the first render.
988
+ *
989
+ * @param {Function} effect
990
+ * @param {Array} deps
991
+ */
992
+ function useUpdateLayoutEffect(effect, deps) {
993
+ const isMounted = useRef(false);
994
+ useLayoutEffect(() => {
995
+ if (isMounted.current) return effect();
996
+ else isMounted.current = true;
997
+ }, deps);
998
+ }
999
+ //#endregion
1000
+ //#region src/components/DropdownButton.js
1001
+ /**
1002
+ *
1003
+ * @param {object} props
1004
+ * @param {string} [props.class]
1005
+ * @param {import('preact').Component[]} [props.menuItems]
1006
+ * @returns
1007
+ */
1008
+ function DropdownButton(props) {
1009
+ const { class: className, children, menuItems = [] } = props;
1010
+ const dropdownRef = useRef(null);
1011
+ const menuRef = useRef(null);
1012
+ const [open, setOpen] = useState(false);
1013
+ const close = () => setOpen(false);
1014
+ function onDropdownToggle(event) {
1015
+ if (menuRef.current && menuRef.current.contains(event.target)) return;
1016
+ event.stopPropagation();
1017
+ setOpen((open) => !open);
1018
+ }
1019
+ function onActionClick(event, action) {
1020
+ event.stopPropagation();
1021
+ close();
1022
+ action();
1023
+ }
1024
+ useGlobalClick([dropdownRef.current], () => close());
1025
+ return jsxs("div", {
1026
+ class: classnames("bio-properties-panel-dropdown-button", { open }, className),
1027
+ onClick: onDropdownToggle,
1028
+ ref: dropdownRef,
1029
+ children: [children, jsx("div", {
1030
+ class: "bio-properties-panel-dropdown-button__menu",
1031
+ ref: menuRef,
1032
+ children: menuItems.map((item, index) => jsx(MenuItem, {
1033
+ onClick: onActionClick,
1034
+ item
1035
+ }, index))
1036
+ })]
1037
+ });
1038
+ }
1039
+ function MenuItem({ item, onClick }) {
1040
+ if (item.separator) return jsx("div", { class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--separator" });
1041
+ if (item.action) return jsx("button", {
1042
+ type: "button",
1043
+ class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--actionable",
1044
+ onClick: (event) => onClick(event, item.action),
1045
+ children: item.entry
1046
+ });
1047
+ return jsx("div", {
1048
+ class: "bio-properties-panel-dropdown-button__menu-item",
1049
+ children: item.entry
1050
+ });
1051
+ }
1052
+ /**
1053
+ *
1054
+ * @param {Array<null | Element>} ignoredElements
1055
+ * @param {Function} callback
1056
+ */
1057
+ function useGlobalClick(ignoredElements, callback) {
1058
+ useEffect(() => {
1059
+ /**
1060
+ * @param {MouseEvent} event
1061
+ */
1062
+ function listener(event) {
1063
+ if (ignoredElements.some((element) => element && element.contains(event.target))) return;
1064
+ callback();
1065
+ }
1066
+ document.addEventListener("click", listener, { capture: true });
1067
+ return () => document.removeEventListener("click", listener, { capture: true });
1068
+ }, [...ignoredElements, callback]);
1069
+ }
1070
+ //#endregion
1071
+ //#region src/components/HeaderButton.js
1072
+ function HeaderButton(props) {
1073
+ const { children = null, class: classname, onClick = () => {}, ...otherProps } = props;
1074
+ return jsx("button", {
1075
+ type: "button",
1076
+ ...otherProps,
1077
+ onClick,
1078
+ class: classnames("bio-properties-panel-group-header-button", classname),
1079
+ children
1080
+ });
1081
+ }
1082
+ //#endregion
1083
+ //#region src/components/util/translateFallback.js
1084
+ /**
1085
+ * @typedef { {
1086
+ * [key: string]: string;
1087
+ * } } TranslateReplacements
1088
+ */
1089
+ /**
1090
+ * A simple translation stub to be used for multi-language support.
1091
+ * Can be easily replaced with a more sophisticated solution.
1092
+ *
1093
+ * @param {string} template to interpolate
1094
+ * @param {TranslateReplacements} [replacements] a map with substitutes
1095
+ *
1096
+ * @return {string} the translated string
1097
+ */
1098
+ function translateFallback(template, replacements) {
1099
+ replacements = replacements || {};
1100
+ return template.replace(/{([^}]+)}/g, function(_, key) {
1101
+ return replacements[key] || "{" + key + "}";
1102
+ });
1103
+ }
1104
+ //#endregion
1105
+ //#region src/components/entries/Collapsible.js
1106
+ function CollapsibleEntry(props) {
1107
+ const { element, entries = [], id, label, open: shouldOpen, remove, translate = translateFallback } = props;
1108
+ const [open, setOpen] = useState(shouldOpen);
1109
+ const toggleOpen = () => setOpen(!open);
1110
+ const { onShow } = useContext(LayoutContext);
1111
+ const propertiesPanelContext = {
1112
+ ...useContext(LayoutContext),
1113
+ onShow: useCallback(() => {
1114
+ setOpen(true);
1115
+ if (isFunction(onShow)) onShow();
1116
+ }, [onShow, setOpen])
1117
+ };
1118
+ const placeholderLabel = translate("<empty>");
1119
+ return jsxs("div", {
1120
+ "data-entry-id": id,
1121
+ class: classnames("bio-properties-panel-collapsible-entry", open ? "open" : ""),
1122
+ children: [jsxs("div", {
1123
+ class: "bio-properties-panel-collapsible-entry-header",
1124
+ onClick: toggleOpen,
1125
+ children: [
1126
+ jsx("div", {
1127
+ class: classnames("bio-properties-panel-collapsible-entry-header-title", !label && "empty"),
1128
+ children: label || placeholderLabel
1129
+ }),
1130
+ jsx("button", {
1131
+ type: "button",
1132
+ title: translate("Toggle list item"),
1133
+ class: "bio-properties-panel-arrow bio-properties-panel-collapsible-entry-arrow",
1134
+ children: jsx(ArrowIcon, { class: open ? "bio-properties-panel-arrow-down" : "bio-properties-panel-arrow-right" })
1135
+ }),
1136
+ remove ? jsx("button", {
1137
+ type: "button",
1138
+ title: translate("Delete item"),
1139
+ class: "bio-properties-panel-remove-entry",
1140
+ onClick: remove,
1141
+ children: jsx(DeleteIcon, {})
1142
+ }) : null
1143
+ ]
1144
+ }), jsx("div", {
1145
+ class: classnames("bio-properties-panel-collapsible-entry-entries", open ? "open" : ""),
1146
+ children: jsx(LayoutContext.Provider, {
1147
+ value: propertiesPanelContext,
1148
+ children: entries.map((entry) => {
1149
+ const { component: Component, id } = entry;
1150
+ return createElement(Component, {
1151
+ ...entry,
1152
+ element,
1153
+ key: id
1154
+ });
1155
+ })
1156
+ })
1157
+ })]
1158
+ });
1159
+ }
1160
+ //#endregion
1161
+ //#region src/components/ListItem.js
1162
+ /**
1163
+ * @param {import('../PropertiesPanel').ListItemDefinition} props
1164
+ */
1165
+ function ListItem(props) {
1166
+ const { autoFocusEntry, autoOpen, translate = translateFallback } = props;
1167
+ useEffect(() => {
1168
+ if (autoOpen && autoFocusEntry) {
1169
+ const entry = query(`[data-entry-id="${autoFocusEntry}"]`);
1170
+ const focusableInput = query(".bio-properties-panel-input", entry);
1171
+ if (focusableInput) {
1172
+ if (isFunction(focusableInput.select)) focusableInput.select();
1173
+ else if (isFunction(focusableInput.focus)) focusableInput.focus();
1174
+ focusableInput.scrollIntoView();
1175
+ }
1176
+ }
1177
+ }, [autoOpen, autoFocusEntry]);
1178
+ return jsx("div", {
1179
+ class: "bio-properties-panel-list-item",
1180
+ children: jsx(CollapsibleEntry, {
1181
+ ...props,
1182
+ open: autoOpen,
1183
+ translate
1184
+ })
1185
+ });
1186
+ }
1187
+ //#endregion
1188
+ //#region src/components/ListGroup.js
1189
+ var noop$6 = () => {};
1190
+ /**
1191
+ * @param {import('../PropertiesPanel').ListGroupDefinition} props
1192
+ */
1193
+ function ListGroup(props) {
1194
+ const { add, element, id, items, label, shouldOpen = false, translate = translateFallback } = props;
1195
+ useEffect(() => {
1196
+ if (props.shouldSort != void 0) console.warn("the property 'shouldSort' is no longer supported");
1197
+ }, [props.shouldSort]);
1198
+ const groupRef = useRef(null);
1199
+ const [open, setOpen] = useLayoutState([
1200
+ "groups",
1201
+ id,
1202
+ "open"
1203
+ ], shouldOpen);
1204
+ const [sticky, setSticky] = useState(false);
1205
+ const onShow = useCallback(() => setOpen(true), [setOpen]);
1206
+ const [localItems, setLocalItems] = useState([]);
1207
+ const [addTriggered, setAddTriggered] = useState(false);
1208
+ const prevElement = usePrevious(element);
1209
+ const toggleOpen = useCallback(() => setOpen(!open), [open]);
1210
+ const openItemIds = element === prevElement && open && addTriggered ? getNewItemIds(items, localItems) : [];
1211
+ useEffect(() => {
1212
+ setLocalItems(items);
1213
+ setAddTriggered(false);
1214
+ }, [items]);
1215
+ useStickyIntersectionObserver(groupRef, "div.bio-properties-panel-scroll-container", setSticky);
1216
+ const hasItems = !!items.length;
1217
+ const propertiesPanelContext = {
1218
+ ...useContext(LayoutContext),
1219
+ onShow
1220
+ };
1221
+ const handleAddClick = (e) => {
1222
+ setAddTriggered(true);
1223
+ setOpen(true);
1224
+ add(e);
1225
+ };
1226
+ const allErrors = useErrors();
1227
+ const hasError = items.some((item) => {
1228
+ if (allErrors[item.id]) return true;
1229
+ if (!item.entries) return;
1230
+ return item.entries.some((entry) => allErrors[entry.id]);
1231
+ });
1232
+ return jsxs("div", {
1233
+ class: "bio-properties-panel-group",
1234
+ "data-group-id": "group-" + id,
1235
+ ref: groupRef,
1236
+ children: [jsxs("div", {
1237
+ class: classnames("bio-properties-panel-group-header", hasItems ? "" : "empty", hasItems && open ? "open" : "", sticky && open ? "sticky" : ""),
1238
+ onClick: hasItems ? toggleOpen : noop$6,
1239
+ children: [jsx("div", {
1240
+ class: "bio-properties-panel-group-header-title",
1241
+ children: jsx(TooltipWrapper, {
1242
+ value: props.tooltip,
1243
+ forId: "group-" + id,
1244
+ element,
1245
+ parent: groupRef,
1246
+ children: label
1247
+ })
1248
+ }), jsxs("div", {
1249
+ class: "bio-properties-panel-group-header-buttons",
1250
+ children: [
1251
+ add ? jsxs("button", {
1252
+ type: "button",
1253
+ title: translate("Create new list item"),
1254
+ class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
1255
+ onClick: handleAddClick,
1256
+ children: [jsx(CreateIcon, {}), !hasItems ? jsx("span", {
1257
+ class: "bio-properties-panel-add-entry-label",
1258
+ children: translate("Create")
1259
+ }) : null]
1260
+ }) : null,
1261
+ hasItems ? jsx("div", {
1262
+ title: translate(`List contains {numOfItems} item${items.length != 1 ? "s" : ""}`, { numOfItems: items.length }),
1263
+ class: classnames("bio-properties-panel-list-badge", hasError ? "bio-properties-panel-list-badge--error" : ""),
1264
+ children: items.length
1265
+ }) : null,
1266
+ hasItems ? jsx("button", {
1267
+ type: "button",
1268
+ title: translate("Toggle section"),
1269
+ class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
1270
+ children: jsx(ArrowIcon, { class: open ? "bio-properties-panel-arrow-down" : "bio-properties-panel-arrow-right" })
1271
+ }) : null
1272
+ ]
1273
+ })]
1274
+ }), jsx("div", {
1275
+ class: classnames("bio-properties-panel-list", open && hasItems ? "open" : ""),
1276
+ children: jsx(LayoutContext.Provider, {
1277
+ value: propertiesPanelContext,
1278
+ children: items.map((item, index) => {
1279
+ if (!item) return;
1280
+ const { id } = item;
1281
+ const autoOpen = openItemIds.includes(item.id);
1282
+ return createElement(ListItem, {
1283
+ ...item,
1284
+ autoOpen,
1285
+ element,
1286
+ index,
1287
+ key: id,
1288
+ translate
1289
+ });
1290
+ })
1291
+ })
1292
+ })]
1293
+ });
1294
+ }
1295
+ function getNewItemIds(newItems, oldItems) {
1296
+ const newIds = newItems.map((item) => item.id);
1297
+ const oldIds = oldItems.map((item) => item.id);
1298
+ return newIds.filter((itemId) => !oldIds.includes(itemId));
1299
+ }
1300
+ //#endregion
1301
+ //#region src/components/entries/Description.js
1302
+ /**
1303
+ * @param {Object} props
1304
+ * @param {Object} props.element
1305
+ * @param {String} props.forId - id of the entry the description is used for
1306
+ * @param {String} props.value
1307
+ */
1308
+ function Description(props) {
1309
+ const { element, forId, value } = props;
1310
+ const contextDescription = useDescriptionContext(forId, element);
1311
+ const description = value || contextDescription;
1312
+ if (description) return jsx("div", {
1313
+ class: "bio-properties-panel-description",
1314
+ children: description
1315
+ });
1316
+ }
1317
+ //#endregion
1318
+ //#region src/components/entries/Checkbox.js
1319
+ function Checkbox(props) {
1320
+ const { id, label, onChange, disabled, value = false, onFocus, onBlur, tooltip } = props;
1321
+ const [localValue, setLocalValue] = useState(value);
1322
+ const handleChangeCallback = ({ target }) => {
1323
+ onChange(target.checked);
1324
+ };
1325
+ const handleChange = (e) => {
1326
+ handleChangeCallback(e);
1327
+ setLocalValue(e.target.value);
1328
+ };
1329
+ useEffect(() => {
1330
+ if (value === localValue) return;
1331
+ setLocalValue(value);
1332
+ }, [value]);
1333
+ const ref = useShowEntryEvent(id);
1334
+ return jsxs("div", {
1335
+ class: "bio-properties-panel-checkbox",
1336
+ children: [jsx("input", {
1337
+ ref,
1338
+ id: prefixId$8(id),
1339
+ name: id,
1340
+ onFocus,
1341
+ onBlur,
1342
+ type: "checkbox",
1343
+ class: "bio-properties-panel-input",
1344
+ onChange: handleChange,
1345
+ checked: localValue,
1346
+ disabled
1347
+ }), jsx("label", {
1348
+ for: prefixId$8(id),
1349
+ class: "bio-properties-panel-label",
1350
+ children: jsx(TooltipWrapper, {
1351
+ value: tooltip,
1352
+ forId: id,
1353
+ element: props.element,
1354
+ children: label
1355
+ })
1356
+ })]
1357
+ });
1358
+ }
1359
+ /**
1360
+ * @param {Object} props
1361
+ * @param {Object} props.element
1362
+ * @param {String} props.id
1363
+ * @param {String} props.description
1364
+ * @param {String} props.label
1365
+ * @param {Function} props.getValue
1366
+ * @param {Function} props.setValue
1367
+ * @param {Function} props.onFocus
1368
+ * @param {Function} props.onBlur
1369
+ * @param {string|import('preact').Component} props.tooltip
1370
+ * @param {boolean} [props.disabled]
1371
+ */
1372
+ function CheckboxEntry(props) {
1373
+ const { element, id, description, label, getValue, setValue, disabled, onFocus, onBlur, tooltip } = props;
1374
+ const value = getValue(element);
1375
+ const error = useError(id);
1376
+ return jsxs("div", {
1377
+ class: "bio-properties-panel-entry bio-properties-panel-checkbox-entry",
1378
+ "data-entry-id": id,
1379
+ children: [
1380
+ jsx(Checkbox, {
1381
+ disabled,
1382
+ id,
1383
+ label,
1384
+ onChange: setValue,
1385
+ onFocus,
1386
+ onBlur,
1387
+ value,
1388
+ tooltip,
1389
+ element
1390
+ }, element),
1391
+ error && jsx("div", {
1392
+ class: "bio-properties-panel-error",
1393
+ children: error
1394
+ }),
1395
+ jsx(Description, {
1396
+ forId: id,
1397
+ element,
1398
+ value: description
1399
+ })
1400
+ ]
1401
+ });
1402
+ }
1403
+ function isEdited(node) {
1404
+ return node && !!node.checked;
1405
+ }
1406
+ function prefixId$8(id) {
1407
+ return `bio-properties-panel-${id}`;
1408
+ }
1409
+ //#endregion
1410
+ //#region src/components/entries/CheckboxGroup.js
1411
+ /**
1412
+ * @param {Object} props
1413
+ * @param {Object} props.element
1414
+ * @param {String} props.id
1415
+ * @param {String} props.label
1416
+ * @param {Array<{label: String, value: *}>} props.options
1417
+ * @param {Function} props.getValue
1418
+ * @param {Function} props.setValue
1419
+ * @param {String} [props.description]
1420
+ * @param {string|import('preact').Component} [props.tooltip]
1421
+ * @param {boolean} [props.disabled]
1422
+ * @param {Function} [props.onFocus]
1423
+ * @param {Function} [props.onBlur]
1424
+ */
1425
+ function CheckboxGroup(props) {
1426
+ const { element, id, label, description, tooltip, options = [], getValue, setValue, disabled, onFocus, onBlur } = props;
1427
+ const value = getValue(element) || [];
1428
+ const handleOptionChange = (optionValue, checked) => {
1429
+ const newValue = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
1430
+ setValue(newValue);
1431
+ };
1432
+ return jsxs("div", {
1433
+ class: "bio-properties-panel-entry bio-properties-panel-checkbox-group",
1434
+ "data-entry-id": id,
1435
+ children: [
1436
+ jsx("div", {
1437
+ class: "bio-properties-panel-group-header",
1438
+ children: jsx("p", {
1439
+ class: "bio-properties-panel-label",
1440
+ children: jsx(TooltipWrapper, {
1441
+ value: tooltip,
1442
+ forId: id,
1443
+ element,
1444
+ children: label
1445
+ })
1446
+ })
1447
+ }),
1448
+ jsx("div", {
1449
+ class: "bio-properties-panel-checkbox-group-entries",
1450
+ children: options.map((option) => jsx(CheckboxEntry, {
1451
+ id: `${id}-${option.value}`,
1452
+ label: option.label,
1453
+ setValue: (checked) => handleOptionChange(option.value, checked),
1454
+ getValue: () => value.includes(option.value),
1455
+ disabled,
1456
+ onFocus,
1457
+ onBlur,
1458
+ element
1459
+ }, option.value))
1460
+ }),
1461
+ jsx(Description, {
1462
+ forId: id,
1463
+ element,
1464
+ value: description
1465
+ })
1466
+ ]
1467
+ });
1468
+ }
1469
+ //#endregion
1470
+ //#region src/components/entries/JsonEditor.js
1471
+ var ExternalChange = Annotation.define();
1472
+ var parseJsonLinter = jsonParseLinter();
1473
+ /**
1474
+ * A CodeMirror based JSON editor for the properties panel.
1475
+ *
1476
+ * @param {object} props
1477
+ * @param {string} props.id
1478
+ * @param {string} props.label
1479
+ * @param {string} [props.value]
1480
+ * @param {Function} props.onInput
1481
+ * @param {boolean} [props.disabled]
1482
+ * @param {string} [props.placeholder]
1483
+ * @param {string} [props.tooltip]
1484
+ * @param {object} [props.element]
1485
+ */
1486
+ function JsonEditor(props) {
1487
+ const { id, label, debounce, value = "", onInput: commitValue, disabled, placeholder: placeholder$1, tooltip, element } = props;
1488
+ const containerRef = useRef(null);
1489
+ const viewRef = useRef(null);
1490
+ const editorRef = useShowEntryEvent(id);
1491
+ const handleChange = useDebounce(useStaticCallback((newValue) => {
1492
+ commitValue(newValue === "" ? void 0 : newValue);
1493
+ }), debounce);
1494
+ useEffect(() => {
1495
+ const view = new EditorView({
1496
+ state: EditorState.create({
1497
+ doc: value,
1498
+ extensions: [
1499
+ highlightSpecialChars(),
1500
+ history(),
1501
+ drawSelection(),
1502
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
1503
+ foldGutter(),
1504
+ bracketMatching(),
1505
+ closeBrackets(),
1506
+ keymap.of([
1507
+ ...defaultKeymap,
1508
+ ...historyKeymap,
1509
+ ...closeBracketsKeymap,
1510
+ ...foldKeymap
1511
+ ]),
1512
+ EditorState.tabSize.of(2),
1513
+ EditorView.lineWrapping,
1514
+ EditorState.readOnly.of(!!disabled),
1515
+ ...placeholder$1 ? [placeholder(placeholder$1)] : [],
1516
+ EditorView.updateListener.of((update) => {
1517
+ const isExternal = update.transactions.some((tr) => tr.annotation(ExternalChange));
1518
+ if (update.docChanged && !isExternal) handleChange(update.state.doc.toString());
1519
+ }),
1520
+ json(),
1521
+ linter((view) => {
1522
+ if (!view.state.doc.toString().trim()) return [];
1523
+ return parseJsonLinter(view);
1524
+ }, { delay: 300 })
1525
+ ]
1526
+ }),
1527
+ parent: containerRef.current
1528
+ });
1529
+ viewRef.current = view;
1530
+ editorRef.current = view.contentDOM;
1531
+ const prefixedId = `bio-properties-panel-${id}`;
1532
+ view.contentDOM.setAttribute("id", prefixedId);
1533
+ view.contentDOM.setAttribute("aria-label", label);
1534
+ return () => {
1535
+ view.destroy();
1536
+ };
1537
+ }, []);
1538
+ useEffect(() => {
1539
+ const view = viewRef.current;
1540
+ if (!view) return;
1541
+ const currentValue = view.state.doc.toString();
1542
+ if (value === currentValue) return;
1543
+ view.dispatch({
1544
+ changes: {
1545
+ from: 0,
1546
+ to: currentValue.length,
1547
+ insert: value || ""
1548
+ },
1549
+ annotations: ExternalChange.of(true)
1550
+ });
1551
+ }, [value]);
1552
+ const focusEditor = () => {
1553
+ viewRef.current && viewRef.current.focus();
1554
+ };
1555
+ return jsxs("div", {
1556
+ class: "bio-properties-panel-json-editor",
1557
+ children: [jsx("label", {
1558
+ class: "bio-properties-panel-label",
1559
+ onClick: focusEditor,
1560
+ children: jsx(TooltipWrapper, {
1561
+ value: tooltip,
1562
+ forId: id,
1563
+ element,
1564
+ children: label
1565
+ })
1566
+ }), jsx("div", {
1567
+ class: classnames("bio-properties-panel-input", value && "edited"),
1568
+ ref: containerRef
1569
+ })]
1570
+ });
1571
+ }
1572
+ /**
1573
+ * Entry wrapper for JsonEditor, handles getValue/setValue, built-in JSON validation, error display, and description.
1574
+ *
1575
+ * @param {object} props
1576
+ * @param {object} props.element
1577
+ * @param {string} props.id
1578
+ * @param {string} [props.description]
1579
+ * @param {string} props.label
1580
+ * @param {Function} props.debounce
1581
+ * @param {Function} props.getValue
1582
+ * @param {Function} props.setValue
1583
+ * @param {boolean} [props.disabled]
1584
+ * @param {string} [props.placeholder]
1585
+ * @param {string} [props.tooltip]
1586
+ * @param {Function} [props.validate]
1587
+ * @param {Function} [props.translate]
1588
+ */
1589
+ function JsonEditorEntry(props) {
1590
+ const { element, id, description, debounce, label, getValue, setValue, disabled, placeholder, tooltip, validate, translate = translateFallback } = props;
1591
+ const globalError = useError(id);
1592
+ let value = getValue(element);
1593
+ const [localError, setLocalError] = useState(() => computeError(validate, value, translate));
1594
+ const [editorValue, setEditorValue] = useState(value);
1595
+ useEffect(() => {
1596
+ if (value === editorValue) return;
1597
+ setEditorValue(value);
1598
+ setLocalError(computeError(validate, value, translate));
1599
+ }, [value, validate]);
1600
+ const onInput = useStaticCallback((newValue) => {
1601
+ setEditorValue(newValue);
1602
+ if (newValue !== getValue(element)) {
1603
+ const newValidationError = computeError(validate, newValue, translate);
1604
+ setValue(newValue, newValidationError);
1605
+ setLocalError(newValidationError);
1606
+ }
1607
+ });
1608
+ const error = globalError || localError;
1609
+ return jsxs("div", {
1610
+ class: classnames("bio-properties-panel-entry", error && "has-error"),
1611
+ "data-entry-id": id,
1612
+ children: [
1613
+ jsx(JsonEditor, {
1614
+ id,
1615
+ label,
1616
+ debounce,
1617
+ value,
1618
+ onInput,
1619
+ disabled,
1620
+ placeholder,
1621
+ tooltip,
1622
+ element
1623
+ }, element),
1624
+ error && jsx("div", {
1625
+ class: "bio-properties-panel-error",
1626
+ children: error
1627
+ }),
1628
+ jsx(Description, {
1629
+ forId: id,
1630
+ element,
1631
+ value: description
1632
+ })
1633
+ ]
1634
+ });
1635
+ }
1636
+ /**
1637
+ * Check if the JSON editor entry has been edited.
1638
+ */
1639
+ function isEdited$2(node) {
1640
+ return node && node.classList.contains("edited");
1641
+ }
1642
+ function computeError(validate, value, translate) {
1643
+ return (isFunction(validate) ? validate(value) : null) || validateJson(value, translate);
1644
+ }
1645
+ function validateJson(value, translate = translateFallback) {
1646
+ if (!value || !value.trim()) return null;
1647
+ try {
1648
+ return isObject(JSON.parse(value)) ? null : translate("JSON contains errors");
1649
+ } catch (e) {
1650
+ return translate("JSON contains errors");
1651
+ }
1652
+ }
1653
+ //#endregion
1654
+ //#region src/components/OpenPopupButton.js
1655
+ /**
1656
+ * Button to open popups.
1657
+ *
1658
+ * @param {Object} props
1659
+ * @param {Function} props.onClick - Callback to trigger when the button is clicked.
1660
+ * @param {string} [props.title] - Tooltip text for the button.
1661
+ * @param {boolean} [props.disabled] - Whether the button is disabled.
1662
+ * @param {string} [props.className] - Additional class names for the button.
1663
+ */
1664
+ function OpenPopupButton({ onClick, title = "Open pop-up editor" }) {
1665
+ return jsx("button", {
1666
+ type: "button",
1667
+ title,
1668
+ class: "bio-properties-panel-open-feel-popup",
1669
+ onClick,
1670
+ children: jsx(OpenPopupIcon, {})
1671
+ });
1672
+ }
1673
+ //#endregion
1674
+ //#region src/components/entries/templating/TemplatingEditor.js
1675
+ var noop$5 = () => {};
1676
+ /**
1677
+ * Buffer `.focus()` calls while the editor is not initialized.
1678
+ * Set Focus inside when the editor is ready.
1679
+ */
1680
+ var useBufferedFocus$1 = function(editor, ref) {
1681
+ const [buffer, setBuffer] = useState(void 0);
1682
+ ref.current = useMemo(() => ({ focus: (offset) => {
1683
+ if (editor) editor.focus(offset);
1684
+ else {
1685
+ if (typeof offset === "undefined") offset = Infinity;
1686
+ setBuffer(offset);
1687
+ }
1688
+ } }), [editor]);
1689
+ useEffect(() => {
1690
+ if (typeof buffer !== "undefined" && editor) {
1691
+ editor.focus(buffer);
1692
+ setBuffer(false);
1693
+ }
1694
+ }, [editor, buffer]);
1695
+ };
1696
+ var TemplatingEditor = forwardRef((props, ref) => {
1697
+ const { onInput, disabled, tooltipContainer, enableGutters, value, onLint = noop$5, onOpenPopup = noop$5, popupOpen, contentAttributes = {}, hostLanguage = null, singleLine = false } = props;
1698
+ const inputRef = useRef();
1699
+ const [editor, setEditor] = useState();
1700
+ const [localValue, setLocalValue] = useState(value || "");
1701
+ useBufferedFocus$1(editor, ref);
1702
+ const handleInput = useStaticCallback((newValue) => {
1703
+ onInput(newValue);
1704
+ setLocalValue(newValue);
1705
+ });
1706
+ useEffect(() => {
1707
+ let editor;
1708
+ editor = new ExpressionEditor({
1709
+ container: inputRef.current,
1710
+ onChange: handleInput,
1711
+ value: localValue,
1712
+ onLint,
1713
+ contentAttributes,
1714
+ tooltipContainer,
1715
+ enableGutters,
1716
+ hostLanguage,
1717
+ singleLine,
1718
+ lineWrap: true
1719
+ });
1720
+ setEditor(editor);
1721
+ return () => {
1722
+ onLint([]);
1723
+ inputRef.current.innerHTML = "";
1724
+ setEditor(null);
1725
+ };
1726
+ }, []);
1727
+ useEffect(() => {
1728
+ if (!editor) return;
1729
+ if (value === localValue) return;
1730
+ editor.setValue(value);
1731
+ setLocalValue(value);
1732
+ }, [value]);
1733
+ const handleClick = () => {
1734
+ ref.current.focus();
1735
+ };
1736
+ return jsxs("div", {
1737
+ class: classnames("bio-properties-panel-feelers-editor-container", popupOpen ? "popupOpen" : null),
1738
+ children: [
1739
+ popupOpen && jsx("div", {
1740
+ class: "bio-properties-panel-feelers-editor__popup-placeholder",
1741
+ children: "Opened in popup"
1742
+ }),
1743
+ jsx("div", {
1744
+ name: props.name,
1745
+ class: classnames("bio-properties-panel-feelers-editor bio-properties-panel-input", localValue ? "edited" : null, disabled ? "disabled" : null),
1746
+ ref: inputRef,
1747
+ onClick: handleClick
1748
+ }),
1749
+ !disabled && jsx(OpenPopupButton, { onClick: () => onOpenPopup("feelers") })
1750
+ ]
1751
+ });
1752
+ });
1753
+ //#endregion
1754
+ //#region src/components/entries/expression/ExpressionEditor.js
1755
+ var noop$4 = () => {};
1756
+ /**
1757
+ * Buffer `.focus()` calls while the editor is not initialized.
1758
+ * Set Focus inside when the editor is ready.
1759
+ */
1760
+ var useBufferedFocus = function(editor, ref) {
1761
+ const [buffer, setBuffer] = useState(void 0);
1762
+ ref.current = useMemo(() => ({ focus: (offset) => {
1763
+ if (editor) editor.focus(offset);
1764
+ else {
1765
+ if (typeof offset === "undefined") offset = Infinity;
1766
+ setBuffer(offset);
1767
+ }
1768
+ } }), [editor]);
1769
+ useEffect(() => {
1770
+ if (typeof buffer !== "undefined" && editor) {
1771
+ editor.focus(buffer);
1772
+ setBuffer(false);
1773
+ }
1774
+ }, [editor, buffer]);
1775
+ };
1776
+ var ExpressionEditor$1 = forwardRef((props, ref) => {
1777
+ const { contentAttributes, enableGutters, value, onInput, onKeyDown: onKeyDownProp = noop$4, onFeelToggle = noop$4, onLint = noop$4, onOpenPopup = noop$4, placeholder, popupOpen, disabled, tooltipContainer, variables, feelLanguageContext } = props;
1778
+ const inputRef = useRef();
1779
+ const [editor, setEditor] = useState();
1780
+ const [localValue, setLocalValue] = useState(value || "");
1781
+ const { builtins, dialect, parserDialect } = feelLanguageContext || {};
1782
+ useBufferedFocus(editor, ref);
1783
+ const handleInput = useStaticCallback((newValue) => {
1784
+ onInput(newValue);
1785
+ setLocalValue(newValue);
1786
+ });
1787
+ useEffect(() => {
1788
+ let editor;
1789
+ const onKeyDown = (e) => {
1790
+ onKeyDownProp(e);
1791
+ if (e.key !== "Backspace" || !editor) return;
1792
+ const selection = editor.getSelection();
1793
+ const range = selection.ranges[selection.mainIndex];
1794
+ if (range.from === 0 && range.to === 0) onFeelToggle();
1795
+ };
1796
+ editor = new Editor({
1797
+ container: inputRef.current,
1798
+ onChange: handleInput,
1799
+ onKeyDown,
1800
+ onLint,
1801
+ placeholder,
1802
+ tooltipContainer,
1803
+ value: localValue,
1804
+ variables,
1805
+ builtins,
1806
+ dialect,
1807
+ parserDialect,
1808
+ extensions: [...enableGutters ? [lineNumbers()] : [], EditorView.lineWrapping],
1809
+ contentAttributes
1810
+ });
1811
+ setEditor(editor);
1812
+ return () => {
1813
+ onLint([]);
1814
+ inputRef.current.innerHTML = "";
1815
+ setEditor(null);
1816
+ };
1817
+ }, []);
1818
+ useEffect(() => {
1819
+ if (!editor) return;
1820
+ if (value === localValue) return;
1821
+ editor.setValue(value);
1822
+ setLocalValue(value);
1823
+ }, [value]);
1824
+ useEffect(() => {
1825
+ if (!editor) return;
1826
+ editor.setVariables(variables);
1827
+ }, [variables]);
1828
+ useEffect(() => {
1829
+ if (!editor) return;
1830
+ editor.setPlaceholder(placeholder);
1831
+ }, [placeholder]);
1832
+ const handleClick = () => {
1833
+ ref.current.focus();
1834
+ };
1835
+ return jsxs("div", {
1836
+ class: classnames("bio-properties-panel-feel-editor-container", disabled ? "disabled" : null, popupOpen ? "popupOpen" : null),
1837
+ children: [
1838
+ popupOpen && jsx("div", {
1839
+ class: "bio-properties-panel-feel-editor__open-popup-placeholder",
1840
+ children: "Opened in editor"
1841
+ }),
1842
+ jsx("div", {
1843
+ name: props.name,
1844
+ class: classnames("bio-properties-panel-input", localValue ? "edited" : null),
1845
+ ref: inputRef,
1846
+ onClick: handleClick
1847
+ }),
1848
+ !disabled && jsx(OpenPopupButton, { onClick: () => onOpenPopup("feel") })
1849
+ ]
1850
+ });
1851
+ });
1852
+ //#endregion
1853
+ //#region src/components/entries/expression/ExpressionIndicator.js
1854
+ function ExpressionIndicator(props) {
1855
+ const { active } = props;
1856
+ if (!active) return null;
1857
+ return jsx("span", {
1858
+ class: "bio-properties-panel-expression-indicator",
1859
+ children: "="
1860
+ });
1861
+ }
1862
+ //#endregion
1863
+ //#region src/components/entries/expression/ExpressionIcon.js
1864
+ var noop$3 = () => {};
1865
+ /**
1866
+ * @param {Object} props
1867
+ * @param {Object} props.label
1868
+ * @param {String} props.feel
1869
+ */
1870
+ function ExpressionIcon(props) {
1871
+ const { feel = false, active, disabled = false, onClick = noop$3 } = props;
1872
+ const feelRequiredLabel = "Expression expression is mandatory";
1873
+ const feelOptionalLabel = `Click to ${active ? "remove" : "set a"} dynamic value with FEEL expression`;
1874
+ const handleClick = (e) => {
1875
+ onClick(e);
1876
+ if (!e.pointerType) e.stopPropagation();
1877
+ };
1878
+ return jsx("button", {
1879
+ type: "button",
1880
+ class: classnames("bio-properties-panel-feel-icon", active ? "active" : null, feel === "required" ? "required" : "optional"),
1881
+ onClick: handleClick,
1882
+ disabled: feel === "required" || disabled,
1883
+ title: feel === "required" ? feelRequiredLabel : feelOptionalLabel,
1884
+ children: jsx(FeelIcon, {})
1885
+ });
1886
+ }
1887
+ //#endregion
1888
+ //#region src/components/util/keyboardUtils.js
1889
+ /**
1890
+ * @param {KeyboardEvent} event
1891
+ * @return {boolean}
1892
+ */
1893
+ function isCmd(event) {
1894
+ if (event.altKey) return false;
1895
+ return event.ctrlKey || event.metaKey;
1896
+ }
1897
+ function isCmdWithChar(event) {
1898
+ return isCmd(event) && event.key.length === 1 && /^[a-zA-Z]$/.test(event.key);
1899
+ }
1900
+ //#endregion
1901
+ //#region src/components/entries/ToggleSwitch.js
1902
+ function ToggleSwitch(props) {
1903
+ const { id, label, onInput, value, switcherLabel, inline, onFocus, onBlur, inputRef, tooltip } = props;
1904
+ const [localValue, setLocalValue] = useState(value);
1905
+ const handleInputCallback = async () => {
1906
+ onInput(!value);
1907
+ };
1908
+ const handleInput = (e) => {
1909
+ handleInputCallback(e);
1910
+ setLocalValue(e.target.value);
1911
+ };
1912
+ useEffect(() => {
1913
+ if (value === localValue) return;
1914
+ setLocalValue(value);
1915
+ }, [value]);
1916
+ return jsxs("div", {
1917
+ class: classnames("bio-properties-panel-toggle-switch", { inline }),
1918
+ children: [jsx("label", {
1919
+ class: "bio-properties-panel-label",
1920
+ for: prefixId$7(id),
1921
+ children: jsx(TooltipWrapper, {
1922
+ value: tooltip,
1923
+ forId: id,
1924
+ element: props.element,
1925
+ children: label
1926
+ })
1927
+ }), jsxs("div", {
1928
+ class: "bio-properties-panel-field-wrapper",
1929
+ children: [jsxs("label", {
1930
+ class: "bio-properties-panel-toggle-switch__switcher",
1931
+ children: [jsx("input", {
1932
+ ref: inputRef,
1933
+ id: prefixId$7(id),
1934
+ class: "bio-properties-panel-input",
1935
+ type: "checkbox",
1936
+ onFocus,
1937
+ onBlur,
1938
+ name: id,
1939
+ onInput: handleInput,
1940
+ checked: !!localValue
1941
+ }), jsx("span", { class: "bio-properties-panel-toggle-switch__slider" })]
1942
+ }), switcherLabel && jsx("p", {
1943
+ class: "bio-properties-panel-toggle-switch__label",
1944
+ children: switcherLabel
1945
+ })]
1946
+ })]
1947
+ });
1948
+ }
1949
+ /**
1950
+ * @param {Object} props
1951
+ * @param {Object} props.element
1952
+ * @param {String} props.id
1953
+ * @param {String} props.description
1954
+ * @param {String} props.label
1955
+ * @param {String} props.switcherLabel
1956
+ * @param {Boolean} props.inline
1957
+ * @param {Function} props.getValue
1958
+ * @param {Function} props.setValue
1959
+ * @param {Function} props.onFocus
1960
+ * @param {Function} props.onBlur
1961
+ * @param {string|import('preact').Component} props.tooltip
1962
+ */
1963
+ function ToggleSwitchEntry(props) {
1964
+ const { element, id, description, label, switcherLabel, inline, getValue, setValue, onFocus, onBlur, tooltip } = props;
1965
+ const value = getValue(element);
1966
+ return jsxs("div", {
1967
+ class: "bio-properties-panel-entry bio-properties-panel-toggle-switch-entry",
1968
+ "data-entry-id": id,
1969
+ children: [jsx(ToggleSwitch, {
1970
+ id,
1971
+ label,
1972
+ value,
1973
+ onInput: setValue,
1974
+ onFocus,
1975
+ onBlur,
1976
+ switcherLabel,
1977
+ inline,
1978
+ tooltip,
1979
+ element
1980
+ }), jsx(Description, {
1981
+ forId: id,
1982
+ element,
1983
+ value: description
1984
+ })]
1985
+ });
1986
+ }
1987
+ function isEdited$9(node) {
1988
+ return node && !!node.checked;
1989
+ }
1990
+ function prefixId$7(id) {
1991
+ return `bio-properties-panel-${id}`;
1992
+ }
1993
+ //#endregion
1994
+ //#region src/components/entries/NumberField.js
1995
+ function NumberField(props) {
1996
+ const { debounce, disabled, displayLabel = true, id, inputRef, label, max, min, onInput, step, value = "", onFocus, onBlur, tooltip } = props;
1997
+ const [localValue, setLocalValue] = useState(value);
1998
+ const handleInputCallback = useMemo(() => {
1999
+ return debounce((target) => {
2000
+ if (target.validity.valid) onInput(target.value ? parseFloat(target.value) : void 0);
2001
+ });
2002
+ }, [onInput, debounce]);
2003
+ const handleInput = (e) => {
2004
+ handleInputCallback(e.target);
2005
+ setLocalValue(e.target.value);
2006
+ };
2007
+ useEffect(() => {
2008
+ if (value === localValue) return;
2009
+ setLocalValue(value);
2010
+ }, [value]);
2011
+ return jsxs("div", {
2012
+ class: "bio-properties-panel-numberfield",
2013
+ children: [displayLabel && jsx("label", {
2014
+ for: prefixId$6(id),
2015
+ class: "bio-properties-panel-label",
2016
+ children: jsx(TooltipWrapper, {
2017
+ value: tooltip,
2018
+ forId: id,
2019
+ element: props.element,
2020
+ children: label
2021
+ })
2022
+ }), jsx("input", {
2023
+ id: prefixId$6(id),
2024
+ ref: inputRef,
2025
+ type: "number",
2026
+ name: id,
2027
+ spellCheck: "false",
2028
+ autoComplete: "off",
2029
+ disabled,
2030
+ class: "bio-properties-panel-input",
2031
+ max,
2032
+ min,
2033
+ onInput: handleInput,
2034
+ onFocus,
2035
+ onBlur,
2036
+ step,
2037
+ value: localValue
2038
+ })]
2039
+ });
2040
+ }
2041
+ /**
2042
+ * @param {Object} props
2043
+ * @param {Boolean} props.debounce
2044
+ * @param {String} props.description
2045
+ * @param {Boolean} props.disabled
2046
+ * @param {Object} props.element
2047
+ * @param {Function} props.getValue
2048
+ * @param {String} props.id
2049
+ * @param {String} props.label
2050
+ * @param {String} props.max
2051
+ * @param {String} props.min
2052
+ * @param {Function} props.setValue
2053
+ * @param {Function} props.onFocus
2054
+ * @param {Function} props.onBlur
2055
+ * @param {String} props.step
2056
+ * @param {Function} props.validate
2057
+ */
2058
+ function NumberFieldEntry(props) {
2059
+ const { debounce, description, disabled, element, getValue, id, label, max, min, setValue, step, onFocus, onBlur, validate, tooltip } = props;
2060
+ const globalError = useError(id);
2061
+ const [localError, setLocalError] = useState(null);
2062
+ let value = getValue(element);
2063
+ useEffect(() => {
2064
+ if (isFunction(validate)) {
2065
+ const newValidationError = validate(value) || null;
2066
+ setLocalError(newValidationError);
2067
+ }
2068
+ }, [value, validate]);
2069
+ const onInput = (newValue) => {
2070
+ let newValidationError = null;
2071
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
2072
+ setValue(newValue, newValidationError);
2073
+ setLocalError(newValidationError);
2074
+ };
2075
+ const error = globalError || localError;
2076
+ return jsxs("div", {
2077
+ class: classnames("bio-properties-panel-entry", error ? "has-error" : ""),
2078
+ "data-entry-id": id,
2079
+ children: [
2080
+ jsx(NumberField, {
2081
+ debounce,
2082
+ disabled,
2083
+ id,
2084
+ label,
2085
+ onFocus,
2086
+ onBlur,
2087
+ onInput,
2088
+ max,
2089
+ min,
2090
+ step,
2091
+ value,
2092
+ tooltip
2093
+ }, element),
2094
+ error && jsx("div", {
2095
+ class: "bio-properties-panel-error",
2096
+ children: error
2097
+ }),
2098
+ jsx(Description, {
2099
+ forId: id,
2100
+ element,
2101
+ value: description
2102
+ })
2103
+ ]
2104
+ });
2105
+ }
2106
+ function isEdited$3(node) {
2107
+ return node && !!node.value;
2108
+ }
2109
+ function prefixId$6(id) {
2110
+ return `bio-properties-panel-${id}`;
2111
+ }
2112
+ //#endregion
2113
+ //#region src/components/entries/expression/Expression.js
2114
+ var noop$2 = () => {};
2115
+ /**
2116
+ * @typedef {'required'|'optional'|'static'} FeelType
2117
+ */
2118
+ /**
2119
+ * @param {Object} props
2120
+ * @param {Boolean} props.debounce
2121
+ * @param {String} props.id
2122
+ * @param {Object} props.element
2123
+ * @param {String} props.label
2124
+ * @param {String} props.hostLanguage
2125
+ * @param {Function} props.onInput
2126
+ * @param {Function} props.onBlur
2127
+ * @param {Function} props.onError
2128
+ * @param {FeelType} [props.feel]
2129
+ * @param {String} props.value
2130
+ * @param {Boolean} [props.singleLine]
2131
+ * @param {Function} props.tooltipContainer
2132
+ * @param {Function | import('preact').Component} props.OptionalComponent
2133
+ * @param {Boolean} props.disabled
2134
+ * @param {Array} props.variables
2135
+ * @param {string} [props.placeholder]
2136
+ * @param {string | import('preact').Component} props.tooltip
2137
+ */
2138
+ function FeelTextfield(props) {
2139
+ const { debounce, id, element, label, hostLanguage, onInput: commitValue, onBlur, onError, placeholder, feel, value = "", disabled = false, variables, singleLine, tooltipContainer, OptionalComponent = OptionalFeelInput, tooltip } = props;
2140
+ const [localValue, setLocalValue] = useState(value);
2141
+ const editorRef = useShowEntryEvent(id);
2142
+ const containerRef = useRef();
2143
+ const onInput = useCallback((newValue) => {
2144
+ commitValue(newValue === "" || newValue === "=" ? void 0 : newValue);
2145
+ }, [commitValue]);
2146
+ const feelActive = isString(localValue) && localValue.startsWith("=") || feel === "required";
2147
+ const feelOnlyValue = isString(localValue) && localValue.startsWith("=") ? localValue.substring(1) : localValue;
2148
+ const feelLanguageContext = useContext(FeelLanguageContext);
2149
+ const [focus, _setFocus] = useState(void 0);
2150
+ const { eventBus } = useContext(EventContext);
2151
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
2152
+ const setFocus = (offset = 0) => {
2153
+ const position = containerRef.current.contains(document.activeElement) ? document.activeElement.selectionStart : Infinity;
2154
+ _setFocus(position + offset);
2155
+ };
2156
+ /**
2157
+ * @type { import('min-dash').DebouncedFunction }
2158
+ */
2159
+ const handleInput = useDebounce(onInput, debounce);
2160
+ const handleFeelToggle = useStaticCallback(() => {
2161
+ if (feel === "required") return;
2162
+ if (!feelActive) {
2163
+ setLocalValue("=" + localValue);
2164
+ handleInput("=" + localValue);
2165
+ } else {
2166
+ setLocalValue(feelOnlyValue);
2167
+ handleInput(feelOnlyValue);
2168
+ }
2169
+ });
2170
+ const handleLocalInput = (newValue, useDebounce = true) => {
2171
+ if (feelActive) newValue = "=" + newValue;
2172
+ if (newValue === localValue) return;
2173
+ setLocalValue(newValue);
2174
+ if (useDebounce) handleInput(newValue);
2175
+ else onInput(newValue);
2176
+ if (!feelActive && isString(newValue) && newValue.startsWith("=")) setFocus(-1);
2177
+ };
2178
+ const handleOnBlur = (e) => {
2179
+ handleInput.cancel?.();
2180
+ if (e.target.type === "checkbox") onInput(e.target.checked);
2181
+ else {
2182
+ const trimmedValue = e.target.value.trim();
2183
+ handleLocalInput(trimmedValue, false);
2184
+ }
2185
+ if (onBlur) onBlur(e);
2186
+ };
2187
+ const handleOnKeyDown = (e) => {
2188
+ if (isCmdWithChar(e)) handleInput.flush();
2189
+ };
2190
+ const handleLint = useStaticCallback((lint = []) => {
2191
+ if (lint.some((report) => report.type === "Syntax Error")) onError("Unparsable FEEL expression.");
2192
+ else onError(void 0);
2193
+ });
2194
+ const handleOpenPopup = (type = "feel") => {
2195
+ const isOpen = eventBus.fire("propertiesPanel.openPopup", {
2196
+ element,
2197
+ entryId: id,
2198
+ hostLanguage,
2199
+ label,
2200
+ onInput: handleLocalInput,
2201
+ singleLine,
2202
+ sourceElement: editorRef.current,
2203
+ tooltipContainer,
2204
+ type,
2205
+ value: feelOnlyValue,
2206
+ variables,
2207
+ feelLanguageContext
2208
+ });
2209
+ if (isOpen) eventBus.once("propertiesPanelPopup.close", () => {
2210
+ setIsPopupOpen(false);
2211
+ });
2212
+ setIsPopupOpen(isOpen === true);
2213
+ };
2214
+ useEffect(() => {
2215
+ if (typeof focus !== "undefined") {
2216
+ editorRef.current.focus(focus);
2217
+ _setFocus(void 0);
2218
+ }
2219
+ }, [focus]);
2220
+ useEffect(() => {
2221
+ if (value === localValue) return;
2222
+ if (!value) {
2223
+ setLocalValue(feelActive ? "=" : "");
2224
+ return;
2225
+ }
2226
+ setLocalValue(value);
2227
+ }, [value]);
2228
+ useEffect(() => {
2229
+ return () => {
2230
+ eventBus.fire("propertiesPanel.closePopup");
2231
+ };
2232
+ }, []);
2233
+ useEffect(() => {
2234
+ const copyHandler = (event) => {
2235
+ if (!feelActive) return;
2236
+ event.clipboardData.setData("application/FEEL", event.clipboardData.getData("text"));
2237
+ };
2238
+ const pasteHandler = (event) => {
2239
+ if (feelActive || isPopupOpen) return;
2240
+ if (event.clipboardData.getData("application/FEEL")) {
2241
+ setTimeout(() => {
2242
+ handleFeelToggle();
2243
+ setFocus();
2244
+ });
2245
+ return;
2246
+ }
2247
+ const input = event.target;
2248
+ const isFieldEmpty = !input.value;
2249
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
2250
+ if (isFieldEmpty || isAllSelected) {
2251
+ const trimmedValue = event.clipboardData.getData("text").trim();
2252
+ setLocalValue(trimmedValue);
2253
+ handleInput(trimmedValue);
2254
+ if (!feelActive && isString(trimmedValue) && trimmedValue.startsWith("=")) setFocus(trimmedValue.length - 1);
2255
+ event.preventDefault();
2256
+ }
2257
+ };
2258
+ containerRef.current.addEventListener("copy", copyHandler);
2259
+ containerRef.current.addEventListener("cut", copyHandler);
2260
+ containerRef.current.addEventListener("paste", pasteHandler);
2261
+ return () => {
2262
+ containerRef.current.removeEventListener("copy", copyHandler);
2263
+ containerRef.current.removeEventListener("cut", copyHandler);
2264
+ containerRef.current.removeEventListener("paste", pasteHandler);
2265
+ };
2266
+ }, [
2267
+ containerRef,
2268
+ feelActive,
2269
+ handleFeelToggle,
2270
+ setFocus
2271
+ ]);
2272
+ return jsxs("div", {
2273
+ class: classnames("bio-properties-panel-feel-entry", { "feel-active": feelActive }),
2274
+ children: [jsxs("label", {
2275
+ for: prefixId$5(id),
2276
+ class: "bio-properties-panel-label",
2277
+ onClick: () => setFocus(),
2278
+ children: [jsx(TooltipWrapper, {
2279
+ value: tooltip,
2280
+ forId: id,
2281
+ element: props.element,
2282
+ children: label
2283
+ }), jsx(ExpressionIcon, {
2284
+ label,
2285
+ feel,
2286
+ onClick: handleFeelToggle,
2287
+ active: feelActive
2288
+ })]
2289
+ }), jsxs("div", {
2290
+ class: "bio-properties-panel-feel-container",
2291
+ ref: containerRef,
2292
+ children: [jsx(ExpressionIndicator, {
2293
+ active: feelActive,
2294
+ disabled: feel !== "optional" || disabled,
2295
+ onClick: handleFeelToggle
2296
+ }), feelActive ? jsx(ExpressionEditor$1, {
2297
+ name: id,
2298
+ onInput: handleLocalInput,
2299
+ onKeyDown: handleOnKeyDown,
2300
+ contentAttributes: {
2301
+ "id": prefixId$5(id),
2302
+ "aria-label": label
2303
+ },
2304
+ disabled,
2305
+ popupOpen: isPopupOpen,
2306
+ onFeelToggle: () => {
2307
+ handleFeelToggle();
2308
+ setFocus(true);
2309
+ },
2310
+ onLint: handleLint,
2311
+ onOpenPopup: handleOpenPopup,
2312
+ placeholder,
2313
+ value: feelOnlyValue,
2314
+ variables,
2315
+ feelLanguageContext,
2316
+ ref: editorRef,
2317
+ tooltipContainer
2318
+ }) : jsx(OptionalComponent, {
2319
+ ...props,
2320
+ popupOpen: isPopupOpen,
2321
+ onInput: handleLocalInput,
2322
+ onKeyDown: handleOnKeyDown,
2323
+ onBlur: handleOnBlur,
2324
+ contentAttributes: {
2325
+ "id": prefixId$5(id),
2326
+ "aria-label": label
2327
+ },
2328
+ value: localValue,
2329
+ ref: editorRef,
2330
+ onOpenPopup: handleOpenPopup,
2331
+ containerRef
2332
+ })]
2333
+ })]
2334
+ });
2335
+ }
2336
+ var OptionalFeelInput = forwardRef((props, ref) => {
2337
+ const { id, disabled, onInput, onKeyDown, value, onFocus, onBlur, placeholder } = props;
2338
+ const inputRef = useRef();
2339
+ ref.current = { focus: (position) => {
2340
+ const input = inputRef.current;
2341
+ if (!input) return;
2342
+ input.focus();
2343
+ if (typeof position === "number") {
2344
+ if (position > value.length) position = value.length;
2345
+ input.setSelectionRange(position, position);
2346
+ }
2347
+ } };
2348
+ return jsx("input", {
2349
+ id: prefixId$5(id),
2350
+ type: "text",
2351
+ ref: inputRef,
2352
+ name: id,
2353
+ spellCheck: "false",
2354
+ autoComplete: "off",
2355
+ disabled,
2356
+ class: "bio-properties-panel-input",
2357
+ onInput: (e) => onInput(e.target.value),
2358
+ onFocus,
2359
+ onKeyDown,
2360
+ onBlur,
2361
+ placeholder,
2362
+ value: value || ""
2363
+ });
2364
+ });
2365
+ var OptionalFeelNumberField = forwardRef((props, ref) => {
2366
+ const { id, debounce, disabled, onInput, value, min, max, step, onFocus, onBlur } = props;
2367
+ const inputRef = useRef();
2368
+ ref.current = { focus: (position) => {
2369
+ const input = inputRef.current;
2370
+ if (!input) return;
2371
+ input.focus();
2372
+ if (typeof position === "number" && position !== Infinity) {
2373
+ if (position > value.length) position = value.length;
2374
+ input.setSelectionRange(position, position);
2375
+ }
2376
+ } };
2377
+ return jsx(NumberField, {
2378
+ id,
2379
+ debounce,
2380
+ disabled,
2381
+ displayLabel: false,
2382
+ inputRef,
2383
+ max,
2384
+ min,
2385
+ onInput,
2386
+ step,
2387
+ value,
2388
+ onFocus,
2389
+ onBlur
2390
+ });
2391
+ });
2392
+ var OptionalFeelTextArea = forwardRef((props, ref) => {
2393
+ const { id, disabled, onInput, value, onFocus, onBlur, placeholder } = props;
2394
+ const inputRef = useRef();
2395
+ ref.current = { focus: () => {
2396
+ const input = inputRef.current;
2397
+ if (!input) return;
2398
+ input.focus();
2399
+ input.setSelectionRange(0, 0);
2400
+ } };
2401
+ return jsx("textarea", {
2402
+ id: prefixId$5(id),
2403
+ type: "text",
2404
+ ref: inputRef,
2405
+ name: id,
2406
+ spellCheck: "false",
2407
+ autoComplete: "off",
2408
+ disabled,
2409
+ class: "bio-properties-panel-input",
2410
+ onInput: (e) => onInput(e.target.value),
2411
+ onFocus,
2412
+ onBlur,
2413
+ placeholder,
2414
+ value: value || "",
2415
+ "data-gramm": "false"
2416
+ });
2417
+ });
2418
+ var OptionalFeelToggleSwitch = forwardRef((props, ref) => {
2419
+ const { id, onInput, value, onFocus, onBlur, switcherLabel } = props;
2420
+ const inputRef = useRef();
2421
+ ref.current = { focus: () => {
2422
+ const input = inputRef.current;
2423
+ if (!input) return;
2424
+ input.focus();
2425
+ } };
2426
+ return jsx(ToggleSwitch, {
2427
+ id,
2428
+ value,
2429
+ inputRef,
2430
+ onInput,
2431
+ onFocus,
2432
+ onBlur,
2433
+ switcherLabel
2434
+ });
2435
+ });
2436
+ var OptionalFeelCheckbox = forwardRef((props, ref) => {
2437
+ const { id, disabled, onInput, value, onFocus, onBlur } = props;
2438
+ const inputRef = useRef();
2439
+ const handleChange = ({ target }) => {
2440
+ onInput(target.checked);
2441
+ };
2442
+ ref.current = { focus: () => {
2443
+ const input = inputRef.current;
2444
+ if (!input) return;
2445
+ input.focus();
2446
+ } };
2447
+ return jsx("input", {
2448
+ ref: inputRef,
2449
+ id: prefixId$5(id),
2450
+ name: id,
2451
+ onFocus,
2452
+ onBlur,
2453
+ type: "checkbox",
2454
+ class: "bio-properties-panel-input",
2455
+ onChange: handleChange,
2456
+ checked: value,
2457
+ disabled
2458
+ });
2459
+ });
2460
+ /**
2461
+ * @param {Object} props
2462
+ * @param {Object} props.element
2463
+ * @param {String} props.id
2464
+ * @param {String} props.description
2465
+ * @param {Boolean} props.debounce
2466
+ * @param {Boolean} props.disabled
2467
+ * @param {FeelType} [props.feel]
2468
+ * @param {String} props.label
2469
+ * @param {Function} props.getValue
2470
+ * @param {Function} props.setValue
2471
+ * @param {Function} props.tooltipContainer
2472
+ * @param {Function} props.validate
2473
+ * @param {Function} props.show
2474
+ * @param {Function} props.example
2475
+ * @param {Array} props.variables
2476
+ * @param {Function} props.onFocus
2477
+ * @param {Function} props.onBlur
2478
+ * @param {string} [props.placeholder]
2479
+ * @param {string|import('preact').Component} props.tooltip
2480
+ */
2481
+ function FeelEntry(props) {
2482
+ const { element, id, description, debounce, disabled, feel, label, getValue, setValue, tooltipContainer, hostLanguage, singleLine, validate, show = noop$2, example, variables, onFocus, onBlur, placeholder, tooltip } = props;
2483
+ const [validationError, setValidationError] = useState(null);
2484
+ const [localError, setLocalError] = useState(null);
2485
+ let value = getValue(element);
2486
+ useEffect(() => {
2487
+ if (isFunction(validate)) {
2488
+ const newValidationError = validate(value) || null;
2489
+ setValidationError(newValidationError);
2490
+ }
2491
+ }, [value, validate]);
2492
+ const onInput = useStaticCallback((newValue) => {
2493
+ const value = getValue(element);
2494
+ let newValidationError = null;
2495
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
2496
+ if (newValue !== value) setValue(newValue, newValidationError);
2497
+ setValidationError(newValidationError);
2498
+ });
2499
+ const onError = useCallback((err) => {
2500
+ setLocalError(err);
2501
+ }, []);
2502
+ const error = useError(id) || localError || validationError;
2503
+ return jsxs("div", {
2504
+ class: classnames(props.class, "bio-properties-panel-entry", error ? "has-error" : ""),
2505
+ "data-entry-id": id,
2506
+ children: [
2507
+ createElement(FeelTextfield, {
2508
+ ...props,
2509
+ debounce,
2510
+ disabled,
2511
+ feel,
2512
+ id,
2513
+ key: element,
2514
+ label,
2515
+ onInput,
2516
+ onError,
2517
+ onFocus,
2518
+ onBlur,
2519
+ placeholder,
2520
+ example,
2521
+ hostLanguage,
2522
+ singleLine,
2523
+ show,
2524
+ value,
2525
+ variables,
2526
+ tooltipContainer,
2527
+ OptionalComponent: props.OptionalComponent,
2528
+ tooltip
2529
+ }),
2530
+ error && jsx("div", {
2531
+ class: "bio-properties-panel-error",
2532
+ children: error
2533
+ }),
2534
+ jsx(Description, {
2535
+ forId: id,
2536
+ element,
2537
+ value: description
2538
+ })
2539
+ ]
2540
+ });
2541
+ }
2542
+ /**
2543
+ * @param {Object} props
2544
+ * @param {Object} props.element
2545
+ * @param {String} props.id
2546
+ * @param {String} props.description
2547
+ * @param {Boolean} props.debounce
2548
+ * @param {Boolean} props.disabled
2549
+ * @param {String} props.max
2550
+ * @param {String} props.min
2551
+ * @param {String} props.step
2552
+ * @param {FeelType} [props.feel]
2553
+ * @param {String} props.label
2554
+ * @param {Function} props.getValue
2555
+ * @param {Function} props.setValue
2556
+ * @param {Function} props.tooltipContainer
2557
+ * @param {Function} props.validate
2558
+ * @param {Function} props.show
2559
+ * @param {Function} props.example
2560
+ * @param {Array} props.variables
2561
+ * @param {Function} props.onFocus
2562
+ * @param {Function} props.onBlur
2563
+ */
2564
+ function FeelNumberEntry(props) {
2565
+ return jsx(FeelEntry, {
2566
+ class: "bio-properties-panel-feel-number",
2567
+ OptionalComponent: OptionalFeelNumberField,
2568
+ ...props
2569
+ });
2570
+ }
2571
+ /**
2572
+ * @param {Object} props
2573
+ * @param {Object} props.element
2574
+ * @param {String} props.id
2575
+ * @param {String} props.description
2576
+ * @param {Boolean} props.debounce
2577
+ * @param {Boolean} props.disabled
2578
+ * @param {FeelType} [props.feel]
2579
+ * @param {String} props.label
2580
+ * @param {Function} props.getValue
2581
+ * @param {Function} props.setValue
2582
+ * @param {Function} props.tooltipContainer
2583
+ * @param {Function} props.validate
2584
+ * @param {Function} props.show
2585
+ * @param {Function} props.example
2586
+ * @param {Array} props.variables
2587
+ * @param {Function} props.onFocus
2588
+ * @param {Function} props.onBlur
2589
+ * @param {string} [props.placeholder]
2590
+ */
2591
+ function FeelTextAreaEntry(props) {
2592
+ return jsx(FeelEntry, {
2593
+ class: "bio-properties-panel-feel-textarea",
2594
+ OptionalComponent: OptionalFeelTextArea,
2595
+ ...props
2596
+ });
2597
+ }
2598
+ /**
2599
+ * @param {Object} props
2600
+ * @param {Object} props.element
2601
+ * @param {String} props.id
2602
+ * @param {String} props.description
2603
+ * @param {Boolean} props.debounce
2604
+ * @param {Boolean} props.disabled
2605
+ * @param {FeelType} [props.feel]
2606
+ * @param {String} props.label
2607
+ * @param {Function} props.getValue
2608
+ * @param {Function} props.setValue
2609
+ * @param {Function} props.tooltipContainer
2610
+ * @param {Function} props.validate
2611
+ * @param {Function} props.show
2612
+ * @param {Function} props.example
2613
+ * @param {Array} props.variables
2614
+ * @param {Function} props.onFocus
2615
+ * @param {Function} props.onBlur
2616
+ */
2617
+ function FeelToggleSwitchEntry(props) {
2618
+ return jsx(FeelEntry, {
2619
+ class: "bio-properties-panel-feel-toggle-switch",
2620
+ OptionalComponent: OptionalFeelToggleSwitch,
2621
+ ...props
2622
+ });
2623
+ }
2624
+ /**
2625
+ * @param {Object} props
2626
+ * @param {Object} props.element
2627
+ * @param {String} props.id
2628
+ * @param {String} props.description
2629
+ * @param {Boolean} props.debounce
2630
+ * @param {Boolean} props.disabled
2631
+ * @param {FeelType} [props.feel]
2632
+ * @param {String} props.label
2633
+ * @param {Function} props.getValue
2634
+ * @param {Function} props.setValue
2635
+ * @param {Function} props.tooltipContainer
2636
+ * @param {Function} props.validate
2637
+ * @param {Function} props.show
2638
+ * @param {Function} props.example
2639
+ * @param {Array} props.variables
2640
+ * @param {Function} props.onFocus
2641
+ * @param {Function} props.onBlur
2642
+ */
2643
+ function FeelCheckboxEntry(props) {
2644
+ return jsx(FeelEntry, {
2645
+ class: "bio-properties-panel-feel-checkbox",
2646
+ OptionalComponent: OptionalFeelCheckbox,
2647
+ ...props
2648
+ });
2649
+ }
2650
+ /**
2651
+ * @param {Object} props
2652
+ * @param {Object} props.element
2653
+ * @param {String} props.id
2654
+ * @param {String} props.description
2655
+ * @param {String} props.hostLanguage
2656
+ * @param {Boolean} props.singleLine
2657
+ * @param {Boolean} props.debounce
2658
+ * @param {Boolean} props.disabled
2659
+ * @param {FeelType} [props.feel]
2660
+ * @param {String} props.label
2661
+ * @param {Function} props.getValue
2662
+ * @param {Function} props.setValue
2663
+ * @param {Function} props.tooltipContainer
2664
+ * @param {Function} props.validate
2665
+ * @param {Function} props.show
2666
+ * @param {Function} props.example
2667
+ * @param {Array} props.variables
2668
+ * @param {Function} props.onFocus
2669
+ * @param {Function} props.onBlur
2670
+ */
2671
+ function FeelTemplatingEntry(props) {
2672
+ return jsx(FeelEntry, {
2673
+ class: "bio-properties-panel-feel-templating",
2674
+ OptionalComponent: TemplatingEditor,
2675
+ ...props
2676
+ });
2677
+ }
2678
+ function isEdited$1(node) {
2679
+ if (!node) return false;
2680
+ if (node.type === "checkbox") return !!node.checked || node.classList.contains("edited");
2681
+ return !!node.value || node.classList.contains("edited");
2682
+ }
2683
+ function prefixId$5(id) {
2684
+ return `bio-properties-panel-${id}`;
2685
+ }
2686
+ //#endregion
2687
+ //#region src/components/entries/templating/Templating.js
2688
+ var noop$1 = () => {};
2689
+ /**
2690
+ * @param {Object} props
2691
+ * @param {Object} props.element
2692
+ * @param {String} props.id
2693
+ * @param {String} props.description
2694
+ * @param {Boolean} props.debounce
2695
+ * @param {Boolean} props.disabled
2696
+ * @param {String} props.label
2697
+ * @param {Function} props.getValue
2698
+ * @param {Function} props.setValue
2699
+ * @param {Function} props.tooltipContainer
2700
+ * @param {Function} props.validate
2701
+ * @param {Function} props.show
2702
+ */
2703
+ function TemplatingEntry(props) {
2704
+ const { element, id, description, debounce, disabled, label, getValue, setValue, tooltipContainer, validate, show = noop$1 } = props;
2705
+ const [validationError, setValidationError] = useState(null);
2706
+ const [localError, setLocalError] = useState(null);
2707
+ let value = getValue(element);
2708
+ useEffect(() => {
2709
+ if (isFunction(validate)) {
2710
+ const newValidationError = validate(value) || null;
2711
+ setValidationError(newValidationError);
2712
+ }
2713
+ }, [value, validate]);
2714
+ const onInput = useStaticCallback((newValue) => {
2715
+ let newValidationError = null;
2716
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
2717
+ if (newValue !== value) setValue(newValue, newValidationError);
2718
+ setValidationError(newValidationError);
2719
+ });
2720
+ const onError = useCallback((err) => {
2721
+ setLocalError(err);
2722
+ }, []);
2723
+ const temporaryError = useError(id);
2724
+ const error = localError || temporaryError || validationError;
2725
+ return jsxs("div", {
2726
+ class: classnames("bio-properties-panel-entry", error ? "has-error" : ""),
2727
+ "data-entry-id": id,
2728
+ children: [
2729
+ jsx(Templating, {
2730
+ debounce,
2731
+ disabled,
2732
+ id,
2733
+ label,
2734
+ onInput,
2735
+ onError,
2736
+ show,
2737
+ value,
2738
+ tooltipContainer
2739
+ }, element),
2740
+ error && jsx("div", {
2741
+ class: "bio-properties-panel-error",
2742
+ children: error
2743
+ }),
2744
+ jsx(Description, {
2745
+ forId: id,
2746
+ element,
2747
+ value: description
2748
+ })
2749
+ ]
2750
+ });
2751
+ }
2752
+ function Templating(props) {
2753
+ const { debounce, id, label, onInput, onError, value = "", disabled = false, tooltipContainer } = props;
2754
+ const [localValue, setLocalValue] = useState(value);
2755
+ const editorRef = useShowEntryEvent(id);
2756
+ const containerRef = useRef();
2757
+ const [focus, _setFocus] = useState(void 0);
2758
+ const setFocus = (offset = 0) => {
2759
+ const position = containerRef.current.contains(document.activeElement) ? document.activeElement.selectionStart : Infinity;
2760
+ _setFocus(position + offset);
2761
+ };
2762
+ const handleInputCallback = useMemo(() => {
2763
+ return debounce((newValue) => onInput(newValue.length ? newValue : void 0));
2764
+ }, [onInput, debounce]);
2765
+ const handleInput = (newValue) => {
2766
+ handleInputCallback(newValue);
2767
+ setLocalValue(newValue);
2768
+ };
2769
+ const handleLint = useStaticCallback((lint) => {
2770
+ if (!(lint && lint.length && lint.filter((e) => e.severity === "error") || []).length) {
2771
+ onError(void 0);
2772
+ return;
2773
+ }
2774
+ const error = lint[0];
2775
+ const message = `${error.source}: ${error.message}`;
2776
+ onError(message);
2777
+ });
2778
+ useEffect(() => {
2779
+ if (typeof focus !== "undefined") {
2780
+ editorRef.current.focus(focus);
2781
+ _setFocus(void 0);
2782
+ }
2783
+ }, [focus]);
2784
+ useEffect(() => {
2785
+ if (value === localValue) return;
2786
+ setLocalValue(value ? value : "");
2787
+ }, [value]);
2788
+ return jsxs("div", {
2789
+ class: "bio-properties-panel-feelers",
2790
+ children: [jsx("label", {
2791
+ id: prefixIdLabel(id),
2792
+ class: "bio-properties-panel-label",
2793
+ onClick: () => setFocus(),
2794
+ children: label
2795
+ }), jsx("div", {
2796
+ class: "bio-properties-panel-feelers-input",
2797
+ ref: containerRef,
2798
+ children: jsx(TemplatingEditor, {
2799
+ name: id,
2800
+ onInput: handleInput,
2801
+ contentAttributes: { "aria-labelledby": prefixIdLabel(id) },
2802
+ disabled,
2803
+ onLint: handleLint,
2804
+ value: localValue,
2805
+ ref: editorRef,
2806
+ tooltipContainer
2807
+ })
2808
+ })]
2809
+ });
2810
+ }
2811
+ function isEdited$6(node) {
2812
+ return node && (!!node.value || node.classList.contains("edited"));
2813
+ }
2814
+ function prefixIdLabel(id) {
2815
+ return `bio-properties-panel-feelers-${id}-label`;
2816
+ }
2817
+ //#endregion
2818
+ //#region src/components/entries/List.js
2819
+ /**
2820
+ * Entry for handling lists represented as nested entries.
2821
+ *
2822
+ * @template Item
2823
+ * @param {object} props
2824
+ * @param {string} props.id
2825
+ * @param {*} props.element
2826
+ * @param {Function} props.onAdd
2827
+ * @param {import('preact').Component} props.component
2828
+ * @param {string} [props.label='<empty>']
2829
+ * @param {Function} [props.onRemove]
2830
+ * @param {Item[]} [props.items]
2831
+ * @param {boolean} [props.open]
2832
+ * @param {string|boolean} [props.autoFocusEntry] either a custom selector string or true to focus the first input
2833
+ * @returns
2834
+ */
2835
+ function List(props) {
2836
+ const { id, element, items = [], component, label = "<empty>", open: shouldOpen, onAdd, onRemove, autoFocusEntry, ...restProps } = props;
2837
+ const entryRef = useRef(null);
2838
+ const [open, setOpen] = useState(!!shouldOpen);
2839
+ const [sticky, setSticky] = useState(false);
2840
+ const hasItems = !!items.length;
2841
+ const toggleOpen = () => hasItems && setOpen(!open);
2842
+ const newItems = useNewItems(items, usePrevious(element) !== element);
2843
+ useEffect(() => {
2844
+ if (open && !hasItems) setOpen(false);
2845
+ }, [open, hasItems]);
2846
+ /**
2847
+ * @param {MouseEvent} event
2848
+ */
2849
+ function addItem(event) {
2850
+ event.stopPropagation();
2851
+ onAdd();
2852
+ if (!open) setOpen(true);
2853
+ }
2854
+ useStickyIntersectionObserver(entryRef, "div.bio-properties-panel-scroll-container", setSticky);
2855
+ return jsxs("div", {
2856
+ "data-entry-id": id,
2857
+ class: classnames("bio-properties-panel-entry", "bio-properties-panel-list-entry", hasItems ? "" : "empty", open ? "open" : ""),
2858
+ ref: entryRef,
2859
+ children: [jsxs("div", {
2860
+ class: classnames("bio-properties-panel-list-entry-header", sticky && open ? "sticky" : ""),
2861
+ onClick: toggleOpen,
2862
+ children: [jsx("div", {
2863
+ class: classnames("bio-properties-panel-list-entry-header-title", open && "open"),
2864
+ children: label
2865
+ }), jsxs("div", {
2866
+ class: "bio-properties-panel-list-entry-header-buttons",
2867
+ children: [
2868
+ jsxs("button", {
2869
+ type: "button",
2870
+ title: "Create new list item",
2871
+ onClick: addItem,
2872
+ class: "bio-properties-panel-add-entry",
2873
+ children: [jsx(CreateIcon, {}), !hasItems ? jsx("span", {
2874
+ class: "bio-properties-panel-add-entry-label",
2875
+ children: "Create"
2876
+ }) : null]
2877
+ }),
2878
+ hasItems && jsx("div", {
2879
+ title: `List contains ${items.length} item${items.length != 1 ? "s" : ""}`,
2880
+ class: "bio-properties-panel-list-badge",
2881
+ children: items.length
2882
+ }),
2883
+ hasItems && jsx("button", {
2884
+ type: "button",
2885
+ title: "Toggle list item",
2886
+ class: "bio-properties-panel-arrow",
2887
+ children: jsx(ArrowIcon, { class: open ? "bio-properties-panel-arrow-down" : "bio-properties-panel-arrow-right" })
2888
+ })
2889
+ ]
2890
+ })]
2891
+ }), hasItems && jsx(ItemsList, {
2892
+ ...restProps,
2893
+ autoFocusEntry,
2894
+ component,
2895
+ element,
2896
+ id,
2897
+ items,
2898
+ newItems,
2899
+ onRemove,
2900
+ open
2901
+ })]
2902
+ });
2903
+ }
2904
+ function ItemsList(props) {
2905
+ const { autoFocusEntry, component: Component, element, id, items, newItems, onRemove, open, ...restProps } = props;
2906
+ const getKey = useKeyFactory();
2907
+ const newItem = newItems[0];
2908
+ useEffect(() => {
2909
+ if (newItem && autoFocusEntry) {
2910
+ const entry = query(`[data-entry-id="${id}"]`);
2911
+ const focusableInput = query(typeof autoFocusEntry === "boolean" ? ".bio-properties-panel-input" : autoFocusEntry, entry);
2912
+ if (focusableInput) {
2913
+ if (isFunction(focusableInput.select)) focusableInput.select();
2914
+ else if (isFunction(focusableInput.focus)) focusableInput.focus();
2915
+ }
2916
+ }
2917
+ }, [
2918
+ newItem,
2919
+ autoFocusEntry,
2920
+ id
2921
+ ]);
2922
+ return jsx("ol", {
2923
+ class: classnames("bio-properties-panel-list-entry-items", open ? "open" : ""),
2924
+ children: items.map((item, index) => {
2925
+ const key = getKey(item);
2926
+ return jsxs("li", {
2927
+ class: "bio-properties-panel-list-entry-item",
2928
+ children: [jsx(Component, {
2929
+ ...restProps,
2930
+ element,
2931
+ id,
2932
+ index,
2933
+ item,
2934
+ open: item === newItem
2935
+ }), onRemove && jsx("button", {
2936
+ type: "button",
2937
+ title: "Delete item",
2938
+ class: "bio-properties-panel-remove-entry bio-properties-panel-remove-list-entry",
2939
+ onClick: () => onRemove && onRemove(item),
2940
+ children: jsx(DeleteIcon, {})
2941
+ })]
2942
+ }, key);
2943
+ })
2944
+ });
2945
+ }
2946
+ function useNewItems(items = [], shouldReset) {
2947
+ const previousItems = usePrevious(items.slice()) || [];
2948
+ if (shouldReset) return [];
2949
+ return previousItems ? items.filter((item) => !previousItems.includes(item)) : [];
2950
+ }
2951
+ //#endregion
2952
+ //#region src/components/entries/Select.js
2953
+ /**
2954
+ * @typedef { { value: string, label: string, disabled: boolean, children: { value: string, label: string, disabled: boolean } } } Option
2955
+ */
2956
+ /**
2957
+ * Provides basic select input.
2958
+ *
2959
+ * @param {object} props
2960
+ * @param {string} props.id
2961
+ * @param {string[]} props.path
2962
+ * @param {string} props.label
2963
+ * @param {Function} props.onChange
2964
+ * @param {Function} props.onFocus
2965
+ * @param {Function} props.onBlur
2966
+ * @param {Array<Option>} [props.options]
2967
+ * @param {string} props.value
2968
+ * @param {boolean} [props.disabled]
2969
+ */
2970
+ function Select(props) {
2971
+ const { id, label, onChange, options = [], value = "", disabled, onFocus, onBlur, tooltip } = props;
2972
+ const ref = useShowEntryEvent(id);
2973
+ const [localValue, setLocalValue] = useState(value);
2974
+ const handleChangeCallback = ({ target }) => {
2975
+ onChange(target.value);
2976
+ };
2977
+ const handleChange = (e) => {
2978
+ handleChangeCallback(e);
2979
+ setLocalValue(e.target.value);
2980
+ };
2981
+ useEffect(() => {
2982
+ if (value === localValue) return;
2983
+ setLocalValue(value);
2984
+ }, [value]);
2985
+ return jsxs("div", {
2986
+ class: "bio-properties-panel-select",
2987
+ children: [jsx("label", {
2988
+ for: prefixId$4(id),
2989
+ class: "bio-properties-panel-label",
2990
+ children: jsx(TooltipWrapper, {
2991
+ value: tooltip,
2992
+ forId: id,
2993
+ element: props.element,
2994
+ children: label
2995
+ })
2996
+ }), jsx("select", {
2997
+ ref,
2998
+ id: prefixId$4(id),
2999
+ name: id,
3000
+ class: "bio-properties-panel-input",
3001
+ onInput: handleChange,
3002
+ onFocus,
3003
+ onBlur,
3004
+ value: localValue,
3005
+ disabled,
3006
+ children: options.map((option, idx) => {
3007
+ if (option.children) return jsx("optgroup", {
3008
+ label: option.label,
3009
+ children: option.children.map((child, idx) => jsx("option", {
3010
+ value: child.value,
3011
+ disabled: child.disabled,
3012
+ children: child.label
3013
+ }, idx))
3014
+ }, idx);
3015
+ return jsx("option", {
3016
+ value: option.value,
3017
+ disabled: option.disabled,
3018
+ children: option.label
3019
+ }, idx);
3020
+ })
3021
+ })]
3022
+ });
3023
+ }
3024
+ /**
3025
+ * @param {object} props
3026
+ * @param {object} props.element
3027
+ * @param {string} props.id
3028
+ * @param {string} [props.description]
3029
+ * @param {string} props.label
3030
+ * @param {Function} props.getValue
3031
+ * @param {Function} props.setValue
3032
+ * @param {Function} props.onFocus
3033
+ * @param {Function} props.onBlur
3034
+ * @param {Function} props.getOptions
3035
+ * @param {boolean} [props.disabled]
3036
+ * @param {Function} [props.validate]
3037
+ * @param {string|import('preact').Component} props.tooltip
3038
+ */
3039
+ function SelectEntry(props) {
3040
+ const { element, id, description, label, getValue, setValue, getOptions, disabled, onFocus, onBlur, validate, tooltip } = props;
3041
+ const options = getOptions(element);
3042
+ const globalError = useError(id);
3043
+ const [localError, setLocalError] = useState(null);
3044
+ let value = getValue(element);
3045
+ useEffect(() => {
3046
+ if (isFunction(validate)) {
3047
+ const newValidationError = validate(value) || null;
3048
+ setLocalError(newValidationError);
3049
+ }
3050
+ }, [value, validate]);
3051
+ const onChange = (newValue) => {
3052
+ let newValidationError = null;
3053
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
3054
+ setValue(newValue, newValidationError);
3055
+ setLocalError(newValidationError);
3056
+ };
3057
+ const error = globalError || localError;
3058
+ return jsxs("div", {
3059
+ class: classnames("bio-properties-panel-entry", error ? "has-error" : ""),
3060
+ "data-entry-id": id,
3061
+ children: [
3062
+ jsx(Select, {
3063
+ id,
3064
+ label,
3065
+ value,
3066
+ onChange,
3067
+ onFocus,
3068
+ onBlur,
3069
+ options,
3070
+ disabled,
3071
+ tooltip,
3072
+ element
3073
+ }, element),
3074
+ error && jsx("div", {
3075
+ class: "bio-properties-panel-error",
3076
+ children: error
3077
+ }),
3078
+ jsx(Description, {
3079
+ forId: id,
3080
+ element,
3081
+ value: description
3082
+ })
3083
+ ]
3084
+ });
3085
+ }
3086
+ function isEdited$4(node) {
3087
+ return node && !!node.value;
3088
+ }
3089
+ function prefixId$4(id) {
3090
+ return `bio-properties-panel-${id}`;
3091
+ }
3092
+ //#endregion
3093
+ //#region src/components/entries/Simple.js
3094
+ /**
3095
+ * @param {Object} props
3096
+ * @param {Function} props.debounce
3097
+ * @param {Boolean} [props.disabled]
3098
+ * @param {Object} props.element
3099
+ * @param {Function} props.getValue
3100
+ * @param {String} props.id
3101
+ * @param {Function} [props.onBlur]
3102
+ * @param {Function} [props.onFocus]
3103
+ * @param {Function} props.setValue
3104
+ */
3105
+ function Simple(props) {
3106
+ const { debounce, disabled, element, getValue, id, onBlur, onFocus, setValue } = props;
3107
+ const value = getValue(element);
3108
+ const [localValue, setLocalValue] = useState(value);
3109
+ const handleInputCallback = useMemo(() => {
3110
+ return debounce((target) => setValue(target.value.length ? target.value : void 0));
3111
+ }, [setValue, debounce]);
3112
+ const handleInput = (e) => {
3113
+ handleInputCallback(e.target);
3114
+ setLocalValue(e.target.value);
3115
+ };
3116
+ useEffect(() => {
3117
+ if (value === localValue) return;
3118
+ setLocalValue(value);
3119
+ }, [value]);
3120
+ return jsx("div", {
3121
+ class: "bio-properties-panel-simple",
3122
+ children: jsx("input", {
3123
+ id: prefixId$3(id),
3124
+ type: "text",
3125
+ name: id,
3126
+ spellCheck: "false",
3127
+ autoComplete: "off",
3128
+ disabled,
3129
+ class: "bio-properties-panel-input",
3130
+ onInput: handleInput,
3131
+ "aria-label": localValue || "<empty>",
3132
+ onFocus,
3133
+ onBlur,
3134
+ value: localValue
3135
+ }, element)
3136
+ });
3137
+ }
3138
+ function isEdited$5(node) {
3139
+ return node && !!node.value;
3140
+ }
3141
+ function prefixId$3(id) {
3142
+ return `bio-properties-panel-${id}`;
3143
+ }
3144
+ //#endregion
3145
+ //#region src/components/entries/TextArea.js
3146
+ function resizeToContents(element) {
3147
+ element.style.height = "auto";
3148
+ element.style.height = `${element.scrollHeight + 2}px`;
3149
+ }
3150
+ function TextArea(props) {
3151
+ const { id, label, debounce, onInput: commitValue, value = "", disabled, monospace, onFocus, onBlur, onPaste, autoResize = true, placeholder, rows = autoResize ? 1 : 2, tooltip } = props;
3152
+ const [localValue, setLocalValue] = useState(value);
3153
+ const ref = useShowEntryEvent(id);
3154
+ const onInput = useCallback((newValue) => {
3155
+ commitValue(newValue === "" ? void 0 : newValue);
3156
+ }, [commitValue]);
3157
+ const visible = useElementVisible(ref.current);
3158
+ /**
3159
+ * @type { import('min-dash').DebouncedFunction }
3160
+ */
3161
+ const handleInput = useDebounce(onInput, debounce);
3162
+ const handleLocalInput = (e) => {
3163
+ autoResize && resizeToContents(e.target);
3164
+ if (e.target.value === localValue) return;
3165
+ setLocalValue(e.target.value);
3166
+ handleInput(e.target.value);
3167
+ };
3168
+ const handleOnBlur = (e) => {
3169
+ const trimmedValue = e.target.value.trim();
3170
+ handleInput.cancel?.();
3171
+ onInput(trimmedValue);
3172
+ setLocalValue(trimmedValue);
3173
+ if (onBlur) onBlur(e);
3174
+ };
3175
+ const handleOnPaste = (e) => {
3176
+ const input = e.target;
3177
+ const isFieldEmpty = !input.value;
3178
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
3179
+ if (isFieldEmpty || isAllSelected) {
3180
+ const trimmedValue = e.clipboardData.getData("text").trim();
3181
+ setLocalValue(trimmedValue);
3182
+ handleInput(trimmedValue);
3183
+ if (onPaste) onPaste(e);
3184
+ e.preventDefault();
3185
+ return;
3186
+ }
3187
+ if (onPaste) onPaste(e);
3188
+ };
3189
+ const handleOnKeyDown = (e) => {
3190
+ if (isCmdWithChar(e)) handleInput.flush();
3191
+ };
3192
+ useLayoutEffect(() => {
3193
+ autoResize && resizeToContents(ref.current);
3194
+ }, []);
3195
+ useLayoutEffect(() => {
3196
+ visible && autoResize && resizeToContents(ref.current);
3197
+ }, [visible]);
3198
+ useEffect(() => {
3199
+ if (value === localValue) return;
3200
+ setLocalValue(value);
3201
+ }, [value]);
3202
+ return jsxs("div", {
3203
+ class: "bio-properties-panel-textarea",
3204
+ children: [jsx("label", {
3205
+ for: prefixId$2(id),
3206
+ class: "bio-properties-panel-label",
3207
+ children: jsx(TooltipWrapper, {
3208
+ value: tooltip,
3209
+ forId: id,
3210
+ element: props.element,
3211
+ children: label
3212
+ })
3213
+ }), jsx("textarea", {
3214
+ ref,
3215
+ id: prefixId$2(id),
3216
+ name: id,
3217
+ spellCheck: "false",
3218
+ class: classnames("bio-properties-panel-input", monospace ? "bio-properties-panel-input-monospace" : "", autoResize ? "auto-resize" : ""),
3219
+ onInput: handleLocalInput,
3220
+ onFocus,
3221
+ onKeyDown: handleOnKeyDown,
3222
+ onBlur: handleOnBlur,
3223
+ onPaste: handleOnPaste,
3224
+ placeholder,
3225
+ rows,
3226
+ value: localValue,
3227
+ disabled,
3228
+ "data-gramm": "false"
3229
+ })]
3230
+ });
3231
+ }
3232
+ /**
3233
+ * @param {object} props
3234
+ * @param {object} props.element
3235
+ * @param {string} props.id
3236
+ * @param {string} props.description
3237
+ * @param {boolean} props.debounce
3238
+ * @param {string} props.label
3239
+ * @param {Function} props.getValue
3240
+ * @param {Function} props.setValue
3241
+ * @param {Function} props.onFocus
3242
+ * @param {Function} props.onBlur
3243
+ * @param {Function} props.onPaste
3244
+ * @param {number} props.rows
3245
+ * @param {boolean} props.monospace
3246
+ * @param {Function} [props.validate]
3247
+ * @param {boolean} [props.disabled]
3248
+ */
3249
+ function TextAreaEntry(props) {
3250
+ const { element, id, description, debounce, label, getValue, setValue, rows, monospace, disabled, validate, onFocus, onBlur, onPaste, placeholder, autoResize, tooltip } = props;
3251
+ const globalError = useError(id);
3252
+ const [localError, setLocalError] = useState(null);
3253
+ let value = getValue(element);
3254
+ useEffect(() => {
3255
+ if (isFunction(validate)) {
3256
+ const newValidationError = validate(value) || null;
3257
+ setLocalError(newValidationError);
3258
+ }
3259
+ }, [value, validate]);
3260
+ const onInput = useStaticCallback((newValue) => {
3261
+ const value = getValue(element);
3262
+ let newValidationError = null;
3263
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
3264
+ if (newValue !== value) setValue(newValue, newValidationError);
3265
+ setLocalError(newValidationError);
3266
+ });
3267
+ const error = globalError || localError;
3268
+ return jsxs("div", {
3269
+ class: classnames("bio-properties-panel-entry", error ? "has-error" : ""),
3270
+ "data-entry-id": id,
3271
+ children: [
3272
+ jsx(TextArea, {
3273
+ id,
3274
+ label,
3275
+ value,
3276
+ onInput,
3277
+ onFocus,
3278
+ onBlur,
3279
+ onPaste,
3280
+ rows,
3281
+ debounce,
3282
+ monospace,
3283
+ disabled,
3284
+ placeholder,
3285
+ autoResize,
3286
+ tooltip,
3287
+ element
3288
+ }, element),
3289
+ error && jsx("div", {
3290
+ class: "bio-properties-panel-error",
3291
+ children: error
3292
+ }),
3293
+ jsx(Description, {
3294
+ forId: id,
3295
+ element,
3296
+ value: description
3297
+ })
3298
+ ]
3299
+ });
3300
+ }
3301
+ function isEdited$7(node) {
3302
+ return node && !!node.value;
3303
+ }
3304
+ function prefixId$2(id) {
3305
+ return `bio-properties-panel-${id}`;
3306
+ }
3307
+ //#endregion
3308
+ //#region src/components/entries/TextField.js
3309
+ function Textfield(props) {
3310
+ const { debounce, disabled = false, id, label, onInput: commitValue, onFocus, onBlur, onPaste, placeholder, value = "", tooltip } = props;
3311
+ const [localValue, setLocalValue] = useState(value || "");
3312
+ const ref = useShowEntryEvent(id);
3313
+ const onInput = useCallback((newValue) => {
3314
+ commitValue(newValue === "" ? void 0 : newValue);
3315
+ }, [commitValue]);
3316
+ /**
3317
+ * @type { import('min-dash').DebouncedFunction }
3318
+ */
3319
+ const handleInput = useDebounce(onInput, debounce);
3320
+ const handleOnBlur = (e) => {
3321
+ const trimmedValue = e.target.value.trim();
3322
+ handleInput.cancel?.();
3323
+ onInput(trimmedValue);
3324
+ setLocalValue(trimmedValue);
3325
+ if (onBlur) onBlur(e);
3326
+ };
3327
+ const handleOnPaste = (e) => {
3328
+ const input = e.target;
3329
+ const isFieldEmpty = !input.value;
3330
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
3331
+ if (isFieldEmpty || isAllSelected) {
3332
+ const trimmedValue = e.clipboardData.getData("text").trim();
3333
+ setLocalValue(trimmedValue);
3334
+ handleInput(trimmedValue);
3335
+ if (onPaste) onPaste(e);
3336
+ e.preventDefault();
3337
+ return;
3338
+ }
3339
+ if (onPaste) onPaste(e);
3340
+ };
3341
+ const handleLocalInput = (e) => {
3342
+ if (e.target.value === localValue) return;
3343
+ setLocalValue(e.target.value);
3344
+ handleInput(e.target.value);
3345
+ };
3346
+ useEffect(() => {
3347
+ if (value === localValue) return;
3348
+ setLocalValue(value);
3349
+ }, [value]);
3350
+ const handleOnKeyDown = (e) => {
3351
+ if (isCmdWithChar(e)) handleInput.flush();
3352
+ };
3353
+ return jsxs("div", {
3354
+ class: "bio-properties-panel-textfield",
3355
+ children: [jsx("label", {
3356
+ for: prefixId$1(id),
3357
+ class: "bio-properties-panel-label",
3358
+ children: jsx(TooltipWrapper, {
3359
+ value: tooltip,
3360
+ forId: id,
3361
+ element: props.element,
3362
+ children: label
3363
+ })
3364
+ }), jsx("input", {
3365
+ ref,
3366
+ id: prefixId$1(id),
3367
+ type: "text",
3368
+ name: id,
3369
+ spellCheck: "false",
3370
+ autoComplete: "off",
3371
+ disabled,
3372
+ class: "bio-properties-panel-input",
3373
+ onInput: handleLocalInput,
3374
+ onFocus,
3375
+ onKeyDown: handleOnKeyDown,
3376
+ onBlur: handleOnBlur,
3377
+ onPaste: handleOnPaste,
3378
+ placeholder,
3379
+ value: localValue
3380
+ })]
3381
+ });
3382
+ }
3383
+ /**
3384
+ * @param {Object} props
3385
+ * @param {Object} props.element
3386
+ * @param {String} props.id
3387
+ * @param {String} props.description
3388
+ * @param {Boolean} props.debounce
3389
+ * @param {Boolean} props.disabled
3390
+ * @param {String} props.label
3391
+ * @param {Function} props.getValue
3392
+ * @param {Function} props.setValue
3393
+ * @param {Function} props.onFocus
3394
+ * @param {Function} props.onBlur
3395
+ * @param {string|import('preact').Component} props.tooltip
3396
+ * @param {Function} props.validate
3397
+ */
3398
+ function TextfieldEntry(props) {
3399
+ const { element, id, description, debounce, disabled, label, getValue, setValue, validate, onFocus, onBlur, onPaste, placeholder, tooltip } = props;
3400
+ const globalError = useError(id);
3401
+ const [localError, setLocalError] = useState(null);
3402
+ let value = getValue(element);
3403
+ useEffect(() => {
3404
+ if (isFunction(validate)) {
3405
+ const newValidationError = validate(value) || null;
3406
+ setLocalError(newValidationError);
3407
+ }
3408
+ }, [value, validate]);
3409
+ const onInput = useStaticCallback((newValue) => {
3410
+ const value = getValue(element);
3411
+ let newValidationError = null;
3412
+ if (isFunction(validate)) newValidationError = validate(newValue) || null;
3413
+ if (newValue !== value) setValue(newValue, newValidationError);
3414
+ setLocalError(newValidationError);
3415
+ });
3416
+ const error = globalError || localError;
3417
+ return jsxs("div", {
3418
+ class: classnames("bio-properties-panel-entry", error ? "has-error" : ""),
3419
+ "data-entry-id": id,
3420
+ children: [
3421
+ jsx(Textfield, {
3422
+ debounce,
3423
+ disabled,
3424
+ id,
3425
+ label,
3426
+ onInput,
3427
+ onFocus,
3428
+ onBlur,
3429
+ onPaste,
3430
+ placeholder,
3431
+ value,
3432
+ tooltip,
3433
+ element
3434
+ }, element),
3435
+ error && jsx("div", {
3436
+ class: "bio-properties-panel-error",
3437
+ children: error
3438
+ }),
3439
+ jsx(Description, {
3440
+ forId: id,
3441
+ element,
3442
+ value: description
3443
+ })
3444
+ ]
3445
+ });
3446
+ }
3447
+ function isEdited$8(node) {
3448
+ return node && !!node.value;
3449
+ }
3450
+ function prefixId$1(id) {
3451
+ return `bio-properties-panel-${id}`;
3452
+ }
3453
+ //#endregion
3454
+ //#region src/features/debounce-input/debounceInput.js
3455
+ var DEFAULT_DEBOUNCE_TIME = 600;
3456
+ /**
3457
+ * Creates a debounced version of a function, delaying its execution based on `debounceDelay`.
3458
+ *
3459
+ * - If `debounceDelay` is `false`, the function executes immediately without debouncing.
3460
+ * - If a number is provided, the function execution is delayed by the given time in milliseconds.
3461
+ *
3462
+ * @param { Boolean | Number } [debounceDelay=600]
3463
+ *
3464
+ * @example
3465
+ * const debounce = debounceInput();
3466
+ * const debouncedFn = debounce(fn);
3467
+ *
3468
+ * debouncedFn(); // Executes after 300ms (default) if no further calls occur.
3469
+ */
3470
+ function debounceInput(debounceDelay) {
3471
+ /**
3472
+ * Applies debounce to the provided function, with a previously setup delay.
3473
+ *
3474
+ * @template { (...args: any[]) => any } T
3475
+ *
3476
+ * @param {T} fn
3477
+ *
3478
+ * @return { (...p: Parameters<T>) => any }
3479
+ */
3480
+ return function debounce$1(fn) {
3481
+ if (debounceDelay === false) return fn;
3482
+ var debounceTime = isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
3483
+ return debounce(fn, debounceTime);
3484
+ };
3485
+ }
3486
+ debounceInput.$inject = ["config.debounceInput"];
3487
+ //#endregion
3488
+ //#region src/features/debounce-input/index.js
3489
+ var debounce_input_default = { debounceInput: ["factory", debounceInput] };
3490
+ //#endregion
3491
+ //#region src/components/util/dragger.js
3492
+ /**
3493
+ * Add a dragger that calls back the passed function with
3494
+ * { event, delta } on drag.
3495
+ *
3496
+ * @example
3497
+ *
3498
+ * function dragMove(event, delta) {
3499
+ * // we are dragging (!!)
3500
+ * }
3501
+ *
3502
+ * domElement.addEventListener('dragstart', dragger(dragMove));
3503
+ *
3504
+ * @param {Function} fn
3505
+ * @param {Element} [dragPreview]
3506
+ *
3507
+ * @return {Function} drag start callback function
3508
+ */
3509
+ function createDragger(fn, dragPreview) {
3510
+ let self;
3511
+ let startX, startY;
3512
+ /** drag start */
3513
+ function onDragStart(event) {
3514
+ self = this;
3515
+ startX = event.clientX;
3516
+ startY = event.clientY;
3517
+ if (event.dataTransfer) event.dataTransfer.setDragImage(dragPreview || emptyCanvas(), 0, 0);
3518
+ document.addEventListener("dragover", onDrag, true);
3519
+ document.addEventListener("dragenter", preventDefault, true);
3520
+ document.addEventListener("dragend", onEnd);
3521
+ document.addEventListener("drop", preventDefault);
3522
+ }
3523
+ function onDrag(event) {
3524
+ const delta = {
3525
+ x: event.clientX - startX,
3526
+ y: event.clientY - startY
3527
+ };
3528
+ return fn.call(self, event, delta);
3529
+ }
3530
+ function onEnd() {
3531
+ document.removeEventListener("dragover", onDrag, true);
3532
+ document.removeEventListener("dragenter", preventDefault, true);
3533
+ document.removeEventListener("dragend", onEnd);
3534
+ document.removeEventListener("drop", preventDefault);
3535
+ }
3536
+ return onDragStart;
3537
+ }
3538
+ function preventDefault(event) {
3539
+ event.preventDefault();
3540
+ event.stopPropagation();
3541
+ }
3542
+ function emptyCanvas() {
3543
+ return domify("<canvas width=\"0\" height=\"0\" />");
3544
+ }
3545
+ //#endregion
3546
+ //#region src/features/expression-popup/components/Popup.js
3547
+ var noop = () => {};
3548
+ /**
3549
+ * A generic popup component.
3550
+ *
3551
+ * @param {Object} props
3552
+ * @param {string} [props.className]
3553
+ * @param {boolean} [props.delayInitialFocus]
3554
+ * @param {{top: number, left: number}} [props.position]
3555
+ * @param {number} [props.width]
3556
+ * @param {number} [props.height]
3557
+ * @param {Function} props.onClose
3558
+ * @param {Function} [props.onPostActivate]
3559
+ * @param {Function} [props.onPostDeactivate]
3560
+ * @param {boolean} [props.returnFocus]
3561
+ * @param {boolean} [props.closeOnEscape]
3562
+ * @param {string} props.title
3563
+ * @param {Ref} [ref]
3564
+ */
3565
+ function PopupComponent(props, globalRef) {
3566
+ const { className, delayInitialFocus, position, width, height, onClose, onPostActivate = noop, onPostDeactivate = noop, returnFocus = true, closeOnEscape = true, title } = props;
3567
+ const focusTrapRef = useRef(null);
3568
+ const localRef = useRef(null);
3569
+ const popupRef = globalRef || localRef;
3570
+ const handleKeydown = (event) => {
3571
+ event.stopPropagation();
3572
+ if (closeOnEscape && event.key === "Escape") onClose();
3573
+ };
3574
+ const handleFocus = () => {
3575
+ if (focusTrapRef.current) focusTrapRef.current.activate();
3576
+ };
3577
+ let style = {};
3578
+ if (position) style = {
3579
+ ...style,
3580
+ top: position.top + "px",
3581
+ left: position.left + "px"
3582
+ };
3583
+ if (width) style.width = width + "px";
3584
+ if (height) style.height = height + "px";
3585
+ useEffect(() => {
3586
+ if (popupRef.current) popupRef.current.addEventListener("focusin", handleFocus);
3587
+ return () => {
3588
+ if (popupRef.current) popupRef.current.removeEventListener("focusin", handleFocus);
3589
+ };
3590
+ }, [popupRef]);
3591
+ useEffect(() => {
3592
+ if (popupRef.current) {
3593
+ focusTrapRef.current = focusTrap.createFocusTrap(popupRef.current, {
3594
+ clickOutsideDeactivates: true,
3595
+ delayInitialFocus,
3596
+ fallbackFocus: popupRef.current,
3597
+ onPostActivate,
3598
+ onPostDeactivate,
3599
+ returnFocusOnDeactivate: returnFocus
3600
+ });
3601
+ focusTrapRef.current.activate();
3602
+ }
3603
+ return () => focusTrapRef.current && focusTrapRef.current.deactivate();
3604
+ }, [popupRef]);
3605
+ return jsx("div", {
3606
+ "aria-label": title,
3607
+ tabIndex: -1,
3608
+ ref: popupRef,
3609
+ onKeyDown: handleKeydown,
3610
+ role: "dialog",
3611
+ class: classnames("bio-properties-panel-popup", className),
3612
+ style,
3613
+ children: props.children
3614
+ });
3615
+ }
3616
+ var Popup = forwardRef(PopupComponent);
3617
+ Popup.Title = Title;
3618
+ Popup.Body = Body;
3619
+ Popup.Footer = Footer;
3620
+ function Title(props) {
3621
+ const { children, className, draggable, eventBus, title, showCloseButton = false, closeButtonTooltip = "Close popup", onClose, ...rest } = props;
3622
+ const context = useRef({
3623
+ startPosition: null,
3624
+ newPosition: null
3625
+ });
3626
+ const dragPreviewRef = useRef();
3627
+ const titleRef = useRef();
3628
+ const onMove = (event, delta) => {
3629
+ cancel(event);
3630
+ const { x: dx, y: dy } = delta;
3631
+ const newPosition = {
3632
+ x: context.current.startPosition.x + dx,
3633
+ y: context.current.startPosition.y + dy
3634
+ };
3635
+ const popupParent = getPopupParent(titleRef.current);
3636
+ popupParent.style.top = newPosition.y + "px";
3637
+ popupParent.style.left = newPosition.x + "px";
3638
+ eventBus?.fire("feelPopup.dragover", {
3639
+ newPosition,
3640
+ delta
3641
+ });
3642
+ };
3643
+ const onMoveStart = (event) => {
3644
+ createDragger(onMove, dragPreviewRef.current)(event);
3645
+ event.stopPropagation();
3646
+ const bounds = getPopupParent(titleRef.current).getBoundingClientRect();
3647
+ context.current.startPosition = {
3648
+ x: bounds.left,
3649
+ y: bounds.top
3650
+ };
3651
+ eventBus?.fire("feelPopup.dragstart");
3652
+ };
3653
+ const onMoveEnd = () => {
3654
+ context.current.newPosition = null;
3655
+ eventBus?.fire("feelPopup.dragend");
3656
+ };
3657
+ return jsxs("div", {
3658
+ class: classnames("bio-properties-panel-popup__header", draggable && "draggable", className),
3659
+ ref: titleRef,
3660
+ draggable,
3661
+ onDragStart: onMoveStart,
3662
+ onDragEnd: onMoveEnd,
3663
+ ...rest,
3664
+ children: [
3665
+ draggable && jsxs(Fragment, { children: [jsx("div", {
3666
+ ref: dragPreviewRef,
3667
+ class: "bio-properties-panel-popup__drag-preview"
3668
+ }), jsx("div", {
3669
+ class: "bio-properties-panel-popup__drag-handle",
3670
+ children: jsx(DragIcon, {})
3671
+ })] }),
3672
+ jsx("div", {
3673
+ class: "bio-properties-panel-popup__title",
3674
+ children: title
3675
+ }),
3676
+ children,
3677
+ showCloseButton && jsx("button", {
3678
+ title: closeButtonTooltip,
3679
+ class: "bio-properties-panel-popup__close",
3680
+ onClick: onClose,
3681
+ children: jsx(CloseIcon, {})
3682
+ })
3683
+ ]
3684
+ });
3685
+ }
3686
+ function Body(props) {
3687
+ const { children, className, ...rest } = props;
3688
+ return jsx("div", {
3689
+ class: classnames("bio-properties-panel-popup__body", className),
3690
+ ...rest,
3691
+ children
3692
+ });
3693
+ }
3694
+ function Footer(props) {
3695
+ const { children, className, ...rest } = props;
3696
+ return jsx("div", {
3697
+ class: classnames("bio-properties-panel-popup__footer", className),
3698
+ ...rest,
3699
+ children: props.children
3700
+ });
3701
+ }
3702
+ function getPopupParent(node) {
3703
+ return node.closest(".bio-properties-panel-popup");
3704
+ }
3705
+ function cancel(event) {
3706
+ event.preventDefault();
3707
+ event.stopPropagation();
3708
+ }
3709
+ /**
3710
+ * Expression editor popup component
3711
+ * @param {ExpressionPopupProps} props
3712
+ */
3713
+ function ExpressionPopup$1(props) {
3714
+ const { entryId, onInput, onClose, title, type, value, links, variables, position, hostLanguage, singleLine, sourceElement, tooltipContainer, eventBus, feelLanguageContext } = props;
3715
+ const editorRef = useRef();
3716
+ const popupRef = useRef();
3717
+ const isAutoCompletionOpen = useRef(false);
3718
+ const handleSetReturnFocus = () => {
3719
+ sourceElement && sourceElement.focus();
3720
+ };
3721
+ const onKeyDownCapture = (event) => {
3722
+ if (event.key === "Escape") isAutoCompletionOpen.current = autoCompletionOpen(event.target);
3723
+ };
3724
+ const onKeyDown = (event) => {
3725
+ if (event.key === "Escape") {
3726
+ if (!isAutoCompletionOpen.current) {
3727
+ onClose();
3728
+ isAutoCompletionOpen.current = false;
3729
+ }
3730
+ }
3731
+ };
3732
+ useEffect(() => {
3733
+ if (editorRef.current) editorRef.current.focus();
3734
+ }, [editorRef]);
3735
+ return jsxs(Popup, {
3736
+ className: "bio-properties-panel-feel-popup",
3737
+ position,
3738
+ title,
3739
+ returnFocus: false,
3740
+ closeOnEscape: false,
3741
+ delayInitialFocus: false,
3742
+ onPostDeactivate: handleSetReturnFocus,
3743
+ height: 250,
3744
+ width: 700,
3745
+ ref: popupRef,
3746
+ children: [jsx(Popup.Title, {
3747
+ title,
3748
+ eventBus,
3749
+ showCloseButton: true,
3750
+ closeButtonTooltip: "Save and close",
3751
+ onClose,
3752
+ draggable: true,
3753
+ children: jsx(Fragment, { children: (links || []).map((link, index) => {
3754
+ return jsxs("a", {
3755
+ rel: "noreferrer",
3756
+ href: link.href,
3757
+ target: "_blank",
3758
+ class: "bio-properties-panel-feel-popup__title-link",
3759
+ children: [link.title, jsx(LaunchIcon, {})]
3760
+ }, index);
3761
+ }) })
3762
+ }), jsx(Popup.Body, { children: jsxs("div", {
3763
+ onKeyDownCapture,
3764
+ onKeyDown,
3765
+ class: "bio-properties-panel-feel-popup__body",
3766
+ children: [type === "feel" && jsx(ExpressionEditor$1, {
3767
+ enableGutters: true,
3768
+ id: prefixId(entryId),
3769
+ name: entryId,
3770
+ onInput,
3771
+ value,
3772
+ variables,
3773
+ feelLanguageContext,
3774
+ ref: editorRef,
3775
+ tooltipContainer
3776
+ }), type === "feelers" && jsx(TemplatingEditor, {
3777
+ id: prefixId(entryId),
3778
+ contentAttributes: { "aria-label": title },
3779
+ enableGutters: true,
3780
+ hostLanguage,
3781
+ name: entryId,
3782
+ onInput,
3783
+ value,
3784
+ ref: editorRef,
3785
+ singleLine,
3786
+ tooltipContainer
3787
+ })]
3788
+ }) })]
3789
+ });
3790
+ }
3791
+ function prefixId(id) {
3792
+ return `bio-properties-panel-${id}`;
3793
+ }
3794
+ function autoCompletionOpen(element) {
3795
+ const editor = element.closest(".cm-editor");
3796
+ return editor ? editor.querySelector(".cm-tooltip-autocomplete") : null;
3797
+ }
3798
+ //#endregion
3799
+ //#region src/features/expression-popup/components/helpers.js
3800
+ function getPopupTitle({ element, label }) {
3801
+ let popupTitle = "";
3802
+ if (element && element.type) popupTitle = `${element.type} / `;
3803
+ return `${popupTitle}${label}`;
3804
+ }
3805
+ function getPopupPosition() {
3806
+ const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
3807
+ const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
3808
+ return {
3809
+ left: Math.max(0, (viewportWidth - 700) / 2),
3810
+ top: Math.max(0, (viewportHeight - 250) / 2)
3811
+ };
3812
+ }
3813
+ //#endregion
3814
+ //#region src/features/expression-popup/ExpressionPopup.js
3815
+ /**
3816
+ * FEEL popup component, built as a singleton.
3817
+ *
3818
+ * In order to implement a custom replacement, handle the following events:
3819
+ * - `propertiesPanel.openPopup`
3820
+ * - `propertiesPanel.closePopup`
3821
+ * - `propertiesPanel.detach`
3822
+ *
3823
+ * Within the custom renderer, make sure to emit the following events:
3824
+ * - `feelPopup.open` - fired before the popup is mounted
3825
+ * - `feelPopup.opened` - fired after the popup is mounted. Event context contains the DOM node of the popup as `domNode`
3826
+ * - `feelPopup.close` - fired before the popup is unmounted. Event context contains the DOM node of the popup as `domNode`
3827
+ * - `feelPopup.closed` - fired after the popup is unmounted
3828
+ */
3829
+ var ExpressionPopup = class {
3830
+ constructor(eventBus, config = {}) {
3831
+ this._eventBus = eventBus;
3832
+ this._config = config;
3833
+ this._isOpen = false;
3834
+ eventBus.on("propertiesPanel.openPopup", (_, context) => {
3835
+ this.open(context.entryId, context, context.sourceElement);
3836
+ return true;
3837
+ });
3838
+ eventBus.on(["propertiesPanel.closePopup", "propertiesPanel.detach"], () => {
3839
+ this.close();
3840
+ });
3841
+ }
3842
+ /**
3843
+ * Check if the FEEL popup is open.
3844
+ * @return {Boolean}
3845
+ */
3846
+ isOpen() {
3847
+ return this._isOpen;
3848
+ }
3849
+ /**
3850
+ * Open the FEEL popup.
3851
+ *
3852
+ * @param {String} entryId
3853
+ * @param {Object} popupConfig
3854
+ * @param {HTMLElement} sourceElement
3855
+ */
3856
+ open(entryId, popupConfig, sourceElement) {
3857
+ this.close();
3858
+ this._openPopup({
3859
+ ...popupConfig,
3860
+ entryId,
3861
+ sourceElement
3862
+ });
3863
+ }
3864
+ /**
3865
+ * Close the FEEL popup.
3866
+ */
3867
+ close() {
3868
+ this._closePopup();
3869
+ }
3870
+ _openPopup(context) {
3871
+ const { element, label, sourceElement, type } = context;
3872
+ this._isOpen = true;
3873
+ this._eventBus.fire("propertiesPanelPopup.open", {
3874
+ container: this._config.feelPopupContainer,
3875
+ config: {
3876
+ ...context,
3877
+ links: this._config.getFeelPopupLinks?.(type) || [],
3878
+ onClose: () => {
3879
+ this._closePopup();
3880
+ setTimeout(() => {
3881
+ sourceElement && sourceElement.focus();
3882
+ }, 0);
3883
+ },
3884
+ position: getPopupPosition(),
3885
+ title: getPopupTitle({
3886
+ element,
3887
+ label
3888
+ })
3889
+ }
3890
+ });
3891
+ }
3892
+ _closePopup() {
3893
+ if (this._isOpen) {
3894
+ this._isOpen = false;
3895
+ this._eventBus.fire("propertiesPanelPopup.close");
3896
+ }
3897
+ }
3898
+ };
3899
+ ExpressionPopup.$inject = ["eventBus", "config.propertiesPanel"];
3900
+ //#endregion
3901
+ //#region src/features/expression-popup/ExpressionPopupRenderer.js
3902
+ var ExpressionPopupRenderer = class {
3903
+ constructor(eventBus) {
3904
+ this._eventBus = eventBus;
3905
+ this._container = null;
3906
+ this._element = null;
3907
+ eventBus.on("propertiesPanelPopup.open", (context) => {
3908
+ this._renderPopup(context);
3909
+ });
3910
+ eventBus.on("propertiesPanelPopup.close", () => {
3911
+ this._removePopup();
3912
+ });
3913
+ }
3914
+ _renderPopup(context) {
3915
+ let { container, config } = context;
3916
+ container = this._container = getContainer(container) || document.body;
3917
+ const element = this._element = createElement$1();
3918
+ container.appendChild(element);
3919
+ this._emit("feelPopup.open");
3920
+ render(jsx(ExpressionPopup$1, {
3921
+ ...config,
3922
+ eventBus: this._eventBus
3923
+ }), element);
3924
+ this._emit("feelPopup.opened", { domNode: element });
3925
+ }
3926
+ _removePopup() {
3927
+ if (!this._container) return;
3928
+ this._emit("feelPopup.close", { domNode: this._element });
3929
+ render(null, this._element);
3930
+ this._container.removeChild(this._element);
3931
+ this._container = null;
3932
+ this._emit("feelPopup.closed");
3933
+ }
3934
+ _emit(event, context) {
3935
+ this._eventBus.fire(event, context);
3936
+ }
3937
+ };
3938
+ ExpressionPopupRenderer.$inject = ["eventBus"];
3939
+ function createElement$1() {
3940
+ const element = document.createElement("div");
3941
+ element.classList.add("bio-properties-panel-popup-container");
3942
+ return element;
3943
+ }
3944
+ function getContainer(container) {
3945
+ if (isString(container)) return query(container);
3946
+ return container;
3947
+ }
3948
+ //#endregion
3949
+ //#region src/features/expression-popup/index.js
3950
+ var expression_popup_default = {
3951
+ __init__: ["expressionPopup", "expressionPopupRenderer"],
3952
+ expressionPopup: ["type", ExpressionPopup],
3953
+ expressionPopupRenderer: ["type", ExpressionPopupRenderer]
3954
+ };
3955
+ //#endregion
3956
+ export { ArrowIcon, CheckboxEntry, CheckboxGroup, CloseIcon, CollapsibleEntry, CreateIcon, debounce_input_default as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DragIcon, DropdownButton, ErrorsContext, EventContext, expression_popup_default as ExpressionPopupModule, ExternalLinkIcon, FeelCheckboxEntry, FeelEntry, FeelIcon, FeelLanguageContext, FeelNumberEntry, FeelTemplatingEntry, FeelTextAreaEntry, FeelToggleSwitchEntry, Group, Header, HeaderButton, JsonEditorEntry, LaunchIcon, LayoutContext, LayoutContext as PropertiesPanelContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, OpenPopupIcon, Placeholder, PropertiesPanel, SelectEntry, Simple as SimpleEntry, TemplatingEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, TooltipContext, TooltipWrapper as TooltipEntry, isEdited as isCheckboxEntryEdited, isEdited$1 as isFeelEntryEdited, isEdited$2 as isJsonEditorEntryEdited, isEdited$3 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$5 as isSimpleEntryEdited, isEdited$6 as isTemplatingEntryEdited, isEdited$7 as isTextAreaEntryEdited, isEdited$8 as isTextFieldEntryEdited, isEdited$9 as isToggleSwitchEntryEdited, useDebounce, useDescriptionContext, useElementVisible, useError, useErrors, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver, useTooltipContext };
3957
+
3958
+ //# sourceMappingURL=index.esm.js.map