@livenetworks/ashlar 1.6.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +202 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -0,0 +1,321 @@
1
+ import { compareValues, detectValueType, dispatchCancelable, getLocale, hashGet, hashSet, hashSortDecode, hashSortEncode, queueBoot, readValue, registerComponent, resolveHashNamespace, attrStr, attrSpec, defineAttrs } from '../../ln-core';
2
+ import { createSortComparator, getAriaSortValue, isExcludedSortItem, isSameSortTarget, normalizeSortDirection, resolveSortItems } from './sort-model.js';
3
+
4
+ (function () {
5
+ const DOM_SELECTOR = 'data-ln-sort';
6
+ const DOM_ATTRIBUTE = 'lnSort';
7
+ const FIELD_ATTR = 'data-ln-sort-field';
8
+ const STATE_ATTR = 'data-ln-sort-state';
9
+ const DIR_ATTR = 'data-ln-sort-dir';
10
+ const HASH_ATTR = 'data-ln-hash';
11
+
12
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
13
+
14
+ function _readOrNull(el, name) {
15
+ return el.getAttribute(name) || null;
16
+ }
17
+
18
+ // ─── Attribute Contract (SSOT) ──────────────────────────
19
+ const ATTRIBUTES = {
20
+ 'data-ln-sort': { prop: 'targetId', type: 'string', read: attrStr, fallback: null, description: 'Target table or list element ID to sort' },
21
+ 'data-ln-sort-field': { prop: 'field', type: 'string', read: _readOrNull, effect: _syncAttribute, description: 'Field name or column key to sort by' },
22
+ 'data-ln-sort-dir': { type: 'enum', values: ['asc', 'desc'], fallback: 'asc', description: 'Default or requested sort direction' },
23
+ 'data-ln-sort-items': { prop: 'itemsSelector', type: 'string', read: _readOrNull, description: 'CSS selector matching sortable child items' },
24
+ 'data-ln-sort-state': { type: 'enum', values: ['asc', 'desc', 'none'], fallback: 'none', effect: _syncAttribute, description: 'Active sort state applied to column or control' },
25
+ 'data-ln-hash': { type: 'string', effect: _syncAttribute, description: 'URL hash routing key for sort state persistence' }
26
+ };
27
+
28
+ const ATTR_SPEC = attrSpec(ATTRIBUTES);
29
+
30
+ // Target-scoped initial DOM order cache.
31
+ const _targetInitialOrders = new WeakMap();
32
+
33
+ function _readItemValue(item, field, column) {
34
+ if (field) {
35
+ const el = item.querySelector('[data-ln-field="' + field + '"]');
36
+ return el ? readValue(el) : '';
37
+ }
38
+ if (column !== null && column !== undefined && item.cells && item.cells[column]) {
39
+ return readValue(item.cells[column]);
40
+ }
41
+ return readValue(item);
42
+ }
43
+
44
+ // ─── Component ─────────────────────────────────────────────
45
+
46
+ function _component(dom) {
47
+ this.dom = dom;
48
+ defineAttrs(this, dom, ATTR_SPEC);
49
+
50
+ const th = dom.closest('th');
51
+ this.column = (!this.field && th) ? th.cellIndex : null;
52
+
53
+ this._state = normalizeSortDirection(dom.getAttribute(STATE_ATTR));
54
+ if (!dom.hasAttribute(STATE_ATTR)) {
55
+ dom.setAttribute(STATE_ATTR, this._state);
56
+ }
57
+ this._destroyed = false;
58
+
59
+ this.nsKey = resolveHashNamespace(dom, 'sort');
60
+ this.hashEnabled = !!this.nsKey;
61
+
62
+ const self = this;
63
+
64
+ this._onClick = function (e) {
65
+ const btn = e.target.closest('[' + DIR_ATTR + ']');
66
+ if (!btn) return;
67
+ const nextDir = normalizeSortDirection(btn.getAttribute(DIR_ATTR));
68
+ self._apply(nextDir);
69
+ };
70
+ dom.addEventListener('click', this._onClick);
71
+
72
+ this._onSortChange = function (e) {
73
+ if (self._destroyed || !e.detail) return;
74
+ const target = self._resolveTarget();
75
+ const isOurTarget = (target && (e.target === target || target.contains(e.target)))
76
+ || (e.detail.targetId && e.detail.targetId === self.targetId);
77
+ if (!isOurTarget) return;
78
+
79
+ const same = isSameSortTarget(
80
+ { field: self.field, column: self.column },
81
+ { field: e.detail.field, column: e.detail.column }
82
+ );
83
+
84
+ if (same) {
85
+ const nextDir = normalizeSortDirection(e.detail.direction);
86
+ if (nextDir && dom.getAttribute(STATE_ATTR) !== nextDir) {
87
+ self._state = nextDir;
88
+ dom.setAttribute(STATE_ATTR, nextDir);
89
+ self._updateAriaSort(nextDir);
90
+ }
91
+ return;
92
+ }
93
+
94
+ // Mutual exclusion: reset losing instances without re-dispatching
95
+ if (dom.getAttribute(STATE_ATTR) !== 'none') {
96
+ self._state = 'none';
97
+ dom.setAttribute(STATE_ATTR, 'none');
98
+ self._updateAriaSort('none');
99
+ }
100
+ };
101
+ document.addEventListener('ln-sort:change', this._onSortChange);
102
+
103
+ // ─── Hash change listener ──────────────────────────────────
104
+ this._onHashChange = function () {
105
+ if (self._destroyed || !self.hashEnabled) return;
106
+ const val = hashGet(self.nsKey);
107
+ const decoded = hashSortDecode(val);
108
+ if (decoded) {
109
+ const matches = (self.field !== null && decoded.fieldOrColumn === self.field)
110
+ || (self.column !== null && String(self.column) === decoded.fieldOrColumn);
111
+ if (matches) {
112
+ if (self._state !== decoded.direction) self._apply(decoded.direction, true);
113
+ } else {
114
+ if (self._state !== 'none') {
115
+ self._state = 'none';
116
+ dom.setAttribute(STATE_ATTR, 'none');
117
+ self._updateAriaSort('none');
118
+ }
119
+ }
120
+ } else {
121
+ if (self._state !== 'none') {
122
+ self._state = 'none';
123
+ dom.setAttribute(STATE_ATTR, 'none');
124
+ self._updateAriaSort('none');
125
+ const target = self._resolveTarget();
126
+ if (target) {
127
+ const evt = dispatchCancelable(target, 'ln-sort:change', {
128
+ field: self.field,
129
+ column: self.column,
130
+ direction: 'none',
131
+ targetId: self.targetId
132
+ });
133
+ if (!evt.defaultPrevented) self._defaultSort(target, 'none');
134
+ }
135
+ }
136
+ }
137
+ };
138
+
139
+ if (this.hashEnabled) {
140
+ window.addEventListener('hashchange', this._onHashChange);
141
+ }
142
+
143
+ // ─── Restore State on Boot ─────────────────────────────────
144
+ let restored = false;
145
+
146
+ if (this.hashEnabled) {
147
+ const hashVal = hashGet(this.nsKey);
148
+ const decoded = hashSortDecode(hashVal);
149
+ if (decoded) {
150
+ const matches = (self.field !== null && decoded.fieldOrColumn === self.field)
151
+ || (self.column !== null && String(self.column) === decoded.fieldOrColumn);
152
+ if (matches) {
153
+ queueBoot(function () {
154
+ if (self._destroyed) return;
155
+ self._apply(decoded.direction, true);
156
+ });
157
+ }
158
+ restored = true;
159
+ }
160
+ }
161
+
162
+ if (!restored) {
163
+ const initialDir = normalizeSortDirection(dom.getAttribute(STATE_ATTR));
164
+ if (initialDir && initialDir !== 'none') {
165
+ queueBoot(function () {
166
+ if (self._destroyed) return;
167
+ self._apply(initialDir, true);
168
+ });
169
+ }
170
+ }
171
+
172
+ return this;
173
+ }
174
+
175
+ _component.prototype._resolveTarget = function () {
176
+ return document.getElementById(this.targetId);
177
+ };
178
+
179
+ _component.prototype._updateAriaSort = function (direction) {
180
+ const th = this.dom.closest('th');
181
+ if (!th) return;
182
+ th.setAttribute('aria-sort', getAriaSortValue(direction));
183
+ };
184
+
185
+ _component.prototype._apply = function (direction, skipStorage) {
186
+ if (this._destroyed) return;
187
+ if (!this.field && this.column === null) {
188
+ const th = this.dom.closest('th');
189
+ if (th && th.cellIndex !== undefined) this.column = th.cellIndex;
190
+ }
191
+ const normalized = normalizeSortDirection(direction);
192
+ this._state = normalized;
193
+ if (this.dom.getAttribute(STATE_ATTR) !== normalized) {
194
+ this.dom.setAttribute(STATE_ATTR, normalized);
195
+ }
196
+
197
+ this._updateAriaSort(normalized);
198
+
199
+ const target = this._resolveTarget();
200
+ if (!target) return;
201
+
202
+ const detail = {
203
+ field: this.field,
204
+ column: this.column,
205
+ direction: normalized,
206
+ targetId: this.targetId
207
+ };
208
+
209
+ if (!skipStorage) {
210
+ if (this.hashEnabled) {
211
+ const encoded = hashSortEncode(this.field !== null ? this.field : this.column, normalized);
212
+ hashSet(this.nsKey, encoded);
213
+ }
214
+ }
215
+
216
+ const evt = dispatchCancelable(target, 'ln-sort:change', detail);
217
+ if (evt.defaultPrevented) return;
218
+
219
+ this._defaultSort(target, normalized);
220
+ };
221
+
222
+ // ─── Default DOM behaviour ─────────────────────────────────
223
+
224
+ _component.prototype._defaultSort = function (target, direction) {
225
+ const allItems = resolveSortItems(target, this.itemsSelector);
226
+ if (!allItems.length) return;
227
+ const parent = allItems[0].parentNode;
228
+
229
+ const items = allItems.filter(function (el) {
230
+ return !isExcludedSortItem(el);
231
+ });
232
+ if (!items.length) return;
233
+
234
+ if (!_targetInitialOrders.has(target)) {
235
+ _targetInitialOrders.set(target, items.slice());
236
+ }
237
+
238
+ let ordered;
239
+ if (direction === 'none') {
240
+ const original = _targetInitialOrders.get(target) || items;
241
+ _targetInitialOrders.delete(target);
242
+ ordered = original.filter(function (el) {
243
+ return el.parentNode === parent && !isExcludedSortItem(el);
244
+ });
245
+ } else {
246
+ const field = this.field;
247
+ const column = this.column;
248
+ const values = items.map(function (el) { return _readItemValue(el, field, column); });
249
+ const type = detectValueType(values);
250
+ const collator = typeof Intl !== 'undefined'
251
+ ? new Intl.Collator(getLocale(this.dom), { sensitivity: 'base', numeric: true })
252
+ : null;
253
+
254
+ const comparator = createSortComparator(direction, type, collator, function (el) {
255
+ return _readItemValue(el, field, column);
256
+ });
257
+
258
+ ordered = items.slice().sort(comparator);
259
+ }
260
+
261
+ const frag = document.createDocumentFragment();
262
+ let orderIdx = 0;
263
+ for (let i = 0; i < allItems.length; i++) {
264
+ const el = allItems[i];
265
+ if (isExcludedSortItem(el)) {
266
+ frag.appendChild(el);
267
+ } else if (orderIdx < ordered.length) {
268
+ frag.appendChild(ordered[orderIdx++]);
269
+ }
270
+ }
271
+ parent.appendChild(frag);
272
+ };
273
+
274
+ // ─── Destroy ───────────────────────────────────────────────
275
+
276
+ _component.prototype.destroy = function () {
277
+ if (this._destroyed) return;
278
+ this._destroyed = true;
279
+ this.dom.removeEventListener('click', this._onClick);
280
+ document.removeEventListener('ln-sort:change', this._onSortChange);
281
+ if (this.hashEnabled && this._onHashChange) {
282
+ window.removeEventListener('hashchange', this._onHashChange);
283
+ }
284
+ delete this.dom[DOM_ATTRIBUTE];
285
+ };
286
+
287
+ // ─── Attribute Sync ────────────────────────────────────────
288
+
289
+ function _syncAttribute(el, attrName) {
290
+ const instance = el[DOM_ATTRIBUTE];
291
+ if (!instance || instance._destroyed) return;
292
+ if (attrName === FIELD_ATTR) {
293
+ const th = el.closest('th');
294
+ instance.column = (!instance.field && th) ? th.cellIndex : null;
295
+ } else if (attrName === STATE_ATTR) {
296
+ const nextState = normalizeSortDirection(el.getAttribute(STATE_ATTR));
297
+ if (nextState !== instance._state) {
298
+ instance._apply(nextState);
299
+ }
300
+ } else if (attrName === HASH_ATTR) {
301
+ if (instance.hashEnabled && instance._onHashChange) {
302
+ window.removeEventListener('hashchange', instance._onHashChange);
303
+ }
304
+ instance.nsKey = resolveHashNamespace(el, 'sort');
305
+ instance.hashEnabled = !!instance.nsKey;
306
+ if (instance.hashEnabled) {
307
+ window.addEventListener('hashchange', instance._onHashChange);
308
+ }
309
+ }
310
+ }
311
+
312
+ // ─── Registration ──────────────────────────────────────────
313
+
314
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-sort', {
315
+ attributes: ATTRIBUTES,
316
+ persist: {
317
+ attr: STATE_ATTR,
318
+ hashActive: function (el) { return !!resolveHashNamespace(el, 'sort'); }
319
+ }
320
+ });
321
+ })();
@@ -0,0 +1,117 @@
1
+ import { compareValues } from '../../ln-core/compare.js';
2
+
3
+ export const SORT_DIRECTIONS = ['none', 'asc', 'desc'];
4
+
5
+ /**
6
+ * Normalizes a sort direction string to 'asc', 'desc', or 'none'.
7
+ * @param {unknown} value
8
+ * @returns {'asc' | 'desc' | 'none'}
9
+ */
10
+ export function normalizeSortDirection(value) {
11
+ const str = String(value || '').trim().toLowerCase();
12
+ if (str === 'asc' || str === 'ascending') return 'asc';
13
+ if (str === 'desc' || str === 'descending') return 'desc';
14
+ return 'none';
15
+ }
16
+
17
+ /**
18
+ * Returns the ARIA sort attribute value corresponding to the direction.
19
+ * @param {unknown} direction
20
+ * @returns {'ascending' | 'descending' | 'none'}
21
+ */
22
+ export function getAriaSortValue(direction) {
23
+ const normalized = normalizeSortDirection(direction);
24
+ if (normalized === 'asc') return 'ascending';
25
+ if (normalized === 'desc') return 'descending';
26
+ return 'none';
27
+ }
28
+
29
+ /**
30
+ * Determines whether two sort targets represent the identical sort key.
31
+ * @param {{ field?: string|null, column?: number|string|null }} a
32
+ * @param {{ field?: string|null, column?: number|string|null }} b
33
+ * @returns {boolean}
34
+ */
35
+ export function isSameSortTarget(a, b) {
36
+ if (!a || !b) return false;
37
+ if (a.field !== null && a.field !== undefined && b.field !== null && b.field !== undefined) {
38
+ return a.field === b.field;
39
+ }
40
+ if (a.column !== null && a.column !== undefined && b.column !== null && b.column !== undefined) {
41
+ return String(a.column) === String(b.column);
42
+ }
43
+ return false;
44
+ }
45
+
46
+ /**
47
+ * Creates a sort comparator function for sorting an array of records/items.
48
+ * @param {'asc' | 'desc' | 'none'} direction
49
+ * @param {'number' | 'string'} valueType
50
+ * @param {Intl.Collator|null} [collator]
51
+ * @param {(item: any) => any} [getValueFn]
52
+ * @returns {(a: any, b: any) => number}
53
+ */
54
+ export function createSortComparator(direction, valueType, collator, getValueFn) {
55
+ const normalized = normalizeSortDirection(direction);
56
+ if (normalized === 'none') return () => 0;
57
+
58
+ const multiplier = normalized === 'desc' ? -1 : 1;
59
+ const getter = typeof getValueFn === 'function' ? getValueFn : (item) => item;
60
+
61
+ return function (a, b) {
62
+ const valA = getter(a);
63
+ const valB = getter(b);
64
+ return compareValues(valA, valB, valueType, collator) * multiplier;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Determines whether a DOM element should be excluded from sorting (e.g. empty-state, hidden rows, templates).
70
+ * @param {Element|unknown} el
71
+ * @returns {boolean}
72
+ */
73
+ export function isExcludedSortItem(el) {
74
+ if (!el || typeof el !== 'object' || el.nodeType !== 1) return true;
75
+ if (el.tagName === 'TEMPLATE') return true;
76
+ if (typeof el.hasAttribute === 'function') {
77
+ if (el.hasAttribute('data-ln-sort-exclude') || el.hasAttribute('hidden')) return true;
78
+ }
79
+ if (el.classList && el.classList.contains('hidden')) return true;
80
+ if (el.style && el.style.display === 'none') return true;
81
+ if (typeof el.matches === 'function') {
82
+ if (el.matches('.empty-state, .ln-table__empty, .ln-table__empty-state, [data-ln-empty], [data-ln-empty-state], [data-ln-table-empty]')) {
83
+ return true;
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Resolves the list of sortable DOM items from a target element.
91
+ * For tables without explicit selector, automatically targets the tbody rows.
92
+ * @param {Element|unknown} target
93
+ * @param {string|null} [itemsSelector]
94
+ * @returns {Element[]}
95
+ */
96
+ export function resolveSortItems(target, itemsSelector) {
97
+ if (!target || typeof target !== 'object' || target.nodeType !== 1) return [];
98
+ const selector = itemsSelector
99
+ || (typeof target.getAttribute === 'function' ? target.getAttribute('data-ln-sort-items') : null)
100
+ || null;
101
+ if (selector && typeof target.querySelectorAll === 'function') {
102
+ return Array.from(target.querySelectorAll(selector));
103
+ }
104
+ if (target.tagName === 'TABLE') {
105
+ const tbody = target.tBodies && target.tBodies.length
106
+ ? target.tBodies[0]
107
+ : (typeof target.querySelector === 'function' ? target.querySelector('tbody') : null);
108
+ if (tbody) {
109
+ return Array.from(tbody.children || []);
110
+ }
111
+ if (typeof target.querySelectorAll === 'function') {
112
+ return Array.from(target.querySelectorAll('tbody tr, tr'));
113
+ }
114
+ }
115
+ return Array.from(target.children || []);
116
+ }
117
+
@@ -1,8 +1,8 @@
1
1
  # ln-sortable
2
2
 
3
- A zero-dependency, high-performance **Drag & Drop Reordering Primitive** driven by browser Pointer Events APIs, designed for seamless mouse and touch interactions.
4
-
5
- It focuses strictly on visual DOM restructuring, delegating server-side synchronization, database persistence, and list state saves completely to the parent coordinator via telemetry events.
3
+ > Applied to a `<ul data-ln-sortable>` or `<ol>` list. It listens for `pointerdown`, `pointermove`, and `pointerup` events
4
+ > on list items (or `[data-ln-sortable-handle]`). On drag, it adds `.ln-sortable--dragging`, repositions items in the DOM via
5
+ > `insertBefore`, and places a placeholder element. On release, it removes drag classes, purges the placeholder, and emits an `ln-sortable:change` CustomEvent.
6
6
 
7
7
  ---
8
8
 
@@ -10,7 +10,7 @@ It focuses strictly on visual DOM restructuring, delegating server-side synchron
10
10
 
11
11
  1. **Pointer Concurrency:** Built natively on Pointer Events APIs (`pointerdown`, `pointermove`, `pointerup`). It replaces heavy HTML5 Drag & Drop frameworks, offering high-performance dragging and layout shifts across desktop, mobile, and hybrid touch displays.
12
12
  2. **Visual Isolation:** The primitive does not import or inject inline styles or layout rules. Instead, it exposes CSS state hooks (e.g. `.ln-sortable--dragging`) on active elements, leaving transitions and placeholders fully to the stylesheet.
13
- 3. **HTML Attribute as Single Source of Truth:** Component states are governed entirely by the `data-ln-sortable` attribute. Standard JS API calls (`enable()`, `disable()`) write directly to the attribute, which is observed via `MutationObserver` to coordinate internals.
13
+ 3. **HTML Attribute as Single Source of Truth:** Component states are governed entirely by the `data-ln-sortable` attribute. Writing the attribute is the only way to enable or disable the component; the write is observed via `MutationObserver` to coordinate internals.
14
14
 
15
15
  ---
16
16
 
@@ -87,11 +87,7 @@ Fired when an item drop successfully changes the DOM index order.
87
87
  - **Payload (`detail`)**: `{ item: HTMLElement, oldIndex: number, newIndex: number }`
88
88
 
89
89
  ### `ln-sortable:enabled` / `ln-sortable:disabled`
90
- Fired when `data-ln-sortable` toggles between enabled and `"disabled"` (via `enable()`/`disable()` or a direct attribute write, picked up by the shared `MutationObserver`).
91
- - **Payload (`detail`)**: `{ target: HTMLElement }`
92
-
93
- ### `ln-sortable:destroyed`
94
- Fired inside `destroy()`, after the `pointerdown` listener is removed.
90
+ Fired when `data-ln-sortable` toggles between enabled and `"disabled"` — written directly on the host and picked up by the shared `MutationObserver`.
95
91
  - **Payload (`detail`)**: `{ target: HTMLElement }`
96
92
 
97
93
  ---
@@ -110,7 +106,7 @@ Fired inside `destroy()`, after the `pointerdown` listener is removed.
110
106
 
111
107
  ## 🔧 Internals
112
108
 
113
- Source: `js/ln-sortable/ln-sortable.js`. Each `[data-ln-sortable]` container gets a `_component` instance at `element.lnSortable` (`dom`, `isEnabled`, `_dragging` — the child currently being dragged, or `null` — and the bound `pointerdown` handler).
109
+ Source: `components/ln-sortable/ln-sortable.js`. Each `[data-ln-sortable]` container gets a `_component` instance at `element.lnSortable` (`dom`, `isEnabled`, `_dragging` — the child currently being dragged, or `null` — and the bound `pointerdown` handler).
114
110
 
115
111
  ### Handle resolution
116
112
 
@@ -126,7 +122,7 @@ On `pointerdown`: if the target (or an ancestor) carries `data-ln-sortable-handl
126
122
 
127
123
  ### Attribute-driven enable/disable
128
124
 
129
- `data-ln-sortable` (absent value) = enabled, `data-ln-sortable="disabled"` = disabled. `enable()`/`disable()` write the attribute; the shared `MutationObserver` picks up the change, updates `isEnabled` inline, and dispatches `ln-sortable:enabled`/`ln-sortable:disabled`. No cancelable before-event for enable/disable — the state change is immediate.
125
+ `data-ln-sortable` (absent value) = enabled, `data-ln-sortable="disabled"` = disabled. The shared `MutationObserver` picks up the attribute write, updates `isEnabled` inline, and dispatches `ln-sortable:enabled`/`ln-sortable:disabled`. No cancelable before-event — the state change is immediate.
130
126
 
131
127
  ### MutationObserver
132
128
 
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Host Container Check: data-ln-sortable belongs on list or grid containers
5
+ [data-ln-sortable]:not(ul, ol, div, tbody) {
6
+ @include dev-inline-error("data-ln-sortable belongs on <ul>, <ol>, <tbody>, or <div> containers");
7
+ }
8
+
9
+ // 2. Handle Nesting Check: sortable handles must be inside a sortable container
10
+ [data-ln-sortable-handle]:not([data-ln-sortable] *) {
11
+ @include dev-inline-error("[data-ln-sortable-handle] is orphaned (not inside a [data-ln-sortable] container)");
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function O(){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 y(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 i=null;for(let s=1;s<t.length;s++)if(t[s]&&t[s].nodeType===1){i=t[s];break}if(!y(i))return}e.apply(console,t)}}function C(e,t,n){const i=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,i),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:i}))}function j(e,t,n){const i=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,i);const s=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:i});return e.dispatchEvent(s),s}function m(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){m(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const D={};function N(e,t){D[e]=t}function P(e){return D[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=N,window.lnCore.getDataMapper=P);function V(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,m(function(){new MutationObserver(function(){C(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const T={};function H(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];T[n]=t}function U(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return T[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=H,window.lnCore.getLocaleFallback=U,window.lnCore.ensureLocaleObserver=V);const A={};function Q(e,t){A[e]||(A[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=A[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function $(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let o=0;o<n.length;o++){const l=n[o],r=l.getAttribute("data-ln-field");t[r]!=null&&(l.textContent=t[r])}const i=e.querySelectorAll("[data-ln-attr]");for(let o=0;o<i.length;o++){const l=i[o],r=l.getAttribute("data-ln-attr").split(",");for(let a=0;a<r.length;a++){const c=r[a].trim().split(":");if(c.length!==2)continue;const u=c[0].trim(),f=c[1].trim();t[f]!=null&&l.setAttribute(u,t[f])}}const s=e.querySelectorAll("[data-ln-show]");for(let o=0;o<s.length;o++){const l=s[o],r=l.getAttribute("data-ln-show");r in t&&l.classList.toggle("hidden",!t[r])}const d=e.querySelectorAll("[data-ln-class]");for(let o=0;o<d.length;o++){const l=d[o],r=l.getAttribute("data-ln-class").split(",");for(let a=0;a<r.length;a++){const c=r[a].trim().split(":");if(c.length!==2)continue;const u=c[0].trim(),f=c[1].trim();f in t&&l.classList.toggle(u,!!t[f])}}return e}function K(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 i=0;i<n.length;i++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[i],t??null),n[i].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 I(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const d=n.currentNode;d.textContent.indexOf("{{")!==-1&&(d.textContent=d.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(o,l){return t[l]!==void 0?t[l]:""}))}const i=function(d,o){return t[o]!==void 0?t[o]:""},s=Array.from(e.querySelectorAll("*"));e.nodeType===1&&s.push(e);for(let d=0;d<s.length;d++){const o=s[d],l=o.attributes;for(let r=0;r<l.length;r++){const a=l[r];a.value.indexOf("{{")!==-1&&o.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,i))}}return e}function Y(e,t,n,i,s,d){const o={};for(let r=0;r<e.children.length;r++){const a=e.children[r],c=a.getAttribute("data-ln-render-key");c&&(o[c]=a)}const l=document.createDocumentFragment();for(let r=0;r<t.length;r++){const a=t[r],c=String(i(a));let u=o[c];if(u)s(u,a,r);else{const f=Q(n,d);if(!f||(I(f,a),u=f.firstElementChild,!u))continue;u.setAttribute("data-ln-render-key",c),s(u,a,r)}l.appendChild(u)}e.textContent="",e.appendChild(l)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=I,window.lnCore.fill=$,window.lnCore.lnFill=K,window.lnCore.renderList=Y);function R(e,t,n,i,s){if(!e||t===null)return!0;const d=i||"ln-component",o=e.type;if(o==="trigger"||o==="marker"||o==="string"||o==="list"||!o||t===""&&e.fallback!==void 0)return!0;const l=r=>{s?console.warn(r,s):console.warn(r)};if(o==="boolean"){const r=t.trim().toLowerCase();return r!==""&&r!=="true"&&r!=="false"&&r!=="1"&&r!=="0"?(l(`[${d}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(o==="enum"){const r=e.values||[];return r.includes(t)?!0:(l(`[${d}] Invalid value "${t}" for attribute "${n}". Allowed: ${r.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(o==="integer"){if(!/^-?\d+$/.test(t))return l(`[${d}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const r=parseInt(t,10);return e.min!==void 0&&r<e.min?(l(`[${d}] Value ${r} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&r>e.max?(l(`[${d}] Value ${r} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(o==="float")return isNaN(Number(t))?(l(`[${d}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(o==="json")try{return JSON.parse(t),!0}catch(r){return l(`[${d}] Invalid JSON for attribute "${n}": ${r.message}. Fallback: ${e.fallback}.`),!1}return!0}function G(e){const t={};for(const n in e){const i=e[n];i&&i.effect&&(t[n]=i.effect)}return Object.keys(t).length?t:null}function J(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function v(e,t,n,i){if(e.nodeType!==1)return;const d=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",o=Array.from(e.querySelectorAll(d));e.matches&&e.matches(d)&&o.push(e);for(const l of o)if(!l[n]){window.lnCore._persistSink&&l.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(l,t);try{l[n]=new i(l)}catch(r){console.error("["+n+"] init failed",l,r)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function X(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function z(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function q(){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 Z(e){const t=q(),n=e.observed||[];for(let i=0;i<n.length;i++){const s=n[i];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 i in e.attributes){t.byDeclaredAttr.has(i)||t.byDeclaredAttr.set(i,[]);const s=t.byDeclaredAttr.get(i);s.some(d=>d.componentTag===e.componentTag)||s.push({spec:e.attributes[i],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const i=J(e);if(i===null)t.reactiveWildcard.push(e);else for(const s of i)t.byReactive.has(s)||t.byReactive.set(s,[]),t.byReactive.get(s).push(e)}e.persist&&t.persist.push(e)}function M(e,t,n,i){for(let s=0;s<e.length;s++){const d=e[s];if(!t[d.attribute])continue;const o=d.effects&&d.effects[n];o?o(t,n,i):d.onAttrChange&&(!d.declared||d.declared.has(n))&&d.onAttrChange(t,n,i)}}function ee(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const i=q(),s=i.byAttr.get(n);if(O()&&i.byDeclaredAttr&&i.byDeclaredAttr.has(n)&&y(t)){const d=i.byDeclaredAttr.get(n),o=t.getAttribute(n);for(let l=0;l<d.length;l++)R(d[l].spec,o,n,d[l].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 d=i.byReactive.get(n);d&&M(d,t,n,e.oldValue),i.reactiveWildcard.length&&M(i.reactiveWildcard,t,n,e.oldValue)}if(s)for(let d=0;d<s.length;d++){const o=s[d];if(o.handler){o.handler(t,n,e.oldValue);continue}o.onAttributeChange&&t[o.attribute]?o.onAttributeChange(t,n):(v(t,o.selector,o.attribute,o.ComponentFn),o.onInit&&o.onInit(t))}}function te(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{ee(t[n])}catch(i){console.error("[ln-core] mutation handler failed",t[n].target,i)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function B(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function ne(e){const t=B();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const i=t[n];if(i.onSubtreeChange){const s=i.query,d=e.target.nodeType===1?e.target.matches(s)?e.target:e.target.closest(s):e.target.parentElement?e.target.parentElement.closest(s):null;d&&i.onSubtreeChange(d,e)}}for(let n=0;n<e.addedNodes.length;n++){const i=e.addedNodes[n];if(i.nodeType===1)for(let s=0;s<t.length;s++){const d=t[s];v(i,d.selector,d.attribute,d.ComponentFn),d.onInit&&d.onInit(i)}}for(let n=0;n<e.removedNodes.length;n++){const i=e.removedNodes[n];if(i.nodeType===1)for(let s=0;s<t.length;s++){const d=t[s],o=d.query,l=Array.from(i.querySelectorAll(o));i.matches&&i.matches(o)&&l.push(i);for(let r=0;r<l.length;r++){const a=l[r];if(!document.contains(a)){const c=a[d.attribute];if(c&&typeof c.destroy=="function")try{c.destroy()}catch(u){console.error("["+d.attribute+"] destroy failed",a,u)}delete a[d.attribute]}}}}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const i=t[n];if(i.type==="childList")try{ne(i)}catch(s){console.error("[ln-core] lifecycle handler failed",i.target,s)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function oe(e,t,n,i,s={}){const d=s.extraAttributes||[],o=s.onAttributeChange||null,l=s.onSubtreeChange||null,r=s.onInit||null,a=s.onAttrChange||null,c=s.effects||null,u=s.attributes||null,f=u?G(u):c,h=u?new Set(Object.keys(u)):null,g=s.persist||null;function b(k){const w=k||document.body;if(v(w,e,t,n),u&&O())for(const p in u){const be=u[p],S=Array.from(w.querySelectorAll("["+p+"]"));w.matches&&w.matches("["+p+"]")&&S.push(w);for(let L=0;L<S.length;L++){const x=S[L];y(x)&&R(be,x.getAttribute(p),p,i,x)}}r&&r(w)}const E=[];if(e.indexOf("[")!==-1){const k=/\[([\w-]+)/g;let w;for(;(w=k.exec(e))!==null;)E.push(w[1])}else E.push(e);Z({selector:e,attribute:t,componentTag:i,attributes:u,ComponentFn:n,onInit:r,observed:E.concat(d),onAttributeChange:o,onAttrChange:a,effects:f,declared:h,persist:g}),te();const we=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";B().push({selector:e,attribute:t,ComponentFn:n,onInit:r,onSubtreeChange:l,query:we}),re(),window[t]=b;function W(){X()>0?z(function(){b(document.body)}):b(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W):W(),b}function F(e){return(e||"").replace(/^#/,"")}function _(e){const t=e===void 0?location.hash:e,n={},i=F(t);if(!i)return n;const s=i.split("&");for(let d=0;d<s.length;d++){const o=s[d];if(!o)continue;const l=o.indexOf(":"),r=l>-1?o.slice(0,l):o,a=l>-1?o.slice(l+1):"";if(r)try{n[r]=decodeURIComponent(a)}catch{n[r]=a}}return n}function ie(e){if(!e)return null;const t=_();return e in t?t[e]:null}function le(e,t){if(!e)return;const n=_();t==null?delete n[e]:n[e]=String(t);const s=Object.keys(n).map(function(d){const o=n[d];return o===""?d:d+":"+encodeURIComponent(o)}).join("&");F(location.hash)!==s&&(location.hash=s)}function se(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function de(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 i=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return i?t?i+"-"+t:i:t||null}function ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function ce(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 ue(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function fe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),i=e.slice(t+1),s=i?i.split(",").map(function(d){try{return decodeURIComponent(d)}catch{return d}}).filter(Boolean):[];return{key:n,values:s}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=_,window.lnCore.hashGet=ie,window.lnCore.hashSet=le,window.lnCore.hashLinkClick=se,window.lnCore.resolveHashNamespace=de,window.lnCore.hashSortEncode=ae,window.lnCore.hashSortDecode=ce,window.lnCore.hashFilterEncode=ue,window.lnCore.hashFilterDecode=fe),(function(){const e="data-ln-sortable",t="lnSortable",n="data-ln-sortable-handle";if(window[t]!==void 0)return;const i={"data-ln-sortable":{effect:d,type:"enum",values:["enabled","disabled"],fallback:"enabled",description:"Enables drag-and-drop item reordering or disables when set to disabled"},"data-ln-sortable-handle":{type:"marker",description:"Designates an element as the drag handle for its parent sortable item"}};function s(o){this.dom=o,this.isEnabled=o.getAttribute(e)!=="disabled",this._dragging=null,o.setAttribute("aria-roledescription","sortable list");const l=this;return this._onPointerDown=function(r){l.isEnabled&&l._handlePointerDown(r)},o.addEventListener("pointerdown",this._onPointerDown),this}s.prototype.destroy=function(){this.dom[t]&&(this.dom.removeEventListener("pointerdown",this._onPointerDown),delete this.dom[t])},s.prototype._handlePointerDown=function(o){let l=o.target.closest("["+n+"]"),r;if(l){for(r=l;r&&r.parentElement!==this.dom;)r=r.parentElement;if(!r||r.parentElement!==this.dom)return}else{if(this.dom.querySelector("["+n+"]"))return;for(r=o.target;r&&r.parentElement!==this.dom;)r=r.parentElement;if(!r||r.parentElement!==this.dom)return;l=r}const c=Array.from(this.dom.children).indexOf(r);if(j(this.dom,"ln-sortable:before-drag",{item:r,index:c}).defaultPrevented)return;o.preventDefault(),l.setPointerCapture(o.pointerId),this._dragging=r,r.classList.add("ln-sortable--dragging"),r.setAttribute("aria-grabbed","true"),this.dom.classList.add("ln-sortable--active"),C(this.dom,"ln-sortable:drag-start",{item:r,index:c});const f=this,h=function(b){f._handlePointerMove(b)},g=function(b){f._handlePointerEnd(b),l.removeEventListener("pointermove",h),l.removeEventListener("pointerup",g),l.removeEventListener("pointercancel",g)};l.addEventListener("pointermove",h),l.addEventListener("pointerup",g),l.addEventListener("pointercancel",g)},s.prototype._handlePointerMove=function(o){if(!this._dragging)return;const l=Array.from(this.dom.children),r=this._dragging;for(const a of l)a.classList.remove("ln-sortable--drop-before","ln-sortable--drop-after");for(const a of l){if(a===r)continue;const c=a.getBoundingClientRect(),u=c.top+c.height/2;if(o.clientY>=c.top&&o.clientY<u){a.classList.add("ln-sortable--drop-before");break}else if(o.clientY>=u&&o.clientY<=c.bottom){a.classList.add("ln-sortable--drop-after");break}}},s.prototype._handlePointerEnd=function(o){if(!this._dragging)return;const l=this._dragging,r=Array.from(this.dom.children),a=r.indexOf(l);let c=null,u=null;for(const f of r){if(f.classList.contains("ln-sortable--drop-before")){c=f,u="before";break}if(f.classList.contains("ln-sortable--drop-after")){c=f,u="after";break}}for(const f of r)f.classList.remove("ln-sortable--drop-before","ln-sortable--drop-after");if(l.classList.remove("ln-sortable--dragging"),l.removeAttribute("aria-grabbed"),this.dom.classList.remove("ln-sortable--active"),c&&c!==l){u==="before"?this.dom.insertBefore(l,c):this.dom.insertBefore(l,c.nextElementSibling);const h=Array.from(this.dom.children).indexOf(l);C(this.dom,"ln-sortable:reordered",{item:l,oldIndex:a,newIndex:h})}this._dragging=null};function d(o){const l=o[t];if(!l)return;const r=o.getAttribute(e)!=="disabled";r!==l.isEnabled&&(l.isEnabled=r,C(o,r?"ln-sortable:enabled":"ln-sortable:disabled",{target:o}))}oe(e,t,s,"ln-sortable",{attributes:i})})()})();
@@ -0,0 +1,36 @@
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-sortable",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-sortable-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-sortable": {
13
+ "direction": "author",
14
+ "sources": [
15
+ "ln-sortable-dev.scss",
16
+ "src/ln-sortable.js"
17
+ ],
18
+ "type": "enum",
19
+ "values": [
20
+ "enabled",
21
+ "disabled"
22
+ ],
23
+ "fallback": "enabled",
24
+ "description": "Enables drag-and-drop item reordering or disables when set to disabled"
25
+ },
26
+ "data-ln-sortable-handle": {
27
+ "direction": "author",
28
+ "sources": [
29
+ "ln-sortable-dev.scss",
30
+ "src/ln-sortable.js"
31
+ ],
32
+ "type": "marker",
33
+ "description": "Designates an element as the drag handle for its parent sortable item"
34
+ }
35
+ }
36
+ }
@@ -9,6 +9,12 @@ import { registerComponent, dispatch, dispatchCancelable } from '../../ln-core';
9
9
 
10
10
  if (window[DOM_ATTRIBUTE] !== undefined) return;
11
11
 
12
+ // ─── Attribute Contract (SSOT) ──────────────────────────
13
+ const ATTRIBUTES = {
14
+ 'data-ln-sortable': { effect: _syncEnabled, type: 'enum', values: ['enabled', 'disabled'], fallback: 'enabled', description: 'Enables drag-and-drop item reordering or disables when set to disabled' },
15
+ 'data-ln-sortable-handle': { type: 'marker', description: 'Designates an element as the drag handle for its parent sortable item' }
16
+ };
17
+
12
18
  // ─── Component ─────────────────────────────────────────────
13
19
 
14
20
  function _component(dom) {
@@ -29,22 +35,11 @@ import { registerComponent, dispatch, dispatchCancelable } from '../../ln-core';
29
35
  return this;
30
36
  }
31
37
 
32
- // ─── Public API ────────────────────────────────────────────
33
-
34
- _component.prototype.enable = function () {
35
- if (this.isEnabled) return;
36
- this.dom.setAttribute(DOM_SELECTOR, '');
37
- };
38
-
39
- _component.prototype.disable = function () {
40
- if (!this.isEnabled) return;
41
- this.dom.setAttribute(DOM_SELECTOR, 'disabled');
42
- };
38
+ // ─── Lifecycle ─────────────────────────────────────────────
43
39
 
44
40
  _component.prototype.destroy = function () {
45
41
  if (!this.dom[DOM_ATTRIBUTE]) return;
46
42
  this.dom.removeEventListener('pointerdown', this._onPointerDown);
47
- dispatch(this.dom, 'ln-sortable:destroyed', { target: this.dom });
48
43
  delete this.dom[DOM_ATTRIBUTE];
49
44
  };
50
45
 
@@ -198,6 +193,6 @@ import { registerComponent, dispatch, dispatchCancelable } from '../../ln-core';
198
193
  // ─── Init ──────────────────────────────────────────────────
199
194
 
200
195
  registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-sortable', {
201
- onAttributeChange: _syncEnabled
196
+ attributes: ATTRIBUTES
202
197
  });
203
198
  })();
@@ -1,5 +1,7 @@
1
1
  # ln-stat
2
2
 
3
+ > Applied to an inline or block element (`<strong data-ln-stat="storeName" data-ln-stat-filter="active:true">`). On initialization, it bubbles an `ln-stat:request-count` CustomEvent to `ln-data-coordinator` and listens for `ln-stat:set-count`, updating its `textContent` with the resolved number and removing the `.is-loading` class. It stores zero count state internally, relying entirely on the coordinator to re-deliver counts on store mutations.
4
+
3
5
  Displays a live count from an `ln-data-store` via the `ln-data-coordinator` binder. Updates automatically on every store mutation. Opt-in: add `data-ln-stat="<storeName>"` to any inline element.
4
6
 
5
7
  ---
@@ -52,4 +54,4 @@ The `is-loading` class is removed on first delivery — add it to show a loading
52
54
 
53
55
  ## 🔧 Internals
54
56
 
55
- Source: `js/ln-stat/ln-stat.js`. The coordinator's `_serveStat` method handles `ln-stat:request-count`: reads `data-ln-stat` from the dispatching element, guards with `_ownsStore(name)`, calls `store.count(filters)` (`filters` from `e.detail.filters`), and dispatches `ln-stat:set-count { count }` back on the element. On every store mutation, the coordinator's `_refreshAll()` re-parses `data-ln-stat-filter` from each stat element and re-calls `store.count()` — the component itself holds no count state between deliveries.
57
+ Source: `components/ln-stat/ln-stat.js`. The coordinator's `_serveStat` method handles `ln-stat:request-count`: reads `data-ln-stat` from the dispatching element, guards with `_ownsStore(name)`, calls `store.count(filters)` (`filters` from `e.detail.filters`), and dispatches `ln-stat:set-count { count }` back on the element. On every store mutation, the coordinator's `_refreshAll()` re-parses `data-ln-stat-filter` from each stat element and re-calls `store.count()` — the component itself holds no count state between deliveries.
@@ -0,0 +1,8 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Metric Store Name Check: data-ln-stat cannot have an empty store name
5
+ [data-ln-stat=""] {
6
+ @include dev-inline-error("data-ln-stat store name cannot be empty");
7
+ }
8
+ }