@bpmn-io/properties-panel 3.20.1 → 3.22.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
@@ -3048,6 +3048,28 @@ function HeaderButton(props) {
3048
3048
  });
3049
3049
  }
3050
3050
 
3051
+ /**
3052
+ * @typedef { {
3053
+ * [key: string]: string;
3054
+ * } } TranslateReplacements
3055
+ */
3056
+
3057
+ /**
3058
+ * A simple translation stub to be used for multi-language support.
3059
+ * Can be easily replaced with a more sophisticated solution.
3060
+ *
3061
+ * @param {string} template to interpolate
3062
+ * @param {TranslateReplacements} [replacements] a map with substitutes
3063
+ *
3064
+ * @return {string} the translated string
3065
+ */
3066
+ function translateFallback(template, replacements) {
3067
+ replacements = replacements || {};
3068
+ return template.replace(/{([^}]+)}/g, function (_, key) {
3069
+ return replacements[key] || '{' + key + '}';
3070
+ });
3071
+ }
3072
+
3051
3073
  function CollapsibleEntry(props) {
3052
3074
  const {
3053
3075
  element,
@@ -3055,7 +3077,8 @@ function CollapsibleEntry(props) {
3055
3077
  id,
3056
3078
  label,
3057
3079
  open: shouldOpen,
3058
- remove
3080
+ remove,
3081
+ translate = translateFallback
3059
3082
  } = props;
3060
3083
  const [open, setOpen] = useState(shouldOpen);
3061
3084
  const toggleOpen = () => setOpen(!open);
@@ -3071,9 +3094,7 @@ function CollapsibleEntry(props) {
3071
3094
  }
3072
3095
  }, [onShow, setOpen])
3073
3096
  };
3074
-
3075
- // todo(pinussilvestrus): translate once we have a translate mechanism for the core
3076
- const placeholderLabel = '<empty>';
3097
+ const placeholderLabel = translate('<empty>');
3077
3098
  return jsxs("div", {
3078
3099
  "data-entry-id": id,
3079
3100
  class: classnames('bio-properties-panel-collapsible-entry', open ? 'open' : ''),
@@ -3086,14 +3107,14 @@ function CollapsibleEntry(props) {
3086
3107
  children: label || placeholderLabel
3087
3108
  }), jsx("button", {
3088
3109
  type: "button",
3089
- title: "Toggle list item",
3110
+ title: translate('Toggle list item'),
3090
3111
  class: "bio-properties-panel-arrow bio-properties-panel-collapsible-entry-arrow",
3091
3112
  children: jsx(ArrowIcon, {
3092
3113
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
3093
3114
  })
3094
3115
  }), remove ? jsx("button", {
3095
3116
  type: "button",
3096
- title: "Delete item",
3117
+ title: translate('Delete item'),
3097
3118
  class: "bio-properties-panel-remove-entry",
3098
3119
  onClick: remove,
3099
3120
  children: jsx(DeleteIcon, {})
@@ -3121,7 +3142,8 @@ function CollapsibleEntry(props) {
3121
3142
  function ListItem(props) {
3122
3143
  const {
3123
3144
  autoFocusEntry,
3124
- autoOpen
3145
+ autoOpen,
3146
+ translate = translateFallback
3125
3147
  } = props;
3126
3148
 
3127
3149
  // focus specified entry on auto open
@@ -3143,7 +3165,8 @@ function ListItem(props) {
3143
3165
  class: "bio-properties-panel-list-item",
3144
3166
  children: jsx(CollapsibleEntry, {
3145
3167
  ...props,
3146
- open: autoOpen
3168
+ open: autoOpen,
3169
+ translate: translate
3147
3170
  })
3148
3171
  });
3149
3172
  }
@@ -3160,7 +3183,8 @@ function ListGroup(props) {
3160
3183
  id,
3161
3184
  items,
3162
3185
  label,
3163
- shouldOpen = true
3186
+ shouldOpen = false,
3187
+ translate = translateFallback
3164
3188
  } = props;
3165
3189
  useEffect(() => {
3166
3190
  if (props.shouldSort != undefined) {
@@ -3168,57 +3192,25 @@ function ListGroup(props) {
3168
3192
  }
3169
3193
  }, [props.shouldSort]);
3170
3194
  const groupRef = useRef(null);
3171
- const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
3195
+ const [open, setOpen] = useLayoutState(['groups', id, 'open'], shouldOpen);
3172
3196
  const [sticky, setSticky] = useState(false);
3173
3197
  const onShow = useCallback(() => setOpen(true), [setOpen]);
3174
3198
  const [localItems, setLocalItems] = useState([]);
3175
- const [newlyAddedItemIds, setNewlyAddedItemIds] = useState([]);
3176
3199
 
3177
3200
  // Flag to mark that add button was clicked in the last render cycle
3178
3201
  const [addTriggered, setAddTriggered] = useState(false);
3179
3202
  const prevElement = usePrevious(element);
3180
- const elementChanged = element !== prevElement;
3181
- const shouldHandleEffects = !elementChanged && shouldOpen;
3182
-
3183
- // (0) delay setting items
3184
- //
3185
- // We need to this to align the render cycles of items
3186
- // with the detection of newly added items.
3187
- // This is important, because the autoOpen property can
3188
- // only set per list item on its very first render.
3189
- useEffect(() => {
3190
- setLocalItems(items);
3191
- }, [items]);
3203
+ const toggleOpen = useCallback(() => setOpen(!open), [open]);
3204
+ const openItemIds = element === prevElement && open && addTriggered ? getNewItemIds(items, localItems) : [];
3192
3205
 
3193
- // (1) handle auto opening when items were added
3206
+ // reset local state after items changed
3194
3207
  useEffect(() => {
3195
- // reset addTriggered flag
3208
+ setLocalItems(items);
3196
3209
  setAddTriggered(false);
3197
- if (shouldHandleEffects && localItems) {
3198
- if (addTriggered) {
3199
- const previousItemIds = localItems.map(item => item.id);
3200
- const currentItemsIds = items.map(item => item.id);
3201
- const newItemIds = currentItemsIds.filter(itemId => !previousItemIds.includes(itemId));
3202
-
3203
- // open if not open, configured and triggered by add button
3204
- //
3205
- // TODO(marstamm): remove once we refactor layout handling for listGroups.
3206
- // Ideally, opening should be handled as part of the `add` callback and
3207
- // not be a concern for the ListGroup component.
3208
- if (!open && shouldOpen && newItemIds.length > 0) {
3209
- toggleOpen();
3210
- }
3211
- setNewlyAddedItemIds(newItemIds);
3212
- } else {
3213
- // ignore newly added items that do not result from a triggered add
3214
- setNewlyAddedItemIds([]);
3215
- }
3216
- }
3217
- }, [items, open, shouldHandleEffects, addTriggered, localItems]);
3210
+ }, [items]);
3218
3211
 
3219
3212
  // set css class when group is sticky to top
3220
3213
  useStickyIntersectionObserver(groupRef, 'div.bio-properties-panel-scroll-container', setSticky);
3221
- const toggleOpen = () => setOpen(!open);
3222
3214
  const hasItems = !!items.length;
3223
3215
  const propertiesPanelContext = {
3224
3216
  ...useContext(LayoutContext),
@@ -3226,6 +3218,7 @@ function ListGroup(props) {
3226
3218
  };
3227
3219
  const handleAddClick = e => {
3228
3220
  setAddTriggered(true);
3221
+ setOpen(true);
3229
3222
  add(e);
3230
3223
  };
3231
3224
  const allErrors = useErrors();
@@ -3262,20 +3255,22 @@ function ListGroup(props) {
3262
3255
  class: "bio-properties-panel-group-header-buttons",
3263
3256
  children: [add ? jsxs("button", {
3264
3257
  type: "button",
3265
- title: "Create new list item",
3258
+ title: translate('Create new list item'),
3266
3259
  class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
3267
3260
  onClick: handleAddClick,
3268
3261
  children: [jsx(CreateIcon, {}), !hasItems ? jsx("span", {
3269
3262
  class: "bio-properties-panel-add-entry-label",
3270
- children: "Create"
3263
+ children: translate('Create')
3271
3264
  }) : null]
3272
3265
  }) : null, hasItems ? jsx("div", {
3273
- title: `List contains ${items.length} item${items.length != 1 ? 's' : ''}`,
3266
+ title: translate(`List contains {numOfItems} item${items.length != 1 ? 's' : ''}`, {
3267
+ numOfItems: items.length
3268
+ }),
3274
3269
  class: classnames('bio-properties-panel-list-badge', hasError ? 'bio-properties-panel-list-badge--error' : ''),
3275
3270
  children: items.length
3276
3271
  }) : null, hasItems ? jsx("button", {
3277
3272
  type: "button",
3278
- title: "Toggle section",
3273
+ title: translate('Toggle section'),
3279
3274
  class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
3280
3275
  children: jsx(ArrowIcon, {
3281
3276
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
@@ -3286,7 +3281,7 @@ function ListGroup(props) {
3286
3281
  class: classnames('bio-properties-panel-list', open && hasItems ? 'open' : ''),
3287
3282
  children: jsx(LayoutContext.Provider, {
3288
3283
  value: propertiesPanelContext,
3289
- children: localItems.map((item, index) => {
3284
+ children: items.map((item, index) => {
3290
3285
  if (!item) {
3291
3286
  return;
3292
3287
  }
@@ -3295,20 +3290,27 @@ function ListGroup(props) {
3295
3290
  } = item;
3296
3291
 
3297
3292
  // if item was added, open it
3298
- // Existing items will not be affected as autoOpen is only applied on first render
3299
- const autoOpen = newlyAddedItemIds.includes(item.id);
3293
+ // existing items will not be affected as autoOpen
3294
+ // is only applied on first render
3295
+ const autoOpen = openItemIds.includes(item.id);
3300
3296
  return createElement(ListItem, {
3301
3297
  ...item,
3302
3298
  autoOpen: autoOpen,
3303
3299
  element: element,
3304
3300
  index: index,
3305
- key: id
3301
+ key: id,
3302
+ translate: translate
3306
3303
  });
3307
3304
  })
3308
3305
  })
3309
3306
  })]
3310
3307
  });
3311
3308
  }
3309
+ function getNewItemIds(newItems, oldItems) {
3310
+ const newIds = newItems.map(item => item.id);
3311
+ const oldIds = oldItems.map(item => item.id);
3312
+ return newIds.filter(itemId => !oldIds.includes(itemId));
3313
+ }
3312
3314
 
3313
3315
  function Checkbox(props) {
3314
3316
  const {