@bpmn-io/form-js-viewer 1.25.0 → 1.26.1
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.cjs +178 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +178 -63
- package/dist/index.es.js.map +1 -1
- package/dist/types/core/FieldFactory.d.ts +10 -3
- package/dist/types/core/Validator.d.ts +2 -1
- package/dist/types/render/components/form-fields/DocumentPreview.d.ts +4 -0
- package/dist/types/render/components/form-fields/FilePicker.d.ts +1 -0
- package/dist/types/render/components/form-fields/Table.d.ts +3 -0
- package/package.json +6 -2
package/dist/index.es.js
CHANGED
|
@@ -16,6 +16,46 @@ import { parseExpression, parseUnaryTests, evaluate, unaryTest } from '@bpmn-io/
|
|
|
16
16
|
import { evaluate as evaluate$1, parseToSimpleTree } from 'feelers';
|
|
17
17
|
import { marked } from 'marked';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @typedef { {
|
|
21
|
+
* [key: string]: string;
|
|
22
|
+
* } } TranslateReplacements
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A simple translation stub to be used for multi-language support
|
|
27
|
+
* in diagrams. Can be easily replaced with a more sophisticated
|
|
28
|
+
* solution.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
*
|
|
32
|
+
* ```javascript
|
|
33
|
+
* // use it inside any diagram component by injecting `translate`.
|
|
34
|
+
*
|
|
35
|
+
* function MyService(translate) {
|
|
36
|
+
* alert(translate('HELLO {you}', { you: 'You!' }));
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @param {string} template to interpolate
|
|
41
|
+
* @param {TranslateReplacements} [replacements] a map with substitutes
|
|
42
|
+
*
|
|
43
|
+
* @return {string} the translated string
|
|
44
|
+
*/
|
|
45
|
+
function translate(template, replacements) {
|
|
46
|
+
replacements = replacements || {};
|
|
47
|
+
return template.replace(/{([^}]+)}/g, function (_, key) {
|
|
48
|
+
return replacements[key] || '{' + key + '}';
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @type { import('didi').ModuleDeclaration }
|
|
54
|
+
*/
|
|
55
|
+
var TranslateModule = {
|
|
56
|
+
translate: ['value', translate]
|
|
57
|
+
};
|
|
58
|
+
|
|
19
59
|
const getFlavouredFeelVariableNames = (feelString, feelFlavour = 'expression', options = {}) => {
|
|
20
60
|
const {
|
|
21
61
|
depth = 0,
|
|
@@ -3047,6 +3087,7 @@ function Datetime(props) {
|
|
|
3047
3087
|
const {
|
|
3048
3088
|
formId
|
|
3049
3089
|
} = useContext(FormContext);
|
|
3090
|
+
const translate = useService('translate');
|
|
3050
3091
|
|
|
3051
3092
|
/** @type {import("preact").RefObject<HTMLDivElement>} */
|
|
3052
3093
|
const dateTimeGroupRef = useRef();
|
|
@@ -3137,8 +3178,8 @@ function Datetime(props) {
|
|
|
3137
3178
|
const allErrors = useMemo(() => {
|
|
3138
3179
|
if (required || subtype !== DATETIME_SUBTYPES.DATETIME) return errors;
|
|
3139
3180
|
const isOnlyOneFieldSet = isValidDate(dateTime.date) && !isValidTime(dateTime.time) || !isValidDate(dateTime.date) && isValidTime(dateTime.time);
|
|
3140
|
-
return isOnlyOneFieldSet ? ['Date and time must both be entered.', ...errors] : errors;
|
|
3141
|
-
}, [required, subtype, dateTime, errors]);
|
|
3181
|
+
return isOnlyOneFieldSet ? [translate('Date and time must both be entered.'), ...errors] : errors;
|
|
3182
|
+
}, [required, subtype, dateTime, errors, translate]);
|
|
3142
3183
|
const setDate = useCallback(date => {
|
|
3143
3184
|
setDateTimeUpdateRequest(prev => prev ? {
|
|
3144
3185
|
...prev,
|
|
@@ -3300,6 +3341,7 @@ function IFrame(props) {
|
|
|
3300
3341
|
const evaluatedLabel = useSingleLineTemplateEvaluation(label, {
|
|
3301
3342
|
debug: true
|
|
3302
3343
|
});
|
|
3344
|
+
const translate = useService('translate');
|
|
3303
3345
|
const [sandbox, allow] = useSecurityAttributesMap(security);
|
|
3304
3346
|
const [iframeRefresh, setIframeRefresh] = useState(0);
|
|
3305
3347
|
|
|
@@ -3316,7 +3358,7 @@ function IFrame(props) {
|
|
|
3316
3358
|
htmlFor: domId,
|
|
3317
3359
|
label: evaluatedLabel
|
|
3318
3360
|
}), !evaluatedUrl && jsx(IFramePlaceholder, {
|
|
3319
|
-
text:
|
|
3361
|
+
text: translate('No content to show.')
|
|
3320
3362
|
}), evaluatedUrl && safeUrl && jsx("iframe", {
|
|
3321
3363
|
src: safeUrl,
|
|
3322
3364
|
title: evaluatedLabel,
|
|
@@ -3326,7 +3368,7 @@ function IFrame(props) {
|
|
|
3326
3368
|
sandbox: sandbox,
|
|
3327
3369
|
allow
|
|
3328
3370
|
}, 'iframe-' + iframeRefresh), evaluatedUrl && !safeUrl && jsx(IFramePlaceholder, {
|
|
3329
|
-
text: "External content couldn't be loaded."
|
|
3371
|
+
text: translate("External content couldn't be loaded.")
|
|
3330
3372
|
})]
|
|
3331
3373
|
});
|
|
3332
3374
|
}
|
|
@@ -3914,6 +3956,7 @@ function Numberfield(props) {
|
|
|
3914
3956
|
const {
|
|
3915
3957
|
required
|
|
3916
3958
|
} = validate;
|
|
3959
|
+
const translate = useService('translate');
|
|
3917
3960
|
|
|
3918
3961
|
/** @type {import("preact").RefObject<HTMLInputElement>} */
|
|
3919
3962
|
const inputRef = useRef();
|
|
@@ -4087,7 +4130,7 @@ function Numberfield(props) {
|
|
|
4087
4130
|
children: [jsx("button", {
|
|
4088
4131
|
type: "button",
|
|
4089
4132
|
class: "fjs-number-arrow-up",
|
|
4090
|
-
"aria-label":
|
|
4133
|
+
"aria-label": translate('Increment'),
|
|
4091
4134
|
onClick: () => increment(),
|
|
4092
4135
|
tabIndex: -1,
|
|
4093
4136
|
children: jsx(SvgAngelUp, {})
|
|
@@ -4096,7 +4139,7 @@ function Numberfield(props) {
|
|
|
4096
4139
|
}), jsx("button", {
|
|
4097
4140
|
type: "button",
|
|
4098
4141
|
class: "fjs-number-arrow-down",
|
|
4099
|
-
"aria-label":
|
|
4142
|
+
"aria-label": translate('Decrement'),
|
|
4100
4143
|
onClick: () => decrement(),
|
|
4101
4144
|
tabIndex: -1,
|
|
4102
4145
|
children: jsx(SvgAngelDown, {})
|
|
@@ -4779,6 +4822,7 @@ function Taglist(props) {
|
|
|
4779
4822
|
/** @type {import("preact").RefObject<HTMLInputElement>} */
|
|
4780
4823
|
const inputRef = useRef();
|
|
4781
4824
|
const eventBus = useService('eventBus');
|
|
4825
|
+
const translate = useService('translate');
|
|
4782
4826
|
const {
|
|
4783
4827
|
loadState,
|
|
4784
4828
|
options
|
|
@@ -4912,7 +4956,7 @@ function Taglist(props) {
|
|
|
4912
4956
|
htmlFor: domId
|
|
4913
4957
|
}), !disabled && !readonly && !!values.length && jsx(SkipLink, {
|
|
4914
4958
|
className: "fjs-taglist-skip-link",
|
|
4915
|
-
label:
|
|
4959
|
+
label: translate('Skip to search'),
|
|
4916
4960
|
onSkip: onSkipToSearch
|
|
4917
4961
|
}), jsxs("div", {
|
|
4918
4962
|
class: classNames('fjs-taglist', {
|
|
@@ -4933,7 +4977,7 @@ function Taglist(props) {
|
|
|
4933
4977
|
children: getLabelCorrelation(entry)
|
|
4934
4978
|
}), !disabled && !readonly && jsx("button", {
|
|
4935
4979
|
type: "button",
|
|
4936
|
-
title:
|
|
4980
|
+
title: translate('Remove tag'),
|
|
4937
4981
|
class: "fjs-taglist-tag-remove",
|
|
4938
4982
|
onFocus: onElementFocus,
|
|
4939
4983
|
onBlur: onElementBlur,
|
|
@@ -4951,7 +4995,7 @@ function Taglist(props) {
|
|
|
4951
4995
|
onChange: onInputChange,
|
|
4952
4996
|
type: "text",
|
|
4953
4997
|
value: filter,
|
|
4954
|
-
placeholder: disabled || readonly ? undefined : 'Search',
|
|
4998
|
+
placeholder: disabled || readonly ? undefined : translate('Search'),
|
|
4955
4999
|
autoComplete: "off",
|
|
4956
5000
|
onKeyDown: onInputKeyDown,
|
|
4957
5001
|
onMouseDown: () => setIsEscapeClose(false),
|
|
@@ -4967,7 +5011,7 @@ function Taglist(props) {
|
|
|
4967
5011
|
values: filteredOptions,
|
|
4968
5012
|
getLabel: option => option.label,
|
|
4969
5013
|
onValueSelected: option => selectValue(option.value),
|
|
4970
|
-
emptyListMessage: hasOptionsLeft ? 'No results' : 'All values selected',
|
|
5014
|
+
emptyListMessage: hasOptionsLeft ? translate('No results') : translate('All values selected'),
|
|
4971
5015
|
listenerElement: inputRef.current
|
|
4972
5016
|
})
|
|
4973
5017
|
}), jsx(Description, {
|
|
@@ -5570,6 +5614,8 @@ var SvgCaretRight = function SvgCaretRight(props) {
|
|
|
5570
5614
|
const type$2 = 'table';
|
|
5571
5615
|
|
|
5572
5616
|
/**
|
|
5617
|
+
* @typedef { import('diagram-js/lib/i18n/translate/translate').default } Translate
|
|
5618
|
+
*
|
|
5573
5619
|
* @typedef {('asc'|'desc')} Direction
|
|
5574
5620
|
*
|
|
5575
5621
|
* @typedef Sorting
|
|
@@ -5619,6 +5665,7 @@ function Table(props) {
|
|
|
5619
5665
|
const chunkedData = isNumber(rowCount) ? chunk(sortedData, rowCount) : [sortedData];
|
|
5620
5666
|
const [currentPage, setCurrentPage] = useState(0);
|
|
5621
5667
|
const currentChunk = chunkedData[currentPage] || [];
|
|
5668
|
+
const translate = useService('translate');
|
|
5622
5669
|
useEffect(() => {
|
|
5623
5670
|
setCurrentPage(0);
|
|
5624
5671
|
}, [rowCount, sortBy]);
|
|
@@ -5650,7 +5697,7 @@ function Table(props) {
|
|
|
5650
5697
|
class: classNames('fjs-table-middle-container', {
|
|
5651
5698
|
'fjs-table-empty': evaluatedColumns.length === 0
|
|
5652
5699
|
}),
|
|
5653
|
-
children: [evaluatedColumns.length === 0 ? 'Nothing to show.' : jsx("div", {
|
|
5700
|
+
children: [evaluatedColumns.length === 0 ? translate('Nothing to show.') : jsx("div", {
|
|
5654
5701
|
class: "fjs-table-inner-container",
|
|
5655
5702
|
children: jsxs("table", {
|
|
5656
5703
|
class: "fjs-table",
|
|
@@ -5675,7 +5722,7 @@ function Table(props) {
|
|
|
5675
5722
|
toggleSortBy(key);
|
|
5676
5723
|
}
|
|
5677
5724
|
},
|
|
5678
|
-
"aria-label": getHeaderAriaLabel(sortBy, key, displayLabel),
|
|
5725
|
+
"aria-label": getHeaderAriaLabel(sortBy, key, displayLabel, translate),
|
|
5679
5726
|
children: jsxs("span", {
|
|
5680
5727
|
class: "fjs-table-th-label",
|
|
5681
5728
|
children: [displayLabel, sortBy !== null && sortBy.key === key ? jsx(Fragment, {
|
|
@@ -5696,7 +5743,7 @@ function Table(props) {
|
|
|
5696
5743
|
children: jsx("td", {
|
|
5697
5744
|
class: "fjs-table-td",
|
|
5698
5745
|
colSpan: evaluatedColumns.length,
|
|
5699
|
-
children:
|
|
5746
|
+
children: translate('Nothing to show.')
|
|
5700
5747
|
})
|
|
5701
5748
|
})
|
|
5702
5749
|
}) : jsx("tbody", {
|
|
@@ -5712,9 +5759,13 @@ function Table(props) {
|
|
|
5712
5759
|
})
|
|
5713
5760
|
}), isNumber(rowCount) && chunkedData.length > 1 && evaluatedColumns.length > 0 ? jsxs("nav", {
|
|
5714
5761
|
class: "fjs-table-nav",
|
|
5715
|
-
children: [
|
|
5762
|
+
children: [jsx("span", {
|
|
5716
5763
|
class: "fjs-table-nav-label",
|
|
5717
|
-
children:
|
|
5764
|
+
children: translate('{page} of {total}', {
|
|
5765
|
+
// stringify: see Validator.js
|
|
5766
|
+
page: (currentPage + 1).toString(),
|
|
5767
|
+
total: chunkedData.length.toString()
|
|
5768
|
+
})
|
|
5718
5769
|
}), jsx("button", {
|
|
5719
5770
|
type: "button",
|
|
5720
5771
|
class: "fjs-table-nav-button",
|
|
@@ -5722,7 +5773,7 @@ function Table(props) {
|
|
|
5722
5773
|
setCurrentPage(page => Math.max(page - 1, 0));
|
|
5723
5774
|
},
|
|
5724
5775
|
disabled: currentPage === 0,
|
|
5725
|
-
"aria-label":
|
|
5776
|
+
"aria-label": translate('Previous page'),
|
|
5726
5777
|
children: jsx(SvgCaretLeft, {})
|
|
5727
5778
|
}), jsx("button", {
|
|
5728
5779
|
type: "button",
|
|
@@ -5731,7 +5782,7 @@ function Table(props) {
|
|
|
5731
5782
|
setCurrentPage(page => Math.min(page + 1, chunkedData.length - 1));
|
|
5732
5783
|
},
|
|
5733
5784
|
disabled: currentPage >= chunkedData.length - 1,
|
|
5734
|
-
"aria-label":
|
|
5785
|
+
"aria-label": translate('Next page'),
|
|
5735
5786
|
children: jsx(SvgCaretRight, {})
|
|
5736
5787
|
})]
|
|
5737
5788
|
}) : null]
|
|
@@ -5881,15 +5932,20 @@ function sortByColumn(array, key, direction) {
|
|
|
5881
5932
|
* @param {null|Sorting} sortBy
|
|
5882
5933
|
* @param {string} key
|
|
5883
5934
|
* @param {string} label
|
|
5935
|
+
* @param {Translate} translate
|
|
5884
5936
|
*/
|
|
5885
|
-
function getHeaderAriaLabel(sortBy, key, label) {
|
|
5937
|
+
function getHeaderAriaLabel(sortBy, key, label, translate) {
|
|
5886
5938
|
if (sortBy === null || sortBy.key !== key) {
|
|
5887
|
-
return
|
|
5939
|
+
return translate('Click to sort by {value} descending', {
|
|
5940
|
+
value: label
|
|
5941
|
+
});
|
|
5888
5942
|
}
|
|
5889
5943
|
if (sortBy.direction === 'asc') {
|
|
5890
|
-
return 'Click to remove sorting';
|
|
5944
|
+
return translate('Click to remove sorting');
|
|
5891
5945
|
}
|
|
5892
|
-
return
|
|
5946
|
+
return translate('Click to sort by {value} ascending', {
|
|
5947
|
+
value: label
|
|
5948
|
+
});
|
|
5893
5949
|
}
|
|
5894
5950
|
|
|
5895
5951
|
/**
|
|
@@ -5960,6 +6016,7 @@ function FilePicker(props) {
|
|
|
5960
6016
|
const errorMessageId = `${domId}-error-message`;
|
|
5961
6017
|
/** @type {File[]} */
|
|
5962
6018
|
const selectedFiles = fileRegistry === null ? EMPTY_ARRAY$1 : fileRegistry.getFiles(filesKey);
|
|
6019
|
+
const translate = useService('translate');
|
|
5963
6020
|
useEffect(() => {
|
|
5964
6021
|
if (filesKey && fileRegistry !== null && !fileRegistry.hasKey(filesKey)) {
|
|
5965
6022
|
onChange({
|
|
@@ -6030,7 +6087,7 @@ function FilePicker(props) {
|
|
|
6030
6087
|
children: "Browse"
|
|
6031
6088
|
}), jsx("span", {
|
|
6032
6089
|
className: "fjs-form-field-label",
|
|
6033
|
-
children: getSelectedFilesLabel(selectedFiles)
|
|
6090
|
+
children: getSelectedFilesLabel(selectedFiles, translate)
|
|
6034
6091
|
})]
|
|
6035
6092
|
}), jsx(Errors, {
|
|
6036
6093
|
id: errorMessageId,
|
|
@@ -6053,16 +6110,19 @@ FilePicker.config = {
|
|
|
6053
6110
|
|
|
6054
6111
|
/**
|
|
6055
6112
|
* @param {File[]} files
|
|
6113
|
+
* @param {Translate} translate
|
|
6056
6114
|
* @returns {string}
|
|
6057
6115
|
*/
|
|
6058
|
-
function getSelectedFilesLabel(files) {
|
|
6116
|
+
function getSelectedFilesLabel(files, translate) {
|
|
6059
6117
|
if (files.length === 0) {
|
|
6060
|
-
return 'No files selected';
|
|
6118
|
+
return translate('No files selected');
|
|
6061
6119
|
}
|
|
6062
6120
|
if (files.length === 1) {
|
|
6063
6121
|
return files[0].name;
|
|
6064
6122
|
}
|
|
6065
|
-
return
|
|
6123
|
+
return translate('{value} files selected', {
|
|
6124
|
+
value: files.length.toString()
|
|
6125
|
+
});
|
|
6066
6126
|
}
|
|
6067
6127
|
|
|
6068
6128
|
var _path$4;
|
|
@@ -6085,6 +6145,10 @@ var SvgDownload = function SvgDownload(props) {
|
|
|
6085
6145
|
|
|
6086
6146
|
const type = 'documentPreview';
|
|
6087
6147
|
|
|
6148
|
+
/**
|
|
6149
|
+
* @typedef { import('diagram-js/lib/i18n/translate/translate').default } Translate
|
|
6150
|
+
*/
|
|
6151
|
+
|
|
6088
6152
|
/**
|
|
6089
6153
|
* @typedef DocumentEndpointBuilder
|
|
6090
6154
|
* @property {(document: DocumentMetadata) => string} [buildUrl]
|
|
@@ -6130,6 +6194,7 @@ function DocumentPreview(props) {
|
|
|
6130
6194
|
const evaluatedLabel = useSingleLineTemplateEvaluation(label, {
|
|
6131
6195
|
debug: true
|
|
6132
6196
|
});
|
|
6197
|
+
const translate = useService('translate');
|
|
6133
6198
|
return jsxs("div", {
|
|
6134
6199
|
class: formFieldClasses(type),
|
|
6135
6200
|
children: [jsx(Label, {
|
|
@@ -6155,7 +6220,7 @@ function DocumentPreview(props) {
|
|
|
6155
6220
|
id: errorMessageId,
|
|
6156
6221
|
errors: getErrors({
|
|
6157
6222
|
dataSource
|
|
6158
|
-
})
|
|
6223
|
+
}, translate)
|
|
6159
6224
|
})]
|
|
6160
6225
|
});
|
|
6161
6226
|
}
|
|
@@ -6177,15 +6242,16 @@ DocumentPreview.config = {
|
|
|
6177
6242
|
* @property {string|undefined} dataSource
|
|
6178
6243
|
*
|
|
6179
6244
|
* @param {GetErrorOptions} options
|
|
6245
|
+
* @param {Translate} translate
|
|
6180
6246
|
* @returns {string[]}
|
|
6181
6247
|
*/
|
|
6182
|
-
function getErrors(options) {
|
|
6248
|
+
function getErrors(options, translate) {
|
|
6183
6249
|
const {
|
|
6184
6250
|
dataSource
|
|
6185
6251
|
} = options;
|
|
6186
6252
|
let errors = [];
|
|
6187
6253
|
if (!isString(dataSource) || dataSource.length < 1) {
|
|
6188
|
-
errors.push('Document reference is not defined.');
|
|
6254
|
+
errors.push(translate('Document reference is not defined.'));
|
|
6189
6255
|
}
|
|
6190
6256
|
return errors;
|
|
6191
6257
|
}
|
|
@@ -6238,6 +6304,7 @@ function PdfRenderer(props) {
|
|
|
6238
6304
|
/** @type {ReturnType<typeof import("preact/hooks").useState<null | string>>} */
|
|
6239
6305
|
const [pdfObjectUrl, setPdfObjectUrl] = useState(null);
|
|
6240
6306
|
const [hasError, setHasError] = useState(false);
|
|
6307
|
+
const translate = useService('translate');
|
|
6241
6308
|
useEffect(() => {
|
|
6242
6309
|
/** @type {null | string} */
|
|
6243
6310
|
let objectUrl = null;
|
|
@@ -6249,7 +6316,13 @@ function PdfRenderer(props) {
|
|
|
6249
6316
|
onError();
|
|
6250
6317
|
return;
|
|
6251
6318
|
}
|
|
6252
|
-
|
|
6319
|
+
|
|
6320
|
+
// Re-type the response instead of trusting its content type. An object URL
|
|
6321
|
+
// inherits the form's origin and <embed> renders it by the blob's own type,
|
|
6322
|
+
// so a response served as HTML would execute script in that origin.
|
|
6323
|
+
const blob = new Blob([await response.arrayBuffer()], {
|
|
6324
|
+
type: 'application/pdf'
|
|
6325
|
+
});
|
|
6253
6326
|
objectUrl = URL.createObjectURL(blob);
|
|
6254
6327
|
setPdfObjectUrl(objectUrl);
|
|
6255
6328
|
} catch {
|
|
@@ -6271,7 +6344,7 @@ function PdfRenderer(props) {
|
|
|
6271
6344
|
class: `fjs-${type}-pdf-viewer`
|
|
6272
6345
|
}) : null, hasError ? jsx(Errors, {
|
|
6273
6346
|
id: errorMessageId,
|
|
6274
|
-
errors: ['Unable to download document']
|
|
6347
|
+
errors: [translate('Unable to download document')]
|
|
6275
6348
|
}) : null]
|
|
6276
6349
|
});
|
|
6277
6350
|
}
|
|
@@ -6351,9 +6424,10 @@ function DocumentRenderer(props) {
|
|
|
6351
6424
|
const isInViewport = useInViewport(ref);
|
|
6352
6425
|
const onError = useCallback(() => setHasError(true), []);
|
|
6353
6426
|
const requestInit = useMemo(() => getDocumentRequestInit(documentEndpointBuilder, documentMetadata), [documentEndpointBuilder, documentMetadata]);
|
|
6427
|
+
const translate = useService('translate');
|
|
6354
6428
|
const singleDocumentContainerClassName = `fjs-${type}-single-document-container`;
|
|
6355
6429
|
const errorMessageId = `${domId}-error-message`;
|
|
6356
|
-
const errorMessage = 'Unable to download document';
|
|
6430
|
+
const errorMessage = translate('Unable to download document');
|
|
6357
6431
|
const isContentTypePresent = typeof metadata.contentType === 'string';
|
|
6358
6432
|
if (isContentTypePresent && metadata.contentType.toLowerCase().startsWith('image/') && isInViewport) {
|
|
6359
6433
|
return jsxs("div", {
|
|
@@ -6431,6 +6505,7 @@ function DownloadButton(props) {
|
|
|
6431
6505
|
onDownloadError,
|
|
6432
6506
|
requestInit
|
|
6433
6507
|
} = props;
|
|
6508
|
+
const translate = useService('translate');
|
|
6434
6509
|
const handleDownload = async () => {
|
|
6435
6510
|
try {
|
|
6436
6511
|
const response = await fetch(endpoint, requestInit);
|
|
@@ -6453,7 +6528,9 @@ function DownloadButton(props) {
|
|
|
6453
6528
|
type: "button",
|
|
6454
6529
|
onClick: handleDownload,
|
|
6455
6530
|
class: classNames(`fjs-${type}-download-button`),
|
|
6456
|
-
"aria-label":
|
|
6531
|
+
"aria-label": translate('Download {fileName}', {
|
|
6532
|
+
fileName
|
|
6533
|
+
}),
|
|
6457
6534
|
children: jsx(SvgDownload, {})
|
|
6458
6535
|
});
|
|
6459
6536
|
}
|
|
@@ -7799,6 +7876,7 @@ class RepeatRenderManager {
|
|
|
7799
7876
|
const {
|
|
7800
7877
|
data
|
|
7801
7878
|
} = this._form._getState();
|
|
7879
|
+
const translate = useService('translate');
|
|
7802
7880
|
const dataPath = this._pathRegistry.getValuePath(repeaterField, {
|
|
7803
7881
|
indexes
|
|
7804
7882
|
});
|
|
@@ -7855,16 +7933,18 @@ class RepeatRenderManager {
|
|
|
7855
7933
|
ref: addButtonRef,
|
|
7856
7934
|
onClick: onAddItem,
|
|
7857
7935
|
children: jsxs(Fragment, {
|
|
7858
|
-
children: [jsx(SvgAdd, {}), " ", 'Add new']
|
|
7936
|
+
children: [jsx(SvgAdd, {}), " ", translate('Add new')]
|
|
7859
7937
|
})
|
|
7860
7938
|
}) : null, collapseEnabled ? jsx("button", {
|
|
7861
7939
|
type: "button",
|
|
7862
7940
|
class: "fjs-repeat-render-collapse",
|
|
7863
7941
|
onClick: toggle,
|
|
7864
7942
|
children: isCollapsed ? jsxs(Fragment, {
|
|
7865
|
-
children: [jsx(SvgExpand, {}), " ",
|
|
7943
|
+
children: [jsx(SvgExpand, {}), " ", translate('Expand all ({value})', {
|
|
7944
|
+
value: values.length - 1
|
|
7945
|
+
})]
|
|
7866
7946
|
}) : jsxs(Fragment, {
|
|
7867
|
-
children: [jsx(SvgCollapse, {}), " ", 'Collapse']
|
|
7947
|
+
children: [jsx(SvgCollapse, {}), " ", translate('Collapse')]
|
|
7868
7948
|
})
|
|
7869
7949
|
}) : null]
|
|
7870
7950
|
});
|
|
@@ -8497,11 +8577,12 @@ const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-
|
|
|
8497
8577
|
const PHONE_PATTERN = /(\+|00)(297|93|244|1264|358|355|376|971|54|374|1684|1268|61|43|994|257|32|229|226|880|359|973|1242|387|590|375|501|1441|591|55|1246|673|975|267|236|1|61|41|56|86|225|237|243|242|682|57|269|238|506|53|5999|61|1345|357|420|49|253|1767|45|1809|1829|1849|213|593|20|291|212|34|372|251|358|679|500|33|298|691|241|44|995|44|233|350|224|590|220|245|240|30|1473|299|502|594|1671|592|852|504|385|509|36|62|44|91|246|353|98|964|354|972|39|1876|44|962|81|76|77|254|996|855|686|1869|82|383|965|856|961|231|218|1758|423|94|266|370|352|371|853|590|212|377|373|261|960|52|692|389|223|356|95|382|976|1670|258|222|1664|596|230|265|60|262|264|687|227|672|234|505|683|31|47|977|674|64|968|92|507|64|51|63|680|675|48|1787|1939|850|351|595|970|689|974|262|40|7|250|966|249|221|65|500|4779|677|232|503|378|252|508|381|211|239|597|421|386|46|268|1721|248|963|1649|235|228|66|992|690|993|670|676|1868|216|90|688|886|255|256|380|598|1|998|3906698|379|1784|58|1284|1340|84|678|681|685|967|27|260|263)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{4,20}$/;
|
|
8498
8578
|
const VALIDATE_FEEL_PROPERTIES = ['min', 'max', 'minLength', 'maxLength'];
|
|
8499
8579
|
class Validator {
|
|
8500
|
-
constructor(expressionLanguage, conditionChecker, form, formFieldRegistry) {
|
|
8580
|
+
constructor(expressionLanguage, conditionChecker, form, formFieldRegistry, translate) {
|
|
8501
8581
|
this._expressionLanguage = expressionLanguage;
|
|
8502
8582
|
this._conditionChecker = conditionChecker;
|
|
8503
8583
|
this._form = form;
|
|
8504
8584
|
this._formFieldRegistry = formFieldRegistry;
|
|
8585
|
+
this._translate = translate;
|
|
8505
8586
|
}
|
|
8506
8587
|
|
|
8507
8588
|
/**
|
|
@@ -8516,13 +8597,13 @@ class Validator {
|
|
|
8516
8597
|
} = field;
|
|
8517
8598
|
let errors = [];
|
|
8518
8599
|
if (type === 'number') {
|
|
8519
|
-
errors = [...errors, ...runNumberValidation(field, value)];
|
|
8600
|
+
errors = [...errors, ...runNumberValidation(field, value, this._translate)];
|
|
8520
8601
|
}
|
|
8521
8602
|
if (!validate) {
|
|
8522
8603
|
return errors;
|
|
8523
8604
|
}
|
|
8524
8605
|
const evaluatedValidation = oldEvaluateFEELValues(validate, this._expressionLanguage, this._conditionChecker, this._form);
|
|
8525
|
-
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
|
|
8606
|
+
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value, this._translate)];
|
|
8526
8607
|
return errors;
|
|
8527
8608
|
}
|
|
8528
8609
|
|
|
@@ -8549,31 +8630,39 @@ class Validator {
|
|
|
8549
8630
|
} = field;
|
|
8550
8631
|
let errors = [];
|
|
8551
8632
|
if (type === 'number') {
|
|
8552
|
-
errors = [...errors, ...runNumberValidation(field, value)];
|
|
8633
|
+
errors = [...errors, ...runNumberValidation(field, value, this._translate)];
|
|
8553
8634
|
}
|
|
8554
8635
|
if (!validate) {
|
|
8555
8636
|
return errors;
|
|
8556
8637
|
}
|
|
8557
8638
|
const evaluatedValidation = evaluateFEELValues(validate, this._expressionLanguage, expressionContextInfo);
|
|
8558
|
-
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
|
|
8639
|
+
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value, this._translate)];
|
|
8559
8640
|
return errors;
|
|
8560
8641
|
}
|
|
8561
8642
|
}
|
|
8562
|
-
Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form', 'formFieldRegistry'];
|
|
8643
|
+
Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form', 'formFieldRegistry', 'translate'];
|
|
8563
8644
|
|
|
8564
8645
|
// helpers //////////
|
|
8565
8646
|
|
|
8566
|
-
function runNumberValidation(field, value) {
|
|
8647
|
+
function runNumberValidation(field, value, translate) {
|
|
8567
8648
|
const {
|
|
8568
8649
|
decimalDigits,
|
|
8569
8650
|
increment
|
|
8570
8651
|
} = field;
|
|
8571
8652
|
const errors = [];
|
|
8572
8653
|
if (value === 'NaN') {
|
|
8573
|
-
errors.push('Value is not a number.');
|
|
8654
|
+
errors.push(translate('Value is not a number.'));
|
|
8574
8655
|
} else if (value) {
|
|
8575
8656
|
if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
|
|
8576
|
-
|
|
8657
|
+
if (decimalDigits === 0) {
|
|
8658
|
+
errors.push(translate('Value is expected to be an integer.'));
|
|
8659
|
+
} else if (decimalDigits === 1) {
|
|
8660
|
+
errors.push(translate('Value is expected to have at most 1 decimal digit.'));
|
|
8661
|
+
} else {
|
|
8662
|
+
errors.push(translate('Value is expected to have at most {value} decimal digits.', {
|
|
8663
|
+
value: decimalDigits.toString()
|
|
8664
|
+
}));
|
|
8665
|
+
}
|
|
8577
8666
|
}
|
|
8578
8667
|
if (increment) {
|
|
8579
8668
|
const bigValue = Big(value);
|
|
@@ -8582,54 +8671,69 @@ function runNumberValidation(field, value) {
|
|
|
8582
8671
|
if (offset.cmp(0) !== 0) {
|
|
8583
8672
|
const previousValue = bigValue.minus(offset);
|
|
8584
8673
|
const nextValue = previousValue.plus(bigIncrement);
|
|
8585
|
-
errors.push(
|
|
8674
|
+
errors.push(translate('Please select a valid value, the two nearest valid values are {previousValue} and {nextValue}.', {
|
|
8675
|
+
previousValue: previousValue,
|
|
8676
|
+
nextValue: nextValue
|
|
8677
|
+
}));
|
|
8586
8678
|
}
|
|
8587
8679
|
}
|
|
8588
8680
|
}
|
|
8589
8681
|
return errors;
|
|
8590
8682
|
}
|
|
8591
|
-
function runPresetValidation(field, validation, value) {
|
|
8683
|
+
function runPresetValidation(field, validation, value, translate) {
|
|
8592
8684
|
const errors = [];
|
|
8593
8685
|
if (validation.pattern && value && !new RegExp(validation.pattern).test(value)) {
|
|
8594
|
-
errors.push(validation.patternErrorMessage ||
|
|
8686
|
+
errors.push(validation.patternErrorMessage || translate('Field must match pattern {value}.', {
|
|
8687
|
+
value: validation.pattern
|
|
8688
|
+
}));
|
|
8595
8689
|
}
|
|
8596
8690
|
if (validation.required) {
|
|
8597
8691
|
const isUncheckedCheckbox = field.type === 'checkbox' && value === false;
|
|
8598
8692
|
const isUnsetValue = isNil(value) || value === '';
|
|
8599
8693
|
const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
|
|
8600
8694
|
if (isUncheckedCheckbox || isUnsetValue || isEmptyMultiselect) {
|
|
8601
|
-
errors.push('Field is required.');
|
|
8695
|
+
errors.push(translate('Field is required.'));
|
|
8602
8696
|
}
|
|
8603
8697
|
}
|
|
8698
|
+
|
|
8699
|
+
// stringify: `translate` falls back to the raw placeholder for a falsy replacement, so 0 must not be passed as a number
|
|
8604
8700
|
if ('min' in validation && (value || value === 0)) {
|
|
8605
8701
|
try {
|
|
8606
8702
|
if (Big(value).lt(Big(validation.min))) {
|
|
8607
|
-
errors.push(
|
|
8703
|
+
errors.push(translate('Field must have minimum value of {value}.', {
|
|
8704
|
+
value: validation.min.toString()
|
|
8705
|
+
}));
|
|
8608
8706
|
}
|
|
8609
8707
|
} catch {
|
|
8610
|
-
errors.push('Min validation value is not a valid number.');
|
|
8708
|
+
errors.push(translate('Min validation value is not a valid number.'));
|
|
8611
8709
|
}
|
|
8612
8710
|
}
|
|
8613
8711
|
if ('max' in validation && (value || value === 0)) {
|
|
8614
8712
|
try {
|
|
8615
8713
|
if (Big(value).gt(Big(validation.max))) {
|
|
8616
|
-
errors.push(
|
|
8714
|
+
errors.push(translate('Field must have maximum value of {value}.', {
|
|
8715
|
+
value: validation.max.toString()
|
|
8716
|
+
}));
|
|
8617
8717
|
}
|
|
8618
8718
|
} catch {
|
|
8619
|
-
errors.push('Max validation value is not a valid number.');
|
|
8719
|
+
errors.push(translate('Max validation value is not a valid number.'));
|
|
8620
8720
|
}
|
|
8621
8721
|
}
|
|
8622
8722
|
if ('minLength' in validation && value && value.trim().length < validation.minLength) {
|
|
8623
|
-
errors.push(
|
|
8723
|
+
errors.push(translate('Field must have minimum length of {value}.', {
|
|
8724
|
+
value: validation.minLength.toString()
|
|
8725
|
+
}));
|
|
8624
8726
|
}
|
|
8625
8727
|
if ('maxLength' in validation && value && value.trim().length > validation.maxLength) {
|
|
8626
|
-
errors.push(
|
|
8728
|
+
errors.push(translate('Field must have maximum length of {value}.', {
|
|
8729
|
+
value: validation.maxLength.toString()
|
|
8730
|
+
}));
|
|
8627
8731
|
}
|
|
8628
8732
|
if ('validationType' in validation && value && validation.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
|
|
8629
|
-
errors.push('Field must be a valid
|
|
8733
|
+
errors.push(translate('Field must be a valid international phone number. (e.g. +4930664040900)'));
|
|
8630
8734
|
}
|
|
8631
8735
|
if ('validationType' in validation && value && validation.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
|
|
8632
|
-
errors.push('Field must be a valid email.');
|
|
8736
|
+
errors.push(translate('Field must be a valid email.'));
|
|
8633
8737
|
}
|
|
8634
8738
|
return errors;
|
|
8635
8739
|
}
|
|
@@ -8787,17 +8891,24 @@ class Importer {
|
|
|
8787
8891
|
}
|
|
8788
8892
|
Importer.$inject = ['formFieldRegistry', 'pathRegistry', 'fieldFactory', 'formLayouter'];
|
|
8789
8893
|
|
|
8894
|
+
/**
|
|
8895
|
+
* @typedef { import('diagram-js/lib/i18n/translate/translate').default } Translate
|
|
8896
|
+
*/
|
|
8897
|
+
|
|
8790
8898
|
class FieldFactory {
|
|
8791
8899
|
/**
|
|
8792
8900
|
* @constructor
|
|
8793
8901
|
*
|
|
8794
|
-
* @param
|
|
8795
|
-
* @param
|
|
8902
|
+
* @param formFieldRegistry
|
|
8903
|
+
* @param pathRegistry
|
|
8904
|
+
* @param formFields
|
|
8905
|
+
* @param {Translate} translate
|
|
8796
8906
|
*/
|
|
8797
|
-
constructor(formFieldRegistry, pathRegistry, formFields) {
|
|
8907
|
+
constructor(formFieldRegistry, pathRegistry, formFields, translate) {
|
|
8798
8908
|
this._formFieldRegistry = formFieldRegistry;
|
|
8799
8909
|
this._pathRegistry = pathRegistry;
|
|
8800
8910
|
this._formFields = formFields;
|
|
8911
|
+
this._translate = translate;
|
|
8801
8912
|
}
|
|
8802
8913
|
create(attrs, isNewField = true) {
|
|
8803
8914
|
const {
|
|
@@ -8830,13 +8941,17 @@ class FieldFactory {
|
|
|
8830
8941
|
isClosed: true,
|
|
8831
8942
|
knownAncestorIds
|
|
8832
8943
|
})) {
|
|
8833
|
-
throw new Error(
|
|
8944
|
+
throw new Error(this._translate("binding path '{value}' is already claimed", {
|
|
8945
|
+
value: [...parentPath, key].join('.')
|
|
8946
|
+
}));
|
|
8834
8947
|
}
|
|
8835
8948
|
if (config.pathed && path && !this._pathRegistry.canClaimPath([...parentPath, ...path.split('.')], {
|
|
8836
8949
|
isRepeatable: config.repeatable,
|
|
8837
8950
|
knownAncestorIds
|
|
8838
8951
|
})) {
|
|
8839
|
-
throw new Error(
|
|
8952
|
+
throw new Error(this._translate("binding path '{value}' is already claimed", {
|
|
8953
|
+
value: [...parentPath, ...path.split('.')].join('.')
|
|
8954
|
+
}));
|
|
8840
8955
|
}
|
|
8841
8956
|
const field = config.create({
|
|
8842
8957
|
...(config.label ? {
|
|
@@ -8897,7 +9012,7 @@ class FieldFactory {
|
|
|
8897
9012
|
return `${field.type}_${random}`;
|
|
8898
9013
|
}
|
|
8899
9014
|
}
|
|
8900
|
-
FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields'];
|
|
9015
|
+
FieldFactory.$inject = ['formFieldRegistry', 'pathRegistry', 'formFields', 'translate'];
|
|
8901
9016
|
|
|
8902
9017
|
/**
|
|
8903
9018
|
* The PathRegistry class manages a hierarchical structure of paths associated with form fields.
|
|
@@ -9907,7 +10022,7 @@ class Form {
|
|
|
9907
10022
|
config: ['value', enrichedConfig]
|
|
9908
10023
|
}, {
|
|
9909
10024
|
form: ['value', this]
|
|
9910
|
-
}, CoreModule, ...modules, ...additionalModules]);
|
|
10025
|
+
}, TranslateModule, CoreModule, ...modules, ...additionalModules]);
|
|
9911
10026
|
}
|
|
9912
10027
|
|
|
9913
10028
|
/**
|