@bpmn-io/properties-panel 0.15.0 → 0.16.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/CHANGELOG.md CHANGED
@@ -6,10 +6,22 @@ All notable changes to [`@bpmn-io/properties-panel`](https://github.com/bpmn-io/
6
6
 
7
7
  ___Note:__ Yet to be released changes appear here._
8
8
 
9
+ ## 0.16.0
10
+
11
+ * `FEAT`: set errors through context ([#160](https://github.com/bpmn-io/properties-panel/pull/160))
12
+ * `FEAT`: useShowEntryEvent hook uses ID ([#160](https://github.com/bpmn-io/properties-panel/pull/160))
13
+ * `FEAT`: useEvent hook subscribes immediately ([#160](https://github.com/bpmn-io/properties-panel/pull/160))
14
+ * `FEAT`: add focus and error to text area ([#160](https://github.com/bpmn-io/properties-panel/pull/160))
15
+
16
+ ### BREAKING CHANGES
17
+
18
+ * useShowEntryEvent hook uses ID instead of callback
19
+ * useShowErrorEvent hook removed, add errors through context instead
20
+
9
21
  ## 0.15.0
10
22
 
11
23
  * `FEAT`: pass props to `List` entries ([#157](https://github.com/bpmn-io/properties-panel/pull/157))
12
- * `FIX`: correct curser jumping on update ([#146](https://github.com/bpmn-io/properties-panel/issues/146))
24
+ * `FIX`: correct cursor jumping on update ([#146](https://github.com/bpmn-io/properties-panel/issues/146))
13
25
  * `CHORE`: use controlled inputs ([#155](https://github.com/bpmn-io/properties-panel/issues/155))
14
26
 
15
27
  ## 0.14.0
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useContext, useEffect, useRef, useMemo, useState, useCallback } from '../preact/hooks';
1
+ import { useContext, useRef, useEffect, useMemo, useState, useCallback } from '../preact/hooks';
2
2
  import { isFunction, isArray, get, assign, set, sortBy, find, isNumber, debounce } from 'min-dash';
3
3
  import classnames from 'classnames';
4
4
  import '../preact/compat';
@@ -166,6 +166,10 @@ const DescriptionContext = createContext({
166
166
  getDescriptionForId: () => {}
167
167
  });
168
168
 
169
+ const ErrorsContext = createContext({
170
+ errors: {}
171
+ });
172
+
169
173
  /**
170
174
  * @typedef {Function} <propertiesPanel.showEntry> callback
171
175
  *
@@ -214,29 +218,48 @@ function useDescriptionContext(id, element) {
214
218
  return getDescriptionForId(id, element);
215
219
  }
216
220
 
217
- const DEFAULT_PRIORITY = 1000;
221
+ function useError(id) {
222
+ const {
223
+ errors
224
+ } = useContext(ErrorsContext);
225
+ return errors[id];
226
+ }
227
+
218
228
  /**
219
- * Subscribe to an event.
229
+ * Subscribe to an event immediately. Update subscription after inputs changed.
220
230
  *
221
231
  * @param {string} event
222
232
  * @param {Function} callback
223
- * @param {number} [priority]
224
- *
225
- * @returns {import('preact').Ref}
226
233
  */
227
234
 
228
- function useEvent(event, callback, priority = DEFAULT_PRIORITY) {
229
- const {
230
- eventBus
231
- } = useContext(EventContext);
235
+ function useEvent(event, callback, eventBus) {
236
+ const eventContext = useContext(EventContext);
237
+
238
+ if (!eventBus) {
239
+ ({
240
+ eventBus
241
+ } = eventContext);
242
+ }
243
+
244
+ const didMount = useRef(false); // (1) subscribe immediately
245
+
246
+ if (eventBus && !didMount.current) {
247
+ eventBus.on(event, callback);
248
+ } // (2) update subscription after inputs changed
249
+
250
+
232
251
  useEffect(() => {
233
- if (!eventBus) {
234
- return;
252
+ if (eventBus && didMount.current) {
253
+ eventBus.on(event, callback);
235
254
  }
236
255
 
237
- eventBus.on(event, priority, callback);
238
- return () => eventBus.off(event, callback);
239
- }, [callback, event, eventBus, priority]);
256
+ didMount.current = true;
257
+ return () => {
258
+ if (eventBus) {
259
+ eventBus.off(event, callback);
260
+ }
261
+ };
262
+ }, [callback, event, eventBus]);
240
263
  }
241
264
 
242
265
  const HIGH_PRIORITY = 10000;
@@ -382,30 +405,28 @@ function usePrevious(value) {
382
405
  /**
383
406
  * Subscribe to `propertiesPanel.showEntry`.
384
407
  *
385
- * @param {Function} show
408
+ * @param {string} id
386
409
  *
387
410
  * @returns {import('preact').Ref}
388
411
  */
389
412
 
390
- function useShowEntryEvent(show) {
413
+ function useShowEntryEvent(id) {
391
414
  const {
392
415
  onShow
393
416
  } = useContext(LayoutContext);
394
417
  const ref = useRef();
395
- const [focus, setFocus] = useState(false);
418
+ const focus = useRef(false);
396
419
  const onShowEntry = useCallback(event => {
397
- if (show(event)) {
398
- if (isFunction(onShow)) {
399
- onShow();
400
- }
420
+ if (event.id === id) {
421
+ onShow();
401
422
 
402
- if (event.focus && !focus) {
403
- setFocus(true);
423
+ if (!focus.current) {
424
+ focus.current = true;
404
425
  }
405
426
  }
406
- }, [show]);
427
+ }, [id]);
407
428
  useEffect(() => {
408
- if (focus && ref.current) {
429
+ if (focus.current && ref.current) {
409
430
  if (isFunction(ref.current.focus)) {
410
431
  ref.current.focus();
411
432
  }
@@ -414,45 +435,13 @@ function useShowEntryEvent(show) {
414
435
  ref.current.select();
415
436
  }
416
437
 
417
- setFocus(false);
438
+ focus.current = false;
418
439
  }
419
- }, [focus]);
440
+ });
420
441
  useEvent('propertiesPanel.showEntry', onShowEntry);
421
442
  return ref;
422
443
  }
423
444
 
424
- /**
425
- * Subscribe to `propertiesPanel.showError`. On `propertiesPanel.showError` set
426
- * temporary error. Fire `propertiesPanel.showEntry` for temporary error to be
427
- * visible. Unset error on `propertiesPanel.updated`.
428
- *
429
- * @param {Function} show
430
- *
431
- * @returns {import('preact').Ref}
432
- */
433
-
434
- function useShowErrorEvent(show) {
435
- const {
436
- eventBus
437
- } = useContext(EventContext);
438
- const [temporaryError, setTemporaryError] = useState(null);
439
- const onPropertiesPanelUpdated = useCallback(() => setTemporaryError(null), []);
440
- useEvent('propertiesPanel.updated', onPropertiesPanelUpdated);
441
- const onShowError = useCallback(event => {
442
- setTemporaryError(null);
443
-
444
- if (show(event)) {
445
- if (eventBus) {
446
- eventBus.fire('propertiesPanel.showEntry', event);
447
- }
448
-
449
- setTemporaryError(event.message);
450
- }
451
- }, [show]);
452
- useEvent('propertiesPanel.showError', onShowError);
453
- return temporaryError;
454
- }
455
-
456
445
  /**
457
446
  * @callback setSticky
458
447
  * @param {boolean} value
@@ -621,7 +610,7 @@ const DEFAULT_LAYOUT = {
621
610
  open: true
622
611
  };
623
612
  const DEFAULT_DESCRIPTION = {};
624
- const bufferedEvents = ['propertiesPanel.showEntry', 'propertiesPanel.showError'];
613
+ const bufferedEvents = ['propertiesPanel.showEntry', 'propertiesPanel.setErrors'];
625
614
  /**
626
615
  * @typedef { {
627
616
  * component: import('preact').Component,
@@ -742,6 +731,16 @@ function PropertiesPanel(props) {
742
731
  getDescriptionForId
743
732
  };
744
733
  useEventBuffer(bufferedEvents, eventBus);
734
+ const [errors, setErrors] = useState({});
735
+
736
+ const onSetErrors = ({
737
+ errors
738
+ }) => setErrors(errors);
739
+
740
+ useEvent('propertiesPanel.setErrors', onSetErrors, eventBus);
741
+ const errorsContext = {
742
+ errors
743
+ };
745
744
  const eventContext = {
746
745
  eventBus
747
746
  };
@@ -762,30 +761,33 @@ function PropertiesPanel(props) {
762
761
 
763
762
  return jsx(LayoutContext.Provider, {
764
763
  value: propertiesPanelContext,
765
- children: jsx(DescriptionContext.Provider, {
766
- value: descriptionContext,
767
- children: jsx(LayoutContext.Provider, {
768
- value: layoutContext,
769
- children: jsx(EventContext.Provider, {
770
- value: eventContext,
771
- children: jsxs("div", {
772
- class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
773
- children: [jsx(Header, {
774
- element: element,
775
- headerProvider: headerProvider
776
- }), jsx("div", {
777
- class: "bio-properties-panel-scroll-container",
778
- children: groups.map(group => {
779
- const {
780
- component: Component = Group,
781
- id
782
- } = group;
783
- return createElement(Component, { ...group,
784
- key: id,
785
- element: element
786
- });
787
- })
788
- })]
764
+ children: jsx(ErrorsContext.Provider, {
765
+ value: errorsContext,
766
+ children: jsx(DescriptionContext.Provider, {
767
+ value: descriptionContext,
768
+ children: jsx(LayoutContext.Provider, {
769
+ value: layoutContext,
770
+ children: jsx(EventContext.Provider, {
771
+ value: eventContext,
772
+ children: jsxs("div", {
773
+ class: classnames('bio-properties-panel', layout.open ? 'open' : ''),
774
+ children: [jsx(Header, {
775
+ element: element,
776
+ headerProvider: headerProvider
777
+ }), jsx("div", {
778
+ class: "bio-properties-panel-scroll-container",
779
+ children: groups.map(group => {
780
+ const {
781
+ component: Component = Group,
782
+ id
783
+ } = group;
784
+ return createElement(Component, { ...group,
785
+ key: id,
786
+ element: element
787
+ });
788
+ })
789
+ })]
790
+ })
789
791
  })
790
792
  })
791
793
  })
@@ -1012,7 +1014,7 @@ function ListItem(props) {
1012
1014
  });
1013
1015
  }
1014
1016
 
1015
- const noop$3 = () => {};
1017
+ const noop = () => {};
1016
1018
  /**
1017
1019
  * @param {import('../PropertiesPanel').ListGroupDefinition} props
1018
1020
  */
@@ -1117,7 +1119,7 @@ function ListGroup(props) {
1117
1119
  ref: groupRef,
1118
1120
  children: [jsxs("div", {
1119
1121
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
1120
- onClick: hasItems ? toggleOpen : noop$3,
1122
+ onClick: hasItems ? toggleOpen : noop,
1121
1123
  children: [jsx("div", {
1122
1124
  title: label,
1123
1125
  class: "bio-properties-panel-group-header-title",
@@ -1205,16 +1207,13 @@ function Description(props) {
1205
1207
  }
1206
1208
  }
1207
1209
 
1208
- const noop$2 = () => {};
1209
-
1210
1210
  function Checkbox(props) {
1211
1211
  const {
1212
1212
  id,
1213
1213
  label,
1214
1214
  onChange,
1215
1215
  disabled,
1216
- value = false,
1217
- show = noop$2
1216
+ value = false
1218
1217
  } = props;
1219
1218
  const [localValue, setLocalValue] = useState(value);
1220
1219
 
@@ -1236,7 +1235,7 @@ function Checkbox(props) {
1236
1235
 
1237
1236
  setLocalValue(value);
1238
1237
  }, [value]);
1239
- const ref = useShowEntryEvent(show);
1238
+ const ref = useShowEntryEvent(id);
1240
1239
  return jsxs("div", {
1241
1240
  class: "bio-properties-panel-checkbox",
1242
1241
  children: [jsx("input", {
@@ -1275,11 +1274,10 @@ function CheckboxEntry(props) {
1275
1274
  label,
1276
1275
  getValue,
1277
1276
  setValue,
1278
- disabled,
1279
- show = noop$2
1277
+ disabled
1280
1278
  } = props;
1281
1279
  const value = getValue(element);
1282
- const error = useShowErrorEvent(show);
1280
+ const error = useError(id);
1283
1281
  return jsxs("div", {
1284
1282
  class: "bio-properties-panel-entry bio-properties-panel-checkbox-entry",
1285
1283
  "data-entry-id": id,
@@ -1288,7 +1286,6 @@ function CheckboxEntry(props) {
1288
1286
  id: id,
1289
1287
  label: label,
1290
1288
  onChange: setValue,
1291
- show: show,
1292
1289
  value: value
1293
1290
  }), error && jsx("div", {
1294
1291
  class: "bio-properties-panel-error",
@@ -1615,25 +1612,6 @@ function prefixId$5(id) {
1615
1612
  return `bio-properties-panel-${id}`;
1616
1613
  }
1617
1614
 
1618
- const noop$1 = () => {};
1619
- /**
1620
- * @typedef { { value: string, label: string, disabled: boolean } } Option
1621
- */
1622
-
1623
- /**
1624
- * Provides basic select input.
1625
- *
1626
- * @param {object} props
1627
- * @param {string} props.id
1628
- * @param {string[]} props.path
1629
- * @param {string} props.label
1630
- * @param {Function} props.onChange
1631
- * @param {Array<Option>} [props.options]
1632
- * @param {string} props.value
1633
- * @param {boolean} [props.disabled]
1634
- */
1635
-
1636
-
1637
1615
  function Select(props) {
1638
1616
  const {
1639
1617
  id,
@@ -1641,10 +1619,9 @@ function Select(props) {
1641
1619
  onChange,
1642
1620
  options = [],
1643
1621
  value,
1644
- disabled,
1645
- show = noop$1
1622
+ disabled
1646
1623
  } = props;
1647
- const ref = useShowEntryEvent(show);
1624
+ const ref = useShowEntryEvent(id);
1648
1625
  const [localValue, setLocalValue] = useState(value);
1649
1626
 
1650
1627
  const handleChangeCallback = ({
@@ -1711,12 +1688,11 @@ function SelectEntry(props) {
1711
1688
  getValue,
1712
1689
  setValue,
1713
1690
  getOptions,
1714
- disabled,
1715
- show = noop$1
1691
+ disabled
1716
1692
  } = props;
1717
1693
  const value = getValue(element);
1718
1694
  const options = getOptions(element);
1719
- const error = useShowErrorEvent(show);
1695
+ const error = useError(id);
1720
1696
  return jsxs("div", {
1721
1697
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
1722
1698
  "data-entry-id": id,
@@ -1726,8 +1702,7 @@ function SelectEntry(props) {
1726
1702
  value: value,
1727
1703
  onChange: setValue,
1728
1704
  options: options,
1729
- disabled: disabled,
1730
- show: show
1705
+ disabled: disabled
1731
1706
  }), error && jsx("div", {
1732
1707
  class: "bio-properties-panel-error",
1733
1708
  children: error
@@ -1830,6 +1805,7 @@ function TextArea(props) {
1830
1805
  monospace
1831
1806
  } = props;
1832
1807
  const [localValue, setLocalValue] = useState(value);
1808
+ const ref = useShowEntryEvent(id);
1833
1809
  const handleInputCallback = useMemo(() => {
1834
1810
  return debounce(({
1835
1811
  target
@@ -1858,6 +1834,7 @@ function TextArea(props) {
1858
1834
  label: label
1859
1835
  })]
1860
1836
  }), jsx("textarea", {
1837
+ ref: ref,
1861
1838
  id: prefixId$2(id),
1862
1839
  name: id,
1863
1840
  spellCheck: "false",
@@ -1901,6 +1878,7 @@ function TextAreaEntry(props) {
1901
1878
  disabled
1902
1879
  } = props;
1903
1880
  const value = getValue(element);
1881
+ const error = useError(id);
1904
1882
  return jsxs("div", {
1905
1883
  class: "bio-properties-panel-entry",
1906
1884
  "data-entry-id": id,
@@ -1914,6 +1892,9 @@ function TextAreaEntry(props) {
1914
1892
  monospace: monospace,
1915
1893
  feel: feel,
1916
1894
  disabled: disabled
1895
+ }), error && jsx("div", {
1896
+ class: "bio-properties-panel-error",
1897
+ children: error
1917
1898
  }), jsx(Description, {
1918
1899
  forId: id,
1919
1900
  element: element,
@@ -1929,8 +1910,6 @@ function prefixId$2(id) {
1929
1910
  return `bio-properties-panel-${id}`;
1930
1911
  }
1931
1912
 
1932
- const noop = () => {};
1933
-
1934
1913
  function Textfield(props) {
1935
1914
  const {
1936
1915
  debounce,
@@ -1939,11 +1918,10 @@ function Textfield(props) {
1939
1918
  label,
1940
1919
  onInput,
1941
1920
  feel = false,
1942
- value = '',
1943
- show = noop
1921
+ value = ''
1944
1922
  } = props;
1945
1923
  const [localValue, setLocalValue] = useState(value || '');
1946
- const ref = useShowEntryEvent(show);
1924
+ const ref = useShowEntryEvent(id);
1947
1925
  const handleInputCallback = useMemo(() => {
1948
1926
  return debounce(({
1949
1927
  target
@@ -2012,17 +1990,17 @@ function TextfieldEntry(props) {
2012
1990
  label,
2013
1991
  getValue,
2014
1992
  setValue,
2015
- validate,
2016
- show = noop
1993
+ validate
2017
1994
  } = props;
2018
1995
  const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
2019
- const [validationError, setValidationError] = useState(null);
1996
+ const globalError = useError(id);
1997
+ const [localError, setLocalError] = useState(null);
2020
1998
  let value = getValue(element);
2021
1999
  const previousValue = usePrevious(value);
2022
2000
  useEffect(() => {
2023
2001
  if (isFunction(validate)) {
2024
2002
  const newValidationError = validate(value) || null;
2025
- setValidationError(newValidationError);
2003
+ setLocalError(newValidationError);
2026
2004
  }
2027
2005
  }, [value]);
2028
2006
 
@@ -2039,15 +2017,14 @@ function TextfieldEntry(props) {
2039
2017
  setValue(newValue);
2040
2018
  }
2041
2019
 
2042
- setValidationError(newValidationError);
2020
+ setLocalError(newValidationError);
2043
2021
  };
2044
2022
 
2045
- if (previousValue === value && validationError) {
2023
+ if (previousValue === value && localError) {
2046
2024
  value = cachedInvalidValue;
2047
2025
  }
2048
2026
 
2049
- const temporaryError = useShowErrorEvent(show);
2050
- const error = temporaryError || validationError;
2027
+ const error = globalError || localError;
2051
2028
  return jsxs("div", {
2052
2029
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
2053
2030
  "data-entry-id": id,
@@ -2058,7 +2035,6 @@ function TextfieldEntry(props) {
2058
2035
  id: id,
2059
2036
  label: label,
2060
2037
  onInput: onInput,
2061
- show: show,
2062
2038
  value: value
2063
2039
  }), error && jsx("div", {
2064
2040
  class: "bio-properties-panel-error",
@@ -2195,5 +2171,5 @@ var index = {
2195
2171
  debounceInput: ['factory', debounceInput]
2196
2172
  };
2197
2173
 
2198
- export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, EventContext, ExternalLinkIcon, FeelOptionalIcon, FeelRequiredIcon, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$6 as isCheckboxEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useEvent, useEventBuffer, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useShowErrorEvent, useStickyIntersectionObserver };
2174
+ export { ArrowIcon, CheckboxEntry, CollapsibleEntry, CreateIcon, index as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelOptionalIcon, FeelRequiredIcon, Group, Header, HeaderButton, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, isEdited$6 as isCheckboxEntryEdited, isEdited$5 as isNumberFieldEntryEdited, isEdited$4 as isSelectEntryEdited, isEdited$3 as isSimpleEntryEdited, isEdited$2 as isTextAreaEntryEdited, isEdited$1 as isTextFieldEntryEdited, isEdited as isToggleSwitchEntryEdited, useDescriptionContext, useError, useEvent, useEventBuffer, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStickyIntersectionObserver };
2199
2175
  //# sourceMappingURL=index.esm.js.map