@bpmn-io/form-js-viewer 0.7.2 → 0.8.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/LICENSE +22 -22
- package/README.md +158 -158
- package/dist/assets/form-js.css +339 -238
- package/dist/index.cjs +688 -145
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +685 -147
- package/dist/index.es.js.map +1 -1
- package/dist/types/Form.d.ts +136 -136
- package/dist/types/core/EventBus.d.ts +1 -1
- package/dist/types/core/FormFieldRegistry.d.ts +17 -17
- package/dist/types/core/Validator.d.ts +7 -7
- package/dist/types/core/index.d.ts +16 -16
- package/dist/types/import/Importer.d.ts +43 -43
- package/dist/types/import/index.d.ts +5 -5
- package/dist/types/index.d.ts +18 -18
- package/dist/types/render/FormFields.d.ts +5 -5
- package/dist/types/render/Renderer.d.ts +23 -23
- package/dist/types/render/components/Description.d.ts +1 -1
- package/dist/types/render/components/Errors.d.ts +1 -1
- package/dist/types/render/components/FormComponent.d.ts +1 -1
- package/dist/types/render/components/FormField.d.ts +1 -1
- package/dist/types/render/components/Label.d.ts +1 -1
- package/dist/types/render/components/PoweredBy.d.ts +1 -1
- package/dist/types/render/components/Sanitizer.d.ts +7 -7
- package/dist/types/render/components/Util.d.ts +6 -4
- package/dist/types/render/components/form-fields/Button.d.ts +11 -11
- package/dist/types/render/components/form-fields/Checkbox.d.ts +13 -10
- package/dist/types/render/components/form-fields/Checklist.d.ts +12 -0
- package/dist/types/render/components/form-fields/Default.d.ts +9 -9
- package/dist/types/render/components/form-fields/Number.d.ts +13 -10
- package/dist/types/render/components/form-fields/Radio.d.ts +12 -15
- package/dist/types/render/components/form-fields/Select.d.ts +12 -15
- package/dist/types/render/components/form-fields/Taglist.d.ts +12 -0
- package/dist/types/render/components/form-fields/Text.d.ts +10 -10
- package/dist/types/render/components/form-fields/Textfield.d.ts +13 -10
- package/dist/types/render/components/form-fields/parts/DropdownList.d.ts +1 -0
- package/dist/types/render/components/index.d.ts +13 -11
- package/dist/types/render/context/FormContext.d.ts +12 -12
- package/dist/types/render/context/FormRenderContext.d.ts +6 -6
- package/dist/types/render/context/index.d.ts +2 -2
- package/dist/types/render/hooks/useKeyDownAction.d.ts +1 -0
- package/dist/types/render/hooks/useService.d.ts +1 -1
- package/dist/types/render/hooks/useValuesAsync.d.ts +28 -0
- package/dist/types/render/index.d.ts +11 -11
- package/dist/types/types.d.ts +35 -35
- package/dist/types/util/form.d.ts +6 -6
- package/dist/types/util/index.d.ts +24 -16
- package/dist/types/util/injector.d.ts +2 -1
- package/package.json +6 -5
package/dist/index.es.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Ids from 'ids';
|
|
2
|
-
import { isArray, isFunction, isNumber, bind, assign, isNil, get, isUndefined, set, isString } from 'min-dash';
|
|
2
|
+
import { isArray, isFunction, isNumber, bind, assign, isNil, get, isUndefined, isObject, set, isString } from 'min-dash';
|
|
3
3
|
import snarkdown from '@bpmn-io/snarkdown';
|
|
4
4
|
import { jsx, jsxs } from 'preact/jsx-runtime';
|
|
5
|
-
import { useContext, useState, useCallback } from 'preact/hooks';
|
|
5
|
+
import { useContext, useState, useEffect, useRef, useMemo, useCallback } from 'preact/hooks';
|
|
6
6
|
import { createContext, createElement, Fragment, render } from 'preact';
|
|
7
|
+
import React, { createPortal } from 'preact/compat';
|
|
8
|
+
import classNames from 'classnames';
|
|
7
9
|
import Markup from 'preact-markup';
|
|
8
|
-
import { createPortal } from 'preact/compat';
|
|
9
10
|
import { Injector } from 'didi';
|
|
10
11
|
|
|
11
12
|
var FN_REF = '__fn';
|
|
@@ -588,52 +589,15 @@ class FormFieldRegistry {
|
|
|
588
589
|
FormFieldRegistry.$inject = ['eventBus'];
|
|
589
590
|
|
|
590
591
|
function createInjector(bootstrapModules) {
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
function hasModule(module) {
|
|
595
|
-
return modules.includes(module);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function addModule(module) {
|
|
599
|
-
modules.push(module);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function visit(module) {
|
|
603
|
-
if (hasModule(module)) {
|
|
604
|
-
return;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
(module.__depends__ || []).forEach(visit);
|
|
608
|
-
|
|
609
|
-
if (hasModule(module)) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
addModule(module);
|
|
614
|
-
(module.__init__ || []).forEach(function (component) {
|
|
615
|
-
components.push(component);
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
bootstrapModules.forEach(visit);
|
|
620
|
-
const injector = new Injector(modules);
|
|
621
|
-
components.forEach(function (component) {
|
|
622
|
-
try {
|
|
623
|
-
injector[typeof component === 'string' ? 'get' : 'invoke'](component);
|
|
624
|
-
} catch (err) {
|
|
625
|
-
console.error('Failed to instantiate component');
|
|
626
|
-
console.error(err.stack);
|
|
627
|
-
throw err;
|
|
628
|
-
}
|
|
629
|
-
});
|
|
592
|
+
const injector = new Injector(bootstrapModules);
|
|
593
|
+
injector.init();
|
|
630
594
|
return injector;
|
|
631
595
|
}
|
|
632
596
|
|
|
633
|
-
/**
|
|
634
|
-
* @param {string?} prefix
|
|
635
|
-
*
|
|
636
|
-
* @returns Element
|
|
597
|
+
/**
|
|
598
|
+
* @param {string?} prefix
|
|
599
|
+
*
|
|
600
|
+
* @returns Element
|
|
637
601
|
*/
|
|
638
602
|
function createFormContainer(prefix = 'fjs') {
|
|
639
603
|
const container = document.createElement('div');
|
|
@@ -679,41 +643,76 @@ function generateIndexForType(type) {
|
|
|
679
643
|
function generateIdForType(type) {
|
|
680
644
|
return `${type}${generateIndexForType(type)}`;
|
|
681
645
|
}
|
|
682
|
-
/**
|
|
683
|
-
* @template T
|
|
684
|
-
* @param {T} data
|
|
685
|
-
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
686
|
-
* @return {T}
|
|
646
|
+
/**
|
|
647
|
+
* @template T
|
|
648
|
+
* @param {T} data
|
|
649
|
+
* @param {(this: any, key: string, value: any) => any} [replacer]
|
|
650
|
+
* @return {T}
|
|
687
651
|
*/
|
|
688
652
|
|
|
689
653
|
function clone(data, replacer) {
|
|
690
654
|
return JSON.parse(JSON.stringify(data, replacer));
|
|
691
655
|
}
|
|
656
|
+
/**
|
|
657
|
+
* Parse the schema for input variables a form might make use of
|
|
658
|
+
*
|
|
659
|
+
* @param {any} schema
|
|
660
|
+
*
|
|
661
|
+
* @return {string[]}
|
|
662
|
+
*/
|
|
663
|
+
|
|
664
|
+
function getSchemaVariables(schema) {
|
|
665
|
+
if (!schema.components) {
|
|
666
|
+
return [];
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return schema.components.reduce((variables, component) => {
|
|
670
|
+
const {
|
|
671
|
+
key,
|
|
672
|
+
valuesKey,
|
|
673
|
+
type
|
|
674
|
+
} = component;
|
|
675
|
+
|
|
676
|
+
if (['text', 'button'].includes(type)) {
|
|
677
|
+
return variables;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (key) {
|
|
681
|
+
variables = [...variables, key];
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (valuesKey && !variables.includes(valuesKey)) {
|
|
685
|
+
variables = [...variables, valuesKey];
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return variables;
|
|
689
|
+
}, []);
|
|
690
|
+
}
|
|
692
691
|
|
|
693
692
|
class Importer {
|
|
694
|
-
/**
|
|
695
|
-
* @constructor
|
|
696
|
-
* @param { import('../core').FormFieldRegistry } formFieldRegistry
|
|
697
|
-
* @param { import('../render/FormFields').default } formFields
|
|
693
|
+
/**
|
|
694
|
+
* @constructor
|
|
695
|
+
* @param { import('../core').FormFieldRegistry } formFieldRegistry
|
|
696
|
+
* @param { import('../render/FormFields').default } formFields
|
|
698
697
|
*/
|
|
699
698
|
constructor(formFieldRegistry, formFields) {
|
|
700
699
|
this._formFieldRegistry = formFieldRegistry;
|
|
701
700
|
this._formFields = formFields;
|
|
702
701
|
}
|
|
703
|
-
/**
|
|
704
|
-
* Import schema adding `id`, `_parent` and `_path`
|
|
705
|
-
* information to each field and adding it to the
|
|
706
|
-
* form field registry.
|
|
707
|
-
*
|
|
708
|
-
* @param {any} schema
|
|
709
|
-
* @param {any} [data]
|
|
710
|
-
*
|
|
711
|
-
* @return { { warnings: Array<any>, schema: any, data: any } }
|
|
702
|
+
/**
|
|
703
|
+
* Import schema adding `id`, `_parent` and `_path`
|
|
704
|
+
* information to each field and adding it to the
|
|
705
|
+
* form field registry.
|
|
706
|
+
*
|
|
707
|
+
* @param {any} schema
|
|
708
|
+
* @param {any} [data]
|
|
709
|
+
*
|
|
710
|
+
* @return { { warnings: Array<any>, schema: any, data: any } }
|
|
712
711
|
*/
|
|
713
712
|
|
|
714
713
|
|
|
715
714
|
importSchema(schema, data = {}) {
|
|
716
|
-
// TODO: Add warnings
|
|
715
|
+
// TODO: Add warnings - https://github.com/bpmn-io/form-js/issues/289
|
|
717
716
|
const warnings = [];
|
|
718
717
|
|
|
719
718
|
try {
|
|
@@ -729,11 +728,11 @@ class Importer {
|
|
|
729
728
|
throw err;
|
|
730
729
|
}
|
|
731
730
|
}
|
|
732
|
-
/**
|
|
733
|
-
* @param {any} formField
|
|
734
|
-
* @param {string} [parentId]
|
|
735
|
-
*
|
|
736
|
-
* @return {any} importedField
|
|
731
|
+
/**
|
|
732
|
+
* @param {any} formField
|
|
733
|
+
* @param {string} [parentId]
|
|
734
|
+
*
|
|
735
|
+
* @return {any} importedField
|
|
737
736
|
*/
|
|
738
737
|
|
|
739
738
|
|
|
@@ -795,10 +794,10 @@ class Importer {
|
|
|
795
794
|
this.importFormField(component, parentId);
|
|
796
795
|
});
|
|
797
796
|
}
|
|
798
|
-
/**
|
|
799
|
-
* @param {Object} data
|
|
800
|
-
*
|
|
801
|
-
* @return {Object} importedData
|
|
797
|
+
/**
|
|
798
|
+
* @param {Object} data
|
|
799
|
+
*
|
|
800
|
+
* @return {Object} importedData
|
|
802
801
|
*/
|
|
803
802
|
|
|
804
803
|
|
|
@@ -807,19 +806,39 @@ class Importer {
|
|
|
807
806
|
const {
|
|
808
807
|
defaultValue,
|
|
809
808
|
_path,
|
|
810
|
-
type
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
809
|
+
type,
|
|
810
|
+
valuesKey
|
|
811
|
+
} = formField; // get values defined via valuesKey
|
|
812
|
+
|
|
813
|
+
if (valuesKey) {
|
|
814
|
+
importedData = { ...importedData,
|
|
815
|
+
[valuesKey]: get(data, [valuesKey])
|
|
816
|
+
};
|
|
817
|
+
} // try to get value from data
|
|
818
|
+
// if unavailable - try to get default value from form field
|
|
819
|
+
// if unavailable - get empty value from form field
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
if (_path) {
|
|
823
|
+
const fieldImplementation = this._formFields.get(type);
|
|
824
|
+
|
|
825
|
+
let valueData = get(data, _path);
|
|
826
|
+
|
|
827
|
+
if (!isUndefined(valueData) && fieldImplementation.sanitizeValue) {
|
|
828
|
+
valueData = fieldImplementation.sanitizeValue({
|
|
829
|
+
formField,
|
|
830
|
+
data,
|
|
831
|
+
value: valueData
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const initialFieldValue = !isUndefined(valueData) ? valueData : !isUndefined(defaultValue) ? defaultValue : fieldImplementation.emptyValue;
|
|
836
|
+
importedData = { ...importedData,
|
|
837
|
+
[_path[0]]: initialFieldValue
|
|
838
|
+
};
|
|
839
|
+
}
|
|
819
840
|
|
|
820
|
-
return
|
|
821
|
-
[_path[0]]: get(data, _path, isUndefined(defaultValue) ? this._formFields.get(type).emptyValue : defaultValue)
|
|
822
|
-
};
|
|
841
|
+
return importedData;
|
|
823
842
|
}, {});
|
|
824
843
|
}
|
|
825
844
|
|
|
@@ -839,11 +858,11 @@ const ALLOWED_URI_PATTERN = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^
|
|
|
839
858
|
const ATTR_WHITESPACE_PATTERN = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g; // eslint-disable-line no-control-regex
|
|
840
859
|
|
|
841
860
|
const FORM_ELEMENT = document.createElement('form');
|
|
842
|
-
/**
|
|
843
|
-
* Sanitize a HTML string and return the cleaned, safe version.
|
|
844
|
-
*
|
|
845
|
-
* @param {string} html
|
|
846
|
-
* @return {string}
|
|
861
|
+
/**
|
|
862
|
+
* Sanitize a HTML string and return the cleaned, safe version.
|
|
863
|
+
*
|
|
864
|
+
* @param {string} html
|
|
865
|
+
* @return {string}
|
|
847
866
|
*/
|
|
848
867
|
|
|
849
868
|
function sanitizeHTML(html) {
|
|
@@ -862,15 +881,15 @@ function sanitizeHTML(html) {
|
|
|
862
881
|
return '';
|
|
863
882
|
}
|
|
864
883
|
}
|
|
865
|
-
/**
|
|
866
|
-
* Recursively sanitize a HTML node, potentially
|
|
867
|
-
* removing it, its children or attributes.
|
|
868
|
-
*
|
|
869
|
-
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
870
|
-
* and https://github.com/cure53/DOMPurify. Simplified
|
|
871
|
-
* for our use-case.
|
|
872
|
-
*
|
|
873
|
-
* @param {Element} node
|
|
884
|
+
/**
|
|
885
|
+
* Recursively sanitize a HTML node, potentially
|
|
886
|
+
* removing it, its children or attributes.
|
|
887
|
+
*
|
|
888
|
+
* Inspired by https://github.com/developit/snarkdown/issues/70
|
|
889
|
+
* and https://github.com/cure53/DOMPurify. Simplified
|
|
890
|
+
* for our use-case.
|
|
891
|
+
*
|
|
892
|
+
* @param {Element} node
|
|
874
893
|
*/
|
|
875
894
|
|
|
876
895
|
function sanitizeNode(node) {
|
|
@@ -917,13 +936,13 @@ function sanitizeNode(node) {
|
|
|
917
936
|
node.childNodes[i]);
|
|
918
937
|
}
|
|
919
938
|
}
|
|
920
|
-
/**
|
|
921
|
-
* Validates attributes for validity.
|
|
922
|
-
*
|
|
923
|
-
* @param {string} lcTag
|
|
924
|
-
* @param {string} lcName
|
|
925
|
-
* @param {string} value
|
|
926
|
-
* @return {boolean}
|
|
939
|
+
/**
|
|
940
|
+
* Validates attributes for validity.
|
|
941
|
+
*
|
|
942
|
+
* @param {string} lcTag
|
|
943
|
+
* @param {string} lcName
|
|
944
|
+
* @param {string} value
|
|
945
|
+
* @return {boolean}
|
|
927
946
|
*/
|
|
928
947
|
|
|
929
948
|
|
|
@@ -979,8 +998,48 @@ function safeMarkdown(markdown) {
|
|
|
979
998
|
const html = markdownToHTML(markdown);
|
|
980
999
|
return sanitizeHTML(html);
|
|
981
1000
|
}
|
|
1001
|
+
function sanitizeSingleSelectValue(options) {
|
|
1002
|
+
const {
|
|
1003
|
+
formField,
|
|
1004
|
+
data,
|
|
1005
|
+
value
|
|
1006
|
+
} = options;
|
|
1007
|
+
const {
|
|
1008
|
+
valuesKey,
|
|
1009
|
+
values
|
|
1010
|
+
} = formField;
|
|
1011
|
+
|
|
1012
|
+
try {
|
|
1013
|
+
const validValues = (valuesKey ? get(data, [valuesKey]) : values).map(v => v.value) || [];
|
|
1014
|
+
return validValues.includes(value) ? value : null;
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
// use default value in case of formatting error
|
|
1017
|
+
// TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function sanitizeMultiSelectValue(options) {
|
|
1022
|
+
const {
|
|
1023
|
+
formField,
|
|
1024
|
+
data,
|
|
1025
|
+
value
|
|
1026
|
+
} = options;
|
|
1027
|
+
const {
|
|
1028
|
+
valuesKey,
|
|
1029
|
+
values
|
|
1030
|
+
} = formField;
|
|
1031
|
+
|
|
1032
|
+
try {
|
|
1033
|
+
const validValues = (valuesKey ? get(data, [valuesKey]) : values).map(v => v.value) || [];
|
|
1034
|
+
return value.filter(v => validValues.includes(v));
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
// use default value in case of formatting error
|
|
1037
|
+
// TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
|
|
1038
|
+
return [];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
982
1041
|
|
|
983
|
-
const type$
|
|
1042
|
+
const type$8 = 'button';
|
|
984
1043
|
function Button(props) {
|
|
985
1044
|
const {
|
|
986
1045
|
disabled,
|
|
@@ -990,7 +1049,7 @@ function Button(props) {
|
|
|
990
1049
|
action = 'submit'
|
|
991
1050
|
} = field;
|
|
992
1051
|
return jsx("div", {
|
|
993
|
-
class: formFieldClasses(type$
|
|
1052
|
+
class: formFieldClasses(type$8),
|
|
994
1053
|
children: jsx("button", {
|
|
995
1054
|
class: "fjs-button",
|
|
996
1055
|
type: action,
|
|
@@ -1007,7 +1066,7 @@ Button.create = function (options = {}) {
|
|
|
1007
1066
|
};
|
|
1008
1067
|
};
|
|
1009
1068
|
|
|
1010
|
-
Button.type = type$
|
|
1069
|
+
Button.type = type$8;
|
|
1011
1070
|
Button.label = 'Button';
|
|
1012
1071
|
Button.keyed = true;
|
|
1013
1072
|
|
|
@@ -1023,11 +1082,11 @@ const FormRenderContext = createContext({
|
|
|
1023
1082
|
}
|
|
1024
1083
|
});
|
|
1025
1084
|
|
|
1026
|
-
/**
|
|
1027
|
-
* @param {string} type
|
|
1028
|
-
* @param {boolean} [strict]
|
|
1029
|
-
*
|
|
1030
|
-
* @returns {any}
|
|
1085
|
+
/**
|
|
1086
|
+
* @param {string} type
|
|
1087
|
+
* @param {boolean} [strict]
|
|
1088
|
+
*
|
|
1089
|
+
* @returns {any}
|
|
1031
1090
|
*/
|
|
1032
1091
|
|
|
1033
1092
|
function getService(type, strict) {}
|
|
@@ -1089,7 +1148,7 @@ function Label(props) {
|
|
|
1089
1148
|
});
|
|
1090
1149
|
}
|
|
1091
1150
|
|
|
1092
|
-
const type$
|
|
1151
|
+
const type$7 = 'checkbox';
|
|
1093
1152
|
function Checkbox(props) {
|
|
1094
1153
|
const {
|
|
1095
1154
|
disabled,
|
|
@@ -1116,7 +1175,7 @@ function Checkbox(props) {
|
|
|
1116
1175
|
formId
|
|
1117
1176
|
} = useContext(FormContext);
|
|
1118
1177
|
return jsxs("div", {
|
|
1119
|
-
class: formFieldClasses(type$
|
|
1178
|
+
class: formFieldClasses(type$7, errors),
|
|
1120
1179
|
children: [jsx(Label, {
|
|
1121
1180
|
id: prefixId(id, formId),
|
|
1122
1181
|
label: label,
|
|
@@ -1142,11 +1201,15 @@ Checkbox.create = function (options = {}) {
|
|
|
1142
1201
|
};
|
|
1143
1202
|
};
|
|
1144
1203
|
|
|
1145
|
-
Checkbox.type = type$
|
|
1204
|
+
Checkbox.type = type$7;
|
|
1146
1205
|
Checkbox.label = 'Checkbox';
|
|
1147
1206
|
Checkbox.keyed = true;
|
|
1148
1207
|
Checkbox.emptyValue = false;
|
|
1149
1208
|
|
|
1209
|
+
Checkbox.sanitizeValue = ({
|
|
1210
|
+
value
|
|
1211
|
+
}) => value === true;
|
|
1212
|
+
|
|
1150
1213
|
function useService (type, strict) {
|
|
1151
1214
|
const {
|
|
1152
1215
|
getService
|
|
@@ -1154,6 +1217,153 @@ function useService (type, strict) {
|
|
|
1154
1217
|
return getService(type, strict);
|
|
1155
1218
|
}
|
|
1156
1219
|
|
|
1220
|
+
/**
|
|
1221
|
+
* @enum { String }
|
|
1222
|
+
*/
|
|
1223
|
+
|
|
1224
|
+
const LOAD_STATES = {
|
|
1225
|
+
LOADING: 'loading',
|
|
1226
|
+
LOADED: 'loaded',
|
|
1227
|
+
ERROR: 'error'
|
|
1228
|
+
};
|
|
1229
|
+
/**
|
|
1230
|
+
* @typedef {Object} ValuesGetter
|
|
1231
|
+
* @property {Object[]} values - The values data
|
|
1232
|
+
* @property {(LOAD_STATES)} state - The values data's loading state, to use for conditional rendering
|
|
1233
|
+
*/
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* A hook to load values for single and multiselect components.
|
|
1237
|
+
*
|
|
1238
|
+
* @param {Object} field - The form field to handle values for
|
|
1239
|
+
* @return {ValuesGetter} valuesGetter - A values getter object providing loading state and values
|
|
1240
|
+
*/
|
|
1241
|
+
|
|
1242
|
+
function useValuesAsync (field) {
|
|
1243
|
+
const {
|
|
1244
|
+
valuesKey,
|
|
1245
|
+
values: staticValues
|
|
1246
|
+
} = field;
|
|
1247
|
+
const [valuesGetter, setValuesGetter] = useState({
|
|
1248
|
+
values: [],
|
|
1249
|
+
error: undefined,
|
|
1250
|
+
state: LOAD_STATES.LOADING
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
const initialData = useService('form')._getState().initialData;
|
|
1254
|
+
|
|
1255
|
+
useEffect(() => {
|
|
1256
|
+
let values = [];
|
|
1257
|
+
|
|
1258
|
+
if (valuesKey !== undefined) {
|
|
1259
|
+
const keyedValues = (initialData || {})[valuesKey];
|
|
1260
|
+
|
|
1261
|
+
if (keyedValues && Array.isArray(keyedValues)) {
|
|
1262
|
+
values = keyedValues;
|
|
1263
|
+
}
|
|
1264
|
+
} else if (staticValues !== undefined) {
|
|
1265
|
+
values = Array.isArray(staticValues) ? staticValues : [];
|
|
1266
|
+
} else {
|
|
1267
|
+
setValuesGetter(getErrorState('No values source defined in the form definition'));
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
setValuesGetter(buildLoadedState(values));
|
|
1272
|
+
}, [valuesKey, staticValues, initialData]);
|
|
1273
|
+
return valuesGetter;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
const getErrorState = error => ({
|
|
1277
|
+
values: [],
|
|
1278
|
+
error,
|
|
1279
|
+
state: LOAD_STATES.ERROR
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
const buildLoadedState = values => ({
|
|
1283
|
+
values,
|
|
1284
|
+
error: undefined,
|
|
1285
|
+
state: LOAD_STATES.LOADED
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
const type$6 = 'checklist';
|
|
1289
|
+
function Checklist(props) {
|
|
1290
|
+
const {
|
|
1291
|
+
disabled,
|
|
1292
|
+
errors = [],
|
|
1293
|
+
field,
|
|
1294
|
+
value = []
|
|
1295
|
+
} = props;
|
|
1296
|
+
const {
|
|
1297
|
+
description,
|
|
1298
|
+
id,
|
|
1299
|
+
label
|
|
1300
|
+
} = field;
|
|
1301
|
+
|
|
1302
|
+
const toggleCheckbox = v => {
|
|
1303
|
+
let newValue = [...value];
|
|
1304
|
+
|
|
1305
|
+
if (!newValue.includes(v)) {
|
|
1306
|
+
newValue.push(v);
|
|
1307
|
+
} else {
|
|
1308
|
+
newValue = newValue.filter(x => x != v);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
props.onChange({
|
|
1312
|
+
field,
|
|
1313
|
+
value: newValue
|
|
1314
|
+
});
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
const {
|
|
1318
|
+
state: loadState,
|
|
1319
|
+
values: options
|
|
1320
|
+
} = useValuesAsync(field);
|
|
1321
|
+
const {
|
|
1322
|
+
formId
|
|
1323
|
+
} = useContext(FormContext);
|
|
1324
|
+
return jsxs("div", {
|
|
1325
|
+
class: formFieldClasses(type$6, errors),
|
|
1326
|
+
children: [jsx(Label, {
|
|
1327
|
+
label: label
|
|
1328
|
+
}), loadState == LOAD_STATES.LOADED && options.map((v, index) => {
|
|
1329
|
+
return jsx(Label, {
|
|
1330
|
+
id: prefixId(`${id}-${index}`, formId),
|
|
1331
|
+
label: v.label,
|
|
1332
|
+
required: false,
|
|
1333
|
+
children: jsx("input", {
|
|
1334
|
+
checked: value.includes(v.value),
|
|
1335
|
+
class: "fjs-input",
|
|
1336
|
+
disabled: disabled,
|
|
1337
|
+
id: prefixId(`${id}-${index}`, formId),
|
|
1338
|
+
type: "checkbox",
|
|
1339
|
+
onClick: () => toggleCheckbox(v.value)
|
|
1340
|
+
})
|
|
1341
|
+
}, `${id}-${index}`);
|
|
1342
|
+
}), jsx(Description, {
|
|
1343
|
+
description: description
|
|
1344
|
+
}), jsx(Errors, {
|
|
1345
|
+
errors: errors
|
|
1346
|
+
})]
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
Checklist.create = function (options = {}) {
|
|
1351
|
+
if (options.valuesKey) return options;
|
|
1352
|
+
return {
|
|
1353
|
+
values: [{
|
|
1354
|
+
label: 'Value',
|
|
1355
|
+
value: 'value'
|
|
1356
|
+
}],
|
|
1357
|
+
...options
|
|
1358
|
+
};
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
Checklist.type = type$6;
|
|
1362
|
+
Checklist.label = 'Checklist';
|
|
1363
|
+
Checklist.keyed = true;
|
|
1364
|
+
Checklist.emptyValue = [];
|
|
1365
|
+
Checklist.sanitizeValue = sanitizeMultiSelectValue;
|
|
1366
|
+
|
|
1157
1367
|
const noop$1 = () => false;
|
|
1158
1368
|
|
|
1159
1369
|
function FormField(props) {
|
|
@@ -1229,10 +1439,10 @@ Default.create = function (options = {}) {
|
|
|
1229
1439
|
Default.type = 'default';
|
|
1230
1440
|
Default.keyed = false;
|
|
1231
1441
|
|
|
1232
|
-
/**
|
|
1233
|
-
* This file must not be changed or exchanged.
|
|
1234
|
-
*
|
|
1235
|
-
* @see http://bpmn.io/license for more information.
|
|
1442
|
+
/**
|
|
1443
|
+
* This file must not be changed or exchanged.
|
|
1444
|
+
*
|
|
1445
|
+
* @see http://bpmn.io/license for more information.
|
|
1236
1446
|
*/
|
|
1237
1447
|
|
|
1238
1448
|
function Logo() {
|
|
@@ -1362,7 +1572,7 @@ function FormComponent(props) {
|
|
|
1362
1572
|
});
|
|
1363
1573
|
}
|
|
1364
1574
|
|
|
1365
|
-
const type$
|
|
1575
|
+
const type$5 = 'number';
|
|
1366
1576
|
function Number(props) {
|
|
1367
1577
|
const {
|
|
1368
1578
|
disabled,
|
|
@@ -1383,10 +1593,11 @@ function Number(props) {
|
|
|
1383
1593
|
const onChange = ({
|
|
1384
1594
|
target
|
|
1385
1595
|
}) => {
|
|
1386
|
-
const parsedValue = parseInt(target.value, 10);
|
|
1387
1596
|
props.onChange({
|
|
1388
1597
|
field,
|
|
1389
|
-
value:
|
|
1598
|
+
value: Number.sanitizeValue({
|
|
1599
|
+
value: target.value
|
|
1600
|
+
})
|
|
1390
1601
|
});
|
|
1391
1602
|
};
|
|
1392
1603
|
|
|
@@ -1394,7 +1605,7 @@ function Number(props) {
|
|
|
1394
1605
|
formId
|
|
1395
1606
|
} = useContext(FormContext);
|
|
1396
1607
|
return jsxs("div", {
|
|
1397
|
-
class: formFieldClasses(type$
|
|
1608
|
+
class: formFieldClasses(type$5, errors),
|
|
1398
1609
|
children: [jsx(Label, {
|
|
1399
1610
|
id: prefixId(id, formId),
|
|
1400
1611
|
label: label,
|
|
@@ -1419,12 +1630,19 @@ Number.create = function (options = {}) {
|
|
|
1419
1630
|
};
|
|
1420
1631
|
};
|
|
1421
1632
|
|
|
1422
|
-
Number.
|
|
1633
|
+
Number.sanitizeValue = ({
|
|
1634
|
+
value
|
|
1635
|
+
}) => {
|
|
1636
|
+
const parsedValue = parseInt(value, 10);
|
|
1637
|
+
return isNaN(parsedValue) ? null : parsedValue;
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
Number.type = type$5;
|
|
1423
1641
|
Number.keyed = true;
|
|
1424
1642
|
Number.label = 'Number';
|
|
1425
1643
|
Number.emptyValue = null;
|
|
1426
1644
|
|
|
1427
|
-
const type$
|
|
1645
|
+
const type$4 = 'radio';
|
|
1428
1646
|
function Radio(props) {
|
|
1429
1647
|
const {
|
|
1430
1648
|
disabled,
|
|
@@ -1436,8 +1654,7 @@ function Radio(props) {
|
|
|
1436
1654
|
description,
|
|
1437
1655
|
id,
|
|
1438
1656
|
label,
|
|
1439
|
-
validate = {}
|
|
1440
|
-
values
|
|
1657
|
+
validate = {}
|
|
1441
1658
|
} = field;
|
|
1442
1659
|
const {
|
|
1443
1660
|
required
|
|
@@ -1450,26 +1667,30 @@ function Radio(props) {
|
|
|
1450
1667
|
});
|
|
1451
1668
|
};
|
|
1452
1669
|
|
|
1670
|
+
const {
|
|
1671
|
+
state: loadState,
|
|
1672
|
+
values: options
|
|
1673
|
+
} = useValuesAsync(field);
|
|
1453
1674
|
const {
|
|
1454
1675
|
formId
|
|
1455
1676
|
} = useContext(FormContext);
|
|
1456
1677
|
return jsxs("div", {
|
|
1457
|
-
class: formFieldClasses(type$
|
|
1678
|
+
class: formFieldClasses(type$4, errors),
|
|
1458
1679
|
children: [jsx(Label, {
|
|
1459
1680
|
label: label,
|
|
1460
1681
|
required: required
|
|
1461
|
-
}),
|
|
1682
|
+
}), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
|
|
1462
1683
|
return jsx(Label, {
|
|
1463
1684
|
id: prefixId(`${id}-${index}`, formId),
|
|
1464
|
-
label:
|
|
1685
|
+
label: option.label,
|
|
1465
1686
|
required: false,
|
|
1466
1687
|
children: jsx("input", {
|
|
1467
|
-
checked:
|
|
1688
|
+
checked: option.value === value,
|
|
1468
1689
|
class: "fjs-input",
|
|
1469
1690
|
disabled: disabled,
|
|
1470
1691
|
id: prefixId(`${id}-${index}`, formId),
|
|
1471
1692
|
type: "radio",
|
|
1472
|
-
onClick: () => onChange(
|
|
1693
|
+
onClick: () => onChange(option.value)
|
|
1473
1694
|
})
|
|
1474
1695
|
}, `${id}-${index}`);
|
|
1475
1696
|
}), jsx(Description, {
|
|
@@ -1481,6 +1702,7 @@ function Radio(props) {
|
|
|
1481
1702
|
}
|
|
1482
1703
|
|
|
1483
1704
|
Radio.create = function (options = {}) {
|
|
1705
|
+
if (options.valuesKey) return options;
|
|
1484
1706
|
return {
|
|
1485
1707
|
values: [{
|
|
1486
1708
|
label: 'Value',
|
|
@@ -1490,12 +1712,13 @@ Radio.create = function (options = {}) {
|
|
|
1490
1712
|
};
|
|
1491
1713
|
};
|
|
1492
1714
|
|
|
1493
|
-
Radio.type = type$
|
|
1715
|
+
Radio.type = type$4;
|
|
1494
1716
|
Radio.label = 'Radio';
|
|
1495
1717
|
Radio.keyed = true;
|
|
1496
1718
|
Radio.emptyValue = null;
|
|
1719
|
+
Radio.sanitizeValue = sanitizeSingleSelectValue;
|
|
1497
1720
|
|
|
1498
|
-
const type$
|
|
1721
|
+
const type$3 = 'select';
|
|
1499
1722
|
function Select(props) {
|
|
1500
1723
|
const {
|
|
1501
1724
|
disabled,
|
|
@@ -1507,8 +1730,7 @@ function Select(props) {
|
|
|
1507
1730
|
description,
|
|
1508
1731
|
id,
|
|
1509
1732
|
label,
|
|
1510
|
-
validate = {}
|
|
1511
|
-
values
|
|
1733
|
+
validate = {}
|
|
1512
1734
|
} = field;
|
|
1513
1735
|
const {
|
|
1514
1736
|
required
|
|
@@ -1523,11 +1745,15 @@ function Select(props) {
|
|
|
1523
1745
|
});
|
|
1524
1746
|
};
|
|
1525
1747
|
|
|
1748
|
+
const {
|
|
1749
|
+
state: loadState,
|
|
1750
|
+
values: options
|
|
1751
|
+
} = useValuesAsync(field);
|
|
1526
1752
|
const {
|
|
1527
1753
|
formId
|
|
1528
1754
|
} = useContext(FormContext);
|
|
1529
1755
|
return jsxs("div", {
|
|
1530
|
-
class: formFieldClasses(type$
|
|
1756
|
+
class: formFieldClasses(type$3, errors),
|
|
1531
1757
|
children: [jsx(Label, {
|
|
1532
1758
|
id: prefixId(id, formId),
|
|
1533
1759
|
label: label,
|
|
@@ -1540,10 +1766,10 @@ function Select(props) {
|
|
|
1540
1766
|
value: value || '',
|
|
1541
1767
|
children: [jsx("option", {
|
|
1542
1768
|
value: ""
|
|
1543
|
-
}),
|
|
1769
|
+
}), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
|
|
1544
1770
|
return jsx("option", {
|
|
1545
|
-
value:
|
|
1546
|
-
children:
|
|
1771
|
+
value: option.value,
|
|
1772
|
+
children: option.label
|
|
1547
1773
|
}, `${id}-${index}`);
|
|
1548
1774
|
})]
|
|
1549
1775
|
}), jsx(Description, {
|
|
@@ -1555,6 +1781,7 @@ function Select(props) {
|
|
|
1555
1781
|
}
|
|
1556
1782
|
|
|
1557
1783
|
Select.create = function (options = {}) {
|
|
1784
|
+
if (options.valuesKey) return options;
|
|
1558
1785
|
return {
|
|
1559
1786
|
values: [{
|
|
1560
1787
|
label: 'Value',
|
|
@@ -1564,10 +1791,317 @@ Select.create = function (options = {}) {
|
|
|
1564
1791
|
};
|
|
1565
1792
|
};
|
|
1566
1793
|
|
|
1567
|
-
Select.type = type$
|
|
1794
|
+
Select.type = type$3;
|
|
1568
1795
|
Select.label = 'Select';
|
|
1569
1796
|
Select.keyed = true;
|
|
1570
1797
|
Select.emptyValue = null;
|
|
1798
|
+
Select.sanitizeValue = sanitizeSingleSelectValue;
|
|
1799
|
+
|
|
1800
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
1801
|
+
var CloseIcon = (({
|
|
1802
|
+
styles = {},
|
|
1803
|
+
...props
|
|
1804
|
+
}) => /*#__PURE__*/React.createElement("svg", _extends({
|
|
1805
|
+
width: "16",
|
|
1806
|
+
height: "16",
|
|
1807
|
+
fill: "none",
|
|
1808
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
1809
|
+
}, props), /*#__PURE__*/React.createElement("path", {
|
|
1810
|
+
fillRule: "evenodd",
|
|
1811
|
+
clipRule: "evenodd",
|
|
1812
|
+
d: "M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7z",
|
|
1813
|
+
fill: "#000"
|
|
1814
|
+
})));
|
|
1815
|
+
|
|
1816
|
+
function useKeyDownAction(targetKey, action, listenerElement = window) {
|
|
1817
|
+
function downHandler({
|
|
1818
|
+
key
|
|
1819
|
+
}) {
|
|
1820
|
+
if (key === targetKey) {
|
|
1821
|
+
action();
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
useEffect(() => {
|
|
1826
|
+
listenerElement.addEventListener('keydown', downHandler);
|
|
1827
|
+
return () => {
|
|
1828
|
+
listenerElement.removeEventListener('keydown', downHandler);
|
|
1829
|
+
};
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
const DEFAULT_LABEL_GETTER = value => value;
|
|
1834
|
+
|
|
1835
|
+
const NOOP = () => {};
|
|
1836
|
+
|
|
1837
|
+
function DropdownList(props) {
|
|
1838
|
+
const {
|
|
1839
|
+
keyEventsListener = window,
|
|
1840
|
+
values = [],
|
|
1841
|
+
getLabel = DEFAULT_LABEL_GETTER,
|
|
1842
|
+
onValueSelected = NOOP,
|
|
1843
|
+
height = 235,
|
|
1844
|
+
emptyListMessage = 'No results'
|
|
1845
|
+
} = props;
|
|
1846
|
+
const [mouseControl, setMouseControl] = useState(true);
|
|
1847
|
+
const [focusedValueIndex, setFocusedValueIndex] = useState(0);
|
|
1848
|
+
const dropdownContainer = useRef();
|
|
1849
|
+
const mouseScreenPos = useRef();
|
|
1850
|
+
const focusedItem = useMemo(() => values.length ? values[focusedValueIndex] : null, [focusedValueIndex, values]);
|
|
1851
|
+
const changeFocusedValueIndex = useCallback(delta => {
|
|
1852
|
+
setFocusedValueIndex(x => Math.min(Math.max(0, x + delta), values.length - 1));
|
|
1853
|
+
}, [values.length]);
|
|
1854
|
+
useEffect(() => {
|
|
1855
|
+
if (focusedValueIndex === 0) return;
|
|
1856
|
+
|
|
1857
|
+
if (!focusedValueIndex || !values.length) {
|
|
1858
|
+
setFocusedValueIndex(0);
|
|
1859
|
+
} else if (focusedValueIndex >= values.length) {
|
|
1860
|
+
setFocusedValueIndex(values.length - 1);
|
|
1861
|
+
}
|
|
1862
|
+
}, [focusedValueIndex, values.length]);
|
|
1863
|
+
useKeyDownAction('ArrowUp', () => {
|
|
1864
|
+
if (values.length) {
|
|
1865
|
+
changeFocusedValueIndex(-1);
|
|
1866
|
+
setMouseControl(false);
|
|
1867
|
+
}
|
|
1868
|
+
}, keyEventsListener);
|
|
1869
|
+
useKeyDownAction('ArrowDown', () => {
|
|
1870
|
+
if (values.length) {
|
|
1871
|
+
changeFocusedValueIndex(1);
|
|
1872
|
+
setMouseControl(false);
|
|
1873
|
+
}
|
|
1874
|
+
}, keyEventsListener);
|
|
1875
|
+
useKeyDownAction('Enter', () => {
|
|
1876
|
+
if (focusedItem) {
|
|
1877
|
+
onValueSelected(focusedItem);
|
|
1878
|
+
}
|
|
1879
|
+
}, keyEventsListener);
|
|
1880
|
+
useEffect(() => {
|
|
1881
|
+
const individualEntries = dropdownContainer.current.children;
|
|
1882
|
+
|
|
1883
|
+
if (individualEntries.length && !mouseControl) {
|
|
1884
|
+
individualEntries[focusedValueIndex].scrollIntoView({
|
|
1885
|
+
block: 'nearest',
|
|
1886
|
+
inline: 'nearest'
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
}, [focusedValueIndex, mouseControl]);
|
|
1890
|
+
|
|
1891
|
+
const mouseMove = (e, i) => {
|
|
1892
|
+
const userMoved = !mouseScreenPos.current || mouseScreenPos.current.x !== e.screenX && mouseScreenPos.current.y !== e.screenY;
|
|
1893
|
+
|
|
1894
|
+
if (userMoved) {
|
|
1895
|
+
mouseScreenPos.current = {
|
|
1896
|
+
x: e.screenX,
|
|
1897
|
+
y: e.screenY
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
if (!mouseControl) {
|
|
1901
|
+
setMouseControl(true);
|
|
1902
|
+
setFocusedValueIndex(i);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
|
|
1907
|
+
return jsxs("div", {
|
|
1908
|
+
ref: dropdownContainer,
|
|
1909
|
+
tabIndex: -1,
|
|
1910
|
+
class: "fjs-dropdownlist",
|
|
1911
|
+
style: {
|
|
1912
|
+
maxHeight: height
|
|
1913
|
+
},
|
|
1914
|
+
children: [!!values.length && values.map((v, i) => {
|
|
1915
|
+
return jsx("div", {
|
|
1916
|
+
class: 'fjs-dropdownlist-item' + (focusedValueIndex === i ? ' focused' : ''),
|
|
1917
|
+
onMouseMove: e => mouseMove(e, i),
|
|
1918
|
+
onMouseEnter: mouseControl ? () => setFocusedValueIndex(i) : undefined,
|
|
1919
|
+
onMouseDown: e => {
|
|
1920
|
+
e.preventDefault();
|
|
1921
|
+
onValueSelected(v);
|
|
1922
|
+
},
|
|
1923
|
+
children: getLabel(v)
|
|
1924
|
+
});
|
|
1925
|
+
}), !values.length && jsx("div", {
|
|
1926
|
+
class: "fjs-dropdownlist-empty",
|
|
1927
|
+
children: emptyListMessage
|
|
1928
|
+
})]
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
const type$2 = 'taglist';
|
|
1933
|
+
function Taglist(props) {
|
|
1934
|
+
const {
|
|
1935
|
+
disabled,
|
|
1936
|
+
errors = [],
|
|
1937
|
+
field,
|
|
1938
|
+
value: values = []
|
|
1939
|
+
} = props;
|
|
1940
|
+
const {
|
|
1941
|
+
description,
|
|
1942
|
+
id,
|
|
1943
|
+
label
|
|
1944
|
+
} = field;
|
|
1945
|
+
const {
|
|
1946
|
+
formId
|
|
1947
|
+
} = useContext(FormContext);
|
|
1948
|
+
const [filter, setFilter] = useState('');
|
|
1949
|
+
const [filteredOptions, setFilteredOptions] = useState([]);
|
|
1950
|
+
const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
|
|
1951
|
+
const [hasOptionsLeft, setHasOptionsLeft] = useState(true);
|
|
1952
|
+
const [isEscapeClosed, setIsEscapeClose] = useState(false);
|
|
1953
|
+
const searchbarRef = useRef();
|
|
1954
|
+
const {
|
|
1955
|
+
state: loadState,
|
|
1956
|
+
values: options
|
|
1957
|
+
} = useValuesAsync(field); // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
|
|
1958
|
+
|
|
1959
|
+
const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
|
|
1960
|
+
[o.value]: options[x]
|
|
1961
|
+
}))), [options]); // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
|
|
1962
|
+
|
|
1963
|
+
useEffect(() => {
|
|
1964
|
+
if (loadState === LOAD_STATES.LOADED) {
|
|
1965
|
+
setFilteredOptions(options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value)));
|
|
1966
|
+
} else {
|
|
1967
|
+
setFilteredOptions([]);
|
|
1968
|
+
}
|
|
1969
|
+
}, [filter, JSON.stringify(values), options, loadState]);
|
|
1970
|
+
useEffect(() => {
|
|
1971
|
+
setHasOptionsLeft(options.length > values.length);
|
|
1972
|
+
}, [options.length, values.length]);
|
|
1973
|
+
|
|
1974
|
+
const onFilterChange = ({
|
|
1975
|
+
target
|
|
1976
|
+
}) => {
|
|
1977
|
+
setIsEscapeClose(false);
|
|
1978
|
+
setFilter(target.value);
|
|
1979
|
+
};
|
|
1980
|
+
|
|
1981
|
+
const selectValue = value => {
|
|
1982
|
+
if (filter) {
|
|
1983
|
+
setFilter('');
|
|
1984
|
+
} // Ensure values cannot be double selected due to latency
|
|
1985
|
+
|
|
1986
|
+
|
|
1987
|
+
if (values.at(-1) === value) {
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
props.onChange({
|
|
1992
|
+
value: [...values, value],
|
|
1993
|
+
field
|
|
1994
|
+
});
|
|
1995
|
+
};
|
|
1996
|
+
|
|
1997
|
+
const deselectValue = value => {
|
|
1998
|
+
props.onChange({
|
|
1999
|
+
value: values.filter(v => v != value),
|
|
2000
|
+
field
|
|
2001
|
+
});
|
|
2002
|
+
};
|
|
2003
|
+
|
|
2004
|
+
const onInputKeyDown = e => {
|
|
2005
|
+
switch (e.key) {
|
|
2006
|
+
case 'ArrowUp':
|
|
2007
|
+
case 'ArrowDown':
|
|
2008
|
+
// We do not want the cursor to seek in the search field when we press up and down
|
|
2009
|
+
e.preventDefault();
|
|
2010
|
+
break;
|
|
2011
|
+
|
|
2012
|
+
case 'Backspace':
|
|
2013
|
+
if (!filter && values.length) {
|
|
2014
|
+
deselectValue(values[values.length - 1]);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
break;
|
|
2018
|
+
|
|
2019
|
+
case 'Escape':
|
|
2020
|
+
setIsEscapeClose(true);
|
|
2021
|
+
break;
|
|
2022
|
+
|
|
2023
|
+
case 'Enter':
|
|
2024
|
+
if (isEscapeClosed) {
|
|
2025
|
+
setIsEscapeClose(false);
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
|
|
2032
|
+
return jsxs("div", {
|
|
2033
|
+
class: formFieldClasses(type$2, errors),
|
|
2034
|
+
children: [jsx(Label, {
|
|
2035
|
+
label: label,
|
|
2036
|
+
id: prefixId(id, formId)
|
|
2037
|
+
}), jsxs("div", {
|
|
2038
|
+
class: classNames('fjs-taglist', {
|
|
2039
|
+
'disabled': disabled
|
|
2040
|
+
}),
|
|
2041
|
+
children: [!disabled && loadState === LOAD_STATES.LOADED && values.map(v => {
|
|
2042
|
+
return jsxs("div", {
|
|
2043
|
+
class: "fjs-taglist-tag",
|
|
2044
|
+
onMouseDown: e => e.preventDefault(),
|
|
2045
|
+
children: [jsx("span", {
|
|
2046
|
+
class: "fjs-taglist-tag-label",
|
|
2047
|
+
children: valueToOptionMap[v] ? valueToOptionMap[v].label : `unexpected value{${v}}`
|
|
2048
|
+
}), jsx("span", {
|
|
2049
|
+
class: "fjs-taglist-tag-remove",
|
|
2050
|
+
onMouseDown: () => deselectValue(v),
|
|
2051
|
+
children: jsx(CloseIcon, {})
|
|
2052
|
+
})]
|
|
2053
|
+
});
|
|
2054
|
+
}), jsx("input", {
|
|
2055
|
+
disabled: disabled,
|
|
2056
|
+
class: "fjs-taglist-input",
|
|
2057
|
+
ref: searchbarRef,
|
|
2058
|
+
id: prefixId(`${id}-search`, formId),
|
|
2059
|
+
onChange: onFilterChange,
|
|
2060
|
+
type: "text",
|
|
2061
|
+
value: filter,
|
|
2062
|
+
placeholder: 'Search',
|
|
2063
|
+
autoComplete: "off",
|
|
2064
|
+
onKeyDown: e => onInputKeyDown(e),
|
|
2065
|
+
onMouseDown: () => setIsEscapeClose(false),
|
|
2066
|
+
onFocus: () => setIsDropdownExpanded(true),
|
|
2067
|
+
onBlur: () => {
|
|
2068
|
+
setIsDropdownExpanded(false);
|
|
2069
|
+
setFilter('');
|
|
2070
|
+
}
|
|
2071
|
+
})]
|
|
2072
|
+
}), jsx("div", {
|
|
2073
|
+
class: "fjs-taglist-anchor",
|
|
2074
|
+
children: !disabled && loadState === LOAD_STATES.LOADED && isDropdownExpanded && !isEscapeClosed && jsx(DropdownList, {
|
|
2075
|
+
values: filteredOptions,
|
|
2076
|
+
getLabel: o => o.label,
|
|
2077
|
+
onValueSelected: o => selectValue(o.value),
|
|
2078
|
+
emptyListMessage: hasOptionsLeft ? 'No results' : 'All values selected',
|
|
2079
|
+
listenerElement: searchbarRef.current
|
|
2080
|
+
})
|
|
2081
|
+
}), jsx(Description, {
|
|
2082
|
+
description: description
|
|
2083
|
+
}), jsx(Errors, {
|
|
2084
|
+
errors: errors
|
|
2085
|
+
})]
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
Taglist.create = function (options = {}) {
|
|
2090
|
+
if (options.valuesKey) return options;
|
|
2091
|
+
return {
|
|
2092
|
+
values: [{
|
|
2093
|
+
label: 'Value',
|
|
2094
|
+
value: 'value'
|
|
2095
|
+
}],
|
|
2096
|
+
...options
|
|
2097
|
+
};
|
|
2098
|
+
};
|
|
2099
|
+
|
|
2100
|
+
Taglist.type = type$2;
|
|
2101
|
+
Taglist.label = 'Taglist';
|
|
2102
|
+
Taglist.keyed = true;
|
|
2103
|
+
Taglist.emptyValue = [];
|
|
2104
|
+
Taglist.sanitizeValue = sanitizeMultiSelectValue;
|
|
1571
2105
|
|
|
1572
2106
|
const type$1 = 'text';
|
|
1573
2107
|
function Text(props) {
|
|
@@ -1657,7 +2191,11 @@ Textfield.label = 'Text Field';
|
|
|
1657
2191
|
Textfield.keyed = true;
|
|
1658
2192
|
Textfield.emptyValue = '';
|
|
1659
2193
|
|
|
1660
|
-
|
|
2194
|
+
Textfield.sanitizeValue = ({
|
|
2195
|
+
value
|
|
2196
|
+
}) => isArray(value) || isObject(value) ? null : String(value);
|
|
2197
|
+
|
|
2198
|
+
const formFields = [Button, Checkbox, Checklist, Default, Number, Radio, Select, Taglist, Text, Textfield];
|
|
1661
2199
|
|
|
1662
2200
|
class FormFields {
|
|
1663
2201
|
constructor() {
|
|
@@ -2144,7 +2682,7 @@ class Form {
|
|
|
2144
2682
|
|
|
2145
2683
|
}
|
|
2146
2684
|
|
|
2147
|
-
const schemaVersion =
|
|
2685
|
+
const schemaVersion = 5;
|
|
2148
2686
|
/**
|
|
2149
2687
|
* @typedef { import('./types').CreateFormOptions } CreateFormOptions
|
|
2150
2688
|
*/
|
|
@@ -2169,5 +2707,5 @@ function createForm(options) {
|
|
|
2169
2707
|
});
|
|
2170
2708
|
}
|
|
2171
2709
|
|
|
2172
|
-
export { Button, Checkbox, Default, Form, FormComponent, FormContext, FormFieldRegistry, FormFields, FormRenderContext, Number, Radio, Select, Text, Textfield, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
|
|
2710
|
+
export { Button, Checkbox, Checklist, Default, Form, FormComponent, FormContext, FormFieldRegistry, FormFields, FormRenderContext, Number, Radio, Select, Taglist, Text, Textfield, clone, createForm, createFormContainer, createInjector, findErrors, formFields, generateIdForType, generateIndexForType, getSchemaVariables, isRequired, pathParse, pathStringify, pathsEqual, schemaVersion };
|
|
2173
2711
|
//# sourceMappingURL=index.es.js.map
|