@livenetworks/ashlar 1.6.1 → 2.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 (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +202 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -1,264 +1,260 @@
1
- # ln-form
2
-
3
- A minimal **form manipulation** primitive. `ln-form` does exactly two
4
- things to a native `<form>`: populate it when an `ln-fill` event delivers
5
- a record, and rewrite `action` / `_method` for RESTful edit routing.
6
- Submit is entirely native HTML — `ln-form` does not listen for `submit`
7
- at all. Validation display and the submit gate are owned entirely by
8
- [`ln-validate`](../ln-validate/README.md).
9
-
10
- ---
11
-
12
- ## 1. Philosophy
13
-
14
- 1. **Populate, don't orchestrate.** `ln-form` writes values into the form
15
- on `ln-fill` and dispatches synthetic `input`/`change` events so
16
- reactive consumers (`ln-validate`, `ln-autoresize`) react in sync. It
17
- keeps no validation state and no submit-button logic.
18
- 2. **Native submit, always.** Forms MUST carry a real `action` and
19
- `method` in HTML. Without JavaScript they do a normal native submit.
20
- PUT/DELETE ride on POST via an auto-ensured hidden `_method` input
21
- (Laravel method spoofing) — `ln-form` never serializes data, never
22
- dispatches a submit event, and never listens for `submit` at all.
23
- 3. **Transport is someone else's job.** Ajax interception (if wanted) is
24
- a separate component's concern — it listens to the native `submit`
25
- event itself. Validation is owned by the browser's constraint
26
- validation plus `ln-validate` for field error display and the submit
27
- gate — a form with at least one `data-ln-validate` field gets
28
- `novalidate` injected automatically and the first such field attaches
29
- the form's submit gate; a form with none keeps native validation as
30
- the default.
31
-
32
- ---
33
-
34
- ## 2. Minimal Blueprint
35
-
36
- ```html
37
- <form id="user-form" data-ln-form action="/users" method="post">
38
- <div class="form-element">
39
- <label for="username">Username</label>
40
- <input id="username" name="username" required data-ln-validate>
41
- <ul data-ln-validate-errors>
42
- <li hidden data-ln-validate-error="required">Username is required</li>
43
- </ul>
44
- </div>
45
-
46
- <ul class="form-actions">
47
- <li><button type="button" data-ln-modal-close>Cancel</button></li>
48
- <li><button type="submit">Save</button></li>
49
- </ul>
50
- </form>
51
- ```
52
-
53
- > [!WARNING]
54
- > Always set `type="button"` on Cancel buttons. Otherwise the browser
55
- > defaults to `type="submit"` and triggers a real submit.
56
-
57
- ---
58
-
59
- ## 3. Declarative API Contract
60
-
61
- ### HTML Attributes
62
-
63
- | Attribute | Elements | Description |
64
- | :--- | :--- | :--- |
65
- | `data-ln-form` | `<form>` | Initializes the coordinator. |
66
- | `data-ln-form-action-edit` | `<form>` | Opt-in RESTful action routing. See below. |
67
- | `data-ln-form-action-method="PUT"` | `<form>` | Override verb for `_method` (default `PUT`). Requires `data-ln-form-action-edit`. |
68
- | `data-ln-form-scope` | `<form>` | Opt-in local-first write path. Empty = nearest ancestor `[data-ln-data-coordinator]`. Named = explicit coordinator override. Intercepts submit; incompatible with native/ajax submit on the same form. |
69
-
70
- ### JS API
71
-
72
- Access the instance directly via the `lnForm` property on the form
73
- element:
74
-
75
- ```javascript
76
- const form = document.getElementById('user-form');
77
-
78
- // Bulk populate fields by name (fires synthetic input/change events)
79
- form.lnForm.fill({ username: 'dalibor', role: 'admin' });
80
-
81
- // Clean up listeners and destroy the instance
82
- form.lnForm.destroy();
83
- ```
84
-
85
- ---
86
-
87
- ## 4. DOM Events
88
-
89
- ### Emitted
90
-
91
- | Event | Bubbles | Payload | Description |
92
- | :--- | :--- | :--- | :--- |
93
- | `ln-form:destroyed` | Yes | `{ target: HTMLElement }` | Dispatched when the coordinator is torn down. |
94
-
95
- ### Received
96
-
97
- | Event | Payload | Description |
98
- | :--- | :--- | :--- |
99
- | `ln-fill` | `record \| null` | Canonical fan-out fill. `detail` → `this.fill(detail)` + apply action mode; `null` → native `form.reset()`. Guarded: only handled when `e.target === self.dom`. |
100
-
101
- ---
102
-
103
- ## 5. RESTful action routing (create vs. edit)
104
-
105
- For Laravel REST backends, `ln-form` can rewrite `form.action` and inject
106
- a hidden `_method` input driven off the fill record — no coordinator JS,
107
- no second click listener.
108
-
109
- **Opt-in:** add `data-ln-form-action-edit` to the `<form>`. Forms without
110
- this attribute are completely inert — this feature does not exist for
111
- them.
112
-
113
- ### Attributes
114
-
115
- | Attribute | On | Description |
116
- | :--- | :--- | :--- |
117
- | `data-ln-form-action-edit` | `<form>` | Opt-in. Empty value = RESTful default (`baseAction + '/' + id`). Non-empty value = template with `:id` placeholder. |
118
- | `data-ln-form-action-edit="/path/:id"` | `<form>` | Template variant — `:id` is replaced with `encodeURIComponent(record.id)`. |
119
- | `data-ln-form-action-method="PUT"` | `<form>` | HTTP verb written into `_method` on edit. Default: `PUT`. |
120
-
121
- > The `<input name="_method">` is auto-ensured — do not author it
122
- > manually. `ln-form` creates it on first fill if absent, or reuses the
123
- > existing one.
124
-
125
- ### Behavior
126
-
127
- | Fill state | `form.action` | `<input name="_method">` |
128
- | :--- | :--- | :--- |
129
- | Edit (record with `id`) | Rewritten to edit URL | `PUT` (or custom verb) |
130
- | New (`null` detail) | Restored to base action | `''` (empty — Laravel ignores) |
131
-
132
- ### Examples
133
-
134
- ```html
135
- <!-- Minimal: baseAction + '/' + id -->
136
- <form data-ln-form action="/packages" data-ln-form-action-edit>
137
-
138
- <!-- Custom template -->
139
- <form data-ln-form action="/packages" data-ln-form-action-edit="/packages/:id">
140
-
141
- <!-- Custom verb -->
142
- <form data-ln-form action="/packages"
143
- data-ln-form-action-edit="/packages/:id"
144
- data-ln-form-action-method="PATCH">
145
- ```
146
-
147
- > [!NOTE]
148
- > This feature only rewrites `action`/`_method`. The actual HTTP request
149
- > (native submit or ajax) is out of scope for `ln-form`.
150
-
151
- ---
152
-
153
- ## 6. What ln-form does NOT do
154
-
155
- - **No transport.** It does not make XHR/fetch requests. Submit is
156
- native HTML; ajax interception (if desired) belongs to a separate
157
- component that listens to the native `submit` event.
158
- - **No serialization, ever.** There is no `serializeForm` call inside
159
- `ln-form`, no `ln-form:submit-record` event, no JSON payload — scoped
160
- forms are serialized by the claiming `ln-data-coordinator`, not by
161
- `ln-form`.
162
- - **No validation orchestration, no submit listener at all.** `ln-form`
163
- never dispatches `ln-validate:request-validate`, never calls
164
- `preventDefault()`, and never touches `novalidate` — all of that is
165
- entirely [`ln-validate`](../ln-validate/README.md)'s job (see its
166
- README §Philosophy). Claiming a valid submit for the write pipeline
167
- (scoped forms only) is `ln-data-coordinator`'s job (native `submit`,
168
- bubble phase, its own `preventDefault()`) — `ln-form` plays no role in
169
- that decision either.
170
-
171
- ---
172
-
173
- ## 5b. Local-first write routing (`data-ln-form-scope`)
174
-
175
- A form carrying `data-ln-form-scope` becomes the universal write entry
176
- point for local-first/SPA pages. The `submit` event is intercepted at
177
- three possible rungs, and whichever rung claims it decides the archetype:
178
-
179
- 1. **Nobody** — native browser submit to `action` (+ `_method`), SSR.
180
- 2. **`ln-ajax`** — fetch to `action`, page stays put (progressive
181
- enhancement). `ln-ajax` explicitly skips forms carrying
182
- `data-ln-form-scope` (see the `ln-ajax` README).
183
- 3. **`ln-data-coordinator`** — a form with `data-ln-form-scope` has its
184
- valid submit left native; `ln-form` never intercepts it beyond the
185
- validation gate (§7 below). The nearest ancestor
186
- `[data-ln-data-coordinator]` (or the named coordinator, if
187
- `data-ln-form-scope="name"`) listens for the native `submit` on
188
- `document` (bubble phase), claims it with `preventDefault()` once its
189
- own scope/containment matches, serializes the form itself, and routes
190
- it through the store → queue → connector write pipeline. See the
191
- `ln-data-coordinator` README for the intake contract.
192
-
193
- `ln-validate`'s submit gate runs on every method — GET included, exactly
194
- like the native browser validation it replaces. A search form nested
195
- inside a coordinator with invalid `data-ln-validate` fields is blocked
196
- the same as any other form; a form with no `data-ln-validate` fields has
197
- no gate at all. `ln-data-coordinator` reads the effective method
198
- (`_method` input if present and non-empty, else the form's `method`
199
- attribute) independently, purely to decide whether to claim the native
200
- submit for the write pipeline (`POST` → create, `PUT`/`PATCH` → update) —
201
- a `GET` form is never claimed, regardless of validity. `ln-form` itself
202
- stays coordinator-blind and validation-blind: it never serializes, never
203
- dispatches a custom event, and never listens for `submit`.
204
-
205
- ---
206
-
207
- ## 7. Common Pitfalls
208
-
209
- - **Setting `input.value` directly:** Doing `input.value = 'new'` is
210
- silent in the DOM. Neither validation nor layout systems will detect
211
- the change. **Always** use `form.lnForm.fill()` or manually dispatch an
212
- `input`/`change` event.
213
- - **Expecting a submit event:** `ln-form` never dispatches anything on
214
- submit — there is no `ln-form:submit` and no `ln-form:submit-record`
215
- either. Listen to the form's native `submit` event, or the resulting
216
- `ln-data-store:created`/`ln-data-store:updated` outcome, instead.
217
-
218
- ---
219
-
220
- ## 8. Integration & Source Files
221
-
222
- - **Unified Bundle**: Loaded automatically with the main bundle:
223
- ```html
224
- <script src="dist/ln-ashlar.iife.js" defer></script>
225
- ```
226
- - **Standalone IIFE**: For lightweight pages, load the standalone,
227
- self-registering IIFE version:
228
- ```html
229
- <script src="js/ln-form/ln-form.js" defer></script>
230
- ```
231
- - **Active Source (ESM)**: Development source is located at
232
- [js/ln-form/src/ln-form.js](file:///c:/laragon/www/ln-ashlar/js/ln-form/src/ln-form.js).
233
-
234
- ---
235
-
236
- ## 🔧 Internals
237
-
238
- Source: `js/ln-form/ln-form.js`. Instance state is minimal: `dom` (the form) and `_baseAction` (the `action` attribute captured once at init, restored on "new" mode). No validation index, no debounce, no submit-button bookkeeping — `ln-form` does not orchestrate submission.
239
-
240
- ### `fill(data)`
241
-
242
- Iterates `form.elements` writing by `name`: checkboxes get `.checked = Boolean(value)`, radios match `el.value === String(value)`, `select-multiple` sets `options[n].selected`, everything else sets `.value`. Each written element then dispatches exactly one synthetic event — `change` for `SELECT`/checkbox/radio, `input` otherwise (the `isChangeBased` discriminator). This mirrors the same discriminator `ln-validate` and `ln-autoresize` use for their own listener registration, so every field fires the event those consumers actually listen for — no double fires, no missed reactions.
243
-
244
- ### `ln-fill` handling
245
-
246
- Guarded to `e.target === self.dom` (ignores bubbled `ln-fill` from nested `[data-ln-fillable]` children). A record calls `fill()` then `_applyActionMode(record)`; `null` calls `form.reset()` — the native `reset` event that fires from that call is what actually restores the base action (see below), not the `ln-fill` handler directly.
247
-
248
- ### Reset routing
249
-
250
- The native `reset` event — whether from `form.reset()` above, a `<button type="reset">`, or any other trigger — always runs `_applyActionMode(null)`, restoring `_baseAction` and clearing `_method`. There is no synthetic input/change replay on reset and no `ln-form:reset-complete` event.
251
-
252
- ### Action-mode guard
253
-
254
- `_applyActionMode` exits immediately on forms without `data-ln-form-action-edit` — unrelated forms are untouched by the reset/fill listeners' side effects.
255
-
256
- ### Relation to ln-validate
257
-
258
- Independent siblings, no imports between them. `ln-validate` injects `novalidate` the moment its first field initializes (authors never write it by hand); a form with zero `data-ln-validate` fields keeps native validation as the default. `ln-validate` has its own separate `reset` listener (clears field error classes / `_touched` state) — unrelated to `ln-form`'s reset listener, which only handles action/`_method`.
259
-
260
- ---
261
-
262
- ## Related
263
- - **[`ln-validate`](../ln-validate/README.md)** — Field-level validation and error display.
264
- - **[`ln-fill`](../ln-fill/README.md)** — Fan-out fill event source.
1
+ # ln-form
2
+
3
+ > Applied to a native `<form data-ln-form action="..." method="...">`. It listens for `ln-fill` events carrying a record payload:
4
+ > populating matching controls (`[name="key"]`) and dispatching synthetic `input`/`change` events to update dependent components.
5
+ > On edit workflows, it updates `form.action` and ensures an `<input type="hidden" name="_method" value="PUT">` exists for RESTful
6
+ > method spoofing, without intercepting native form submission.
7
+
8
+ ---
9
+
10
+ ## 1. Philosophy
11
+
12
+ 1. **Populate, don't orchestrate.** `ln-form` writes values into the form
13
+ on `ln-fill` and dispatches synthetic `input`/`change` events so
14
+ reactive consumers (`ln-validate`, `ln-autoresize`) react in sync. It
15
+ keeps no validation state and no submit-button logic.
16
+ 2. **Native submit, always.** Forms MUST carry a real `action` and
17
+ `method` in HTML. Without JavaScript they do a normal native submit.
18
+ PUT/DELETE ride on POST via an auto-ensured hidden `_method` input
19
+ (Laravel method spoofing) — `ln-form` never serializes data, never
20
+ dispatches a submit event, and never listens for `submit` at all.
21
+ 3. **Transport is someone else's job.** Ajax interception (if wanted) is
22
+ a separate component's concern — it listens to the native `submit`
23
+ event itself. Validation is owned by the browser's constraint
24
+ validation plus `ln-validate` for field error display and the submit
25
+ gate — a form with at least one `data-ln-validate` field gets
26
+ `novalidate` injected automatically and the first such field attaches
27
+ the form's submit gate; a form with none keeps native validation as
28
+ the default.
29
+
30
+ ---
31
+
32
+ ## 2. Minimal Blueprint
33
+
34
+ ```html
35
+ <form id="user-form" data-ln-form action="/users" method="post">
36
+ <div class="form-element">
37
+ <label for="username">Username</label>
38
+ <input id="username" name="username" required data-ln-validate>
39
+ <ul data-ln-validate-errors>
40
+ <li hidden data-ln-validate-error="required">Username is required</li>
41
+ </ul>
42
+ </div>
43
+
44
+ <ul class="form-actions">
45
+ <li><button type="button" data-ln-modal-close>Cancel</button></li>
46
+ <li><button type="submit">Save</button></li>
47
+ </ul>
48
+ </form>
49
+ ```
50
+
51
+ > [!WARNING]
52
+ > Always set `type="button"` on Cancel buttons. Otherwise the browser
53
+ > defaults to `type="submit"` and triggers a real submit.
54
+
55
+ ---
56
+
57
+ ## 3. Declarative API Contract
58
+
59
+ ### HTML Attributes
60
+
61
+ | Attribute | Elements | Description |
62
+ | :--- | :--- | :--- |
63
+ | `data-ln-form` | `<form>` | Initializes the coordinator. |
64
+ | `data-ln-form-action-edit` | `<form>` | Opt-in RESTful action routing. See below. |
65
+ | `data-ln-form-action-method="PUT"` | `<form>` | Override verb for `_method` (default `PUT`). Requires `data-ln-form-action-edit`. |
66
+ | `data-ln-data-coordinator-scope` | `<form>` | Opt-in local-first write path, carried by the form but read by `ln-data-coordinator`. Empty = nearest ancestor `[data-ln-data-coordinator]`. Named = explicit coordinator override. Intercepts submit; incompatible with native/ajax submit on the same form. |
67
+
68
+ ### JS API
69
+
70
+ Access the instance directly via the `lnForm` property on the form
71
+ element:
72
+
73
+ ```javascript
74
+ const form = document.getElementById('user-form');
75
+
76
+ // Bulk populate fields by name (fires synthetic input/change events)
77
+ form.lnForm.fill({ username: 'dalibor', role: 'admin' });
78
+
79
+ // Clean up listeners and destroy the instance
80
+ form.lnForm.destroy();
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 4. DOM Events
86
+
87
+ ### Emitted
88
+
89
+ `ln-form` emits no custom events.
90
+
91
+ ### Received
92
+
93
+ | Event | Payload | Description |
94
+ | :--- | :--- | :--- |
95
+ | `ln-fill` | `record \| null` | Canonical fan-out fill. `detail` → `this.fill(detail)` + apply action mode; `null` → native `form.reset()`. Guarded: only handled when `e.target === self.dom`. |
96
+
97
+ ---
98
+
99
+ ## 5. RESTful action routing (create vs. edit)
100
+
101
+ For Laravel REST backends, `ln-form` can rewrite `form.action` and inject
102
+ a hidden `_method` input driven off the fill record — no coordinator JS,
103
+ no second click listener.
104
+
105
+ **Opt-in:** add `data-ln-form-action-edit` to the `<form>`. Forms without
106
+ this attribute are completely inert — this feature does not exist for
107
+ them.
108
+
109
+ ### Attributes
110
+
111
+ | Attribute | On | Description |
112
+ | :--- | :--- | :--- |
113
+ | `data-ln-form-action-edit` | `<form>` | Opt-in. Empty value = RESTful default (`baseAction + '/' + id`). Non-empty value = template with `:id` placeholder. |
114
+ | `data-ln-form-action-edit="/path/:id"` | `<form>` | Template variant — `:id` is replaced with `encodeURIComponent(record.id)`. |
115
+ | `data-ln-form-action-method="PUT"` | `<form>` | HTTP verb written into `_method` on edit. Default: `PUT`. |
116
+
117
+ > The `<input name="_method">` is auto-ensured — do not author it
118
+ > manually. `ln-form` creates it on first fill if absent, or reuses the
119
+ > existing one.
120
+
121
+ ### Behavior
122
+
123
+ | Fill state | `form.action` | `<input name="_method">` |
124
+ | :--- | :--- | :--- |
125
+ | Edit (record with `id`) | Rewritten to edit URL | `PUT` (or custom verb) |
126
+ | New (`null` detail) | Restored to base action | `''` (empty — Laravel ignores) |
127
+
128
+ ### Examples
129
+
130
+ ```html
131
+ <!-- Minimal: baseAction + '/' + id -->
132
+ <form data-ln-form action="/packages" data-ln-form-action-edit>
133
+
134
+ <!-- Custom template -->
135
+ <form data-ln-form action="/packages" data-ln-form-action-edit="/packages/:id">
136
+
137
+ <!-- Custom verb -->
138
+ <form data-ln-form action="/packages"
139
+ data-ln-form-action-edit="/packages/:id"
140
+ data-ln-form-action-method="PATCH">
141
+ ```
142
+
143
+ > [!NOTE]
144
+ > This feature only rewrites `action`/`_method`. The actual HTTP request
145
+ > (native submit or ajax) is out of scope for `ln-form`.
146
+
147
+ ---
148
+
149
+ ## 6. What ln-form does NOT do
150
+
151
+ - **No transport.** It does not make XHR/fetch requests. Submit is
152
+ native HTML; ajax interception (if desired) belongs to a separate
153
+ component that listens to the native `submit` event.
154
+ - **No serialization, ever.** There is no `serializeForm` call inside
155
+ `ln-form`, no `ln-form:submit-record` event, no JSON payload — scoped
156
+ forms are serialized by the claiming `ln-data-coordinator`, not by
157
+ `ln-form`.
158
+ - **No validation orchestration, no submit listener at all.** `ln-form`
159
+ never dispatches `ln-validate:request-validate`, never calls
160
+ `preventDefault()`, and never touches `novalidate` — all of that is
161
+ entirely [`ln-validate`](../ln-validate/README.md)'s job (see its
162
+ README §Philosophy). Claiming a valid submit for the write pipeline
163
+ (scoped forms only) is `ln-data-coordinator`'s job (native `submit`,
164
+ bubble phase, its own `preventDefault()`) — `ln-form` plays no role in
165
+ that decision either.
166
+
167
+ ---
168
+
169
+ ## 5b. Local-first write routing (`data-ln-data-coordinator-scope`)
170
+
171
+ A form carrying `data-ln-data-coordinator-scope` becomes the universal write entry
172
+ point for local-first/SPA pages. The `submit` event is intercepted at
173
+ three possible rungs, and whichever rung claims it decides the archetype:
174
+
175
+ 1. **Nobody** — native browser submit to `action` (+ `_method`), SSR.
176
+ 2. **`ln-ajax`** — fetch to `action`, page stays put (progressive
177
+ enhancement). `ln-ajax` explicitly skips forms carrying
178
+ `data-ln-data-coordinator-scope` (see the `ln-ajax` README).
179
+ 3. **`ln-data-coordinator`** — a form with `data-ln-data-coordinator-scope` has its
180
+ valid submit left native; `ln-form` never intercepts it beyond the
181
+ validation gate (§7 below). The nearest ancestor
182
+ `[data-ln-data-coordinator]` (or the named coordinator, if
183
+ `data-ln-data-coordinator-scope="name"`) listens for the native `submit` on
184
+ `document` (bubble phase), claims it with `preventDefault()` once its
185
+ own scope/containment matches, serializes the form itself, and routes
186
+ it through the store → queue → connector write pipeline. See the
187
+ `ln-data-coordinator` README for the intake contract.
188
+
189
+ `ln-validate`'s submit gate runs on every method — GET included, exactly
190
+ like the native browser validation it replaces. A search form nested
191
+ inside a coordinator with invalid `data-ln-validate` fields is blocked
192
+ the same as any other form; a form with no `data-ln-validate` fields has
193
+ no gate at all. `ln-data-coordinator` reads the effective method
194
+ (`_method` input if present and non-empty, else the form's `method`
195
+ attribute) independently, purely to decide whether to claim the native
196
+ submit for the write pipeline (`POST` → create, `PUT`/`PATCH` → update) —
197
+ a `GET` form is never claimed, regardless of validity. `ln-form` itself
198
+ stays coordinator-blind and validation-blind: it never serializes, never
199
+ dispatches a custom event, and never listens for `submit`.
200
+
201
+ ---
202
+
203
+ ## 7. Common Pitfalls
204
+
205
+ - **Setting `input.value` directly:** Doing `input.value = 'new'` is
206
+ silent in the DOM. Neither validation nor layout systems will detect
207
+ the change. **Always** use `form.lnForm.fill()` or manually dispatch an
208
+ `input`/`change` event.
209
+ - **Expecting a submit event:** `ln-form` never dispatches anything on
210
+ submit — there is no `ln-form:submit` and no `ln-form:submit-record`
211
+ either. Listen to the form's native `submit` event, or the resulting
212
+ `ln-data-store:created`/`ln-data-store:updated` outcome, instead.
213
+
214
+ ---
215
+
216
+ ## 8. Integration & Source Files
217
+
218
+ - **Unified Bundle**: Loaded automatically with the main bundle:
219
+ ```html
220
+ <script src="dist/ln-ashlar.iife.js" defer></script>
221
+ ```
222
+ - **Standalone IIFE**: For lightweight pages, load the standalone,
223
+ self-registering IIFE version:
224
+ ```html
225
+ <script src="components/ln-form/ln-form.js" defer></script>
226
+ ```
227
+ - **Active Source (ESM)**: Development source is located at
228
+ [components/ln-form/src/ln-form.js](file:///c:/laragon/www/ln-ashlar/components/ln-form/src/ln-form.js).
229
+
230
+ ---
231
+
232
+ ## 🔧 Internals
233
+
234
+ Source: `components/ln-form/ln-form.js`. Instance state is minimal: `dom` (the form) and `_baseAction` (the `action` attribute captured once at init, restored on "new" mode). No validation index, no debounce, no submit-button bookkeeping — `ln-form` does not orchestrate submission.
235
+
236
+ ### `fill(data)`
237
+
238
+ Iterates `form.elements` writing by `name`: checkboxes get `.checked = Boolean(value)`, radios match `el.value === String(value)`, `select-multiple` sets `options[n].selected`, everything else sets `.value`. Each written element then dispatches exactly one synthetic event — `change` for `SELECT`/checkbox/radio, `input` otherwise (the `isChangeBased` discriminator). This mirrors the same discriminator `ln-validate` and `ln-autoresize` use for their own listener registration, so every field fires the event those consumers actually listen for — no double fires, no missed reactions.
239
+
240
+ ### `ln-fill` handling
241
+
242
+ Guarded to `e.target === self.dom` (ignores bubbled `ln-fill` from nested `[data-ln-fillable]` children). A record calls `fill()` then `_applyActionMode(record)`; `null` calls `form.reset()` — the native `reset` event that fires from that call is what actually restores the base action (see below), not the `ln-fill` handler directly.
243
+
244
+ ### Reset routing
245
+
246
+ The native `reset` event — whether from `form.reset()` above, a `<button type="reset">`, or any other trigger — always runs `_applyActionMode(null)`, restoring `_baseAction` and clearing `_method`. There is no synthetic input/change replay on reset and no `ln-form:reset-complete` event.
247
+
248
+ ### Action-mode guard
249
+
250
+ `_applyActionMode` exits immediately on forms without `data-ln-form-action-edit` — unrelated forms are untouched by the reset/fill listeners' side effects.
251
+
252
+ ### Relation to ln-validate
253
+
254
+ Independent siblings, no imports between them. `ln-validate` injects `novalidate` the moment its first field initializes (authors never write it by hand); a form with zero `data-ln-validate` fields keeps native validation as the default. `ln-validate` has its own separate `reset` listener (clears field error classes / `_touched` state) — unrelated to `ln-form`'s reset listener, which only handles action/`_method`.
255
+
256
+ ---
257
+
258
+ ## Related
259
+ - **[`ln-validate`](../ln-validate/README.md)** — Field-level validation and error display.
260
+ - **[`ln-fill`](../ln-fill/README.md)** — Fan-out fill event source.
@@ -0,0 +1,8 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Host Element Check: data-ln-form belongs only on <form> elements
5
+ [data-ln-form]:not(form) {
6
+ @include dev-inline-error("data-ln-form belongs only on <form> elements");
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function E(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function g(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!g(o))return}e.apply(console,t)}}function B(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function b(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){b(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function j(e){if(typeof e!="string")return!!e;const t=e.trim().toLowerCase();return t!=="false"&&t!=="0"&&t!==""&&t!=="off"&&t!=="no"}function W(e,t){const n=e.elements,o=[],l={};for(let i=0;i<n.length;i++){const r=n[i];r.name&&r.type==="checkbox"&&(l[r.name]=(l[r.name]||0)+1)}for(let i=0;i<n.length;i++){const r=n[i];if(r.type==="file"||r.type==="submit"||r.type==="button")continue;const c=r.getAttribute("data-ln-fill-as")||r.name;if(!c||!(c in t))continue;const s=t[c];if(r.type==="checkbox"){if(Array.isArray(s))r.checked=s.indexOf(r.value)!==-1;else if(l[r.name]>1){const u=String(s).split(",").map(function(a){return a.trim()});r.checked=u.indexOf(r.value)!==-1}else r.checked=j(s);o.push(r)}else if(r.type==="radio")r.checked=r.value===String(s),o.push(r);else if(r.type==="select-multiple"){if(Array.isArray(s))for(let u=0;u<r.options.length;u++)r.options[u].selected=s.indexOf(r.options[u].value)!==-1;o.push(r)}else r.value=s,o.push(r)}return o}const L={};function U(e,t){L[e]=t}function H(e){return L[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=H);function V(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,b(function(){new MutationObserver(function(){B(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const T={};function P(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];T[n]=t}function K(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return T[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=P,window.lnCore.getLocaleFallback=K,window.lnCore.ensureLocaleObserver=V);const m={};function Q(e,t){m[e]||(m[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=m[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function I(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let r=0;r<n.length;r++){const c=n[r],s=c.getAttribute("data-ln-field");t[s]!=null&&(c.textContent=t[s])}const o=e.querySelectorAll("[data-ln-attr]");for(let r=0;r<o.length;r++){const c=o[r],s=c.getAttribute("data-ln-attr").split(",");for(let u=0;u<s.length;u++){const a=s[u].trim().split(":");if(a.length!==2)continue;const d=a[0].trim(),f=a[1].trim();t[f]!=null&&c.setAttribute(d,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let r=0;r<l.length;r++){const c=l[r],s=c.getAttribute("data-ln-show");s in t&&c.classList.toggle("hidden",!t[s])}const i=e.querySelectorAll("[data-ln-class]");for(let r=0;r<i.length;r++){const c=i[r],s=c.getAttribute("data-ln-class").split(",");for(let u=0;u<s.length;u++){const a=s[u].trim().split(":");if(a.length!==2)continue;const d=a[0].trim(),f=a[1].trim();f in t&&c.classList.toggle(d,!!t[f])}}return e}function J(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let o=0;o<n.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[o],t??null),n[o].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)I(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function $(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const i=n.currentNode;i.textContent.indexOf("{{")!==-1&&(i.textContent=i.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(r,c){return t[c]!==void 0?t[c]:""}))}const o=function(i,r){return t[r]!==void 0?t[r]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let i=0;i<l.length;i++){const r=l[i],c=r.attributes;for(let s=0;s<c.length;s++){const u=c[s];u.value.indexOf("{{")!==-1&&r.setAttribute(u.name,u.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function G(e,t,n,o,l,i){const r={};for(let s=0;s<e.children.length;s++){const u=e.children[s],a=u.getAttribute("data-ln-render-key");a&&(r[a]=u)}const c=document.createDocumentFragment();for(let s=0;s<t.length;s++){const u=t[s],a=String(o(u));let d=r[a];if(d)l(d,u,s);else{const f=Q(n,i);if(!f||($(f,u),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",a),l(d,u,s)}c.appendChild(d)}e.textContent="",e.appendChild(c)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=$,window.lnCore.fill=I,window.lnCore.lnFill=J,window.lnCore.renderList=G);function C(e,t,n){const o=e.getAttribute(t);return o===null?n:o}function X(e,t,n){const o=parseInt(e.getAttribute(t),10);return isNaN(o)?n:o}function z(e,t,n=!1){const o=e.getAttribute(t);if(o===null)return!!n;const l=o.trim().toLowerCase();return!(l==="false"||l==="0")}function Y(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const y=new Map;function Z(e,t){const n=(e?e.join("|"):"")+"::"+t;if(y.has(n))return y.get(n);const o=new Set(e||[]),l=function(i,r){const c=i.getAttribute(r);return c!==null&&o.has(c)?c:t};return y.set(n,l),l}function ee(e,t,n){const o=parseFloat(e.getAttribute(t));return isNaN(o)?n:o}function te(e,t,n){const o=e.getAttribute(t);if(!o)return n;try{return JSON.parse(o)}catch{return n}}function R(e,t,n,o,l){if(!e||t===null)return!0;const i=o||"ln-component",r=e.type;if(r==="trigger"||r==="marker"||r==="string"||r==="list"||!r||t===""&&e.fallback!==void 0)return!0;const c=s=>{l?console.warn(s,l):console.warn(s)};if(r==="boolean"){const s=t.trim().toLowerCase();return s!==""&&s!=="true"&&s!=="false"&&s!=="1"&&s!=="0"?(c(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(r==="enum"){const s=e.values||[];return s.includes(t)?!0:(c(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${s.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(r==="integer"){if(!/^-?\d+$/.test(t))return c(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const s=parseInt(t,10);return e.min!==void 0&&s<e.min?(c(`[${i}] Value ${s} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&s>e.max?(c(`[${i}] Value ${s} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(r==="float")return isNaN(Number(t))?(c(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(r==="json")try{return JSON.parse(t),!0}catch(s){return c(`[${i}] Invalid JSON for attribute "${n}": ${s.message}. Fallback: ${e.fallback}.`),!1}return!0}function ne(e,t,n){for(const o in n){const[l,i,r]=n[o];Object.defineProperty(e,o,{get:function(){return l(t,i,r)},enumerable:!0,configurable:!0})}return e}function oe(e){const t={};for(const n in e){const o=e[n];if(!o||!o.prop)continue;let l=o.read;if(!l&&o.type)switch(o.type){case"enum":l=Z(o.values,o.fallback);break;case"integer":l=X;break;case"float":l=ee;break;case"boolean":l=z;break;case"list":l=Y;break;case"json":l=te;break;case"string":default:l=C;break}l||(l=C),t[o.prop]=[l,n,o.fallback]}return t}function re(e){const t={};for(const n in e){const o=e[n];o&&o.effect&&(t[n]=o.effect)}return Object.keys(t).length?t:null}function ie(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function A(e,t,n,o){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",r=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&r.push(e);for(const c of r)if(!c[n]){window.lnCore._persistSink&&c.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(c,t);try{c[n]=new o(c)}catch(s){console.error("["+n+"] init failed",c,s)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function le(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function se(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function D(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function ce(e){const t=D(),n=e.observed||[];for(let o=0;o<n.length;o++){const l=n[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(i=>i.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=ie(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function M(e,t,n,o){for(let l=0;l<e.length;l++){const i=e[l];if(!t[i.attribute])continue;const r=i.effects&&i.effects[n];r?r(t,n,o):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,o)}}function ue(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=D(),l=o.byAttr.get(n);if(E()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&g(t)){const i=o.byDeclaredAttr.get(n),r=t.getAttribute(n);for(let c=0;c<i.length;c++)R(i[c].spec,r,n,i[c].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||l)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=o.byReactive.get(n);i&&M(i,t,n,e.oldValue),o.reactiveWildcard.length&&M(o.reactiveWildcard,t,n,e.oldValue)}if(l)for(let i=0;i<l.length;i++){const r=l[i];if(r.handler){r.handler(t,n,e.oldValue);continue}r.onAttributeChange&&t[r.attribute]?r.onAttributeChange(t,n):(A(t,r.selector,r.attribute,r.ComponentFn),r.onInit&&r.onInit(t))}}function ae(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,b(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{ue(t[n])}catch(o){console.error("[ln-core] mutation handler failed",t[n].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function F(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function de(e){const t=F();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const o=t[n];if(o.onSubtreeChange){const l=o.query,i=e.target.nodeType===1?e.target.matches(l)?e.target:e.target.closest(l):e.target.parentElement?e.target.parentElement.closest(l):null;i&&o.onSubtreeChange(i,e)}}for(let n=0;n<e.addedNodes.length;n++){const o=e.addedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const i=t[l];A(o,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(o)}}for(let n=0;n<e.removedNodes.length;n++){const o=e.removedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const i=t[l],r=i.query,c=Array.from(o.querySelectorAll(r));o.matches&&o.matches(r)&&c.push(o);for(let s=0;s<c.length;s++){const u=c[s];if(!document.contains(u)){const a=u[i.attribute];if(a&&typeof a.destroy=="function")try{a.destroy()}catch(d){console.error("["+i.attribute+"] destroy failed",u,d)}delete u[i.attribute]}}}}}}function fe(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,b(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const o=t[n];if(o.type==="childList")try{de(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function we(e,t,n,o,l={}){const i=l.extraAttributes||[],r=l.onAttributeChange||null,c=l.onSubtreeChange||null,s=l.onInit||null,u=l.onAttrChange||null,a=l.effects||null,d=l.attributes||null,f=d?re(d):a,_e=d?new Set(Object.keys(d)):null,ve=l.persist||null;function p(k){const w=k||document.body;if(A(w,e,t,n),d&&E())for(const h in d){const Se=d[h],S=Array.from(w.querySelectorAll("["+h+"]"));w.matches&&w.matches("["+h+"]")&&S.push(w);for(let O=0;O<S.length;O++){const x=S[O];g(x)&&R(Se,x.getAttribute(h),h,o,x)}}s&&s(w)}const v=[];if(e.indexOf("[")!==-1){const k=/\[([\w-]+)/g;let w;for(;(w=k.exec(e))!==null;)v.push(w[1])}else v.push(e);ce({selector:e,attribute:t,componentTag:o,attributes:d,ComponentFn:n,onInit:s,observed:v.concat(i),onAttributeChange:r,onAttrChange:u,effects:f,declared:_e,persist:ve}),ae();const ke=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";F().push({selector:e,attribute:t,ComponentFn:n,onInit:s,onSubtreeChange:c,query:ke}),fe(),window[t]=p;function N(){le()>0?se(function(){p(document.body)}):p(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",N):N(),p}function q(e){return(e||"").replace(/^#/,"")}function _(e){const t=e===void 0?location.hash:e,n={},o=q(t);if(!o)return n;const l=o.split("&");for(let i=0;i<l.length;i++){const r=l[i];if(!r)continue;const c=r.indexOf(":"),s=c>-1?r.slice(0,c):r,u=c>-1?r.slice(c+1):"";if(s)try{n[s]=decodeURIComponent(u)}catch{n[s]=u}}return n}function he(e){if(!e)return null;const t=_();return e in t?t[e]:null}function be(e,t){if(!e)return;const n=_();t==null?delete n[e]:n[e]=String(t);const l=Object.keys(n).map(function(i){const r=n[i];return r===""?i:i+":"+encodeURIComponent(r)}).join("&");q(location.hash)!==l&&(location.hash=l)}function pe(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function ge(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function me(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function Ce(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function ye(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Ae(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=_,window.lnCore.hashGet=he,window.lnCore.hashSet=be,window.lnCore.hashLinkClick=pe,window.lnCore.resolveHashNamespace=ge,window.lnCore.hashSortEncode=me,window.lnCore.hashSortDecode=Ce,window.lnCore.hashFilterEncode=ye,window.lnCore.hashFilterDecode=Ae),(function(){const e="data-ln-form",t="lnForm";if(window[t]!==void 0)return;const n={"data-ln-form":{type:"marker",description:"Identifies the form element as an enhanced ln-form"},"data-ln-form-action-edit":{prop:"_actionEdit",type:"string",read:C,fallback:"",description:"URL template or endpoint used when editing an existing record"},"data-ln-form-action-method":{prop:"_actionMethod",type:"enum",values:["PUT","POST","PATCH"],fallback:"PUT",description:"HTTP method used when submitting edit action"}},o=oe(n);function l(i){this.dom=i,ne(this,i,o),this._baseAction=i.getAttribute("action")||"";const r=this;return this._onLnFill=function(c){c.target===r.dom&&(c.detail?(r.fill(c.detail),r._applyActionMode(c.detail)):r.dom.reset())},this._onReset=function(){r._applyActionMode(null)},i.addEventListener("ln-fill",this._onLnFill),i.addEventListener("reset",this._onReset),this}l.prototype.fill=function(i){const r=W(this.dom,i);for(let c=0;c<r.length;c++){const s=r[c],u=s.tagName==="SELECT"||s.type==="checkbox"||s.type==="radio";s.dispatchEvent(new Event(u?"change":"input",{bubbles:!0}))}},l.prototype._ensureMethodInput=function(){let i=this.dom.querySelector('input[name="_method"]');return i||(i=document.createElement("input"),i.type="hidden",i.name="_method",i.value="",this.dom.appendChild(i)),i},l.prototype._applyActionMode=function(i){if(!this.dom.hasAttribute("data-ln-form-action-edit"))return;const r=i&&i.id!=null&&i.id!==""?i.id:null,c=this._ensureMethodInput();if(r!==null){const s=this._actionEdit;s?this.dom.setAttribute("action",s.replace(":id",encodeURIComponent(r))):this.dom.setAttribute("action",this._baseAction.replace(/\/$/,"")+"/"+encodeURIComponent(r)),c.value=this._actionMethod||"PUT"}else this.dom.setAttribute("action",this._baseAction),c.value=""},l.prototype.destroy=function(){this.dom[t]&&(this.dom.removeEventListener("ln-fill",this._onLnFill),this.dom.removeEventListener("reset",this._onReset),delete this.dom[t])},we(e,t,l,"ln-form",{attributes:n})})()})();