@formio/js 5.6.1 → 5.7.0-api912.2

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.
Files changed (104) hide show
  1. package/dist/formio.builder.css +5 -5
  2. package/dist/formio.builder.min.css +1 -1
  3. package/dist/formio.embed.js +1 -1
  4. package/dist/formio.embed.min.js +1 -1
  5. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  6. package/dist/formio.form.css +5 -5
  7. package/dist/formio.form.js +5212 -5185
  8. package/dist/formio.form.min.css +1 -1
  9. package/dist/formio.form.min.js +1 -1
  10. package/dist/formio.form.min.js.LICENSE.txt +3 -1
  11. package/dist/formio.full.css +5 -5
  12. package/dist/formio.full.js +6063 -6036
  13. package/dist/formio.full.min.css +1 -1
  14. package/dist/formio.full.min.js +1 -1
  15. package/dist/formio.full.min.js.LICENSE.txt +3 -1
  16. package/dist/formio.js +1211 -1184
  17. package/dist/formio.min.js +1 -1
  18. package/dist/formio.min.js.LICENSE.txt +3 -1
  19. package/dist/formio.utils.js +1102 -1075
  20. package/dist/formio.utils.min.js +1 -1
  21. package/dist/formio.utils.min.js.LICENSE.txt +3 -1
  22. package/lib/cjs/Embed.js +38 -25
  23. package/lib/cjs/Form.d.ts +8 -2
  24. package/lib/cjs/Form.js +1 -1
  25. package/lib/cjs/Formio.js +1 -1
  26. package/lib/cjs/PDF.d.ts +1 -0
  27. package/lib/cjs/PDF.js +21 -5
  28. package/lib/cjs/PDFBuilder.d.ts +1 -0
  29. package/lib/cjs/PDFBuilder.js +1 -0
  30. package/lib/cjs/Webform.d.ts +13 -1
  31. package/lib/cjs/Webform.js +79 -26
  32. package/lib/cjs/Wizard.d.ts +1 -1
  33. package/lib/cjs/Wizard.js +12 -6
  34. package/lib/cjs/components/_classes/component/Component.js +0 -65
  35. package/lib/cjs/components/_classes/nested/NestedComponent.form.js +13 -0
  36. package/lib/cjs/components/_classes/nested/NestedComponent.js +4 -0
  37. package/lib/cjs/components/content/editForm/Content.edit.display.js +8 -0
  38. package/lib/cjs/components/currency/editForm/Currency.edit.display.js +20 -0
  39. package/lib/cjs/components/day/editForm/Day.edit.display.js +8 -0
  40. package/lib/cjs/components/editgrid/EditGrid.d.ts +1 -0
  41. package/lib/cjs/components/editgrid/EditGrid.js +22 -2
  42. package/lib/cjs/components/email/editForm/Email.edit.display.js +12 -0
  43. package/lib/cjs/components/fieldset/editForm/Fieldset.edit.display.js +8 -0
  44. package/lib/cjs/components/file/editForm/File.edit.file.js +1 -1
  45. package/lib/cjs/components/form/Form.js +21 -1
  46. package/lib/cjs/components/hidden/Hidden.d.ts +0 -1
  47. package/lib/cjs/components/hidden/Hidden.js +1 -1
  48. package/lib/cjs/components/hidden/editForm/Hidden.edit.display.js +8 -0
  49. package/lib/cjs/components/html/editForm/HTML.edit.display.js +8 -0
  50. package/lib/cjs/components/number/Number.d.ts +11 -0
  51. package/lib/cjs/components/number/Number.js +35 -0
  52. package/lib/cjs/components/number/editForm/Number.edit.display.js +12 -0
  53. package/lib/cjs/components/password/editForm/Password.edit.display.js +12 -0
  54. package/lib/cjs/components/phonenumber/PhoneNumber.form.js +8 -0
  55. package/lib/cjs/components/select/editForm/Select.edit.data.d.ts +1 -1
  56. package/lib/cjs/components/select/editForm/Select.edit.data.js +1 -0
  57. package/lib/cjs/components/tabs/editForm/Tabs.edit.display.js +8 -0
  58. package/lib/cjs/components/textarea/editForm/TextArea.edit.display.js +12 -0
  59. package/lib/cjs/components/url/editForm/Url.edit.display.js +12 -0
  60. package/lib/cjs/components/well/editForm/Well.edit.display.js +8 -0
  61. package/lib/cjs/package.json +1 -1
  62. package/lib/cjs/utils/utils.js +6 -18
  63. package/lib/mjs/Embed.js +37 -24
  64. package/lib/mjs/Form.d.ts +8 -2
  65. package/lib/mjs/Form.js +1 -1
  66. package/lib/mjs/Formio.js +1 -1
  67. package/lib/mjs/PDF.d.ts +1 -0
  68. package/lib/mjs/PDF.js +21 -5
  69. package/lib/mjs/PDFBuilder.d.ts +1 -0
  70. package/lib/mjs/PDFBuilder.js +1 -0
  71. package/lib/mjs/Webform.d.ts +13 -1
  72. package/lib/mjs/Webform.js +75 -24
  73. package/lib/mjs/Wizard.d.ts +1 -1
  74. package/lib/mjs/Wizard.js +12 -6
  75. package/lib/mjs/components/_classes/component/Component.js +0 -65
  76. package/lib/mjs/components/_classes/nested/NestedComponent.form.js +13 -0
  77. package/lib/mjs/components/_classes/nested/NestedComponent.js +4 -0
  78. package/lib/mjs/components/content/editForm/Content.edit.display.js +8 -0
  79. package/lib/mjs/components/currency/editForm/Currency.edit.display.js +20 -0
  80. package/lib/mjs/components/day/editForm/Day.edit.display.js +8 -0
  81. package/lib/mjs/components/editgrid/EditGrid.d.ts +1 -0
  82. package/lib/mjs/components/editgrid/EditGrid.js +22 -2
  83. package/lib/mjs/components/email/editForm/Email.edit.display.js +12 -0
  84. package/lib/mjs/components/fieldset/editForm/Fieldset.edit.display.js +8 -0
  85. package/lib/mjs/components/file/editForm/File.edit.file.js +1 -1
  86. package/lib/mjs/components/form/Form.js +21 -1
  87. package/lib/mjs/components/hidden/Hidden.d.ts +0 -1
  88. package/lib/mjs/components/hidden/Hidden.js +1 -1
  89. package/lib/mjs/components/hidden/editForm/Hidden.edit.display.js +8 -0
  90. package/lib/mjs/components/html/editForm/HTML.edit.display.js +8 -0
  91. package/lib/mjs/components/number/Number.d.ts +11 -0
  92. package/lib/mjs/components/number/Number.js +34 -0
  93. package/lib/mjs/components/number/editForm/Number.edit.display.js +12 -0
  94. package/lib/mjs/components/password/editForm/Password.edit.display.js +12 -0
  95. package/lib/mjs/components/phonenumber/PhoneNumber.form.js +8 -0
  96. package/lib/mjs/components/select/editForm/Select.edit.data.d.ts +1 -1
  97. package/lib/mjs/components/select/editForm/Select.edit.data.js +1 -0
  98. package/lib/mjs/components/tabs/editForm/Tabs.edit.display.js +8 -0
  99. package/lib/mjs/components/textarea/editForm/TextArea.edit.display.js +12 -0
  100. package/lib/mjs/components/url/editForm/Url.edit.display.js +12 -0
  101. package/lib/mjs/components/well/editForm/Well.edit.display.js +8 -0
  102. package/lib/mjs/package.json +1 -1
  103. package/lib/mjs/utils/utils.js +6 -18
  104. package/package.json +1 -1
@@ -20,7 +20,9 @@
20
20
 
21
21
  /*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
22
22
 
23
- /*! formiojs v5.6.1 | https://unpkg.com/formiojs@5.6.1/LICENSE.txt */
23
+ /*! @license DOMPurify 3.4.14 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.14/LICENSE */
24
+
25
+ /*! formiojs v5.7.0-api912.2 | https://unpkg.com/formiojs@5.7.0-api912.2/LICENSE.txt */
24
26
 
25
27
  /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
26
28
 
package/lib/cjs/Embed.js CHANGED
@@ -273,7 +273,6 @@ class Formio {
273
273
  });
274
274
  }
275
275
  static init(element, options = {}, builder = false) {
276
- var _b;
277
276
  return __awaiter(this, void 0, void 0, function* () {
278
277
  _a.cdn = new CDN_js_1.default(_a.config.cdn, _a.config.cdnUrls || {});
279
278
  _a.config.libs = _a.config.libs || {
@@ -345,7 +344,10 @@ class Formio {
345
344
  };
346
345
  });
347
346
  const id = _a.config.id || `formio-${Math.random().toString(36).substring(7)}`;
348
- const hasQuillComponent = (components = []) => {
347
+ // Quill and CKEditor cannot run in the shadow DOM (https://github.com/quilljs/quill/issues/2021,
348
+ // https://github.com/ckeditor/ckeditor5/issues/15824), so fall back to the light DOM when the
349
+ // form contains one of them.
350
+ const hasShadowIncompatibleEditor = (components) => {
349
351
  if (!Array.isArray(components)) {
350
352
  return false;
351
353
  }
@@ -354,16 +356,39 @@ class Formio {
354
356
  if (!component || typeof component !== 'object') {
355
357
  return false;
356
358
  }
357
- const isQuillTextarea = component.type === 'textarea' &&
358
- component.wysiwyg === true &&
359
- component.editor === 'quill';
360
- return (isQuillTextarea ||
361
- hasQuillComponent(component.components) ||
362
- hasQuillComponent((_b = component.columns) === null || _b === void 0 ? void 0 : _b.flatMap((column) => column.components || [])) ||
363
- hasQuillComponent((_c = component.rows) === null || _c === void 0 ? void 0 : _c.flatMap((row) => row.flatMap((cell) => cell.components || []))));
359
+ if (component.type === 'textarea') {
360
+ // A textarea with `wysiwyg` set and no explicit `editor` defaults to CKEditor at render time.
361
+ const editor = component.editor || (component.wysiwyg ? 'ckeditor' : '');
362
+ if (['quill', 'ckeditor'].includes(editor)) {
363
+ return true;
364
+ }
365
+ }
366
+ const children = (component.components || []).concat(((_b = component.columns) === null || _b === void 0 ? void 0 : _b.flatMap((column) => column.components || [])) || [], ((_c = component.rows) === null || _c === void 0 ? void 0 : _c.flatMap((row) => row.flatMap((cell) => cell.components || []))) || []);
367
+ return hasShadowIncompatibleEditor(children);
364
368
  });
365
369
  };
366
- const disableShadowForQuill = hasQuillComponent((_b = _a.config.form) === null || _b === void 0 ? void 0 : _b.components);
370
+ const shouldSkipShadowDom = () => __awaiter(this, void 0, void 0, function* () {
371
+ var _b;
372
+ if ((_b = _a.config.form) === null || _b === void 0 ? void 0 : _b.components) {
373
+ return hasShadowIncompatibleEditor(_a.config.form.components);
374
+ }
375
+ const formUrl = _a.config.src || (typeof _a.config.form === 'string' ? _a.config.form : '');
376
+ if (!formUrl) {
377
+ return false;
378
+ }
379
+ try {
380
+ const response = yield fetch(formUrl);
381
+ if (!response.ok) {
382
+ throw new Error(`Request failed with status ${response.status}`);
383
+ }
384
+ const form = yield response.json();
385
+ return hasShadowIncompatibleEditor(form === null || form === void 0 ? void 0 : form.components);
386
+ }
387
+ catch (err) {
388
+ _a.debug('Could not load the form to check for shadow DOM support', err);
389
+ return false;
390
+ }
391
+ });
367
392
  // Create a new wrapper and add the element inside of a new wrapper.
368
393
  let wrapper = _a.createElement('div', {
369
394
  id: `${id}-wrapper`,
@@ -372,20 +397,8 @@ class Formio {
372
397
  // If we include the libraries, then we will attempt to run this in shadow dom.
373
398
  const useShadowDom = _a.config.includeLibs &&
374
399
  !_a.config.noshadow &&
375
- !disableShadowForQuill &&
376
- typeof wrapper.attachShadow === 'function';
377
- if (useShadowDom) {
378
- wrapper = wrapper.attachShadow({
379
- mode: 'open',
380
- });
381
- options.shadowRoot = wrapper;
382
- // Due to an issue with quill not loading styles in the shadowdom, we need to add quill styles and js to the shadowdom
383
- const quill = {
384
- js: `${_a.cdn.quill}/quill.js`,
385
- css: `${_a.cdn.quill}/quill.snow.css`,
386
- };
387
- yield _a.addLibrary(wrapper, quill, 'quill');
388
- }
400
+ typeof wrapper.attachShadow === 'function' &&
401
+ !(yield shouldSkipShadowDom());
389
402
  element.parentNode.removeChild(element);
390
403
  wrapper.appendChild(element);
391
404
  // If this is inside of shadow dom, then we need to add the styles and scripts to the shadow dom.
@@ -500,7 +513,7 @@ Formio.formioReady = new Promise((ready, reject) => {
500
513
  _a._formioReady = ready;
501
514
  _a._formioReadyReject = reject;
502
515
  });
503
- Formio.version = '5.6.1';
516
+ Formio.version = '5.7.0-api912.2';
504
517
  // Create a report.
505
518
  Formio.Report = {
506
519
  create: (element, submission, options = {}) => __awaiter(void 0, void 0, void 0, function* () {
package/lib/cjs/Form.d.ts CHANGED
@@ -45,7 +45,7 @@
45
45
  * @property {number} [saveDraftThrottle] - The throttle for the save draft feature.
46
46
  * @property {boolean} [readOnly] - Set this form to readOnly.
47
47
  * @property {boolean} [noAlerts] - Disable the alerts dialog.
48
- * @property {{[key: string]: string} | { translationsUrl: string }} [i18n] - The translation file for this rendering.
48
+ * @property {{[key: string]: string} | { translationsUrl: string } | { resource: { form: string, codeComponent: string, mapComponent: string } }} [i18n] - The translations for this rendering: inline, a hosted file, or a Form.io language resource (one submission per language).
49
49
  * @property {string} [template] - Custom logic for creation of elements.
50
50
  * @property {boolean} [noDefaults] - Exclude default values from the settings.
51
51
  * @property {any} [fileService] - The file service for this form.
@@ -198,12 +198,18 @@ export type FormOptions = {
198
198
  */
199
199
  noAlerts?: boolean | undefined;
200
200
  /**
201
- * - The translation file for this rendering.
201
+ * - The translations for this rendering: inline, a hosted file, or a Form.io language resource (one submission per language).
202
202
  */
203
203
  i18n?: {
204
204
  [key: string]: string;
205
205
  } | {
206
206
  translationsUrl: string;
207
+ } | {
208
+ resource: {
209
+ form: string;
210
+ codeComponent: string;
211
+ mapComponent: string;
212
+ };
207
213
  } | undefined;
208
214
  /**
209
215
  * - Custom logic for creation of elements.
package/lib/cjs/Form.js CHANGED
@@ -56,7 +56,7 @@ const utils_1 = __importDefault(require("./utils"));
56
56
  * @property {number} [saveDraftThrottle] - The throttle for the save draft feature.
57
57
  * @property {boolean} [readOnly] - Set this form to readOnly.
58
58
  * @property {boolean} [noAlerts] - Disable the alerts dialog.
59
- * @property {{[key: string]: string} | { translationsUrl: string }} [i18n] - The translation file for this rendering.
59
+ * @property {{[key: string]: string} | { translationsUrl: string } | { resource: { form: string, codeComponent: string, mapComponent: string } }} [i18n] - The translations for this rendering: inline, a hosted file, or a Form.io language resource (one submission per language).
60
60
  * @property {string} [template] - Custom logic for creation of elements.
61
61
  * @property {boolean} [noDefaults] - Exclude default values from the settings.
62
62
  * @property {any} [fileService] - The file service for this form.
package/lib/cjs/Formio.js CHANGED
@@ -11,7 +11,7 @@ const CDN_1 = __importDefault(require("./CDN"));
11
11
  const providers_1 = __importDefault(require("./providers"));
12
12
  sdk_1.Formio.cdn = new CDN_1.default();
13
13
  sdk_1.Formio.Providers = providers_1.default;
14
- sdk_1.Formio.version = '5.6.1';
14
+ sdk_1.Formio.version = '5.7.0-api912.2';
15
15
  CDN_1.default.defaultCDN = sdk_1.Formio.version.includes('rc')
16
16
  ? 'https://cdn.test-form.io'
17
17
  : 'https://cdn.form.io';
package/lib/cjs/PDF.d.ts CHANGED
@@ -2,6 +2,7 @@ export default class PDF extends Webform {
2
2
  constructor(element: any, options: any);
3
3
  components: any[];
4
4
  init(): void;
5
+ forwardRefreshEventsToIframe(): void;
5
6
  render(): any;
6
7
  submitButton: any;
7
8
  attachComponents(element: any, components: any, container: any): Promise<void>;
package/lib/cjs/PDF.js CHANGED
@@ -19,10 +19,7 @@ const isTrustedIframeMessage = (event, form) => {
19
19
  var _a;
20
20
  const iframeWindow = (_a = form === null || form === void 0 ? void 0 : form.iframeElement) === null || _a === void 0 ? void 0 : _a.contentWindow;
21
21
  const iframeOrigin = (form === null || form === void 0 ? void 0 : form.iframeElement) && getIframeOrigin(form.iframeElement);
22
- return Boolean(iframeWindow &&
23
- iframeOrigin &&
24
- event.source === iframeWindow &&
25
- event.origin === iframeOrigin);
22
+ return Boolean(iframeWindow && iframeOrigin && event.source === iframeWindow && event.origin === iframeOrigin);
26
23
  };
27
24
  class PDF extends Webform_1.default {
28
25
  constructor(element, options) {
@@ -31,7 +28,9 @@ class PDF extends Webform_1.default {
31
28
  this.components = [];
32
29
  }
33
30
  init() {
31
+ this.options.pdfShell = true;
34
32
  super.init();
33
+ this.forwardRefreshEventsToIframe();
35
34
  // Handle an iframe submission.
36
35
  this.on('iframe-submission', (submission) => this.setValue(submission, {
37
36
  fromIframe: true,
@@ -62,6 +61,17 @@ class PDF extends Webform_1.default {
62
61
  // Re-emit pageEnd without the iframe- prefix for consumers tracking PDF scroll position.
63
62
  this.on('iframe-pageEnd', (data) => this.emit('pageEnd', data), true);
64
63
  }
64
+ // The iframe renders the components in its own realm with its own emitter, so an event they
65
+ // refresh on only reaches them across the bridge.
66
+ forwardRefreshEventsToIframe() {
67
+ const refreshEvents = new Set();
68
+ (0, utils_1.eachComponent)(this.component.components, (component) => {
69
+ if (component.refreshOnEvent) {
70
+ refreshEvents.add(component.refreshOnEvent);
71
+ }
72
+ });
73
+ refreshEvents.forEach((event) => this.on(event, () => this.postMessage({ name: 'emit', data: event }), true));
74
+ }
65
75
  render() {
66
76
  this.submitButton = this.addComponent({
67
77
  disabled: this.form.disableWizardSubmit,
@@ -318,7 +328,13 @@ if (typeof window !== 'undefined') {
318
328
  if (eventData.compPath) {
319
329
  const comp = form.getComponent(eventData.compPath);
320
330
  if (comp) {
321
- comp.emit(eventData.name, eventData.data);
331
+ // Emit component instance for focus/blur events to match payload of those events on other forms
332
+ if (['focus', 'blur'].includes(eventData.name)) {
333
+ comp.emit(eventData.name, comp);
334
+ }
335
+ else {
336
+ comp.emit(eventData.name, eventData.data);
337
+ }
322
338
  }
323
339
  }
324
340
  else {
@@ -19,6 +19,7 @@ export default class PDFBuilder extends WebformBuilder {
19
19
  file: boolean;
20
20
  htmlelement: boolean;
21
21
  signrequestsignature: boolean;
22
+ datasource: boolean;
22
23
  };
23
24
  };
24
25
  basic: boolean;
@@ -51,6 +51,7 @@ class PDFBuilder extends WebformBuilder_1.default {
51
51
  file: true,
52
52
  htmlelement: true,
53
53
  signrequestsignature: true,
54
+ datasource: true,
54
55
  },
55
56
  },
56
57
  basic: false,
@@ -293,8 +293,20 @@ declare class Webform extends NestedDataComponent {
293
293
  /**
294
294
  * Focus on selected component.
295
295
  * @param {string} key - The key of selected component.
296
+ * @param {string} [componentKey] - The schema key of the component that produced the error, if known.
296
297
  */
297
- focusOnComponent(key: string): void;
298
+ focusOnComponent(key: string, componentKey?: string | undefined): void;
299
+ /**
300
+ * Resolves the component that produced an error, to attach the error to it or to focus it from
301
+ * the error list. A checkbox with inputType 'radio' takes its `name` as its path, so several
302
+ * components can share `key`; the schema key of the component that produced the error tells them
303
+ * apart. The shared-path lookup is only consulted when `getComponent` picked another component,
304
+ * and only trusted when it finds the one with that schema key.
305
+ * @param {string} key - The path the error was reported at.
306
+ * @param {string} [componentKey] - The schema key of the component that produced the error, if known.
307
+ * @returns {any} - The component that produced the error, if found.
308
+ */
309
+ getErrorComponent(key: string, componentKey?: string | undefined): any;
298
310
  /**
299
311
  * @returns {boolean} - Whether or not the draft submission is being edited
300
312
  */
@@ -981,16 +981,19 @@ class Webform extends NestedDataComponent_1.default {
981
981
  }
982
982
  this.loadRefs(this.alert, { errorRef: 'multiple' });
983
983
  if (this.refs.errorRef && this.refs.errorRef.length) {
984
- this.refs.errorRef.forEach((el) => {
985
- this.addEventListener(el, 'click', (e) => {
986
- const key = e.currentTarget.dataset.componentKey;
987
- this.focusOnComponent(key);
988
- });
984
+ this.refs.errorRef.forEach((el, index) => {
985
+ var _a;
986
+ // The Nth link renders the Nth error; a custom template that reorders or filters the list
987
+ // fails this check, and the link then resolves by its key alone.
988
+ const listItem = (_a = options === null || options === void 0 ? void 0 : options.errors) === null || _a === void 0 ? void 0 : _a[index];
989
+ const isLinkForListItem = !!listItem && listItem.keyOrPath === el.dataset.componentKey;
990
+ const componentKey = isLinkForListItem ? listItem.componentKey : undefined;
991
+ const focusOnLinkedComponent = (e) => this.focusOnComponent(e.currentTarget.dataset.componentKey, componentKey);
992
+ this.addEventListener(el, 'click', focusOnLinkedComponent);
989
993
  this.addEventListener(el, 'keydown', (e) => {
990
994
  if (e.keyCode === 13) {
991
995
  e.preventDefault();
992
- const key = e.currentTarget.dataset.componentKey;
993
- this.focusOnComponent(key);
996
+ focusOnLinkedComponent(e);
994
997
  }
995
998
  });
996
999
  });
@@ -1000,16 +1003,36 @@ class Webform extends NestedDataComponent_1.default {
1000
1003
  /**
1001
1004
  * Focus on selected component.
1002
1005
  * @param {string} key - The key of selected component.
1006
+ * @param {string} [componentKey] - The schema key of the component that produced the error, if known.
1003
1007
  */
1004
- focusOnComponent(key) {
1008
+ focusOnComponent(key, componentKey) {
1005
1009
  if (key) {
1006
- const component = this.getComponent(key);
1010
+ const component = this.getErrorComponent(key, componentKey);
1007
1011
  if (component) {
1008
1012
  component.focus();
1009
1013
  component.scrollIntoView();
1010
1014
  }
1011
1015
  }
1012
1016
  }
1017
+ /**
1018
+ * Resolves the component that produced an error, to attach the error to it or to focus it from
1019
+ * the error list. A checkbox with inputType 'radio' takes its `name` as its path, so several
1020
+ * components can share `key`; the schema key of the component that produced the error tells them
1021
+ * apart. The shared-path lookup is only consulted when `getComponent` picked another component,
1022
+ * and only trusted when it finds the one with that schema key.
1023
+ * @param {string} key - The path the error was reported at.
1024
+ * @param {string} [componentKey] - The schema key of the component that produced the error, if known.
1025
+ * @returns {any} - The component that produced the error, if found.
1026
+ */
1027
+ getErrorComponent(key, componentKey) {
1028
+ const component = this.getComponent(key);
1029
+ if (!componentKey || (component === null || component === void 0 ? void 0 : component.component.key) === componentKey) {
1030
+ return component;
1031
+ }
1032
+ // GOTCHA(G-FJS06)
1033
+ const componentWithKey = (0, utils_1.resolveInstanceAtPath)(this.componentsMap, key, componentKey);
1034
+ return (componentWithKey === null || componentWithKey === void 0 ? void 0 : componentWithKey.component.key) === componentKey ? componentWithKey : component;
1035
+ }
1013
1036
  /**
1014
1037
  * @returns {boolean} - Whether or not the draft submission is being edited
1015
1038
  */
@@ -1045,29 +1068,29 @@ class Webform extends NestedDataComponent_1.default {
1045
1068
  return;
1046
1069
  }
1047
1070
  // Mark any components as invalid if in a custom message.
1048
- const componentErrors = {};
1071
+ const errorsByComponent = new Map();
1049
1072
  errors.forEach((err) => {
1050
- var _a, _b;
1073
+ var _a, _b, _c;
1051
1074
  const path = (0, utils_1.getStringFromComponentPath)(err.path) || ((_a = err.context) === null || _a === void 0 ? void 0 : _a.path) || ((_b = err.component) === null || _b === void 0 ? void 0 : _b.key);
1052
- if (!componentErrors[path]) {
1053
- componentErrors[path] = [];
1075
+ const component = path && this.getErrorComponent(path, (_c = err.component) === null || _c === void 0 ? void 0 : _c.key);
1076
+ if (!component) {
1077
+ return;
1078
+ }
1079
+ if (!errorsByComponent.has(component)) {
1080
+ errorsByComponent.set(component, []);
1054
1081
  }
1055
- componentErrors[path].push(err);
1082
+ errorsByComponent.get(component).push(err);
1056
1083
  });
1057
1084
  // Iterate through all of our component errors and apply them to the components.
1058
- for (let path in componentErrors) {
1059
- const component = this.getComponent(path);
1060
- const errors = componentErrors[path];
1061
- if (component) {
1062
- component.serverErrors = errors.filter((err) => err.fromServer);
1063
- component.setCustomValidity(errors, true);
1064
- }
1065
- }
1085
+ errorsByComponent.forEach((componentErrors, component) => {
1086
+ component.serverErrors = componentErrors.filter((err) => err.fromServer);
1087
+ component.setCustomValidity(componentErrors, true);
1088
+ });
1066
1089
  const displayedErrors = [];
1067
1090
  if (errors.length) {
1068
1091
  errors = lodash_1.default.uniqBy(errors, (error) => { var _a, _b; return [error.message, (_a = error.component) === null || _a === void 0 ? void 0 : _a.id, (_b = error.context) === null || _b === void 0 ? void 0 : _b.path].join(); });
1069
1092
  const createListItem = (message, index) => {
1070
- var _a, _b, _c;
1093
+ var _a, _b, _c, _d;
1071
1094
  const err = errors[index];
1072
1095
  const messageFromIndex = !lodash_1.default.isUndefined(index) && errors && errors[index];
1073
1096
  const keyOrPath = (messageFromIndex === null || messageFromIndex === void 0 ? void 0 : messageFromIndex.formattedKeyOrPath) ||
@@ -1083,6 +1106,7 @@ class Webform extends NestedDataComponent_1.default {
1083
1106
  return {
1084
1107
  message: (0, utils_1.unescapeHTML)(message),
1085
1108
  keyOrPath: formattedKeyOrPath,
1109
+ componentKey: (_d = err.component) === null || _d === void 0 ? void 0 : _d.key,
1086
1110
  };
1087
1111
  };
1088
1112
  errors.forEach(({ message, context, fromServer, component }, index) => {
@@ -1096,7 +1120,7 @@ class Webform extends NestedDataComponent_1.default {
1096
1120
  // GOTCHA(G-FJS02)
1097
1121
  // Only paint the alert from a subform when the root won't double paint it to avoid a painful flicker
1098
1122
  if (this === this.root || !((_a = this.root) === null || _a === void 0 ? void 0 : _a.submitted)) {
1099
- (_b = this.root) === null || _b === void 0 ? void 0 : _b.setAlert('danger', errorsList);
1123
+ (_b = this.root) === null || _b === void 0 ? void 0 : _b.setAlert('danger', errorsList, { errors: displayedErrors });
1100
1124
  }
1101
1125
  if (triggerEvent) {
1102
1126
  this.emit('error', errors);
@@ -1517,7 +1541,7 @@ class Webform extends NestedDataComponent_1.default {
1517
1541
  }
1518
1542
  }
1519
1543
  loadTranslations() {
1520
- var _a;
1544
+ var _a, _b, _c;
1521
1545
  // We only need a resolve since if translations cannot load, we still want to proceed and render the for
1522
1546
  let translationsLoadResolve;
1523
1547
  const promise = new Promise((resolve) => {
@@ -1526,9 +1550,16 @@ class Webform extends NestedDataComponent_1.default {
1526
1550
  const translationsUrl = typeof ((_a = this.options.i18n) === null || _a === void 0 ? void 0 : _a.translationsUrl) === 'string'
1527
1551
  ? this.options.i18n.translationsUrl
1528
1552
  : null;
1553
+ // Translations can also live in a Form.io resource, one submission per language.
1554
+ const languageResource = (_b = this.options.i18n) === null || _b === void 0 ? void 0 : _b.resource;
1555
+ const usesLanguageResource = Boolean(languageResource === null || languageResource === void 0 ? void 0 : languageResource.form);
1529
1556
  if (translationsUrl) {
1530
1557
  const url = this.sanitize(this.interpolate(translationsUrl, {}), this.shouldSanitizeValue);
1531
- Formio_1.Formio.makeStaticRequest(url, 'GET')
1558
+ // Simple CORS GET: skip Content-Type / token so static hosts need no preflight.
1559
+ Formio_1.Formio.makeStaticRequest(url, 'GET', null, {
1560
+ headers: { Accept: 'application/json' },
1561
+ noToken: true,
1562
+ })
1532
1563
  .then((response) => {
1533
1564
  let languages = response;
1534
1565
  try {
@@ -1555,6 +1586,28 @@ class Webform extends NestedDataComponent_1.default {
1555
1586
  translationsLoadResolve();
1556
1587
  });
1557
1588
  }
1589
+ else if (usesLanguageResource) {
1590
+ const { form: resourceId, codeComponent, mapComponent } = languageResource;
1591
+ const language = ((_c = this.i18next) === null || _c === void 0 ? void 0 : _c.originalLanguage) || this.options.language;
1592
+ const languageQuery = `data.${codeComponent}=${encodeURIComponent(language)}`;
1593
+ // The resource is addressed by id, so it needs the /form/ route — the bare
1594
+ // `{project}/{alias}` route resolves by path only and would 404 on an id.
1595
+ const url = `${Formio_1.Formio.getProjectUrl()}/form/${resourceId}/submission?${languageQuery}&limit=1`;
1596
+ Formio_1.Formio.makeStaticRequest(url, 'GET')
1597
+ .then((submissions) => {
1598
+ var _a, _b;
1599
+ const translations = (_b = (_a = submissions === null || submissions === void 0 ? void 0 : submissions[0]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b[mapComponent];
1600
+ // No submission for this language just leaves the form in its default language.
1601
+ if (translations) {
1602
+ this.addLanguage(language, translations, true);
1603
+ }
1604
+ translationsLoadResolve();
1605
+ })
1606
+ .catch((err) => {
1607
+ console.warn(err);
1608
+ translationsLoadResolve();
1609
+ });
1610
+ }
1558
1611
  else {
1559
1612
  translationsLoadResolve();
1560
1613
  }
@@ -112,7 +112,7 @@ declare class Wizard extends Webform {
112
112
  pageId(page: any): any;
113
113
  onChange(flags: any, changed: any, modified: any, changes: any): void;
114
114
  checkValidity(data: any, dirty: any, row: any, currentPageOnly: any, childErrors?: any[]): any;
115
- focusOnComponent(key: any): void | Promise<void>;
115
+ focusOnComponent(key: any, componentKey: any): void | Promise<void>;
116
116
  triggerButtonCaptcha(page: any): void;
117
117
  }
118
118
  declare namespace Wizard {
package/lib/cjs/Wizard.js CHANGED
@@ -129,9 +129,15 @@ class Wizard extends Webform_1.default {
129
129
  { name: 'cancel', method: 'cancel' },
130
130
  { name: 'previous', method: 'prevPage' },
131
131
  { name: 'next', method: 'nextPage' },
132
- { name: 'submit', method: 'submit', args: [false, {
132
+ {
133
+ name: 'submit',
134
+ method: 'submit',
135
+ args: [
136
+ false,
137
+ {
133
138
  state: 'submitted',
134
- }],
139
+ },
140
+ ],
135
141
  },
136
142
  ].forEach((button) => {
137
143
  if (this.hasButton(button.name)) {
@@ -1022,8 +1028,8 @@ class Wizard extends Webform_1.default {
1022
1028
  get errors() {
1023
1029
  return !this.isLastPage() && !this.submitted ? this.currentPage.errors : super.errors;
1024
1030
  }
1025
- focusOnComponent(key) {
1026
- const component = this.getComponent(key);
1031
+ focusOnComponent(key, componentKey) {
1032
+ const component = this.getErrorComponent(key, componentKey);
1027
1033
  if (component) {
1028
1034
  let topPanel = component.parent;
1029
1035
  while (!(topPanel.parent instanceof Wizard)) {
@@ -1033,11 +1039,11 @@ class Wizard extends Webform_1.default {
1033
1039
  if (pageIndex >= 0) {
1034
1040
  const page = this.pages[pageIndex];
1035
1041
  if (page && page !== this.currentPage) {
1036
- return this.setPage(pageIndex).then(() => super.focusOnComponent(key));
1042
+ return this.setPage(pageIndex).then(() => super.focusOnComponent(key, componentKey));
1037
1043
  }
1038
1044
  }
1039
1045
  }
1040
- return super.focusOnComponent(key);
1046
+ return super.focusOnComponent(key, componentKey);
1041
1047
  }
1042
1048
  triggerButtonCaptcha(page) {
1043
1049
  if (!page.components) {
@@ -2606,19 +2606,6 @@ class Component extends Element_1.default {
2606
2606
  return Promise.resolve(editor);
2607
2607
  }
2608
2608
  else {
2609
- // Due to an issue with ckeditor not loading styles in the shadowdom (https://github.com/ckeditor/ckeditor5/issues/15824), we need to copy cke-styles to the shadowdom
2610
- let current = element;
2611
- while (current) {
2612
- if (current instanceof ShadowRoot) {
2613
- const ckeStyles = document.querySelector('style[data-cke="true"]');
2614
- const clone = document.createElement('style');
2615
- clone.setAttribute('data-cke', 'true');
2616
- clone.textContent = ckeStyles.textContent;
2617
- current.prepend(clone);
2618
- break;
2619
- }
2620
- current = current.parentNode || current.host;
2621
- }
2622
2609
  return ClassicEditor.create(element, settings).then((editor) => {
2623
2610
  editor.model.document.on('change', () => onChange(editor.data.get()));
2624
2611
  return editor;
@@ -2639,58 +2626,6 @@ class Component extends Element_1.default {
2639
2626
  return Promise.reject();
2640
2627
  }
2641
2628
  this.quill = new Quill(element, isIEBrowser ? Object.assign(Object.assign({}, settings), { modules: {} }) : settings);
2642
- const root = element.getRootNode();
2643
- if (root instanceof ShadowRoot && root.getSelection) {
2644
- const sel = this.quill.selection;
2645
- // 1. getNativeRange: read selection from shadowRoot instead of document
2646
- sel.getNativeRange = () => {
2647
- const shadowSelection = root.getSelection();
2648
- if (shadowSelection == null || shadowSelection.rangeCount <= 0)
2649
- return null;
2650
- const nativeRange = shadowSelection.getRangeAt(0);
2651
- if (nativeRange == null)
2652
- return null;
2653
- return sel.normalizeNative(nativeRange);
2654
- };
2655
- // 2. hasFocus: check shadowRoot.activeElement instead of document.activeElement
2656
- sel.hasFocus = () => {
2657
- return (root.activeElement === sel.root ||
2658
- (root.activeElement != null && sel.root.contains(root.activeElement)));
2659
- };
2660
- // 3. setNativeRange: use shadowRoot's selection to add/remove ranges
2661
- const origSetNativeRange = sel.setNativeRange.bind(sel);
2662
- sel.setNativeRange = (startNode, startOffset, endNode, endOffset, force) => {
2663
- // Delegate to original logic for the null case (blur)
2664
- if (startNode == null) {
2665
- origSetNativeRange(null);
2666
- return;
2667
- }
2668
- if (!sel.hasFocus()) {
2669
- sel.root.focus({ preventScroll: true });
2670
- }
2671
- const shadowSelection = root.getSelection();
2672
- if (shadowSelection == null)
2673
- return;
2674
- const { native } = sel.getNativeRange() || {};
2675
- endNode = endNode !== null && endNode !== void 0 ? endNode : startNode;
2676
- endOffset = endOffset !== null && endOffset !== void 0 ? endOffset : startOffset;
2677
- if (native == null ||
2678
- force ||
2679
- startNode !== native.startContainer ||
2680
- startOffset !== native.startOffset ||
2681
- endNode !== native.endContainer ||
2682
- endOffset !== native.endOffset) {
2683
- const range = document.createRange();
2684
- range.setStart(startNode, startOffset);
2685
- range.setEnd(endNode, endOffset);
2686
- shadowSelection.removeAllRanges();
2687
- shadowSelection.addRange(range);
2688
- }
2689
- };
2690
- document.addEventListener('selectionchange', () => {
2691
- sel.update();
2692
- });
2693
- }
2694
2629
  /** This block of code adds the [source] capabilities. See https://codepen.io/anon/pen/ZyEjrQ */
2695
2630
  const txtArea = document.createElement('textarea');
2696
2631
  txtArea.setAttribute('class', 'quill-source-code');
@@ -11,6 +11,19 @@ const Components_1 = __importDefault(require("../../Components"));
11
11
  */
12
12
  function default_1(...extend) {
13
13
  return Components_1.default.baseEditForm([
14
+ {
15
+ key: 'display',
16
+ components: [
17
+ {
18
+ key: 'labelWidth',
19
+ ignore: true,
20
+ },
21
+ {
22
+ key: 'labelMargin',
23
+ ignore: true,
24
+ },
25
+ ],
26
+ },
14
27
  {
15
28
  key: 'data',
16
29
  ignore: true,
@@ -831,6 +831,10 @@ class NestedComponent extends Field_1.default {
831
831
  // if no value is provided and noDefault flag, set empty value
832
832
  else if (flags.noDefault && component.allowData) {
833
833
  flags.resetValue = true;
834
+ // Preserve the live value instead of wiping it, so client-side calculations that read it don't blank out.
835
+ if (component.component.persistent === 'client-only') {
836
+ return false;
837
+ }
834
838
  return component.setValue(component.emptyValue, flags);
835
839
  }
836
840
  else if ((!this.rootPristine || component.visible) &&
@@ -5,6 +5,14 @@ exports.default = [
5
5
  key: 'labelPosition',
6
6
  ignore: true,
7
7
  },
8
+ {
9
+ key: 'labelWidth',
10
+ ignore: true,
11
+ },
12
+ {
13
+ key: 'labelMargin',
14
+ ignore: true,
15
+ },
8
16
  {
9
17
  key: 'placeholder',
10
18
  ignore: true,