@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.js CHANGED
@@ -1961,6 +1961,32 @@ function prefixId$6(id) {
1961
1961
  }
1962
1962
 
1963
1963
  const noop$2 = () => {};
1964
+
1965
+ /**
1966
+ * @typedef {'required'|'optional'|'static'} FeelType
1967
+ */
1968
+
1969
+ /**
1970
+ * @param {Object} props
1971
+ * @param {Boolean} props.debounce
1972
+ * @param {String} props.id
1973
+ * @param {Object} props.element
1974
+ * @param {String} props.label
1975
+ * @param {String} props.hostLanguage
1976
+ * @param {Function} props.onInput
1977
+ * @param {Function} props.onBlur
1978
+ * @param {Function} props.onError
1979
+ * @param {FeelType} [props.feel]
1980
+ * @param {String} props.value
1981
+ * @param {Boolean} [props.singleLine]
1982
+ * @param {Function} props.tooltipContainer
1983
+ * @param {Function | import('preact').Component} props.OptionalComponent
1984
+ * @param {Boolean} props.disabled
1985
+ * @param {Array} props.variables
1986
+ * @param {string} [props.placeholder]
1987
+ * @param {string | import('preact').Component} props.tooltip
1988
+ */
1989
+
1964
1990
  function FeelTextfieldComponent(props) {
1965
1991
  const {
1966
1992
  debounce,
@@ -1969,6 +1995,7 @@ function FeelTextfieldComponent(props) {
1969
1995
  label,
1970
1996
  hostLanguage,
1971
1997
  onInput,
1998
+ onBlur,
1972
1999
  onError,
1973
2000
  placeholder,
1974
2001
  feel,
@@ -1980,7 +2007,7 @@ function FeelTextfieldComponent(props) {
1980
2007
  OptionalComponent = OptionalFeelInput,
1981
2008
  tooltip
1982
2009
  } = props;
1983
- const [localValue, _setLocalValue] = hooks.useState(value);
2010
+ const [localValue, setLocalValue] = hooks.useState(value);
1984
2011
  const editorRef = useShowEntryEvent(id);
1985
2012
  const containerRef = hooks.useRef();
1986
2013
  const feelActive = minDash.isString(localValue) && localValue.startsWith('=') || feel === 'required';
@@ -1998,18 +2025,20 @@ function FeelTextfieldComponent(props) {
1998
2025
  const position = hasFocus ? document.activeElement.selectionStart : Infinity;
1999
2026
  _setFocus(position + offset);
2000
2027
  };
2028
+
2029
+ /**
2030
+ * @type { import('min-dash').DebouncedFunction }
2031
+ */
2001
2032
  const handleInputCallback = hooks.useMemo(() => {
2002
2033
  return debounce(newValue => {
2003
2034
  onInput(newValue);
2004
2035
  });
2005
2036
  }, [onInput, debounce]);
2006
- const setLocalValue = newValue => {
2007
- _setLocalValue(newValue);
2008
- if (typeof newValue === 'undefined' || newValue === '' || newValue === '=') {
2009
- handleInputCallback(undefined);
2010
- } else {
2011
- handleInputCallback(newValue);
2012
- }
2037
+ const handleInput = newValue => {
2038
+ // we don't commit empty FEEL expressions,
2039
+ // but instead serialize them as <undefined>
2040
+ const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
2041
+ handleInputCallback(newModelValue);
2013
2042
  };
2014
2043
  const handleFeelToggle = useStaticCallback(() => {
2015
2044
  if (feel === 'required') {
@@ -2017,8 +2046,10 @@ function FeelTextfieldComponent(props) {
2017
2046
  }
2018
2047
  if (!feelActive) {
2019
2048
  setLocalValue('=' + localValue);
2049
+ handleInput('=' + localValue);
2020
2050
  } else {
2021
2051
  setLocalValue(feelOnlyValue);
2052
+ handleInput(feelOnlyValue);
2022
2053
  }
2023
2054
  });
2024
2055
  const handleLocalInput = newValue => {
@@ -2029,11 +2060,25 @@ function FeelTextfieldComponent(props) {
2029
2060
  return;
2030
2061
  }
2031
2062
  setLocalValue(newValue);
2063
+ handleInput(newValue);
2032
2064
  if (!feelActive && minDash.isString(newValue) && newValue.startsWith('=')) {
2033
2065
  // focus is behind `=` sign that will be removed
2034
2066
  setFocus(-1);
2035
2067
  }
2036
2068
  };
2069
+ const handleOnBlur = e => {
2070
+ const value = e.target.value;
2071
+
2072
+ // we trim the value, if it is needed
2073
+ // and update input accordingly
2074
+ if (value.trim() !== value) {
2075
+ setLocalValue(value.trim());
2076
+ handleInput(value.trim());
2077
+ }
2078
+ if (onBlur) {
2079
+ onBlur(e);
2080
+ }
2081
+ };
2037
2082
  const handleLint = useStaticCallback((lint = []) => {
2038
2083
  const syntaxError = lint.some(report => report.type === 'Syntax Error');
2039
2084
  if (syntaxError) {
@@ -2155,6 +2200,7 @@ function FeelTextfieldComponent(props) {
2155
2200
  ...props,
2156
2201
  popupOpen: popuOpen,
2157
2202
  onInput: handleLocalInput,
2203
+ onBlur: handleOnBlur,
2158
2204
  contentAttributes: {
2159
2205
  'id': prefixId$5(id),
2160
2206
  'aria-label': label
@@ -2381,7 +2427,7 @@ const OptionalFeelCheckbox = compat.forwardRef((props, ref) => {
2381
2427
  * @param {String} props.description
2382
2428
  * @param {Boolean} props.debounce
2383
2429
  * @param {Boolean} props.disabled
2384
- * @param {Boolean} props.feel
2430
+ * @param {FeelType} [props.feel]
2385
2431
  * @param {String} props.label
2386
2432
  * @param {Function} props.getValue
2387
2433
  * @param {Function} props.setValue
@@ -2389,7 +2435,7 @@ const OptionalFeelCheckbox = compat.forwardRef((props, ref) => {
2389
2435
  * @param {Function} props.validate
2390
2436
  * @param {Function} props.show
2391
2437
  * @param {Function} props.example
2392
- * @param {Function} props.variables
2438
+ * @param {Array} props.variables
2393
2439
  * @param {Function} props.onFocus
2394
2440
  * @param {Function} props.onBlur
2395
2441
  * @param {string} [props.placeholder]
@@ -2427,7 +2473,7 @@ function FeelEntry(props) {
2427
2473
  setValidationError(newValidationError);
2428
2474
  }
2429
2475
  }, [value, validate]);
2430
- const onInput = useStaticCallback(newValue => {
2476
+ const onInput = hooks.useCallback(newValue => {
2431
2477
  let newValidationError = null;
2432
2478
  if (minDash.isFunction(validate)) {
2433
2479
  newValidationError = validate(newValue) || null;
@@ -2438,7 +2484,7 @@ function FeelEntry(props) {
2438
2484
  setValue(newValue, newValidationError);
2439
2485
  }
2440
2486
  setValidationError(newValidationError);
2441
- });
2487
+ }, [element]);
2442
2488
  const onError = hooks.useCallback(err => {
2443
2489
  setLocalError(err);
2444
2490
  }, []);
@@ -2490,7 +2536,7 @@ function FeelEntry(props) {
2490
2536
  * @param {String} props.max
2491
2537
  * @param {String} props.min
2492
2538
  * @param {String} props.step
2493
- * @param {Boolean} props.feel
2539
+ * @param {FeelType} [props.feel]
2494
2540
  * @param {String} props.label
2495
2541
  * @param {Function} props.getValue
2496
2542
  * @param {Function} props.setValue
@@ -2498,7 +2544,7 @@ function FeelEntry(props) {
2498
2544
  * @param {Function} props.validate
2499
2545
  * @param {Function} props.show
2500
2546
  * @param {Function} props.example
2501
- * @param {Function} props.variables
2547
+ * @param {Array} props.variables
2502
2548
  * @param {Function} props.onFocus
2503
2549
  * @param {Function} props.onBlur
2504
2550
  */
@@ -2517,7 +2563,7 @@ function FeelNumberEntry(props) {
2517
2563
  * @param {String} props.description
2518
2564
  * @param {Boolean} props.debounce
2519
2565
  * @param {Boolean} props.disabled
2520
- * @param {Boolean} props.feel
2566
+ * @param {FeelType} [props.feel]
2521
2567
  * @param {String} props.label
2522
2568
  * @param {Function} props.getValue
2523
2569
  * @param {Function} props.setValue
@@ -2525,7 +2571,7 @@ function FeelNumberEntry(props) {
2525
2571
  * @param {Function} props.validate
2526
2572
  * @param {Function} props.show
2527
2573
  * @param {Function} props.example
2528
- * @param {Function} props.variables
2574
+ * @param {Array} props.variables
2529
2575
  * @param {Function} props.onFocus
2530
2576
  * @param {Function} props.onBlur
2531
2577
  * @param {string} [props.placeholder]
@@ -2545,7 +2591,7 @@ function FeelTextAreaEntry(props) {
2545
2591
  * @param {String} props.description
2546
2592
  * @param {Boolean} props.debounce
2547
2593
  * @param {Boolean} props.disabled
2548
- * @param {Boolean} props.feel
2594
+ * @param {FeelType} [props.feel]
2549
2595
  * @param {String} props.label
2550
2596
  * @param {Function} props.getValue
2551
2597
  * @param {Function} props.setValue
@@ -2553,7 +2599,7 @@ function FeelTextAreaEntry(props) {
2553
2599
  * @param {Function} props.validate
2554
2600
  * @param {Function} props.show
2555
2601
  * @param {Function} props.example
2556
- * @param {Function} props.variables
2602
+ * @param {Array} props.variables
2557
2603
  * @param {Function} props.onFocus
2558
2604
  * @param {Function} props.onBlur
2559
2605
  */
@@ -2572,7 +2618,7 @@ function FeelToggleSwitchEntry(props) {
2572
2618
  * @param {String} props.description
2573
2619
  * @param {Boolean} props.debounce
2574
2620
  * @param {Boolean} props.disabled
2575
- * @param {Boolean} props.feel
2621
+ * @param {FeelType} [props.feel]
2576
2622
  * @param {String} props.label
2577
2623
  * @param {Function} props.getValue
2578
2624
  * @param {Function} props.setValue
@@ -2580,7 +2626,7 @@ function FeelToggleSwitchEntry(props) {
2580
2626
  * @param {Function} props.validate
2581
2627
  * @param {Function} props.show
2582
2628
  * @param {Function} props.example
2583
- * @param {Function} props.variables
2629
+ * @param {Array} props.variables
2584
2630
  * @param {Function} props.onFocus
2585
2631
  * @param {Function} props.onBlur
2586
2632
  */
@@ -2601,7 +2647,7 @@ function FeelCheckboxEntry(props) {
2601
2647
  * @param {Boolean} props.singleLine
2602
2648
  * @param {Boolean} props.debounce
2603
2649
  * @param {Boolean} props.disabled
2604
- * @param {Boolean} props.feel
2650
+ * @param {FeelType} [props.feel]
2605
2651
  * @param {String} props.label
2606
2652
  * @param {Function} props.getValue
2607
2653
  * @param {Function} props.setValue
@@ -2609,7 +2655,7 @@ function FeelCheckboxEntry(props) {
2609
2655
  * @param {Function} props.validate
2610
2656
  * @param {Function} props.show
2611
2657
  * @param {Function} props.example
2612
- * @param {Function} props.variables
2658
+ * @param {Array} props.variables
2613
2659
  * @param {Function} props.onFocus
2614
2660
  * @param {Function} props.onBlur
2615
2661
  */
@@ -3975,13 +4021,36 @@ function TextArea(props) {
3975
4021
  const [localValue, setLocalValue] = hooks.useState(value);
3976
4022
  const ref = useShowEntryEvent(id);
3977
4023
  const visible = useElementVisible(ref.current);
4024
+
4025
+ /**
4026
+ * @type { import('min-dash').DebouncedFunction }
4027
+ */
3978
4028
  const handleInputCallback = hooks.useMemo(() => {
3979
- return debounce(target => onInput(target.value.length ? target.value : undefined));
4029
+ return debounce(newValue => {
4030
+ onInput(newValue);
4031
+ });
3980
4032
  }, [onInput, debounce]);
3981
- const handleInput = e => {
3982
- handleInputCallback(e.target);
4033
+ const handleInput = newValue => {
4034
+ const newModelValue = newValue === '' ? undefined : newValue;
4035
+ handleInputCallback(newModelValue);
4036
+ };
4037
+ const handleLocalInput = e => {
3983
4038
  autoResize && resizeToContents(e.target);
4039
+ if (e.target.value === localValue) {
4040
+ return;
4041
+ }
3984
4042
  setLocalValue(e.target.value);
4043
+ handleInput(e.target.value);
4044
+ };
4045
+ const handleOnBlur = e => {
4046
+ const value = e.target.value;
4047
+ if (value.trim() !== value) {
4048
+ setLocalValue(value.trim());
4049
+ handleInput(value.trim());
4050
+ }
4051
+ if (onBlur) {
4052
+ onBlur(e);
4053
+ }
3985
4054
  };
3986
4055
  hooks.useLayoutEffect(() => {
3987
4056
  autoResize && resizeToContents(ref.current);
@@ -4012,9 +4081,9 @@ function TextArea(props) {
4012
4081
  name: id,
4013
4082
  spellCheck: "false",
4014
4083
  class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
4015
- onInput: handleInput,
4084
+ onInput: handleLocalInput,
4016
4085
  onFocus: onFocus,
4017
- onBlur: onBlur,
4086
+ onBlur: handleOnBlur,
4018
4087
  placeholder: placeholder,
4019
4088
  rows: rows,
4020
4089
  value: localValue,
@@ -4068,14 +4137,16 @@ function TextAreaEntry(props) {
4068
4137
  setLocalError(newValidationError);
4069
4138
  }
4070
4139
  }, [value, validate]);
4071
- const onInput = newValue => {
4140
+ const onInput = hooks.useCallback(newValue => {
4072
4141
  let newValidationError = null;
4073
4142
  if (minDash.isFunction(validate)) {
4074
4143
  newValidationError = validate(newValue) || null;
4075
4144
  }
4076
- setValue(newValue, newValidationError);
4145
+ if (newValue !== value) {
4146
+ setValue(newValue, newValidationError);
4147
+ }
4077
4148
  setLocalError(newValidationError);
4078
- };
4149
+ }, [element]);
4079
4150
  const error = globalError || localError;
4080
4151
  return jsxRuntime.jsxs("div", {
4081
4152
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
@@ -4130,12 +4201,35 @@ function Textfield(props) {
4130
4201
  } = props;
4131
4202
  const [localValue, setLocalValue] = hooks.useState(value || '');
4132
4203
  const ref = useShowEntryEvent(id);
4204
+
4205
+ /**
4206
+ * @type { import('min-dash').DebouncedFunction }
4207
+ */
4133
4208
  const handleInputCallback = hooks.useMemo(() => {
4134
- return debounce(target => onInput(target.value.length ? target.value : undefined));
4209
+ return debounce(newValue => {
4210
+ onInput(newValue);
4211
+ });
4135
4212
  }, [onInput, debounce]);
4136
- const handleInput = e => {
4137
- handleInputCallback(e.target);
4213
+ const handleOnBlur = e => {
4214
+ const value = e.target.value;
4215
+ if (value.trim() !== value) {
4216
+ setLocalValue(value.trim());
4217
+ handleInput(value.trim());
4218
+ }
4219
+ if (onBlur) {
4220
+ onBlur(e);
4221
+ }
4222
+ };
4223
+ const handleInput = newValue => {
4224
+ const newModelValue = newValue === '' ? undefined : newValue;
4225
+ handleInputCallback(newModelValue);
4226
+ };
4227
+ const handleLocalInput = e => {
4228
+ if (e.target.value === localValue) {
4229
+ return;
4230
+ }
4138
4231
  setLocalValue(e.target.value);
4232
+ handleInput(e.target.value);
4139
4233
  };
4140
4234
  hooks.useEffect(() => {
4141
4235
  if (value === localValue) {
@@ -4163,9 +4257,9 @@ function Textfield(props) {
4163
4257
  autoComplete: "off",
4164
4258
  disabled: disabled,
4165
4259
  class: "bio-properties-panel-input",
4166
- onInput: handleInput,
4260
+ onInput: handleLocalInput,
4167
4261
  onFocus: onFocus,
4168
- onBlur: onBlur,
4262
+ onBlur: handleOnBlur,
4169
4263
  placeholder: placeholder,
4170
4264
  value: localValue
4171
4265
  })]
@@ -4212,14 +4306,16 @@ function TextfieldEntry(props) {
4212
4306
  setLocalError(newValidationError);
4213
4307
  }
4214
4308
  }, [value, validate]);
4215
- const onInput = newValue => {
4309
+ const onInput = hooks.useCallback(newValue => {
4216
4310
  let newValidationError = null;
4217
4311
  if (minDash.isFunction(validate)) {
4218
4312
  newValidationError = validate(newValue) || null;
4219
4313
  }
4220
- setValue(newValue, newValidationError);
4314
+ if (newValue !== value) {
4315
+ setValue(newValue, newValidationError);
4316
+ }
4221
4317
  setLocalError(newValidationError);
4222
- };
4318
+ }, [element]);
4223
4319
  const error = globalError || localError;
4224
4320
  return jsxRuntime.jsxs("div", {
4225
4321
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
@@ -4257,14 +4353,37 @@ function prefixId(id) {
4257
4353
  }
4258
4354
 
4259
4355
  const DEFAULT_DEBOUNCE_TIME = 300;
4356
+
4357
+ /**
4358
+ * Creates a debounced version of a function, delaying its execution based on `debounceDelay`.
4359
+ *
4360
+ * - If `debounceDelay` is `false`, the function executes immediately without debouncing.
4361
+ * - If a number is provided, the function execution is delayed by the given time in milliseconds.
4362
+ *
4363
+ * @param { Boolean | Number } [debounceDelay=300]
4364
+ *
4365
+ * @example
4366
+ * const debounce = debounceInput();
4367
+ * const debouncedFn = debounce(fn);
4368
+ *
4369
+ * debouncedFn(); // Executes after 300ms (default) if no further calls occur.
4370
+ */
4260
4371
  function debounceInput(debounceDelay) {
4261
- return function _debounceInput(fn) {
4262
- if (debounceDelay !== false) {
4263
- var debounceTime = minDash.isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
4264
- return minDash.debounce(fn, debounceTime);
4265
- } else {
4372
+ /**
4373
+ * Applies debounce to the provided function, with a previously setup delay.
4374
+ *
4375
+ * @template { (...args: any[]) => any } T
4376
+ *
4377
+ * @param {T} fn
4378
+ *
4379
+ * @return { (...p: Parameters<T>) => any }
4380
+ */
4381
+ return function debounce(fn) {
4382
+ if (debounceDelay === false) {
4266
4383
  return fn;
4267
4384
  }
4385
+ var debounceTime = minDash.isNumber(debounceDelay) ? debounceDelay : DEFAULT_DEBOUNCE_TIME;
4386
+ return minDash.debounce(fn, debounceTime);
4268
4387
  };
4269
4388
  }
4270
4389
  debounceInput.$inject = ['config.debounceInput'];