@livenetworks/ashlar 1.6.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +190 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -1,140 +1,150 @@
1
- # ln-dropdown
2
-
3
- > A menu-grade coordinator that adds click-outside, top-layer promotion, and automatic positioning on top of `ln-toggle`.
4
-
5
- ---
6
-
7
- ## 1. Philosophy & The Dropdown Mindset
8
-
9
- In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating a heavy component that bundles state, LIFO click stacks, top-layer contexts, and styles, `ln-dropdown` splits them cleanly:
10
-
11
- 1. **State Primitive (`ln-toggle`)**: Open/close state lives entirely on the inner `data-ln-toggle` attribute on the menu. `ln-dropdown` does not re-implement state; it is a thin behavior layer on top.
12
- 2. **Behavior & Positioning (JavaScript)**: The `ln-dropdown` coordinator handles click-outside detection, viewport resize closures (which makes absolute positioning unreliable), top-layer promotion via the native Popover API (`popover="manual"`) to escape parent `overflow: hidden` clips and stacking contexts, and scroll position tracking.
13
- 3. **Visual Presentation (CSS)**: Visual layouts, background shadows, and borders are handled in Vanilla CSS. The library ships mixins like `@include dropdown` and `@include dropdown-menu` to style the wrapper and popup elements.
14
-
15
- ---
16
-
17
- ## 2. Minimal Blueprint
18
-
19
- Triggers and dropdown menus are paired by ID inside a wrapper container. Inactive menus are hidden via `ln-toggle` default rules.
20
-
21
- ```html
22
- <!-- The Wrapper -->
23
- <div data-ln-dropdown>
24
- <!-- The Trigger -->
25
- <button type="button" data-ln-toggle-for="options-menu">Options</button>
26
-
27
- <!-- The Menu (State Primitive) -->
28
- <ul id="options-menu" data-ln-toggle>
29
- <li><a href="/profile">Profile</a></li>
30
- <li><a href="/settings">Settings</a></li>
31
- <li><hr></li>
32
- <li><a href="/logout">Log out</a></li>
33
- </ul>
34
- </div>
35
- ```
36
-
37
- ### Key Anatomy Rules
38
- - **The Wrapper (`data-ln-dropdown`)**: Creates the dropdown coordinator instance.
39
- - **The Trigger (`data-ln-toggle-for="id"`)**: Standard `ln-toggle` button. ARIA attributes `aria-haspopup="menu"` and `aria-expanded` are synced automatically.
40
- - **The Menu (`data-ln-toggle`)**: Standard `ln-toggle` element. Value `open` represents open; anything else is closed. Role `menu` is auto-injected.
41
-
42
- ---
43
-
44
- ## 3. Declarative API & State Contract
45
-
46
- There are no imperative JavaScript methods (like `open()` or `close()`) on the coordinator instance. **The HTML attribute is the sole contract.**
47
-
48
- Outside clicks, window resizes, triggers, and custom scripts all change state by writing the active attribute on the inner menu element:
49
-
50
- ```js
51
- const wrapper = document.querySelector('[data-ln-dropdown]');
52
- const menu = wrapper.querySelector('[data-ln-toggle]');
53
-
54
- // Open the menu (dropdown promotes it to the top layer and positions it automatically)
55
- menu.setAttribute('data-ln-toggle', 'open');
56
-
57
- // Close the menu
58
- menu.setAttribute('data-ln-toggle', 'close');
59
-
60
- // Cleanup the coordinator instance
61
- wrapper.lnDropdown.destroy();
62
- ```
63
-
64
- ### Attributes
65
- - `data-ln-dropdown`: Placed on the wrapper element to create the coordinator.
66
- - `data-ln-toggle-for="id"`: Placed on trigger referencing the menu ID.
67
- - `data-ln-toggle`: Placed on the menu element. Value `"open"` = open; anything else = closed.
68
-
69
- ---
70
-
71
- ## 4. Transition Events
72
-
73
- All events bubble. The dispatch target is the inner menu element (except `:destroyed` which dispatches on the wrapper).
74
-
75
- | Event | Bubbles | Detail | Dispatched When |
76
- |---|---|---|---|
77
- | **`ln-dropdown:open`** | Yes | `{ target: menuElement }` | After top-layer promotion and positioning are complete. |
78
- | **`ln-dropdown:close`** | Yes | `{ target: menuElement }` | After menu is closed, exits the top layer, and outside listeners removed. |
79
- | **`ln-dropdown:destroyed`** | Yes | `{ target: wrapperElement }` | Inside `destroy()`, after removing listeners. |
80
-
81
- *Note*: Open/close state is managed by `ln-toggle`. Use `ln-toggle:before-open` / `ln-toggle:before-close` to cancel transitions.
82
-
83
- ```js
84
- // Example: React to dropdown open
85
- document.addEventListener('ln-dropdown:open', (e) => {
86
- console.log('Active dropdown:', e.detail.target.id);
87
- });
88
- ```
89
-
90
- ---
91
-
92
- ## 5. Behavior & Integration
93
-
94
- - **Top-Layer Promotion**: On open, the menu is shown via the native Popover API (`showPopover()`), which renders it in the browser's top layer — escaping ancestor `overflow: hidden` clipping and stacking contexts without moving it in the DOM. `popover="manual"` keeps dismissal entirely under `ln-dropdown`'s own control (no native light-dismiss).
95
- - **Positioning**: The menu opens below the trigger, right-aligned to it. It automatically flips above if there is no vertical space below, and left-aligned if there is no horizontal space on the right.
96
- - **Scroll & Resize Tracking**: Repositions automatically on every scroll to track the trigger. A window viewport resize closes the menu to prevent layout misalignments.
97
-
98
- ---
99
-
100
- ## 6. Integration & Source Files
101
-
102
- - **Unified Bundle**: Loaded automatically with the main bundle:
103
- ```html
104
- <script src="dist/ln-ashlar.iife.js" defer></script>
105
- ```
106
- - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
107
- ```html
108
- <script src="js/ln-dropdown/ln-dropdown.js" defer></script>
109
- ```
110
- - **Active Source (ESM)**: Development source is located at [js/ln-dropdown/src/ln-dropdown.js](file:///c:/laragon/www/ln-ashlar/js/ln-dropdown/src/ln-dropdown.js).
111
-
112
- ---
113
-
114
- ## Related
115
- - **[`ln-toggle`](../ln-toggle/README.md)** — Binary disclosure state primitive.
116
- - **[`ln-popover`](../ln-popover/README.md)** — Viewport-aware click-triggered rich-content overlays.
117
-
118
- ---
119
-
120
- ## 🔧 Internals
121
-
122
- Source: `js/ln-dropdown/ln-dropdown.js`. Each `[data-ln-dropdown]` gets a `_component` instance at `element.lnDropdown` (`dom` the wrapper, `toggleEl` the menu, `triggerBtn`, plus the bound outside-click / scroll-reposition / resize-close handles, cleared on close and destroy).
123
-
124
- ### Dependency on ln-toggle
125
-
126
- Dropdown does not manage open/close state itself — it listens for `ln-toggle:open`/`ln-toggle:close` on the inner `[data-ln-toggle]` menu and runs top-layer/positioning/listener-lifecycle steps in response. This is the library's **Presentation Coordinator** pattern layered on a state primitive; see [Coordinator Doctrine Reference](../../docs/architecture/coordinator.md).
127
-
128
- ### Top-layer + placement
129
-
130
- The menu carries `popover="manual"` (set at construction). On `ln-toggle:open`: `showPopover()` promotes it to the browser's top layer — escaping ancestor `overflow: hidden` clips and stacking contexts without moving it in the DOM — then `_reposition()` measures and places it. On `ln-toggle:close`: the inline `top`/`left` are cleared and `hidePopover()` retracts it (guarded by `:popover-open`, since a menu opened at boot via persisted/static state was never shown through `showPopover()`).
131
-
132
- `_reposition()` reads the trigger's bounding rect, measures the menu via `measureHidden` (ln-core's hidden-safe dimension read), and calls `computePlacement(rect, size, 'bottom-end', gap)` — `gap` from the `--size-xs` token. `'bottom-end'` means below the trigger, right-aligned; `computePlacement` flips to `'top-end'` if there's no room below (alignment preserved through the flip) and clamps to the viewport.
133
-
134
- ### Listener lifecycle
135
-
136
- `ln-toggle:open`/`ln-toggle:close` are bound at construction and removed at `destroy()`. On open only: an outside-click listener on `document` (deferred one tick via `setTimeout(0)` so the opening click itself doesn't immediately close the menu), a `scroll` listener on `window` (reposition), and a `resize` listener on `window` (close — resize makes fixed-position coordinates stale). All three are removed on close.
137
-
138
- ### MutationObserver
139
-
140
- One global observer on `document.body`: `childList` (subtree) auto-initializes new `[data-ln-dropdown]` wrappers; `attributes` on `data-ln-dropdown` initializes on attribute addition.
1
+ # ln-dropdown
2
+
3
+ > Applied to a `<div data-ln-dropdown>` wrapper enclosing a trigger button and a `<ul data-ln-toggle>` menu.
4
+ > When the menu emits `ln-toggle:open`, it sets `popover="manual"` on the menu, calls `showPopover()`, and calculates inline `top`/`left` coordinates.
5
+ > On outside `click` or `Escape` key, it sets `setAttribute('data-ln-toggle', 'close')` on the menu.
6
+
7
+ ---
8
+
9
+ ## 1. Philosophy & The Dropdown Mindset
10
+
11
+ In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating a heavy component that bundles state, LIFO click stacks, top-layer contexts, and styles, `ln-dropdown` splits them cleanly:
12
+
13
+ 1. **State Primitive (`ln-toggle`)**: Open/close state lives entirely on the inner `data-ln-toggle` attribute on the menu. `ln-dropdown` does not re-implement state; it is a thin behavior layer on top.
14
+ 2. **Behavior & Positioning (JavaScript)**: The `ln-dropdown` coordinator handles click-outside detection, viewport resize closures (which makes absolute positioning unreliable), top-layer promotion via the native Popover API (`popover="manual"`) to escape parent `overflow: hidden` clips and stacking contexts, and scroll position tracking.
15
+ 3. **Visual Presentation (CSS)**: Visual layouts, background shadows, and borders are handled in Vanilla CSS. The library ships mixins like `@include dropdown` and `@include dropdown-menu` to style the wrapper and popup elements.
16
+
17
+ ---
18
+
19
+ ## 2. Minimal Blueprint
20
+
21
+ Triggers and dropdown menus are paired by ID inside a wrapper container. Inactive menus are hidden via `ln-toggle` default rules.
22
+
23
+ ```html
24
+ <!-- The Wrapper -->
25
+ <div data-ln-dropdown>
26
+ <!-- The Trigger -->
27
+ <button type="button" data-ln-toggle-for="options-menu">Options</button>
28
+
29
+ <!-- The Menu (State Primitive) -->
30
+ <ul id="options-menu" data-ln-toggle>
31
+ <li><a href="/profile">Profile</a></li>
32
+ <li><a href="/settings">Settings</a></li>
33
+ <li><hr></li>
34
+ <li><a href="/logout">Log out</a></li>
35
+ </ul>
36
+ </div>
37
+ ```
38
+
39
+ ### Key Anatomy Rules
40
+ - **The Wrapper (`data-ln-dropdown`)**: Creates the dropdown coordinator instance.
41
+ - **The Trigger (`data-ln-toggle-for="id"`)**: Standard `ln-toggle` button. ARIA attributes `aria-haspopup="menu"` and `aria-expanded` are synced automatically.
42
+ - **The Menu (`data-ln-toggle`)**: Standard `ln-toggle` element. Value `open` represents open; anything else is closed. Role `menu` is auto-injected on `<ul>`, `role="none"` on `<li>`, and `role="menuitem"` with roving `tabindex` on interactive child buttons/anchors.
43
+
44
+ ---
45
+
46
+ ## 3. Declarative API & State Contract
47
+
48
+ There are no imperative JavaScript methods (like `open()` or `close()`) on the coordinator instance. **The HTML attribute is the sole contract.**
49
+
50
+ Outside clicks, window resizes, triggers, and custom scripts all change state by writing the active attribute on the inner menu element:
51
+
52
+ ```js
53
+ const wrapper = document.querySelector('[data-ln-dropdown]');
54
+ const menu = wrapper.querySelector('[data-ln-toggle]');
55
+
56
+ // Open the menu (dropdown promotes it to the top layer and positions it automatically)
57
+ menu.setAttribute('data-ln-toggle', 'open');
58
+
59
+ // Close the menu
60
+ menu.setAttribute('data-ln-toggle', 'close');
61
+
62
+ // Cleanup the coordinator instance
63
+ wrapper.lnDropdown.destroy();
64
+ ```
65
+
66
+ ### Attributes
67
+ - `data-ln-dropdown`: Placed on the wrapper element to create the coordinator.
68
+ - `data-ln-dropdown-position`: Placed on the wrapper element to configure target placement (e.g. `"bottom-start"`, `"bottom-end"`, default: `"bottom-end"`).
69
+ - `data-ln-dropdown-placement`: Dynamically set on the menu element by JS with the computed winning placement.
70
+ - `data-ln-toggle-for="id"`: Placed on trigger referencing the menu ID.
71
+ - `data-ln-toggle`: Placed on the menu element. Value `"open"` = open; anything else = closed.
72
+
73
+ ---
74
+
75
+ ## 4. Transition Events
76
+
77
+ All events bubble. The dispatch target is the inner menu element.
78
+
79
+ | Event | Bubbles | Detail | Dispatched When |
80
+ |---|---|---|---|
81
+ | **`ln-dropdown:open`** | Yes | `{ target: menuElement }` | After top-layer promotion and positioning are complete. |
82
+ | **`ln-dropdown:close`** | Yes | `{ target: menuElement }` | After menu is closed, exits the top layer, and outside listeners removed. |
83
+
84
+ *Note*: Open/close state is managed by `ln-toggle`. Use `ln-toggle:before-open` / `ln-toggle:before-close` to cancel transitions.
85
+
86
+ ```js
87
+ // Example: React to dropdown open
88
+ document.addEventListener('ln-dropdown:open', (e) => {
89
+ console.log('Active dropdown:', e.detail.target.id);
90
+ });
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 5. Behavior, Positioning & Keyboard Navigation
96
+
97
+ - **Top-Layer Promotion**: On open, the menu is shown via the native Popover API (`showPopover()`), which renders it in the browser's top layer — escaping ancestor `overflow: hidden` clipping and stacking contexts without moving it in the DOM. `popover="manual"` keeps dismissal entirely under `ln-dropdown`'s own control (no native light-dismiss).
98
+ - **Positioning**: Reads `data-ln-dropdown-position` (default: `"bottom-end"`). It opens aligned to the trigger, automatically flips if space is constrained, and reflects the winning placement in `data-ln-dropdown-placement`.
99
+ - **Scroll & Resize Tracking**: Repositions automatically on every scroll to track the trigger. A window viewport resize closes the menu to prevent layout misalignments.
100
+ - **ARIA Semantics & Structure**: `role="menu"` is set on the `<ul>`, `role="none"` on each `<li>` to strip list semantics, and `role="menuitem"` with roving `tabindex` (`0` on active/first, `-1` on siblings) on inner `<button>` and `<a href>` elements. All of this is injected and managed dynamically by JS — authors do not need to annotate children manually.
101
+ - **ARIA APG Keyboard Navigation**:
102
+ - `ArrowDown` / `ArrowUp` on the trigger opens the menu and focuses the first/last menu item.
103
+ - Inside the menu, `ArrowDown` and `ArrowUp` rove focus cyclically across interactive items (`<button>`, `<a href>`).
104
+ - `Home` / `End` immediately jump to the first / last menu item.
105
+ - `Escape` closes the menu (`data-ln-toggle="close"`), stops propagation, and returns focus to the trigger button.
106
+ - `Tab` returns focus to the trigger button and closes the menu, allowing the native Tab flow to advance to the next page element seamlessly.
107
+
108
+ ---
109
+
110
+ ## 6. Integration & Source Files
111
+
112
+ - **Unified Bundle**: Loaded automatically with the main bundle:
113
+ ```html
114
+ <script src="dist/ln-ashlar.iife.js" defer></script>
115
+ ```
116
+ - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
117
+ ```html
118
+ <script src="components/ln-dropdown/ln-dropdown.js" defer></script>
119
+ ```
120
+ - **Active Source (ESM)**: Development source is located at [components/ln-dropdown/src/ln-dropdown.js](file:///c:/laragon/www/ln-ashlar/components/ln-dropdown/src/ln-dropdown.js).
121
+
122
+ ---
123
+
124
+ ## Related
125
+ - **[`ln-toggle`](../ln-toggle/README.md)** — Binary disclosure state primitive.
126
+ - **[`ln-popover`](../ln-popover/README.md)** — Viewport-aware click-triggered rich-content overlays.
127
+
128
+ ---
129
+
130
+ ## 🔧 Internals
131
+
132
+ Source: `components/ln-dropdown/ln-dropdown.js`. Each `[data-ln-dropdown]` gets a `_component` instance at `element.lnDropdown` (`dom` the wrapper, `toggleEl` the menu, `triggerBtn`, plus the bound outside-click / scroll-reposition / resize-close / keydown handles, cleared on close and destroy).
133
+
134
+ ### Dependency on ln-toggle
135
+
136
+ Dropdown does not manage open/close state itself — it listens for `ln-toggle:open`/`ln-toggle:close` on the inner `[data-ln-toggle]` menu and runs top-layer/positioning/listener-lifecycle steps in response. This is the library's **Presentation Coordinator** pattern layered on a state primitive; see [Coordinator Doctrine Reference](../../docs/architecture/coordinator.md).
137
+
138
+ ### Top-layer + placement
139
+
140
+ The menu carries `popover="manual"` (set at construction). On `ln-toggle:open`: `showPopover()` promotes it to the browser's top layer — escaping ancestor `overflow: hidden` clips and stacking contexts without moving it in the DOM — then `_reposition()` measures and places it. On `ln-toggle:close`: the inline `top`/`left` and `data-ln-dropdown-placement` are cleared and `hidePopover()` retracts it (guarded by `:popover-open`, since a menu opened at boot via persisted/static state was never shown through `showPopover()`).
141
+
142
+ `_reposition()` reads the trigger's bounding rect, measures the menu via `measureHidden` (ln-core's hidden-safe dimension read), and calls `computePlacement(rect, size, position, gap)` — `gap` from the `--size-xs` token, `position` from `data-ln-dropdown-position` (default `'bottom-end'`). `computePlacement` flips if there's no room (alignment preserved through the flip) and clamps to the viewport.
143
+
144
+ ### Listener lifecycle
145
+
146
+ `ln-toggle:open`/`ln-toggle:close` and `keydown` are bound at construction and removed at `destroy()`. On open only: an outside-click listener on `document` (deferred one tick via `setTimeout(0)` so the opening click itself doesn't immediately close the menu), a `scroll` listener on `window` (reposition), and a `resize` listener on `window` (close — resize makes fixed-position coordinates stale). All three are removed on close.
147
+
148
+ ### MutationObserver
149
+
150
+ One global observer on `document.body`: `childList` (subtree) auto-initializes new `[data-ln-dropdown]` wrappers; `attributes` on `data-ln-dropdown` initializes on attribute addition.
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Dropdown Menu Check: dropdown coordinator must wrap a menu [data-ln-toggle]
5
+ [data-ln-dropdown]:not(:has([data-ln-toggle])) {
6
+ @include dev-dom-error("[data-ln-dropdown] is missing a required child menu [data-ln-toggle].");
7
+ }
8
+
9
+ // 2. Dropdown Trigger Check: dropdown coordinator must wrap a trigger [data-ln-toggle-for]
10
+ [data-ln-dropdown]:not(:has([data-ln-toggle-for])) {
11
+ @include dev-dom-error("[data-ln-dropdown] is missing a required child trigger [data-ln-toggle-for].");
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function R(){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 E(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 s=1;s<t.length;s++)if(t[s]&&t[s].nodeType===1){r=t[s];break}if(!E(r))return}e.apply(console,t)}}function k(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 v(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){v(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const q={};function H(e,t){q[e]=t}function V(e){return q[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=H,window.lnCore.getDataMapper=V);function U(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,v(function(){new MutationObserver(function(){k(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const B={};function K(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];B[n]=t}function Q(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return B[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=K,window.lnCore.getLocaleFallback=Q,window.lnCore.ensureLocaleObserver=U);const S={};function J(e,t){S[e]||(S[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=S[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 i=0;i<n.length;i++){const o=n[i],l=o.getAttribute("data-ln-field");t[l]!=null&&(o.textContent=t[l])}const r=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<r.length;i++){const o=r[i],l=o.getAttribute("data-ln-attr").split(",");for(let c=0;c<l.length;c++){const d=l[c].trim().split(":");if(d.length!==2)continue;const a=d[0].trim(),f=d[1].trim();t[f]!=null&&o.setAttribute(a,t[f])}}const s=e.querySelectorAll("[data-ln-show]");for(let i=0;i<s.length;i++){const o=s[i],l=o.getAttribute("data-ln-show");l in t&&o.classList.toggle("hidden",!t[l])}const u=e.querySelectorAll("[data-ln-class]");for(let i=0;i<u.length;i++){const o=u[i],l=o.getAttribute("data-ln-class").split(",");for(let c=0;c<l.length;c++){const d=l[c].trim().split(":");if(d.length!==2)continue;const a=d[0].trim(),f=d[1].trim();f in t&&o.classList.toggle(a,!!t[f])}}return e}function G(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let 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)$(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 M(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const u=n.currentNode;u.textContent.indexOf("{{")!==-1&&(u.textContent=u.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,o){return t[o]!==void 0?t[o]:""}))}const r=function(u,i){return t[i]!==void 0?t[i]:""},s=Array.from(e.querySelectorAll("*"));e.nodeType===1&&s.push(e);for(let u=0;u<s.length;u++){const i=s[u],o=i.attributes;for(let l=0;l<o.length;l++){const c=o[l];c.value.indexOf("{{")!==-1&&i.setAttribute(c.name,c.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function X(e,t,n,r,s,u){const i={};for(let l=0;l<e.children.length;l++){const c=e.children[l],d=c.getAttribute("data-ln-render-key");d&&(i[d]=c)}const o=document.createDocumentFragment();for(let l=0;l<t.length;l++){const c=t[l],d=String(r(c));let a=i[d];if(a)s(a,c,l);else{const f=J(n,u);if(!f||(M(f,c),a=f.firstElementChild,!a))continue;a.setAttribute("data-ln-render-key",d),s(a,c,l)}o.appendChild(a)}e.textContent="",e.appendChild(o)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=M,window.lnCore.fill=$,window.lnCore.lnFill=G,window.lnCore.renderList=X);function F(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function Y(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function Z(e,t,n=!1){const r=e.getAttribute(t);if(r===null)return!!n;const s=r.trim().toLowerCase();return!(s==="false"||s==="0")}function ee(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const L=new Map;function te(e,t){const n=(e?e.join("|"):"")+"::"+t;if(L.has(n))return L.get(n);const r=new Set(e||[]),s=function(u,i){const o=u.getAttribute(i);return o!==null&&r.has(o)?o:t};return L.set(n,s),s}function ne(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function oe(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function N(e,t,n,r,s){if(!e||t===null)return!0;const u=r||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const o=l=>{s?console.warn(l,s):console.warn(l)};if(i==="boolean"){const l=t.trim().toLowerCase();return l!==""&&l!=="true"&&l!=="false"&&l!=="1"&&l!=="0"?(o(`[${u}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const l=e.values||[];return l.includes(t)?!0:(o(`[${u}] Invalid value "${t}" for attribute "${n}". Allowed: ${l.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return o(`[${u}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const l=parseInt(t,10);return e.min!==void 0&&l<e.min?(o(`[${u}] Value ${l} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&l>e.max?(o(`[${u}] Value ${l} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(o(`[${u}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(l){return o(`[${u}] Invalid JSON for attribute "${n}": ${l.message}. Fallback: ${e.fallback}.`),!1}return!0}function re(e,t,n){for(const r in n){const[s,u,i]=n[r];Object.defineProperty(e,r,{get:function(){return s(t,u,i)},enumerable:!0,configurable:!0})}return e}function ie(e){const t={};for(const n in e){const r=e[n];if(!r||!r.prop)continue;let s=r.read;if(!s&&r.type)switch(r.type){case"enum":s=te(r.values,r.fallback);break;case"integer":s=Y;break;case"float":s=ne;break;case"boolean":s=Z;break;case"list":s=ee;break;case"json":s=oe;break;case"string":default:s=F;break}s||(s=F),t[r.prop]=[s,n,r.fallback]}return t}function le(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 se(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 O(e,t,n,r){if(e.nodeType!==1)return;const u=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i=Array.from(e.querySelectorAll(u));e.matches&&e.matches(u)&&i.push(e);for(const o of i)if(!o[n]){window.lnCore._persistSink&&o.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(o,t);try{o[n]=new r(o)}catch(l){console.error("["+n+"] init failed",o,l)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function ue(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function ce(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 W(){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 de(e){const t=W(),n=e.observed||[];for(let r=0;r<n.length;r++){const s=n[r];t.byAttr.has(s)||t.byAttr.set(s,[]),t.byAttr.get(s).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 s=t.byDeclaredAttr.get(r);s.some(u=>u.componentTag===e.componentTag)||s.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=se(e);if(r===null)t.reactiveWildcard.push(e);else for(const s of r)t.byReactive.has(s)||t.byReactive.set(s,[]),t.byReactive.get(s).push(e)}e.persist&&t.persist.push(e)}function j(e,t,n,r){for(let s=0;s<e.length;s++){const u=e[s];if(!t[u.attribute])continue;const i=u.effects&&u.effects[n];i?i(t,n,r):u.onAttrChange&&(!u.declared||u.declared.has(n))&&u.onAttrChange(t,n,r)}}function ae(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=W(),s=r.byAttr.get(n);if(R()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&E(t)){const u=r.byDeclaredAttr.get(n),i=t.getAttribute(n);for(let o=0;o<u.length;o++)N(u[o].spec,i,n,u[o].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||s)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const u=r.byReactive.get(n);u&&j(u,t,n,e.oldValue),r.reactiveWildcard.length&&j(r.reactiveWildcard,t,n,e.oldValue)}if(s)for(let u=0;u<s.length;u++){const i=s[u];if(i.handler){i.handler(t,n,e.oldValue);continue}i.onAttributeChange&&t[i.attribute]?i.onAttributeChange(t,n):(O(t,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(t))}}function fe(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,v(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{ae(t[n])}catch(r){console.error("[ln-core] mutation handler failed",t[n].target,r)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function P(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function ge(e){const t=P();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const r=t[n];if(r.onSubtreeChange){const s=r.query,u=e.target.nodeType===1?e.target.matches(s)?e.target:e.target.closest(s):e.target.parentElement?e.target.parentElement.closest(s):null;u&&r.onSubtreeChange(u,e)}}for(let n=0;n<e.addedNodes.length;n++){const r=e.addedNodes[n];if(r.nodeType===1)for(let s=0;s<t.length;s++){const u=t[s];O(r,u.selector,u.attribute,u.ComponentFn),u.onInit&&u.onInit(r)}}for(let n=0;n<e.removedNodes.length;n++){const r=e.removedNodes[n];if(r.nodeType===1)for(let s=0;s<t.length;s++){const u=t[s],i=u.query,o=Array.from(r.querySelectorAll(i));r.matches&&r.matches(i)&&o.push(r);for(let l=0;l<o.length;l++){const c=o[l];if(!document.contains(c)){const d=c[u.attribute];if(d&&typeof d.destroy=="function")try{d.destroy()}catch(a){console.error("["+u.attribute+"] destroy failed",c,a)}delete c[u.attribute]}}}}}}function pe(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,v(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{ge(r)}catch(s){console.error("[ln-core] lifecycle handler failed",r.target,s)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function he(e,t,n,r,s={}){const u=s.extraAttributes||[],i=s.onAttributeChange||null,o=s.onSubtreeChange||null,l=s.onInit||null,c=s.onAttrChange||null,d=s.effects||null,a=s.attributes||null,f=a?le(a):d,A=a?new Set(Object.keys(a)):null,_=s.persist||null;function C(h){const p=h||document.body;if(O(p,e,t,n),a&&R())for(const w in a){const Se=a[w],D=Array.from(p.querySelectorAll("["+w+"]"));p.matches&&p.matches("["+w+"]")&&D.push(p);for(let x=0;x<D.length;x++){const I=D[x];E(I)&&N(Se,I.getAttribute(w),w,r,I)}}l&&l(p)}const b=[];if(e.indexOf("[")!==-1){const h=/\[([\w-]+)/g;let p;for(;(p=h.exec(e))!==null;)b.push(p[1])}else b.push(e);de({selector:e,attribute:t,componentTag:r,attributes:a,ComponentFn:n,onInit:l,observed:b.concat(u),onAttributeChange:i,onAttrChange:c,effects:f,declared:A,persist:_}),fe();const m=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";P().push({selector:e,attribute:t,ComponentFn:n,onInit:l,onSubtreeChange:o,query:m}),pe(),window[t]=C;function g(){ue()>0?ce(function(){C(document.body)}):C(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",g):g(),C}function z(e){return(e||"").replace(/^#/,"")}function T(e){const t=e===void 0?location.hash:e,n={},r=z(t);if(!r)return n;const s=r.split("&");for(let u=0;u<s.length;u++){const i=s[u];if(!i)continue;const o=i.indexOf(":"),l=o>-1?i.slice(0,o):i,c=o>-1?i.slice(o+1):"";if(l)try{n[l]=decodeURIComponent(c)}catch{n[l]=c}}return n}function we(e){if(!e)return null;const t=T();return e in t?t[e]:null}function be(e,t){if(!e)return;const n=T();t==null?delete n[e]:n[e]=String(t);const s=Object.keys(n).map(function(u){const i=n[u];return i===""?u:u+":"+encodeURIComponent(i)}).join("&");z(location.hash)!==s&&(location.hash=s)}function me(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function Ce(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 ye(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 Ae(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function ve(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),s=r?r.split(",").map(function(u){try{return decodeURIComponent(u)}catch{return u}}).filter(Boolean):[];return{key:n,values:s}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=T,window.lnCore.hashGet=we,window.lnCore.hashSet=be,window.lnCore.hashLinkClick=me,window.lnCore.resolveHashNamespace=Ce,window.lnCore.hashSortEncode=ye,window.lnCore.hashSortDecode=_e,window.lnCore.hashFilterEncode=Ae,window.lnCore.hashFilterDecode=ve);function Ee(e,t,n,r){const s=typeof r=="number"?r:4,u=window.innerWidth,i=window.innerHeight,o=t.width,l=t.height,c=n.split("-"),d=c[0],a=c[1]==="start"||c[1]==="end"?c[1]:"center",f={top:["top","bottom","right","left"],bottom:["bottom","top","right","left"],left:["left","right","top","bottom"],right:["right","left","top","bottom"]},A=f[d]||f.bottom;function _(g){return g==="top"||g==="bottom"?a==="start"?e.left:a==="end"?e.right-o:e.left+(e.width-o)/2:a==="start"?e.top:a==="end"?e.bottom-l:e.top+(e.height-l)/2}function C(g){let h,p,w=!0;return g==="top"?(h=e.top-s-l,p=_(g),h<0&&(w=!1)):g==="bottom"?(h=e.bottom+s,p=_(g),h+l>i&&(w=!1)):g==="left"?(h=_(g),p=e.left-s-o,p<0&&(w=!1)):(h=_(g),p=e.right+s,p+o>u&&(w=!1)),{top:h,left:p,side:g,fits:w}}let b=null;for(let g=0;g<A.length;g++){const h=C(A[g]);if(h.fits){b=h;break}}b||(b=C(A[0]));let y=b.top,m=b.left;return o>=u?m=0:(m<0&&(m=0),m+o>u&&(m=u-o)),l>=i?y=0:(y<0&&(y=0),y+l>i&&(y=i-l)),{top:y,left:m,placement:b.side}}function ke(e){if(!e)return{width:0,height:0};const t=e.style,n=t.visibility,r=t.display,s=t.position;t.visibility="hidden",t.display="block",t.position="fixed";const u=e.offsetWidth,i=e.offsetHeight;return t.visibility=n,t.display=r,t.position=s,{width:u,height:i}}(function(){const e="data-ln-dropdown",t="lnDropdown",n="bottom-end";if(window[t]!==void 0)return;const r={"data-ln-dropdown":{type:"marker",description:"Initializes the dropdown container"},"data-ln-dropdown-position":{prop:"position",type:"enum",values:["top","top-start","top-end","bottom","bottom-start","bottom-end","left","left-start","left-end","right","right-start","right-end"],fallback:n,description:"Preferred positioning anchor"},"data-ln-dropdown-placement":{type:"enum",values:["top","top-start","top-end","bottom","bottom-start","bottom-end","left","left-start","left-end","right","right-start","right-end"],description:"Active calculated placement applied at runtime"},"data-ln-dropdown-menu":{type:"marker",description:"Dropdown menu element containing items"}},s=ie(r);function u(i){this.dom=i,re(this,i,s),this.toggleEl=i.querySelector("[data-ln-toggle]"),this._boundDocClick=null,this._docClickTimeout=null,this._boundScrollReposition=null,this._boundResizeClose=null,this.toggleEl&&(this.toggleEl.setAttribute("data-ln-dropdown-menu",""),this.toggleEl.setAttribute("role","menu"),this.toggleEl.setAttribute("popover","manual"),this._initMenuAria()),this.triggerBtn=i.querySelector("[data-ln-toggle-for]"),this.triggerBtn&&(this.triggerBtn.setAttribute("aria-haspopup","menu"),this.triggerBtn.setAttribute("aria-expanded","false"));const o=this;return this._onRequestOpen=function(){o.toggleEl&&o.toggleEl.setAttribute("data-ln-toggle","open")},this._onRequestClose=function(){o.toggleEl&&o.toggleEl.setAttribute("data-ln-toggle","close")},this._onRequestToggle=function(){if(o.toggleEl){const l=o.toggleEl.getAttribute("data-ln-toggle");o.toggleEl.setAttribute("data-ln-toggle",l==="open"?"close":"open")}},this._onKeydown=function(l){const c=o.toggleEl&&o.toggleEl.getAttribute("data-ln-toggle")==="open";if(l.key==="Escape"){c&&(l.preventDefault(),l.stopPropagation(),o.toggleEl.setAttribute("data-ln-toggle","close"),o.triggerBtn&&o.triggerBtn.focus());return}if(l.key==="Tab"){c&&(o.triggerBtn&&o.triggerBtn.focus(),o.toggleEl.setAttribute("data-ln-toggle","close"));return}const d=o._getMenuItems();if(d.length===0)return;if(!c&&(l.key==="ArrowDown"||l.key==="ArrowUp")){l.preventDefault(),o.toggleEl.setAttribute("data-ln-toggle","open"),setTimeout(function(){const f=o._getMenuItems();f.length>0&&o._focusItem(f,l.key==="ArrowDown"?0:f.length-1)},0);return}if(!c)return;const a=d.indexOf(document.activeElement);if(l.key==="ArrowDown"){l.preventDefault();const f=a<d.length-1?a+1:0;o._focusItem(d,f)}else if(l.key==="ArrowUp"){l.preventDefault();const f=a>0?a-1:d.length-1;o._focusItem(d,f)}else l.key==="Home"?(l.preventDefault(),o._focusItem(d,0)):l.key==="End"&&(l.preventDefault(),o._focusItem(d,d.length-1))},this.dom.addEventListener("ln-dropdown:request-open",this._onRequestOpen),this.dom.addEventListener("ln-dropdown:request-close",this._onRequestClose),this.dom.addEventListener("ln-dropdown:request-toggle",this._onRequestToggle),this.dom.addEventListener("keydown",this._onKeydown),this._onToggleOpen=function(l){!l.detail||l.detail.target!==o.toggleEl||(o.triggerBtn&&o.triggerBtn.setAttribute("aria-expanded","true"),typeof o.toggleEl.showPopover=="function"&&o.toggleEl.showPopover(),o._initMenuAria(),o._reposition(),o._addOutsideClickListener(),o._addScrollRepositionListener(),o._addResizeCloseListener(),k(i,"ln-dropdown:open",{target:l.detail.target}))},this._onToggleClose=function(l){!l.detail||l.detail.target!==o.toggleEl||(o.triggerBtn&&o.triggerBtn.setAttribute("aria-expanded","false"),o._removeOutsideClickListener(),o._removeScrollRepositionListener(),o._removeResizeCloseListener(),o.toggleEl.style.top="",o.toggleEl.style.left="",o.toggleEl.removeAttribute("data-ln-dropdown-placement"),typeof o.toggleEl.hidePopover=="function"&&o.toggleEl.matches(":popover-open")&&o.toggleEl.hidePopover(),k(i,"ln-dropdown:close",{target:l.detail.target}))},this.toggleEl&&(this.toggleEl.addEventListener("ln-toggle:open",this._onToggleOpen),this.toggleEl.addEventListener("ln-toggle:close",this._onToggleClose)),this}u.prototype._initMenuAria=function(){if(!this.toggleEl)return;const i=this.toggleEl.querySelectorAll("li");for(const l of i)l.setAttribute("role","none");const o=this._getMenuItems();for(let l=0;l<o.length;l++)o[l].setAttribute("role","menuitem"),o[l].setAttribute("tabindex",l===0?"0":"-1")},u.prototype._getMenuItems=function(){return this.toggleEl?Array.from(this.toggleEl.querySelectorAll('a[href], button:not([disabled]), [role="menuitem"]:not([disabled])')):[]},u.prototype._focusItem=function(i,o){for(let l=0;l<i.length;l++)i[l].setAttribute("tabindex",l===o?"0":"-1");i[o]&&i[o].focus()},u.prototype._reposition=function(){if(!this.triggerBtn||!this.toggleEl)return;const i=this.triggerBtn.getBoundingClientRect(),o=ke(this.toggleEl),l=parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--size-xs"))*16||4,c=this.position||n,d=Ee(i,o,c,l);this.toggleEl.style.top=d.top+"px",this.toggleEl.style.left=d.left+"px",this.toggleEl.setAttribute("data-ln-dropdown-placement",d.placement)},u.prototype._addOutsideClickListener=function(){if(this._boundDocClick)return;const i=this;this._boundDocClick=function(o){i.dom.contains(o.target)||i.toggleEl&&i.toggleEl.contains(o.target)||i.toggleEl&&i.toggleEl.getAttribute("data-ln-toggle")==="open"&&i.toggleEl.setAttribute("data-ln-toggle","close")},i._docClickTimeout=setTimeout(function(){i._docClickTimeout=null,document.addEventListener("click",i._boundDocClick)},0)},u.prototype._removeOutsideClickListener=function(){this._docClickTimeout&&(clearTimeout(this._docClickTimeout),this._docClickTimeout=null),this._boundDocClick&&(document.removeEventListener("click",this._boundDocClick),this._boundDocClick=null)},u.prototype._addScrollRepositionListener=function(){const i=this;this._boundScrollReposition=function(){i._reposition()},window.addEventListener("scroll",this._boundScrollReposition,{passive:!0,capture:!0})},u.prototype._removeScrollRepositionListener=function(){this._boundScrollReposition&&(window.removeEventListener("scroll",this._boundScrollReposition,{capture:!0}),this._boundScrollReposition=null)},u.prototype._addResizeCloseListener=function(){const i=this;this._boundResizeClose=function(){i.toggleEl&&i.toggleEl.getAttribute("data-ln-toggle")==="open"&&i.toggleEl.setAttribute("data-ln-toggle","close")},window.addEventListener("resize",this._boundResizeClose)},u.prototype._removeResizeCloseListener=function(){this._boundResizeClose&&(window.removeEventListener("resize",this._boundResizeClose),this._boundResizeClose=null)},u.prototype.destroy=function(){this.dom[t]&&(this.dom.removeEventListener("ln-dropdown:request-open",this._onRequestOpen),this.dom.removeEventListener("ln-dropdown:request-close",this._onRequestClose),this.dom.removeEventListener("ln-dropdown:request-toggle",this._onRequestToggle),this.dom.removeEventListener("keydown",this._onKeydown),this._removeOutsideClickListener(),this._removeScrollRepositionListener(),this._removeResizeCloseListener(),this.toggleEl&&typeof this.toggleEl.hidePopover=="function"&&this.toggleEl.matches(":popover-open")&&this.toggleEl.hidePopover(),this.toggleEl&&(this.toggleEl.removeAttribute("data-ln-dropdown-placement"),this.toggleEl.removeEventListener("ln-toggle:open",this._onToggleOpen),this.toggleEl.removeEventListener("ln-toggle:close",this._onToggleClose)),delete this.dom[t])},he(e,t,u,"ln-dropdown",{attributes:r})})()})();
@@ -0,0 +1,90 @@
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-dropdown",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-dropdown-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-dropdown": {
13
+ "direction": null,
14
+ "sources": [
15
+ "ln-dropdown-dev.scss",
16
+ "ln-dropdown.scss",
17
+ "src/ln-dropdown.js"
18
+ ],
19
+ "type": "marker",
20
+ "description": "Initializes the dropdown container"
21
+ },
22
+ "data-ln-dropdown-menu": {
23
+ "direction": "runtime",
24
+ "sources": [
25
+ "ln-dropdown.scss",
26
+ "src/ln-dropdown.js"
27
+ ],
28
+ "type": "marker",
29
+ "description": "Dropdown menu element containing items"
30
+ },
31
+ "data-ln-dropdown-placement": {
32
+ "direction": "runtime",
33
+ "sources": [
34
+ "src/ln-dropdown.js"
35
+ ],
36
+ "type": "enum",
37
+ "values": [
38
+ "top",
39
+ "top-start",
40
+ "top-end",
41
+ "bottom",
42
+ "bottom-start",
43
+ "bottom-end",
44
+ "left",
45
+ "left-start",
46
+ "left-end",
47
+ "right",
48
+ "right-start",
49
+ "right-end"
50
+ ],
51
+ "description": "Active calculated placement applied at runtime"
52
+ },
53
+ "data-ln-dropdown-position": {
54
+ "direction": null,
55
+ "sources": [
56
+ "src/ln-dropdown.js"
57
+ ],
58
+ "type": "enum",
59
+ "values": [
60
+ "top",
61
+ "top-start",
62
+ "top-end",
63
+ "bottom",
64
+ "bottom-start",
65
+ "bottom-end",
66
+ "left",
67
+ "left-start",
68
+ "left-end",
69
+ "right",
70
+ "right-start",
71
+ "right-end"
72
+ ],
73
+ "description": "Preferred positioning anchor"
74
+ },
75
+ "data-ln-toggle": {
76
+ "direction": "both",
77
+ "sources": [
78
+ "ln-dropdown-dev.scss",
79
+ "src/ln-dropdown.js"
80
+ ]
81
+ },
82
+ "data-ln-toggle-for": {
83
+ "direction": "author",
84
+ "sources": [
85
+ "ln-dropdown-dev.scss",
86
+ "src/ln-dropdown.js"
87
+ ]
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,18 @@
1
+ // ==========================================================================
2
+ // ln-dropdown — Functional State & Placement Rules
3
+ // ==========================================================================
4
+
5
+ [data-ln-dropdown] {
6
+ position: relative;
7
+ }
8
+
9
+ [data-ln-dropdown-menu] {
10
+ display: none;
11
+ position: fixed;
12
+ inset: auto;
13
+ margin: 0;
14
+
15
+ &.open {
16
+ display: block;
17
+ }
18
+ }