@bpmn-io/properties-panel 3.28.2 → 3.29.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.
@@ -819,13 +819,20 @@ textarea.bio-properties-panel-input {
819
819
  }
820
820
 
821
821
  .bio-properties-panel-list-entry-header {
822
- position: relative;
822
+ position: sticky;
823
823
  overflow: hidden;
824
824
  display: flex;
825
825
  justify-content: space-between;
826
826
  height: 32px;
827
827
  }
828
828
 
829
+ .bio-properties-panel-list-entry-header.sticky {
830
+ background-color: var(--color-white);
831
+ border-bottom: 1px solid var(--sticky-group-bottom-border-color);
832
+ top: 32px;
833
+ z-index: 9;
834
+ }
835
+
829
836
  /* Nested list dot */
830
837
  .bio-properties-panel-list-entry::before {
831
838
  content: "";
package/dist/index.esm.js CHANGED
@@ -2143,6 +2143,50 @@ function prefixId$6(id) {
2143
2143
  return `bio-properties-panel-${id}`;
2144
2144
  }
2145
2145
 
2146
+ function TextInput({
2147
+ debounce,
2148
+ element,
2149
+ id,
2150
+ getValue,
2151
+ onBlur,
2152
+ setValue,
2153
+ validate,
2154
+ Component,
2155
+ ...props
2156
+ }) {
2157
+ const modelValue = getValue(element);
2158
+ const setModelValue = useCallback((newValue, error) => {
2159
+ if (isFunction(validate)) {
2160
+ error = validate(newValue) || null;
2161
+ }
2162
+ setValue(newValue, error);
2163
+ }, [setValue, validate]);
2164
+ const debouncedSetValue = useDebounce(setModelValue, debounce);
2165
+ const handleInput = useCallback(newValue => {
2166
+ if (newValue !== modelValue) {
2167
+ debouncedSetValue(newValue);
2168
+ }
2169
+ }, [modelValue, debouncedSetValue]);
2170
+ const handleBlur = useCallback(value => {
2171
+ const newValue = value.trim?.() || value;
2172
+ if (newValue !== modelValue) {
2173
+ setModelValue(newValue);
2174
+ }
2175
+ if (isFunction(onBlur)) {
2176
+ onBlur(newValue);
2177
+ }
2178
+ }, [modelValue, setModelValue]);
2179
+ return jsx(Component, {
2180
+ ...props,
2181
+ debounce: debounce,
2182
+ element: element,
2183
+ id: id,
2184
+ onInput: handleInput,
2185
+ onBlur: handleBlur,
2186
+ value: modelValue
2187
+ });
2188
+ }
2189
+
2146
2190
  const noop$2 = () => {};
2147
2191
 
2148
2192
  /**
@@ -2171,7 +2215,6 @@ const noop$2 = () => {};
2171
2215
  */
2172
2216
  function FeelTextfield(props) {
2173
2217
  const {
2174
- debounce,
2175
2218
  id,
2176
2219
  element,
2177
2220
  label,
@@ -2206,16 +2249,11 @@ function FeelTextfield(props) {
2206
2249
  const position = hasFocus ? document.activeElement.selectionStart : Infinity;
2207
2250
  _setFocus(position + offset);
2208
2251
  };
2209
-
2210
- /**
2211
- * @type { import('min-dash').DebouncedFunction }
2212
- */
2213
- const handleInputCallback = useDebounce(onInput, debounce);
2214
2252
  const handleInput = newValue => {
2215
2253
  // we don't commit empty FEEL expressions,
2216
2254
  // but instead serialize them as <undefined>
2217
2255
  const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
2218
- handleInputCallback(newModelValue);
2256
+ onInput(newModelValue);
2219
2257
  };
2220
2258
  const handleFeelToggle = useStaticCallback(() => {
2221
2259
  if (feel === 'required') {
@@ -2243,14 +2281,8 @@ function FeelTextfield(props) {
2243
2281
  setFocus(-1);
2244
2282
  }
2245
2283
  };
2246
- const handleOnBlur = e => {
2247
- const trimmedValue = e.target.value.trim();
2248
-
2249
- // trim and commit on blur
2250
- onInput(trimmedValue);
2251
- if (onBlur) {
2252
- onBlur(e);
2253
- }
2284
+ const handleOnBlur = () => {
2285
+ onBlur?.(localValue);
2254
2286
  };
2255
2287
  const handleLint = useStaticCallback((lint = []) => {
2256
2288
  const syntaxError = lint.some(report => report.type === 'Syntax Error');
@@ -2647,46 +2679,28 @@ function FeelEntry(props) {
2647
2679
  placeholder,
2648
2680
  tooltip
2649
2681
  } = props;
2650
- const [validationError, setValidationError] = useState(null);
2651
2682
  const [localError, setLocalError] = useState(null);
2652
- let value = getValue(element);
2653
- useEffect(() => {
2654
- if (isFunction(validate)) {
2655
- const newValidationError = validate(value) || null;
2656
- setValidationError(newValidationError);
2657
- }
2658
- }, [value, validate]);
2659
- const onInput = useCallback(newValue => {
2660
- const value = getValue(element);
2661
- let newValidationError = null;
2662
- if (isFunction(validate)) {
2663
- newValidationError = validate(newValue) || null;
2664
- }
2665
-
2666
- // don't create multiple commandStack entries for the same value
2667
- if (newValue !== value) {
2668
- setValue(newValue, newValidationError);
2669
- }
2670
- setValidationError(newValidationError);
2671
- }, [element, getValue, setValue, validate]);
2672
- const onError = useCallback(err => {
2673
- setLocalError(err);
2674
- }, []);
2683
+ const value = getValue(element);
2684
+ const validationError = validate?.(value) || null;
2675
2685
  const temporaryError = useError(id);
2676
2686
  const error = temporaryError || localError || validationError;
2677
2687
  return jsxs("div", {
2678
2688
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
2679
2689
  "data-entry-id": id,
2680
- children: [createElement$1(FeelTextfield, {
2690
+ children: [createElement$1(TextInput, {
2681
2691
  ...props,
2692
+ Component: FeelTextfield,
2693
+ element: element,
2694
+ getValue: getValue,
2682
2695
  debounce: debounce,
2696
+ setValue: setValue,
2697
+ validate: validate,
2683
2698
  disabled: disabled,
2684
2699
  feel: feel,
2685
2700
  id: id,
2686
2701
  key: element,
2687
2702
  label: label,
2688
- onInput: onInput,
2689
- onError: onError,
2703
+ onError: setLocalError,
2690
2704
  onFocus: onFocus,
2691
2705
  onBlur: onBlur,
2692
2706
  placeholder: placeholder,
@@ -3044,7 +3058,9 @@ function List(props) {
3044
3058
  autoFocusEntry,
3045
3059
  ...restProps
3046
3060
  } = props;
3061
+ const entryRef = useRef(null);
3047
3062
  const [open, setOpen] = useState(!!shouldOpen);
3063
+ const [sticky, setSticky] = useState(false);
3048
3064
  const hasItems = !!items.length;
3049
3065
  const toggleOpen = () => hasItems && setOpen(!open);
3050
3066
  const elementChanged = usePrevious(element) !== element;
@@ -3065,11 +3081,15 @@ function List(props) {
3065
3081
  setOpen(true);
3066
3082
  }
3067
3083
  }
3084
+
3085
+ // set css class when entry is sticky to top
3086
+ useStickyIntersectionObserver(entryRef, 'div.bio-properties-panel-scroll-container', setSticky);
3068
3087
  return jsxs("div", {
3069
3088
  "data-entry-id": id,
3070
3089
  class: classnames('bio-properties-panel-entry', 'bio-properties-panel-list-entry', hasItems ? '' : 'empty', open ? 'open' : ''),
3090
+ ref: entryRef,
3071
3091
  children: [jsxs("div", {
3072
- class: "bio-properties-panel-list-entry-header",
3092
+ class: classnames('bio-properties-panel-list-entry-header', sticky && open ? 'sticky' : ''),
3073
3093
  onClick: toggleOpen,
3074
3094
  children: [jsx("div", {
3075
3095
  title: label,
@@ -3428,14 +3448,8 @@ function TextArea(props) {
3428
3448
  setLocalValue(e.target.value);
3429
3449
  handleInput(e.target.value);
3430
3450
  };
3431
- const handleOnBlur = e => {
3432
- const trimmedValue = e.target.value.trim();
3433
-
3434
- // trim and commit on blur
3435
- onInput(trimmedValue);
3436
- if (onBlur) {
3437
- onBlur(e);
3438
- }
3451
+ const handleOnBlur = () => {
3452
+ onBlur?.(localValue);
3439
3453
  };
3440
3454
  useLayoutEffect(() => {
3441
3455
  autoResize && resizeToContents(ref.current);
@@ -3513,35 +3527,21 @@ function TextAreaEntry(props) {
3513
3527
  autoResize,
3514
3528
  tooltip
3515
3529
  } = props;
3530
+ const value = getValue(element);
3516
3531
  const globalError = useError(id);
3517
- const [localError, setLocalError] = useState(null);
3518
- let value = getValue(element);
3519
- useEffect(() => {
3520
- if (isFunction(validate)) {
3521
- const newValidationError = validate(value) || null;
3522
- setLocalError(newValidationError);
3523
- }
3524
- }, [value, validate]);
3525
- const onInput = useCallback(newValue => {
3526
- const value = getValue(element);
3527
- let newValidationError = null;
3528
- if (isFunction(validate)) {
3529
- newValidationError = validate(newValue) || null;
3530
- }
3531
- if (newValue !== value) {
3532
- setValue(newValue, newValidationError);
3533
- }
3534
- setLocalError(newValidationError);
3535
- }, [element, getValue, setValue, validate]);
3532
+ const localError = validate?.(value) || null;
3536
3533
  const error = globalError || localError;
3537
3534
  return jsxs("div", {
3538
3535
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
3539
3536
  "data-entry-id": id,
3540
- children: [jsx(TextArea, {
3537
+ children: [jsx(TextInput, {
3538
+ Component: TextArea,
3539
+ getValue: getValue,
3540
+ setValue: setValue,
3541
+ validate: validate,
3541
3542
  id: id,
3542
3543
  label: label,
3543
3544
  value: value,
3544
- onInput: onInput,
3545
3545
  onFocus: onFocus,
3546
3546
  onBlur: onBlur,
3547
3547
  rows: rows,
@@ -3574,7 +3574,6 @@ function prefixId$2(id) {
3574
3574
 
3575
3575
  function Textfield(props) {
3576
3576
  const {
3577
- debounce,
3578
3577
  disabled = false,
3579
3578
  id,
3580
3579
  label,
@@ -3587,23 +3586,12 @@ function Textfield(props) {
3587
3586
  } = props;
3588
3587
  const [localValue, setLocalValue] = useState(value || '');
3589
3588
  const ref = useShowEntryEvent(id);
3590
-
3591
- /**
3592
- * @type { import('min-dash').DebouncedFunction }
3593
- */
3594
- const handleInputCallback = useDebounce(onInput, debounce);
3595
- const handleOnBlur = e => {
3596
- const trimmedValue = e.target.value.trim();
3597
-
3598
- // trim and commit on blur
3599
- onInput(trimmedValue);
3600
- if (onBlur) {
3601
- onBlur(e);
3602
- }
3589
+ const handleOnBlur = () => {
3590
+ onBlur?.(localValue);
3603
3591
  };
3604
3592
  const handleInput = newValue => {
3605
3593
  const newModelValue = newValue === '' ? undefined : newValue;
3606
- handleInputCallback(newModelValue);
3594
+ onInput(newModelValue);
3607
3595
  };
3608
3596
  const handleLocalInput = e => {
3609
3597
  if (e.target.value === localValue) {
@@ -3678,39 +3666,25 @@ function TextfieldEntry(props) {
3678
3666
  placeholder,
3679
3667
  tooltip
3680
3668
  } = props;
3669
+ const value = getValue(element);
3681
3670
  const globalError = useError(id);
3682
- const [localError, setLocalError] = useState(null);
3683
- let value = getValue(element);
3684
- useEffect(() => {
3685
- if (isFunction(validate)) {
3686
- const newValidationError = validate(value) || null;
3687
- setLocalError(newValidationError);
3688
- }
3689
- }, [value, validate]);
3690
- const onInput = useCallback(newValue => {
3691
- const value = getValue(element);
3692
- let newValidationError = null;
3693
- if (isFunction(validate)) {
3694
- newValidationError = validate(newValue) || null;
3695
- }
3696
- if (newValue !== value) {
3697
- setValue(newValue, newValidationError);
3698
- }
3699
- setLocalError(newValidationError);
3700
- }, [element, getValue, setValue, validate]);
3671
+ const localError = validate?.(value) || null;
3701
3672
  const error = globalError || localError;
3702
3673
  return jsxs("div", {
3703
3674
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
3704
3675
  "data-entry-id": id,
3705
- children: [jsx(Textfield, {
3676
+ children: [jsx(TextInput, {
3677
+ Component: Textfield,
3706
3678
  debounce: debounce,
3707
3679
  disabled: disabled,
3680
+ getValue: getValue,
3708
3681
  id: id,
3709
3682
  label: label,
3710
- onInput: onInput,
3711
3683
  onFocus: onFocus,
3712
3684
  onBlur: onBlur,
3713
3685
  placeholder: placeholder,
3686
+ setValue: setValue,
3687
+ validate: validate,
3714
3688
  value: value,
3715
3689
  tooltip: tooltip,
3716
3690
  element: element
@@ -3734,7 +3708,7 @@ function prefixId$1(id) {
3734
3708
  return `bio-properties-panel-${id}`;
3735
3709
  }
3736
3710
 
3737
- const DEFAULT_DEBOUNCE_TIME = 300;
3711
+ const DEFAULT_DEBOUNCE_TIME = 600;
3738
3712
 
3739
3713
  /**
3740
3714
  * Creates a debounced version of a function, delaying its execution based on `debounceDelay`.