@livenetworks/ashlar 1.6.0 → 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 (560) 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/components/ln-confirm/README.md +216 -0
  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 -167
  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/README.md +0 -162
  414. package/js/ln-confirm/_ln-confirm.scss +0 -24
  415. package/js/ln-confirm/ln-confirm.js +0 -1
  416. package/js/ln-core/crypto.js +0 -83
  417. package/js/ln-core/helpers.js +0 -788
  418. package/js/ln-core/index.js +0 -7
  419. package/js/ln-core/persist.js +0 -97
  420. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  421. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  422. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  423. package/js/ln-data-store/ln-data-store.js +0 -1
  424. package/js/ln-date/ln-date.js +0 -1
  425. package/js/ln-date/src/ln-date.js +0 -603
  426. package/js/ln-debug/README.md +0 -58
  427. package/js/ln-debug/ln-debug.js +0 -1
  428. package/js/ln-debug/src/ln-debug.js +0 -19
  429. package/js/ln-dropdown/ln-dropdown.js +0 -1
  430. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  431. package/js/ln-editor/ln-editor.js +0 -1
  432. package/js/ln-editor/ln-editor.scss +0 -43
  433. package/js/ln-external-links/ln-external-links.js +0 -1
  434. package/js/ln-fill/ln-fill.js +0 -1
  435. package/js/ln-filter/README.md +0 -189
  436. package/js/ln-filter/ln-filter-dev.scss +0 -8
  437. package/js/ln-filter/ln-filter.js +0 -1
  438. package/js/ln-filter/src/ln-filter.js +0 -419
  439. package/js/ln-form/ln-form.js +0 -1
  440. package/js/ln-http/ln-http.js +0 -1
  441. package/js/ln-link/ln-link.js +0 -1
  442. package/js/ln-list/README.md +0 -129
  443. package/js/ln-list/ln-list.js +0 -1
  444. package/js/ln-list/ln-list.scss +0 -28
  445. package/js/ln-modal/ln-modal-dev.scss +0 -13
  446. package/js/ln-modal/ln-modal.js +0 -1
  447. package/js/ln-modal/ln-modal.scss +0 -35
  448. package/js/ln-modal-coordinator/README.md +0 -99
  449. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  450. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  451. package/js/ln-nav/ln-nav.js +0 -1
  452. package/js/ln-number/README.md +0 -149
  453. package/js/ln-number/ln-number.js +0 -1
  454. package/js/ln-number/src/ln-number.js +0 -443
  455. package/js/ln-options/ln-options.js +0 -1
  456. package/js/ln-popover/ln-popover.js +0 -1
  457. package/js/ln-progress/ln-progress.js +0 -1
  458. package/js/ln-progress/src/ln-progress.js +0 -98
  459. package/js/ln-router/ln-router.js +0 -1
  460. package/js/ln-search/README.md +0 -158
  461. package/js/ln-search/ln-search-dev.scss +0 -8
  462. package/js/ln-search/ln-search.js +0 -1
  463. package/js/ln-search/src/ln-search.js +0 -117
  464. package/js/ln-slug/ln-slug.js +0 -1
  465. package/js/ln-sort/ln-sort.js +0 -1
  466. package/js/ln-sort/src/ln-sort.js +0 -178
  467. package/js/ln-sortable/ln-sortable.js +0 -1
  468. package/js/ln-stat/ln-stat.js +0 -1
  469. package/js/ln-table/ln-table.js +0 -1
  470. package/js/ln-table/ln-table.scss +0 -11
  471. package/js/ln-table-coordinator/README.md +0 -73
  472. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  473. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  474. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  475. package/js/ln-tabs/ln-tabs.js +0 -1
  476. package/js/ln-tabs/src/ln-tabs.js +0 -188
  477. package/js/ln-time/ln-time.js +0 -1
  478. package/js/ln-toast/ln-toast.js +0 -1
  479. package/js/ln-toggle/ln-toggle.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.js +0 -1
  481. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  482. package/js/ln-translations/ln-translations.js +0 -1
  483. package/js/ln-upload/README.md +0 -255
  484. package/js/ln-upload/ln-upload.js +0 -1
  485. package/js/ln-upload/ln-upload.scss +0 -20
  486. package/js/ln-upload/src/ln-upload.js +0 -423
  487. package/js/ln-validate/ln-validate.js +0 -1
  488. package/scss/components/_button.scss +0 -20
  489. package/scss/components/_confirm.scss +0 -5
  490. package/scss/components/_form.scss +0 -107
  491. package/scss/components/_status-badge.scss +0 -4
  492. package/scss/components/_toast.scss +0 -68
  493. package/scss/components/_upload.scss +0 -51
  494. package/scss/config/_theme.scss +0 -216
  495. package/scss/config/mixins/_btn.scss +0 -179
  496. package/scss/config/mixins/_confirm.scss +0 -31
  497. package/scss/config/mixins/_dropdown.scss +0 -141
  498. package/scss/config/mixins/_popover.scss +0 -64
  499. package/scss/ln-ashlar-dev.scss +0 -39
  500. /package/{js → components}/ln-core/positioning.js +0 -0
  501. /package/{js → components}/ln-core/reactive.js +0 -0
  502. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  503. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  504. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  505. /package/{js → components}/ln-search/ln-search.scss +0 -0
  506. /package/{js → components}/ln-toast/template.html +0 -0
  507. /package/{scss → theme}/components/_accordion.scss +0 -0
  508. /package/{scss → theme}/components/_ajax.scss +0 -0
  509. /package/{scss → theme}/components/_alert.scss +0 -0
  510. /package/{scss → theme}/components/_avatar.scss +0 -0
  511. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  512. /package/{scss → theme}/components/_card.scss +0 -0
  513. /package/{scss → theme}/components/_chip.scss +0 -0
  514. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  515. /package/{scss → theme}/components/_editor.scss +0 -0
  516. /package/{scss → theme}/components/_empty-state.scss +0 -0
  517. /package/{scss → theme}/components/_filter.scss +0 -0
  518. /package/{scss → theme}/components/_link.scss +0 -0
  519. /package/{scss → theme}/components/_loader.scss +0 -0
  520. /package/{scss → theme}/components/_modal.scss +0 -0
  521. /package/{scss → theme}/components/_nav.scss +0 -0
  522. /package/{scss → theme}/components/_page-header.scss +0 -0
  523. /package/{scss → theme}/components/_popover.scss +0 -0
  524. /package/{scss → theme}/components/_progress.scss +0 -0
  525. /package/{scss → theme}/components/_prose.scss +0 -0
  526. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  527. /package/{scss → theme}/components/_sections.scss +0 -0
  528. /package/{scss → theme}/components/_sidebar.scss +0 -0
  529. /package/{scss → theme}/components/_stat-card.scss +0 -0
  530. /package/{scss → theme}/components/_stepper.scss +0 -0
  531. /package/{scss → theme}/components/_timeline.scss +0 -0
  532. /package/{scss → theme}/components/_tooltip.scss +0 -0
  533. /package/{scss → theme}/components/_translations.scss +0 -0
  534. /package/{scss → theme}/config/_icons.scss +0 -0
  535. /package/{scss → theme}/config/_mixins.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  559. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  560. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -0,0 +1 @@
1
+ (function(){"use strict";function K(){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 A(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(!A(r))return}e.apply(console,t)}}function q(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 z(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 m(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){m(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const N={};function G(e,t){N[e]=t}function X(e){return N[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=G,window.lnCore.getDataMapper=X);function Y(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,m(function(){new MutationObserver(function(){q(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const $={};function Z(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];$[n]=t}function ee(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return $[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=Z,window.lnCore.getLocaleFallback=ee,window.lnCore.ensureLocaleObserver=Y);const v={};function te(e,t){v[e]||(v[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=v[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function D(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let a=0;a<n.length;a++){const c=n[a],i=c.getAttribute("data-ln-field");t[i]!=null&&(c.textContent=t[i])}const r=e.querySelectorAll("[data-ln-attr]");for(let a=0;a<r.length;a++){const c=r[a],i=c.getAttribute("data-ln-attr").split(",");for(let u=0;u<i.length;u++){const d=i[u].trim().split(":");if(d.length!==2)continue;const l=d[0].trim(),f=d[1].trim();t[f]!=null&&c.setAttribute(l,t[f])}}const o=e.querySelectorAll("[data-ln-show]");for(let a=0;a<o.length;a++){const c=o[a],i=c.getAttribute("data-ln-show");i in t&&c.classList.toggle("hidden",!t[i])}const s=e.querySelectorAll("[data-ln-class]");for(let a=0;a<s.length;a++){const c=s[a],i=c.getAttribute("data-ln-class").split(",");for(let u=0;u<i.length;u++){const d=i[u].trim().split(":");if(d.length!==2)continue;const l=d[0].trim(),f=d[1].trim();f in t&&c.classList.toggle(l,!!t[f])}}return e}function ne(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)D(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 R(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const s=n.currentNode;s.textContent.indexOf("{{")!==-1&&(s.textContent=s.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(a,c){return t[c]!==void 0?t[c]:""}))}const r=function(s,a){return t[a]!==void 0?t[a]:""},o=Array.from(e.querySelectorAll("*"));e.nodeType===1&&o.push(e);for(let s=0;s<o.length;s++){const a=o[s],c=a.attributes;for(let i=0;i<c.length;i++){const u=c[i];u.value.indexOf("{{")!==-1&&a.setAttribute(u.name,u.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function re(e,t,n,r,o,s){const a={};for(let i=0;i<e.children.length;i++){const u=e.children[i],d=u.getAttribute("data-ln-render-key");d&&(a[d]=u)}const c=document.createDocumentFragment();for(let i=0;i<t.length;i++){const u=t[i],d=String(r(u));let l=a[d];if(l)o(l,u,i);else{const f=te(n,s);if(!f||(R(f,u),l=f.firstElementChild,!l))continue;l.setAttribute("data-ln-render-key",d),o(l,u,i)}c.appendChild(l)}e.textContent="",e.appendChild(c)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=R,window.lnCore.fill=D,window.lnCore.lnFill=ne,window.lnCore.renderList=re);function I(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function oe(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function F(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 ie(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const _=new Map;function se(e,t){const n=(e?e.join("|"):"")+"::"+t;if(_.has(n))return _.get(n);const r=new Set(e||[]),o=function(s,a){const c=s.getAttribute(a);return c!==null&&r.has(c)?c:t};return _.set(n,o),o}function ae(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function le(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function M(e,t,n,r,o){if(!e||t===null)return!0;const s=r||"ln-component",a=e.type;if(a==="trigger"||a==="marker"||a==="string"||a==="list"||!a||t===""&&e.fallback!==void 0)return!0;const c=i=>{o?console.warn(i,o):console.warn(i)};if(a==="boolean"){const i=t.trim().toLowerCase();return i!==""&&i!=="true"&&i!=="false"&&i!=="1"&&i!=="0"?(c(`[${s}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(a==="enum"){const i=e.values||[];return i.includes(t)?!0:(c(`[${s}] Invalid value "${t}" for attribute "${n}". Allowed: ${i.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(a==="integer"){if(!/^-?\d+$/.test(t))return c(`[${s}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const i=parseInt(t,10);return e.min!==void 0&&i<e.min?(c(`[${s}] Value ${i} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&i>e.max?(c(`[${s}] Value ${i} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(a==="float")return isNaN(Number(t))?(c(`[${s}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(a==="json")try{return JSON.parse(t),!0}catch(i){return c(`[${s}] Invalid JSON for attribute "${n}": ${i.message}. Fallback: ${e.fallback}.`),!1}return!0}function ce(e,t,n){for(const r in n){const[o,s,a]=n[r];Object.defineProperty(e,r,{get:function(){return o(t,s,a)},enumerable:!0,configurable:!0})}return e}function ue(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=se(r.values,r.fallback);break;case"integer":o=oe;break;case"float":o=ae;break;case"boolean":o=F;break;case"list":o=ie;break;case"json":o=le;break;case"string":default:o=I;break}o||(o=I),t[r.prop]=[o,n,r.fallback]}return t}function de(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 fe(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 k(e,t,n,r){if(e.nodeType!==1)return;const s=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",a=Array.from(e.querySelectorAll(s));e.matches&&e.matches(s)&&a.push(e);for(const c of a)if(!c[n]){window.lnCore._persistSink&&c.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(c,t);try{c[n]=new r(c)}catch(i){console.error("["+n+"] init failed",c,i)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function he(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function be(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 j(){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 we(e){const t=j(),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(s=>s.componentTag===e.componentTag)||o.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=fe(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 B(e,t,n,r){for(let o=0;o<e.length;o++){const s=e[o];if(!t[s.attribute])continue;const a=s.effects&&s.effects[n];a?a(t,n,r):s.onAttrChange&&(!s.declared||s.declared.has(n))&&s.onAttrChange(t,n,r)}}function pe(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=j(),o=r.byAttr.get(n);if(K()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&A(t)){const s=r.byDeclaredAttr.get(n),a=t.getAttribute(n);for(let c=0;c<s.length;c++)M(s[c].spec,a,n,s[c].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 s=r.byReactive.get(n);s&&B(s,t,n,e.oldValue),r.reactiveWildcard.length&&B(r.reactiveWildcard,t,n,e.oldValue)}if(o)for(let s=0;s<o.length;s++){const a=o[s];if(a.handler){a.handler(t,n,e.oldValue);continue}a.onAttributeChange&&t[a.attribute]?a.onAttributeChange(t,n):(k(t,a.selector,a.attribute,a.ComponentFn),a.onInit&&a.onInit(t))}}function ge(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{pe(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 H(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function me(e){const t=H();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,s=e.target.nodeType===1?e.target.matches(o)?e.target:e.target.closest(o):e.target.parentElement?e.target.parentElement.closest(o):null;s&&r.onSubtreeChange(s,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 s=t[o];k(r,s.selector,s.attribute,s.ComponentFn),s.onInit&&s.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 s=t[o],a=s.query,c=Array.from(r.querySelectorAll(a));r.matches&&r.matches(a)&&c.push(r);for(let i=0;i<c.length;i++){const u=c[i];if(!document.contains(u)){const d=u[s.attribute];if(d&&typeof d.destroy=="function")try{d.destroy()}catch(l){console.error("["+s.attribute+"] destroy failed",u,l)}delete u[s.attribute]}}}}}}function ye(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{me(r)}catch(o){console.error("[ln-core] lifecycle handler failed",r.target,o)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function Ce(e,t,n,r,o={}){const s=o.extraAttributes||[],a=o.onAttributeChange||null,c=o.onSubtreeChange||null,i=o.onInit||null,u=o.onAttrChange||null,d=o.effects||null,l=o.attributes||null,f=l?de(l):d,b=l?new Set(Object.keys(l)):null,C=o.persist||null;function h(E){const w=E||document.body;if(k(w,e,t,n),l&&K())for(const g in l){const Ee=l[g],T=Array.from(w.querySelectorAll("["+g+"]"));w.matches&&w.matches("["+g+"]")&&T.push(w);for(let O=0;O<T.length;O++){const x=T[O];A(x)&&M(Ee,x.getAttribute(g),g,r,x)}}i&&i(w)}const L=[];if(e.indexOf("[")!==-1){const E=/\[([\w-]+)/g;let w;for(;(w=E.exec(e))!==null;)L.push(w[1])}else L.push(e);we({selector:e,attribute:t,componentTag:r,attributes:l,ComponentFn:n,onInit:i,observed:L.concat(s),onAttributeChange:a,onAttrChange:u,effects:f,declared:b,persist:C}),ge();const Le=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";H().push({selector:e,attribute:t,ComponentFn:n,onInit:i,onSubtreeChange:c,query:Le}),ye(),window[t]=h;function J(){he()>0?be(function(){h(document.body)}):h(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",J):J(),h}function W(e){return(e||"").replace(/^#/,"")}function S(e){const t=e===void 0?location.hash:e,n={},r=W(t);if(!r)return n;const o=r.split("&");for(let s=0;s<o.length;s++){const a=o[s];if(!a)continue;const c=a.indexOf(":"),i=c>-1?a.slice(0,c):a,u=c>-1?a.slice(c+1):"";if(i)try{n[i]=decodeURIComponent(u)}catch{n[i]=u}}return n}function p(e){if(!e)return null;const t=S();return e in t?t[e]:null}function y(e,t){if(!e)return;const n=S();t==null?delete n[e]:n[e]=String(t);const o=Object.keys(n).map(function(s){const a=n[s];return a===""?s:s+":"+encodeURIComponent(a)}).join("&");W(location.hash)!==o&&(location.hash=o)}function P(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 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 ve(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function _e(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 ke(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function Se(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(s){try{return decodeURIComponent(s)}catch{return s}}).filter(Boolean):[];return{key:n,values:o}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=S,window.lnCore.hashGet=p,window.lnCore.hashSet=y,window.lnCore.hashLinkClick=P,window.lnCore.resolveHashNamespace=Ae,window.lnCore.hashSortEncode=ve,window.lnCore.hashSortDecode=_e,window.lnCore.hashFilterEncode=ke,window.lnCore.hashFilterDecode=Se);function V(e,t,n,r){const o=(e||"").toLowerCase().trim();if(o)return o;if((t||"").toUpperCase()!=="A")return"";const s=n||"";if(!s.startsWith("#"))return"";const a=s.slice(1);if(!a)return"";const c=a.split("&"),i=(r||"").toLowerCase().trim();if(i)for(const l of c){const f=l.indexOf(":");if(f>0&&l.slice(0,f).toLowerCase().trim()===i)return l.slice(f+1).toLowerCase().trim()}const u=c[c.length-1]||"",d=u.indexOf(":");return(d>0?u.slice(d+1):u).toLowerCase().trim()}function U(e,t){if(!Array.isArray(e)||e.length===0)return{hashEnabled:!1,warning:null};const n=e.filter(s=>(s.tagName||"").toUpperCase()==="A"&&(s.href||"").startsWith("#")),r=n.length>0&&n.length===e.length,o=(t||"").toLowerCase().trim();return n.length>0&&n.length!==e.length?{hashEnabled:!1,warning:"mixed"}:r&&!o?{hashEnabled:!1,warning:"missing-namespace"}:{hashEnabled:r&&!!o,warning:null}}function Q(e,t,n){const r=(e||"").toLowerCase().trim();return r&&Array.isArray(t)&&t.includes(r)?r:(n||"").toLowerCase().trim()}(function(){const e="data-ln-tabs",t="lnTabs";if(window[t]!==void 0&&window[t]!==null)return;function n(i){const u=i.getAttribute("data-ln-tabs-active");i[t]&&i[t]._applyActive(u)}function r(i,u){return(i.getAttribute(u)||i.id||"").toLowerCase().trim()}const o={"data-ln-tabs":{type:"marker",description:"Mounts lnTabs component instance on tabs container"},"data-ln-tabs-active":{effect:n,type:"string",description:"Active tab key identifier"},"data-ln-tabs-default":{type:"string",description:"Default fallback tab key when none selected"},"data-ln-tabs-focus":{prop:"autoFocus",read:F,type:"boolean",fallback:!0,description:"Whether to shift focus to newly activated tab panel"},"data-ln-tabs-key":{prop:"nsKey",read:r,type:"string",description:"Hash namespace key for URL hash synchronization"},"data-ln-tab":{type:"string",description:"Tab trigger key identifier"},"data-ln-panel":{type:"string",description:"Tab content panel key identifier matching corresponding tab"}},s=ue(o);function a(i){return this.dom=i,ce(this,i,s),this.activeKey=null,c.call(this),this}function c(){this.tabs=Array.from(this.dom.querySelectorAll("[data-ln-tab]")),this.panels=Array.from(this.dom.querySelectorAll("[data-ln-panel]"));const i=this.tabs.map(l=>({tagName:l.tagName,href:l.getAttribute("href")})),u=U(i,this.nsKey);this.hashEnabled=u.hashEnabled,u.warning==="mixed"?console.warn('[ln-tabs] Mixed <a href="#…"> and <button> triggers in one group — using persist mode. Pick one: anchors for URL hash, buttons for localStorage persist.',this.dom):u.warning==="missing-namespace"&&console.warn("[ln-tabs] Anchor triggers need a hash namespace — add id or data-ln-tabs-key to the wrapper. Falling back to non-hash mode.",this.dom),this.mapTabs={},this.mapPanels={};for(const l of this.tabs){const f=V(l.getAttribute("data-ln-tab"),l.tagName,l.getAttribute("href"),this.nsKey);f?this.mapTabs[f]=l:console.warn('[ln-tabs] Trigger has no resolvable key — needs `data-ln-tab="key"` or `<a href="#…">`.',l)}for(const l of this.panels){const f=(l.getAttribute("data-ln-panel")||"").toLowerCase().trim();f&&(this.mapPanels[f]=l)}this.defaultKey=(this.dom.getAttribute("data-ln-tabs-default")||"").toLowerCase().trim()||Object.keys(this.mapTabs)[0]||"";const d=this;this._clickHandlers=[];for(const l of this.tabs){if(l[t+"Trigger"])continue;const f=function(b){const C=l.tagName==="A";if(!C&&(b.ctrlKey||b.metaKey||b.button===1))return;const h=V(l.getAttribute("data-ln-tab"),l.tagName,l.getAttribute("href"),d.nsKey);h&&(C&&!P(b)||(d.hashEnabled?p(d.nsKey)===h?d.dom.setAttribute("data-ln-tabs-active",h):y(d.nsKey,h):d.dom.setAttribute("data-ln-tabs-active",h)))};l.addEventListener("click",f),l[t+"Trigger"]=f,d._clickHandlers.push({el:l,handler:f})}if(this._onRequestSelect=function(l){const f=l.detail&&(l.detail.key||l.detail.tab);f&&d.select(f)},this.dom.addEventListener("ln-tabs:request-select",this._onRequestSelect),this._hashHandler=function(){if(!d.hashEnabled)return;const l=p(d.nsKey);d.dom.setAttribute("data-ln-tabs-active",l!==null?l:d.defaultKey)},this.hashEnabled)window.addEventListener("hashchange",this._hashHandler),this._hashHandler();else{const l=Q(this.dom.getAttribute("data-ln-tabs-active"),Object.keys(this.mapPanels),this.defaultKey);this.dom.setAttribute("data-ln-tabs-active",l)}}a.prototype.select=function(i){const u=(i+"").toLowerCase().trim();u&&(this.hashEnabled?p(this.nsKey)===u?this.dom.setAttribute("data-ln-tabs-active",u):y(this.nsKey,u):this.dom.setAttribute("data-ln-tabs-active",u))},a.prototype._applyActive=function(i){var d;if(i=Q(i,Object.keys(this.mapPanels),this.defaultKey),i===this.activeKey)return;const u=this.activeKey;if(u!==null&&z(this.dom,"ln-tabs:before-change",{key:i,previousKey:u,tab:this.mapTabs[i],panel:this.mapPanels[i],target:this.dom}).defaultPrevented){u in this.mapPanels&&(this.dom.setAttribute("data-ln-tabs-active",u),this.hashEnabled&&p(this.nsKey)!==u&&y(this.nsKey,u));return}this.activeKey=i;for(const l in this.mapTabs){const f=this.mapTabs[l];l===i?(f.setAttribute("data-active",""),f.setAttribute("aria-selected","true")):(f.removeAttribute("data-active"),f.setAttribute("aria-selected","false"))}for(const l in this.mapPanels){const f=this.mapPanels[l],b=l===i;f.classList.toggle("hidden",!b),f.setAttribute("aria-hidden",b?"false":"true")}if(this.autoFocus){const l=(d=this.mapPanels[i])==null?void 0:d.querySelector('input,button,select,textarea,[tabindex]:not([tabindex="-1"])');l&&setTimeout(()=>l.focus({preventScroll:!0}),0)}q(this.dom,"ln-tabs:change",{key:i,previousKey:u,tab:this.mapTabs[i],panel:this.mapPanels[i],target:this.dom})},a.prototype.destroy=function(){if(this.dom[t]){this.dom.removeEventListener("ln-tabs:request-select",this._onRequestSelect);for(const{el:i,handler:u}of this._clickHandlers)i.removeEventListener("click",u),delete i[t+"Trigger"];this.hashEnabled&&window.removeEventListener("hashchange",this._hashHandler),delete this.dom[t]}},Ce(e,t,a,"ln-tabs",{attributes:o,persist:{attr:"data-ln-tabs-active",hashActive:function(i){const u=Array.from(i.querySelectorAll("[data-ln-tab]")).map(function(l){return{tagName:l.tagName,href:l.getAttribute("href")}}),d=(i.getAttribute("data-ln-tabs-key")||i.id||"").toLowerCase().trim();return U(u,d).hashEnabled}}})})()})();
@@ -0,0 +1,75 @@
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-tabs",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-tabs-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-panel": {
13
+ "direction": "author",
14
+ "sources": [
15
+ "ln-tabs.scss",
16
+ "src/ln-tabs.js"
17
+ ],
18
+ "type": "string",
19
+ "description": "Tab content panel key identifier matching corresponding tab"
20
+ },
21
+ "data-ln-tab": {
22
+ "direction": "author",
23
+ "sources": [
24
+ "ln-tabs-dev.scss",
25
+ "src/ln-tabs.js"
26
+ ],
27
+ "type": "string",
28
+ "description": "Tab trigger key identifier"
29
+ },
30
+ "data-ln-tabs": {
31
+ "direction": null,
32
+ "sources": [
33
+ "ln-tabs-dev.scss",
34
+ "ln-tabs.scss",
35
+ "src/ln-tabs.js"
36
+ ],
37
+ "type": "marker",
38
+ "description": "Mounts lnTabs component instance on tabs container"
39
+ },
40
+ "data-ln-tabs-active": {
41
+ "direction": "both",
42
+ "sources": [
43
+ "src/ln-tabs.js"
44
+ ],
45
+ "type": "string",
46
+ "description": "Active tab key identifier"
47
+ },
48
+ "data-ln-tabs-default": {
49
+ "direction": "author",
50
+ "sources": [
51
+ "src/ln-tabs.js"
52
+ ],
53
+ "type": "string",
54
+ "description": "Default fallback tab key when none selected"
55
+ },
56
+ "data-ln-tabs-focus": {
57
+ "direction": null,
58
+ "sources": [
59
+ "src/ln-tabs.js"
60
+ ],
61
+ "type": "boolean",
62
+ "fallback": true,
63
+ "description": "Whether to shift focus to newly activated tab panel"
64
+ },
65
+ "data-ln-tabs-key": {
66
+ "direction": "author",
67
+ "sources": [
68
+ "ln-tabs-dev.scss",
69
+ "src/ln-tabs.js"
70
+ ],
71
+ "type": "string",
72
+ "description": "Hash namespace key for URL hash synchronization"
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,8 @@
1
+ // ==========================================================================
2
+ // ln-tabs — Functional Panel State
3
+ // ==========================================================================
4
+
5
+ [data-ln-tabs] [data-ln-panel].hidden,
6
+ [data-ln-tabs] [data-ln-panel][aria-hidden="true"] {
7
+ display: none;
8
+ }
@@ -0,0 +1,207 @@
1
+ /* Live Networks - lnTabs (hash-aware tabs — supports <button> and <a href="#nsKey:key"> triggers) */
2
+ import { dispatch, dispatchCancelable, hashGet, hashLinkClick, hashSet, registerComponent, attrSpec, defineAttrs, attrBool } from '../../ln-core';
3
+ import { deriveKeyFromTrigger, determineTabsMode, resolveActiveTabKey } from './tabs-model.js';
4
+
5
+ (function () {
6
+ const DOM_SELECTOR = "data-ln-tabs";
7
+ const DOM_ATTRIBUTE = "lnTabs";
8
+
9
+ if (window[DOM_ATTRIBUTE] !== undefined && window[DOM_ATTRIBUTE] !== null) return;
10
+
11
+ function _syncActive(el) {
12
+ const key = el.getAttribute('data-ln-tabs-active');
13
+ if (el[DOM_ATTRIBUTE]) el[DOM_ATTRIBUTE]._applyActive(key);
14
+ }
15
+
16
+ function _readTabsKey(el, name) { return (el.getAttribute(name) || el.id || '').toLowerCase().trim(); }
17
+
18
+ // ─── Attribute Contract (SSOT) ──────────────────────────
19
+ const ATTRIBUTES = {
20
+ 'data-ln-tabs': { type: 'marker', description: 'Mounts lnTabs component instance on tabs container' },
21
+ 'data-ln-tabs-active': { effect: _syncActive, type: 'string', description: 'Active tab key identifier' },
22
+ 'data-ln-tabs-default': { type: 'string', description: 'Default fallback tab key when none selected' },
23
+ 'data-ln-tabs-focus': { prop: 'autoFocus', read: attrBool, type: 'boolean', fallback: true, description: 'Whether to shift focus to newly activated tab panel' },
24
+ 'data-ln-tabs-key': { prop: 'nsKey', read: _readTabsKey, type: 'string', description: 'Hash namespace key for URL hash synchronization' },
25
+ 'data-ln-tab': { type: 'string', description: 'Tab trigger key identifier' },
26
+ 'data-ln-panel': { type: 'string', description: 'Tab content panel key identifier matching corresponding tab' }
27
+ };
28
+
29
+ const ATTR_SPEC = attrSpec(ATTRIBUTES);
30
+
31
+ function _component(dom) { this.dom = dom; defineAttrs(this, dom, ATTR_SPEC); this.activeKey = null; _init.call(this); return this; }
32
+
33
+ function _init() {
34
+ this.tabs = Array.from(this.dom.querySelectorAll("[data-ln-tab]"));
35
+ this.panels = Array.from(this.dom.querySelectorAll("[data-ln-panel]"));
36
+
37
+ const triggerDescriptors = this.tabs.map(t => ({
38
+ tagName: t.tagName,
39
+ href: t.getAttribute('href')
40
+ }));
41
+ const modeInfo = determineTabsMode(triggerDescriptors, this.nsKey);
42
+ this.hashEnabled = modeInfo.hashEnabled;
43
+
44
+ if (modeInfo.warning === 'mixed') {
45
+ console.warn('[ln-tabs] Mixed <a href="#…"> and <button> triggers in one group — using persist mode. Pick one: anchors for URL hash, buttons for localStorage persist.', this.dom);
46
+ } else if (modeInfo.warning === 'missing-namespace') {
47
+ console.warn('[ln-tabs] Anchor triggers need a hash namespace — add id or data-ln-tabs-key to the wrapper. Falling back to non-hash mode.', this.dom);
48
+ }
49
+
50
+ this.mapTabs = {};
51
+ this.mapPanels = {};
52
+ for (const t of this.tabs) {
53
+ const key = deriveKeyFromTrigger(t.getAttribute('data-ln-tab'), t.tagName, t.getAttribute('href'), this.nsKey);
54
+ if (key) {
55
+ this.mapTabs[key] = t;
56
+ } else {
57
+ console.warn('[ln-tabs] Trigger has no resolvable key — needs `data-ln-tab="key"` or `<a href="#…">`.', t);
58
+ }
59
+ }
60
+ for (const p of this.panels) {
61
+ const key = (p.getAttribute("data-ln-panel") || "").toLowerCase().trim();
62
+ if (key) this.mapPanels[key] = p;
63
+ }
64
+
65
+ // Not a pure attribute mirror: falls back to the first tab key, which
66
+ // only exists once mapTabs is built. A reader cannot reach instance state.
67
+ this.defaultKey = (this.dom.getAttribute("data-ln-tabs-default") || "").toLowerCase().trim()
68
+ || Object.keys(this.mapTabs)[0] || "";
69
+
70
+ const self = this;
71
+ this._clickHandlers = [];
72
+ for (const t of this.tabs) {
73
+ if (t[DOM_ATTRIBUTE + 'Trigger']) continue;
74
+ const handler = function (e) {
75
+ const isAnchor = t.tagName === "A";
76
+ if (!isAnchor && (e.ctrlKey || e.metaKey || e.button === 1)) return;
77
+ const key = deriveKeyFromTrigger(t.getAttribute('data-ln-tab'), t.tagName, t.getAttribute('href'), self.nsKey);
78
+ if (!key) return;
79
+ if (isAnchor && !hashLinkClick(e)) return;
80
+ if (self.hashEnabled) {
81
+ if (hashGet(self.nsKey) === key) self.dom.setAttribute('data-ln-tabs-active', key);
82
+ else hashSet(self.nsKey, key);
83
+ } else {
84
+ self.dom.setAttribute('data-ln-tabs-active', key);
85
+ }
86
+ };
87
+ t.addEventListener("click", handler);
88
+ t[DOM_ATTRIBUTE + 'Trigger'] = handler;
89
+ self._clickHandlers.push({ el: t, handler: handler });
90
+ }
91
+
92
+ this._onRequestSelect = function (e) {
93
+ const key = e.detail && (e.detail.key || e.detail.tab);
94
+ if (key) self.select(key);
95
+ };
96
+ this.dom.addEventListener('ln-tabs:request-select', this._onRequestSelect);
97
+
98
+ this._hashHandler = function () {
99
+ if (!self.hashEnabled) return;
100
+ const val = hashGet(self.nsKey);
101
+ self.dom.setAttribute('data-ln-tabs-active', val !== null ? val : self.defaultKey);
102
+ };
103
+
104
+ if (this.hashEnabled) {
105
+ window.addEventListener("hashchange", this._hashHandler);
106
+ this._hashHandler();
107
+ } else {
108
+ const initialKey = resolveActiveTabKey(this.dom.getAttribute('data-ln-tabs-active'), Object.keys(this.mapPanels), this.defaultKey);
109
+ this.dom.setAttribute('data-ln-tabs-active', initialKey);
110
+ }
111
+ }
112
+
113
+ _component.prototype.select = function (key) {
114
+ const k = (key + '').toLowerCase().trim();
115
+ if (!k) return;
116
+ if (this.hashEnabled) {
117
+ if (hashGet(this.nsKey) === k) this.dom.setAttribute('data-ln-tabs-active', k);
118
+ else hashSet(this.nsKey, k);
119
+ } else {
120
+ this.dom.setAttribute('data-ln-tabs-active', k);
121
+ }
122
+ };
123
+
124
+ _component.prototype._applyActive = function (key) {
125
+ key = resolveActiveTabKey(key, Object.keys(this.mapPanels), this.defaultKey);
126
+ if (key === this.activeKey) return;
127
+
128
+ const prevKey = this.activeKey;
129
+ if (prevKey !== null) {
130
+ const before = dispatchCancelable(this.dom, 'ln-tabs:before-change', {
131
+ key: key,
132
+ previousKey: prevKey,
133
+ tab: this.mapTabs[key],
134
+ panel: this.mapPanels[key],
135
+ target: this.dom
136
+ });
137
+ if (before.defaultPrevented) {
138
+ if (prevKey in this.mapPanels) {
139
+ this.dom.setAttribute('data-ln-tabs-active', prevKey);
140
+ if (this.hashEnabled && hashGet(this.nsKey) !== prevKey) {
141
+ hashSet(this.nsKey, prevKey);
142
+ }
143
+ }
144
+ return;
145
+ }
146
+ }
147
+
148
+ this.activeKey = key;
149
+
150
+ for (const k in this.mapTabs) {
151
+ const btn = this.mapTabs[k];
152
+ if (k === key) {
153
+ btn.setAttribute("data-active", "");
154
+ btn.setAttribute("aria-selected", "true");
155
+ } else {
156
+ btn.removeAttribute("data-active");
157
+ btn.setAttribute("aria-selected", "false");
158
+ }
159
+ }
160
+ for (const k in this.mapPanels) {
161
+ const panel = this.mapPanels[k];
162
+ const show = (k === key);
163
+ panel.classList.toggle("hidden", !show);
164
+ panel.setAttribute("aria-hidden", show ? "false" : "true");
165
+ }
166
+ if (this.autoFocus) {
167
+ const first = this.mapPanels[key]?.querySelector('input,button,select,textarea,[tabindex]:not([tabindex="-1"])');
168
+ if (first) setTimeout(() => first.focus({ preventScroll: true }), 0);
169
+ }
170
+ dispatch(this.dom, 'ln-tabs:change', {
171
+ key: key,
172
+ previousKey: prevKey,
173
+ tab: this.mapTabs[key],
174
+ panel: this.mapPanels[key],
175
+ target: this.dom
176
+ });
177
+ };
178
+
179
+ _component.prototype.destroy = function () {
180
+ if (!this.dom[DOM_ATTRIBUTE]) return;
181
+ this.dom.removeEventListener('ln-tabs:request-select', this._onRequestSelect);
182
+ for (const { el, handler } of this._clickHandlers) {
183
+ el.removeEventListener("click", handler);
184
+ delete el[DOM_ATTRIBUTE + 'Trigger'];
185
+ }
186
+ if (this.hashEnabled) {
187
+ window.removeEventListener("hashchange", this._hashHandler);
188
+ }
189
+ delete this.dom[DOM_ATTRIBUTE];
190
+ };
191
+
192
+ // ─── Init ──────────────────────────────────────────────────
193
+
194
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-tabs', {
195
+ attributes: ATTRIBUTES,
196
+ persist: {
197
+ attr: 'data-ln-tabs-active',
198
+ hashActive: function (el) {
199
+ const triggers = Array.from(el.querySelectorAll('[data-ln-tab]')).map(function (t) {
200
+ return { tagName: t.tagName, href: t.getAttribute('href') };
201
+ });
202
+ const nsKey = (el.getAttribute('data-ln-tabs-key') || el.id || '').toLowerCase().trim();
203
+ return determineTabsMode(triggers, nsKey).hashEnabled;
204
+ }
205
+ }
206
+ });
207
+ })();
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Derives a tab key from an explicit attribute or anchor href hash fragment.
3
+ * @param {string|null} explicitTabAttr
4
+ * @param {string} tagName
5
+ * @param {string|null} href
6
+ * @param {string|null} [nsKey]
7
+ * @returns {string}
8
+ */
9
+ export function deriveKeyFromTrigger(explicitTabAttr, tagName, href, nsKey) {
10
+ const explicit = (explicitTabAttr || '').toLowerCase().trim();
11
+ if (explicit) return explicit;
12
+
13
+ if ((tagName || '').toUpperCase() !== 'A') return '';
14
+
15
+ const rawHref = href || '';
16
+ if (!rawHref.startsWith('#')) return '';
17
+
18
+ const raw = rawHref.slice(1);
19
+ if (!raw) return '';
20
+
21
+ const fragments = raw.split('&');
22
+ const normalizedNs = (nsKey || '').toLowerCase().trim();
23
+
24
+ if (normalizedNs) {
25
+ for (const frag of fragments) {
26
+ const sep = frag.indexOf(':');
27
+ if (sep > 0 && frag.slice(0, sep).toLowerCase().trim() === normalizedNs) {
28
+ return frag.slice(sep + 1).toLowerCase().trim();
29
+ }
30
+ }
31
+ }
32
+
33
+ const last = fragments[fragments.length - 1] || '';
34
+ const sep = last.indexOf(':');
35
+ return (sep > 0 ? last.slice(sep + 1) : last).toLowerCase().trim();
36
+ }
37
+
38
+ /**
39
+ * Determines whether tabs should use URL hash sync mode or local persist mode.
40
+ * @param {Array<{ tagName: string, href?: string|null }>} triggers
41
+ * @param {string|null} nsKey
42
+ * @returns {{ hashEnabled: boolean, warning: 'mixed' | 'missing-namespace' | null }}
43
+ */
44
+ export function determineTabsMode(triggers, nsKey) {
45
+ if (!Array.isArray(triggers) || triggers.length === 0) {
46
+ return { hashEnabled: false, warning: null };
47
+ }
48
+
49
+ const anchorTabs = triggers.filter(
50
+ t => (t.tagName || '').toUpperCase() === 'A' && (t.href || '').startsWith('#')
51
+ );
52
+ const allAnchors = anchorTabs.length > 0 && anchorTabs.length === triggers.length;
53
+ const normalizedNs = (nsKey || '').toLowerCase().trim();
54
+
55
+ if (anchorTabs.length > 0 && anchorTabs.length !== triggers.length) {
56
+ return { hashEnabled: false, warning: 'mixed' };
57
+ }
58
+
59
+ if (allAnchors && !normalizedNs) {
60
+ return { hashEnabled: false, warning: 'missing-namespace' };
61
+ }
62
+
63
+ return {
64
+ hashEnabled: allAnchors && Boolean(normalizedNs),
65
+ warning: null
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Resolves the valid active key against registered panel keys with fallback.
71
+ * @param {string|null} requestedKey
72
+ * @param {string[]} validPanelKeys
73
+ * @param {string} defaultKey
74
+ * @returns {string}
75
+ */
76
+ export function resolveActiveTabKey(requestedKey, validPanelKeys, defaultKey) {
77
+ const key = (requestedKey || '').toLowerCase().trim();
78
+ if (key && Array.isArray(validPanelKeys) && validPanelKeys.includes(key)) {
79
+ return key;
80
+ }
81
+ return (defaultKey || '').toLowerCase().trim();
82
+ }
@@ -1,95 +1,96 @@
1
- # ln-time
2
-
3
- A zero-dependency, progressive **Timezone-Aware Timestamp Formatter** that localizes standard HTML `<time>` elements using native browser APIs.
4
-
5
- It replaces server-rendered fallback text with localized, timezone-aware date and time formats, performing automatic live updates for relative timestamps using a single shared timer scheduler.
6
-
7
- ---
8
-
9
- ## 🧭 Philosophy & Architecture
10
-
11
- 1. **Progressive Enhancement:** Server layouts (e.g. Laravel Blade templates) render standard text fallbacks inside native `<time>` elements. If JavaScript fails or is blocked, the original text is preserved; when active, the component compiles and overwrites it.
12
- 2. **Standard-Based API Concurrency:** Built entirely on standard browser APIs (`Intl.DateTimeFormat` and `Intl.RelativeTimeFormat`). It bypasses heavy date-parsing libraries, leveraging the browser's native locale and timezone mappings.
13
- 3. **Shared Interval Loop:** Dynamic relative timestamps (e.g., "5 minutes ago") auto-update every 60 seconds. Instead of spinning up individual intervals, a single global interval manager coordinates all active elements, automatically purging detached nodes.
14
- 4. **Reactive State Resolution:** All state changes are governed by native HTML attributes. Changing `datetime` or `data-ln-time` triggers automatic DOM re-renders via `MutationObserver` synchronization.
15
-
16
- ---
17
-
18
- ## 📦 Minimal Blueprint
19
-
20
- ### Static Localized Date
21
- Provide a Unix timestamp (in seconds) via `datetime` and specify the formatting mode.
22
- ```html
23
- <!-- Server renders a fallback, JS enhances with user timezone and locale -->
24
- <time data-ln-time="full" datetime="1736952600">January 15, 2025</time>
25
- ```
26
-
27
- ### Auto-Updating Relative Timestamp
28
- ```html
29
- <!-- Automatically updates every 60s (e.g. "3 minutes ago") -->
30
- <time data-ln-time="relative" datetime="1736952600">Jan 15</time>
31
- ```
32
-
33
- ### Custom Localized Override
34
- ```html
35
- <!-- Localizes the time string to Macedonian regardless of browser defaults -->
36
- <time data-ln-time="full" datetime="1736952600" data-ln-time-locale="mk">Јануари 15, 2025</time>
37
- ```
38
-
39
- ---
40
-
41
- ## 🛠️ Declarative API Contract
42
-
43
- ### HTML Attributes
44
-
45
- | Attribute | Elements | Description |
46
- | :--- | :--- | :--- |
47
- | `data-ln-time` | `<time>` | Format mode: `relative`, `short`, `full`, `date`, `time`. |
48
- | `datetime` | `<time>` | **Required**. Unix timestamp in **seconds** (not milliseconds). |
49
- | `data-ln-time-locale`| `<time>` | Opt-in. Force-overrides translation locale (e.g. `"de"`, `"mk"`). |
50
-
51
- ### Format Modes
52
-
53
- | Mode | Output Example | Intl Engine |
54
- | :--- | :--- | :--- |
55
- | `relative` | `"3 hr. ago"`, `"just now"`, `"in 2 hr."` | `Intl.RelativeTimeFormat` |
56
- | `short` | `"Jan 15"` (current year) / `"Jan 15, 2024"` | `{ month: 'short', day: 'numeric' }` |
57
- | `full` | `"January 15, 2025 at 2:30 PM"` | `{ dateStyle: 'long', timeStyle: 'short' }` |
58
- | `date` | `"Jan 15, 2025"` | `{ dateStyle: 'medium' }` |
59
- | `time` | `"2:30 PM"` | `{ timeStyle: 'short' }` |
60
-
61
- ---
62
-
63
- ## ⚡ Live Relative Thresholds
64
-
65
- | Elapsed Time | Evaluated Unit | Output Example (English) |
66
- | :--- | :--- | :--- |
67
- | `< 10 seconds` | — | `"now"` |
68
- | `< 60 seconds` | `second` | `"45 sec. ago"` |
69
- | `< 60 minutes` | `minute` | `"5 min. ago"` |
70
- | `< 24 hours` | `hour` | `"3 hr. ago"` |
71
- | `< 7 days` | `day` | `"2 days ago"` |
72
- | `< 30 days` | `week` | `"2 wk. ago"` |
73
- | `>= 30 days` | — | Falls back to static `short` date. |
74
-
75
- ---
76
-
77
- ## ⚠️ Common Pitfalls
78
-
79
- - **Passing Millisecond Timestamps:** Standard database fields and JS date objects often return milliseconds (13 digits). `ln-time` maps strictly to Unix seconds (10 digits). Divide milliseconds by `1000` before rendering.
80
- - **Using ISO Strings:** `datetime="2025-01-15T14:30:00Z"` is not parsed. The component skips non-numeric values, leaving the original fallback text.
81
- - **Empty `datetime` Attributes:** If the `datetime` attribute is omitted or empty, the component will skip formatting to preserve server fallback strings.
82
-
83
- ---
84
-
85
- ## 🔧 Internals
86
-
87
- Source: `js/ln-time/ln-time.js`. Registers via `ln-core`'s `registerComponent` with `extraAttributes: ['datetime']` — the shared observer treats mutations to `datetime` (not just `data-ln-time`) as a re-render trigger, so swapping the timestamp on an existing element re-formats it without a manual re-init call.
88
-
89
- ### Shared interval
90
-
91
- All `relative`-mode elements auto-refresh off a single shared `setInterval` (60s), not one timer per element. The interval starts lazily on the first relative element and stops when the last one is gone; elements removed from the DOM without calling `destroy()` are purged from the pool automatically on the next tick rather than leaking.
92
-
93
- ### Dependencies
94
-
95
- Built entirely on `Intl.DateTimeFormat` and `Intl.RelativeTimeFormat` (both browser built-ins) — no date-parsing library, no polyfill.
1
+ # ln-time
2
+
3
+ > Applied to a `<time data-ln-time="..." datetime="...">` element where `datetime` holds a Unix timestamp in seconds.
4
+ > On initialization and on attribute mutation, it formats the timestamp using browser `Intl.DateTimeFormat` or `Intl.RelativeTimeFormat`
5
+ > and writes the localized string to `time.textContent`. When set to `relative`, a shared 60-second timer periodically recalculates
6
+ > and updates `textContent` without polling the server.
7
+
8
+ ---
9
+
10
+ ## 🧭 Philosophy & Architecture
11
+
12
+ 1. **Progressive Enhancement:** Server layouts (e.g. Laravel Blade templates) render standard text fallbacks inside native `<time>` elements. If JavaScript fails or is blocked, the original text is preserved; when active, the component compiles and overwrites it.
13
+ 2. **Standard-Based API Concurrency:** Built entirely on standard browser APIs (`Intl.DateTimeFormat` and `Intl.RelativeTimeFormat`). It bypasses heavy date-parsing libraries, leveraging the browser's native locale and timezone mappings.
14
+ 3. **Shared Interval Loop:** Dynamic relative timestamps (e.g., "5 minutes ago") auto-update every 60 seconds. Instead of spinning up individual intervals, a single global interval manager coordinates all active elements, automatically purging detached nodes.
15
+ 4. **Reactive State Resolution:** All state changes are governed by native HTML attributes. Changing `datetime` or `data-ln-time` triggers automatic DOM re-renders via `MutationObserver` synchronization.
16
+
17
+ ---
18
+
19
+ ## 📦 Minimal Blueprint
20
+
21
+ ### Static Localized Date
22
+ Provide a Unix timestamp (in seconds) via `datetime` and specify the formatting mode.
23
+ ```html
24
+ <!-- Server renders a fallback, JS enhances with user timezone and locale -->
25
+ <time data-ln-time="full" datetime="1736952600">January 15, 2025</time>
26
+ ```
27
+
28
+ ### Auto-Updating Relative Timestamp
29
+ ```html
30
+ <!-- Automatically updates every 60s (e.g. "3 minutes ago") -->
31
+ <time data-ln-time="relative" datetime="1736952600">Jan 15</time>
32
+ ```
33
+
34
+ ### Custom Localized Override
35
+ ```html
36
+ <!-- Localizes the time string to Macedonian regardless of browser defaults -->
37
+ <time data-ln-time="full" datetime="1736952600" data-ln-time-locale="mk">Јануари 15, 2025</time>
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 🛠️ Declarative API Contract
43
+
44
+ ### HTML Attributes
45
+
46
+ | Attribute | Elements | Description |
47
+ | :--- | :--- | :--- |
48
+ | `data-ln-time` | `<time>` | Format mode: `relative`, `short`, `full`, `date`, `time`. |
49
+ | `datetime` | `<time>` | **Required**. Unix timestamp in **seconds**. Milliseconds and ISO 8601 strings are also read (via `ln-core`'s `parseDateInput`). |
50
+ | `data-ln-time-locale`| `<time>` | Opt-in. Force-overrides translation locale (e.g. `"de"`, `"mk"`). |
51
+
52
+ ### Format Modes
53
+
54
+ | Mode | Output Example | Intl Engine |
55
+ | :--- | :--- | :--- |
56
+ | `relative` | `"3 hr. ago"`, `"just now"`, `"in 2 hr."` | `Intl.RelativeTimeFormat` |
57
+ | `short` | `"Jan 15"` (current year) / `"Jan 15, 2024"` | `{ month: 'short', day: 'numeric' }` |
58
+ | `full` | `"January 15, 2025 at 2:30 PM"` | `{ dateStyle: 'long', timeStyle: 'short' }` |
59
+ | `date` | `"Jan 15, 2025"` | `{ dateStyle: 'medium' }` |
60
+ | `time` | `"2:30 PM"` | `{ timeStyle: 'short' }` |
61
+
62
+ ---
63
+
64
+ ## ⚡ Live Relative Thresholds
65
+
66
+ | Elapsed Time | Evaluated Unit | Output Example (English) |
67
+ | :--- | :--- | :--- |
68
+ | `< 10 seconds` | — | `"now"` |
69
+ | `< 60 seconds` | `second` | `"45 sec. ago"` |
70
+ | `< 60 minutes` | `minute` | `"5 min. ago"` |
71
+ | `< 24 hours` | `hour` | `"3 hr. ago"` |
72
+ | `< 7 days` | `day` | `"2 days ago"` |
73
+ | `< 30 days` | `week` | `"2 wk. ago"` |
74
+ | `>= 30 days` | — | Falls back to static `short` date. |
75
+
76
+ ---
77
+
78
+ ## ⚠️ Common Pitfalls
79
+
80
+ - **Seconds vs milliseconds is decided by magnitude:** a value ≥ 10¹¹ is read as milliseconds, smaller as seconds, so `1736952600` and `1736952600000` render the same instant.
81
+ - **Date-only ISO is local:** `datetime="2025-01-15"` is local midnight of that day; a full timestamp (`2025-01-15T14:30:00Z`) is that exact instant.
82
+ - **Empty `datetime` Attributes:** If the `datetime` attribute is omitted or empty, the component will skip formatting to preserve server fallback strings.
83
+
84
+ ---
85
+
86
+ ## 🔧 Internals
87
+
88
+ Source: `components/ln-time/ln-time.js`. Registers via `ln-core`'s `registerComponent` with `extraAttributes: ['datetime']` — the shared observer treats mutations to `datetime` (not just `data-ln-time`) as a re-render trigger, so swapping the timestamp on an existing element re-formats it without a manual re-init call.
89
+
90
+ ### Shared interval
91
+
92
+ All `relative`-mode elements auto-refresh off a single shared `setInterval` (60s), not one timer per element. The interval starts lazily on the first relative element and stops when the last one is gone; elements removed from the DOM without calling `destroy()` are purged from the pool automatically on the next tick rather than leaking.
93
+
94
+ ### Dependencies
95
+
96
+ Built entirely on `Intl.DateTimeFormat` and `Intl.RelativeTimeFormat` (both browser built-ins) — no date-parsing library, no polyfill.
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Host Element Check: data-ln-time belongs on native <time> elements
5
+ [data-ln-time]:not(time) {
6
+ @include dev-inline-error("data-ln-time belongs on native <time> elements");
7
+ }
8
+
9
+ // 2. Datetime Attribute Check: <time data-ln-time> requires a datetime attribute
10
+ time[data-ln-time]:not([datetime]) {
11
+ @include dev-inline-error("<time data-ln-time> is missing required datetime attribute");
12
+ }
13
+ }