@verbb/formie-browser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE.md +40 -0
  3. package/README.md +34 -0
  4. package/dist/chunks/address-finder-DfMCiW89.js +47 -0
  5. package/dist/chunks/api-CbqEMQT5.js +181 -0
  6. package/dist/chunks/api-DE7LfK-R.js +960 -0
  7. package/dist/chunks/api-DOfDzYC_.js +538 -0
  8. package/dist/chunks/async-B3DUf1GZ.js +26 -0
  9. package/dist/chunks/bpoint-Ciy3yY9Q.js +34 -0
  10. package/dist/chunks/calculations-CkYAqO_-.js +197 -0
  11. package/dist/chunks/captcha-eu-DnOWhMwr.js +43 -0
  12. package/dist/chunks/checkbox-radio-0x7Tc0br.js +197 -0
  13. package/dist/chunks/chunk-K6L4z4UQ.js +24 -0
  14. package/dist/chunks/conditions-4fXKhEJS.js +609 -0
  15. package/dist/chunks/date-picker-B6iZkjHS.js +6204 -0
  16. package/dist/chunks/debug-KnZeKYBI.js +39 -0
  17. package/dist/chunks/dist-D09GnXMW.js +2663 -0
  18. package/dist/chunks/event-names-DamGPtXR.js +51 -0
  19. package/dist/chunks/eway-DEAYcwT0.js +86 -0
  20. package/dist/chunks/field-references.keys-BpBZ_quS.js +24 -0
  21. package/dist/chunks/field-references.resolver-Ba6xhiJC.js +183 -0
  22. package/dist/chunks/file-upload-Bh63PQSE.js +430 -0
  23. package/dist/chunks/friendly-captcha-v1-CqO4WVre.js +40 -0
  24. package/dist/chunks/friendly-captcha-v2-CyykcJcM.js +47 -0
  25. package/dist/chunks/friendly-challenge-Dg8XkStd.js +1101 -0
  26. package/dist/chunks/go-cardless-CuND59rR.js +26 -0
  27. package/dist/chunks/google-address--uR8WDSm.js +208 -0
  28. package/dist/chunks/hcaptcha-CmaFUesv.js +72 -0
  29. package/dist/chunks/hidden-CYnZYple.js +36 -0
  30. package/dist/chunks/http-18nn97DZ.js +29 -0
  31. package/dist/chunks/i18n-vUh-KGiH.js +55 -0
  32. package/dist/chunks/loqate-BICNJlVK.js +97 -0
  33. package/dist/chunks/mollie-DwlsgHZ1.js +26 -0
  34. package/dist/chunks/moneris-B_IFZFTx.js +159 -0
  35. package/dist/chunks/opayo-U2x_TOII.js +192 -0
  36. package/dist/chunks/paddle-BqXFrc79.js +75 -0
  37. package/dist/chunks/paypal-Cn_DYGDb.js +121 -0
  38. package/dist/chunks/payway-Rnq796eC.js +75 -0
  39. package/dist/chunks/phone-country-B6Me4lK0.js +3317 -0
  40. package/dist/chunks/place-kit-ldUl-u9w.js +56 -0
  41. package/dist/chunks/placekit-autocomplete.esm-D-lGeaZl.js +1771 -0
  42. package/dist/chunks/recaptcha-enterprise-DPJNyv1X.js +72 -0
  43. package/dist/chunks/recaptcha-shared-DTI4qWVR.js +22 -0
  44. package/dist/chunks/recaptcha-v2-checkbox-zFjpvJ5c.js +49 -0
  45. package/dist/chunks/recaptcha-v2-invisible-CnYtkNvz.js +62 -0
  46. package/dist/chunks/recaptcha-v3-EAlWhnkX.js +33 -0
  47. package/dist/chunks/repeater-CXD1eLSn.js +151 -0
  48. package/dist/chunks/rich-text-DkmZRhGj.js +442 -0
  49. package/dist/chunks/scripts-BGD_iU_6.js +41 -0
  50. package/dist/chunks/sdk-B7u9fTlP.js +2103 -0
  51. package/dist/chunks/shared-DC6_1u8X.js +85 -0
  52. package/dist/chunks/signature-E9KyYXS1.js +765 -0
  53. package/dist/chunks/snaptcha-CCDunGeb.js +8 -0
  54. package/dist/chunks/square-BLqK51rS.js +61 -0
  55. package/dist/chunks/stripe-B8gHpZNC.js +273 -0
  56. package/dist/chunks/styles-BIh6g7V_.js +22 -0
  57. package/dist/chunks/summary-EcNE0cvg.js +191 -0
  58. package/dist/chunks/table-yxEDL6kA.js +124 -0
  59. package/dist/chunks/text-limit-D0H_Ca2c.js +179 -0
  60. package/dist/chunks/theme-classes-vSHpdCUO.js +59 -0
  61. package/dist/chunks/turnstile-DP0bdR7T.js +68 -0
  62. package/dist/chunks/utils-ByrEVYrJ.js +49584 -0
  63. package/dist/css/formie-base.css +78 -0
  64. package/dist/css/formie-theme.css +19 -0
  65. package/dist/css/formie.css +2 -0
  66. package/dist/css/theme/_buttons.css +249 -0
  67. package/dist/css/theme/_loading.css +37 -0
  68. package/dist/css/theme/_messages.css +39 -0
  69. package/dist/css/theme/_progress.css +62 -0
  70. package/dist/css/theme/_tokens.css +361 -0
  71. package/dist/css/theme/_typography.css +70 -0
  72. package/dist/css/theme/fields/_address.css +17 -0
  73. package/dist/css/theme/fields/_check-radio.css +108 -0
  74. package/dist/css/theme/fields/_file.css +58 -0
  75. package/dist/css/theme/fields/_group.css +13 -0
  76. package/dist/css/theme/fields/_input.css +48 -0
  77. package/dist/css/theme/fields/_nested.css +19 -0
  78. package/dist/css/theme/fields/_repeater.css +69 -0
  79. package/dist/css/theme/fields/_rich-text.css +201 -0
  80. package/dist/css/theme/fields/_select.css +37 -0
  81. package/dist/css/theme/fields/_signature.css +39 -0
  82. package/dist/css/theme/fields/_summary.css +53 -0
  83. package/dist/css/theme/fields/_table.css +121 -0
  84. package/dist/css/theme/fields/_text-limit.css +10 -0
  85. package/dist/css/theme/forms/_field.css +62 -0
  86. package/dist/css/theme/forms/_form.css +166 -0
  87. package/dist/css/theme/integrations/_payment-modal.css +32 -0
  88. package/dist/css/theme/integrations/_paypal.css +10 -0
  89. package/dist/css/theme/integrations/_payway.css +10 -0
  90. package/dist/css/theme/integrations/_stripe.css +24 -0
  91. package/dist/css/theme/utilities/_accessibility.css +13 -0
  92. package/dist/css/theme-base/_controls.css +41 -0
  93. package/dist/css/theme-base/_primitives.css +34 -0
  94. package/dist/index.d.ts +30 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +3206 -0
  97. package/dist/js/compatibility/dom-adapter.d.ts +12 -0
  98. package/dist/js/compatibility/dom-adapter.d.ts.map +1 -0
  99. package/dist/js/compatibility/event-map.d.ts +25 -0
  100. package/dist/js/compatibility/event-map.d.ts.map +1 -0
  101. package/dist/js/compatibility/validator-adapter.d.ts +17 -0
  102. package/dist/js/compatibility/validator-adapter.d.ts.map +1 -0
  103. package/dist/js/contracts/client.d.ts +40 -0
  104. package/dist/js/contracts/client.d.ts.map +1 -0
  105. package/dist/js/contracts/common.d.ts +5 -0
  106. package/dist/js/contracts/common.d.ts.map +1 -0
  107. package/dist/js/contracts/modules.d.ts +47 -0
  108. package/dist/js/contracts/modules.d.ts.map +1 -0
  109. package/dist/js/contracts/schema.d.ts +79 -0
  110. package/dist/js/contracts/schema.d.ts.map +1 -0
  111. package/dist/js/contracts/theme.d.ts +2 -0
  112. package/dist/js/contracts/theme.d.ts.map +1 -0
  113. package/dist/js/core/create-formie-client.d.ts +3 -0
  114. package/dist/js/core/create-formie-client.d.ts.map +1 -0
  115. package/dist/js/core/dom-events.d.ts +2 -0
  116. package/dist/js/core/dom-events.d.ts.map +1 -0
  117. package/dist/js/core/formie.d.ts +27 -0
  118. package/dist/js/core/formie.d.ts.map +1 -0
  119. package/dist/js/core/hydrate-modules.d.ts +22 -0
  120. package/dist/js/core/hydrate-modules.d.ts.map +1 -0
  121. package/dist/js/core/page-client-event.d.ts +9 -0
  122. package/dist/js/core/page-client-event.d.ts.map +1 -0
  123. package/dist/js/core/page-tab-errors.d.ts +2 -0
  124. package/dist/js/core/page-tab-errors.d.ts.map +1 -0
  125. package/dist/js/core/submit-flow.d.ts +21 -0
  126. package/dist/js/core/submit-flow.d.ts.map +1 -0
  127. package/dist/js/core/submit-result-state.d.ts +8 -0
  128. package/dist/js/core/submit-result-state.d.ts.map +1 -0
  129. package/dist/js/core/submit-result-ui.d.ts +10 -0
  130. package/dist/js/core/submit-result-ui.d.ts.map +1 -0
  131. package/dist/js/events/event-bus.d.ts +21 -0
  132. package/dist/js/events/event-bus.d.ts.map +1 -0
  133. package/dist/js/modules/address/address-finder.d.ts +2 -0
  134. package/dist/js/modules/address/address-finder.d.ts.map +1 -0
  135. package/dist/js/modules/address/api.d.ts +8 -0
  136. package/dist/js/modules/address/api.d.ts.map +1 -0
  137. package/dist/js/modules/address/constants.d.ts +15 -0
  138. package/dist/js/modules/address/constants.d.ts.map +1 -0
  139. package/dist/js/modules/address/factories.d.ts +35 -0
  140. package/dist/js/modules/address/factories.d.ts.map +1 -0
  141. package/dist/js/modules/address/google-address.d.ts +2 -0
  142. package/dist/js/modules/address/google-address.d.ts.map +1 -0
  143. package/dist/js/modules/address/host.d.ts +30 -0
  144. package/dist/js/modules/address/host.d.ts.map +1 -0
  145. package/dist/js/modules/address/index.d.ts +3 -0
  146. package/dist/js/modules/address/index.d.ts.map +1 -0
  147. package/dist/js/modules/address/loqate.d.ts +2 -0
  148. package/dist/js/modules/address/loqate.d.ts.map +1 -0
  149. package/dist/js/modules/address/place-kit.d.ts +2 -0
  150. package/dist/js/modules/address/place-kit.d.ts.map +1 -0
  151. package/dist/js/modules/captchas/api.d.ts +9 -0
  152. package/dist/js/modules/captchas/api.d.ts.map +1 -0
  153. package/dist/js/modules/captchas/captcha-eu.d.ts +2 -0
  154. package/dist/js/modules/captchas/captcha-eu.d.ts.map +1 -0
  155. package/dist/js/modules/captchas/constants.d.ts +5 -0
  156. package/dist/js/modules/captchas/constants.d.ts.map +1 -0
  157. package/dist/js/modules/captchas/factories.d.ts +63 -0
  158. package/dist/js/modules/captchas/factories.d.ts.map +1 -0
  159. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts +2 -0
  160. package/dist/js/modules/captchas/friendly-captcha-v1.d.ts.map +1 -0
  161. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts +2 -0
  162. package/dist/js/modules/captchas/friendly-captcha-v2.d.ts.map +1 -0
  163. package/dist/js/modules/captchas/hcaptcha.d.ts +2 -0
  164. package/dist/js/modules/captchas/hcaptcha.d.ts.map +1 -0
  165. package/dist/js/modules/captchas/host.d.ts +78 -0
  166. package/dist/js/modules/captchas/host.d.ts.map +1 -0
  167. package/dist/js/modules/captchas/index.d.ts +3 -0
  168. package/dist/js/modules/captchas/index.d.ts.map +1 -0
  169. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts +2 -0
  170. package/dist/js/modules/captchas/recaptcha-enterprise.d.ts.map +1 -0
  171. package/dist/js/modules/captchas/recaptcha-shared.d.ts +26 -0
  172. package/dist/js/modules/captchas/recaptcha-shared.d.ts.map +1 -0
  173. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts +2 -0
  174. package/dist/js/modules/captchas/recaptcha-v2-checkbox.d.ts.map +1 -0
  175. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts +2 -0
  176. package/dist/js/modules/captchas/recaptcha-v2-invisible.d.ts.map +1 -0
  177. package/dist/js/modules/captchas/recaptcha-v3.d.ts +2 -0
  178. package/dist/js/modules/captchas/recaptcha-v3.d.ts.map +1 -0
  179. package/dist/js/modules/captchas/snaptcha.d.ts +2 -0
  180. package/dist/js/modules/captchas/snaptcha.d.ts.map +1 -0
  181. package/dist/js/modules/captchas/turnstile.d.ts +2 -0
  182. package/dist/js/modules/captchas/turnstile.d.ts.map +1 -0
  183. package/dist/js/modules/captchas/utils.d.ts +13 -0
  184. package/dist/js/modules/captchas/utils.d.ts.map +1 -0
  185. package/dist/js/modules/fields/calculations.d.ts +3 -0
  186. package/dist/js/modules/fields/calculations.d.ts.map +1 -0
  187. package/dist/js/modules/fields/checkbox-radio.d.ts +3 -0
  188. package/dist/js/modules/fields/checkbox-radio.d.ts.map +1 -0
  189. package/dist/js/modules/fields/conditions/config.d.ts +5 -0
  190. package/dist/js/modules/fields/conditions/config.d.ts.map +1 -0
  191. package/dist/js/modules/fields/conditions/effects.d.ts +2 -0
  192. package/dist/js/modules/fields/conditions/effects.d.ts.map +1 -0
  193. package/dist/js/modules/fields/conditions/evaluator.d.ts +6 -0
  194. package/dist/js/modules/fields/conditions/evaluator.d.ts.map +1 -0
  195. package/dist/js/modules/fields/conditions/references.d.ts +5 -0
  196. package/dist/js/modules/fields/conditions/references.d.ts.map +1 -0
  197. package/dist/js/modules/fields/conditions/transforms.d.ts +3 -0
  198. package/dist/js/modules/fields/conditions/transforms.d.ts.map +1 -0
  199. package/dist/js/modules/fields/conditions/types.d.ts +30 -0
  200. package/dist/js/modules/fields/conditions/types.d.ts.map +1 -0
  201. package/dist/js/modules/fields/conditions/values.d.ts +5 -0
  202. package/dist/js/modules/fields/conditions/values.d.ts.map +1 -0
  203. package/dist/js/modules/fields/conditions.d.ts +3 -0
  204. package/dist/js/modules/fields/conditions.d.ts.map +1 -0
  205. package/dist/js/modules/fields/date-picker.d.ts +3 -0
  206. package/dist/js/modules/fields/date-picker.d.ts.map +1 -0
  207. package/dist/js/modules/fields/file-upload.d.ts +3 -0
  208. package/dist/js/modules/fields/file-upload.d.ts.map +1 -0
  209. package/dist/js/modules/fields/hidden.d.ts +3 -0
  210. package/dist/js/modules/fields/hidden.d.ts.map +1 -0
  211. package/dist/js/modules/fields/index.d.ts +3 -0
  212. package/dist/js/modules/fields/index.d.ts.map +1 -0
  213. package/dist/js/modules/fields/phone-country.d.ts +3 -0
  214. package/dist/js/modules/fields/phone-country.d.ts.map +1 -0
  215. package/dist/js/modules/fields/repeater.d.ts +3 -0
  216. package/dist/js/modules/fields/repeater.d.ts.map +1 -0
  217. package/dist/js/modules/fields/rich-text.d.ts +3 -0
  218. package/dist/js/modules/fields/rich-text.d.ts.map +1 -0
  219. package/dist/js/modules/fields/shared.d.ts +20 -0
  220. package/dist/js/modules/fields/shared.d.ts.map +1 -0
  221. package/dist/js/modules/fields/signature.d.ts +3 -0
  222. package/dist/js/modules/fields/signature.d.ts.map +1 -0
  223. package/dist/js/modules/fields/summary.d.ts +3 -0
  224. package/dist/js/modules/fields/summary.d.ts.map +1 -0
  225. package/dist/js/modules/fields/table.d.ts +3 -0
  226. package/dist/js/modules/fields/table.d.ts.map +1 -0
  227. package/dist/js/modules/fields/text-limit.d.ts +3 -0
  228. package/dist/js/modules/fields/text-limit.d.ts.map +1 -0
  229. package/dist/js/modules/loader.d.ts +11 -0
  230. package/dist/js/modules/loader.d.ts.map +1 -0
  231. package/dist/js/modules/payments/api.d.ts +8 -0
  232. package/dist/js/modules/payments/api.d.ts.map +1 -0
  233. package/dist/js/modules/payments/bpoint.d.ts +2 -0
  234. package/dist/js/modules/payments/bpoint.d.ts.map +1 -0
  235. package/dist/js/modules/payments/constants.d.ts +5 -0
  236. package/dist/js/modules/payments/constants.d.ts.map +1 -0
  237. package/dist/js/modules/payments/eway.d.ts +9 -0
  238. package/dist/js/modules/payments/eway.d.ts.map +1 -0
  239. package/dist/js/modules/payments/factories.d.ts +54 -0
  240. package/dist/js/modules/payments/factories.d.ts.map +1 -0
  241. package/dist/js/modules/payments/go-cardless.d.ts +2 -0
  242. package/dist/js/modules/payments/go-cardless.d.ts.map +1 -0
  243. package/dist/js/modules/payments/host.d.ts +70 -0
  244. package/dist/js/modules/payments/host.d.ts.map +1 -0
  245. package/dist/js/modules/payments/index.d.ts +3 -0
  246. package/dist/js/modules/payments/index.d.ts.map +1 -0
  247. package/dist/js/modules/payments/mollie.d.ts +2 -0
  248. package/dist/js/modules/payments/mollie.d.ts.map +1 -0
  249. package/dist/js/modules/payments/moneris.d.ts +2 -0
  250. package/dist/js/modules/payments/moneris.d.ts.map +1 -0
  251. package/dist/js/modules/payments/opayo.d.ts +25 -0
  252. package/dist/js/modules/payments/opayo.d.ts.map +1 -0
  253. package/dist/js/modules/payments/paddle.d.ts +2 -0
  254. package/dist/js/modules/payments/paddle.d.ts.map +1 -0
  255. package/dist/js/modules/payments/paypal.d.ts +2 -0
  256. package/dist/js/modules/payments/paypal.d.ts.map +1 -0
  257. package/dist/js/modules/payments/payway.d.ts +2 -0
  258. package/dist/js/modules/payments/payway.d.ts.map +1 -0
  259. package/dist/js/modules/payments/square.d.ts +2 -0
  260. package/dist/js/modules/payments/square.d.ts.map +1 -0
  261. package/dist/js/modules/payments/stripe.d.ts +2 -0
  262. package/dist/js/modules/payments/stripe.d.ts.map +1 -0
  263. package/dist/js/modules/payments/utils.d.ts +17 -0
  264. package/dist/js/modules/payments/utils.d.ts.map +1 -0
  265. package/dist/js/modules/registry.d.ts +9 -0
  266. package/dist/js/modules/registry.d.ts.map +1 -0
  267. package/dist/js/modules/styles.d.ts +2 -0
  268. package/dist/js/modules/styles.d.ts.map +1 -0
  269. package/dist/js/submit/pipeline.d.ts +18 -0
  270. package/dist/js/submit/pipeline.d.ts.map +1 -0
  271. package/dist/js/theme/theme-classes.d.ts +10 -0
  272. package/dist/js/theme/theme-classes.d.ts.map +1 -0
  273. package/dist/js/transport/forms-api.d.ts +11 -0
  274. package/dist/js/transport/forms-api.d.ts.map +1 -0
  275. package/dist/js/utils/async.d.ts +8 -0
  276. package/dist/js/utils/async.d.ts.map +1 -0
  277. package/dist/js/utils/debug.d.ts +10 -0
  278. package/dist/js/utils/debug.d.ts.map +1 -0
  279. package/dist/js/utils/event-names.d.ts +13 -0
  280. package/dist/js/utils/event-names.d.ts.map +1 -0
  281. package/dist/js/utils/field-references.d.ts +6 -0
  282. package/dist/js/utils/field-references.d.ts.map +1 -0
  283. package/dist/js/utils/field-references.keys.d.ts +4 -0
  284. package/dist/js/utils/field-references.keys.d.ts.map +1 -0
  285. package/dist/js/utils/field-references.parser.d.ts +3 -0
  286. package/dist/js/utils/field-references.parser.d.ts.map +1 -0
  287. package/dist/js/utils/field-references.registry.d.ts +3 -0
  288. package/dist/js/utils/field-references.registry.d.ts.map +1 -0
  289. package/dist/js/utils/field-references.resolver.d.ts +4 -0
  290. package/dist/js/utils/field-references.resolver.d.ts.map +1 -0
  291. package/dist/js/utils/field-references.types.d.ts +27 -0
  292. package/dist/js/utils/field-references.types.d.ts.map +1 -0
  293. package/dist/js/utils/fields.d.ts +5 -0
  294. package/dist/js/utils/fields.d.ts.map +1 -0
  295. package/dist/js/utils/http.d.ts +9 -0
  296. package/dist/js/utils/http.d.ts.map +1 -0
  297. package/dist/js/utils/i18n.d.ts +7 -0
  298. package/dist/js/utils/i18n.d.ts.map +1 -0
  299. package/dist/js/utils/scripts.d.ts +13 -0
  300. package/dist/js/utils/scripts.d.ts.map +1 -0
  301. package/dist/js/utils/unload-warning.d.ts +10 -0
  302. package/dist/js/utils/unload-warning.d.ts.map +1 -0
  303. package/dist/js/validation/rules/email.d.ts +4 -0
  304. package/dist/js/validation/rules/email.d.ts.map +1 -0
  305. package/dist/js/validation/rules/match.d.ts +4 -0
  306. package/dist/js/validation/rules/match.d.ts.map +1 -0
  307. package/dist/js/validation/rules/number.d.ts +4 -0
  308. package/dist/js/validation/rules/number.d.ts.map +1 -0
  309. package/dist/js/validation/rules/required.d.ts +4 -0
  310. package/dist/js/validation/rules/required.d.ts.map +1 -0
  311. package/dist/js/validation/rules/shared.d.ts +7 -0
  312. package/dist/js/validation/rules/shared.d.ts.map +1 -0
  313. package/dist/js/validation/rules/url.d.ts +4 -0
  314. package/dist/js/validation/rules/url.d.ts.map +1 -0
  315. package/dist/js/validation/rules.d.ts +10 -0
  316. package/dist/js/validation/rules.d.ts.map +1 -0
  317. package/dist/js/validation/types.d.ts +44 -0
  318. package/dist/js/validation/types.d.ts.map +1 -0
  319. package/dist/js/validation/validator.d.ts +53 -0
  320. package/dist/js/validation/validator.d.ts.map +1 -0
  321. package/package.json +78 -0
  322. package/src/css/formie-base.css +78 -0
  323. package/src/css/formie-theme.css +19 -0
  324. package/src/css/formie.css +2 -0
  325. package/src/css/theme/_buttons.css +249 -0
  326. package/src/css/theme/_loading.css +37 -0
  327. package/src/css/theme/_messages.css +39 -0
  328. package/src/css/theme/_progress.css +62 -0
  329. package/src/css/theme/_tokens.css +361 -0
  330. package/src/css/theme/_typography.css +70 -0
  331. package/src/css/theme/fields/_address.css +17 -0
  332. package/src/css/theme/fields/_check-radio.css +108 -0
  333. package/src/css/theme/fields/_file.css +58 -0
  334. package/src/css/theme/fields/_group.css +13 -0
  335. package/src/css/theme/fields/_input.css +48 -0
  336. package/src/css/theme/fields/_nested.css +19 -0
  337. package/src/css/theme/fields/_repeater.css +69 -0
  338. package/src/css/theme/fields/_rich-text.css +201 -0
  339. package/src/css/theme/fields/_select.css +37 -0
  340. package/src/css/theme/fields/_signature.css +39 -0
  341. package/src/css/theme/fields/_summary.css +53 -0
  342. package/src/css/theme/fields/_table.css +121 -0
  343. package/src/css/theme/fields/_text-limit.css +10 -0
  344. package/src/css/theme/forms/_field.css +62 -0
  345. package/src/css/theme/forms/_form.css +166 -0
  346. package/src/css/theme/integrations/_payment-modal.css +32 -0
  347. package/src/css/theme/integrations/_paypal.css +10 -0
  348. package/src/css/theme/integrations/_payway.css +10 -0
  349. package/src/css/theme/integrations/_stripe.css +24 -0
  350. package/src/css/theme/utilities/_accessibility.css +13 -0
  351. package/src/css/theme-base/_controls.css +41 -0
  352. package/src/css/theme-base/_primitives.css +34 -0
  353. package/src/icons/rich-text/aligncenter.svg +6 -0
  354. package/src/icons/rich-text/alignleft.svg +6 -0
  355. package/src/icons/rich-text/alignright.svg +6 -0
  356. package/src/icons/rich-text/bold.svg +4 -0
  357. package/src/icons/rich-text/clear.svg +6 -0
  358. package/src/icons/rich-text/code.svg +4 -0
  359. package/src/icons/rich-text/heading1.svg +3 -0
  360. package/src/icons/rich-text/heading2.svg +3 -0
  361. package/src/icons/rich-text/image.svg +6 -0
  362. package/src/icons/rich-text/italic.svg +5 -0
  363. package/src/icons/rich-text/line.svg +3 -0
  364. package/src/icons/rich-text/link.svg +4 -0
  365. package/src/icons/rich-text/olist.svg +8 -0
  366. package/src/icons/rich-text/paragraph.svg +3 -0
  367. package/src/icons/rich-text/quote.svg +4 -0
  368. package/src/icons/rich-text/strikethrough.svg +4 -0
  369. package/src/icons/rich-text/ulist.svg +8 -0
  370. package/src/icons/rich-text/underline.svg +4 -0
  371. package/src/index.ts +125 -0
  372. package/src/js/compatibility/dom-adapter.ts +129 -0
  373. package/src/js/compatibility/event-map.ts +72 -0
  374. package/src/js/compatibility/validator-adapter.ts +105 -0
  375. package/src/js/contracts/client.ts +43 -0
  376. package/src/js/contracts/common.ts +14 -0
  377. package/src/js/contracts/modules.ts +53 -0
  378. package/src/js/contracts/schema.ts +83 -0
  379. package/src/js/contracts/theme.ts +1 -0
  380. package/src/js/core/create-formie-client.ts +1519 -0
  381. package/src/js/core/dom-events.ts +8 -0
  382. package/src/js/core/formie.ts +242 -0
  383. package/src/js/core/hydrate-modules.ts +102 -0
  384. package/src/js/core/page-client-event.ts +129 -0
  385. package/src/js/core/page-tab-errors.ts +37 -0
  386. package/src/js/core/submit-flow.ts +120 -0
  387. package/src/js/core/submit-result-state.ts +597 -0
  388. package/src/js/core/submit-result-ui.ts +448 -0
  389. package/src/js/events/event-bus.ts +109 -0
  390. package/src/js/modules/address/address-finder.ts +85 -0
  391. package/src/js/modules/address/api.ts +22 -0
  392. package/src/js/modules/address/constants.ts +15 -0
  393. package/src/js/modules/address/factories.ts +203 -0
  394. package/src/js/modules/address/google-address.ts +345 -0
  395. package/src/js/modules/address/host.ts +137 -0
  396. package/src/js/modules/address/index.ts +10 -0
  397. package/src/js/modules/address/loqate.ts +128 -0
  398. package/src/js/modules/address/place-kit.ts +94 -0
  399. package/src/js/modules/captchas/api.ts +25 -0
  400. package/src/js/modules/captchas/captcha-eu.ts +86 -0
  401. package/src/js/modules/captchas/constants.ts +4 -0
  402. package/src/js/modules/captchas/factories.ts +485 -0
  403. package/src/js/modules/captchas/friendly-captcha-v1.ts +65 -0
  404. package/src/js/modules/captchas/friendly-captcha-v2.ts +84 -0
  405. package/src/js/modules/captchas/hcaptcha.ts +153 -0
  406. package/src/js/modules/captchas/host.ts +448 -0
  407. package/src/js/modules/captchas/index.ts +16 -0
  408. package/src/js/modules/captchas/recaptcha-enterprise.ts +138 -0
  409. package/src/js/modules/captchas/recaptcha-shared.ts +69 -0
  410. package/src/js/modules/captchas/recaptcha-v2-checkbox.ts +72 -0
  411. package/src/js/modules/captchas/recaptcha-v2-invisible.ts +108 -0
  412. package/src/js/modules/captchas/recaptcha-v3.ts +62 -0
  413. package/src/js/modules/captchas/snaptcha.ts +10 -0
  414. package/src/js/modules/captchas/turnstile.ts +131 -0
  415. package/src/js/modules/captchas/utils.ts +85 -0
  416. package/src/js/modules/fields/calculations.ts +273 -0
  417. package/src/js/modules/fields/checkbox-radio.ts +295 -0
  418. package/src/js/modules/fields/conditions/config.ts +79 -0
  419. package/src/js/modules/fields/conditions/effects.ts +166 -0
  420. package/src/js/modules/fields/conditions/evaluator.ts +44 -0
  421. package/src/js/modules/fields/conditions/references.ts +165 -0
  422. package/src/js/modules/fields/conditions/transforms.ts +206 -0
  423. package/src/js/modules/fields/conditions/types.ts +33 -0
  424. package/src/js/modules/fields/conditions/values.ts +115 -0
  425. package/src/js/modules/fields/conditions.ts +229 -0
  426. package/src/js/modules/fields/date-picker.ts +272 -0
  427. package/src/js/modules/fields/file-upload.ts +628 -0
  428. package/src/js/modules/fields/hidden.ts +58 -0
  429. package/src/js/modules/fields/index.ts +19 -0
  430. package/src/js/modules/fields/phone-country.ts +226 -0
  431. package/src/js/modules/fields/repeater.ts +231 -0
  432. package/src/js/modules/fields/rich-text.ts +217 -0
  433. package/src/js/modules/fields/shared.ts +238 -0
  434. package/src/js/modules/fields/signature.ts +202 -0
  435. package/src/js/modules/fields/summary.ts +272 -0
  436. package/src/js/modules/fields/table.ts +197 -0
  437. package/src/js/modules/fields/text-limit.ts +280 -0
  438. package/src/js/modules/loader.ts +331 -0
  439. package/src/js/modules/payments/api.ts +20 -0
  440. package/src/js/modules/payments/bpoint.ts +48 -0
  441. package/src/js/modules/payments/constants.ts +17 -0
  442. package/src/js/modules/payments/eway.ts +132 -0
  443. package/src/js/modules/payments/factories.ts +332 -0
  444. package/src/js/modules/payments/go-cardless.ts +37 -0
  445. package/src/js/modules/payments/host.ts +459 -0
  446. package/src/js/modules/payments/index.ts +17 -0
  447. package/src/js/modules/payments/mollie.ts +38 -0
  448. package/src/js/modules/payments/moneris.ts +216 -0
  449. package/src/js/modules/payments/opayo.ts +272 -0
  450. package/src/js/modules/payments/paddle.ts +111 -0
  451. package/src/js/modules/payments/payment.ts +183 -0
  452. package/src/js/modules/payments/paypal.ts +214 -0
  453. package/src/js/modules/payments/payway.ts +114 -0
  454. package/src/js/modules/payments/square.ts +106 -0
  455. package/src/js/modules/payments/stripe.ts +426 -0
  456. package/src/js/modules/payments/stub-payment-module.ts +87 -0
  457. package/src/js/modules/payments/utils.ts +60 -0
  458. package/src/js/modules/registry.ts +38 -0
  459. package/src/js/modules/styles.ts +29 -0
  460. package/src/js/submit/pipeline.ts +514 -0
  461. package/src/js/theme/theme-classes.ts +106 -0
  462. package/src/js/transport/forms-api.ts +345 -0
  463. package/src/js/utils/async.ts +81 -0
  464. package/src/js/utils/debug.ts +59 -0
  465. package/src/js/utils/event-names.ts +60 -0
  466. package/src/js/utils/field-references.keys.ts +47 -0
  467. package/src/js/utils/field-references.parser.ts +121 -0
  468. package/src/js/utils/field-references.registry.ts +50 -0
  469. package/src/js/utils/field-references.resolver.ts +115 -0
  470. package/src/js/utils/field-references.ts +11 -0
  471. package/src/js/utils/field-references.types.ts +31 -0
  472. package/src/js/utils/fields.ts +58 -0
  473. package/src/js/utils/http.ts +51 -0
  474. package/src/js/utils/i18n.ts +98 -0
  475. package/src/js/utils/scripts.ts +84 -0
  476. package/src/js/utils/unload-warning.ts +190 -0
  477. package/src/js/validation/rules/email.ts +18 -0
  478. package/src/js/validation/rules/match.ts +26 -0
  479. package/src/js/validation/rules/minmax.ts +47 -0
  480. package/src/js/validation/rules/number.ts +55 -0
  481. package/src/js/validation/rules/pattern.ts +29 -0
  482. package/src/js/validation/rules/required.ts +30 -0
  483. package/src/js/validation/rules/shared.ts +47 -0
  484. package/src/js/validation/rules/url.ts +23 -0
  485. package/src/js/validation/rules.ts +16 -0
  486. package/src/js/validation/types.ts +50 -0
  487. package/src/js/validation/validator.ts +643 -0
  488. package/src/vendor.d.ts +100 -0
  489. package/src/vite-env.d.ts +22 -0
  490. package/vite-dev.mjs +22 -0
@@ -0,0 +1,226 @@
1
+ import intlTelInput from 'intl-tel-input';
2
+ import intlTelInputCss from 'intl-tel-input/styles?inline';
3
+
4
+ import type { FormieModuleDefinition } from '#contracts/modules';
5
+ import { dispatchFieldEvent, getModuleFieldContainers, releaseFormValidators, retainFormValidators } from '#modules/fields/shared';
6
+ import { ensureModuleStyles } from '#modules/styles';
7
+ import { createDebug } from '#utils/debug';
8
+
9
+ const PHONE_SELECTOR = 'input[type="tel"][data-formie-phone-input]';
10
+ const COUNTRY_SELECTOR = 'input[data-formie-phone-country-input]';
11
+ const MODULE_ID = 'phone-country';
12
+ const PHONE_COUNTRY_VALIDATOR = 'phoneCountry';
13
+ const VALIDATOR_SCOPE = 'phone-country';
14
+ const debug = createDebug('fields', 'phone');
15
+
16
+ ensureModuleStyles(MODULE_ID, [intlTelInputCss]);
17
+
18
+ type PhoneCountryOptions = {
19
+ countryDefaultValue?: string;
20
+ countryAllowed?: string[];
21
+ language?: string;
22
+ };
23
+
24
+ type PhoneInput = HTMLInputElement & {
25
+ validator?: ReturnType<typeof intlTelInput>;
26
+ restrictedCountries?: boolean;
27
+ allowedCountries?: string[];
28
+ $countryInput?: HTMLInputElement;
29
+ };
30
+
31
+ type IntlCountryCode = Parameters<ReturnType<typeof intlTelInput>['setCountry']>[0];
32
+
33
+ function registerValidators(form: HTMLFormElement | null): void {
34
+ retainFormValidators(form, VALIDATOR_SCOPE, (validator) => {
35
+ validator.addValidator(PHONE_COUNTRY_VALIDATOR, ({ input }) => {
36
+ if (input.type !== 'tel') {
37
+ return true;
38
+ }
39
+
40
+ const phoneInput = input as PhoneInput;
41
+ const phoneValidator = phoneInput.validator;
42
+
43
+ if (!input.value.trim() || !phoneValidator) {
44
+ return true;
45
+ }
46
+
47
+ if (!phoneValidator.isValidNumber()) {
48
+ return false;
49
+ }
50
+
51
+ const selectedCountryCode = phoneValidator.getSelectedCountryData()?.iso2 || '';
52
+
53
+ if (phoneInput.restrictedCountries) {
54
+ const allowedCountries = phoneInput.allowedCountries || [];
55
+
56
+ if (!allowedCountries.includes(selectedCountryCode)) {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ if (phoneInput.$countryInput && selectedCountryCode) {
62
+ phoneInput.$countryInput.value = selectedCountryCode.toUpperCase();
63
+ }
64
+
65
+ return true;
66
+ }, ({ input, t }) => {
67
+ const phoneInput = input as PhoneInput;
68
+ const errorMap = ['Invalid number', 'Invalid country code', 'Too short', 'Too long'];
69
+ const errorCode = phoneInput.validator?.getValidationError() ?? 0;
70
+ const errorMessage = errorMap[errorCode] || errorMap[0];
71
+
72
+ return t(errorMessage);
73
+ });
74
+ });
75
+ }
76
+
77
+ function unregisterValidators(form: HTMLFormElement | null): void {
78
+ releaseFormValidators(form, VALIDATOR_SCOPE, [PHONE_COUNTRY_VALIDATOR]);
79
+ }
80
+
81
+ function buildOptions(options: PhoneCountryOptions): Record<string, unknown> {
82
+ const intlOptions: Record<string, unknown> = {
83
+ allowDropdown: true,
84
+ nationalMode: false,
85
+ separateDialCode: true,
86
+ initialCountry: 'auto',
87
+ autoPlaceholder: 'off',
88
+ formatOnDisplay: false,
89
+ formatAsYouType: false,
90
+ loadUtils: () => import('intl-tel-input/utils'),
91
+ };
92
+
93
+ const allowedCountries = (options.countryAllowed || [])
94
+ .map((item) => {
95
+ return item.toLowerCase();
96
+ })
97
+ .filter(Boolean);
98
+
99
+ if (allowedCountries.length) {
100
+ intlOptions.onlyCountries = allowedCountries;
101
+ intlOptions.initialCountry = allowedCountries[0];
102
+
103
+ // One allowed country behaves more like a formatting helper than a full
104
+ // country-picker UI, so tighten the plugin options accordingly.
105
+ if (allowedCountries.length === 1) {
106
+ intlOptions.allowDropdown = false;
107
+ intlOptions.separateDialCode = false;
108
+ intlOptions.nationalMode = true;
109
+ }
110
+ }
111
+
112
+ if (options.countryDefaultValue) {
113
+ intlOptions.initialCountry = options.countryDefaultValue.toLowerCase();
114
+ }
115
+
116
+ if (Array.isArray(intlOptions.onlyCountries) && typeof intlOptions.initialCountry === 'string') {
117
+ const initialCountry = intlOptions.initialCountry.toLowerCase();
118
+ if (!intlOptions.onlyCountries.includes(initialCountry)) {
119
+ intlOptions.onlyCountries.push(initialCountry);
120
+ }
121
+ }
122
+
123
+ return intlOptions;
124
+ }
125
+
126
+ function initPhoneField(phoneInput: PhoneInput, countryInput: HTMLInputElement, options: PhoneCountryOptions): () => void {
127
+ const intlOptions = buildOptions(options);
128
+
129
+ dispatchFieldEvent(phoneInput, MODULE_ID, 'before-init', {
130
+ phoneCountry: phoneInput,
131
+ options: intlOptions,
132
+ });
133
+
134
+ const validator = intlTelInput(phoneInput, intlOptions);
135
+ const allowedCountries = Array.isArray((intlOptions as { onlyCountries?: unknown[] }).onlyCountries)
136
+ ? (intlOptions as { onlyCountries?: unknown[] }).onlyCountries!.map(String)
137
+ : [];
138
+ phoneInput.validator = validator;
139
+ phoneInput.allowedCountries = allowedCountries;
140
+ phoneInput.$countryInput = countryInput;
141
+ phoneInput.restrictedCountries = allowedCountries.length > 0;
142
+
143
+ if (countryInput.value) {
144
+ validator.setCountry(countryInput.value.toLowerCase() as IntlCountryCode);
145
+ }
146
+
147
+ const syncCountry = () => {
148
+ const countryData = validator.getSelectedCountryData();
149
+ const isoCode = countryData?.iso2 || '';
150
+ if (isoCode) {
151
+ // Keep the hidden country field authoritative for submit payloads while
152
+ // the tel input focuses on the visible formatted number experience.
153
+ countryInput.value = isoCode.toUpperCase();
154
+ }
155
+ };
156
+
157
+ phoneInput.addEventListener('countrychange', syncCountry);
158
+ phoneInput.addEventListener('blur', syncCountry);
159
+ syncCountry();
160
+ debug.log('Initialized.', {
161
+ inputName: phoneInput.name,
162
+ restrictedCountries: phoneInput.restrictedCountries,
163
+ });
164
+
165
+ dispatchFieldEvent(phoneInput, MODULE_ID, 'init', {
166
+ phoneCountry: phoneInput,
167
+ validator,
168
+ validatorOptions: intlOptions,
169
+ });
170
+
171
+ return () => {
172
+ phoneInput.removeEventListener('countrychange', syncCountry);
173
+ phoneInput.removeEventListener('blur', syncCountry);
174
+ validator.destroy();
175
+ delete phoneInput.allowedCountries;
176
+ delete phoneInput.validator;
177
+ delete phoneInput.$countryInput;
178
+ delete phoneInput.restrictedCountries;
179
+ debug.log('Destroyed.', {
180
+ inputName: phoneInput.name,
181
+ });
182
+ };
183
+ }
184
+
185
+ export const phoneCountryModule: FormieModuleDefinition = {
186
+ id: MODULE_ID,
187
+ kind: 'field',
188
+ match: (ctx) => {
189
+ return !!ctx.target.querySelector(PHONE_SELECTOR);
190
+ },
191
+ setup: async (ctx) => {
192
+ const options = (ctx.options || {}) as PhoneCountryOptions;
193
+ const fields = getModuleFieldContainers(ctx);
194
+
195
+ registerValidators(ctx.form);
196
+
197
+ const cleanups = fields.map((field) => {
198
+ const phoneInput = field.querySelector(PHONE_SELECTOR);
199
+ const countryInput = field.querySelector(COUNTRY_SELECTOR);
200
+
201
+ if (!(phoneInput instanceof HTMLInputElement) || !(countryInput instanceof HTMLInputElement)) {
202
+ debug.warn('Missing phone/country input; skipping field.');
203
+ return () => { };
204
+ }
205
+
206
+ return initPhoneField(phoneInput as PhoneInput, countryInput, options);
207
+ });
208
+ debug.log('Module setup.', { fieldCount: fields.length });
209
+
210
+ await ctx.emit('formie:module:phone-country:init', {
211
+ count: cleanups.length,
212
+ });
213
+
214
+ return {
215
+ destroy: () => {
216
+ cleanups.forEach((cleanup) => {
217
+ cleanup();
218
+ });
219
+
220
+ unregisterValidators(ctx.form);
221
+ debug.log('Module destroy.', { fieldCount: fields.length });
222
+ void ctx.emit('formie:module:phone-country:destroy', {});
223
+ },
224
+ };
225
+ },
226
+ };
@@ -0,0 +1,231 @@
1
+ import repeaterCss from '#theme/fields/_repeater.css?inline';
2
+
3
+ import type { FormieModuleDefinition } from '#contracts/modules';
4
+ import { dispatchFieldEvent, getTemplateSource, getTemplateSourceHtml } from '#modules/fields/shared';
5
+ import { ensureModuleStyles } from '#modules/styles';
6
+ import { sleep } from '#utils/async';
7
+ import { createDebug } from '#utils/debug';
8
+
9
+ const FIELD_SELECTOR = '[data-formie-repeater-field-layout]';
10
+ const CONTAINER_SELECTOR = '[data-formie-repeater-container]';
11
+ const ROW_SELECTOR = '[data-formie-repeater-item]';
12
+ const ADD_SELECTOR = '[data-formie-repeater-add]';
13
+ const REMOVE_SELECTOR = '[data-formie-repeater-remove]';
14
+ const TEMPLATE_ID_ATTR = 'data-formie-template-id';
15
+ const MODULE_ID = 'repeater';
16
+ const debug = createDebug('fields', 'repeater');
17
+
18
+ ensureModuleStyles(MODULE_ID, [repeaterCss]);
19
+
20
+ function getTemplate(field: HTMLElement, templateId?: string | null): HTMLElement | HTMLTemplateElement | HTMLScriptElement | null {
21
+ return getTemplateSource(field, templateId);
22
+ }
23
+
24
+ function buildRowFromTemplate(templateHtml: string, rowId: number): HTMLElement | null {
25
+ const wrapper = document.createElement('div');
26
+ wrapper.innerHTML = templateHtml.replaceAll('__ROW__', String(rowId)).trim();
27
+
28
+ return wrapper.firstElementChild instanceof HTMLElement ? wrapper.firstElementChild : null;
29
+ }
30
+
31
+ function getRowCount(field: HTMLElement): number {
32
+ return field.querySelectorAll(ROW_SELECTOR).length;
33
+ }
34
+
35
+ function syncAddButton(addButton: HTMLButtonElement | null, rowCount: number): void {
36
+ if (!addButton) {
37
+ return;
38
+ }
39
+
40
+ const maxRows = parseInt(addButton.getAttribute('data-formie-max-rows') || '', 10);
41
+ if (maxRows > 0 && rowCount >= maxRows) {
42
+ addButton.disabled = true;
43
+ return;
44
+ }
45
+
46
+ addButton.disabled = false;
47
+ }
48
+
49
+ function bindRepeaterField(field: HTMLElement): () => void {
50
+ const container = field.matches(CONTAINER_SELECTOR)
51
+ ? field
52
+ : field.querySelector(CONTAINER_SELECTOR);
53
+ const addButton = field.querySelector(ADD_SELECTOR);
54
+
55
+ if (!(container instanceof HTMLElement)) {
56
+ debug.warn('Missing repeater container; skipping field.');
57
+ return () => {};
58
+ }
59
+
60
+ const removeHandlers = new Map<HTMLElement, EventListener>();
61
+ // Start after the highest existing row id so SSR rows and client-added rows
62
+ // keep producing unique `__ROW__` replacements across one repeater instance.
63
+ let rowCounter = Array.from(field.querySelectorAll(ROW_SELECTOR)).reduce((max, row) => {
64
+ const current = parseInt((row as HTMLElement).getAttribute('data-formie-repeater-item-id') || '', 10);
65
+ return Number.isNaN(current) ? max : Math.max(max, current + 1);
66
+ }, 0);
67
+
68
+ const bindRemoveButtons = () => {
69
+ field.querySelectorAll(REMOVE_SELECTOR).forEach((button) => {
70
+ if (!(button instanceof HTMLElement) || removeHandlers.has(button)) {
71
+ return;
72
+ }
73
+
74
+ const handler: EventListener = (event) => {
75
+ event.preventDefault();
76
+ const row = button.closest(ROW_SELECTOR);
77
+
78
+ if (!(row instanceof HTMLElement)) {
79
+ return;
80
+ }
81
+
82
+ const minRows = parseInt((addButton instanceof HTMLButtonElement ? addButton.getAttribute('data-formie-min-rows') : '') || '', 10);
83
+ if (minRows > 0 && getRowCount(field) <= minRows) {
84
+ return;
85
+ }
86
+
87
+ row.remove();
88
+ syncAddButton(addButton instanceof HTMLButtonElement ? addButton : null, getRowCount(field));
89
+ debug.log('Row removed.', {
90
+ rowCount: getRowCount(field),
91
+ });
92
+ dispatchFieldEvent(field, MODULE_ID, 'remove', {
93
+ repeater: field,
94
+ row,
95
+ });
96
+ };
97
+
98
+ button.addEventListener('click', handler);
99
+ removeHandlers.set(button, handler);
100
+ });
101
+ };
102
+
103
+ const addRow = async() => {
104
+ if (!(addButton instanceof HTMLButtonElement)) {
105
+ return;
106
+ }
107
+
108
+ const handle = addButton.getAttribute('data-formie-repeater-add');
109
+ if (!handle) {
110
+ debug.warn('Add handle missing.');
111
+ return;
112
+ }
113
+
114
+ const templateId = addButton.getAttribute(TEMPLATE_ID_ATTR) || field.getAttribute(TEMPLATE_ID_ATTR);
115
+
116
+ const maxRows = parseInt(addButton.getAttribute('data-formie-max-rows') || '', 10);
117
+ if (maxRows > 0 && getRowCount(field) >= maxRows) {
118
+ return;
119
+ }
120
+
121
+ const template = getTemplate(field, templateId);
122
+ if (!template) {
123
+ debug.warn('Template not found for add action.', { handle });
124
+ return;
125
+ }
126
+
127
+ const row = buildRowFromTemplate(getTemplateSourceHtml(template), rowCounter++);
128
+ if (!row) {
129
+ debug.warn('Failed to build row from template.');
130
+ return;
131
+ }
132
+
133
+ container.appendChild(row);
134
+ // Give downstream field modules a tick to see the new row before we emit
135
+ // the public append/init-row lifecycle events used for nested enhancements.
136
+ await sleep(50);
137
+ bindRemoveButtons();
138
+ syncAddButton(addButton, getRowCount(field));
139
+ debug.log('Row appended.', {
140
+ rowCount: getRowCount(field),
141
+ });
142
+ dispatchFieldEvent(field, MODULE_ID, 'append', {
143
+ repeater: field,
144
+ row,
145
+ });
146
+ dispatchFieldEvent(field, MODULE_ID, 'init-row', {
147
+ repeater: field,
148
+ row,
149
+ });
150
+ };
151
+
152
+ const addHandler = (event: Event) => {
153
+ event.preventDefault();
154
+ void addRow();
155
+ };
156
+
157
+ if (addButton instanceof HTMLButtonElement) {
158
+ addButton.addEventListener('click', addHandler);
159
+ }
160
+
161
+ bindRemoveButtons();
162
+ syncAddButton(addButton instanceof HTMLButtonElement ? addButton : null, getRowCount(field));
163
+
164
+ if (addButton instanceof HTMLButtonElement && getRowCount(field) === 0) {
165
+ const minRows = parseInt(addButton.getAttribute('data-formie-min-rows') || '', 10);
166
+ // Seed required minimum rows through the same add flow so templates,
167
+ // events, and row ids behave exactly like user-triggered additions.
168
+ for (let index = 0; index < minRows; index += 1) {
169
+ void addRow();
170
+ }
171
+ }
172
+
173
+ dispatchFieldEvent(field, MODULE_ID, 'init', {
174
+ repeater: field,
175
+ });
176
+ debug.log('Field initialized.', {
177
+ rowCount: getRowCount(field),
178
+ });
179
+
180
+ return () => {
181
+ if (addButton instanceof HTMLButtonElement) {
182
+ addButton.removeEventListener('click', addHandler);
183
+ }
184
+
185
+ removeHandlers.forEach((handler, button) => {
186
+ button.removeEventListener('click', handler);
187
+ });
188
+ };
189
+ }
190
+
191
+ export const repeaterModule: FormieModuleDefinition = {
192
+ id: MODULE_ID,
193
+ kind: 'field',
194
+ match: (ctx) => {
195
+ return ctx.target instanceof HTMLElement && (
196
+ ctx.target.matches(FIELD_SELECTOR) ||
197
+ !!ctx.target.querySelector(FIELD_SELECTOR)
198
+ );
199
+ },
200
+ setup: async(ctx) => {
201
+ if (!(ctx.target instanceof HTMLElement)) {
202
+ return;
203
+ }
204
+
205
+ const fields = ctx.target.matches(FIELD_SELECTOR)
206
+ ? [ctx.target]
207
+ : Array.from(ctx.target.querySelectorAll(FIELD_SELECTOR)).filter((field): field is HTMLElement => {
208
+ return field instanceof HTMLElement;
209
+ });
210
+
211
+ const destroyBindings = fields.map((field) => {
212
+ return bindRepeaterField(field);
213
+ });
214
+ debug.log('Module setup.', { fieldCount: fields.length });
215
+
216
+ await ctx.emit('formie:module:repeater:init', {
217
+ count: fields.length,
218
+ });
219
+
220
+ return {
221
+ destroy: () => {
222
+ destroyBindings.forEach((destroyBinding) => {
223
+ destroyBinding();
224
+ });
225
+
226
+ debug.log('Module destroy.', { fieldCount: fields.length });
227
+ void ctx.emit('formie:module:repeater:destroy', {});
228
+ },
229
+ };
230
+ },
231
+ };
@@ -0,0 +1,217 @@
1
+ import { exec, init as initPell } from 'pell';
2
+ import pellCss from 'pell/dist/pell.min.css?inline';
3
+ import tokensCss from '#theme/_tokens.css?inline';
4
+ import richTextCss from '#theme/fields/_rich-text.css?inline';
5
+ import alignCenterIcon from '#icons/rich-text/aligncenter.svg?raw';
6
+ import alignLeftIcon from '#icons/rich-text/alignleft.svg?raw';
7
+ import alignRightIcon from '#icons/rich-text/alignright.svg?raw';
8
+ import boldIcon from '#icons/rich-text/bold.svg?raw';
9
+ import clearIcon from '#icons/rich-text/clear.svg?raw';
10
+ import codeIcon from '#icons/rich-text/code.svg?raw';
11
+ import heading1Icon from '#icons/rich-text/heading1.svg?raw';
12
+ import heading2Icon from '#icons/rich-text/heading2.svg?raw';
13
+ import imageIcon from '#icons/rich-text/image.svg?raw';
14
+ import italicIcon from '#icons/rich-text/italic.svg?raw';
15
+ import lineIcon from '#icons/rich-text/line.svg?raw';
16
+ import linkIcon from '#icons/rich-text/link.svg?raw';
17
+ import olistIcon from '#icons/rich-text/olist.svg?raw';
18
+ import paragraphIcon from '#icons/rich-text/paragraph.svg?raw';
19
+ import quoteIcon from '#icons/rich-text/quote.svg?raw';
20
+ import strikethroughIcon from '#icons/rich-text/strikethrough.svg?raw';
21
+ import ulistIcon from '#icons/rich-text/ulist.svg?raw';
22
+ import underlineIcon from '#icons/rich-text/underline.svg?raw';
23
+
24
+ import type { FormieModuleDefinition } from '#contracts/modules';
25
+ import { dispatchFieldEvent, getModuleFieldContainers } from '#modules/fields/shared';
26
+ import { ensureModuleStyles } from '#modules/styles';
27
+
28
+ const CONTAINER_SELECTOR = '[data-formie-rich-text]';
29
+ const FIELD_SELECTOR = '[data-formie-field], [data-formie-field-handle]';
30
+ const INPUT_SELECTOR = 'textarea[data-formie-multi-line-text-input]';
31
+ const MODULE_ID = 'rich-text';
32
+
33
+ ensureModuleStyles(MODULE_ID, [tokensCss, pellCss, richTextCss]);
34
+
35
+ type RichTextOptions = {
36
+ buttons?: string[];
37
+ };
38
+
39
+ type RichTextAction = {
40
+ name: string;
41
+ icon: string;
42
+ title?: string;
43
+ result?: () => void;
44
+ };
45
+
46
+ type PellEditor = {
47
+ content: HTMLElement;
48
+ };
49
+
50
+ type RichTextInput = HTMLTextAreaElement & {
51
+ richText?: PellEditor;
52
+ };
53
+
54
+ const RICH_TEXT_ICONS = {
55
+ bold: boldIcon,
56
+ italic: italicIcon,
57
+ underline: underlineIcon,
58
+ strikethrough: strikethroughIcon,
59
+ heading1: heading1Icon,
60
+ heading2: heading2Icon,
61
+ paragraph: paragraphIcon,
62
+ quote: quoteIcon,
63
+ olist: olistIcon,
64
+ ulist: ulistIcon,
65
+ code: codeIcon,
66
+ line: lineIcon,
67
+ link: linkIcon,
68
+ image: imageIcon,
69
+ alignleft: alignLeftIcon,
70
+ aligncenter: alignCenterIcon,
71
+ alignright: alignRightIcon,
72
+ clear: clearIcon,
73
+ } as const;
74
+
75
+ function hasRichTextField(target: Element): boolean {
76
+ if (target.matches(FIELD_SELECTOR)) {
77
+ return !!target.querySelector(CONTAINER_SELECTOR) && !!target.querySelector(INPUT_SELECTOR);
78
+ }
79
+
80
+ return Array.from(target.querySelectorAll(FIELD_SELECTOR)).some((field) => {
81
+ return !!field.querySelector(CONTAINER_SELECTOR) && !!field.querySelector(INPUT_SELECTOR);
82
+ });
83
+ }
84
+
85
+ function getActionDefinitions(): RichTextAction[] {
86
+ return [
87
+ { name: 'bold', icon: RICH_TEXT_ICONS.bold },
88
+ { name: 'italic', icon: RICH_TEXT_ICONS.italic },
89
+ { name: 'underline', icon: RICH_TEXT_ICONS.underline },
90
+ { name: 'strikethrough', icon: RICH_TEXT_ICONS.strikethrough },
91
+ { name: 'heading1', icon: RICH_TEXT_ICONS.heading1 },
92
+ { name: 'heading2', icon: RICH_TEXT_ICONS.heading2 },
93
+ { name: 'paragraph', icon: RICH_TEXT_ICONS.paragraph },
94
+ { name: 'quote', icon: RICH_TEXT_ICONS.quote },
95
+ { name: 'olist', icon: RICH_TEXT_ICONS.olist },
96
+ { name: 'ulist', icon: RICH_TEXT_ICONS.ulist },
97
+ { name: 'code', icon: RICH_TEXT_ICONS.code },
98
+ { name: 'line', icon: RICH_TEXT_ICONS.line },
99
+ { name: 'link', icon: RICH_TEXT_ICONS.link },
100
+ { name: 'image', icon: RICH_TEXT_ICONS.image },
101
+ { name: 'alignleft', icon: RICH_TEXT_ICONS.alignleft, title: 'Align Left', result: () => exec('justifyLeft', '') },
102
+ { name: 'aligncenter', icon: RICH_TEXT_ICONS.aligncenter, title: 'Align Center', result: () => exec('justifyCenter', '') },
103
+ { name: 'alignright', icon: RICH_TEXT_ICONS.alignright, title: 'Align Right', result: () => exec('justifyRight', '') },
104
+ {
105
+ name: 'clear',
106
+ icon: RICH_TEXT_ICONS.clear,
107
+ title: 'Clear',
108
+ result: () => {
109
+ const selection = window.getSelection()?.toString() || '';
110
+ if (selection) {
111
+ const linesToDelete = selection.split('\n').join('<br>');
112
+ exec('formatBlock', '<p>');
113
+ document.execCommand('insertHTML', false, linesToDelete);
114
+ return;
115
+ }
116
+
117
+ exec('formatBlock', '<p>');
118
+ },
119
+ },
120
+ ];
121
+ }
122
+
123
+ function getActions(buttons?: string[]): Array<string | RichTextAction> {
124
+ const selectedButtons = buttons?.length ? buttons : ['bold', 'italic'];
125
+ const definitions = getActionDefinitions();
126
+
127
+ return selectedButtons.map((button) => {
128
+ return definitions.find((definition) => {
129
+ return definition.name === button;
130
+ });
131
+ }).filter((definition): definition is RichTextAction => {
132
+ return !!definition;
133
+ });
134
+ }
135
+
136
+ function initRichTextField(container: HTMLElement, input: RichTextInput, options: RichTextOptions): () => void {
137
+ const pellOptions = {
138
+ element: container,
139
+ defaultParagraphSeparator: 'p',
140
+ styleWithCSS: true,
141
+ actions: getActions(options.buttons),
142
+ onChange: (html: string) => {
143
+ const emptyParagraph = '<p><br></p>';
144
+ input.value = input.placeholder && html === emptyParagraph ? '' : html;
145
+ // Bubble both generic input and a namespaced rich-text change hook.
146
+ input.dispatchEvent(new Event('input', { bubbles: true }));
147
+ dispatchFieldEvent(input, MODULE_ID, 'populate', {
148
+ richText: input,
149
+ value: input.value,
150
+ });
151
+ },
152
+ classes: {
153
+ actionbar: 'formie-rich-text-toolbar',
154
+ button: 'formie-rich-text-button',
155
+ content: 'formie-input formie-rich-text-content',
156
+ selected: 'formie-rich-text-selected',
157
+ },
158
+ };
159
+
160
+ dispatchFieldEvent(input, MODULE_ID, 'before-init', {
161
+ richText: input,
162
+ options: pellOptions,
163
+ });
164
+
165
+ const editor = initPell(pellOptions) as PellEditor;
166
+ input.richText = editor;
167
+ editor.content.innerHTML = input.value || '';
168
+
169
+ if (input.placeholder) {
170
+ editor.content.setAttribute('data-placeholder', input.placeholder);
171
+ }
172
+
173
+ dispatchFieldEvent(input, MODULE_ID, 'after-init', {
174
+ richText: editor,
175
+ });
176
+
177
+ return () => {
178
+ container.innerHTML = '';
179
+ delete input.richText;
180
+ };
181
+ }
182
+
183
+ export const richTextModule: FormieModuleDefinition = {
184
+ id: MODULE_ID,
185
+ kind: 'field',
186
+ match: (ctx) => {
187
+ return ctx.target instanceof HTMLElement && hasRichTextField(ctx.target);
188
+ },
189
+ setup: async(ctx) => {
190
+ const options = (ctx.options || {}) as RichTextOptions;
191
+ const fields = getModuleFieldContainers(ctx);
192
+ const cleanups = fields.map((field) => {
193
+ const container = field.querySelector(CONTAINER_SELECTOR);
194
+ const input = field.querySelector(INPUT_SELECTOR);
195
+
196
+ if (!(container instanceof HTMLElement) || !(input instanceof HTMLTextAreaElement)) {
197
+ return () => {};
198
+ }
199
+
200
+ return initRichTextField(container, input as RichTextInput, options);
201
+ });
202
+
203
+ await ctx.emit('formie:module:rich-text:init', {
204
+ count: cleanups.length,
205
+ });
206
+
207
+ return {
208
+ destroy: () => {
209
+ cleanups.forEach((cleanup) => {
210
+ cleanup();
211
+ });
212
+
213
+ void ctx.emit('formie:module:rich-text:destroy', {});
214
+ },
215
+ };
216
+ },
217
+ };