@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
@@ -0,0 +1,228 @@
1
+ import { registerComponent, dispatch, dispatchCancelable } from '../../ln-core';
2
+
3
+ (function () {
4
+ 'use strict';
5
+
6
+ const DOM_SELECTOR = 'data-ln-picklist';
7
+ const DOM_ATTRIBUTE = 'lnPicklist';
8
+ const LIST_ATTR = 'data-ln-picklist-list';
9
+ const MAX_ATTR = 'data-ln-picklist-max';
10
+
11
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
12
+
13
+ // ─── Attribute Contract (SSOT) ──────────────────────────
14
+ const ATTRIBUTES = {
15
+ 'data-ln-picklist': { type: 'enum', values: ['enabled', 'disabled'], fallback: 'enabled', effect: _syncEnabled, description: 'Controls enabled/disabled state of the dual-list picklist' },
16
+ 'data-ln-picklist-max': { type: 'integer', fallback: Infinity, min: 1, effect: _syncMax, description: 'Maximum selectable items in the selected list' },
17
+ 'data-ln-picklist-list': { type: 'enum', values: ['available', 'selected'], description: 'Role marker for available or selected picklist columns' }
18
+ };
19
+
20
+ // ─── Component ─────────────────────────────────────────────
21
+ //
22
+ // The checkbox IS the state. Its `checked` property and the list the item
23
+ // currently sits in are the same fact, and `name`/`value` carry the
24
+ // selection to the server natively — unchecked boxes are not submitted.
25
+ // Hence no hidden inputs, no state attribute and no JS mirror.
26
+
27
+ function _component(dom) {
28
+ this.dom = dom;
29
+ this.isEnabled = dom.getAttribute(DOM_SELECTOR) !== 'disabled';
30
+ this.max = _readMax(dom);
31
+
32
+ this.available = dom.querySelector('[' + LIST_ATTR + '="available"]');
33
+ this.selected = dom.querySelector('[' + LIST_ATTR + '="selected"]');
34
+
35
+ // Report the gap rather than failing on the first click.
36
+ if (!this.available || !this.selected) {
37
+ console.warn('[ln-picklist] requires both [' + LIST_ATTR + '="available"] and [' + LIST_ATTR + '="selected"]', dom);
38
+ return this;
39
+ }
40
+
41
+ // Bound once so destroy() can detach the same reference.
42
+ this._onChange = this._onChange.bind(this);
43
+ dom.addEventListener('change', this._onChange);
44
+
45
+ // Snapshot the authored items in document order.
46
+ this._initial = [];
47
+ for (const list of [this.available, this.selected]) {
48
+ for (const item of list.children) {
49
+ this._initial.push(item);
50
+ }
51
+ }
52
+
53
+ // Initial sync: transfer checked items to selected, unchecked to available.
54
+ this.sync();
55
+
56
+ this._form = dom.closest('form');
57
+ if (this._form) {
58
+ this._onFormReset = this._onFormReset.bind(this);
59
+ this._form.addEventListener('reset', this._onFormReset);
60
+ }
61
+
62
+ return this;
63
+ }
64
+
65
+ // ─── Lifecycle ─────────────────────────────────────────────
66
+
67
+ _component.prototype.destroy = function () {
68
+ if (!this.dom[DOM_ATTRIBUTE]) return;
69
+ this._destroyed = true;
70
+ if (this._onChange) this.dom.removeEventListener('change', this._onChange);
71
+ if (this._form) this._form.removeEventListener('reset', this._onFormReset);
72
+ delete this.dom[DOM_ATTRIBUTE];
73
+ };
74
+
75
+ // ─── Instance API ──────────────────────────────────────────
76
+
77
+ _component.prototype.enable = function () {
78
+ this.dom.setAttribute(DOM_SELECTOR, '');
79
+ };
80
+
81
+ _component.prototype.disable = function () {
82
+ this.dom.setAttribute(DOM_SELECTOR, 'disabled');
83
+ };
84
+
85
+ _component.prototype.sync = function () {
86
+ if (!this.available || !this.selected) return;
87
+
88
+ // Include any dynamically added items not yet tracked
89
+ const currentItems = Array.from(this.available.children).concat(Array.from(this.selected.children));
90
+ for (let i = 0; i < currentItems.length; i++) {
91
+ if (!this._initial.includes(currentItems[i])) {
92
+ this._initial.push(currentItems[i]);
93
+ }
94
+ }
95
+
96
+ let selectedCount = 0;
97
+ for (let i = 0; i < this._initial.length; i++) {
98
+ const item = this._initial[i];
99
+ if (!item.isConnected) continue;
100
+ const checkbox = item.querySelector('input[type="checkbox"]');
101
+ if (!checkbox) continue;
102
+
103
+ let targetList;
104
+ if (checkbox.checked) {
105
+ if (this.max === null || selectedCount < this.max) {
106
+ targetList = this.selected;
107
+ selectedCount++;
108
+ } else {
109
+ checkbox.checked = false;
110
+ targetList = this.available;
111
+ }
112
+ } else {
113
+ targetList = this.available;
114
+ }
115
+ targetList.appendChild(item);
116
+ }
117
+ };
118
+
119
+ // ─── Form Reset ────────────────────────────────────────────
120
+ //
121
+ // Resetting a form restores every control to its default SILENTLY — the
122
+ // spec fires no `change` and no `input` for them, only `reset` on the form
123
+ // itself. Re-syncing in recorded order rebuilds both lists according to
124
+ // defaultChecked.
125
+
126
+ _component.prototype._onFormReset = function (e) {
127
+ const self = this;
128
+
129
+ // `reset` fires BEFORE the controls are restored, and it is cancelable,
130
+ // so the work is deferred and re-checked — the same shape ln-editor
131
+ // uses to re-read its textarea.
132
+ setTimeout(function () {
133
+ if (self._destroyed || e.defaultPrevented) return;
134
+ self.sync();
135
+ }, 0);
136
+ };
137
+
138
+ // ─── Change Handler ────────────────────────────────────────
139
+
140
+ _component.prototype._onChange = function (e) {
141
+ // `change` bubbles, so anything the author composes inside the root —
142
+ // an ln-search input, a select — reaches this listener. Filter hard.
143
+ const checkbox = e.target.closest('input[type="checkbox"]');
144
+ if (!checkbox) return;
145
+
146
+ const item = checkbox.closest('li');
147
+ if (!item) return;
148
+
149
+ const from = item.parentElement;
150
+ if (from !== this.available && from !== this.selected) return;
151
+
152
+ // The browser has already toggled the box by the time this runs, so a
153
+ // bare return would leave it checked while the item stays put — which
154
+ // is exactly the contradiction this component exists to avoid.
155
+ if (!this.isEnabled) {
156
+ checkbox.checked = !checkbox.checked;
157
+ return;
158
+ }
159
+
160
+ const to = checkbox.checked ? this.selected : this.available;
161
+
162
+ // Inconsistent authored markup (a checked item rendered on the left),
163
+ // or a synthetic change that did not flip the box, would otherwise
164
+ // re-append in place and emit a move that never happened.
165
+ if (from === to) return;
166
+
167
+ // Enforce maximum selection limit if configured
168
+ if (to === this.selected && this.max !== null && this.selected.children.length >= this.max) {
169
+ checkbox.checked = !checkbox.checked;
170
+ dispatch(this.dom, 'ln-picklist:max-reached', {
171
+ max: this.max,
172
+ item: item,
173
+ checkbox: checkbox,
174
+ count: this.selected.children.length
175
+ });
176
+ return;
177
+ }
178
+
179
+ // One record, both events — they describe the same move.
180
+ const detail = { item: item, from: from, to: to, checkbox: checkbox };
181
+
182
+ if (dispatchCancelable(this.dom, 'ln-picklist:before-move', detail).defaultPrevented) {
183
+ checkbox.checked = !checkbox.checked;
184
+ return;
185
+ }
186
+
187
+ // Re-parenting a node that holds focus blurs it to <body> in Chromium.
188
+ // Only restore when the focus really was here: a synthetic change from
189
+ // a script must not steal focus away from whatever the user is using.
190
+ const hadFocus = document.activeElement === checkbox;
191
+
192
+ to.appendChild(item);
193
+
194
+ if (hadFocus) checkbox.focus();
195
+
196
+ dispatch(this.dom, 'ln-picklist:move', detail);
197
+ };
198
+
199
+ // ─── Attribute Sync ────────────────────────────────────────
200
+
201
+ function _readMax(el) {
202
+ const raw = el.getAttribute(MAX_ATTR);
203
+ if (raw === null || raw === '') return null;
204
+ const parsed = parseInt(raw, 10);
205
+ return isNaN(parsed) || parsed < 0 ? null : parsed;
206
+ }
207
+
208
+ function _syncEnabled(el) {
209
+ const instance = el[DOM_ATTRIBUTE];
210
+ if (!instance) return;
211
+ const shouldBeEnabled = el.getAttribute(DOM_SELECTOR) !== 'disabled';
212
+ if (shouldBeEnabled === instance.isEnabled) return;
213
+ instance.isEnabled = shouldBeEnabled;
214
+ dispatch(el, shouldBeEnabled ? 'ln-picklist:enabled' : 'ln-picklist:disabled', { target: el });
215
+ }
216
+
217
+ function _syncMax(el) {
218
+ const instance = el[DOM_ATTRIBUTE];
219
+ if (!instance) return;
220
+ instance.max = _readMax(el);
221
+ }
222
+
223
+ // ─── Init ──────────────────────────────────────────────────
224
+
225
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-picklist', {
226
+ attributes: ATTRIBUTES
227
+ });
228
+ })();
@@ -1,162 +1,164 @@
1
- # ln-popover
2
-
3
- > Viewport-aware click-triggered rich-content overlays, managed reactively via the DOM.
4
-
5
- ---
6
-
7
- ## 1. Philosophy & Mindset
8
-
9
- In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating a heavy component that bundles state, positioning algorithms, LIFO click stacks, and styles, `ln-popover` splits them cleanly:
10
-
11
- 1. **State & Positioning (JavaScript + native Popover API)**: The `ln-popover` script handles binary `open` / `closed` states in the DOM, bridged to the native Popover API (`popover="manual"`, `showPopover()`/`hidePopover()`) purely for top-layer rendering — no ancestor `overflow`/`z-index` can clip or bury it. Because `manual` mode has no native light-dismiss, the script still owns 100% of the behavior: viewport-boundary flip placement, keyboard focus save/restore, and the LIFO (Last-In, First-Out) ESC dismissal stack.
12
- 2. **Visual Presentation (CSS)**: Visual layouts, background shadows, and borders are handled in Vanilla CSS. The library ships a premium `@mixin popover` to style popover cards.
13
- 3. **Trigger decoupling (HTML)**: Triggers (`data-ln-popover-for="popoverId"`) and popover containers (`id="popoverId"`) are bound purely by ID, allowing the trigger button and the popup card to live anywhere in the document.
14
-
15
- ---
16
-
17
- ## 2. Minimal Blueprint
18
-
19
- Triggers and popovers are paired by ID. Wrap content in a `div` carrying the `data-ln-popover` attribute.
20
-
21
- ```html
22
- <!-- Trigger anywhere -->
23
- <button data-ln-popover-for="account-menu">Account</button>
24
-
25
- <!-- Popover anywhere — role="dialog", tabindex="-1", and popover="manual" are injected automatically -->
26
- <div data-ln-popover id="account-menu">
27
- <p><strong>user@example.com</strong></p>
28
- <nav>
29
- <a href="/settings">Settings</a>
30
- <a href="/logout">Sign out</a>
31
- </nav>
32
- </div>
33
- ```
34
-
35
- ### Key Anatomy Rules
36
- - **The Popover wrapper (`data-ln-popover`)**: Driven by the value `"open"` (open) and `"closed"` or empty (closed).
37
- - **The Trigger (`data-ln-popover-for="id"`)**: Sets `aria-expanded` and manages focus automatically.
38
- - **Visual styling**: Re-apply `@mixin popover` in your SCSS on the popover element.
39
-
40
- ---
41
-
42
- ## 3. Declarative API & State Contract
43
-
44
- The initialized instance is exposed as `dom.lnPopover`, with `open(trigger)`, `close()`, `toggle(trigger)`, and `destroy()` methods, plus `isOpen` and `trigger` properties. **The HTML attribute remains the primary contract** — the instance methods are thin wrappers that ultimately write the same attribute.
45
-
46
- Triggers, LIFO click outside handlers, viewport edge flips, and custom scripts all change state by writing the active attribute on the popover element:
47
-
48
- ```js
49
- const popover = document.getElementById('account-menu');
50
-
51
- // Open the popover
52
- popover.setAttribute('data-ln-popover', 'open');
53
-
54
- // Close the popover
55
- popover.setAttribute('data-ln-popover', 'closed');
56
-
57
- // Equivalent imperative calls via the instance
58
- popover.lnPopover.open();
59
- popover.lnPopover.close();
60
-
61
- // Read-only state query
62
- popover.lnPopover.isOpen; // Returns true/false
63
- ```
64
-
65
- ### Attributes
66
- - `data-ln-popover`: Placed on the popover card. Value `"open"` = open; `"closed"` = closed.
67
- - `data-ln-popover-for="id"`: Placed on triggers referencing the popover ID.
68
- - `data-ln-popover-position="top|bottom|left|right"`: Preferred placement side. Default: `bottom`.
69
- - `data-ln-popover-placement`: Set automatically by JS to indicate the *actual* active side after auto-flip is resolved.
70
-
71
- ---
72
-
73
- ## 4. Transition Events
74
-
75
- All events bubble. The dispatch target is the popover element itself.
76
-
77
- | Event | Cancelable | `detail` | Dispatched When |
78
- |---|---|---|---|
79
- | **`ln-popover:before-open`** | **Yes** | `{ popoverId, target, trigger }` | Before opening. `event.preventDefault()` cancels the transition and reverts the attribute. |
80
- | **`ln-popover:open`** | No | `{ popoverId, target, trigger }` | After popover is positioned, classes added, and focus moved. |
81
- | **`ln-popover:before-close`** | **Yes** | `{ popoverId, target, trigger }` | Before closing. `event.preventDefault()` cancels the close and reverts the attribute. |
82
- | **`ln-popover:close`** | No | `{ popoverId, target, trigger }` | After popover is closed and focus restored back to the trigger. |
83
-
84
- ```js
85
- // Example: Cancel open transition for unauthorized zones
86
- document.addEventListener('ln-popover:before-open', (e) => {
87
- if (e.detail.popoverId === 'admin-zone' && !currentUser.isAdmin) {
88
- e.preventDefault(); // Reverts attribute back to "closed"
89
- }
90
- });
91
- ```
92
-
93
- ---
94
-
95
- ## 5. Integration Patterns
96
-
97
- ### A. Position Preferences & Auto-Flip
98
- Preferred placement is easily configured. If there isn't enough room in the viewport, the positioning engine flips the popover to the opposite side:
99
- ```html
100
- <div data-ln-popover data-ln-popover-position="right" id="help-menu">...</div>
101
- ```
102
-
103
- ### B. Nested Popovers (A stays open when B opens)
104
- Opening B from inside A leaves A open. Pressing `ESC` once closes B first, and then A:
105
- ```html
106
- <div data-ln-popover id="popover-a">
107
- <button data-ln-popover-for="popover-b">Open B</button>
108
- </div>
109
- <div data-ln-popover id="popover-b">...</div>
110
- ```
111
-
112
- ---
113
-
114
- ## 6. Integration & Source Files
115
-
116
- - **Unified Bundle**: Loaded automatically with the main bundle:
117
- ```html
118
- <script src="dist/ln-ashlar.iife.js" defer></script>
119
- ```
120
- - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
121
- ```html
122
- <script src="js/ln-popover/ln-popover.js" defer></script>
123
- ```
124
- - **Active Source (ESM)**: Development source is located at [js/ln-popover/src/ln-popover.js](file:///c:/laragon/www/ln-ashlar/js/ln-popover/src/ln-popover.js).
125
-
126
- ---
127
-
128
- ## Related
129
- - **[`ln-dropdown`](../ln-dropdown/README.md)** — Menu wrapper adding click-outside/top-layer promotion.
130
- - **[`ln-toggle`](../ln-toggle/README.md)** — Binary disclosure state primitive.
131
-
132
- ---
133
-
134
- ## 🔧 Internals
135
-
136
- Source: `js/ln-popover/ln-popover.js`.
137
-
138
- ### Open-stack
139
-
140
- A module-level `openStack` array tracks all currently-open instances. It drives two behaviors: a single `document` `keydown` listener, registered only while the stack is non-empty, pops the top entry and calls `.close()` on it (LIFO Escape); and nested popovers stay independent because each owns its own outside-click listener scoped to its own subtree.
141
-
142
- ### `_applyOpen(trigger)` — after the cancelable `before-open` passes
143
-
144
- Set `isOpen = true`, store `trigger` (`null` if opened via direct attribute mutation) and `document.activeElement` (for focus restore) → `teleportToBody(dom)` (moves the element to end of `<body>`, storing a restore function, so `position: fixed` coordinates are reliable regardless of ancestor `transform`/`contain`) → `measureHidden(dom)` for dimensions even if `display: none` → `computePlacement(triggerRect, size, preferred, 8)` writes `top`/`left` inline (unavoidable for floating UI) and `data-ln-popover-placement` → `aria-expanded="true"` on trigger → focus the first focusable child, or the container itself → outside-click listener registered one tick later via `setTimeout` (so the opening click itself doesn't immediately close it) → scroll/resize listeners for repositioning while open → push onto `openStack`, ensure ESC listener → dispatch `ln-popover:open`.
145
-
146
- ### `_applyClose()` — mirror of `_applyOpen`
147
-
148
- Clears `isOpen`, removes the outside-click/scroll/resize listeners, clears inline `top`/`left` and the placement attribute, sets `aria-expanded="false"`, runs the teleport-restore function, splices itself from `openStack` (removing the ESC listener if the stack is now empty), then restores focus to the trigger **only if** the trigger was the previously-focused element (click→Escape, click→programmatic close) or `activeElement === document.body` (Escape from inside the popover, outside-click on inert whitespace) — an outside-click landing on another focusable element keeps that element's focus. Dispatches `ln-popover:close`.
149
-
150
- ### Focus management
151
-
152
- A disclosure pattern, not a modal — no Tab trap. Because the popover is teleported to the end of `<body>`, Tab exits it into whatever follows in body order. `tabindex="-1"` and `role="dialog"` are set automatically at construction so the container itself is a valid focus target when there's no focusable child.
153
-
154
- ### Nested popovers
155
-
156
- Outside-click tests `self.dom.contains(e.target)`, so a click inside A's subtree (including a button that opens B) never triggers A's close path. Both popovers land on `openStack`; LIFO Escape closes B first, then A on the next press. There is no "close all."
157
-
158
- ### Observer & registration
159
-
160
- Single observer on `document.body` (`subtree`/`childList`/`attributes`, filtered to `data-ln-popover`/`data-ln-popover-for`): childList additions re-run `_findPopovers`/`_attachTriggers`; attribute mutations dispatch to `_syncAttribute` for existing instances or re-run discovery for new ones. Two separate `registerComponent` calls wire popover elements (`el.lnPopover`) and trigger elements (`el.lnPopoverTrigger`) independently — trigger init sets `aria-haspopup="dialog"`, `aria-expanded="false"`, `aria-controls`, once, on attach.
161
-
162
- `destroy()` on a popover instance closes it if open, removes its listeners, splices it from `openStack`, and dispatches `ln-popover:destroyed`. Trigger `destroy` is independent and only removes the trigger's click handler.
1
+ # ln-popover
2
+
3
+ > Applied to a container via `data-ln-popover id="..."`, paired with a `<button data-ln-popover-for="...">`.
4
+ > On trigger `click`, it sets `popover="manual"` on the container, calls `showPopover()`, sets inline `top`/`left` coordinates, and writes `data-ln-popover="open"`.
5
+ > On outside `click` or `Escape`, it calls `hidePopover()` and writes `data-ln-popover="closed"`.
6
+
7
+ ---
8
+
9
+ ## 1. Philosophy & Mindset
10
+
11
+ In `ln-ashlar`, the core design principle is **orthogonality**. Rather than creating a heavy component that bundles state, positioning algorithms, LIFO click stacks, and styles, `ln-popover` splits them cleanly:
12
+
13
+ 1. **State & Positioning (JavaScript + native Popover API)**: The `ln-popover` script handles binary `open` / `closed` states in the DOM, bridged to the native Popover API (`popover="manual"`, `showPopover()`/`hidePopover()`) purely for top-layer rendering — no ancestor `overflow`/`z-index` can clip or bury it. Because `manual` mode has no native light-dismiss, the script still owns 100% of the behavior: viewport-boundary flip placement, keyboard focus save/restore, and the LIFO (Last-In, First-Out) ESC dismissal stack.
14
+ 2. **Visual Presentation (CSS)**: Visual layouts, background shadows, and borders are handled in Vanilla CSS. The library ships a premium `@mixin popover` to style popover cards.
15
+ 3. **Trigger decoupling (HTML)**: Triggers (`data-ln-popover-for="popoverId"`) and popover containers (`id="popoverId"`) are bound purely by ID, allowing the trigger button and the popup card to live anywhere in the document.
16
+
17
+ ---
18
+
19
+ ## 2. Minimal Blueprint
20
+
21
+ Triggers and popovers are paired by ID. Wrap content in a `div` carrying the `data-ln-popover` attribute.
22
+
23
+ ```html
24
+ <!-- Trigger anywhere -->
25
+ <button data-ln-popover-for="account-menu">Account</button>
26
+
27
+ <!-- Popover anywhere — role="dialog", tabindex="-1", and popover="manual" are injected automatically -->
28
+ <div data-ln-popover id="account-menu">
29
+ <p><strong>user@example.com</strong></p>
30
+ <nav>
31
+ <a href="/settings">Settings</a>
32
+ <a href="/logout">Sign out</a>
33
+ </nav>
34
+ </div>
35
+ ```
36
+
37
+ ### Key Anatomy Rules
38
+ - **The Popover wrapper (`data-ln-popover`)**: Driven by the value `"open"` (open) and `"closed"` or empty (closed).
39
+ - **The Trigger (`data-ln-popover-for="id"`)**: Sets `aria-expanded` and manages focus automatically.
40
+ - **Visual styling**: Re-apply `@mixin popover` in your SCSS on the popover element.
41
+
42
+ ---
43
+
44
+ ## 3. Declarative API & State Contract
45
+
46
+ The initialized instance is exposed as `dom.lnPopover`, with `open(trigger)`, `close()`, `toggle(trigger)`, and `destroy()` methods, plus `isOpen` and `trigger` properties. **The HTML attribute remains the primary contract** — the instance methods are thin wrappers that ultimately write the same attribute.
47
+
48
+ Triggers, LIFO click outside handlers, viewport edge flips, and custom scripts all change state by writing the active attribute on the popover element:
49
+
50
+ ```js
51
+ const popover = document.getElementById('account-menu');
52
+
53
+ // Open the popover
54
+ popover.setAttribute('data-ln-popover', 'open');
55
+
56
+ // Close the popover
57
+ popover.setAttribute('data-ln-popover', 'closed');
58
+
59
+ // Equivalent imperative calls via the instance
60
+ popover.lnPopover.open();
61
+ popover.lnPopover.close();
62
+
63
+ // Read-only state query
64
+ popover.lnPopover.isOpen; // Returns true/false
65
+ ```
66
+
67
+ ### Attributes
68
+ - `data-ln-popover`: Placed on the popover card. Value `"open"` = open; `"closed"` = closed.
69
+ - `data-ln-popover-for="id"`: Placed on triggers referencing the popover ID.
70
+ - `data-ln-popover-position="top|bottom|left|right"`: Preferred placement side. Default: `bottom`.
71
+ - `data-ln-popover-placement`: Set automatically by JS to indicate the *actual* active side after auto-flip is resolved.
72
+
73
+ ---
74
+
75
+ ## 4. Transition Events
76
+
77
+ All events bubble. The dispatch target is the popover element itself.
78
+
79
+ | Event | Cancelable | `detail` | Dispatched When |
80
+ |---|---|---|---|
81
+ | **`ln-popover:before-open`** | **Yes** | `{ popoverId, target, trigger }` | Before opening. `event.preventDefault()` cancels the transition and reverts the attribute. |
82
+ | **`ln-popover:open`** | No | `{ popoverId, target, trigger }` | After popover is positioned, classes added, and focus moved. |
83
+ | **`ln-popover:before-close`** | **Yes** | `{ popoverId, target, trigger }` | Before closing. `event.preventDefault()` cancels the close and reverts the attribute. |
84
+ | **`ln-popover:close`** | No | `{ popoverId, target, trigger }` | After popover is closed and focus restored back to the trigger. |
85
+
86
+ ```js
87
+ // Example: Cancel open transition for unauthorized zones
88
+ document.addEventListener('ln-popover:before-open', (e) => {
89
+ if (e.detail.popoverId === 'admin-zone' && !currentUser.isAdmin) {
90
+ e.preventDefault(); // Reverts attribute back to "closed"
91
+ }
92
+ });
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 5. Integration Patterns
98
+
99
+ ### A. Position Preferences & Auto-Flip
100
+ Preferred placement is easily configured. If there isn't enough room in the viewport, the positioning engine flips the popover to the opposite side:
101
+ ```html
102
+ <div data-ln-popover data-ln-popover-position="right" id="help-menu">...</div>
103
+ ```
104
+
105
+ ### B. Nested Popovers (A stays open when B opens)
106
+ Opening B from inside A leaves A open. Pressing `ESC` once closes B first, and then A:
107
+ ```html
108
+ <div data-ln-popover id="popover-a">
109
+ <button data-ln-popover-for="popover-b">Open B</button>
110
+ </div>
111
+ <div data-ln-popover id="popover-b">...</div>
112
+ ```
113
+
114
+ ---
115
+
116
+ ## 6. Integration & Source Files
117
+
118
+ - **Unified Bundle**: Loaded automatically with the main bundle:
119
+ ```html
120
+ <script src="dist/ln-ashlar.iife.js" defer></script>
121
+ ```
122
+ - **Standalone IIFE**: For lightweight pages, load the standalone, self-registering IIFE version:
123
+ ```html
124
+ <script src="components/ln-popover/ln-popover.js" defer></script>
125
+ ```
126
+ - **Active Source (ESM)**: Development source is located at [components/ln-popover/src/ln-popover.js](file:///c:/laragon/www/ln-ashlar/components/ln-popover/src/ln-popover.js).
127
+
128
+ ---
129
+
130
+ ## Related
131
+ - **[`ln-dropdown`](../ln-dropdown/README.md)** — Menu wrapper adding click-outside/top-layer promotion.
132
+ - **[`ln-toggle`](../ln-toggle/README.md)** — Binary disclosure state primitive.
133
+
134
+ ---
135
+
136
+ ## 🔧 Internals
137
+
138
+ Source: `components/ln-popover/ln-popover.js`.
139
+
140
+ ### Open-stack
141
+
142
+ A module-level `openStack` array tracks all currently-open instances. It drives two behaviors: a single `document` `keydown` listener, registered only while the stack is non-empty, pops the top entry and calls `.close()` on it (LIFO Escape); and nested popovers stay independent because each owns its own outside-click listener scoped to its own subtree.
143
+
144
+ ### `_applyOpen(trigger)` — after the cancelable `before-open` passes
145
+
146
+ Set `isOpen = true`, store `trigger` (`null` if opened via direct attribute mutation) and `document.activeElement` (for focus restore) → promotes the element to the top layer via `showPopover()` (so `position: fixed` coordinates are reliable regardless of ancestor `transform`, `contain`, or `overflow: hidden`) → `measureHidden(dom)` for dimensions even if `display: none` → `computePlacement(triggerRect, size, preferred, 8)` writes `top`/`left` inline (unavoidable for floating UI) and `data-ln-popover-placement` → `aria-expanded="true"` on trigger → focus the first focusable child, or the container itself → outside-click listener registered one tick later via `setTimeout` (so the opening click itself doesn't immediately close it) → scroll/resize listeners for repositioning while open → push onto `openStack`, ensure ESC listener → dispatch `ln-popover:open`.
147
+
148
+ ### `_applyClose()` — mirror of `_applyOpen`
149
+
150
+ Clears `isOpen`, removes the outside-click/scroll/resize listeners, clears inline `top`/`left` and the placement attribute, sets `aria-expanded="false"`, exits the top layer via `hidePopover()`, splices itself from `openStack` (removing the ESC listener if the stack is now empty), then restores focus to the trigger **only if** the trigger was the previously-focused element (click→Escape, click→programmatic close) or `activeElement === document.body` (Escape from inside the popover, outside-click on inert whitespace) — an outside-click landing on another focusable element keeps that element's focus. Dispatches `ln-popover:close`.
151
+
152
+ ### Focus management
153
+
154
+ A disclosure pattern, not a modal — no Tab trap. Because the popover is promoted to the top layer purely for rendering, it remains in its authored DOM position, so Tab order follows the markup rather than jumping to the end of `<body>`. `tabindex="-1"` and `role="dialog"` are set automatically at construction so the container itself is a valid focus target when there's no focusable child.
155
+
156
+ ### Nested popovers
157
+
158
+ Outside-click tests `self.dom.contains(e.target)`, so a click inside A's subtree (including a button that opens B) never triggers A's close path. Both popovers land on `openStack`; LIFO Escape closes B first, then A on the next press. There is no "close all."
159
+
160
+ ### Observer & registration
161
+
162
+ Two separate `registerComponent` calls wire popover elements (`el.lnPopover`) and trigger elements (`el.lnPopoverTrigger`) independently — `registerComponent` handles DOM discovery and lifecycle. On attribute mutations to `data-ln-popover`, `registerComponent`'s `onAttributeChange` callback evaluates open/closed transitions and dispatches `before-open` / `before-close` events. Trigger init sets `aria-haspopup="dialog"`, `aria-expanded="false"`, and `aria-controls` once on attach.
163
+
164
+ `destroy()` on a popover instance closes it if open, removes its listeners, and splices it from `openStack`. Trigger `destroy` is independent and only removes the trigger's click handler.
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Popover Missing ID: popovers controlled by data-ln-popover must have an id for trigger linking
5
+ [data-ln-popover]:not([id]) {
6
+ @include dev-dom-error("[data-ln-popover] is missing a required 'id' attribute for trigger linking.");
7
+ }
8
+
9
+ // 2. Popover Trigger Empty ID: trigger elements must point to a target popover ID
10
+ [data-ln-popover-for=""] {
11
+ @include dev-inline-error("data-ln-popover-for target ID cannot be empty");
12
+ }
13
+ }