@bpmn-io/properties-panel 3.18.2 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useContext, useState, useRef, useEffect, useMemo, useCallback, useLayoutEffect } from '../preact/hooks';
2
- import { isFunction, isString, isArray, get, assign, set, sortBy, find, isNumber, debounce } from 'min-dash';
2
+ import { isFunction, isString, isArray, get, assign, set, isNumber, debounce } from 'min-dash';
3
3
  import { createPortal, forwardRef } from '../preact/compat';
4
4
  import { jsx, jsxs, Fragment } from '../preact/jsx-runtime';
5
5
  import { createContext, createElement } from '../preact';
@@ -974,6 +974,7 @@ const CodeEditor = forwardRef((props, ref) => {
974
974
  onFeelToggle = noop$5,
975
975
  onLint = noop$5,
976
976
  onPopupOpen = noop$5,
977
+ placeholder,
977
978
  popupOpen,
978
979
  disabled,
979
980
  tooltipContainer,
@@ -1010,6 +1011,7 @@ const CodeEditor = forwardRef((props, ref) => {
1010
1011
  onChange: handleInput,
1011
1012
  onKeyDown: onKeyDown,
1012
1013
  onLint: onLint,
1014
+ placeholder: placeholder,
1013
1015
  tooltipContainer: tooltipContainer,
1014
1016
  value: localValue,
1015
1017
  variables: variables,
@@ -1039,6 +1041,12 @@ const CodeEditor = forwardRef((props, ref) => {
1039
1041
  }
1040
1042
  editor.setVariables(variables);
1041
1043
  }, [variables]);
1044
+ useEffect(() => {
1045
+ if (!editor) {
1046
+ return;
1047
+ }
1048
+ editor.setPlaceholder(placeholder);
1049
+ }, [placeholder]);
1042
1050
  const handleClick = () => {
1043
1051
  ref.current.focus();
1044
1052
  };
@@ -1944,6 +1952,7 @@ function FeelTextfieldComponent(props) {
1944
1952
  hostLanguage,
1945
1953
  onInput,
1946
1954
  onError,
1955
+ placeholder,
1947
1956
  feel,
1948
1957
  value = '',
1949
1958
  disabled = false,
@@ -2118,6 +2127,7 @@ function FeelTextfieldComponent(props) {
2118
2127
  },
2119
2128
  onLint: handleLint,
2120
2129
  onPopupOpen: handlePopupOpen,
2130
+ placeholder: placeholder,
2121
2131
  value: feelOnlyValue,
2122
2132
  variables: variables,
2123
2133
  ref: editorRef,
@@ -2146,7 +2156,8 @@ const OptionalFeelInput = forwardRef((props, ref) => {
2146
2156
  onInput,
2147
2157
  value,
2148
2158
  onFocus,
2149
- onBlur
2159
+ onBlur,
2160
+ placeholder
2150
2161
  } = props;
2151
2162
  const inputRef = useRef();
2152
2163
 
@@ -2179,6 +2190,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
2179
2190
  onInput: e => onInput(e.target.value),
2180
2191
  onFocus: onFocus,
2181
2192
  onBlur: onBlur,
2193
+ placeholder: placeholder,
2182
2194
  value: value || ''
2183
2195
  });
2184
2196
  });
@@ -2236,7 +2248,8 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
2236
2248
  onInput,
2237
2249
  value,
2238
2250
  onFocus,
2239
- onBlur
2251
+ onBlur,
2252
+ placeholder
2240
2253
  } = props;
2241
2254
  const inputRef = useRef();
2242
2255
 
@@ -2264,6 +2277,7 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
2264
2277
  onInput: e => onInput(e.target.value),
2265
2278
  onFocus: onFocus,
2266
2279
  onBlur: onBlur,
2280
+ placeholder: placeholder,
2267
2281
  value: value || '',
2268
2282
  "data-gramm": "false"
2269
2283
  });
@@ -2359,6 +2373,7 @@ const OptionalFeelCheckbox = forwardRef((props, ref) => {
2359
2373
  * @param {Function} props.variables
2360
2374
  * @param {Function} props.onFocus
2361
2375
  * @param {Function} props.onBlur
2376
+ * @param {string} [props.placeholder]
2362
2377
  * @param {string|import('preact').Component} props.tooltip
2363
2378
  */
2364
2379
  function FeelEntry(props) {
@@ -2381,6 +2396,7 @@ function FeelEntry(props) {
2381
2396
  variables,
2382
2397
  onFocus,
2383
2398
  onBlur,
2399
+ placeholder,
2384
2400
  tooltip
2385
2401
  } = props;
2386
2402
  const [validationError, setValidationError] = useState(null);
@@ -2424,6 +2440,7 @@ function FeelEntry(props) {
2424
2440
  onError: onError,
2425
2441
  onFocus: onFocus,
2426
2442
  onBlur: onBlur,
2443
+ placeholder: placeholder,
2427
2444
  example: example,
2428
2445
  hostLanguage: hostLanguage,
2429
2446
  singleLine: singleLine,
@@ -2492,6 +2509,7 @@ function FeelNumberEntry(props) {
2492
2509
  * @param {Function} props.variables
2493
2510
  * @param {Function} props.onFocus
2494
2511
  * @param {Function} props.onBlur
2512
+ * @param {string} [props.placeholder]
2495
2513
  */
2496
2514
  function FeelTextAreaEntry(props) {
2497
2515
  return jsx(FeelEntry, {
@@ -2666,7 +2684,6 @@ const DEFAULT_TOOLTIP = {};
2666
2684
  * id: String,
2667
2685
  * items: Array<ListItemDefinition>,
2668
2686
  * label: String,
2669
- * shouldSort?: Boolean,
2670
2687
  * shouldOpen?: Boolean
2671
2688
  * } } ListGroupDefinition
2672
2689
  *
@@ -3099,6 +3116,7 @@ function ListItem(props) {
3099
3116
  } else if (isFunction(focusableInput.focus)) {
3100
3117
  focusableInput.focus();
3101
3118
  }
3119
+ focusableInput.scrollIntoView();
3102
3120
  }
3103
3121
  }
3104
3122
  }, [autoOpen, autoFocusEntry]);
@@ -3123,97 +3141,61 @@ function ListGroup(props) {
3123
3141
  id,
3124
3142
  items,
3125
3143
  label,
3126
- shouldOpen = true,
3127
- shouldSort = true
3144
+ shouldOpen = true
3128
3145
  } = props;
3146
+ useEffect(() => {
3147
+ if (props.shouldSort != undefined) {
3148
+ console.warn('the property \'shouldSort\' is no longer supported');
3149
+ }
3150
+ }, [props.shouldSort]);
3129
3151
  const groupRef = useRef(null);
3130
3152
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
3131
3153
  const [sticky, setSticky] = useState(false);
3132
3154
  const onShow = useCallback(() => setOpen(true), [setOpen]);
3133
- const [ordering, setOrdering] = useState([]);
3134
- const [newItemAdded, setNewItemAdded] = useState(false);
3155
+ const [localItems, setLocalItems] = useState([]);
3156
+ const [newlyAddedItemIds, setNewlyAddedItemIds] = useState([]);
3135
3157
 
3136
3158
  // Flag to mark that add button was clicked in the last render cycle
3137
3159
  const [addTriggered, setAddTriggered] = useState(false);
3138
- const prevItems = usePrevious(items);
3139
3160
  const prevElement = usePrevious(element);
3140
3161
  const elementChanged = element !== prevElement;
3141
- const shouldHandleEffects = !elementChanged && (shouldSort || shouldOpen);
3142
-
3143
- // reset initial ordering when element changes (before first render)
3144
- if (elementChanged) {
3145
- setOrdering(createOrdering(shouldSort ? sortItems(items) : items));
3146
- }
3147
-
3148
- // keep ordering in sync to items - and open changes
3149
-
3150
- // (0) set initial ordering from given items
3162
+ const shouldHandleEffects = !elementChanged && shouldOpen;
3163
+
3164
+ // (0) delay setting items
3165
+ //
3166
+ // We need to this to align the render cycles of items
3167
+ // with the detection of newly added items.
3168
+ // This is important, because the autoOpen property can
3169
+ // only set per list item on its very first render.
3151
3170
  useEffect(() => {
3152
- if (!prevItems || !shouldSort) {
3153
- setOrdering(createOrdering(items));
3154
- }
3155
- }, [items, element]);
3171
+ setLocalItems(items);
3172
+ }, [items]);
3156
3173
 
3157
- // (1) items were added
3174
+ // (1) handle auto opening when items were added
3158
3175
  useEffect(() => {
3159
3176
  // reset addTriggered flag
3160
3177
  setAddTriggered(false);
3161
- if (shouldHandleEffects && prevItems && items.length > prevItems.length) {
3162
- let add = [];
3163
- items.forEach(item => {
3164
- if (!ordering.includes(item.id)) {
3165
- add.push(item.id);
3178
+ if (shouldHandleEffects && localItems) {
3179
+ if (addTriggered) {
3180
+ const previousItemIds = localItems.map(item => item.id);
3181
+ const currentItemsIds = items.map(item => item.id);
3182
+ const newItemIds = currentItemsIds.filter(itemId => !previousItemIds.includes(itemId));
3183
+
3184
+ // open if not open, configured and triggered by add button
3185
+ //
3186
+ // TODO(marstamm): remove once we refactor layout handling for listGroups.
3187
+ // Ideally, opening should be handled as part of the `add` callback and
3188
+ // not be a concern for the ListGroup component.
3189
+ if (!open && shouldOpen && newItemIds.length > 0) {
3190
+ toggleOpen();
3166
3191
  }
3167
- });
3168
- let newOrdering = ordering;
3169
-
3170
- // open if not open, configured and triggered by add button
3171
- //
3172
- // TODO(marstamm): remove once we refactor layout handling for listGroups.
3173
- // Ideally, opening should be handled as part of the `add` callback and
3174
- // not be a concern for the ListGroup component.
3175
- if (addTriggered && !open && shouldOpen) {
3176
- toggleOpen();
3177
- }
3178
-
3179
- // filter when not open and configured
3180
- if (!open && shouldSort) {
3181
- newOrdering = createOrdering(sortItems(items));
3182
- }
3183
-
3184
- // add new items on top or bottom depending on sorting behavior
3185
- newOrdering = newOrdering.filter(item => !add.includes(item));
3186
- if (shouldSort) {
3187
- newOrdering.unshift(...add);
3192
+ setNewlyAddedItemIds(newItemIds);
3188
3193
  } else {
3189
- newOrdering.push(...add);
3194
+ // ignore newly added items that do not result from a triggered add
3195
+ setNewlyAddedItemIds([]);
3190
3196
  }
3191
- setOrdering(newOrdering);
3192
- setNewItemAdded(addTriggered);
3193
- } else {
3194
- setNewItemAdded(false);
3195
- }
3196
- }, [items, open, shouldHandleEffects, addTriggered]);
3197
-
3198
- // (2) sort items on open if shouldSort is set
3199
- useEffect(() => {
3200
- if (shouldSort && open && !newItemAdded) {
3201
- setOrdering(createOrdering(sortItems(items)));
3202
3197
  }
3203
- }, [open, shouldSort]);
3204
-
3205
- // (3) items were deleted
3206
- useEffect(() => {
3207
- if (shouldHandleEffects && prevItems && items.length < prevItems.length) {
3208
- let keep = [];
3209
- ordering.forEach(o => {
3210
- if (getItem(items, o)) {
3211
- keep.push(o);
3212
- }
3213
- });
3214
- setOrdering(keep);
3215
- }
3216
- }, [items, shouldHandleEffects]);
3198
+ }, [items, open, shouldHandleEffects, addTriggered, localItems]);
3217
3199
 
3218
3200
  // set css class when group is sticky to top
3219
3201
  useStickyIntersectionObserver(groupRef, 'div.bio-properties-panel-scroll-container', setSticky);
@@ -3285,8 +3267,7 @@ function ListGroup(props) {
3285
3267
  class: classnames('bio-properties-panel-list', open && hasItems ? 'open' : ''),
3286
3268
  children: jsx(LayoutContext.Provider, {
3287
3269
  value: propertiesPanelContext,
3288
- children: ordering.map((o, index) => {
3289
- const item = getItem(items, o);
3270
+ children: localItems.map((item, index) => {
3290
3271
  if (!item) {
3291
3272
  return;
3292
3273
  }
@@ -3296,7 +3277,7 @@ function ListGroup(props) {
3296
3277
 
3297
3278
  // if item was added, open it
3298
3279
  // Existing items will not be affected as autoOpen is only applied on first render
3299
- const autoOpen = newItemAdded;
3280
+ const autoOpen = newlyAddedItemIds.includes(item.id);
3300
3281
  return createElement(ListItem, {
3301
3282
  ...item,
3302
3283
  autoOpen: autoOpen,
@@ -3310,21 +3291,6 @@ function ListGroup(props) {
3310
3291
  });
3311
3292
  }
3312
3293
 
3313
- // helpers ////////////////////
3314
-
3315
- /**
3316
- * Sorts given items alphanumeric by label
3317
- */
3318
- function sortItems(items) {
3319
- return sortBy(items, i => i.label.toLowerCase());
3320
- }
3321
- function getItem(items, id) {
3322
- return find(items, i => i.id === id);
3323
- }
3324
- function createOrdering(items) {
3325
- return items.map(i => i.id);
3326
- }
3327
-
3328
3294
  function Checkbox(props) {
3329
3295
  const {
3330
3296
  id,
@@ -3616,16 +3582,12 @@ function List(props) {
3616
3582
  onAdd,
3617
3583
  onRemove,
3618
3584
  autoFocusEntry,
3619
- compareFn,
3620
3585
  ...restProps
3621
3586
  } = props;
3622
3587
  const [open, setOpen] = useState(!!shouldOpen);
3623
3588
  const hasItems = !!items.length;
3624
3589
  const toggleOpen = () => hasItems && setOpen(!open);
3625
- const opening = !usePrevious(open) && open;
3626
3590
  const elementChanged = usePrevious(element) !== element;
3627
- const shouldReset = opening || elementChanged;
3628
- const sortedItems = useSortedItems(items, compareFn, shouldReset);
3629
3591
  const newItems = useNewItems(items, elementChanged);
3630
3592
  useEffect(() => {
3631
3593
  if (open && !hasItems) {
@@ -3683,7 +3645,7 @@ function List(props) {
3683
3645
  component: component,
3684
3646
  element: element,
3685
3647
  id: id,
3686
- items: sortedItems,
3648
+ items: items,
3687
3649
  newItems: newItems,
3688
3650
  onRemove: onRemove,
3689
3651
  open: open
@@ -3747,41 +3709,6 @@ function ItemsList(props) {
3747
3709
  })
3748
3710
  });
3749
3711
  }
3750
-
3751
- /**
3752
- * Place new items in the beginning of the list and sort the rest with provided function.
3753
- *
3754
- * @template Item
3755
- * @param {Item[]} currentItems
3756
- * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
3757
- * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
3758
- * @returns {Item[]}
3759
- */
3760
- function useSortedItems(currentItems, compareFn, shouldReset = false) {
3761
- const itemsRef = useRef(currentItems.slice());
3762
-
3763
- // (1) Reset and optionally sort.
3764
- if (shouldReset) {
3765
- itemsRef.current = currentItems.slice();
3766
- if (compareFn) {
3767
- itemsRef.current.sort(compareFn);
3768
- }
3769
- } else {
3770
- const items = itemsRef.current;
3771
-
3772
- // (2) Add new item to the list.
3773
- for (const item of currentItems) {
3774
- if (!items.includes(item)) {
3775
- // Unshift or push depending on whether we have a compareFn
3776
- compareFn ? items.unshift(item) : items.push(item);
3777
- }
3778
- }
3779
-
3780
- // (3) Filter out removed items.
3781
- itemsRef.current = items.filter(item => currentItems.includes(item));
3782
- }
3783
- return itemsRef.current;
3784
- }
3785
3712
  function useNewItems(items = [], shouldReset) {
3786
3713
  const previousItems = usePrevious(items.slice()) || [];
3787
3714
  if (shouldReset) {
@@ -4017,6 +3944,7 @@ function TextArea(props) {
4017
3944
  onFocus,
4018
3945
  onBlur,
4019
3946
  autoResize,
3947
+ placeholder,
4020
3948
  rows = autoResize ? 1 : 2,
4021
3949
  tooltip
4022
3950
  } = props;
@@ -4059,6 +3987,7 @@ function TextArea(props) {
4059
3987
  onInput: handleInput,
4060
3988
  onFocus: onFocus,
4061
3989
  onBlur: onBlur,
3990
+ placeholder: placeholder,
4062
3991
  rows: rows,
4063
3992
  value: localValue,
4064
3993
  disabled: disabled,
@@ -4098,6 +4027,7 @@ function TextAreaEntry(props) {
4098
4027
  validate,
4099
4028
  onFocus,
4100
4029
  onBlur,
4030
+ placeholder,
4101
4031
  autoResize,
4102
4032
  tooltip
4103
4033
  } = props;
@@ -4133,6 +4063,7 @@ function TextAreaEntry(props) {
4133
4063
  debounce: debounce,
4134
4064
  monospace: monospace,
4135
4065
  disabled: disabled,
4066
+ placeholder: placeholder,
4136
4067
  autoResize: autoResize,
4137
4068
  tooltip: tooltip,
4138
4069
  element: element
@@ -4165,6 +4096,7 @@ function Textfield(props) {
4165
4096
  onInput,
4166
4097
  onFocus,
4167
4098
  onBlur,
4099
+ placeholder,
4168
4100
  value = '',
4169
4101
  tooltip
4170
4102
  } = props;
@@ -4206,6 +4138,7 @@ function Textfield(props) {
4206
4138
  onInput: handleInput,
4207
4139
  onFocus: onFocus,
4208
4140
  onBlur: onBlur,
4141
+ placeholder: placeholder,
4209
4142
  value: localValue
4210
4143
  })]
4211
4144
  });
@@ -4239,6 +4172,7 @@ function TextfieldEntry(props) {
4239
4172
  validate,
4240
4173
  onFocus,
4241
4174
  onBlur,
4175
+ placeholder,
4242
4176
  tooltip
4243
4177
  } = props;
4244
4178
  const globalError = useError(id);
@@ -4270,6 +4204,7 @@ function TextfieldEntry(props) {
4270
4204
  onInput: onInput,
4271
4205
  onFocus: onFocus,
4272
4206
  onBlur: onBlur,
4207
+ placeholder: placeholder,
4273
4208
  value: value,
4274
4209
  tooltip: tooltip,
4275
4210
  element: element