@defra/forms-engine-plugin 5.0.0-alpha.10 → 5.0.0-alpha.11
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/.public/javascripts/application.min.js +1 -1
- package/.public/javascripts/application.min.js.LICENSE.txt +7 -0
- package/.public/javascripts/application.min.js.map +1 -1
- package/.public/javascripts/shared.min.js +1 -1
- package/.public/javascripts/shared.min.js.LICENSE.txt +7 -0
- package/.public/javascripts/shared.min.js.map +1 -1
- package/.server/client/javascripts/file-upload.js +1 -0
- package/.server/client/javascripts/file-upload.js.map +1 -1
- package/.server/client/javascripts/geospatial-map.js +2 -28
- package/.server/client/javascripts/geospatial-map.js.map +1 -1
- package/.server/client/javascripts/govuk.js +2 -1
- package/.server/client/javascripts/govuk.js.map +1 -1
- package/.server/client/javascripts/location-map.js +3 -2
- package/.server/client/javascripts/location-map.js.map +1 -1
- package/.server/client/javascripts/map.d.ts +6 -0
- package/.server/client/javascripts/map.js +37 -0
- package/.server/client/javascripts/map.js.map +1 -1
- package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +3 -0
- package/.server/server/plugins/engine/components/EastingNorthingField.js +17 -0
- package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
- package/.server/server/plugins/engine/components/FileUploadField.js +3 -1
- package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
- package/.server/server/plugins/engine/components/LatLongField.d.ts +2 -0
- package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +3 -1
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js +3 -1
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +3 -1
- package/.server/server/plugins/engine/components/helpers/geospatial.js +32 -0
- package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
- package/.server/server/plugins/engine/i18n/translations/cy.json +13 -3
- package/.server/server/plugins/engine/i18n/translations/en-GB.json +13 -3
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +6 -0
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +34 -1
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +4 -0
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/views/components/_location-field-base.html +2 -1
- package/.server/server/plugins/engine/views/file-upload.html +1 -1
- package/.server/server/utils/utils.d.ts +6 -0
- package/.server/server/utils/utils.js +18 -1
- package/.server/server/utils/utils.js.map +1 -1
- package/.server/server/utils/utils.test.js +21 -1
- package/.server/server/utils/utils.test.js.map +1 -1
- package/package.json +2 -1
- package/src/client/javascripts/file-upload.js +1 -0
- package/src/client/javascripts/geospatial-map.js +2 -31
- package/src/client/javascripts/govuk.js +2 -0
- package/src/client/javascripts/location-map.js +3 -1
- package/src/client/javascripts/map.js +42 -0
- package/src/server/plugins/engine/components/EastingNorthingField.ts +31 -0
- package/src/server/plugins/engine/components/FileUploadField.ts +3 -1
- package/src/server/plugins/engine/components/LocationFieldHelpers.ts +6 -2
- package/src/server/plugins/engine/components/helpers/geospatial.ts +53 -0
- package/src/server/plugins/engine/i18n/translations/cy.json +13 -3
- package/src/server/plugins/engine/i18n/translations/en-GB.json +13 -3
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +42 -1
- package/src/server/plugins/engine/types.ts +4 -0
- package/src/server/plugins/engine/views/components/_location-field-base.html +2 -1
- package/src/server/plugins/engine/views/file-upload.html +1 -1
- package/src/server/utils/utils.js +19 -1
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getLocationFieldViewModel
|
|
16
16
|
} from './LocationFieldHelpers.js'
|
|
17
17
|
import { NumberField } from './NumberField.js'
|
|
18
|
+
import { getEastingNorthingCountryValidator } from './helpers/geospatial.js'
|
|
18
19
|
import {
|
|
19
20
|
createLowerFirstExpression,
|
|
20
21
|
getTranslatedLabel
|
|
@@ -112,6 +113,18 @@ export class EastingNorthingField extends FormComponent {
|
|
|
112
113
|
)
|
|
113
114
|
|
|
114
115
|
this.options = options
|
|
116
|
+
|
|
117
|
+
const country = options.countries?.at(0)
|
|
118
|
+
|
|
119
|
+
if (country) {
|
|
120
|
+
this.collection.formSchema = this.collection.formSchema.custom(
|
|
121
|
+
getEastingNorthingCountryValidator(this, country)
|
|
122
|
+
)
|
|
123
|
+
this.collection.stateSchema = this.collection.stateSchema.custom(
|
|
124
|
+
getEastingNorthingCountryValidator(this, country)
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
115
128
|
this.formSchema = this.collection.formSchema
|
|
116
129
|
this.stateSchema = this.collection.stateSchema
|
|
117
130
|
}
|
|
@@ -164,6 +177,24 @@ export class EastingNorthingField extends FormComponent {
|
|
|
164
177
|
return getLocationFieldViewModel(this, viewModel, context)
|
|
165
178
|
}
|
|
166
179
|
|
|
180
|
+
getErrors(
|
|
181
|
+
translator: Translator,
|
|
182
|
+
errors?: FormSubmissionError[]
|
|
183
|
+
): FormSubmissionError[] | undefined {
|
|
184
|
+
const fieldErrors = super.getErrors(translator, errors)
|
|
185
|
+
|
|
186
|
+
fieldErrors?.forEach((err) => {
|
|
187
|
+
if (err.context?.country) {
|
|
188
|
+
err.text = translator.t(
|
|
189
|
+
'components.eastingNorthingField.wrongCountry',
|
|
190
|
+
{ country: err.context.country }
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return fieldErrors
|
|
196
|
+
}
|
|
197
|
+
|
|
167
198
|
getViewErrors(
|
|
168
199
|
translator: Translator,
|
|
169
200
|
errors?: FormSubmissionError[]
|
|
@@ -52,7 +52,9 @@ export const tempFileSchema = fileSchema.append({
|
|
|
52
52
|
.string()
|
|
53
53
|
.valid(FileStatus.complete, FileStatus.rejected, FileStatus.pending)
|
|
54
54
|
.required(),
|
|
55
|
-
errorMessage: joi.string().optional()
|
|
55
|
+
errorMessage: joi.string().optional(),
|
|
56
|
+
errorCode: joi.string().optional(),
|
|
57
|
+
errorParams: joi.any().optional()
|
|
56
58
|
})
|
|
57
59
|
|
|
58
60
|
export const formFileSchema = fileSchema.append({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Context, type CustomValidator } from 'joi'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { EastingNorthingField } from './EastingNorthingField.js'
|
|
4
4
|
import { isFormValue } from './FormComponent.js'
|
|
5
5
|
import { type GeospatialField } from './GeospatialField.js'
|
|
6
6
|
import { type LatLongField } from './LatLongField.js'
|
|
@@ -190,7 +190,11 @@ export function getLocationFieldViewModel(
|
|
|
190
190
|
...viewModel,
|
|
191
191
|
fieldset,
|
|
192
192
|
items,
|
|
193
|
-
mapLayers: getMapLayers(component)
|
|
193
|
+
mapLayers: getMapLayers(component),
|
|
194
|
+
country:
|
|
195
|
+
component instanceof EastingNorthingField
|
|
196
|
+
? component.options.countries?.at(0)
|
|
197
|
+
: undefined
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
if (component.options.instructionText) {
|
|
@@ -5,8 +5,13 @@ import {
|
|
|
5
5
|
} from '@defra/forms-model'
|
|
6
6
|
import Bourne from '@hapi/bourne'
|
|
7
7
|
import { booleanWithin } from '@turf/boolean-within'
|
|
8
|
+
import { point } from '@turf/helpers'
|
|
9
|
+
// @ts-expect-error - no types
|
|
10
|
+
import OsGridRef from 'geodesy/osgridref.js'
|
|
8
11
|
import JoiBase, { type CustomValidator } from 'joi'
|
|
9
12
|
|
|
13
|
+
import { type EastingNorthingField } from '../EastingNorthingField.js'
|
|
14
|
+
import { type EastingNorthingState } from '../types.js'
|
|
10
15
|
import {
|
|
11
16
|
type Coordinates,
|
|
12
17
|
type Feature,
|
|
@@ -171,3 +176,51 @@ export function getGeospatialSchema(
|
|
|
171
176
|
def
|
|
172
177
|
)
|
|
173
178
|
}
|
|
179
|
+
|
|
180
|
+
export function getEastingNorthingCountryValidator(
|
|
181
|
+
field: EastingNorthingField,
|
|
182
|
+
country: GeospatialFieldOptionsCountry
|
|
183
|
+
) {
|
|
184
|
+
const validateCountryBounds: CustomValidator = (value, helpers) => {
|
|
185
|
+
const countryFeature = countries.features.find(
|
|
186
|
+
(feature) => feature.id === country
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if (!countryFeature) {
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
191
|
+
return value
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const eastingNorthing = field.getFormValueFromState(
|
|
195
|
+
value as EastingNorthingState
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if (!eastingNorthing) {
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
200
|
+
return value
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const gridRef = new OsGridRef(
|
|
204
|
+
eastingNorthing.easting,
|
|
205
|
+
eastingNorthing.northing
|
|
206
|
+
)
|
|
207
|
+
const latLong = gridRef.toLatLon()
|
|
208
|
+
const coord = point([latLong.longitude, latLong.latitude])
|
|
209
|
+
const within = booleanWithin(coord, countryFeature)
|
|
210
|
+
|
|
211
|
+
if (!within) {
|
|
212
|
+
return helpers.error(
|
|
213
|
+
'any.custom',
|
|
214
|
+
{
|
|
215
|
+
country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]
|
|
216
|
+
},
|
|
217
|
+
{ path: [field.name] }
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
222
|
+
return value
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return validateCountryBounds
|
|
226
|
+
}
|
|
@@ -129,7 +129,13 @@
|
|
|
129
129
|
"removeFileTitle": "Ydych chi'n siŵr eich bod chi eisiau dileu'r ffeil hon?",
|
|
130
130
|
"removeFileBody": "Ni allwch adfer ffeiliau a dynnwyd.",
|
|
131
131
|
"removeFileButton": "Dileu ffeil",
|
|
132
|
-
"cancel": "Canslo"
|
|
132
|
+
"cancel": "Canslo",
|
|
133
|
+
"FILE_VIRUS": "Mae'r ffeil a ddewiswyd yn cynnwys firws",
|
|
134
|
+
"FILE_EMPTY": "Mae'r ffeil a ddewiswyd yn wag",
|
|
135
|
+
"FILE_TOO_LARGE": "Rhaid i'r ffeil a ddewiswyd fod yn llai na [[maxFileSizeFormatted]]",
|
|
136
|
+
"FILE_INVALID_TYPE": "Rhaid i'r ffeil a ddewiswyd fod yn [[fileExtensions]]",
|
|
137
|
+
"FILE_UPLOAD_FAILED": "Ni ellid uwchlwytho'r ffeil a ddewiswyd - rhowch gynnig arall arni",
|
|
138
|
+
"FILE_ERROR_UNKNOWN": "Gwall anhysbys"
|
|
133
139
|
},
|
|
134
140
|
|
|
135
141
|
"exit": {
|
|
@@ -244,7 +250,8 @@
|
|
|
244
250
|
"eastingDigits": "Rhaid i {{#label}} ar gyfer [[fieldLabel]] fod rhwng 1 a 6 digid",
|
|
245
251
|
"northingDigits": "Rhaid i {{#label}} ar gyfer [[fieldLabel]] fod rhwng 1 a 7 digid",
|
|
246
252
|
"eastingRange": "Rhaid i {{#label}} ar gyfer [[fieldLabel]] fod rhwng [[min]] a [[max]]",
|
|
247
|
-
"northingRange": "Rhaid i {{#label}} ar gyfer [[fieldLabel]] fod rhwng [[min]] a [[max]]"
|
|
253
|
+
"northingRange": "Rhaid i {{#label}} ar gyfer [[fieldLabel]] fod rhwng [[min]] a [[max]]",
|
|
254
|
+
"wrongCountry": "Rhaid i leoliad fod y tu mewn i [[country]]"
|
|
248
255
|
},
|
|
249
256
|
|
|
250
257
|
"nationalGridField": {
|
|
@@ -319,7 +326,10 @@
|
|
|
319
326
|
"filesCount_two": "Uwchlwythwyd [[count]] ffeil",
|
|
320
327
|
"filesCount_few": "Uwchlwythwyd [[count]] ffeil",
|
|
321
328
|
"filesCount_many": "Uwchlwythwyd [[count]] o ffeiliau",
|
|
322
|
-
"filesCount_other": "Uwchlwythwyd [[count]] ffeil"
|
|
329
|
+
"filesCount_other": "Uwchlwythwyd [[count]] ffeil",
|
|
330
|
+
"chooseFile": "Dewiswch ffeil",
|
|
331
|
+
"dropFile": "neu ollwng ffeil",
|
|
332
|
+
"noFileChosen": "Dim ffeil wedi'i dewis"
|
|
323
333
|
},
|
|
324
334
|
|
|
325
335
|
"characterCount": {
|
|
@@ -113,7 +113,13 @@
|
|
|
113
113
|
"removeFileTitle": "Are you sure you want to remove this file?",
|
|
114
114
|
"removeFileBody": "You cannot recover removed files.",
|
|
115
115
|
"removeFileButton": "Remove file",
|
|
116
|
-
"cancel": "Cancel"
|
|
116
|
+
"cancel": "Cancel",
|
|
117
|
+
"FILE_VIRUS": "The selected file contains a virus",
|
|
118
|
+
"FILE_EMPTY": "The selected file is empty",
|
|
119
|
+
"FILE_TOO_LARGE": "The selected file must be smaller than [[maxFileSizeFormatted]]",
|
|
120
|
+
"FILE_INVALID_TYPE": "The selected file must be a [[fileExtensions]]",
|
|
121
|
+
"FILE_UPLOAD_FAILED": "The selected file could not be uploaded - try again",
|
|
122
|
+
"FILE_ERROR_UNKNOWN": "Unknown error"
|
|
117
123
|
},
|
|
118
124
|
|
|
119
125
|
"exit": {
|
|
@@ -224,7 +230,8 @@
|
|
|
224
230
|
"eastingDigits": "{{#label}} for [[fieldLabel]] must be between 1 and 6 digits",
|
|
225
231
|
"northingDigits": "{{#label}} for [[fieldLabel]] must be between 1 and 7 digits",
|
|
226
232
|
"eastingRange": "{{#label}} for [[fieldLabel]] must be between [[min]] and [[max]]",
|
|
227
|
-
"northingRange": "{{#label}} for [[fieldLabel]] must be between [[min]] and [[max]]"
|
|
233
|
+
"northingRange": "{{#label}} for [[fieldLabel]] must be between [[min]] and [[max]]",
|
|
234
|
+
"wrongCountry": "Location must be within [[country]]"
|
|
228
235
|
},
|
|
229
236
|
|
|
230
237
|
"nationalGridField": {
|
|
@@ -291,7 +298,10 @@
|
|
|
291
298
|
"uploadFailed": "There was a problem with your uploaded files. Re-upload them before submitting the form again.",
|
|
292
299
|
"uploadedFilesHeading": "Uploaded files",
|
|
293
300
|
"filesCount_one": "[[count]] file uploaded",
|
|
294
|
-
"filesCount_other": "[[count]] files uploaded"
|
|
301
|
+
"filesCount_other": "[[count]] files uploaded",
|
|
302
|
+
"chooseFile": "Choose file",
|
|
303
|
+
"dropFile": "or drop file",
|
|
304
|
+
"noFileChosen": "No file chosen"
|
|
295
305
|
},
|
|
296
306
|
|
|
297
307
|
"characterCount": {
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
type FormRequestPayload,
|
|
43
43
|
type FormResponseToolkit
|
|
44
44
|
} from '../../../routes/types.js'
|
|
45
|
+
import { joinWithOr } from '../../../utils/utils.js'
|
|
45
46
|
|
|
46
47
|
const MAX_UPLOADS = 25
|
|
47
48
|
const CDP_UPLOAD_TIMEOUT_MS = 60000 // 1 minute
|
|
@@ -285,6 +286,13 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
285
286
|
summaryList: formComponent.model.upload?.summaryList ?? { rows: [] },
|
|
286
287
|
uploadingLabel: t('components.fileUploadField.uploading')
|
|
287
288
|
}
|
|
289
|
+
formComponent.model = {
|
|
290
|
+
...formComponent.model,
|
|
291
|
+
javascript: true,
|
|
292
|
+
chooseFilesButtonText: t('components.fileUploadField.chooseFile'),
|
|
293
|
+
dropInstructionText: t('components.fileUploadField.dropFile'),
|
|
294
|
+
noFileChosenText: t('components.fileUploadField.noFileChosen')
|
|
295
|
+
}
|
|
288
296
|
|
|
289
297
|
const index = components.indexOf(formComponent)
|
|
290
298
|
|
|
@@ -451,6 +459,9 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
451
459
|
|
|
452
460
|
const allErrors: FormSubmissionError[] = []
|
|
453
461
|
|
|
462
|
+
const translator = this.getTranslator(request)
|
|
463
|
+
const { t } = translator
|
|
464
|
+
|
|
454
465
|
for (const file of uploadedFiles) {
|
|
455
466
|
if (file.fileStatus === FileStatus.complete) {
|
|
456
467
|
const perFileState: FileState = {
|
|
@@ -465,7 +476,14 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
465
476
|
// Collect the error for rejected/pending files.
|
|
466
477
|
const { fileUpload } = this
|
|
467
478
|
const name = fileUpload.name
|
|
468
|
-
const
|
|
479
|
+
const errorParams = this.expandErrorParams(
|
|
480
|
+
file.errorParams,
|
|
481
|
+
t('common.or')
|
|
482
|
+
)
|
|
483
|
+
const text = t(
|
|
484
|
+
`pages.fileUpload.${file.errorCode ?? 'FILE_ERROR_UNKNOWN'}`,
|
|
485
|
+
errorParams
|
|
486
|
+
)
|
|
469
487
|
allErrors.push({ path: [name], href: `#${name}`, name, text })
|
|
470
488
|
}
|
|
471
489
|
}
|
|
@@ -482,6 +500,29 @@ export class FileUploadPageController extends QuestionPageController {
|
|
|
482
500
|
}
|
|
483
501
|
}
|
|
484
502
|
|
|
503
|
+
/**
|
|
504
|
+
* Process the error parameters so any arrays get expanded (joined), ready to be passed into the error message
|
|
505
|
+
* @param errorParams - received from the file upload (may be a number, a string, or a string array)
|
|
506
|
+
* @param finalSeparator - 'or' in English, and 'neu' in Welsh
|
|
507
|
+
*/
|
|
508
|
+
private expandErrorParams(
|
|
509
|
+
errorParams: Record<string, number | string | string[]> | undefined,
|
|
510
|
+
finalSeparator: string
|
|
511
|
+
) {
|
|
512
|
+
if (!errorParams) {
|
|
513
|
+
return {}
|
|
514
|
+
}
|
|
515
|
+
const expanded = {} as Record<string, number | string>
|
|
516
|
+
for (const [key, value] of Object.entries(errorParams)) {
|
|
517
|
+
if (Array.isArray(value)) {
|
|
518
|
+
expanded[key] = joinWithOr(value, finalSeparator)
|
|
519
|
+
} else {
|
|
520
|
+
expanded[key] = value
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return expanded
|
|
524
|
+
}
|
|
525
|
+
|
|
485
526
|
/**
|
|
486
527
|
* Checks the payload for a file getting removed
|
|
487
528
|
* and removes it from the upload files if found
|
|
@@ -245,11 +245,15 @@ export type FileUpload = {
|
|
|
245
245
|
} & (
|
|
246
246
|
| {
|
|
247
247
|
fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending
|
|
248
|
+
errorCode?: string
|
|
248
249
|
errorMessage?: string
|
|
250
|
+
errorParams?: Record<string, number | string | string[]>
|
|
249
251
|
}
|
|
250
252
|
| {
|
|
251
253
|
fileStatus: FileStatus.complete
|
|
254
|
+
errorCode?: string
|
|
252
255
|
errorMessage?: undefined
|
|
256
|
+
errorParams?: Record<string, number | string | string[]>
|
|
253
257
|
}
|
|
254
258
|
)
|
|
255
259
|
|
|
@@ -70,8 +70,9 @@
|
|
|
70
70
|
|
|
71
71
|
{% set hasErrors = showFieldsetError and component.model.errorMessage %}
|
|
72
72
|
|
|
73
|
-
<div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}"
|
|
73
|
+
<div id="{{component.model.name}}" class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}"
|
|
74
74
|
data-locationtype="{{component.type | lower}}"
|
|
75
|
+
data-country="{{component.model.country}}"
|
|
75
76
|
data-maplayers="{{component.model.mapLayers}}">
|
|
76
77
|
{{ govukFieldset({
|
|
77
78
|
legend: {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{% if formAction %}
|
|
16
16
|
{{ govukButton({
|
|
17
17
|
text: t('pages.fileUpload.upload'),
|
|
18
|
-
classes: "govuk-button--secondary upload-file-button",
|
|
18
|
+
classes: "govuk-button--secondary upload-file-button js-hidden",
|
|
19
19
|
preventDoubleClick: true,
|
|
20
20
|
disabled: context.isForceAccess
|
|
21
21
|
}) }}
|
|
@@ -44,6 +44,24 @@ export function resolveLanguage(request) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Create a comma-separated list with the last seperator being 'or'
|
|
48
|
+
* @param { string[] | undefined } items
|
|
49
|
+
* @param {string} [finalSeparator]
|
|
50
|
+
*/
|
|
51
|
+
export function joinWithOr(items, finalSeparator = 'or') {
|
|
52
|
+
if (!items || items.length === 0) {
|
|
53
|
+
return ''
|
|
54
|
+
}
|
|
55
|
+
if (items.length === 1) {
|
|
56
|
+
return items[0]
|
|
57
|
+
}
|
|
58
|
+
if (items.length === 2) {
|
|
59
|
+
return `${items[0]} ${finalSeparator} ${items[1]}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return `${items.slice(0, -1).join(', ')} ${finalSeparator} ${items.at(-1)}`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
48
66
|
* @import { AnyFormRequest } from '../plugins/engine/types.js'
|
|
49
67
|
*/
|