@verbb/formie-browser 1.0.6 → 1.0.8

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/CHANGELOG.md +23 -0
  2. package/dist/chunks/_survey-presentations-CFsgToRT.js +4 -0
  3. package/dist/chunks/{address-finder-yXU5yMjG.js → address-finder-k7_BXaRe.js} +1 -1
  4. package/dist/chunks/{api-DMK8NSUI.js → api-BUb6P-xu.js} +57 -51
  5. package/dist/chunks/{api-CmwLRq_n.js → api-C8wyLC-s.js} +235 -193
  6. package/dist/chunks/{bpoint-DSjqRv63.js → bpoint-CwSw_p7A.js} +1 -1
  7. package/dist/chunks/{captcha-eu-CWJb64_j.js → captcha-eu-CGm8Lbrj.js} +2 -2
  8. package/dist/chunks/{conditions-DjZOtHfS.js → conditions-B6_nzpeE.js} +95 -83
  9. package/dist/chunks/{eway-D3ctTbEk.js → eway-B30BMlDp.js} +2 -2
  10. package/dist/chunks/{friendly-captcha-v1-ByXzENYZ.js → friendly-captcha-v1-C_2reOXr.js} +1 -1
  11. package/dist/chunks/{friendly-captcha-v2-BPdlmhQu.js → friendly-captcha-v2-CLSAbkqx.js} +1 -1
  12. package/dist/chunks/{go-cardless-BwASSYSK.js → go-cardless-BiF3iLel.js} +1 -1
  13. package/dist/chunks/{google-address-OhKilPWE.js → google-address-BH3Zhi06.js} +1 -1
  14. package/dist/chunks/{hcaptcha-CdCd9oEi.js → hcaptcha-CGLAmgjO.js} +2 -2
  15. package/dist/chunks/{loqate-Cp2zvwpE.js → loqate-CJPzBmSm.js} +1 -1
  16. package/dist/chunks/{mollie-COG-ko2D.js → mollie-D__SwtcH.js} +1 -1
  17. package/dist/chunks/{moneris-CMH8PlRc.js → moneris-KJS1BKx7.js} +1 -1
  18. package/dist/chunks/{opayo-D2jsR5vh.js → opayo-A_LIkc-v.js} +4 -4
  19. package/dist/chunks/{paddle-DxF88RfC.js → paddle-BjbOOb0m.js} +2 -2
  20. package/dist/chunks/{paypal-b5pnwrA2.js → paypal-CyjHIV8v.js} +2 -2
  21. package/dist/chunks/{payway-BJYu9DIR.js → payway-a_cyrr3j.js} +2 -2
  22. package/dist/chunks/{recaptcha-enterprise-BSwi8IVX.js → recaptcha-enterprise-XPId5-bS.js} +19 -20
  23. package/dist/chunks/{recaptcha-shared-B4zAescW.js → recaptcha-shared-CLYJvojk.js} +15 -3
  24. package/dist/chunks/{recaptcha-v2-checkbox-CnDQmsKa.js → recaptcha-v2-checkbox-Cy-0PEPc.js} +2 -2
  25. package/dist/chunks/{recaptcha-v2-invisible-Cz5uIlp-.js → recaptcha-v2-invisible-BwRH540O.js} +2 -2
  26. package/dist/chunks/recaptcha-v3-No4Ykrhk.js +28 -0
  27. package/dist/chunks/scripts-CbQ7agX3.js +30 -0
  28. package/dist/chunks/{snaptcha-vz1RnZ15.js → snaptcha-DKx98oon.js} +1 -1
  29. package/dist/chunks/{square-BP8RWAKB.js → square-Bga6WXHV.js} +2 -2
  30. package/dist/chunks/{stripe-MihN63uC.js → stripe-BlqVocl3.js} +2 -2
  31. package/dist/chunks/{summary-D3AjxpYN.js → summary-Dks0gXS3.js} +42 -38
  32. package/dist/chunks/survey-likert-DmJ3klhP.js +20 -0
  33. package/dist/chunks/survey-rank-DeUenftw.js +127 -0
  34. package/dist/chunks/survey-rating-BPUYDgBJ.js +88 -0
  35. package/dist/chunks/{turnstile-D_Pi1CKM.js → turnstile-DJakPzQD.js} +3 -3
  36. package/dist/css/formie-theme.css +1 -0
  37. package/dist/css/theme/fields/_survey-presentations.css +396 -0
  38. package/dist/index.js +651 -597
  39. package/dist/js/contracts/schema.d.ts +5 -0
  40. package/dist/js/contracts/schema.d.ts.map +1 -1
  41. package/dist/js/core/create-formie-client.d.ts.map +1 -1
  42. package/dist/js/core/error-aria-live.d.ts +6 -0
  43. package/dist/js/core/error-aria-live.d.ts.map +1 -0
  44. package/dist/js/core/page-client-event.d.ts +8 -4
  45. package/dist/js/core/page-client-event.d.ts.map +1 -1
  46. package/dist/js/core/submit-result-state.d.ts.map +1 -1
  47. package/dist/js/core/submit-result-ui.d.ts.map +1 -1
  48. package/dist/js/modules/captchas/factories.d.ts.map +1 -1
  49. package/dist/js/modules/captchas/host.d.ts +1 -0
  50. package/dist/js/modules/captchas/host.d.ts.map +1 -1
  51. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -1
  52. package/dist/js/modules/captchas/recaptcha-shared.d.ts +1 -0
  53. package/dist/js/modules/captchas/recaptcha-shared.d.ts.map +1 -1
  54. package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -1
  55. package/dist/js/modules/captchas/turnstile.d.ts.map +1 -1
  56. package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -1
  57. package/dist/js/modules/fields/index.d.ts.map +1 -1
  58. package/dist/js/modules/fields/summary.d.ts.map +1 -1
  59. package/dist/js/modules/fields/survey-likert.d.ts +3 -0
  60. package/dist/js/modules/fields/survey-likert.d.ts.map +1 -0
  61. package/dist/js/modules/fields/survey-rank.d.ts +3 -0
  62. package/dist/js/modules/fields/survey-rank.d.ts.map +1 -0
  63. package/dist/js/modules/fields/survey-rating.d.ts +3 -0
  64. package/dist/js/modules/fields/survey-rating.d.ts.map +1 -0
  65. package/dist/js/transport/forms-api.d.ts.map +1 -1
  66. package/dist/js/utils/form-started-at.d.ts +2 -0
  67. package/dist/js/utils/form-started-at.d.ts.map +1 -0
  68. package/dist/js/utils/scripts.d.ts.map +1 -1
  69. package/dist/js/utils/unload-warning.d.ts.map +1 -1
  70. package/dist/js/validation/enter-key-guard.d.ts +2 -0
  71. package/dist/js/validation/enter-key-guard.d.ts.map +1 -0
  72. package/dist/js/validation/types.d.ts +1 -0
  73. package/dist/js/validation/types.d.ts.map +1 -1
  74. package/dist/js/validation/validator.d.ts.map +1 -1
  75. package/package.json +2 -2
  76. package/src/css/formie-theme.css +1 -0
  77. package/src/css/theme/fields/_survey-presentations.css +396 -0
  78. package/src/js/contracts/schema.ts +6 -0
  79. package/src/js/core/create-formie-client.ts +21 -1
  80. package/src/js/core/error-aria-live.ts +45 -0
  81. package/src/js/core/page-client-event.ts +86 -18
  82. package/src/js/core/submit-result-state.ts +10 -2
  83. package/src/js/core/submit-result-ui.ts +8 -4
  84. package/src/js/modules/captchas/factories.ts +4 -0
  85. package/src/js/modules/captchas/host.ts +14 -2
  86. package/src/js/modules/captchas/recaptcha-enterprise.ts +14 -11
  87. package/src/js/modules/captchas/recaptcha-shared.ts +14 -0
  88. package/src/js/modules/captchas/recaptcha-v3.ts +7 -4
  89. package/src/js/modules/captchas/turnstile.ts +9 -3
  90. package/src/js/modules/fields/conditions/effects.ts +42 -0
  91. package/src/js/modules/fields/index.ts +3 -0
  92. package/src/js/modules/fields/summary.ts +17 -3
  93. package/src/js/modules/fields/survey-likert.ts +50 -0
  94. package/src/js/modules/fields/survey-rank.ts +385 -0
  95. package/src/js/modules/fields/survey-rating.ts +182 -0
  96. package/src/js/transport/forms-api.ts +1 -0
  97. package/src/js/utils/form-started-at.ts +25 -0
  98. package/src/js/utils/scripts.ts +16 -0
  99. package/src/js/utils/unload-warning.ts +1 -0
  100. package/src/js/validation/enter-key-guard.ts +63 -0
  101. package/src/js/validation/types.ts +1 -0
  102. package/src/js/validation/validator.ts +14 -5
  103. package/dist/chunks/recaptcha-v3-BtbbP111.js +0 -27
  104. package/dist/chunks/scripts-6CYSUGKQ.js +0 -24
@@ -0,0 +1,396 @@
1
+ @layer formie-theme {
2
+
3
+ .formie-likert-field-layout {
4
+ display: table;
5
+ width: 100%;
6
+ table-layout: fixed;
7
+ border-collapse: collapse;
8
+ border-spacing: 0;
9
+ }
10
+
11
+ .formie-likert-field-layout thead {
12
+ display: table-header-group;
13
+ }
14
+
15
+ .formie-likert-field-layout tbody {
16
+ display: table-row-group;
17
+ }
18
+
19
+ .formie-likert-field-layout tr {
20
+ display: table-row;
21
+ }
22
+
23
+ .formie-likert-field-layout th,
24
+ .formie-likert-field-layout td {
25
+ display: table-cell;
26
+ }
27
+
28
+ .formie-likert-column-label {
29
+ text-align: center;
30
+ color: var(--formie-survey-likert-label-color, var(--formie-color-heading));
31
+ word-break: break-word;
32
+ padding: 0 0.35rem 0.5rem;
33
+ vertical-align: bottom;
34
+ font-size: var(--formie-survey-likert-column-label-font-size, 0.875rem);
35
+ line-height: 1.3;
36
+ }
37
+
38
+ [data-formie-field-control]:has(> [data-formie-likert-multiple-rows]) {
39
+ overflow-x: auto;
40
+ -webkit-overflow-scrolling: touch;
41
+ }
42
+
43
+ @media (min-width: 40rem) {
44
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] {
45
+ width: max-content;
46
+ min-width: 100%;
47
+ }
48
+
49
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-column-label {
50
+ min-width: var(--formie-survey-likert-column-min-width, 5.5rem);
51
+ }
52
+
53
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-option {
54
+ min-width: var(--formie-survey-likert-column-min-width, 5.5rem);
55
+ }
56
+ }
57
+
58
+ .formie-likert-row-label-header,
59
+ .formie-likert-row-label {
60
+ text-align: left;
61
+ color: var(--formie-survey-likert-label-color, var(--formie-color-heading));
62
+ word-break: break-word;
63
+ padding: 0.65rem 0.75rem 0.65rem 0;
64
+ vertical-align: middle;
65
+ font-weight: var(--formie-font-weight-medium);
66
+ }
67
+
68
+ .formie-likert-row-label-header {
69
+ padding-bottom: 0.5rem;
70
+ font-weight: var(--formie-font-weight-normal);
71
+ }
72
+
73
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] {
74
+ table-layout: auto;
75
+ }
76
+
77
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-row-label-header,
78
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-row-label {
79
+ width: 1%;
80
+ white-space: nowrap;
81
+ padding-right: 1rem;
82
+ }
83
+
84
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row .formie-likert-row-label {
85
+ border-top-left-radius: var(--formie-border-radius);
86
+ border-bottom-left-radius: var(--formie-border-radius);
87
+ }
88
+
89
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row .formie-likert-option:first-child {
90
+ border-top-left-radius: 0;
91
+ border-bottom-left-radius: 0;
92
+ }
93
+
94
+ .formie-likert-inputs-row--alt .formie-likert-row-label,
95
+ .formie-likert-inputs-row--alt .formie-likert-option {
96
+ background-color: var(--formie-survey-likert-option-background-alt, var(--formie-color-surface));
97
+ }
98
+
99
+ .formie-likert-option {
100
+ text-align: center;
101
+ vertical-align: middle;
102
+ padding: 0.65rem 0.35rem;
103
+ background-color: var(--formie-survey-likert-option-background, var(--formie-color-surface-muted));
104
+ }
105
+
106
+ .formie-likert-inputs-row .formie-likert-option:first-child {
107
+ border-top-left-radius: var(--formie-border-radius);
108
+ border-bottom-left-radius: var(--formie-border-radius);
109
+ }
110
+
111
+ .formie-likert-inputs-row .formie-likert-option:last-child {
112
+ border-top-right-radius: var(--formie-border-radius);
113
+ border-bottom-right-radius: var(--formie-border-radius);
114
+ }
115
+
116
+ .formie-likert-option .formie-radio-option-label {
117
+ padding-left: 0;
118
+ width: var(--formie-check-size);
119
+ height: var(--formie-check-size);
120
+ line-height: var(--formie-check-size);
121
+ overflow: hidden;
122
+ }
123
+
124
+ .formie-likert-option .formie-likert-option-label-text {
125
+ display: none;
126
+ }
127
+
128
+ .formie-likert-option .formie-radio-option-label::before {
129
+ top: 0;
130
+ left: 0;
131
+ }
132
+
133
+ @media (max-width: 39.999rem) {
134
+ [data-formie-field-control]:has(> [data-formie-likert-field-layout]) {
135
+ overflow-x: visible;
136
+ }
137
+
138
+ .formie-likert-field-layout {
139
+ display: block;
140
+ width: 100%;
141
+ table-layout: auto;
142
+ border-radius: 0;
143
+ background-color: transparent;
144
+ padding: 0;
145
+ }
146
+
147
+ .formie-likert-column-labels {
148
+ position: absolute;
149
+ width: 1px;
150
+ height: 1px;
151
+ margin: -1px;
152
+ padding: 0;
153
+ overflow: hidden;
154
+ clip: rect(0, 0, 0, 0);
155
+ clip-path: inset(50%);
156
+ white-space: nowrap;
157
+ border: 0;
158
+ }
159
+
160
+ .formie-likert-field-layout thead,
161
+ .formie-likert-field-layout tbody,
162
+ .formie-likert-field-layout tr,
163
+ .formie-likert-field-layout th,
164
+ .formie-likert-field-layout td {
165
+ display: block;
166
+ width: 100%;
167
+ }
168
+
169
+ .formie-likert-inputs-row {
170
+ margin: 0 0 var(--formie-space-2);
171
+ padding: var(--formie-space-2) var(--formie-space-3);
172
+ border-radius: var(--formie-border-radius);
173
+ background-color: var(--formie-survey-likert-option-background, var(--formie-color-surface-muted));
174
+ }
175
+
176
+ .formie-likert-inputs-row:last-child {
177
+ margin-bottom: 0;
178
+ }
179
+
180
+ .formie-likert-inputs-row--alt {
181
+ background-color: var(--formie-survey-likert-option-background-alt, var(--formie-color-surface));
182
+ }
183
+
184
+ .formie-likert-option {
185
+ display: block;
186
+ padding: var(--formie-space-2) 0;
187
+ text-align: left;
188
+ background-color: transparent;
189
+ border-radius: 0;
190
+ min-width: 0;
191
+ }
192
+
193
+ .formie-likert-option:first-child {
194
+ padding-top: 0;
195
+ }
196
+
197
+ .formie-likert-option:last-child {
198
+ padding-bottom: 0;
199
+ }
200
+
201
+ .formie-likert-option .formie-radio-option-label {
202
+ width: auto;
203
+ height: auto;
204
+ overflow: visible;
205
+ padding-left: var(--formie-check-label-padding-left);
206
+ line-height: var(--formie-check-line-height);
207
+ font-weight: var(--formie-font-weight-normal);
208
+ color: var(--formie-color-text);
209
+ }
210
+
211
+ .formie-likert-option .formie-likert-option-label-text {
212
+ display: inline;
213
+ }
214
+
215
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-row-label {
216
+ display: block;
217
+ width: 100%;
218
+ padding: 0 0 var(--formie-space-2);
219
+ font-weight: var(--formie-font-weight-medium);
220
+ background-color: transparent;
221
+ white-space: normal;
222
+ }
223
+
224
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row {
225
+ border-top: 0;
226
+ }
227
+
228
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row .formie-likert-row-label {
229
+ border-radius: 0;
230
+ }
231
+
232
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row .formie-likert-option:first-child {
233
+ border-radius: 0;
234
+ }
235
+
236
+ .formie-likert-field-layout[data-formie-likert-multiple-rows] .formie-likert-inputs-row .formie-likert-option:last-child {
237
+ border-radius: 0;
238
+ }
239
+ }
240
+
241
+ .formie-rank-field-layout {
242
+ width: 100%;
243
+ }
244
+
245
+ .formie-rank-list,
246
+ .formie-rank-field-layout .formie-rank-list.formie-field-options {
247
+ list-style: none;
248
+ margin: var(--formie-space-1) 0 0;
249
+ padding: 0;
250
+ display: flex;
251
+ flex-direction: column;
252
+ flex-wrap: nowrap;
253
+ gap: var(--formie-survey-rank-list-gap, var(--formie-space-2));
254
+ }
255
+
256
+ .formie-rank-list[data-formie-rank-sorting="true"] {
257
+ user-select: none;
258
+ }
259
+
260
+ .formie-rank-item {
261
+ display: flex;
262
+ align-items: center;
263
+ gap: var(--formie-space-2);
264
+ margin: 0;
265
+ padding: var(--formie-space-2) var(--formie-space-3);
266
+ border: var(--formie-border-width) solid var(--formie-survey-rank-item-border-color, var(--formie-color-border));
267
+ border-radius: var(--formie-border-radius);
268
+ background: var(--formie-survey-rank-item-background, var(--formie-color-surface));
269
+ cursor: move;
270
+ touch-action: none;
271
+ }
272
+
273
+ .formie-rank-placeholder {
274
+ box-sizing: border-box;
275
+ margin: 0;
276
+ padding: 0;
277
+ list-style: none;
278
+ border: 0;
279
+ background: transparent;
280
+ }
281
+
282
+ .formie-rank-handle {
283
+ display: inline-flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ width: 0.875rem;
287
+ height: 1.125rem;
288
+ padding: 0;
289
+ border: 0;
290
+ background: transparent;
291
+ color: var(--formie-survey-rank-handle-color, var(--formie-color-text-muted));
292
+ opacity: 0.6;
293
+ cursor: move;
294
+ flex-shrink: 0;
295
+ pointer-events: none;
296
+ }
297
+
298
+ .formie-rank-handle::before {
299
+ display: block;
300
+ width: 13px;
301
+ height: 0.875rem;
302
+ content: "";
303
+ background: currentColor;
304
+ mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3E%3Cpath d='M288 128C288 92.7 259.3 64 224 64C188.7 64 160 92.7 160 128C160 163.3 188.7 192 224 192C259.3 192 288 163.3 288 128zM288 320C288 284.7 259.3 256 224 256C188.7 256 160 284.7 160 320C160 355.3 188.7 384 224 384C259.3 384 288 355.3 288 320zM160 512C160 547.3 188.7 576 224 576C259.3 576 288 547.3 288 512C288 476.7 259.3 448 224 448C188.7 448 160 476.7 160 512zM480 128C480 92.7 451.3 64 416 64C380.7 64 352 92.7 352 128C352 163.3 380.7 192 416 192C451.3 192 480 163.3 480 128zM352 320C352 355.3 380.7 384 416 384C451.3 384 480 355.3 480 320C480 284.7 451.3 256 416 256C380.7 256 352 284.7 352 320zM480 512C480 476.7 451.3 448 416 448C380.7 448 352 476.7 352 512C352 547.3 380.7 576 416 576C451.3 576 480 547.3 480 512z'/%3E%3C/svg%3E") center / contain no-repeat;
305
+ }
306
+
307
+ .formie-rank-option-label {
308
+ flex: 1 1 auto;
309
+ font-size: var(--formie-check-font-size);
310
+ line-height: var(--formie-check-line-height);
311
+ }
312
+
313
+ .formie-rank-input {
314
+ display: none;
315
+ }
316
+
317
+ .formie-rating-field-layout {
318
+ width: 100%;
319
+ }
320
+
321
+ .formie-rating-stars,
322
+ .formie-rating-field-layout .formie-rating-stars.formie-field-options {
323
+ display: inline-flex;
324
+ flex-wrap: nowrap;
325
+ align-items: center;
326
+ padding: var(--formie-space-2) 0;
327
+ gap: 0;
328
+ --formie-survey-rating-star-size: 26px;
329
+ --formie-survey-rating-star-spacing: var(--formie-space-1);
330
+ --formie-survey-rating-star-outline: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2023%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M22.2%208.05333L15.7466%207.49333L13.2266%201.56C12.7733%200.48%2011.2266%200.48%2010.7733%201.56L8.25331%207.50667L1.81331%208.05333C0.639975%208.14667%200.159975%209.61333%201.05331%2010.3867L5.94664%2014.6267L4.47998%2020.92C4.21331%2022.0667%205.45331%2022.9733%206.46664%2022.36L12%2019.0267L17.5333%2022.3733C18.5466%2022.9867%2019.7866%2022.08%2019.52%2020.9333L18.0533%2014.6267L22.9466%2010.3867C23.84%209.61333%2023.3733%208.14667%2022.2%208.05333ZM12%2016.5333L6.98664%2019.56L8.31998%2013.8533L3.89331%2010.0133L9.73331%209.50667L12%204.13333L14.28%209.52L20.12%2010.0267L15.6933%2013.8667L17.0266%2019.5733L12%2016.5333Z%22%20fill%3D%22%23E27730%22%2F%3E%3C%2Fsvg%3E");
331
+ --formie-survey-rating-star-filled: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2023%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M12%2019.0267L17.5333%2022.3733C18.5466%2022.9867%2019.7866%2022.08%2019.52%2020.9333L18.0533%2014.64L22.9466%2010.4C23.84%209.62667%2023.36%208.16%2022.1866%208.06667L15.7466%207.52L13.2266%201.57334C12.7733%200.493336%2011.2266%200.493336%2010.7733%201.57334L8.25331%207.50667L1.81331%208.05334C0.639975%208.14667%200.159975%209.61334%201.05331%2010.3867L5.94664%2014.6267L4.47997%2020.92C4.21331%2022.0667%205.45331%2022.9733%206.46664%2022.36L12%2019.0267Z%22%20fill%3D%22%23E27730%22%2F%3E%3C%2Fsvg%3E");
332
+ }
333
+
334
+ .formie-rating-option {
335
+ position: relative;
336
+ margin: 0;
337
+ gap: 0;
338
+ align-items: center;
339
+ cursor: pointer;
340
+ }
341
+
342
+ .formie-rating-input {
343
+ position: absolute;
344
+ width: 1px;
345
+ height: 1px;
346
+ margin: -1px;
347
+ padding: 0;
348
+ overflow: hidden;
349
+ clip: rect(0, 0, 0, 0);
350
+ clip-path: inset(50%);
351
+ white-space: nowrap;
352
+ border: 0;
353
+ }
354
+
355
+ .formie-rating-option-label {
356
+ display: inline-flex;
357
+ cursor: pointer;
358
+ padding-inline: calc(var(--formie-survey-rating-star-spacing, var(--formie-space-1)) / 2);
359
+ }
360
+
361
+ .formie-rating-star {
362
+ display: block;
363
+ width: var(--formie-survey-rating-star-size, 26px);
364
+ height: var(--formie-survey-rating-star-size, 26px);
365
+ background: center / contain no-repeat;
366
+ background-image: var(--formie-survey-rating-star-outline);
367
+ }
368
+
369
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="1"] .formie-rating-option:nth-child(-n+1) .formie-rating-star,
370
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="2"] .formie-rating-option:nth-child(-n+2) .formie-rating-star,
371
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="3"] .formie-rating-option:nth-child(-n+3) .formie-rating-star,
372
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="4"] .formie-rating-option:nth-child(-n+4) .formie-rating-star,
373
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="5"] .formie-rating-option:nth-child(-n+5) .formie-rating-star,
374
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="6"] .formie-rating-option:nth-child(-n+6) .formie-rating-star,
375
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="7"] .formie-rating-option:nth-child(-n+7) .formie-rating-star,
376
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="8"] .formie-rating-option:nth-child(-n+8) .formie-rating-star,
377
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="9"] .formie-rating-option:nth-child(-n+9) .formie-rating-star,
378
+ .formie-rating-stars:not([data-formie-rating-hover])[data-formie-rating-value="10"] .formie-rating-option:nth-child(-n+10) .formie-rating-star,
379
+ .formie-rating-stars[data-formie-rating-hover="1"] .formie-rating-option:nth-child(-n+1) .formie-rating-star,
380
+ .formie-rating-stars[data-formie-rating-hover="2"] .formie-rating-option:nth-child(-n+2) .formie-rating-star,
381
+ .formie-rating-stars[data-formie-rating-hover="3"] .formie-rating-option:nth-child(-n+3) .formie-rating-star,
382
+ .formie-rating-stars[data-formie-rating-hover="4"] .formie-rating-option:nth-child(-n+4) .formie-rating-star,
383
+ .formie-rating-stars[data-formie-rating-hover="5"] .formie-rating-option:nth-child(-n+5) .formie-rating-star,
384
+ .formie-rating-stars[data-formie-rating-hover="6"] .formie-rating-option:nth-child(-n+6) .formie-rating-star,
385
+ .formie-rating-stars[data-formie-rating-hover="7"] .formie-rating-option:nth-child(-n+7) .formie-rating-star,
386
+ .formie-rating-stars[data-formie-rating-hover="8"] .formie-rating-option:nth-child(-n+8) .formie-rating-star,
387
+ .formie-rating-stars[data-formie-rating-hover="9"] .formie-rating-option:nth-child(-n+9) .formie-rating-star,
388
+ .formie-rating-stars[data-formie-rating-hover="10"] .formie-rating-option:nth-child(-n+10) .formie-rating-star {
389
+ background-image: var(--formie-survey-rating-star-filled);
390
+ }
391
+
392
+ .formie-rating-input:focus-visible+.formie-rating-option-label .formie-rating-star {
393
+ outline: 2px solid var(--formie-color-focus-ring);
394
+ outline-offset: 2px;
395
+ }
396
+ }
@@ -43,6 +43,11 @@ export type FormRedirect = {
43
43
  target?: 'same-tab' | 'new-tab';
44
44
  };
45
45
 
46
+ export type FormClientEvent = {
47
+ event: string;
48
+ payload: Record<string, string>;
49
+ };
50
+
46
51
  export type FormSubmitResult = {
47
52
  ok: boolean;
48
53
  action?: FormAction;
@@ -56,6 +61,7 @@ export type FormSubmitResult = {
56
61
  nextPage?: { id: string } | null;
57
62
  redirect?: FormRedirect | null;
58
63
  submitData?: unknown[];
64
+ clientEvents?: FormClientEvent[];
59
65
  meta?: Record<string, unknown>;
60
66
  };
61
67
 
@@ -11,7 +11,8 @@ import type { FormieModuleDefinition, FormieModuleInstance } from '#contracts/mo
11
11
  import type { FormEndpointPayload, FormModuleManifest, FormSubmitResult } from '#contracts/schema';
12
12
  import type { ThemeClassMap } from '#contracts/theme';
13
13
  import { dispatchFormieDomEvent } from '#core/dom-events';
14
- import { dispatchPageClientEventForSubmit } from '#core/page-client-event';
14
+ import { getErrorAriaLivePreference } from '#core/error-aria-live';
15
+ import { dispatchPageClientEventForSubmit, dispatchPendingClientEventsFromForm } from '#core/page-client-event';
15
16
  import { syncPageTabErrors } from '#core/page-tab-errors';
16
17
  import {
17
18
  enhanceServerRenderedFieldErrors,
@@ -27,11 +28,13 @@ import { loadModulesFromManifest } from '#modules/loader';
27
28
  import { registerThemeClassMap } from '#theme/theme-classes';
28
29
  import { runSubmitPipeline } from '#submit/pipeline';
29
30
  import { clearSubmissionOnUnload, requestGraphqlRender, requestRefreshTokens, requestRender, requestSetPage } from '#transport/forms-api';
31
+ import { ensureFormStartedAt } from '#utils/form-started-at';
30
32
  import { createDebug } from '#utils/debug';
31
33
  import { createFormUnloadWarningGuard } from '#utils/unload-warning';
32
34
  import { getValidationScope } from '#validation/scope';
33
35
  import { FormieValidator } from '#validation/validator';
34
36
  import { bindSubmitReadiness } from '#validation/submit-readiness';
37
+ import { bindEnterKeyGuard } from '#validation/enter-key-guard';
35
38
 
36
39
  type InternalInstanceState = {
37
40
  options: FormMountOptions;
@@ -989,6 +992,7 @@ export function createFormieClient(): FormieClient {
989
992
 
990
993
  const validator = form ? new FormieValidator(form, {
991
994
  live: parseBooleanDatasetValue(form.dataset.formieValidationOnFocus),
995
+ errorAriaLive: getErrorAriaLivePreference(form),
992
996
  errorMessage: form.dataset.formieErrorMessage || '',
993
997
  fieldContainerErrorClass: resolvedThemeClassMap.fieldLayoutError || [],
994
998
  inputErrorClass: resolvedThemeClassMap.fieldControlError || [],
@@ -1014,6 +1018,16 @@ export function createFormieClient(): FormieClient {
1014
1018
  }
1015
1019
 
1016
1020
  if (form) {
1021
+ ensureFormStartedAt(form);
1022
+
1023
+ if (normalizedOptions.themeConfig && typeof normalizedOptions.themeConfig === 'object') {
1024
+ form.setAttribute('data-formie-theme-config', JSON.stringify(normalizedOptions.themeConfig));
1025
+ }
1026
+
1027
+ if (normalizedOptions.theme && normalizedOptions.theme !== 'formie') {
1028
+ form.setAttribute('data-formie-frontend-theme', normalizedOptions.theme);
1029
+ }
1030
+
1017
1031
  if (renderPayload || normalizedOptions.endpoint || (target as HTMLElement).dataset.formieEndpoint) {
1018
1032
  normalizeHeadlessManagedUrls(target, form, normalizedOptions);
1019
1033
  }
@@ -1166,6 +1180,7 @@ export function createFormieClient(): FormieClient {
1166
1180
 
1167
1181
  if (validator) {
1168
1182
  unbinds.push(bindSubmitReadiness(form, validator, target));
1183
+ unbinds.push(bindEnterKeyGuard(form));
1169
1184
  }
1170
1185
 
1171
1186
  await refreshTokensIfNeeded(target, normalizedOptions, form);
@@ -1242,6 +1257,11 @@ export function createFormieClient(): FormieClient {
1242
1257
  id,
1243
1258
  mode: normalizedOptions.mode as FormMode,
1244
1259
  });
1260
+
1261
+ if (form instanceof HTMLFormElement) {
1262
+ dispatchPendingClientEventsFromForm(form);
1263
+ }
1264
+
1245
1265
  debug.log('Mount complete.', {
1246
1266
  id,
1247
1267
  target: getTargetDebugLabel(target),
@@ -0,0 +1,45 @@
1
+ export type ErrorAriaLivePreference = 'polite' | 'assertive' | 'off';
2
+
3
+ export function getErrorAriaLivePreference(form: HTMLFormElement): ErrorAriaLivePreference {
4
+ const value = (form.dataset.formieErrorAriaLive || 'polite').trim().toLowerCase();
5
+
6
+ if (value === 'assertive' || value === 'off') {
7
+ return value;
8
+ }
9
+
10
+ return 'polite';
11
+ }
12
+
13
+ export function resolveValidationErrorAriaLive(
14
+ preference: ErrorAriaLivePreference,
15
+ submitted: boolean,
16
+ ): 'polite' | 'assertive' | null {
17
+ if (preference === 'off') {
18
+ return null;
19
+ }
20
+
21
+ if (!submitted) {
22
+ return 'polite';
23
+ }
24
+
25
+ return preference;
26
+ }
27
+
28
+ export function resolveSubmitErrorAriaLive(preference: ErrorAriaLivePreference): 'polite' | 'assertive' | null {
29
+ if (preference === 'off') {
30
+ return null;
31
+ }
32
+
33
+ return preference;
34
+ }
35
+
36
+ export function applyErrorAriaLive(element: HTMLElement, ariaLive: 'polite' | 'assertive' | null): void {
37
+ if (ariaLive) {
38
+ element.setAttribute('aria-live', ariaLive);
39
+ element.setAttribute('aria-atomic', 'true');
40
+ return;
41
+ }
42
+
43
+ element.removeAttribute('aria-live');
44
+ element.removeAttribute('aria-atomic');
45
+ }
@@ -3,11 +3,17 @@ import { createDebug } from '#utils/debug';
3
3
  const debug = createDebug('general', 'page-client-event');
4
4
 
5
5
  const CLIENT_EVENT_ATTR = 'data-formie-client-event';
6
+ const PENDING_CLIENT_EVENTS_ATTR = 'data-formie-pending-client-events';
6
7
 
7
8
  type ClientEventPayload = {
8
9
  fields?: Array<{ label?: string; value?: string }>;
9
10
  };
10
11
 
12
+ export type ResolvedClientEvent = {
13
+ event: string;
14
+ payload: Record<string, string>;
15
+ };
16
+
11
17
  function escapePageIdForSelector(pageId: string): string {
12
18
  if (typeof window !== 'undefined' && window.CSS?.escape) {
13
19
  return window.CSS.escape(pageId);
@@ -70,11 +76,83 @@ function buildPayloadObject(fields: Array<{ label?: string; value?: string }>):
70
76
  return out;
71
77
  }
72
78
 
79
+ function normalizeResolvedClientEvents(events: unknown): ResolvedClientEvent[] {
80
+ if (!Array.isArray(events)) {
81
+ return [];
82
+ }
83
+
84
+ return events
85
+ .map((item) => {
86
+ if (!item || typeof item !== 'object') {
87
+ return null;
88
+ }
89
+
90
+ const record = item as ResolvedClientEvent;
91
+ const eventName = typeof record.event === 'string' ? record.event.trim() : '';
92
+ const payload = record.payload && typeof record.payload === 'object' ? record.payload : null;
93
+
94
+ if (!eventName || !payload) {
95
+ return null;
96
+ }
97
+
98
+ return {
99
+ event: eventName,
100
+ payload,
101
+ };
102
+ })
103
+ .filter((item): item is ResolvedClientEvent => item !== null);
104
+ }
105
+
106
+ export function dispatchResolvedClientEvents(form: HTMLFormElement, events: ResolvedClientEvent[]): void {
107
+ if (!events.length) {
108
+ return;
109
+ }
110
+
111
+ const win = window as Window & { dataLayer?: unknown[] };
112
+ win.dataLayer = win.dataLayer || [];
113
+
114
+ events.forEach((item) => {
115
+ win.dataLayer!.push(item.payload);
116
+
117
+ form.dispatchEvent(new CustomEvent('formie:client-event', {
118
+ bubbles: true,
119
+ detail: {
120
+ event: item.event,
121
+ payload: item.payload,
122
+ },
123
+ }));
124
+ });
125
+
126
+ debug.log('Dispatched resolved client events.', {
127
+ count: events.length,
128
+ events: events.map((item) => item.event),
129
+ });
130
+ }
131
+
132
+ export function dispatchPendingClientEventsFromForm(form: HTMLFormElement): void {
133
+ const rawAttr = form.getAttribute(PENDING_CLIENT_EVENTS_ATTR);
134
+
135
+ if (!rawAttr?.trim()) {
136
+ return;
137
+ }
138
+
139
+ try {
140
+ const parsed = JSON.parse(rawAttr) as unknown;
141
+ const events = normalizeResolvedClientEvents(parsed);
142
+
143
+ if (events.length) {
144
+ dispatchResolvedClientEvents(form, events);
145
+ }
146
+ } catch {
147
+ debug.warn('Invalid pending client events JSON on form element.');
148
+ } finally {
149
+ form.removeAttribute(PENDING_CLIENT_EVENTS_ATTR);
150
+ }
151
+ }
152
+
73
153
  /**
74
- * When the builder enables JavaScript events for a page, the theme emits
75
- * `data-formie-client-event` on that page's section. On each successful
76
- * **submit** (not back/save), push the configured key/value object to
77
- * `window.dataLayer` (when present) and dispatch `formie:client-event`.
154
+ * Legacy static page attribute dispatch. Prefer server-resolved `clientEvents`
155
+ * from the submit response when available.
78
156
  */
79
157
  export function dispatchPageClientEventForSubmit(form: HTMLFormElement, action: string): void {
80
158
  if (action !== 'submit') {
@@ -112,18 +190,8 @@ export function dispatchPageClientEventForSubmit(form: HTMLFormElement, action:
112
190
  }
113
191
 
114
192
  const payload = buildPayloadObject(config.fields);
115
- const win = window as Window & { dataLayer?: unknown[] };
116
-
117
- win.dataLayer = win.dataLayer || [];
118
- win.dataLayer.push(payload);
119
-
120
- form.dispatchEvent(new CustomEvent('formie:client-event', {
121
- bubbles: true,
122
- detail: { payload },
123
- }));
124
-
125
- debug.log('Dispatched page client event.', {
126
- pageId,
127
- keys: Object.keys(payload),
128
- });
193
+ dispatchResolvedClientEvents(form, [{
194
+ event: typeof payload.event === 'string' && payload.event !== '' ? payload.event : 'formPageSubmission',
195
+ payload,
196
+ }]);
129
197
  }
@@ -1,6 +1,7 @@
1
1
  import type { FormAction } from '#contracts/common';
2
2
  import type { FormSubmitResult } from '#contracts/schema';
3
- import { dispatchPageClientEventForSubmit } from '#core/page-client-event';
3
+ import { dispatchFormieDomEvent } from '#core/dom-events';
4
+ import { dispatchPageClientEventForSubmit, dispatchResolvedClientEvents } from '#core/page-client-event';
4
5
  import { syncPageTabErrors } from '#core/page-tab-errors';
5
6
  import { addThemeClasses, removeThemeClasses, toggleThemeClasses } from '#theme/theme-classes';
6
7
  import { createDebug } from '#utils/debug';
@@ -567,7 +568,11 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
567
568
  return;
568
569
  }
569
570
 
570
- dispatchPageClientEventForSubmit(form, action);
571
+ if (Array.isArray(result.clientEvents) && result.clientEvents.length > 0) {
572
+ dispatchResolvedClientEvents(form, result.clientEvents);
573
+ } else {
574
+ dispatchPageClientEventForSubmit(form, action);
575
+ }
571
576
 
572
577
  if (result.nextPage?.id) {
573
578
  // Advancing pages is treated as a fresh validation cycle for the next page.
@@ -576,6 +581,9 @@ export function applySubmitResultState(form: HTMLFormElement, result: FormSubmit
576
581
  const validator = formWithValidationApi.formieValidation;
577
582
  validator?.resetLiveState();
578
583
  applyPageState(form, result.nextPage.id);
584
+ dispatchFormieDomEvent(form, 'formie:page:navigate:after', {
585
+ pageId: result.nextPage.id,
586
+ });
579
587
  debug.log('Advanced to next page.', {
580
588
  nextPageId: result.nextPage.id,
581
589
  });