@bpmn-io/properties-panel 3.26.4 → 3.27.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
@@ -1940,6 +1940,32 @@ function prefixId$6(id) {
1940
1940
  }
1941
1941
 
1942
1942
  const noop$2 = () => {};
1943
+
1944
+ /**
1945
+ * @typedef {'required'|'optional'|'static'} FeelType
1946
+ */
1947
+
1948
+ /**
1949
+ * @param {Object} props
1950
+ * @param {Boolean} props.debounce
1951
+ * @param {String} props.id
1952
+ * @param {Object} props.element
1953
+ * @param {String} props.label
1954
+ * @param {String} props.hostLanguage
1955
+ * @param {Function} props.onInput
1956
+ * @param {Function} props.onBlur
1957
+ * @param {Function} props.onError
1958
+ * @param {FeelType} [props.feel]
1959
+ * @param {String} props.value
1960
+ * @param {Boolean} [props.singleLine]
1961
+ * @param {Function} props.tooltipContainer
1962
+ * @param {Function | import('preact').Component} props.OptionalComponent
1963
+ * @param {Boolean} props.disabled
1964
+ * @param {Array} props.variables
1965
+ * @param {string} [props.placeholder]
1966
+ * @param {string | import('preact').Component} props.tooltip
1967
+ */
1968
+
1943
1969
  function FeelTextfieldComponent(props) {
1944
1970
  const {
1945
1971
  debounce,
@@ -1948,6 +1974,7 @@ function FeelTextfieldComponent(props) {
1948
1974
  label,
1949
1975
  hostLanguage,
1950
1976
  onInput,
1977
+ onBlur,
1951
1978
  onError,
1952
1979
  placeholder,
1953
1980
  feel,
@@ -1959,7 +1986,7 @@ function FeelTextfieldComponent(props) {
1959
1986
  OptionalComponent = OptionalFeelInput,
1960
1987
  tooltip
1961
1988
  } = props;
1962
- const [localValue, _setLocalValue] = useState(value);
1989
+ const [localValue, setLocalValue] = useState(value);
1963
1990
  const editorRef = useShowEntryEvent(id);
1964
1991
  const containerRef = useRef();
1965
1992
  const feelActive = isString(localValue) && localValue.startsWith('=') || feel === 'required';
@@ -1977,18 +2004,20 @@ function FeelTextfieldComponent(props) {
1977
2004
  const position = hasFocus ? document.activeElement.selectionStart : Infinity;
1978
2005
  _setFocus(position + offset);
1979
2006
  };
2007
+
2008
+ /**
2009
+ * @type { import('min-dash').DebouncedFunction }
2010
+ */
1980
2011
  const handleInputCallback = useMemo(() => {
1981
2012
  return debounce(newValue => {
1982
2013
  onInput(newValue);
1983
2014
  });
1984
2015
  }, [onInput, debounce]);
1985
- const setLocalValue = newValue => {
1986
- _setLocalValue(newValue);
1987
- if (typeof newValue === 'undefined' || newValue === '' || newValue === '=') {
1988
- handleInputCallback(undefined);
1989
- } else {
1990
- handleInputCallback(newValue);
1991
- }
2016
+ const handleInput = newValue => {
2017
+ // we don't commit empty FEEL expressions,
2018
+ // but instead serialize them as <undefined>
2019
+ const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
2020
+ handleInputCallback(newModelValue);
1992
2021
  };
1993
2022
  const handleFeelToggle = useStaticCallback(() => {
1994
2023
  if (feel === 'required') {
@@ -1996,8 +2025,10 @@ function FeelTextfieldComponent(props) {
1996
2025
  }
1997
2026
  if (!feelActive) {
1998
2027
  setLocalValue('=' + localValue);
2028
+ handleInput('=' + localValue);
1999
2029
  } else {
2000
2030
  setLocalValue(feelOnlyValue);
2031
+ handleInput(feelOnlyValue);
2001
2032
  }
2002
2033
  });
2003
2034
  const handleLocalInput = newValue => {
@@ -2008,11 +2039,25 @@ function FeelTextfieldComponent(props) {
2008
2039
  return;
2009
2040
  }
2010
2041
  setLocalValue(newValue);
2042
+ handleInput(newValue);
2011
2043
  if (!feelActive && isString(newValue) && newValue.startsWith('=')) {
2012
2044
  // focus is behind `=` sign that will be removed
2013
2045
  setFocus(-1);
2014
2046
  }
2015
2047
  };
2048
+ const handleOnBlur = e => {
2049
+ const value = e.target.value;
2050
+
2051
+ // we trim the value, if it is needed
2052
+ // and update input accordingly
2053
+ if (value.trim() !== value) {
2054
+ setLocalValue(value.trim());
2055
+ handleInput(value.trim());
2056
+ }
2057
+ if (onBlur) {
2058
+ onBlur(e);
2059
+ }
2060
+ };
2016
2061
  const handleLint = useStaticCallback((lint = []) => {
2017
2062
  const syntaxError = lint.some(report => report.type === 'Syntax Error');
2018
2063
  if (syntaxError) {
@@ -2134,6 +2179,7 @@ function FeelTextfieldComponent(props) {
2134
2179
  ...props,
2135
2180
  popupOpen: popuOpen,
2136
2181
  onInput: handleLocalInput,
2182
+ onBlur: handleOnBlur,
2137
2183
  contentAttributes: {
2138
2184
  'id': prefixId$5(id),
2139
2185
  'aria-label': label
@@ -2360,7 +2406,7 @@ const OptionalFeelCheckbox = forwardRef((props, ref) => {
2360
2406
  * @param {String} props.description
2361
2407
  * @param {Boolean} props.debounce
2362
2408
  * @param {Boolean} props.disabled
2363
- * @param {Boolean} props.feel
2409
+ * @param {FeelType} [props.feel]
2364
2410
  * @param {String} props.label
2365
2411
  * @param {Function} props.getValue
2366
2412
  * @param {Function} props.setValue
@@ -2368,7 +2414,7 @@ const OptionalFeelCheckbox = forwardRef((props, ref) => {
2368
2414
  * @param {Function} props.validate
2369
2415
  * @param {Function} props.show
2370
2416
  * @param {Function} props.example
2371
- * @param {Function} props.variables
2417
+ * @param {Array} props.variables
2372
2418
  * @param {Function} props.onFocus
2373
2419
  * @param {Function} props.onBlur
2374
2420
  * @param {string} [props.placeholder]
@@ -2406,7 +2452,7 @@ function FeelEntry(props) {
2406
2452
  setValidationError(newValidationError);
2407
2453
  }
2408
2454
  }, [value, validate]);
2409
- const onInput = useStaticCallback(newValue => {
2455
+ const onInput = useCallback(newValue => {
2410
2456
  let newValidationError = null;
2411
2457
  if (isFunction(validate)) {
2412
2458
  newValidationError = validate(newValue) || null;
@@ -2417,7 +2463,7 @@ function FeelEntry(props) {
2417
2463
  setValue(newValue, newValidationError);
2418
2464
  }
2419
2465
  setValidationError(newValidationError);
2420
- });
2466
+ }, [element]);
2421
2467
  const onError = useCallback(err => {
2422
2468
  setLocalError(err);
2423
2469
  }, []);
@@ -2469,7 +2515,7 @@ function FeelEntry(props) {
2469
2515
  * @param {String} props.max
2470
2516
  * @param {String} props.min
2471
2517
  * @param {String} props.step
2472
- * @param {Boolean} props.feel
2518
+ * @param {FeelType} [props.feel]
2473
2519
  * @param {String} props.label
2474
2520
  * @param {Function} props.getValue
2475
2521
  * @param {Function} props.setValue
@@ -2477,7 +2523,7 @@ function FeelEntry(props) {
2477
2523
  * @param {Function} props.validate
2478
2524
  * @param {Function} props.show
2479
2525
  * @param {Function} props.example
2480
- * @param {Function} props.variables
2526
+ * @param {Array} props.variables
2481
2527
  * @param {Function} props.onFocus
2482
2528
  * @param {Function} props.onBlur
2483
2529
  */
@@ -2496,7 +2542,7 @@ function FeelNumberEntry(props) {
2496
2542
  * @param {String} props.description
2497
2543
  * @param {Boolean} props.debounce
2498
2544
  * @param {Boolean} props.disabled
2499
- * @param {Boolean} props.feel
2545
+ * @param {FeelType} [props.feel]
2500
2546
  * @param {String} props.label
2501
2547
  * @param {Function} props.getValue
2502
2548
  * @param {Function} props.setValue
@@ -2504,7 +2550,7 @@ function FeelNumberEntry(props) {
2504
2550
  * @param {Function} props.validate
2505
2551
  * @param {Function} props.show
2506
2552
  * @param {Function} props.example
2507
- * @param {Function} props.variables
2553
+ * @param {Array} props.variables
2508
2554
  * @param {Function} props.onFocus
2509
2555
  * @param {Function} props.onBlur
2510
2556
  * @param {string} [props.placeholder]
@@ -2524,7 +2570,7 @@ function FeelTextAreaEntry(props) {
2524
2570
  * @param {String} props.description
2525
2571
  * @param {Boolean} props.debounce
2526
2572
  * @param {Boolean} props.disabled
2527
- * @param {Boolean} props.feel
2573
+ * @param {FeelType} [props.feel]
2528
2574
  * @param {String} props.label
2529
2575
  * @param {Function} props.getValue
2530
2576
  * @param {Function} props.setValue
@@ -2532,7 +2578,7 @@ function FeelTextAreaEntry(props) {
2532
2578
  * @param {Function} props.validate
2533
2579
  * @param {Function} props.show
2534
2580
  * @param {Function} props.example
2535
- * @param {Function} props.variables
2581
+ * @param {Array} props.variables
2536
2582
  * @param {Function} props.onFocus
2537
2583
  * @param {Function} props.onBlur
2538
2584
  */
@@ -2551,7 +2597,7 @@ function FeelToggleSwitchEntry(props) {
2551
2597
  * @param {String} props.description
2552
2598
  * @param {Boolean} props.debounce
2553
2599
  * @param {Boolean} props.disabled
2554
- * @param {Boolean} props.feel
2600
+ * @param {FeelType} [props.feel]
2555
2601
  * @param {String} props.label
2556
2602
  * @param {Function} props.getValue
2557
2603
  * @param {Function} props.setValue
@@ -2559,7 +2605,7 @@ function FeelToggleSwitchEntry(props) {
2559
2605
  * @param {Function} props.validate
2560
2606
  * @param {Function} props.show
2561
2607
  * @param {Function} props.example
2562
- * @param {Function} props.variables
2608
+ * @param {Array} props.variables
2563
2609
  * @param {Function} props.onFocus
2564
2610
  * @param {Function} props.onBlur
2565
2611
  */
@@ -2580,7 +2626,7 @@ function FeelCheckboxEntry(props) {
2580
2626
  * @param {Boolean} props.singleLine
2581
2627
  * @param {Boolean} props.debounce
2582
2628
  * @param {Boolean} props.disabled
2583
- * @param {Boolean} props.feel
2629
+ * @param {FeelType} [props.feel]
2584
2630
  * @param {String} props.label
2585
2631
  * @param {Function} props.getValue
2586
2632
  * @param {Function} props.setValue
@@ -2588,7 +2634,7 @@ function FeelCheckboxEntry(props) {
2588
2634
  * @param {Function} props.validate
2589
2635
  * @param {Function} props.show
2590
2636
  * @param {Function} props.example
2591
- * @param {Function} props.variables
2637
+ * @param {Array} props.variables
2592
2638
  * @param {Function} props.onFocus
2593
2639
  * @param {Function} props.onBlur
2594
2640
  */
@@ -3954,13 +4000,36 @@ function TextArea(props) {
3954
4000
  const [localValue, setLocalValue] = useState(value);
3955
4001
  const ref = useShowEntryEvent(id);
3956
4002
  const visible = useElementVisible(ref.current);
4003
+
4004
+ /**
4005
+ * @type { import('min-dash').DebouncedFunction }
4006
+ */
3957
4007
  const handleInputCallback = useMemo(() => {
3958
- return debounce(target => onInput(target.value.length ? target.value : undefined));
4008
+ return debounce(newValue => {
4009
+ onInput(newValue);
4010
+ });
3959
4011
  }, [onInput, debounce]);
3960
- const handleInput = e => {
3961
- handleInputCallback(e.target);
4012
+ const handleInput = newValue => {
4013
+ const newModelValue = newValue === '' ? undefined : newValue;
4014
+ handleInputCallback(newModelValue);
4015
+ };
4016
+ const handleLocalInput = e => {
3962
4017
  autoResize && resizeToContents(e.target);
4018
+ if (e.target.value === localValue) {
4019
+ return;
4020
+ }
3963
4021
  setLocalValue(e.target.value);
4022
+ handleInput(e.target.value);
4023
+ };
4024
+ const handleOnBlur = e => {
4025
+ const value = e.target.value;
4026
+ if (value.trim() !== value) {
4027
+ setLocalValue(value.trim());
4028
+ handleInput(value.trim());
4029
+ }
4030
+ if (onBlur) {
4031
+ onBlur(e);
4032
+ }
3964
4033
  };
3965
4034
  useLayoutEffect(() => {
3966
4035
  autoResize && resizeToContents(ref.current);
@@ -3991,9 +4060,9 @@ function TextArea(props) {
3991
4060
  name: id,
3992
4061
  spellCheck: "false",
3993
4062
  class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
3994
- onInput: handleInput,
4063
+ onInput: handleLocalInput,
3995
4064
  onFocus: onFocus,
3996
- onBlur: onBlur,
4065
+ onBlur: handleOnBlur,
3997
4066
  placeholder: placeholder,
3998
4067
  rows: rows,
3999
4068
  value: localValue,
@@ -4047,14 +4116,16 @@ function TextAreaEntry(props) {
4047
4116
  setLocalError(newValidationError);
4048
4117
  }
4049
4118
  }, [value, validate]);
4050
- const onInput = newValue => {
4119
+ const onInput = useCallback(newValue => {
4051
4120
  let newValidationError = null;
4052
4121
  if (isFunction(validate)) {
4053
4122
  newValidationError = validate(newValue) || null;
4054
4123
  }
4055
- setValue(newValue, newValidationError);
4124
+ if (newValue !== value) {
4125
+ setValue(newValue, newValidationError);
4126
+ }
4056
4127
  setLocalError(newValidationError);
4057
- };
4128
+ }, [element]);
4058
4129
  const error = globalError || localError;
4059
4130
  return jsxs("div", {
4060
4131
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
@@ -4109,12 +4180,35 @@ function Textfield(props) {
4109
4180
  } = props;
4110
4181
  const [localValue, setLocalValue] = useState(value || '');
4111
4182
  const ref = useShowEntryEvent(id);
4183
+
4184
+ /**
4185
+ * @type { import('min-dash').DebouncedFunction }
4186
+ */
4112
4187
  const handleInputCallback = useMemo(() => {
4113
- return debounce(target => onInput(target.value.length ? target.value : undefined));
4188
+ return debounce(newValue => {
4189
+ onInput(newValue);
4190
+ });
4114
4191
  }, [onInput, debounce]);
4115
- const handleInput = e => {
4116
- handleInputCallback(e.target);
4192
+ const handleOnBlur = e => {
4193
+ const value = e.target.value;
4194
+ if (value.trim() !== value) {
4195
+ setLocalValue(value.trim());
4196
+ handleInput(value.trim());
4197
+ }
4198
+ if (onBlur) {
4199
+ onBlur(e);
4200
+ }
4201
+ };
4202
+ const handleInput = newValue => {
4203
+ const newModelValue = newValue === '' ? undefined : newValue;
4204
+ handleInputCallback(newModelValue);
4205
+ };
4206
+ const handleLocalInput = e => {
4207
+ if (e.target.value === localValue) {
4208
+ return;
4209
+ }
4117
4210
  setLocalValue(e.target.value);
4211
+ handleInput(e.target.value);
4118
4212
  };
4119
4213
  useEffect(() => {
4120
4214
  if (value === localValue) {
@@ -4142,9 +4236,9 @@ function Textfield(props) {
4142
4236
  autoComplete: "off",
4143
4237
  disabled: disabled,
4144
4238
  class: "bio-properties-panel-input",
4145
- onInput: handleInput,
4239
+ onInput: handleLocalInput,
4146
4240
  onFocus: onFocus,
4147
- onBlur: onBlur,
4241
+ onBlur: handleOnBlur,
4148
4242
  placeholder: placeholder,
4149
4243
  value: localValue
4150
4244
  })]
@@ -4191,14 +4285,16 @@ function TextfieldEntry(props) {
4191
4285
  setLocalError(newValidationError);
4192
4286
  }
4193
4287
  }, [value, validate]);
4194
- const onInput = newValue => {
4288
+ const onInput = useCallback(newValue => {
4195
4289
  let newValidationError = null;
4196
4290
  if (isFunction(validate)) {
4197
4291
  newValidationError = validate(newValue) || null;
4198
4292
  }
4199
- setValue(newValue, newValidationError);
4293
+ if (newValue !== value) {
4294
+ setValue(newValue, newValidationError);
4295
+ }
4200
4296
  setLocalError(newValidationError);
4201
- };
4297
+ }, [element]);
4202
4298
  const error = globalError || localError;
4203
4299
  return jsxs("div", {
4204
4300
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
@@ -4236,14 +4332,37 @@ function prefixId(id) {
4236
4332
  }
4237
4333
 
4238
4334
  const DEFAULT_DEBOUNCE_TIME = 300;
4335
+
4336
+ /**
4337
+ * Creates a debounced version of a function, delaying its execution based on `debounceDelay`.
4338
+ *
4339
+ * - If `debounceDelay` is `false`, the function executes immediately without debouncing.
4340
+ * - If a number is provided, the function execution is delayed by the given time in milliseconds.
4341
+ *
4342
+ * @param { Boolean | Number } [debounceDelay=300]
4343
+ *
4344
+ * @example
4345
+ * const debounce = debounceInput();
4346
+ * const debouncedFn = debounce(fn);
4347
+ *
4348
+ * debouncedFn(); // Executes after 300ms (default) if no further calls occur.
4349
+ */
4239
4350
  function debounceInput(debounceDelay) {
4240
- return function _debounceInput(fn) {
4241
- if (debounceDelay !== false) {
4242
- var debounceTime = isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
4243
- return debounce(fn, debounceTime);
4244
- } else {
4351
+ /**
4352
+ * Applies debounce to the provided function, with a previously setup delay.
4353
+ *
4354
+ * @template { (...args: any[]) => any } T
4355
+ *
4356
+ * @param {T} fn
4357
+ *
4358
+ * @return { (...p: Parameters<T>) => any }
4359
+ */
4360
+ return function debounce$1(fn) {
4361
+ if (debounceDelay === false) {
4245
4362
  return fn;
4246
4363
  }
4364
+ var debounceTime = isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
4365
+ return debounce(fn, debounceTime);
4247
4366
  };
4248
4367
  }
4249
4368
  debounceInput.$inject = ['config.debounceInput'];