@bpmn-io/properties-panel 3.18.1 → 3.19.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';
@@ -967,6 +967,7 @@ const useBufferedFocus = function (editor, ref) {
967
967
  };
968
968
  const CodeEditor = forwardRef((props, ref) => {
969
969
  const {
970
+ contentAttributes,
970
971
  enableGutters,
971
972
  value,
972
973
  onInput,
@@ -1012,7 +1013,8 @@ const CodeEditor = forwardRef((props, ref) => {
1012
1013
  tooltipContainer: tooltipContainer,
1013
1014
  value: localValue,
1014
1015
  variables: variables,
1015
- extensions: [...(enableGutters ? [lineNumbers()] : []), EditorView.lineWrapping]
1016
+ extensions: [...(enableGutters ? [lineNumbers()] : []), EditorView.lineWrapping],
1017
+ contentAttributes
1016
1018
  });
1017
1019
  setEditor(editor);
1018
1020
  return () => {
@@ -2102,9 +2104,12 @@ function FeelTextfieldComponent(props) {
2102
2104
  disabled: feel !== 'optional' || disabled,
2103
2105
  onClick: handleFeelToggle
2104
2106
  }), feelActive ? jsx(CodeEditor, {
2105
- id: prefixId$5(id),
2106
2107
  name: id,
2107
2108
  onInput: handleLocalInput,
2109
+ contentAttributes: {
2110
+ 'id': prefixId$5(id),
2111
+ 'aria-label': label
2112
+ },
2108
2113
  disabled: disabled,
2109
2114
  popupOpen: popuOpen,
2110
2115
  onFeelToggle: () => {
@@ -2661,7 +2666,6 @@ const DEFAULT_TOOLTIP = {};
2661
2666
  * id: String,
2662
2667
  * items: Array<ListItemDefinition>,
2663
2668
  * label: String,
2664
- * shouldSort?: Boolean,
2665
2669
  * shouldOpen?: Boolean
2666
2670
  * } } ListGroupDefinition
2667
2671
  *
@@ -3094,6 +3098,7 @@ function ListItem(props) {
3094
3098
  } else if (isFunction(focusableInput.focus)) {
3095
3099
  focusableInput.focus();
3096
3100
  }
3101
+ focusableInput.scrollIntoView();
3097
3102
  }
3098
3103
  }
3099
3104
  }, [autoOpen, autoFocusEntry]);
@@ -3118,97 +3123,61 @@ function ListGroup(props) {
3118
3123
  id,
3119
3124
  items,
3120
3125
  label,
3121
- shouldOpen = true,
3122
- shouldSort = true
3126
+ shouldOpen = true
3123
3127
  } = props;
3128
+ useEffect(() => {
3129
+ if (props.shouldSort != undefined) {
3130
+ console.warn('the property \'shouldSort\' is no longer supported');
3131
+ }
3132
+ }, [props.shouldSort]);
3124
3133
  const groupRef = useRef(null);
3125
3134
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
3126
3135
  const [sticky, setSticky] = useState(false);
3127
3136
  const onShow = useCallback(() => setOpen(true), [setOpen]);
3128
- const [ordering, setOrdering] = useState([]);
3129
- const [newItemAdded, setNewItemAdded] = useState(false);
3137
+ const [localItems, setLocalItems] = useState([]);
3138
+ const [newlyAddedItemIds, setNewlyAddedItemIds] = useState([]);
3130
3139
 
3131
3140
  // Flag to mark that add button was clicked in the last render cycle
3132
3141
  const [addTriggered, setAddTriggered] = useState(false);
3133
- const prevItems = usePrevious(items);
3134
3142
  const prevElement = usePrevious(element);
3135
3143
  const elementChanged = element !== prevElement;
3136
- const shouldHandleEffects = !elementChanged && (shouldSort || shouldOpen);
3137
-
3138
- // reset initial ordering when element changes (before first render)
3139
- if (elementChanged) {
3140
- setOrdering(createOrdering(shouldSort ? sortItems(items) : items));
3141
- }
3142
-
3143
- // keep ordering in sync to items - and open changes
3144
-
3145
- // (0) set initial ordering from given items
3144
+ const shouldHandleEffects = !elementChanged && shouldOpen;
3145
+
3146
+ // (0) delay setting items
3147
+ //
3148
+ // We need to this to align the render cycles of items
3149
+ // with the detection of newly added items.
3150
+ // This is important, because the autoOpen property can
3151
+ // only set per list item on its very first render.
3146
3152
  useEffect(() => {
3147
- if (!prevItems || !shouldSort) {
3148
- setOrdering(createOrdering(items));
3149
- }
3150
- }, [items, element]);
3153
+ setLocalItems(items);
3154
+ }, [items]);
3151
3155
 
3152
- // (1) items were added
3156
+ // (1) handle auto opening when items were added
3153
3157
  useEffect(() => {
3154
3158
  // reset addTriggered flag
3155
3159
  setAddTriggered(false);
3156
- if (shouldHandleEffects && prevItems && items.length > prevItems.length) {
3157
- let add = [];
3158
- items.forEach(item => {
3159
- if (!ordering.includes(item.id)) {
3160
- add.push(item.id);
3160
+ if (shouldHandleEffects && localItems) {
3161
+ if (addTriggered) {
3162
+ const previousItemIds = localItems.map(item => item.id);
3163
+ const currentItemsIds = items.map(item => item.id);
3164
+ const newItemIds = currentItemsIds.filter(itemId => !previousItemIds.includes(itemId));
3165
+
3166
+ // open if not open, configured and triggered by add button
3167
+ //
3168
+ // TODO(marstamm): remove once we refactor layout handling for listGroups.
3169
+ // Ideally, opening should be handled as part of the `add` callback and
3170
+ // not be a concern for the ListGroup component.
3171
+ if (!open && shouldOpen && newItemIds.length > 0) {
3172
+ toggleOpen();
3161
3173
  }
3162
- });
3163
- let newOrdering = ordering;
3164
-
3165
- // open if not open, configured and triggered by add button
3166
- //
3167
- // TODO(marstamm): remove once we refactor layout handling for listGroups.
3168
- // Ideally, opening should be handled as part of the `add` callback and
3169
- // not be a concern for the ListGroup component.
3170
- if (addTriggered && !open && shouldOpen) {
3171
- toggleOpen();
3172
- }
3173
-
3174
- // filter when not open and configured
3175
- if (!open && shouldSort) {
3176
- newOrdering = createOrdering(sortItems(items));
3177
- }
3178
-
3179
- // add new items on top or bottom depending on sorting behavior
3180
- newOrdering = newOrdering.filter(item => !add.includes(item));
3181
- if (shouldSort) {
3182
- newOrdering.unshift(...add);
3174
+ setNewlyAddedItemIds(newItemIds);
3183
3175
  } else {
3184
- newOrdering.push(...add);
3176
+ // ignore newly added items that do not result from a triggered add
3177
+ setNewlyAddedItemIds([]);
3185
3178
  }
3186
- setOrdering(newOrdering);
3187
- setNewItemAdded(addTriggered);
3188
- } else {
3189
- setNewItemAdded(false);
3190
- }
3191
- }, [items, open, shouldHandleEffects, addTriggered]);
3192
-
3193
- // (2) sort items on open if shouldSort is set
3194
- useEffect(() => {
3195
- if (shouldSort && open && !newItemAdded) {
3196
- setOrdering(createOrdering(sortItems(items)));
3197
- }
3198
- }, [open, shouldSort]);
3199
-
3200
- // (3) items were deleted
3201
- useEffect(() => {
3202
- if (shouldHandleEffects && prevItems && items.length < prevItems.length) {
3203
- let keep = [];
3204
- ordering.forEach(o => {
3205
- if (getItem(items, o)) {
3206
- keep.push(o);
3207
- }
3208
- });
3209
- setOrdering(keep);
3210
3179
  }
3211
- }, [items, shouldHandleEffects]);
3180
+ }, [items, open, shouldHandleEffects, addTriggered, localItems]);
3212
3181
 
3213
3182
  // set css class when group is sticky to top
3214
3183
  useStickyIntersectionObserver(groupRef, 'div.bio-properties-panel-scroll-container', setSticky);
@@ -3280,8 +3249,7 @@ function ListGroup(props) {
3280
3249
  class: classnames('bio-properties-panel-list', open && hasItems ? 'open' : ''),
3281
3250
  children: jsx(LayoutContext.Provider, {
3282
3251
  value: propertiesPanelContext,
3283
- children: ordering.map((o, index) => {
3284
- const item = getItem(items, o);
3252
+ children: localItems.map((item, index) => {
3285
3253
  if (!item) {
3286
3254
  return;
3287
3255
  }
@@ -3291,7 +3259,7 @@ function ListGroup(props) {
3291
3259
 
3292
3260
  // if item was added, open it
3293
3261
  // Existing items will not be affected as autoOpen is only applied on first render
3294
- const autoOpen = newItemAdded;
3262
+ const autoOpen = newlyAddedItemIds.includes(item.id);
3295
3263
  return createElement(ListItem, {
3296
3264
  ...item,
3297
3265
  autoOpen: autoOpen,
@@ -3305,21 +3273,6 @@ function ListGroup(props) {
3305
3273
  });
3306
3274
  }
3307
3275
 
3308
- // helpers ////////////////////
3309
-
3310
- /**
3311
- * Sorts given items alphanumeric by label
3312
- */
3313
- function sortItems(items) {
3314
- return sortBy(items, i => i.label.toLowerCase());
3315
- }
3316
- function getItem(items, id) {
3317
- return find(items, i => i.id === id);
3318
- }
3319
- function createOrdering(items) {
3320
- return items.map(i => i.id);
3321
- }
3322
-
3323
3276
  function Checkbox(props) {
3324
3277
  const {
3325
3278
  id,
@@ -3611,16 +3564,12 @@ function List(props) {
3611
3564
  onAdd,
3612
3565
  onRemove,
3613
3566
  autoFocusEntry,
3614
- compareFn,
3615
3567
  ...restProps
3616
3568
  } = props;
3617
3569
  const [open, setOpen] = useState(!!shouldOpen);
3618
3570
  const hasItems = !!items.length;
3619
3571
  const toggleOpen = () => hasItems && setOpen(!open);
3620
- const opening = !usePrevious(open) && open;
3621
3572
  const elementChanged = usePrevious(element) !== element;
3622
- const shouldReset = opening || elementChanged;
3623
- const sortedItems = useSortedItems(items, compareFn, shouldReset);
3624
3573
  const newItems = useNewItems(items, elementChanged);
3625
3574
  useEffect(() => {
3626
3575
  if (open && !hasItems) {
@@ -3678,7 +3627,7 @@ function List(props) {
3678
3627
  component: component,
3679
3628
  element: element,
3680
3629
  id: id,
3681
- items: sortedItems,
3630
+ items: items,
3682
3631
  newItems: newItems,
3683
3632
  onRemove: onRemove,
3684
3633
  open: open
@@ -3742,41 +3691,6 @@ function ItemsList(props) {
3742
3691
  })
3743
3692
  });
3744
3693
  }
3745
-
3746
- /**
3747
- * Place new items in the beginning of the list and sort the rest with provided function.
3748
- *
3749
- * @template Item
3750
- * @param {Item[]} currentItems
3751
- * @param {(a: Item, b: Item) => 0 | 1 | -1} [compareFn] function used to sort items
3752
- * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
3753
- * @returns {Item[]}
3754
- */
3755
- function useSortedItems(currentItems, compareFn, shouldReset = false) {
3756
- const itemsRef = useRef(currentItems.slice());
3757
-
3758
- // (1) Reset and optionally sort.
3759
- if (shouldReset) {
3760
- itemsRef.current = currentItems.slice();
3761
- if (compareFn) {
3762
- itemsRef.current.sort(compareFn);
3763
- }
3764
- } else {
3765
- const items = itemsRef.current;
3766
-
3767
- // (2) Add new item to the list.
3768
- for (const item of currentItems) {
3769
- if (!items.includes(item)) {
3770
- // Unshift or push depending on whether we have a compareFn
3771
- compareFn ? items.unshift(item) : items.push(item);
3772
- }
3773
- }
3774
-
3775
- // (3) Filter out removed items.
3776
- itemsRef.current = items.filter(item => currentItems.includes(item));
3777
- }
3778
- return itemsRef.current;
3779
- }
3780
3694
  function useNewItems(items = [], shouldReset) {
3781
3695
  const previousItems = usePrevious(items.slice()) || [];
3782
3696
  if (shouldReset) {