@bpmn-io/form-js-editor 1.10.1 → 1.11.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.es.js CHANGED
@@ -1496,6 +1496,8 @@ function Palette(props) {
1496
1496
  const initialPaletteEntries = useRef(collectPaletteEntries(formFields));
1497
1497
  const [paletteEntries, setPaletteEntries] = useState(initialPaletteEntries.current);
1498
1498
  const [searchTerm, setSearchTerm] = useState('');
1499
+
1500
+ /** @type {import("preact").RefObject<HTMLInputElement>} */
1499
1501
  const inputRef = useRef();
1500
1502
  const groups = groupEntries(paletteEntries);
1501
1503
  const simplifyString = useCallback(str => {
@@ -1618,7 +1620,8 @@ function collectPaletteEntries(formFields) {
1618
1620
  config: fieldConfig
1619
1621
  } = formField;
1620
1622
  return {
1621
- label: fieldConfig.label,
1623
+ // fieldConfig.label is used to maintain backwards compatibility with custom form fields
1624
+ label: fieldConfig.name || fieldConfig.label,
1622
1625
  type: type,
1623
1626
  group: fieldConfig.group,
1624
1627
  icon: fieldConfig.icon,
@@ -1921,6 +1924,29 @@ class Dragging {
1921
1924
  }
1922
1925
  return !target.classList.contains(DRAG_NO_DROP_CLS);
1923
1926
  },
1927
+ transformOffset: (offset, event, element) => {
1928
+ if (element.classList.contains(DRAG_ROW_MOVE_CLS)) {
1929
+ const rowOffset = {
1930
+ x: -5,
1931
+ y: -60
1932
+ };
1933
+ return {
1934
+ left: event.clientX + rowOffset.x,
1935
+ top: event.clientY + rowOffset.y
1936
+ };
1937
+ }
1938
+ if (element.classList.contains(DRAG_MOVE_CLS)) {
1939
+ const iconOffset = {
1940
+ x: -5,
1941
+ y: -15
1942
+ };
1943
+ return {
1944
+ left: event.clientX + iconOffset.x,
1945
+ top: event.clientY + iconOffset.y
1946
+ };
1947
+ }
1948
+ return offset;
1949
+ },
1924
1950
  slideFactorX: 10,
1925
1951
  slideFactorY: 5
1926
1952
  };
@@ -2216,6 +2242,8 @@ function Element$1(props) {
2216
2242
  type,
2217
2243
  showOutline
2218
2244
  } = field;
2245
+
2246
+ /** @type {import("preact").RefObject<HTMLDivElement>} */
2219
2247
  const ref = useRef();
2220
2248
  const [hovered, setHovered] = useState(false);
2221
2249
  useEffect(() => {
@@ -7224,12 +7252,13 @@ function FeelTextfieldComponent(props) {
7224
7252
  setFocus(-1);
7225
7253
  }
7226
7254
  };
7227
- const handleLint = useStaticCallback(lint => {
7228
- if (!(lint && lint.length)) {
7255
+ const handleLint = useStaticCallback((lint = []) => {
7256
+ const syntaxError = lint.some(report => report.type === 'Syntax Error');
7257
+ if (syntaxError) {
7258
+ onError('Unparsable FEEL expression.');
7259
+ } else {
7229
7260
  onError(undefined);
7230
- return;
7231
7261
  }
7232
- onError('Unparsable FEEL expression.');
7233
7262
  });
7234
7263
  const handlePopupOpen = (type = 'feel') => {
7235
7264
  const popupOptions = {
@@ -8961,82 +8990,6 @@ const FormPropertiesPanelContext = createContext({
8961
8990
  getService
8962
8991
  });
8963
8992
 
8964
- function arrayAdd(array, index, item) {
8965
- const copy = [...array];
8966
- copy.splice(index, 0, item);
8967
- return copy;
8968
- }
8969
- function countDecimals(number) {
8970
- const num = Big(number);
8971
- if (num.toString() === num.toFixed(0)) return 0;
8972
- return num.toFixed().split('.')[1].length || 0;
8973
- }
8974
-
8975
- /**
8976
- *
8977
- * @param {unknown} value
8978
- * @returns {boolean}
8979
- */
8980
- function isValidNumber(value) {
8981
- return (typeof value === 'number' || typeof value === 'string') && value !== '' && !isNaN(Number(value));
8982
- }
8983
- function textToLabel(text) {
8984
- if (typeof text != 'string') return null;
8985
- for (const line of text.split('\n')) {
8986
- const displayLine = line.trim();
8987
-
8988
- // we use the first non-whitespace line in the text as label
8989
- if (displayLine !== '') {
8990
- return displayLine;
8991
- }
8992
- }
8993
- return null;
8994
- }
8995
-
8996
- /**
8997
- * @param {string} path
8998
- */
8999
- function isValidDotPath(path) {
9000
- return /^\w+(\.\w+)*$/.test(path);
9001
- }
9002
-
9003
- /**
9004
- * @param {string} path
9005
- */
9006
- function isProhibitedPath(path) {
9007
- const prohibitedSegments = ['__proto__', 'prototype', 'constructor'];
9008
- return path.split('.').some(segment => prohibitedSegments.includes(segment));
9009
- }
9010
- const LABELED_NON_INPUTS = ['button', 'group', 'dynamiclist', 'iframe', 'table'];
9011
- const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
9012
- const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
9013
- function hasEntryConfigured(formFieldDefinition, entryId) {
9014
- const {
9015
- propertiesPanelEntries = []
9016
- } = formFieldDefinition;
9017
- if (!propertiesPanelEntries.length) {
9018
- return false;
9019
- }
9020
- return propertiesPanelEntries.some(id => id === entryId);
9021
- }
9022
- function hasOptionsGroupsConfigured(formFieldDefinition) {
9023
- const {
9024
- propertiesPanelEntries = []
9025
- } = formFieldDefinition;
9026
- if (!propertiesPanelEntries.length) {
9027
- return false;
9028
- }
9029
- return propertiesPanelEntries.some(id => id === 'values');
9030
- }
9031
-
9032
- /**
9033
- * @param {string} path
9034
- */
9035
- function hasIntegerPathSegment(path) {
9036
- return path.split('.').some(segment => /^\d+$/.test(segment));
9037
- }
9038
-
9039
- const headerlessTypes = ['spacer', 'separator', 'expression', 'html'];
9040
8993
  function getPropertiesPanelHeaderProvider(options = {}) {
9041
8994
  const {
9042
8995
  getDocumentationRef,
@@ -9047,19 +9000,8 @@ function getPropertiesPanelHeaderProvider(options = {}) {
9047
9000
  const {
9048
9001
  type
9049
9002
  } = field;
9050
- if (headerlessTypes.includes(type)) {
9051
- return '';
9052
- }
9053
- if (type === 'text') {
9054
- return textToLabel(field.text);
9055
- }
9056
- if (type === 'image') {
9057
- return field.alt;
9058
- }
9059
- if (type === 'default') {
9060
- return field.id;
9061
- }
9062
- return field.label;
9003
+ const fieldDefinition = formFields.get(type).config;
9004
+ return fieldDefinition.getSubheading ? fieldDefinition.getSubheading(field) : field.label;
9063
9005
  },
9064
9006
  getElementIcon: field => {
9065
9007
  const {
@@ -9088,7 +9030,7 @@ function getPropertiesPanelHeaderProvider(options = {}) {
9088
9030
  return 'Form';
9089
9031
  }
9090
9032
  const fieldDefinition = formFields.get(type).config;
9091
- return fieldDefinition.label || type;
9033
+ return fieldDefinition.name || fieldDefinition.label || type;
9092
9034
  },
9093
9035
  getDocumentationRef
9094
9036
  };
@@ -9489,6 +9431,69 @@ function asArray(length) {
9489
9431
  }).map((_, i) => i + 1);
9490
9432
  }
9491
9433
 
9434
+ function arrayAdd(array, index, item) {
9435
+ const copy = [...array];
9436
+ copy.splice(index, 0, item);
9437
+ return copy;
9438
+ }
9439
+ function countDecimals(number) {
9440
+ const num = Big(number);
9441
+ if (num.toString() === num.toFixed(0)) return 0;
9442
+ return num.toFixed().split('.')[1].length || 0;
9443
+ }
9444
+
9445
+ /**
9446
+ *
9447
+ * @param {unknown} value
9448
+ * @returns {boolean}
9449
+ */
9450
+ function isValidNumber(value) {
9451
+ return (typeof value === 'number' || typeof value === 'string') && value !== '' && !isNaN(Number(value));
9452
+ }
9453
+
9454
+ /**
9455
+ * @param {string} path
9456
+ */
9457
+ function isValidDotPath(path) {
9458
+ return /^\w+(\.\w+)*$/.test(path);
9459
+ }
9460
+
9461
+ /**
9462
+ * @param {string} path
9463
+ */
9464
+ function isProhibitedPath(path) {
9465
+ const prohibitedSegments = ['__proto__', 'prototype', 'constructor'];
9466
+ return path.split('.').some(segment => prohibitedSegments.includes(segment));
9467
+ }
9468
+ const LABELED_NON_INPUTS = ['button', 'group', 'dynamiclist', 'iframe', 'table'];
9469
+ const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea', 'filepicker'];
9470
+ const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
9471
+ function hasEntryConfigured(formFieldDefinition, entryId) {
9472
+ const {
9473
+ propertiesPanelEntries = []
9474
+ } = formFieldDefinition;
9475
+ if (!propertiesPanelEntries.length) {
9476
+ return false;
9477
+ }
9478
+ return propertiesPanelEntries.some(id => id === entryId);
9479
+ }
9480
+ function hasOptionsGroupsConfigured(formFieldDefinition) {
9481
+ const {
9482
+ propertiesPanelEntries = []
9483
+ } = formFieldDefinition;
9484
+ if (!propertiesPanelEntries.length) {
9485
+ return false;
9486
+ }
9487
+ return propertiesPanelEntries.some(id => id === 'values');
9488
+ }
9489
+
9490
+ /**
9491
+ * @param {string} path
9492
+ */
9493
+ function hasIntegerPathSegment(path) {
9494
+ return path.split('.').some(segment => /^\d+$/.test(segment));
9495
+ }
9496
+
9492
9497
  function DescriptionEntry(props) {
9493
9498
  const {
9494
9499
  editField,
@@ -9501,7 +9506,7 @@ function DescriptionEntry(props) {
9501
9506
  editField: editField,
9502
9507
  field: field,
9503
9508
  isEdited: isEdited$6,
9504
- isDefaultVisible: field => INPUTS.includes(field.type)
9509
+ isDefaultVisible: field => field.type !== 'filepicker' && INPUTS.includes(field.type)
9505
9510
  });
9506
9511
  return entries;
9507
9512
  }
@@ -9534,7 +9539,7 @@ function Description(props) {
9534
9539
  });
9535
9540
  }
9536
9541
 
9537
- const EMPTY_OPTION = null;
9542
+ const EMPTY_OPTION = '';
9538
9543
  function DefaultValueEntry(props) {
9539
9544
  const {
9540
9545
  editField,
@@ -9553,26 +9558,26 @@ function DefaultValueEntry(props) {
9553
9558
  return matchers(field);
9554
9559
  };
9555
9560
  }
9556
- const defaulValueBase = {
9561
+ const defaultValueBase = {
9557
9562
  editField,
9558
9563
  field,
9559
9564
  id: 'defaultValue',
9560
9565
  label: 'Default value'
9561
9566
  };
9562
9567
  entries.push({
9563
- ...defaulValueBase,
9568
+ ...defaultValueBase,
9564
9569
  component: DefaultValueCheckbox,
9565
9570
  isEdited: isEdited$3,
9566
9571
  isDefaultVisible: isDefaultVisible(field => field.type === 'checkbox')
9567
9572
  });
9568
9573
  entries.push({
9569
- ...defaulValueBase,
9574
+ ...defaultValueBase,
9570
9575
  component: DefaultValueNumber,
9571
9576
  isEdited: isEdited,
9572
9577
  isDefaultVisible: isDefaultVisible(field => field.type === 'number')
9573
9578
  });
9574
9579
  entries.push({
9575
- ...defaulValueBase,
9580
+ ...defaultValueBase,
9576
9581
  component: DefaultValueSingleSelect,
9577
9582
  isEdited: isEdited$3,
9578
9583
  isDefaultVisible: isDefaultVisible(field => field.type === 'radio' || field.type === 'select')
@@ -9581,13 +9586,13 @@ function DefaultValueEntry(props) {
9581
9586
  // todo(Skaiir): implement a multiselect equivalent (cf. https://github.com/bpmn-io/form-js/issues/265)
9582
9587
 
9583
9588
  entries.push({
9584
- ...defaulValueBase,
9589
+ ...defaultValueBase,
9585
9590
  component: DefaultValueTextfield,
9586
9591
  isEdited: isEdited,
9587
9592
  isDefaultVisible: isDefaultVisible(field => field.type === 'textfield')
9588
9593
  });
9589
9594
  entries.push({
9590
- ...defaulValueBase,
9595
+ ...defaultValueBase,
9591
9596
  component: DefaultValueTextarea,
9592
9597
  isEdited: isEdited$1,
9593
9598
  isDefaultVisible: isDefaultVisible(field => field.type === 'textarea')
@@ -10669,7 +10674,7 @@ function Text(props) {
10669
10674
  };
10670
10675
  return FeelTemplatingEntry({
10671
10676
  debounce,
10672
- description: description$1,
10677
+ description: description$2,
10673
10678
  element: field,
10674
10679
  getValue,
10675
10680
  id,
@@ -10679,7 +10684,7 @@ function Text(props) {
10679
10684
  variables
10680
10685
  });
10681
10686
  }
10682
- const description$1 = jsxs(Fragment$1, {
10687
+ const description$2 = jsxs(Fragment$1, {
10683
10688
  children: ["Supports markdown and templating.", ' ', jsx("a", {
10684
10689
  href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-text/",
10685
10690
  target: "_blank",
@@ -10721,7 +10726,7 @@ function Content(props) {
10721
10726
  };
10722
10727
  return FeelTemplatingEntry({
10723
10728
  debounce,
10724
- description,
10729
+ description: description$1,
10725
10730
  element: field,
10726
10731
  getValue,
10727
10732
  id,
@@ -10735,7 +10740,7 @@ function Content(props) {
10735
10740
 
10736
10741
  // helpers //////////
10737
10742
 
10738
- const description = jsxs(Fragment$1, {
10743
+ const description$1 = jsxs(Fragment$1, {
10739
10744
  children: ["Supports HTML, styling, and templating. Styles are automatically scoped to the HTML component.", ' ', jsx("a", {
10740
10745
  href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-html/",
10741
10746
  target: "_blank",
@@ -12551,6 +12556,108 @@ function VersionTag(props) {
12551
12556
  });
12552
12557
  }
12553
12558
 
12559
+ function AcceptEntry(props) {
12560
+ const {
12561
+ editField,
12562
+ field
12563
+ } = props;
12564
+ const entries = [];
12565
+ entries.push({
12566
+ id: 'accept',
12567
+ component: Accept,
12568
+ editField: editField,
12569
+ field: field,
12570
+ isEdited: isEdited$6,
12571
+ isDefaultVisible: field => field.type === 'filepicker'
12572
+ });
12573
+ return entries;
12574
+ }
12575
+ function Accept(props) {
12576
+ const {
12577
+ editField,
12578
+ field,
12579
+ id
12580
+ } = props;
12581
+ const debounce = useService('debounce');
12582
+ const variables = useVariables().map(name => ({
12583
+ name
12584
+ }));
12585
+ const path = ['accept'];
12586
+ const getValue = () => {
12587
+ return get(field, path, '');
12588
+ };
12589
+ const setValue = value => {
12590
+ return editField(field, path, value);
12591
+ };
12592
+ return FeelTemplatingEntry({
12593
+ debounce,
12594
+ element: field,
12595
+ getValue,
12596
+ id,
12597
+ label: 'Supported file formats',
12598
+ singleLine: true,
12599
+ setValue,
12600
+ variables,
12601
+ description
12602
+ });
12603
+ }
12604
+
12605
+ // helpers //////////
12606
+
12607
+ const description = jsxs(Fragment$1, {
12608
+ children: ["A comma-separated list of", ' ', jsx("a", {
12609
+ href: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers",
12610
+ target: "_blank",
12611
+ children: "file type specifiers"
12612
+ })]
12613
+ });
12614
+
12615
+ function MultipleEntry(props) {
12616
+ const {
12617
+ editField,
12618
+ field
12619
+ } = props;
12620
+ const entries = [];
12621
+ entries.push({
12622
+ id: 'multiple',
12623
+ component: Multiple,
12624
+ editField: editField,
12625
+ field: field,
12626
+ isEdited: isEdited$6,
12627
+ isDefaultVisible: field => field.type === 'filepicker'
12628
+ });
12629
+ return entries;
12630
+ }
12631
+ function Multiple(props) {
12632
+ const {
12633
+ editField,
12634
+ field,
12635
+ id
12636
+ } = props;
12637
+ const debounce = useService('debounce');
12638
+ const variables = useVariables().map(name => ({
12639
+ name
12640
+ }));
12641
+ const path = ['multiple'];
12642
+ const getValue = () => {
12643
+ return get(field, path, '');
12644
+ };
12645
+ const setValue = value => {
12646
+ return editField(field, path, value);
12647
+ };
12648
+ return FeelToggleSwitchEntry({
12649
+ debounce,
12650
+ element: field,
12651
+ feel: 'optional',
12652
+ getValue,
12653
+ id,
12654
+ label: 'Upload multiple files',
12655
+ inline: true,
12656
+ setValue,
12657
+ variables
12658
+ });
12659
+ }
12660
+
12554
12661
  function GeneralGroup(field, editField, getService) {
12555
12662
  const entries = [...IdEntry({
12556
12663
  field,
@@ -12617,6 +12724,12 @@ function GeneralGroup(field, editField, getService) {
12617
12724
  }), ...SelectEntries({
12618
12725
  field,
12619
12726
  editField
12727
+ }), ...AcceptEntry({
12728
+ field,
12729
+ editField
12730
+ }), ...MultipleEntry({
12731
+ field,
12732
+ editField
12620
12733
  }), ...DisabledEntry({
12621
12734
  field,
12622
12735
  editField