@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
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Pure decision layer for the multi-region navigation pipeline. Takes DOM-read
3
+ * descriptors (one per registered region) and decides which regions clear,
4
+ * which swap, and which owns title/focus/scroll — without touching the DOM.
5
+ *
6
+ * descriptors: ordered array of
7
+ * { regionKey, match, targetEl, isPending, hasKeep, hasHydrate, hasChildren, mountedTemplate }
8
+ *
9
+ * @param {Array<Object>} descriptors
10
+ * @param {Object} [options]
11
+ * @param {boolean} [options.isHydration]
12
+ * @param {boolean} [options.hasPrimaryRegion]
13
+ * @param {Object|null} [options.primaryMatch]
14
+ * @returns {{ notFound: boolean, clears: Array<Object>, swaps: Array<Object>, owner: Object|null }}
15
+ */
16
+ export function planRegions(descriptors, { isHydration = false, hasPrimaryRegion = false, primaryMatch = null } = {}) {
17
+ const notFound = hasPrimaryRegion
18
+ ? !primaryMatch
19
+ : !descriptors.some(d => d.match);
20
+
21
+ const clears = [];
22
+ const swaps = [];
23
+
24
+ for (const d of descriptors) {
25
+ if (!d.targetEl && !d.isPending) continue;
26
+
27
+ if (!d.match) {
28
+ const isHydrationKeep = isHydration && d.hasHydrate && d.hasChildren;
29
+ if (!d.hasKeep && d.hasChildren && !isHydrationKeep && d.targetEl) {
30
+ clears.push(d);
31
+ }
32
+ continue;
33
+ }
34
+
35
+ if (d.hasKeep && d.mountedTemplate === d.match.route.templateNode) {
36
+ continue; // keep-region, same template already mounted — neither swap nor clear
37
+ }
38
+
39
+ swaps.push(Object.assign({}, d, {
40
+ skipMount: isHydration && d.hasHydrate && d.hasChildren
41
+ }));
42
+ }
43
+
44
+ swaps.sort((a, b) => (a.regionKey === '__primary__' ? -1 : b.regionKey === '__primary__' ? 1 : 0));
45
+
46
+ const primarySwap = swaps.find(d => d.regionKey === '__primary__');
47
+ const owner = primarySwap || swaps[0] || null;
48
+
49
+ return { notFound, clears, swaps, owner };
50
+ }
@@ -0,0 +1,45 @@
1
+ # ln-scroll
2
+
3
+ > Binds to `<a>` elements carrying `data-ln-scroll` and smoothly scrolls to the target specified by their `href` hash anchor (e.g. `href="#apply"`).
4
+ > Supports declarative form field population via `data-ln-scroll-set`, post-scroll focus management, and cancelable lifecycle events.
5
+
6
+ ## Markup anatomy
7
+
8
+ ```html
9
+ <!-- Basic Smooth Scroll Link -->
10
+ <a href="#apply" data-ln-scroll>Apply Now</a>
11
+
12
+ <!-- Smooth Scroll with Form Field Preset -->
13
+ <a href="#apply"
14
+ data-ln-scroll
15
+ data-ln-scroll-set="#opportunity_type:Company Driver">
16
+ Apply as Company Driver
17
+ </a>
18
+
19
+ <!-- Customized Scroll Alignment & Target Focus -->
20
+ <a href="#quote"
21
+ data-ln-scroll
22
+ data-ln-scroll-behavior="smooth"
23
+ data-ln-scroll-block="center"
24
+ data-ln-scroll-focus="#first_name">
25
+ Get a Quote
26
+ </a>
27
+ ```
28
+
29
+ ## Attribute contract
30
+
31
+ | Attribute | Role | Default | Description |
32
+ |---|---|---|---|
33
+ | `data-ln-scroll` | Component selector | — | Activates smooth scrolling on `<a>` elements. Target is read from `href`. |
34
+ | `href` | Target selector | — | **Required.** Standard anchor reference (e.g. `#apply`). |
35
+ | `data-ln-scroll-set` | Field preset | — | Optional `selector:value` to populate an input/select element prior to scrolling. |
36
+ | `data-ln-scroll-behavior` | Scroll behavior | `smooth` | Scroll behavior: `'smooth'` or `'auto'`. |
37
+ | `data-ln-scroll-block` | Scroll alignment | `start` | Vertical alignment: `'start'`, `'center'`, `'end'`, or `'nearest'`. |
38
+ | `data-ln-scroll-focus` | Focus target | first input/select/textarea | Focuses matching field in target container. Set to `'false'` to disable. |
39
+ | `data-ln-scroll-delay` | Focus delay | `450` | Delay in milliseconds before focusing the target field. |
40
+ | `data-ln-scroll-update-hash` | Hash update | `false` | When `'true'`, updates browser address bar with `href` via `pushState`. |
41
+
42
+ ## Events API
43
+
44
+ - `ln-scroll:before-scroll`: Dispatched before scrolling. Cancelable via `e.preventDefault()`. Detail: `{ target, link, href }`.
45
+ - `ln-scroll:scrolled`: Dispatched immediately upon initiating scroll. Detail: `{ target, link, href }`.
@@ -0,0 +1 @@
1
+ (function(){"use strict";function x(){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 m(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 r=1;r<t.length;r++)if(t[r]&&t[r].nodeType===1){o=t[r];break}if(!m(o))return}e.apply(console,t)}}function D(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 N(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o);const r=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:o});return e.dispatchEvent(r),r}function y(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){y(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const L={};function j(e,t){L[e]=t}function V(e){return L[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=j,window.lnCore.getDataMapper=V);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,y(function(){new MutationObserver(function(){D(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const T={};function K(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];T[n]=t}function U(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=K,window.lnCore.getLocaleFallback=U,window.lnCore.ensureLocaleObserver=H);const A={};function Q(e,t){A[e]||(A[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=A[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 l=0;l<n.length;l++){const c=n[l],s=c.getAttribute("data-ln-field");t[s]!=null&&(c.textContent=t[s])}const o=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<o.length;l++){const c=o[l],s=c.getAttribute("data-ln-attr").split(",");for(let a=0;a<s.length;a++){const u=s[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();t[f]!=null&&c.setAttribute(d,t[f])}}const r=e.querySelectorAll("[data-ln-show]");for(let l=0;l<r.length;l++){const c=r[l],s=c.getAttribute("data-ln-show");s in t&&c.classList.toggle("hidden",!t[s])}const i=e.querySelectorAll("[data-ln-class]");for(let l=0;l<i.length;l++){const c=i[l],s=c.getAttribute("data-ln-class").split(",");for(let a=0;a<s.length;a++){const u=s[a].trim().split(":");if(u.length!==2)continue;const d=u[0].trim(),f=u[1].trim();f in t&&c.classList.toggle(d,!!t[f])}}return e}function G(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(l,c){return t[c]!==void 0?t[c]:""}))}const o=function(i,l){return t[l]!==void 0?t[l]:""},r=Array.from(e.querySelectorAll("*"));e.nodeType===1&&r.push(e);for(let i=0;i<r.length;i++){const l=r[i],c=l.attributes;for(let s=0;s<c.length;s++){const a=c[s];a.value.indexOf("{{")!==-1&&l.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function J(e,t,n,o,r,i){const l={};for(let s=0;s<e.children.length;s++){const a=e.children[s],u=a.getAttribute("data-ln-render-key");u&&(l[u]=a)}const c=document.createDocumentFragment();for(let s=0;s<t.length;s++){const a=t[s],u=String(o(a));let d=l[u];if(d)r(d,a,s);else{const f=Q(n,i);if(!f||($(f,a),d=f.firstElementChild,!d))continue;d.setAttribute("data-ln-render-key",u),r(d,a,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=G,window.lnCore.renderList=J);function P(e,t,n=!1){const o=e.getAttribute(t);if(o===null)return!!n;const r=o.trim().toLowerCase();return!(r==="false"||r==="0")}function q(e,t,n,o,r){if(!e||t===null)return!0;const i=o||"ln-component",l=e.type;if(l==="trigger"||l==="marker"||l==="string"||l==="list"||!l||t===""&&e.fallback!==void 0)return!0;const c=s=>{r?console.warn(s,r):console.warn(s)};if(l==="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(l==="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(l==="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(l==="float")return isNaN(Number(t))?(c(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(l==="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 X(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 z(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 v(e,t,n,o){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",l=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&l.push(e);for(const c of l)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 Y(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function Z(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 R(){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 ee(e){const t=R(),n=e.observed||[];for(let o=0;o<n.length;o++){const r=n[o];t.byAttr.has(r)||t.byAttr.set(r,[]),t.byAttr.get(r).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 r=t.byDeclaredAttr.get(o);r.some(i=>i.componentTag===e.componentTag)||r.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=z(e);if(o===null)t.reactiveWildcard.push(e);else for(const r of o)t.byReactive.has(r)||t.byReactive.set(r,[]),t.byReactive.get(r).push(e)}e.persist&&t.persist.push(e)}function B(e,t,n,o){for(let r=0;r<e.length;r++){const i=e[r];if(!t[i.attribute])continue;const l=i.effects&&i.effects[n];l?l(t,n,o):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,o)}}function te(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=R(),r=o.byAttr.get(n);if(x()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&m(t)){const i=o.byDeclaredAttr.get(n),l=t.getAttribute(n);for(let c=0;c<i.length;c++)q(i[c].spec,l,n,i[c].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||r)&&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&&B(i,t,n,e.oldValue),o.reactiveWildcard.length&&B(o.reactiveWildcard,t,n,e.oldValue)}if(r)for(let i=0;i<r.length;i++){const l=r[i];if(l.handler){l.handler(t,n,e.oldValue);continue}l.onAttributeChange&&t[l.attribute]?l.onAttributeChange(t,n):(v(t,l.selector,l.attribute,l.ComponentFn),l.onInit&&l.onInit(t))}}function ne(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,y(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{te(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 W(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function oe(e){const t=W();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const o=t[n];if(o.onSubtreeChange){const r=o.query,i=e.target.nodeType===1?e.target.matches(r)?e.target:e.target.closest(r):e.target.parentElement?e.target.parentElement.closest(r):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 r=0;r<t.length;r++){const i=t[r];v(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 r=0;r<t.length;r++){const i=t[r],l=i.query,c=Array.from(o.querySelectorAll(l));o.matches&&o.matches(l)&&c.push(o);for(let s=0;s<c.length;s++){const a=c[s];if(!document.contains(a)){const u=a[i.attribute];if(u&&typeof u.destroy=="function")try{u.destroy()}catch(d){console.error("["+i.attribute+"] destroy failed",a,d)}delete a[i.attribute]}}}}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,y(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const o=t[n];if(o.type==="childList")try{oe(o)}catch(r){console.error("[ln-core] lifecycle handler failed",o.target,r)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function ie(e,t,n,o,r={}){const i=r.extraAttributes||[],l=r.onAttributeChange||null,c=r.onSubtreeChange||null,s=r.onInit||null,a=r.onAttrChange||null,u=r.effects||null,d=r.attributes||null,f=d?X(d):u,h=d?new Set(Object.keys(d)):null,p=r.persist||null;function w(k){const g=k||document.body;if(v(g,e,t,n),d&&x())for(const C in d){const be=d[C],S=Array.from(g.querySelectorAll("["+C+"]"));g.matches&&g.matches("["+C+"]")&&S.push(g);for(let O=0;O<S.length;O++){const E=S[O];m(E)&&q(be,E.getAttribute(C),C,o,E)}}s&&s(g)}const b=[];if(e.indexOf("[")!==-1){const k=/\[([\w-]+)/g;let g;for(;(g=k.exec(e))!==null;)b.push(g[1])}else b.push(e);ee({selector:e,attribute:t,componentTag:o,attributes:d,ComponentFn:n,onInit:s,observed:b.concat(i),onAttributeChange:l,onAttrChange:a,effects:f,declared:h,persist:p}),ne();const he=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";W().push({selector:e,attribute:t,ComponentFn:n,onInit:s,onSubtreeChange:c,query:he}),re(),window[t]=w;function M(){Y()>0?Z(function(){w(document.body)}):w(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",M):M(),w}function F(e){return(e||"").replace(/^#/,"")}function _(e){const t=e===void 0?location.hash:e,n={},o=F(t);if(!o)return n;const r=o.split("&");for(let i=0;i<r.length;i++){const l=r[i];if(!l)continue;const c=l.indexOf(":"),s=c>-1?l.slice(0,c):l,a=c>-1?l.slice(c+1):"";if(s)try{n[s]=decodeURIComponent(a)}catch{n[s]=a}}return n}function le(e){if(!e)return null;const t=_();return e in t?t[e]:null}function se(e,t){if(!e)return;const n=_();t==null?delete n[e]:n[e]=String(t);const r=Object.keys(n).map(function(i){const l=n[i];return l===""?i:i+":"+encodeURIComponent(l)}).join("&");F(location.hash)!==r&&(location.hash=r)}function ce(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function ae(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 de(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function ue(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 fe(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function we(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),r=o?o.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:r}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=_,window.lnCore.hashGet=le,window.lnCore.hashSet=se,window.lnCore.hashLinkClick=ce,window.lnCore.resolveHashNamespace=ae,window.lnCore.hashSortEncode=de,window.lnCore.hashSortDecode=ue,window.lnCore.hashFilterEncode=fe,window.lnCore.hashFilterDecode=we),(function(){const e="data-ln-scroll",t="lnScroll";if(window[t]!==void 0)return;const n={"data-ln-scroll":{effect:null,type:"string",description:"Target element ID or CSS selector to scroll into view"},"data-ln-scroll-behavior":{effect:null,type:"enum",values:["smooth","auto"],fallback:"smooth",description:"Scroll animation behavior transition"},"data-ln-scroll-block":{effect:null,type:"enum",values:["start","center","end","nearest"],fallback:"start",description:"Vertical alignment positioning of scrolled target"},"data-ln-scroll-set":{effect:null,type:"string",description:"Optional state attribute assignment on target after scroll (e.g. data-ln-toggle=open)"},"data-ln-scroll-focus":{effect:null,type:"string",description:'CSS selector of element to focus after scroll, or "false" to disable'},"data-ln-scroll-delay":{effect:null,type:"integer",fallback:450,min:0,description:"Delay in milliseconds before shifting keyboard focus"},"data-ln-scroll-update-hash":{effect:null,type:"boolean",fallback:!1,description:"Whether to update URL hash with target ID"}};function o(r){if(r[t])return r[t];if(!(!r||r.tagName!=="A"&&r.tagName!=="BUTTON"))return r[t]=this,this.dom=r,this._handleClick=this._handleClick.bind(this),this.dom.addEventListener("click",this._handleClick),this}o.prototype._handleClick=function(r){if(this.dom.tagName==="A"&&(r.ctrlKey||r.metaKey||r.shiftKey||r.altKey||r.button!==0))return;let i=(this.dom.getAttribute("data-ln-scroll")||"").trim();if(i||(i=(this.dom.getAttribute("href")||"").trim()),!i||i==="#")return;!i.startsWith("#")&&!i.startsWith(".")&&!i.startsWith("[")&&(i="#"+i);let l=null;try{l=document.querySelector(i)}catch{const p=i.replace(/^#/,"");l=document.getElementById(p)}if(!l)return;r.preventDefault();const c=this.dom.getAttribute("data-ln-scroll-set");if(c){const h=c.indexOf(":");if(h!==-1){const p=c.slice(0,h).trim(),w=c.slice(h+1).trim();try{const b=document.querySelector(p);b&&(b.value=w,b.dispatchEvent(new Event("input",{bubbles:!0})),b.dispatchEvent(new Event("change",{bubbles:!0})))}catch{}}}if(N(this.dom,"ln-scroll:before-scroll",{target:l,link:this.dom,href:i}).defaultPrevented)return;const a=this.dom.getAttribute("data-ln-scroll-behavior")||"smooth",u=this.dom.getAttribute("data-ln-scroll-block")||"start";if(l.scrollIntoView({behavior:a,block:u}),P(this.dom,"data-ln-scroll-update-hash",!1))try{window.history&&window.history.pushState&&window.history.pushState(null,"",i)}catch{}const f=this.dom.getAttribute("data-ln-scroll-focus");if(f!=="false"&&f!=="0"){const h=parseInt(this.dom.getAttribute("data-ln-scroll-delay"),10),p=isNaN(h)?450:h;window.setTimeout(function(){let w=null;if(f&&f!=="true"&&f!=="1")try{w=document.querySelector(f)}catch{}w||(w=l.querySelector('input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])')),!w&&l.getAttribute("tabindex")!==null&&(w=l),w&&typeof w.focus=="function"&&w.focus({preventScroll:!0})},p)}D(this.dom,"ln-scroll:scrolled",{target:l,link:this.dom,href:i})},o.prototype.destroy=function(){this.dom[t]&&(this.dom.removeEventListener("click",this._handleClick),delete this.dom[t])},ie(e,t,o,"ln-scroll",{attributes:n})})()})();
@@ -0,0 +1,85 @@
1
+ {
2
+ "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
+ "component": "ln-scroll",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-scroll": {
7
+ "direction": "author",
8
+ "sources": [
9
+ "src/ln-scroll.js"
10
+ ],
11
+ "type": "string",
12
+ "description": "Target element ID or CSS selector to scroll into view"
13
+ },
14
+ "data-ln-scroll-behavior": {
15
+ "direction": "author",
16
+ "sources": [
17
+ "src/ln-scroll.js"
18
+ ],
19
+ "type": "enum",
20
+ "values": [
21
+ "smooth",
22
+ "auto"
23
+ ],
24
+ "fallback": "smooth",
25
+ "description": "Scroll animation behavior transition"
26
+ },
27
+ "data-ln-scroll-block": {
28
+ "direction": "author",
29
+ "sources": [
30
+ "src/ln-scroll.js"
31
+ ],
32
+ "type": "enum",
33
+ "values": [
34
+ "start",
35
+ "center",
36
+ "end",
37
+ "nearest"
38
+ ],
39
+ "fallback": "start",
40
+ "description": "Vertical alignment positioning of scrolled target"
41
+ },
42
+ "data-ln-scroll-delay": {
43
+ "direction": "author",
44
+ "sources": [
45
+ "src/ln-scroll.js"
46
+ ],
47
+ "type": "integer",
48
+ "fallback": 450,
49
+ "min": 0,
50
+ "description": "Delay in milliseconds before shifting keyboard focus"
51
+ },
52
+ "data-ln-scroll-focus": {
53
+ "direction": "author",
54
+ "sources": [
55
+ "src/ln-scroll.js"
56
+ ],
57
+ "type": "string",
58
+ "fallback": true,
59
+ "description": "CSS selector of element to focus after scroll, or "
60
+ },
61
+ "data-ln-scroll-set": {
62
+ "direction": "author",
63
+ "sources": [
64
+ "src/ln-scroll.js"
65
+ ],
66
+ "type": "string",
67
+ "description": "Optional state attribute assignment on target after scroll (e.g. data-ln-toggle=open)"
68
+ },
69
+ "data-ln-scroll-update-hash": {
70
+ "direction": null,
71
+ "sources": [
72
+ "src/ln-scroll.js"
73
+ ],
74
+ "type": "boolean",
75
+ "fallback": false,
76
+ "description": "Whether to update URL hash with target ID"
77
+ },
78
+ "data-ln-toggle": {
79
+ "direction": null,
80
+ "sources": [
81
+ "src/ln-scroll.js"
82
+ ]
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,150 @@
1
+ import { dispatch, dispatchCancelable, registerComponent, attrBool } from '../../ln-core';
2
+
3
+ (function () {
4
+ const DOM_SELECTOR = 'data-ln-scroll';
5
+ const DOM_ATTRIBUTE = 'lnScroll';
6
+
7
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
8
+
9
+ // ─── Attribute Contract (SSOT) ──────────────────────────
10
+ const ATTRIBUTES = {
11
+ 'data-ln-scroll': { effect: null, type: 'string', description: 'Target element ID or CSS selector to scroll into view' },
12
+ 'data-ln-scroll-behavior': { effect: null, type: 'enum', values: ['smooth', 'auto'], fallback: 'smooth', description: 'Scroll animation behavior transition' },
13
+ 'data-ln-scroll-block': { effect: null, type: 'enum', values: ['start', 'center', 'end', 'nearest'], fallback: 'start', description: 'Vertical alignment positioning of scrolled target' },
14
+ 'data-ln-scroll-set': { effect: null, type: 'string', description: 'Optional state attribute assignment on target after scroll (e.g. data-ln-toggle=open)' },
15
+ 'data-ln-scroll-focus': { effect: null, type: 'string', description: 'CSS selector of element to focus after scroll, or "false" to disable' },
16
+ 'data-ln-scroll-delay': { effect: null, type: 'integer', fallback: 450, min: 0, description: 'Delay in milliseconds before shifting keyboard focus' },
17
+ 'data-ln-scroll-update-hash': { effect: null, type: 'boolean', fallback: false, description: 'Whether to update URL hash with target ID' }
18
+ };
19
+
20
+ // ─── Component Constructor ───────────────────────────────
21
+ function _component(dom) {
22
+ if (dom[DOM_ATTRIBUTE]) return dom[DOM_ATTRIBUTE];
23
+ // Allow both <a> and <button> elements
24
+ if (!dom || (dom.tagName !== 'A' && dom.tagName !== 'BUTTON')) return;
25
+
26
+ dom[DOM_ATTRIBUTE] = this;
27
+ this.dom = dom;
28
+ this._handleClick = this._handleClick.bind(this);
29
+ this.dom.addEventListener('click', this._handleClick);
30
+
31
+ return this;
32
+ }
33
+
34
+ // ─── Click & Smooth Scroll Handler ───────────────────────
35
+ _component.prototype._handleClick = function (e) {
36
+ // Allow native navigation if modifier keys are pressed on <a> links
37
+ if (this.dom.tagName === 'A' && (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0)) {
38
+ return;
39
+ }
40
+
41
+ // Target resolution: data-ln-scroll="#apply" takes priority, falls back to href
42
+ let targetSelector = (this.dom.getAttribute('data-ln-scroll') || '').trim();
43
+ if (!targetSelector) {
44
+ targetSelector = (this.dom.getAttribute('href') || '').trim();
45
+ }
46
+ if (!targetSelector || targetSelector === '#') {
47
+ return;
48
+ }
49
+ // If provided as a raw ID without '#', normalize to ID selector
50
+ if (!targetSelector.startsWith('#') && !targetSelector.startsWith('.') && !targetSelector.startsWith('[')) {
51
+ targetSelector = '#' + targetSelector;
52
+ }
53
+
54
+ let targetEl = null;
55
+ try {
56
+ targetEl = document.querySelector(targetSelector);
57
+ } catch (_) {
58
+ const id = targetSelector.replace(/^#/, '');
59
+ targetEl = document.getElementById(id);
60
+ }
61
+
62
+ if (!targetEl) return;
63
+
64
+ e.preventDefault();
65
+
66
+ // Optional field preset: selector:value (e.g. data-ln-scroll-set="#opportunity_type:Company Driver")
67
+ const setAttr = this.dom.getAttribute('data-ln-scroll-set');
68
+ if (setAttr) {
69
+ const colonIdx = setAttr.indexOf(':');
70
+ if (colonIdx !== -1) {
71
+ const selector = setAttr.slice(0, colonIdx).trim();
72
+ const val = setAttr.slice(colonIdx + 1).trim();
73
+ try {
74
+ const field = document.querySelector(selector);
75
+ if (field) {
76
+ field.value = val;
77
+ field.dispatchEvent(new Event('input', { bubbles: true }));
78
+ field.dispatchEvent(new Event('change', { bubbles: true }));
79
+ }
80
+ } catch (_) {}
81
+ }
82
+ }
83
+
84
+ // Dispatch cancelable before-scroll event
85
+ const beforeEvent = dispatchCancelable(this.dom, 'ln-scroll:before-scroll', {
86
+ target: targetEl,
87
+ link: this.dom,
88
+ href: targetSelector
89
+ });
90
+ if (beforeEvent.defaultPrevented) return;
91
+
92
+ // Perform smooth scroll
93
+ const behavior = this.dom.getAttribute('data-ln-scroll-behavior') || 'smooth';
94
+ const block = this.dom.getAttribute('data-ln-scroll-block') || 'start';
95
+ targetEl.scrollIntoView({ behavior: behavior, block: block });
96
+
97
+ // Optional URL hash update
98
+ const updateHash = attrBool(this.dom, 'data-ln-scroll-update-hash', false);
99
+ if (updateHash) {
100
+ try {
101
+ if (window.history && window.history.pushState) {
102
+ window.history.pushState(null, '', targetSelector);
103
+ }
104
+ } catch (_) {}
105
+ }
106
+
107
+ // Shift focus after scrolling to preserve keyboard flow
108
+ const focusTargetAttr = this.dom.getAttribute('data-ln-scroll-focus');
109
+ if (focusTargetAttr !== 'false' && focusTargetAttr !== '0') {
110
+ const rawDelay = parseInt(this.dom.getAttribute('data-ln-scroll-delay'), 10);
111
+ const delay = isNaN(rawDelay) ? 450 : rawDelay;
112
+
113
+ window.setTimeout(function () {
114
+ let focusEl = null;
115
+ if (focusTargetAttr && focusTargetAttr !== 'true' && focusTargetAttr !== '1') {
116
+ try {
117
+ focusEl = document.querySelector(focusTargetAttr);
118
+ } catch (_) {}
119
+ }
120
+ if (!focusEl) {
121
+ focusEl = targetEl.querySelector('input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])');
122
+ }
123
+ if (!focusEl && targetEl.getAttribute('tabindex') !== null) {
124
+ focusEl = targetEl;
125
+ }
126
+ if (focusEl && typeof focusEl.focus === 'function') {
127
+ focusEl.focus({ preventScroll: true });
128
+ }
129
+ }, delay);
130
+ }
131
+
132
+ // Dispatch post-scroll event
133
+ dispatch(this.dom, 'ln-scroll:scrolled', {
134
+ target: targetEl,
135
+ link: this.dom,
136
+ href: targetSelector
137
+ });
138
+ };
139
+
140
+ _component.prototype.destroy = function () {
141
+ if (!this.dom[DOM_ATTRIBUTE]) return;
142
+ this.dom.removeEventListener('click', this._handleClick);
143
+ delete this.dom[DOM_ATTRIBUTE];
144
+ };
145
+
146
+ // ─── Registration ─────────────────────────────────────────
147
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-scroll', {
148
+ attributes: ATTRIBUTES
149
+ });
150
+ })();
@@ -0,0 +1,188 @@
1
+ # ln-search
2
+
3
+ > Applied via a two-host bridge: an `<input type="search" data-ln-search-for="targetId">` controls an element
4
+ > (`data-ln-search id="targetId"`). Typing fires native `input` events that write `target.setAttribute('data-ln-search', input.value)`.
5
+ > A `MutationObserver` on the target observes attribute changes, matches items against search tokens, and adds `data-ln-search-hide`
6
+ > and `.hidden` to non-matching elements. Clicking `[data-ln-search-clear]` sets `data-ln-search=""` and clears the input.
7
+
8
+ ---
9
+
10
+ ## ✅ Canonical Markup — Copy This (REQUIRED)
11
+
12
+ **Hard rule — non-negotiable.** Every search or filter text input MUST use the full `<search>` chrome below: the leading magnifier icon **and** the clear ("x") button.
13
+
14
+ ```html
15
+ <search aria-label="Search records">
16
+ <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
17
+ <input type="search" placeholder="Search …" data-ln-search-for="<targetId>">
18
+ <button type="button" data-ln-search-clear aria-label="Clear search">
19
+ <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
20
+ </button>
21
+ </search>
22
+
23
+ <ul id="<targetId>" data-ln-search="">
24
+ <li>Item Alpha</li>
25
+ <li>Item Beta</li>
26
+ <li data-ln-search-exclude>Pinned Item (Always Visible)</li>
27
+ </ul>
28
+ ```
29
+
30
+ For deep targeting (e.g. table rows or checkbox lists) add `data-ln-search-items="<selector>"` on the **target element**:
31
+
32
+ ```html
33
+ <table id="<targetId>" data-ln-search="" data-ln-search-items="tbody tr">
34
+ ...
35
+ </table>
36
+ ```
37
+
38
+ ---
39
+
40
+ ## 📋 SSR Table Search — Without `ln-table` (REQUIRED shape)
41
+
42
+ When a table's rows are already printed in `<tbody>` by the server and the
43
+ **only** interaction is text search — no sort, no column filters, no
44
+ data-driven mode — `ln-search` binds directly to the `<table>`. Do **not**
45
+ add `data-ln-table`.
46
+
47
+ ```html
48
+ <header class="ln-table__toolbar">
49
+ <search aria-label="Product search">
50
+ <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-search"></use></svg>
51
+ <input type="search" data-ln-search-for="demo-table" aria-label="Search products">
52
+ <button type="button" data-ln-search-clear aria-label="Clear search">
53
+ <svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-x"></use></svg>
54
+ </button>
55
+ </search>
56
+ </header>
57
+
58
+ <!-- data-ln-persist: the term survives a page reload. Drop it when the
59
+ search should start empty every visit. -->
60
+ <table data-ln-search="" data-ln-search-items="tbody tr" data-ln-persist id="demo-table">
61
+ ...
62
+ </table>
63
+ ```
64
+
65
+ Three rules govern this shape:
66
+
67
+ 1. `data-ln-search` goes on the `<table>` itself, not a wrapper `<div>` —
68
+ `data-ln-table` is absent.
69
+ 2. `data-ln-search-items="tbody tr"` is required. Without it, `_apply()`
70
+ falls back to `dom.children` — the `<thead>` and `<tbody>`, not rows. It
71
+ must be `tbody tr`, not bare `tr` — otherwise the header row's own `<tr>`
72
+ gets `data-ln-search-hide` when nothing matches.
73
+ 3. The toolbar is a `<header class="ln-table__toolbar">`, a sibling of the
74
+ `<table>` — never a `<tr><th colspan="N">` row inside `<thead>`. Only that
75
+ wrapper gets the compact toolbar layout from
76
+ `theme/components/_ln-table.scss`.
77
+
78
+ See `demo/admin/src/pages/table.html` for the live reference.
79
+
80
+ **This is a different case from `ln-table`.** The moment the table also
81
+ needs column filters (`data-ln-table-filter-col`) or data-driven rows
82
+ (`data-ln-table-source`), `data-ln-table` goes on the wrapper and `ln-table`
83
+ owns search instead — see [`ln-table`](../ln-table/README.md#search-integration-ln-search).
84
+ `ln-table` listens for `ln-search:change` on itself and calls
85
+ `preventDefault()` unconditionally; since `dispatchCancelable` bubbles, the
86
+ generic `ln-search` DOM-filter path (including `data-ln-search-items`) never
87
+ runs while `data-ln-table` is present. The two modes are not meant to be
88
+ combined on the same target — pick one per table.
89
+
90
+ ---
91
+
92
+ ## 🧭 Philosophy & Architecture
93
+
94
+ 1. **Two-Host Separation (Attribute Bridge):**
95
+ - **Control (`data-ln-search-for="targetId"`):** Forwards user input and writes the raw value to `target.setAttribute('data-ln-search', rawValue)`.
96
+ - **State Host (`data-ln-search="term"`):** Uses `MutationObserver` (`_syncAttribute`) to detect attribute changes, syncs all matching controls, and dispatches `ln-search:change`.
97
+ 2. **Tokenized AND Matching:** By default, splits search queries on whitespace and tests tokens with substring checks (`indexOf`). An item matches only if every token is present in its text.
98
+ 3. **Exemptions & Subtree Exclusion (`data-ln-search-exclude`):**
99
+ - **On item root:** The item is exempt from filtering — always visible, never given `data-ln-search-hide`.
100
+ - **On descendant:** The subtree text is excluded from search matching.
101
+ 4. **Cancelable Change Event:** Consumers (`ln-table`, `ln-list`, `ln-data-store`) call `e.preventDefault()` on `ln-search:change` to handle their own records and skip default DOM show/hide.
102
+ 5. **Boot Seeding:** Pre-filled or deep-linked `data-ln-search` attributes seed safely using `queueBoot`.
103
+
104
+ ---
105
+
106
+ ## 🛠️ Declarative API Contract
107
+
108
+ ### HTML Attributes
109
+
110
+ | Attribute | Element | Description |
111
+ | :--- | :--- | :--- |
112
+ | `data-ln-search-for="targetId"` | `<input>` / wrapper | Pointer to the target element ID. |
113
+ | `data-ln-search="term"` | Target element | Search term state. Single source of truth. |
114
+ | `data-ln-search-items="selector"` | Target element | Deep CSS selector (e.g. `tbody tr`) to query items instead of direct children. |
115
+ | `data-ln-search-fields="a,b"` | Target element | Comma-separated list of field keys forwarded in event detail. |
116
+ | `data-ln-search-exclude` | Item root or descendant | Excludes item from filtering (on root) or excludes subtree text (on descendant). |
117
+ | `data-ln-search-clear` | `<button>` | Identifies a clear button (in search control or target empty state). Clears input and resets target state. |
118
+ | `data-ln-search-clear-for="targetId"` | `<button>` | Remote clear button targeting a specific element ID anywhere on the page. |
119
+ | `data-ln-search-hide="true"` | Items in target | State attribute automatically set on non-matching elements (`display: none !important`). |
120
+ | `data-ln-hash` | Target / Control | Opt-in. Synchronizes search query to URL hash fragment (e.g. `#users-search:john`). Value is custom namespace; if empty defaults to `[targetId]-search`. |
121
+ | `data-ln-persist` | Target element | **Recommended.** Persists the search term to `localStorage` and restores it before boot; omit it only when the search must start empty on every visit. Goes on `[data-ln-search]`, never on the input. The target needs an `id`, or give the attribute an explicit value (`data-ln-persist="key"`). No effect while `data-ln-hash` is active on the target — hash wins, never double-written. Stored key format: `ln:{id}:data-ln-search` (global by default); add `data-ln-persist-scope="page"` to opt into page-scoping, which stores `ln:{id}:{pathname}:data-ln-search`. |
122
+ | `data-ln-persist-scope` | Target element | Opt-in, independent attribute. `"page"` scopes the persisted key to the current URL pathname. |
123
+
124
+ ### JavaScript API
125
+
126
+ | Instance | Property / Method | Description |
127
+ | :--- | :--- | :--- |
128
+ | `el.lnSearchControl` (Control) | `targetId`, `input`, `destroy()` | Control instance managing input. |
129
+ | `el.lnSearch` (State Host) | `term`, `nsKey`, `hashEnabled`, `_apply()`, `destroy()` | State instance owning term, URL hash sync, and DOM filter logic. |
130
+
131
+
132
+ ---
133
+
134
+ ## ⚡ DOM Events
135
+
136
+ ### `ln-search:change`
137
+ Dispatched on the **target** element whenever the search term updates.
138
+ - **Cancelable**: Yes (`e.preventDefault()` disables default DOM show/hide).
139
+ - **Payload (`detail`)**:
140
+ ```js
141
+ {
142
+ term: string, // Trimmed and lowercased string
143
+ tokens: string[], // Array of whitespace-separated tokens
144
+ targetId: string, // ID of the target
145
+ fields: string[]|null // Forwarded field names
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 🌐 Backend Search Contract (Whitespace & Wildcard Handling)
152
+
153
+ To keep remote search consistent with client-side DOM and table filtering, backend APIs (e.g. `api.php`, SQL endpoints) must mirror the tokenized search contract:
154
+
155
+ 1. **Whitespace Tokenization (`AND` Logic):** Split the query string on whitespace (`\s+`). Every token must be present in the record.
156
+ 2. **Wildcard `*` Conversion:** Convert asterisks (`*`) into SQL `%` or Regex `.*`.
157
+ 3. **Multi-Field Matching (`OR` per token):** Each token can match anywhere across the allowed searchable columns/fields.
158
+
159
+ ```php
160
+ // Reference PHP / SQLite PDO implementation
161
+ $search = isset($_GET['search']) ? trim($_GET['search']) : (isset($_GET['q']) ? trim($_GET['q']) : '');
162
+ if ($search !== '') {
163
+ $tokens = preg_split('/\s+/', $search, -1, PREG_SPLIT_NO_EMPTY);
164
+ $searchFields = ['title', 'department', 'owner', 'tags'];
165
+
166
+ foreach ($tokens as $token) {
167
+ $tokenPattern = '%' . str_replace('*', '%', $token) . '%';
168
+ $tokenPattern = preg_replace('/%+/', '%', $tokenPattern);
169
+
170
+ $fieldClauses = [];
171
+ foreach ($searchFields as $field) {
172
+ $fieldClauses[] = "$field LIKE ?";
173
+ $params[] = $tokenPattern;
174
+ }
175
+ $where[] = '(' . implode(' OR ', $fieldClauses) . ')';
176
+ }
177
+ }
178
+ ```
179
+
180
+ ---
181
+
182
+ ## ⚠️ Common Pitfalls
183
+
184
+ - **Using `data-ln-search="tableId"` on inputs:** `data-ln-search` is the target state attribute. Inputs must use `data-ln-search-for="tableId"`.
185
+ - **Programmatic Value Mutations:** Assigning `input.value = "text"` programmatically does not trigger native `input` events. Either dispatch a native `input` event on the control or set the attribute directly on the target:
186
+ ```js
187
+ document.getElementById('my-table').setAttribute('data-ln-search', 'query');
188
+ ```
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Search Target Missing ID: elements with data-ln-search must have an id for control linking
5
+ [data-ln-search]:not([id]) {
6
+ @include dev-dom-error("[data-ln-search] is missing a required 'id' attribute for search control linking.");
7
+ }
8
+
9
+ // 2. Search Control Empty Target: search inputs/controls must target a component ID
10
+ [data-ln-search-for=""] {
11
+ @include dev-inline-error("data-ln-search-for target ID cannot be empty");
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function j(){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 I(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 r=null;for(let o=1;o<t.length;o++)if(t[o]&&t[o].nodeType===1){r=t[o];break}if(!I(r))return}e.apply(console,t)}}function Z(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}function ee(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r);const o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:r});return e.dispatchEvent(o),o}function k(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){k(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const W={};function te(e,t){W[e]=t}function ne(e){return W[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=te,window.lnCore.getDataMapper=ne);function re(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,k(function(){new MutationObserver(function(){Z(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const V={};function oe(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];V[n]=t}function ie(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return V[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=oe,window.lnCore.getLocaleFallback=ie,window.lnCore.ensureLocaleObserver=re);const O={};function le(e,t){O[e]||(O[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=O[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function K(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let l=0;l<n.length;l++){const d=n[l],a=d.getAttribute("data-ln-field");t[a]!=null&&(d.textContent=t[a])}const r=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<r.length;l++){const d=r[l],a=d.getAttribute("data-ln-attr").split(",");for(let f=0;f<a.length;f++){const p=a[f].trim().split(":");if(p.length!==2)continue;const h=p[0].trim(),b=p[1].trim();t[b]!=null&&d.setAttribute(h,t[b])}}const o=e.querySelectorAll("[data-ln-show]");for(let l=0;l<o.length;l++){const d=o[l],a=d.getAttribute("data-ln-show");a in t&&d.classList.toggle("hidden",!t[a])}const i=e.querySelectorAll("[data-ln-class]");for(let l=0;l<i.length;l++){const d=i[l],a=d.getAttribute("data-ln-class").split(",");for(let f=0;f<a.length;f++){const p=a[f].trim().split(":");if(p.length!==2)continue;const h=p[0].trim(),b=p[1].trim();b in t&&d.classList.toggle(h,!!t[b])}}return e}function se(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 r=0;r<n.length;r++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[r],t??null),n[r].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)K(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 U(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(l,d){return t[d]!==void 0?t[d]:""}))}const r=function(i,l){return t[l]!==void 0?t[l]:""},o=Array.from(e.querySelectorAll("*"));e.nodeType===1&&o.push(e);for(let i=0;i<o.length;i++){const l=o[i],d=l.attributes;for(let a=0;a<d.length;a++){const f=d[a];f.value.indexOf("{{")!==-1&&l.setAttribute(f.name,f.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function ce(e,t,n,r,o,i){const l={};for(let a=0;a<e.children.length;a++){const f=e.children[a],p=f.getAttribute("data-ln-render-key");p&&(l[p]=f)}const d=document.createDocumentFragment();for(let a=0;a<t.length;a++){const f=t[a],p=String(r(f));let h=l[p];if(h)o(h,f,a);else{const b=le(n,i);if(!b||(U(b,f),h=b.firstElementChild,!h))continue;h.setAttribute("data-ln-render-key",p),o(h,f,a)}d.appendChild(h)}e.textContent="",e.appendChild(d)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=U,window.lnCore.fill=K,window.lnCore.lnFill=se,window.lnCore.renderList=ce);function L(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function ae(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function ue(e,t,n=!1){const r=e.getAttribute(t);if(r===null)return!!n;const o=r.trim().toLowerCase();return!(o==="false"||o==="0")}function de(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const R=new Map;function fe(e,t){const n=(e?e.join("|"):"")+"::"+t;if(R.has(n))return R.get(n);const r=new Set(e||[]),o=function(i,l){const d=i.getAttribute(l);return d!==null&&r.has(d)?d:t};return R.set(n,o),o}function he(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function we(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function Q(e,t,n,r,o){if(!e||t===null)return!0;const i=r||"ln-component",l=e.type;if(l==="trigger"||l==="marker"||l==="string"||l==="list"||!l||t===""&&e.fallback!==void 0)return!0;const d=a=>{o?console.warn(a,o):console.warn(a)};if(l==="boolean"){const a=t.trim().toLowerCase();return a!==""&&a!=="true"&&a!=="false"&&a!=="1"&&a!=="0"?(d(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(l==="enum"){const a=e.values||[];return a.includes(t)?!0:(d(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${a.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(l==="integer"){if(!/^-?\d+$/.test(t))return d(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const a=parseInt(t,10);return e.min!==void 0&&a<e.min?(d(`[${i}] Value ${a} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&a>e.max?(d(`[${i}] Value ${a} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(l==="float")return isNaN(Number(t))?(d(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(l==="json")try{return JSON.parse(t),!0}catch(a){return d(`[${i}] Invalid JSON for attribute "${n}": ${a.message}. Fallback: ${e.fallback}.`),!1}return!0}function pe(e,t,n){for(const r in n){const[o,i,l]=n[r];Object.defineProperty(e,r,{get:function(){return o(t,i,l)},enumerable:!0,configurable:!0})}return e}function ge(e){const t={};for(const n in e){const r=e[n];if(!r||!r.prop)continue;let o=r.read;if(!o&&r.type)switch(r.type){case"enum":o=fe(r.values,r.fallback);break;case"integer":o=ae;break;case"float":o=he;break;case"boolean":o=ue;break;case"list":o=de;break;case"json":o=we;break;case"string":default:o=L;break}o||(o=L),t[r.prop]=[o,n,r.fallback]}return t}function be(e){const t={};for(const n in e){const r=e[n];r&&r.effect&&(t[n]=r.effect)}return Object.keys(t).length?t:null}function me(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 D(e,t,n,r){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",l=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&l.push(e);for(const d of l)if(!d[n]){window.lnCore._persistSink&&d.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(d,t);try{d[n]=new r(d)}catch(a){console.error("["+n+"] init failed",d,a)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function Ce(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function q(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 P(){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 ye(e){const t=P(),n=e.observed||[];for(let r=0;r<n.length;r++){const o=n[r];t.byAttr.has(o)||t.byAttr.set(o,[]),t.byAttr.get(o).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const r in e.attributes){t.byDeclaredAttr.has(r)||t.byDeclaredAttr.set(r,[]);const o=t.byDeclaredAttr.get(r);o.some(i=>i.componentTag===e.componentTag)||o.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=me(e);if(r===null)t.reactiveWildcard.push(e);else for(const o of r)t.byReactive.has(o)||t.byReactive.set(o,[]),t.byReactive.get(o).push(e)}e.persist&&t.persist.push(e)}function J(e,t,n,r){for(let o=0;o<e.length;o++){const i=e[o];if(!t[i.attribute])continue;const l=i.effects&&i.effects[n];l?l(t,n,r):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,r)}}function Ae(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=P(),o=r.byAttr.get(n);if(j()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&I(t)){const i=r.byDeclaredAttr.get(n),l=t.getAttribute(n);for(let d=0;d<i.length;d++)Q(i[d].spec,l,n,i[d].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||o)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=r.byReactive.get(n);i&&J(i,t,n,e.oldValue),r.reactiveWildcard.length&&J(r.reactiveWildcard,t,n,e.oldValue)}if(o)for(let i=0;i<o.length;i++){const l=o[i];if(l.handler){l.handler(t,n,e.oldValue);continue}l.onAttributeChange&&t[l.attribute]?l.onAttributeChange(t,n):(D(t,l.selector,l.attribute,l.ComponentFn),l.onInit&&l.onInit(t))}}function _e(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,k(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{Ae(t[n])}catch(r){console.error("[ln-core] mutation handler failed",t[n].target,r)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function z(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function Se(e){const t=z();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const r=t[n];if(r.onSubtreeChange){const o=r.query,i=e.target.nodeType===1?e.target.matches(o)?e.target:e.target.closest(o):e.target.parentElement?e.target.parentElement.closest(o):null;i&&r.onSubtreeChange(i,e)}}for(let n=0;n<e.addedNodes.length;n++){const r=e.addedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o];D(r,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(r)}}for(let n=0;n<e.removedNodes.length;n++){const r=e.removedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o],l=i.query,d=Array.from(r.querySelectorAll(l));r.matches&&r.matches(l)&&d.push(r);for(let a=0;a<d.length;a++){const f=d[a];if(!document.contains(f)){const p=f[i.attribute];if(p&&typeof p.destroy=="function")try{p.destroy()}catch(h){console.error("["+i.attribute+"] destroy failed",f,h)}delete f[i.attribute]}}}}}}function ve(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,k(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{Se(r)}catch(o){console.error("[ln-core] lifecycle handler failed",r.target,o)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function G(e,t,n,r,o={}){const i=o.extraAttributes||[],l=o.onAttributeChange||null,d=o.onSubtreeChange||null,a=o.onInit||null,f=o.onAttrChange||null,p=o.effects||null,h=o.attributes||null,b=h?be(h):p,x=h?new Set(Object.keys(h)):null,F=o.persist||null;function _(u){const c=u||document.body;if(D(c,e,t,n),h&&j())for(const s in h){const w=h[s],m=Array.from(c.querySelectorAll("["+s+"]"));c.matches&&c.matches("["+s+"]")&&m.push(c);for(let A=0;A<m.length;A++){const g=m[A];I(g)&&Q(w,g.getAttribute(s),s,r,g)}}a&&a(c)}const S=[];if(e.indexOf("[")!==-1){const u=/\[([\w-]+)/g;let c;for(;(c=u.exec(e))!==null;)S.push(c[1])}else S.push(e);ye({selector:e,attribute:t,componentTag:r,attributes:h,ComponentFn:n,onInit:a,observed:S.concat(i),onAttributeChange:l,onAttrChange:f,effects:b,declared:x,persist:F}),_e();const M=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";z().push({selector:e,attribute:t,ComponentFn:n,onInit:a,onSubtreeChange:d,query:M}),ve(),window[t]=_;function E(){Ce()>0?q(function(){_(document.body)}):_(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",E):E(),_}function X(e){return(e||"").replace(/^#/,"")}function $(e){const t=e===void 0?location.hash:e,n={},r=X(t);if(!r)return n;const o=r.split("&");for(let i=0;i<o.length;i++){const l=o[i];if(!l)continue;const d=l.indexOf(":"),a=d>-1?l.slice(0,d):l,f=d>-1?l.slice(d+1):"";if(a)try{n[a]=decodeURIComponent(f)}catch{n[a]=f}}return n}function B(e){if(!e)return null;const t=$();return e in t?t[e]:null}function Y(e,t){if(!e)return;const n=$();t==null?delete n[e]:n[e]=String(t);const o=Object.keys(n).map(function(i){const l=n[i];return l===""?i:i+":"+encodeURIComponent(l)}).join("&");X(location.hash)!==o&&(location.hash=o)}function Ee(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function N(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 r=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return r?t?r+"-"+t:r:t||null}function Te(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function ke(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 xe(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Ie(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),r=e.slice(t+1),o=r?r.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:o}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=$,window.lnCore.hashGet=B,window.lnCore.hashSet=Y,window.lnCore.hashLinkClick=Ee,window.lnCore.resolveHashNamespace=N,window.lnCore.hashSortEncode=Te,window.lnCore.hashSortDecode=ke,window.lnCore.hashFilterEncode=xe,window.lnCore.hashFilterDecode=Ie);function H(e){return String(e||"").trim().toLowerCase()}function Oe(e){const t=H(e);return t?t.split(/\s+/).filter(Boolean):[]}function Le(e){if(e==null)return null;const t=String(e).split(",").map(n=>n.trim()).filter(Boolean);return t.length?t:null}function Re(e,t){if(!t||t.length===0)return!0;if(!e)return!1;const n=String(e).toLowerCase();for(let r=0;r<t.length;r++)if(n.indexOf(t[r])===-1)return!1;return!0}function De(e){return!e||e.length===0?"":e.join(" ").replace(/\s+/g," ").trim().toLowerCase()}(function(){const e="data-ln-search",t="lnSearch",n="data-ln-search-for",r="lnSearchControl",o="data-ln-search-items",i="data-ln-search-fields",l="data-ln-search-exclude",d="data-ln-search-hide",a="data-ln-hash";if(window[t]!==void 0)return;const f={"data-ln-search":{type:"string",effect:E,description:"Active search query term on target element or container"},"data-ln-hash":{type:"string",effect:E,description:"URL hash routing key for search state persistence"},"data-ln-search-for":{prop:"targetId",type:"string",read:L,fallback:null,description:"Target table or list element ID that this input controls"},"data-ln-search-fields":{type:"list",description:"Comma-separated field names to include in client search"},"data-ln-search-items":{type:"string",description:"CSS selector matching searchable child items"},"data-ln-search-exclude":{type:"string",description:"CSS selector matching child items to exclude from search"},"data-ln-search-hide":{type:"enum",values:["collapse","none"],fallback:"collapse",description:"CSS hiding strategy for non-matching rows"},"data-ln-search-clear-for":{type:"trigger",description:"Click trigger to clear search input for target"}},p=ge(f);function h(u){const c=N(u,"search");if(c)return c;if(u.id){const s=document.querySelector("["+n+'="'+u.id+'"]');if(s){const w=N(s,"search");if(w)return w}}return null}function b(u){return u.matches("input, textarea")?u:u.querySelector("input, textarea")}function x(u,c){const s=u.childNodes;for(let w=0;w<s.length;w++){const m=s[w];if(m.nodeType===3){c.push(m.nodeValue);continue}m.nodeType===1&&(m.hasAttribute(l)||x(m,c))}}function F(u){if(u._lnSearchText!==void 0)return u._lnSearchText;const c=[];x(u,c);const s=De(c);return u._lnSearchText=s,s}function _(u,c){if(!u.id)return;const s=document.querySelectorAll("["+n+'="'+u.id+'"]');for(const w of s){const m=b(w);m&&m.value!==c&&(m.value=c)}}function S(u){this.dom=u,this.term=u.getAttribute(e)||"",this._destroyed=!1;const c=this;return this.nsKey=h(u),this.hashEnabled=!!this.nsKey,this._onHashChange=function(){if(c._destroyed||!c.hashEnabled)return;const s=B(c.nsKey),w=c.dom.getAttribute(e)||"";s!==null&&s!==w?c.dom.setAttribute(e,s):s===null&&w!==""&&c.dom.setAttribute(e,"")},this.hashEnabled&&window.addEventListener("hashchange",this._onHashChange),q(function(){if(!c._destroyed){if(c.hashEnabled){const s=B(c.nsKey);if(s!==null&&s!==c.term){c.term=s,c.dom.setAttribute(e,s),_(c.dom,s),c._apply();return}}H(c.term)&&(_(c.dom,c.term),c._apply())}}),this}S.prototype._apply=function(){const u=this.dom,c=H(this.term),s=Oe(c);this.hashEnabled&&Y(this.nsKey,this.term?this.term:null);const w=Le(u.getAttribute(i));if(ee(u,"ln-search:change",{term:c,tokens:s,targetId:u.id,fields:w}).defaultPrevented)return;const A=u.getAttribute(o),g=A?u.querySelectorAll(A):u.children;for(let C=0;C<g.length;C++){const y=g[C];if(y.removeAttribute(d),y.hasAttribute(l)||s.length===0)continue;const T=F(y);Re(T,s)||y.setAttribute(d,"true")}},S.prototype.destroy=function(){this.dom[t]&&(this._destroyed=!0,this.hashEnabled&&this._onHashChange&&window.removeEventListener("hashchange",this._onHashChange),delete this.dom[t])};function v(u){if(this.dom=u,pe(this,u,p),this.input=b(u),this._attachHandler(),this.input&&this.input.value.trim()){const c=this;q(function(){const s=document.getElementById(c.targetId);s&&((s.getAttribute(e)||"").trim()||c._write(c.input.value))})}return this}v.prototype._write=function(u){const c=document.getElementById(this.targetId);c&&c.getAttribute(e)!==u&&c.setAttribute(e,u)},v.prototype._attachHandler=function(){if(!this.input)return;const u=this;this._onInput=function(){u._write(u.input.value)},this.input.addEventListener("input",this._onInput)},v.prototype.destroy=function(){this.dom[r]&&(this.input&&this._onInput&&this.input.removeEventListener("input",this._onInput),delete this.dom[r])};function M(u){const c=u.getAttribute("data-ln-search-clear-for");if(c){const g=document.getElementById(c),C=document.querySelector("["+n+'="'+c+'"]'),y=C?b(C):null;return{target:g,input:y}}const s=u.closest("["+e+"]");if(s){const g=s.id?document.querySelector("["+n+'="'+s.id+'"]'):null,C=g?b(g):null;return{target:s,input:C}}const w=u.closest("[data-ln-table-source], [data-ln-list-source]");if(w){const g=w.getAttribute("data-ln-table-source")||w.getAttribute("data-ln-list-source"),C=g?document.getElementById(g):null;if(C&&C.hasAttribute(e)){const y=document.querySelector("["+n+'="'+g+'"]'),T=y?b(y):null;return{target:C,input:T}}}const m=u.closest("["+n+"]");if(m){const g=m.getAttribute(n),C=g?document.getElementById(g):null,y=b(m);return{target:C,input:y}}const A=u.parentElement;if(A){const g=A.querySelector("["+n+"]");if(g){const C=g.getAttribute(n),y=C?document.getElementById(C):null,T=b(g);return{target:y,input:T}}}return{target:null,input:null}}document.addEventListener("click",function(u){const c=u.target.closest("[data-ln-search-clear], [data-ln-search-clear-for]");if(!c)return;const s=M(c);!s.target&&!s.input||(u.preventDefault(),s.input&&(s.input.value="",s.input.focus()),s.target&&s.target.setAttribute(e,""))});function E(u,c){const s=u[t];if(!s||s._destroyed)return;if(c===a){s._onHashChange&&window.removeEventListener("hashchange",s._onHashChange),s.nsKey=h(u),s.hashEnabled=!!s.nsKey,s.hashEnabled&&window.addEventListener("hashchange",s._onHashChange);return}const w=u.getAttribute(e)||"";w!==s.term&&(s.term=w,_(u,w),s._apply())}G(e,t,S,"ln-search",{attributes:f,onSubtreeChange:function(u,c){const s=c.target;s&&s._lnSearchText!==void 0&&delete s._lnSearchText,s&&s.parentElement&&s.parentElement._lnSearchText!==void 0&&delete s.parentElement._lnSearchText},persist:{attr:e,hashActive:function(u){return!!h(u)}}}),G(n,r,v,"ln-search-control")})()})();