@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
@@ -1,196 +1,198 @@
1
- # ln-toggle
2
-
3
- > The smallest reactive primitive in `ln-ashlar` — a highly-specialized binary state machine.
4
-
5
- ---
6
-
7
- ## 1. Philosophy & The Primitive Mindset
8
-
9
- In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating heavy components that mix state, visual presentation, and layout, we separate them into isolated concerns:
10
-
11
- 1. **The State Machine (JavaScript)**: The `ln-toggle` component (145 lines) only manages binary `open` / `close` state in the DOM and synchronizes ARIA accessibility. It does not own animations or visual geometries.
12
- 2. **The Visual Presentation (CSS)**: Visual transitions are handled in Vanilla CSS. The component simply toggles the `.open` class on the panel. CSS reads this class and runs transitions (e.g. height collapse or sliding drawers).
13
- 3. **Decoupled Binding (HTML)**: Triggers and panels are matched purely by ID. They can live anywhere in the DOM. Multiple triggers pointing to a single panel are supported natively, and all triggers stay perfectly synchronized.
14
-
15
- ---
16
-
17
- ## 2. Minimal Blueprint
18
-
19
- Triggers and panels are bound via ID. A panel must have a unique `id` and the `data-ln-toggle` attribute.
20
-
21
- ```html
22
- <!-- Trigger anywhere -->
23
- <button data-ln-toggle-for="example-panel">Toggle Options</button>
24
-
25
- <!-- Panel anywhere -->
26
- <section id="example-panel" data-ln-toggle class="collapsible">
27
- <article class="collapsible-body">
28
- <p>This is smooth collapsible content.</p>
29
- </article>
30
- </section>
31
- ```
32
-
33
- ### Key Anatomy Rules
34
- - **The Panel (`data-ln-toggle`)**: The value `open` represents open; anything else (empty or `close`) represents closed.
35
- - **The Trigger (`data-ln-toggle-for="id"`)**: Automatically intercepts clicks to toggle the panel.
36
- - **The Close Trigger (`data-ln-toggle-action="close"`)**: Forces the trigger to only close the panel (e.g. an "X" button inside a sidebar drawer).
37
- - **The Body (`.collapsible-body`)**: Holds all padding and margins. The parent `.collapsible` container must have zero padding so it can transition to exactly `0px` height cleanly.
38
-
39
- ---
40
-
41
- ## 3. The Declarative API & State Contract
42
-
43
- There are no imperative JavaScript methods (like `open()` or `close()`) on the component instance. **The HTML attribute is the sole contract.**
44
-
45
- Triggers, sibling components, external scripts, and manual DevTools edits all change state by writing the attribute:
46
-
47
- ```js
48
- const panel = document.getElementById('example-panel');
49
-
50
- // Open the panel
51
- panel.setAttribute('data-ln-toggle', 'open');
52
-
53
- // Close the panel
54
- panel.setAttribute('data-ln-toggle', 'close');
55
-
56
- // Read-only state query
57
- panel.lnToggle.isOpen; // Returns true/false
58
- ```
59
-
60
- ### Attributes
61
- - `data-ln-toggle`: Placed on the panel to create the toggle instance.
62
- - `data-ln-toggle-for`: Placed on triggers referencing the panel ID.
63
- - `data-ln-toggle-action="open|close"`: Forces a trigger button to only open or only close the target.
64
- - `data-ln-persist`: Saves the panel state individually in `localStorage`.
65
- - storage key: `ln:toggle:{pagePath}:{id}`. Same IDs on different pages store separately.
66
-
67
- ---
68
-
69
- ## 4. Transition Events
70
-
71
- All events bubble. `event.detail.target` is always the panel element.
72
-
73
- | Event | Cancelable | Dispatched When |
74
- |---|---|---|
75
- | **`ln-toggle:before-open`** | **Yes** | After attribute flips to `"open"`, before transition starts. Calling `event.preventDefault()` cancels the transition and reverts the attribute. |
76
- | **`ln-toggle:open`** | No | After panel is fully open, classes added, and ARIA synced. |
77
- | **`ln-toggle:before-close`** | **Yes** | After attribute flips to `"close"`, before transition starts. Calling `event.preventDefault()` cancels the close and reverts the attribute. |
78
- | **`ln-toggle:close`** | No | After panel is fully closed, classes removed, and ARIA synced. |
79
-
80
- ```js
81
- // Example: Cancel open transition for unauthorized users
82
- document.addEventListener('ln-toggle:before-open', (e) => {
83
- if (e.detail.target.id === 'secure-panel' && !currentUser.isAdmin) {
84
- e.preventDefault(); // Reverts attribute back to "close"
85
- }
86
- });
87
- ```
88
-
89
- ---
90
-
91
- ## 5. Integration Patterns
92
-
93
- ### A. Sidebar Drawer
94
- Combine the panel with the library's `@mixin sidebar-drawer` and add a close button inside the sidebar:
95
- ```html
96
- <aside id="menu" data-ln-toggle data-ln-persist class="sidebar">
97
- <button data-ln-toggle-for="menu" data-ln-toggle-action="close">×</button>
98
- </aside>
99
- ```
100
-
101
- ### B. Smooth Height Collapsible
102
- Combine the panel with the library's `@mixin collapsible` and `.collapsible-body` wrapper to animate height cleanly:
103
- ```html
104
- <section id="panel" data-ln-toggle class="collapsible">
105
- <div class="collapsible-body">Content goes here...</div>
106
- </section>
107
- ```
108
-
109
- ### C. Dismissible Alert
110
- Combine the alert card with `data-ln-persist` so that once the user closes the alert, it stays closed across page reloads:
111
- ```html
112
- <div class="alert" id="promo-banner" data-ln-toggle="open" data-ln-persist>
113
- <p>Promo code active!</p>
114
- <button data-ln-toggle-for="promo-banner" data-ln-toggle-action="close">×</button>
115
- </div>
116
- ```
117
-
118
- ---
119
-
120
- ## 6. Integration & Source Files
121
-
122
- - **Unified Bundle**: Loaded automatically with the main bundle:
123
- ```html
124
- <script src="dist/ln-ashlar.iife.js" defer></script>
125
- ```
126
- - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
127
- ```html
128
- <script src="js/ln-toggle/ln-toggle.js" defer></script>
129
- ```
130
- - **Active Source (ESM)**: Development source is located at [js/ln-toggle/src/ln-toggle.js](file:///c:/laragon/www/ln-ashlar/js/ln-toggle/src/ln-toggle.js).
131
-
132
- ---
133
-
134
- ## Related
135
- - **[`ln-accordion`](../ln-accordion/README.md)** — Single-open coordinator for toggle panels.
136
- - **[`ln-dropdown`](../ln-dropdown/README.md)** — Menu wrapper adding click-outside/top-layer promotion.
137
-
138
- ---
139
-
140
- ## 🔧 Internals
141
-
142
- Source: `js/ln-toggle/ln-toggle.js`. Imports `registerComponent`, `dispatch`, `dispatchCancelable` from `ln-core/helpers.js` and `persistGet`/`persistSet` from `ln-core/persist.js` — no other library component.
143
-
144
- ### Instance state
145
-
146
- | Field | Description |
147
- |---|---|
148
- | `dom` | The panel element |
149
- | `isOpen` | Cache of the attribute, kept current by `_syncAttribute`; used only to skip no-op transitions and as a read-only convenience getter |
150
-
151
- No cached trigger list, no saved previous-value for revert, no timer/queue — every attribute change runs the pipeline immediately. Triggers are re-queried document-wide on each state change; cheap because the matching set is typically 1-3 elements.
152
-
153
- ### Init
154
-
155
- `registerComponent` scans for `[data-ln-toggle]`, watches `data-ln-toggle`/`data-ln-toggle-for` mutations, and calls `_attachTriggers(document.body)` on init. `_component(dom)`:
156
- 1. **Persistence restore** — if `data-ln-persist` is present, `persistGet('toggle', dom)`; a saved value is applied via `setAttribute` *inside the constructor*. The observer does fire for that write, but `el[DOM_ATTRIBUTE]` is still `undefined` at that point (assigned only after the constructor returns), so `_syncAttribute`'s instance guard catches it and the pipeline does not run — no spurious `:before-open`/`:open` during restore.
157
- 2. Reads the (possibly restored) attribute into `isOpen`, adds `.open` if true, calls `_syncTriggerAria` for the initial ARIA state.
158
-
159
- No `:open`/`:close` event fires at init — the attribute is already in its final state; there's been no transition.
160
-
161
- ### Trigger attachment
162
-
163
- `_attachTriggers(root)` finds every `[data-ln-toggle-for]` under `root` (skipping ones already wired via a stashed-handler guard) and binds `click`: ignores modifier-key clicks, `preventDefault`s, resolves the target by ID, then writes `data-ln-toggle` — `"open"`/`"close"` for an explicit `data-ln-toggle-action`, or the inverse of the current value for the default toggle action. The handler reference is stashed on the trigger for `destroy` to remove later. Newly attached triggers also get their `aria-expanded` set immediately from the target's current state (no waiting for the next transition).
164
-
165
- ### `_syncAttribute` — the transition pipeline
166
-
167
- Runs only when the mutated element already has an `lnToggle` instance (a brand-new element getting the attribute takes the init/upgrade path instead, via `findElements`, not a transition). Order, for the "open" direction (close mirrors it):
168
-
169
- 1. `dispatchCancelable('ln-toggle:before-open')` — listeners see `isOpen` at its **pre-transition** value.
170
- 2. If canceled: revert the attribute to `'close'` and return. The revert re-fires the observer, but `isOpen` was never changed, so the second pass is a no-op — no loop, no duplicate event.
171
- 3. `isOpen = true`, `.open` class added.
172
- 4. `_syncTriggerAria(el, true)` — before the post-event, so `:open` listeners see settled ARIA.
173
- 5. `dispatch('ln-toggle:open')`.
174
- 6. If `data-ln-persist`: `persistSet('toggle', el, 'open')` — last, so a listener that removes `data-ln-persist` synchronously during `:open` skips the write.
175
-
176
- This ordering (cancelable → class → aria → event → persist) is the same shape for every transition in the library that follows the attribute-as-contract pattern.
177
-
178
- ### `_syncTriggerAria`
179
-
180
- Document-scoped `querySelectorAll('[data-ln-toggle-for="<id>"]')`, sets `aria-expanded` on each match — document-scoped because triggers can live anywhere relative to their panel. Drives both screen-reader state and the CSS chevron rotation rule in `scss/components/_toggle.scss`. Called from init, every transition, and `_attachTriggers` (so late-added triggers inherit current state).
181
-
182
- ### Persistence
183
-
184
- Key: `ln:toggle:{pagePath}:{id}` (`_resolveKey` in `ln-core/persist.js`); path-scoped and non-overrideable, so the same panel `id` on two routes stores independently. `persistGet`/`persistSet` wrap `localStorage` in `try/catch` and swallow failures silently (private browsing, quota, disabled storage) — toggle keeps working without persistence, no error surfaces.
185
-
186
- ### Destroy
187
-
188
- Guards double-destroy, dispatches `ln-toggle:destroyed`, removes the click listener from every matching trigger (using the stashed handler ref), deletes the instance. Does **not** remove `data-ln-toggle` or the `.open` class — only the JS coupling is severed; the consumer removes markup state separately if a full teardown is wanted.
189
-
190
- ### What it deliberately does not do
191
-
192
- No keyboard handling (ESC, Space/Enter on the panel), no outside-click, no focus management, no resize listener. Each is a separate concern owned by a wrapping component: `ln-dropdown` (outside-click/resize/teleport), `ln-modal` (focus trap), or project code.
193
-
194
- ### Cross-component contract
195
-
196
- `ln-accordion` and `ln-dropdown` both reach in only through the public contract — listening for `ln-toggle:open`/`:close`, and writing `data-ln-toggle="..."` directly (never an instance method). `ln-accordion` writes `'close'` on siblings from its wrapper listener; `ln-dropdown` listens on its inner toggle and teleports/wires outside-click on `:open`, reverses on `:close`. Because the attribute is the only mutation path, cancelable events and persistence fire identically regardless of which caller changed the state — there is no private mutator to bypass the pipeline.
1
+ # ln-toggle
2
+
3
+ > Applied to any collapsible panel element via `data-ln-toggle`, with triggers bound from anywhere in the DOM via `data-ln-toggle-for="id"`.
4
+ > It listens for trigger clicks and reacts to direct attribute mutations to toggle binary `open`/`close` state. The `data-ln-toggle`
5
+ > attribute is the single source of truth — automatically keeping `aria-expanded` and the `.open` CSS class in sync with zero JS height calculations or shadow state.
6
+
7
+ ---
8
+
9
+ ## 1. Philosophy & The Primitive Mindset
10
+
11
+ In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating heavy components that mix state, visual presentation, and layout, we separate them into isolated concerns:
12
+
13
+ 1. **The State Machine (JavaScript)**: The `ln-toggle` component (145 lines) only manages binary `open` / `close` state in the DOM and synchronizes ARIA accessibility. It does not own animations or visual geometries.
14
+ 2. **The Visual Presentation (CSS)**: Visual transitions are handled in Vanilla CSS. The component synchronizes the `.open` class and `[data-ln-toggle="open"]` on the panel. CSS reads these selectors and runs transitions (e.g. height collapse or sliding drawers).
15
+ 3. **Decoupled Binding (HTML)**: Triggers and panels are matched purely by ID. They can live anywhere in the DOM. Multiple triggers pointing to a single panel are supported natively, and all triggers stay perfectly synchronized.
16
+
17
+ ---
18
+
19
+ ## 2. Minimal Blueprint
20
+
21
+ Triggers and panels are bound via ID. A panel must have a unique `id` and the `data-ln-toggle` attribute.
22
+
23
+ ```html
24
+ <!-- Trigger anywhere -->
25
+ <button data-ln-toggle-for="example-panel">Toggle Options</button>
26
+
27
+ <!-- Panel anywhere -->
28
+ <section id="example-panel" data-ln-toggle class="collapsible">
29
+ <article class="collapsible-body">
30
+ <p>This is smooth collapsible content.</p>
31
+ </article>
32
+ </section>
33
+ ```
34
+
35
+ ### Key Anatomy Rules
36
+ - **The Panel (`data-ln-toggle`)**: The value `open` represents open; anything else (empty or `close`) represents closed.
37
+ - **The Trigger (`data-ln-toggle-for="id"`)**: Automatically intercepts clicks to toggle the panel.
38
+ - **The Close Trigger (`data-ln-toggle-action="close"`)**: Forces the trigger to only close the panel (e.g. an "X" button inside a sidebar drawer).
39
+ - **The Body (`.collapsible-body`)**: Holds all padding and margins. The parent `.collapsible` container must have zero padding so it can transition to exactly `0px` height cleanly.
40
+
41
+ ---
42
+
43
+ ## 3. The Declarative API & State Contract
44
+
45
+ There are no imperative JavaScript methods (like `open()` or `close()`) on the component instance. **The HTML attribute is the sole contract.**
46
+
47
+ Triggers, sibling components, external scripts, and manual DevTools edits all change state by writing the attribute:
48
+
49
+ ```js
50
+ const panel = document.getElementById('example-panel');
51
+
52
+ // Open the panel
53
+ panel.setAttribute('data-ln-toggle', 'open');
54
+
55
+ // Close the panel
56
+ panel.setAttribute('data-ln-toggle', 'close');
57
+
58
+ // Read-only state query (committed state tracker)
59
+ panel.lnToggle.isOpen; // Returns true/false
60
+ ```
61
+
62
+ ### Attributes
63
+ - `data-ln-toggle`: Placed on the panel to create the toggle instance.
64
+ - `data-ln-toggle-for`: Placed on triggers referencing the panel ID.
65
+ - `data-ln-toggle-action="open|close"`: Forces a trigger button to only open or only close the target.
66
+ - `data-ln-persist`: Saves the panel state individually in `localStorage`.
67
+ - storage key: `ln:{id}:data-ln-toggle` (global by default).
68
+ - `data-ln-persist-scope="page"`: Opt-in, independent attribute. Scopes the storage key to the current pathname: `ln:{id}:{pagePath}:data-ln-toggle`. Same IDs on different pages then store separately.
69
+
70
+ ---
71
+
72
+ ## 4. Transition Events
73
+
74
+ All events bubble. `event.detail.target` is always the panel element.
75
+
76
+ | Event | Cancelable | Dispatched When |
77
+ |---|---|---|
78
+ | **`ln-toggle:before-open`** | **Yes** | After attribute flips to `"open"`, before transition starts. Calling `event.preventDefault()` cancels the transition and reverts the attribute. |
79
+ | **`ln-toggle:open`** | No | After panel is fully open, classes added, and ARIA synced. |
80
+ | **`ln-toggle:before-close`** | **Yes** | After attribute flips to `"close"`, before transition starts. Calling `event.preventDefault()` cancels the close and reverts the attribute. |
81
+ | **`ln-toggle:close`** | No | After panel is fully closed, classes removed, and ARIA synced. |
82
+
83
+ ```js
84
+ // Example: Cancel open transition for unauthorized users
85
+ document.addEventListener('ln-toggle:before-open', (e) => {
86
+ if (e.detail.target.id === 'secure-panel' && !currentUser.isAdmin) {
87
+ e.preventDefault(); // Reverts attribute back to "close"
88
+ }
89
+ });
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 5. Integration Patterns
95
+
96
+ ### A. Sidebar Drawer
97
+ Combine the panel with the library's `@mixin sidebar-drawer` and add a close button inside the sidebar:
98
+ ```html
99
+ <aside id="menu" data-ln-toggle data-ln-persist class="sidebar">
100
+ <button data-ln-toggle-for="menu" data-ln-toggle-action="close">×</button>
101
+ </aside>
102
+ ```
103
+
104
+ ### B. Smooth Height Collapsible
105
+ Combine the panel with the library's `@mixin collapsible` and `.collapsible-body` wrapper to animate height cleanly:
106
+ ```html
107
+ <section id="panel" data-ln-toggle class="collapsible">
108
+ <div class="collapsible-body">Content goes here...</div>
109
+ </section>
110
+ ```
111
+
112
+ ### C. Dismissible Alert
113
+ Combine the alert card with `data-ln-persist` so that once the user closes the alert, it stays closed across page reloads:
114
+ ```html
115
+ <div class="alert" id="promo-banner" data-ln-toggle="open" data-ln-persist>
116
+ <p>Promo code active!</p>
117
+ <button data-ln-toggle-for="promo-banner" data-ln-toggle-action="close">×</button>
118
+ </div>
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 6. Integration & Source Files
124
+
125
+ - **Unified Bundle**: Loaded automatically with the main bundle:
126
+ ```html
127
+ <script src="dist/ln-ashlar.iife.js" defer></script>
128
+ ```
129
+ - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
130
+ ```html
131
+ <script src="components/ln-toggle/ln-toggle.js" defer></script>
132
+ ```
133
+ - **Active Source (ESM)**: Development source is located at [components/ln-toggle/src/ln-toggle.js](file:///c:/laragon/www/ln-ashlar/components/ln-toggle/src/ln-toggle.js).
134
+
135
+ ---
136
+
137
+ ## Related
138
+ - **[`ln-accordion`](../ln-accordion/README.md)** — Single-open coordinator for toggle panels.
139
+ - **[`ln-dropdown`](../ln-dropdown/README.md)** — Menu wrapper adding click-outside/top-layer promotion.
140
+
141
+ ---
142
+
143
+ ## 🔧 Internals
144
+
145
+ Source: `components/ln-toggle/ln-toggle.js`. Imports `registerComponent`, `dispatch`, `dispatchCancelable` from `ln-core` — no other library component. Persistence is declared, not imported: `registerComponent(...)` is passed `persist: { attr: DOM_SELECTOR, hashActive: null }`, and `ln-persist` (a separate component) does the restoring/saving from outside.
146
+
147
+ ### Instance state
148
+
149
+ | Field | Description |
150
+ |---|---|
151
+ | `dom` | The panel element |
152
+ | `isOpen` | Cache of the attribute, kept current by `_syncAttribute`; used only to skip no-op transitions and as a read-only convenience getter |
153
+
154
+ No cached trigger list, no saved previous-value for revert, no timer/queue — every attribute change runs the pipeline immediately. Triggers are re-queried document-wide on each state change; cheap because the matching set is typically 1-3 elements.
155
+
156
+ ### Init
157
+
158
+ `registerComponent` scans for `[data-ln-toggle]`, watches `data-ln-toggle` mutations, and instantiates panels. Triggers are handled document-wide by a single delegated click listener. `_component(dom)`:
159
+ 1. **Persistence restore already happened** — if `data-ln-persist` is present, `ln-persist`'s sink already wrote the saved value onto `data-ln-toggle` via `setAttribute`, synchronously, *before* this constructor runs (inside `findElements`, immediately before `el[attribute] = new ComponentClass(el)`). The mutation-delivery microtask for that write fires *after* the constructor has already set `el.lnToggle` and read `isOpen` from the (already-restored) attribute, so `_syncAttribute`'s `shouldBeOpen === instance.isOpen` guard finds them equal and no-ops — no spurious `:before-open`/`:open` during restore.
160
+ 2. Reads the (possibly restored) attribute into `isOpen`, adds `.open` if true, calls `_syncTriggerAria` for the initial ARIA state.
161
+
162
+ No `:open`/`:close` event fires at init — the attribute is already in its final state; there's been no transition.
163
+
164
+ ### Delegated trigger handling
165
+
166
+ A module-level `instances` Set tracks all initialized toggle panels. A single delegated `document` click listener (`_ensureClickListener`) intercepts clicks on `[data-ln-toggle-for]`: ignores modifier-key clicks, ignores disabled targets (`isTargetDisabled`), calls `preventDefault()`, resolves the target panel by ID, and writes `data-ln-toggle` — `"open"`/`"close"` for an explicit `data-ln-toggle-action`, or the inverse of the current value for the default toggle action. There are no per-trigger event listeners or stashed handlers.
167
+
168
+ ### `_syncAttribute` — the transition pipeline
169
+
170
+ Runs only when the mutated element already has an `lnToggle` instance (a brand-new element getting the attribute takes the init/upgrade path instead, via `findElements`, not a transition). Order, for the "open" direction (close mirrors it):
171
+
172
+ 1. `dispatchCancelable('ln-toggle:before-open')` — listeners see `isOpen` at its **pre-transition** value.
173
+ 2. If canceled: revert the attribute to `'close'` and return. The revert re-fires the observer, but `isOpen` was never changed, so the second pass is a no-op — no loop, no duplicate event.
174
+ 3. `isOpen = true`, `.open` class added.
175
+ 4. `_syncTriggerAria(el, true)` — before the post-event, so `:open` listeners see settled ARIA.
176
+ 5. `dispatch('ln-toggle:open')`.
177
+
178
+ Persistence is not a step in this pipeline — `ln-persist`'s save handler is wired separately via `observeAttributes([DOM_SELECTOR], ...)` and reacts to the `data-ln-toggle` mutation on its own, after this function returns.
179
+
180
+ ### `_syncTriggerAria`
181
+
182
+ Document-scoped `querySelectorAll('[data-ln-toggle-for="<id>"]')`, sets `aria-expanded` on each match — document-scoped because triggers can live anywhere relative to their panel. Drives both screen-reader state and the CSS chevron rotation rule in `theme/components/_toggle.scss`. Called from init and every transition.
183
+
184
+ ### Persistence
185
+
186
+ Key: `ln:{id}:data-ln-toggle` (global by default), or `ln:{id}:{pagePath}:data-ln-toggle` when `data-ln-persist-scope="page"` is present, so the same panel `id` on two routes can be made to store independently (`_resolveKey` in `ln-persist/src/ln-persist.js`). The sink wraps `localStorage` in `try/catch` and swallows failures silently (private browsing, quota, disabled storage) — toggle keeps working without persistence, no error surfaces.
187
+
188
+ ### Destroy
189
+
190
+ Guards double-destroy, removes request listeners (`ln-toggle:request-open/close/toggle`), removes the instance from the module-level `instances` Set, unbinds the delegated `document` click listener if no instances remain (`_maybeRemoveClickListener`), and deletes `el.lnToggle`. Does **not** remove `data-ln-toggle` or the `.open` class — only the JS coupling is severed; the consumer removes markup state separately if a full teardown is wanted.
191
+
192
+ ### What it deliberately does not do
193
+
194
+ No keyboard handling (ESC, Space/Enter on the panel), no outside-click, no focus management, no resize listener. Each is a separate concern owned by a wrapping component: `ln-dropdown` (outside-click/resize), `ln-modal` (focus trap), or project code.
195
+
196
+ ### Cross-component contract
197
+
198
+ `ln-accordion` and `ln-dropdown` both reach in only through the public contract — listening for `ln-toggle:open`/`:close`, and writing `data-ln-toggle="..."` directly (never an instance method). `ln-accordion` writes `'close'` on siblings from its wrapper listener; `ln-dropdown` listens on its inner toggle and wires outside-click on `:open`, reverses on `:close`. Because the attribute is the only mutation path, cancelable events and persistence fire identically regardless of which caller changed the state — there is no private mutator to bypass the pipeline.
@@ -1,13 +1,13 @@
1
- @use '../../scss/config/mixins' as *;
2
-
3
- [data-ln-debug] {
4
- // 1. Toggle Panel Missing ID: panels controlled by data-ln-toggle must have an id for triggering/ARIA linking
5
- [data-ln-toggle]:not([id]) {
6
- @include dev-dom-error("[data-ln-toggle] is missing a required 'id' attribute for trigger linking.");
7
- }
8
-
9
- // 2. Toggle Trigger Empty ID: trigger elements must point to a target panel ID
10
- [data-ln-toggle-for=""] {
11
- @include dev-inline-error("data-ln-toggle-for target ID cannot be empty");
12
- }
13
- }
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Toggle Panel Missing ID: panels controlled by data-ln-toggle must have an id for triggering/ARIA linking
5
+ [data-ln-toggle]:not([id]) {
6
+ @include dev-dom-error("[data-ln-toggle] is missing a required 'id' attribute for trigger linking.");
7
+ }
8
+
9
+ // 2. Toggle Trigger Empty ID: trigger elements must point to a target panel ID
10
+ [data-ln-toggle-for=""] {
11
+ @include dev-inline-error("data-ln-toggle-for target ID cannot be empty");
12
+ }
13
+ }
@@ -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 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 o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!A(o))return}e.apply(console,t)}}function _(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 R(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o);const l=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:o});return e.dispatchEvent(l),l}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()}function H(e){return!!(!e||e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||typeof e.button=="number"&&e.button!==0)}function K(e){return!!(!e||e.disabled||typeof e.getAttribute=="function"&&e.getAttribute("aria-disabled")==="true"||typeof e.closest=="function"&&e.closest("[inert]"))}const I={};function U(e,t){I[e]=t}function Q(e){return I[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=U,window.lnCore.getDataMapper=Q);function G(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,y(function(){new MutationObserver(function(){_(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const D={};function P(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];D[n]=t}function J(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return D[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=P,window.lnCore.getLocaleFallback=J,window.lnCore.ensureLocaleObserver=G);const v={};function z(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 $(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let r=0;r<n.length;r++){const u=n[r],s=u.getAttribute("data-ln-field");t[s]!=null&&(u.textContent=t[s])}const o=e.querySelectorAll("[data-ln-attr]");for(let r=0;r<o.length;r++){const u=o[r],s=u.getAttribute("data-ln-attr").split(",");for(let d=0;d<s.length;d++){const f=s[d].trim().split(":");if(f.length!==2)continue;const a=f[0].trim(),b=f[1].trim();t[b]!=null&&u.setAttribute(a,t[b])}}const l=e.querySelectorAll("[data-ln-show]");for(let r=0;r<l.length;r++){const u=l[r],s=u.getAttribute("data-ln-show");s in t&&u.classList.toggle("hidden",!t[s])}const i=e.querySelectorAll("[data-ln-class]");for(let r=0;r<i.length;r++){const u=i[r],s=u.getAttribute("data-ln-class").split(",");for(let d=0;d<s.length;d++){const f=s[d].trim().split(":");if(f.length!==2)continue;const a=f[0].trim(),b=f[1].trim();b in t&&u.classList.toggle(a,!!t[b])}}return e}function X(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)$(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 B(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const i=n.currentNode;i.textContent.indexOf("{{")!==-1&&(i.textContent=i.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(r,u){return t[u]!==void 0?t[u]:""}))}const o=function(i,r){return t[r]!==void 0?t[r]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let i=0;i<l.length;i++){const r=l[i],u=r.attributes;for(let s=0;s<u.length;s++){const d=u[s];d.value.indexOf("{{")!==-1&&r.setAttribute(d.name,d.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function Y(e,t,n,o,l,i){const r={};for(let s=0;s<e.children.length;s++){const d=e.children[s],f=d.getAttribute("data-ln-render-key");f&&(r[f]=d)}const u=document.createDocumentFragment();for(let s=0;s<t.length;s++){const d=t[s],f=String(o(d));let a=r[f];if(a)l(a,d,s);else{const b=z(n,i);if(!b||(B(b,d),a=b.firstElementChild,!a))continue;a.setAttribute("data-ln-render-key",f),l(a,d,s)}u.appendChild(a)}e.textContent="",e.appendChild(u)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=B,window.lnCore.fill=$,window.lnCore.lnFill=X,window.lnCore.renderList=Y);function F(e,t,n,o,l){if(!e||t===null)return!0;const i=o||"ln-component",r=e.type;if(r==="trigger"||r==="marker"||r==="string"||r==="list"||!r||t===""&&e.fallback!==void 0)return!0;const u=s=>{l?console.warn(s,l):console.warn(s)};if(r==="boolean"){const s=t.trim().toLowerCase();return s!==""&&s!=="true"&&s!=="false"&&s!=="1"&&s!=="0"?(u(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(r==="enum"){const s=e.values||[];return s.includes(t)?!0:(u(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${s.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(r==="integer"){if(!/^-?\d+$/.test(t))return u(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const s=parseInt(t,10);return e.min!==void 0&&s<e.min?(u(`[${i}] Value ${s} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&s>e.max?(u(`[${i}] Value ${s} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(r==="float")return isNaN(Number(t))?(u(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(r==="json")try{return JSON.parse(t),!0}catch(s){return u(`[${i}] Invalid JSON for attribute "${n}": ${s.message}. Fallback: ${e.fallback}.`),!1}return!0}function Z(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 ee(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,o){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",r=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&r.push(e);for(const u of r)if(!u[n]){window.lnCore._persistSink&&u.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(u,t);try{u[n]=new o(u)}catch(s){console.error("["+n+"] init failed",u,s)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function te(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function ne(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 M(){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 oe(e){const t=M(),n=e.observed||[];for(let o=0;o<n.length;o++){const l=n[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(i=>i.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=ee(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function W(e,t,n,o){for(let l=0;l<e.length;l++){const i=e[l];if(!t[i.attribute])continue;const r=i.effects&&i.effects[n];r?r(t,n,o):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,o)}}function re(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=M(),l=o.byAttr.get(n);if(x()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&A(t)){const i=o.byDeclaredAttr.get(n),r=t.getAttribute(n);for(let u=0;u<i.length;u++)F(i[u].spec,r,n,i[u].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||l)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=o.byReactive.get(n);i&&W(i,t,n,e.oldValue),o.reactiveWildcard.length&&W(o.reactiveWildcard,t,n,e.oldValue)}if(l)for(let i=0;i<l.length;i++){const r=l[i];if(r.handler){r.handler(t,n,e.oldValue);continue}r.onAttributeChange&&t[r.attribute]?r.onAttributeChange(t,n):(k(t,r.selector,r.attribute,r.ComponentFn),r.onInit&&r.onInit(t))}}function ie(){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{re(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 N(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function le(e){const t=N();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const o=t[n];if(o.onSubtreeChange){const l=o.query,i=e.target.nodeType===1?e.target.matches(l)?e.target:e.target.closest(l):e.target.parentElement?e.target.parentElement.closest(l):null;i&&o.onSubtreeChange(i,e)}}for(let n=0;n<e.addedNodes.length;n++){const o=e.addedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const i=t[l];k(o,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(o)}}for(let n=0;n<e.removedNodes.length;n++){const o=e.removedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const i=t[l],r=i.query,u=Array.from(o.querySelectorAll(r));o.matches&&o.matches(r)&&u.push(o);for(let s=0;s<u.length;s++){const d=u[s];if(!document.contains(d)){const f=d[i.attribute];if(f&&typeof f.destroy=="function")try{f.destroy()}catch(a){console.error("["+i.attribute+"] destroy failed",d,a)}delete d[i.attribute]}}}}}}function se(){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{le(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function ue(e,t,n,o,l={}){const i=l.extraAttributes||[],r=l.onAttributeChange||null,u=l.onSubtreeChange||null,s=l.onInit||null,d=l.onAttrChange||null,f=l.effects||null,a=l.attributes||null,b=a?Z(a):f,c=a?new Set(Object.keys(a)):null,g=l.persist||null;function h(L){const p=L||document.body;if(k(p,e,t,n),a&&x())for(const C in a){const pe=a[C],T=Array.from(p.querySelectorAll("["+C+"]"));p.matches&&p.matches("["+C+"]")&&T.push(p);for(let E=0;E<T.length;E++){const q=T[E];A(q)&&F(pe,q.getAttribute(C),C,o,q)}}s&&s(p)}const w=[];if(e.indexOf("[")!==-1){const L=/\[([\w-]+)/g;let p;for(;(p=L.exec(e))!==null;)w.push(p[1])}else w.push(e);oe({selector:e,attribute:t,componentTag:o,attributes:a,ComponentFn:n,onInit:s,observed:w.concat(i),onAttributeChange:r,onAttrChange:d,effects:b,declared:c,persist:g}),ie();const O=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";N().push({selector:e,attribute:t,ComponentFn:n,onInit:s,onSubtreeChange:u,query:O}),se(),window[t]=h;function V(){te()>0?ne(function(){h(document.body)}):h(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",V):V(),h}function j(e){return(e||"").replace(/^#/,"")}function S(e){const t=e===void 0?location.hash:e,n={},o=j(t);if(!o)return n;const l=o.split("&");for(let i=0;i<l.length;i++){const r=l[i];if(!r)continue;const u=r.indexOf(":"),s=u>-1?r.slice(0,u):r,d=u>-1?r.slice(u+1):"";if(s)try{n[s]=decodeURIComponent(d)}catch{n[s]=d}}return n}function ce(e){if(!e)return null;const t=S();return e in t?t[e]:null}function de(e,t){if(!e)return;const n=S();t==null?delete n[e]:n[e]=String(t);const l=Object.keys(n).map(function(i){const r=n[i];return r===""?i:i+":"+encodeURIComponent(r)}).join("&");j(location.hash)!==l&&(location.hash=l)}function ae(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function fe(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 ge(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function we(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 be(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function he(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=S,window.lnCore.hashGet=ce,window.lnCore.hashSet=de,window.lnCore.hashLinkClick=ae,window.lnCore.resolveHashNamespace=fe,window.lnCore.hashSortEncode=ge,window.lnCore.hashSortDecode=we,window.lnCore.hashFilterEncode=be,window.lnCore.hashFilterDecode=he),(function(){const e="data-ln-toggle",t="lnToggle",n="data-ln-toggle-for",o="data-ln-toggle-action";if(window[t]!==void 0)return;const l={"data-ln-toggle":{effect:b,type:"enum",values:["open","close"],fallback:"close",description:"Visibility state of toggleable element"},"data-ln-toggle-for":{type:"string",description:"Target element ID to toggle on trigger click"},"data-ln-toggle-action":{type:"enum",values:["open","close","toggle"],fallback:"toggle",description:"Action performed on target element when trigger is clicked"}},i=new Set;let r=null;function u(c,g){return g==="open"?"open":g==="close"||c==="open"?"close":"open"}function s(){r||(r=function(c){if(H(c))return;const g=c.target.closest("["+n+"]");if(!g||K(g))return;const h=g.getAttribute(n);if(!h)return;const w=document.getElementById(h);if(!w||!w[t])return;c.preventDefault();const m=g.getAttribute(o)||"toggle",O=w.getAttribute(e);w.setAttribute(e,u(O,m))},document.addEventListener("click",r))}function d(){i.size>0||!r||(document.removeEventListener("click",r),r=null)}function f(c,g){if(!c||!c.id)return;const h=document.querySelectorAll("["+n+'="'+c.id+'"]');for(let w=0;w<h.length;w++)h[w].setAttribute("aria-expanded",g?"true":"false")}function a(c){this.dom=c;const g=this;return this._onRequestOpen=function(){g.open()},this._onRequestClose=function(){g.close()},this._onRequestToggle=function(){g.toggle()},this.dom.addEventListener("ln-toggle:request-open",this._onRequestOpen),this.dom.addEventListener("ln-toggle:request-close",this._onRequestClose),this.dom.addEventListener("ln-toggle:request-toggle",this._onRequestToggle),this.isOpen=c.getAttribute(e)==="open",this.isOpen&&c.classList.add("open"),f(c,this.isOpen),i.add(this),s(),this}a.prototype.open=function(){this.dom.setAttribute(e,"open")},a.prototype.close=function(){this.dom.setAttribute(e,"close")},a.prototype.toggle=function(){const c=this.dom.getAttribute(e);this.dom.setAttribute(e,u(c,"toggle"))},a.prototype.destroy=function(){this.dom[t]&&(this.dom.removeEventListener("ln-toggle:request-open",this._onRequestOpen),this.dom.removeEventListener("ln-toggle:request-close",this._onRequestClose),this.dom.removeEventListener("ln-toggle:request-toggle",this._onRequestToggle),i.delete(this),delete this.dom[t],d())};function b(c){const g=c[t];if(!g)return;const w=c.getAttribute(e)==="open";if(w!==g.isOpen)if(w){if(R(c,"ln-toggle:before-open",{target:c}).defaultPrevented){c.setAttribute(e,"close");return}g.isOpen=!0,c.classList.add("open"),f(c,!0),_(c,"ln-toggle:open",{target:c})}else{if(R(c,"ln-toggle:before-close",{target:c}).defaultPrevented){c.setAttribute(e,"open");return}g.isOpen=!1,c.classList.remove("open"),f(c,!1),_(c,"ln-toggle:close",{target:c})}}ue(e,t,a,"ln-toggle",{attributes:l,persist:{attr:e,hashActive:null}})})()})();
@@ -0,0 +1,52 @@
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-toggle",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-toggle-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-toggle": {
13
+ "direction": "both",
14
+ "sources": [
15
+ "ln-toggle-dev.scss",
16
+ "ln-toggle.scss",
17
+ "src/ln-toggle.js"
18
+ ],
19
+ "type": "enum",
20
+ "values": [
21
+ "open",
22
+ "close"
23
+ ],
24
+ "fallback": "close",
25
+ "description": "Visibility state of toggleable element"
26
+ },
27
+ "data-ln-toggle-action": {
28
+ "direction": "author",
29
+ "sources": [
30
+ "src/ln-toggle.js"
31
+ ],
32
+ "type": "enum",
33
+ "values": [
34
+ "open",
35
+ "close",
36
+ "toggle"
37
+ ],
38
+ "fallback": "toggle",
39
+ "description": "Action performed on target element when trigger is clicked"
40
+ },
41
+ "data-ln-toggle-for": {
42
+ "direction": "author",
43
+ "sources": [
44
+ "ln-toggle-dev.scss",
45
+ "ln-toggle.scss",
46
+ "src/ln-toggle.js"
47
+ ],
48
+ "type": "string",
49
+ "description": "Target element ID to toggle on trigger click"
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,37 @@
1
+ // ==========================================================================
2
+ // ln-toggle — Functional Grid Collapse & Chevron Mechanics
3
+ // ==========================================================================
4
+
5
+ .collapsible {
6
+ display: grid;
7
+ grid-template-rows: 0fr;
8
+ @media (prefers-reduced-motion: no-preference) {
9
+ transition: grid-template-rows 200ms ease;
10
+ }
11
+
12
+ &.open,
13
+ &[data-ln-toggle="open"] {
14
+ grid-template-rows: 1fr;
15
+ }
16
+
17
+ > * {
18
+ overflow: hidden;
19
+ min-height: 0;
20
+ }
21
+ }
22
+
23
+ .collapsible-body {
24
+ overflow: hidden;
25
+ min-height: 0;
26
+ }
27
+
28
+ [data-ln-toggle-for] .ln-chevron {
29
+ flex-shrink: 0;
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ transition: transform 200ms ease;
32
+ }
33
+ }
34
+
35
+ [data-ln-toggle-for][aria-expanded="true"] .ln-chevron {
36
+ transform: rotate(180deg);
37
+ }