@livenetworks/ashlar 1.6.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +190 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -1,170 +1,178 @@
1
- # ln-sort
2
-
3
- A zero-dependency, event-driven **Sort Control Primitive** that announces a sort intent on a
4
- target element via a cancelable event, letting tables, lists, or custom integrations intercept
5
- it or fall back to a built-in DOM-reorder default.
6
-
7
- It follows the same contract shape as `ln-search`: dispatch-only-to-target, cancelable,
8
- default-DOM-behaviour fallback. Unlike `ln-filter`, it never dual-dispatches to itself.
9
-
10
- ---
11
-
12
- ## 🧭 Philosophy & Architecture
13
-
14
- 1. **No cycle logic in JS.** A click reads `data-ln-sort-dir` off the clicked button, writes it to
15
- `data-ln-sort-state` on the root, and dispatches. The circular order
16
- `none → asc → desc → none` is produced entirely by CSS — only one `<button data-ln-sort-dir>`
17
- is visible at a time, selected by `[data-ln-sort-state]` on the root. See
18
- `scss/config/mixins/_sort.scss`.
19
- 2. **Field is optional.** Author `data-ln-sort-field="name"` when the target is data-driven
20
- (server needs a name, not an index). Omit it inside a `<th>` to fall back to
21
- `th.cellIndex` — SSR/DOM-only, resolved once at construction, never bridged to `field`.
22
- The dispatched payload always carries both keys, exactly one non-null:
23
- `{ field: string|null, column: number|null, direction, targetId }`.
24
- 3. **Single sort, mutual exclusion via the target.** Every instance listens for
25
- `ln-sort:change` on its OWN target (not on itself — events bubble up, listening on self would
26
- never hear a sibling). When an incoming event's field/column doesn't match this instance's own,
27
- it resets its own `data-ln-sort-state` to `"none"`. No registry, no sibling awareness.
28
- 4. **Type is inferred once per sort, not per pair.** `ln-core.detectValueType` scans the current
29
- value set; if every non-empty value is a finite number, comparison is numeric, else
30
- `localeCompare` via `ln-core.getLocale`. Per-pair type inference breaks comparator
31
- transitivity and produces wrong ordering — never do it.
32
-
33
- ---
34
-
35
- ## ✅ Canonical Markup
36
-
37
- ```html
38
- <th>
39
- <span>Name</span>
40
- <ul data-ln-sort="users-table" data-ln-sort-field="name" data-ln-sort-state="none">
41
- <li><button type="button" data-ln-sort-dir="asc" aria-label="Sort ascending"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrows-sort"></use></svg></button></li>
42
- <li><button type="button" data-ln-sort-dir="desc" aria-label="Sort descending"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrow-up"></use></svg></button></li>
43
- <li><button type="button" data-ln-sort-dir="none" aria-label="Remove sort"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrow-down"></use></svg></button></li>
44
- </ul>
45
- </th>
46
- ```
47
-
48
- **Icon convention — READ BEFORE COPYING THIS MARKUP.** Each button carries the icon of the sort
49
- STATE it is visible in, never the icon of the direction it sets:
50
-
51
- | Button | Visible when `data-ln-sort-state` = | Icon it carries |
52
- |---|---|---|
53
- | `data-ln-sort-dir="asc"` | `none` | `#ln-icon-arrows-sort` |
54
- | `data-ln-sort-dir="desc"` | `asc` | `#ln-icon-arrow-up` |
55
- | `data-ln-sort-dir="none"` | `desc` | `#ln-icon-arrow-down` |
56
-
57
- **This is not a typo.** The `dir="desc"` button contains an UP arrow. The visible button always
58
- performs the NEXT action (clicking `dir="desc"` sorts descending), but its icon always reports the
59
- CURRENT state (the column is currently ascending — hence the up arrow). If the icon instead
60
- matched the button's own `data-ln-sort-dir`, the header would visually lie about its own sort
61
- order the instant the column reaches `state="asc"`: the user would see a down-arrow next to a
62
- column that is actually sorted up. Do not "fix" the icons to match `data-ln-sort-dir` when
63
- hand-authoring markup — that reintroduces the exact bug this convention exists to prevent. `content`
64
- cannot substitute for this — these icons are an SVG sprite via `<use href>`, not a font glyph a
65
- `::before { content }` rule could swap (see `docs/architecture/reference.md` §Icons).
66
-
67
- - **SSR table column** — omit `data-ln-sort-field`; the `<ul>` must be a descendant of the `<th>`
68
- so the `th.cellIndex` fallback resolves. `data-ln-sort-field` and the SSR index path are never
69
- bridged — setting a field on an SSR-table column leaves `column` null and `ln-table`'s SSR sort
70
- has nothing to key off. Don't do both.
71
- - **Data-driven table column** — set `data-ln-sort-field` to the same value as the `<th>`'s
72
- `data-ln-table-col`.
73
- - **Plain list** — target a generic `<ul>`/`<li>` container by `id`; set
74
- `data-ln-sort-items="<selector>"` for deep targeting (mirrors `data-ln-search-items`).
75
-
76
- ---
77
-
78
- ## 🛠️ Declarative API Contract
79
-
80
- ### HTML Attributes
81
-
82
- | Attribute | Elements | Description |
83
- | :--- | :--- | :--- |
84
- | `data-ln-sort` | `<ul>` (root) | Component root. Value is the `id` of the target being sorted. |
85
- | `data-ln-sort-field` | Same as root | Opt-in. The record field name (data-driven). Omit for the `th.cellIndex` fallback (SSR/DOM only). |
86
- | `data-ln-sort-state` | Same as root | *State*. `"none" \| "asc" \| "desc"`. Drives which trigger button is visible via CSS. |
87
- | `data-ln-sort-items` | Same as root | Opt-in. Deep CSS selector for default-DOM-behaviour reordering (mirrors `data-ln-search-items`). |
88
- | `data-ln-sort-dir` | `<button>` (inside root) | `"asc" \| "desc" \| "none"`. Identifies which action this trigger performs. |
89
- | `data-ln-persist` | Same as root | Opt-in. Persists `{ field, column, direction }` to `localStorage`. Give each `[data-ln-sort]` its own value (or `id`) — persistence is per-instance, not per-table. |
90
-
91
- ### JavaScript API (`el.lnSort`)
92
-
93
- | Member | Type | Description |
94
- | :--- | :--- | :--- |
95
- | `targetId` | `string` | The `id` of the target element. |
96
- | `field` | `string \| null` | The authored field name, or `null`. |
97
- | `column` | `number \| null` | The resolved `th.cellIndex` fallback, or `null`. |
98
- | `destroy()` | `() => void` | Removes listeners and tears down the instance. |
99
-
100
- ---
101
-
102
- ## ⚡ DOM Events
103
-
104
- ### `ln-sort:change`
105
-
106
- Dispatched on the **target** element (never on the sort control itself) whenever a trigger button
107
- is clicked, or on persisted-state restore.
108
- - **Cancelable**: Yes. `e.preventDefault()` disables the default DOM-reorder and hands the intent
109
- fully to the consumer (e.g. `ln-table`, `ln-list`).
110
- - **Payload (`detail`)**: `{ field: string|null, column: number|null, direction: 'asc'|'desc'|'none', targetId: string }`.
111
-
112
- ---
113
-
114
- ## ⚠️ Common Pitfalls
115
-
116
- - **Setting both `data-ln-sort-field` and expecting the index fallback to also work.** They are
117
- never bridged. Pick one per instance based on the mode (SSR → index fallback; data-driven →
118
- field).
119
- - **Multi-column sort.** Not supported — single sort only. Multiple `[data-ln-sort]` instances
120
- targeting the same target enforce mutual exclusion automatically (see Philosophy §3).
121
- - **Expecting JS to own the click cycle.** It doesn't — the circular order is CSS-driven via
122
- `[data-ln-sort-state]`. If the three trigger buttons aren't authored per the canonical markup,
123
- the cycle breaks visually even though the JS click handling still works.
124
- - **Persisting without a stable key.** `data-ln-persist` with no value falls back to `el.id`; a
125
- bare `<ul data-ln-sort data-ln-persist>` with no `id` silently skips persistence
126
- (see `ln-core.persistGet`/`persistSet` — `console.warn`s once).
127
-
128
- ---
129
-
130
- ## 🔧 Internals
131
-
132
- Source: `js/ln-sort/ln-sort.js`. One instance per `[data-ln-sort]`, stored at `element.lnSort`.
133
-
134
- ### The three-step click (no cycle logic)
135
-
136
- `_onClick` reads `data-ln-sort-dir` off the clicked button and calls `_apply(direction)`, which:
137
- writes `data-ln-sort-state` on the root, dispatches `ln-sort:change` on the target, and — only if
138
- not prevented — runs `_defaultSort`. There is no `if (current === 'asc') then 'desc'` branch
139
- anywhere in this file; the SCSS cycle in `scss/config/mixins/_sort.scss` decides which button is
140
- even clickable next.
141
-
142
- ### Mutual exclusion
143
-
144
- Each instance adds its `ln-sort:change` listener to the resolved **target** element (captured once
145
- at construction as `this._target`), not to itself. A self-originated dispatch also re-enters this
146
- same listener (dispatch is synchronous and the listener is on the same target) — but the
147
- field/column comparison naturally evaluates "same" for a self-dispatch, so it's a safe no-op, not
148
- an explicit self-check.
149
-
150
- ### Default DOM behaviour
151
-
152
- Only runs when the consumer does not call `preventDefault()`. Reorders `target.children` (or
153
- `data-ln-sort-items` matches) by moving nodes into a `DocumentFragment` and re-appending — a single
154
- reflow, not N individual moves. Value extraction: `readValue` on the `[data-ln-field="<field>"]`
155
- descendant if `field` is set, else `readValue(item)` directly. The `column` index fallback is
156
- **never** consulted here — it exists solely to populate the event payload for SSR/DOM table
157
- consumers; see README "Field is optional" above.
158
-
159
- ### Persistence
160
-
161
- `data-ln-persist` on the root, restored in the constructor via `persistGet('sort', dom)` and
162
- applied via `_apply(saved.direction, true)` — the `skipPersist` flag prevents re-writing the value
163
- just read (same pattern as `ln-filter`'s restore). Cleared by setting `direction: 'none'`
164
- (`persistSet('sort', dom, null)`).
165
-
166
- ### Destroy
167
-
168
- Removes the click listener (root) and the `ln-sort:change` listener (target, if resolved). Does
169
- not restore the DOM to its pre-sort order — that's a rendering side effect, not the component's
170
- own teardown responsibility (same doctrine as `ln-search.destroy()`).
1
+ # ln-sort
2
+
3
+ > Applied to a `<ul data-ln-sort="targetId" data-ln-sort-state="none">` control containing directional buttons (`data-ln-sort-dir="asc|desc|none"`).
4
+ > Clicking a button updates `data-ln-sort-state` on the control and dispatches a cancelable `ln-sort:change` event to the target element.
5
+ > If unprevented, it reorders target child rows or items in the DOM using numeric or `localeCompare` string sorting.
6
+
7
+ ---
8
+
9
+ ## 🧭 Philosophy & Architecture
10
+
11
+ 1. **No cycle logic in JS.** A click reads `data-ln-sort-dir` off the clicked button, writes it to
12
+ `data-ln-sort-state` on the root, and dispatches. The circular order
13
+ `none → asc → desc → none` is produced entirely by CSS — only one `<button data-ln-sort-dir>`
14
+ is visible at a time, selected by `[data-ln-sort-state]` on the root. See
15
+ `theme/config/mixins/_sort.scss`.
16
+ 2. **Field is optional.** Author `data-ln-sort-field="name"` when the target is data-driven
17
+ (server needs a name, not an index). Omit it inside a `<th>` to fall back to
18
+ `th.cellIndex` — SSR/DOM-only, resolved once at construction, never bridged to `field`.
19
+ The dispatched payload always carries both keys, exactly one non-null:
20
+ `{ field: string|null, column: number|null, direction, targetId }`.
21
+ 3. **Single sort, mutual exclusion & multi-control sync.** Every instance listens for
22
+ `ln-sort:change` on its OWN target. When an incoming event's field/column doesn't match this
23
+ instance's own, it resets its own `data-ln-sort-state` to `"none"`. When an incoming event matches
24
+ the same field/column, duplicate controls (e.g. table header button and mobile toolbar)
25
+ synchronize their `data-ln-sort-state` in real time.
26
+ 4. **Deferred initialization via `queueBoot`.** Restoring saved sort (`data-ln-persist`) or booting
27
+ pre-authored `data-ln-sort-state="asc"|"desc"` is deferred via `queueBoot` to ensure consumers
28
+ (`ln-table`, `ln-list`, `ln-data-store`) have finished initializing before the initial sort fires.
29
+ 5. **Type is inferred once per sort, not per pair.** `ln-core.detectValueType` scans the current
30
+ value set; if every non-empty value is a finite number, comparison is numeric, else
31
+ `localeCompare` via `ln-core.getLocale`. Per-pair type inference breaks comparator
32
+ transitivity and produces wrong ordering — never do it.
33
+
34
+ ---
35
+
36
+ ## ✅ Canonical Markup
37
+
38
+ ```html
39
+ <th>
40
+ <span>Name</span>
41
+ <ul data-ln-sort="users-table" data-ln-sort-field="name" data-ln-sort-state="none">
42
+ <li><button type="button" data-ln-sort-dir="asc" aria-label="Sort ascending"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrows-sort"></use></svg></button></li>
43
+ <li><button type="button" data-ln-sort-dir="desc" aria-label="Sort descending"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrow-up"></use></svg></button></li>
44
+ <li><button type="button" data-ln-sort-dir="none" aria-label="Remove sort"><svg class="ln-icon" aria-hidden="true"><use href="#ln-icon-arrow-down"></use></svg></button></li>
45
+ </ul>
46
+ </th>
47
+ ```
48
+
49
+ **Icon convention — READ BEFORE COPYING THIS MARKUP.** Each button carries the icon of the sort
50
+ STATE it is visible in, never the icon of the direction it sets:
51
+
52
+ | Button | Visible when `data-ln-sort-state` = | Icon it carries |
53
+ |---|---|---|
54
+ | `data-ln-sort-dir="asc"` | `none` | `#ln-icon-arrows-sort` |
55
+ | `data-ln-sort-dir="desc"` | `asc` | `#ln-icon-arrow-up` |
56
+ | `data-ln-sort-dir="none"` | `desc` | `#ln-icon-arrow-down` |
57
+
58
+ **This is not a typo.** The `dir="desc"` button contains an UP arrow. The visible button always
59
+ performs the NEXT action (clicking `dir="desc"` sorts descending), but its icon always reports the
60
+ CURRENT state (the column is currently ascending — hence the up arrow). If the icon instead
61
+ matched the button's own `data-ln-sort-dir`, the header would visually lie about its own sort
62
+ order the instant the column reaches `state="asc"`: the user would see a down-arrow next to a
63
+ column that is actually sorted up. Do not "fix" the icons to match `data-ln-sort-dir` when
64
+ hand-authoring markup — that reintroduces the exact bug this convention exists to prevent. `content`
65
+ cannot substitute for this — these icons are an SVG sprite via `<use href>`, not a font glyph a
66
+ `::before { content }` rule could swap (see `docs/architecture/reference.md` §Icons).
67
+
68
+ - **SSR table column** — omit `data-ln-sort-field`; the `<ul>` must be a descendant of the `<th>`
69
+ so the `th.cellIndex` fallback resolves. `data-ln-sort-field` and the SSR index path are never
70
+ bridged — setting a field on an SSR-table column leaves `column` null and `ln-table`'s SSR sort
71
+ has nothing to key off. Don't do both.
72
+ - **Data-driven table column** — set `data-ln-sort-field` to the same value as the `<th>`'s
73
+ `data-ln-table-col`.
74
+ - **Plain list** — target a generic `<ul>`/`<li>` container by `id`; set
75
+ `data-ln-sort-items="<selector>"` for deep targeting (mirrors `data-ln-search-items`).
76
+
77
+ ---
78
+
79
+ ## 🛠️ Declarative API Contract
80
+
81
+ ### HTML Attributes
82
+
83
+ | Attribute | Elements | Description |
84
+ | :--- | :--- | :--- |
85
+ | `data-ln-sort` | `<ul>` (root) | Component root. Value is the `id` of the target being sorted. |
86
+ | `data-ln-sort-field` | Same as root | Opt-in. The record field name (data-driven). Omit for the `th.cellIndex` fallback (SSR/DOM only). |
87
+ | `data-ln-sort-state` | Same as root | *State*. `"none" \| "asc" \| "desc"`. Drives which trigger button is visible via CSS. Observed by `MutationObserver`. |
88
+ | `data-ln-sort-items` | Same as root | Opt-in. Deep CSS selector for default-DOM-behaviour reordering (mirrors `data-ln-search-items`). |
89
+ | `data-ln-sort-dir` | `<button>` (inside root) | `"asc" \| "desc" \| "none"`. Identifies which action this trigger performs. |
90
+ | `data-ln-persist` | Same as root | Opt-in. Persists `{ field, column, direction }` to `localStorage`. Give each `[data-ln-sort]` its own value (or `id`) — persistence is per-instance, not per-table. |
91
+ | `data-ln-hash` | Same as root | Opt-in. Synchronizes sort state to URL hash fragment (e.g. `#users-sort:price.asc`). Value is custom namespace; if empty defaults to `[targetId]-sort`. |
92
+
93
+ ### JavaScript API (`el.lnSort`)
94
+
95
+ | Member | Type | Description |
96
+ | :--- | :--- | :--- |
97
+ | `targetId` | `string` | The `id` of the target element. |
98
+ | `field` | `string \| null` | The authored field name, or `null`. |
99
+ | `column` | `number \| null` | The resolved `th.cellIndex` fallback, or `null`. |
100
+ | `nsKey` | `string \| null` | The resolved URL hash namespace, or `null`. |
101
+ | `hashEnabled` | `boolean` | True if URL hash synchronization is active on this instance. |
102
+ | `destroy()` | `() => void` | Removes listeners and tears down the instance. |
103
+
104
+
105
+ ---
106
+
107
+ ## ⚡ DOM Events
108
+
109
+ ### `ln-sort:change`
110
+
111
+ Dispatched on the **target** element (never on the sort control itself) whenever a trigger button
112
+ is clicked, or on persisted-state restore.
113
+ - **Cancelable**: Yes. `e.preventDefault()` disables the default DOM-reorder and hands the intent
114
+ fully to the consumer (e.g. `ln-table`, `ln-list`).
115
+ - **Payload (`detail`)**: `{ field: string|null, column: number|null, direction: 'asc'|'desc'|'none', targetId: string }`.
116
+
117
+ ---
118
+
119
+ ## ⚠️ Common Pitfalls
120
+
121
+ - **Setting both `data-ln-sort-field` and expecting the index fallback to also work.** They are
122
+ never bridged. Pick one per instance based on the mode (SSR → index fallback; data-driven →
123
+ field).
124
+ - **Multi-column sort.** Not supported — single sort only. Multiple `[data-ln-sort]` instances
125
+ targeting the same target enforce mutual exclusion automatically (see Philosophy §3).
126
+ - **Expecting JS to own the click cycle.** It doesn't — the circular order is CSS-driven via
127
+ `[data-ln-sort-state]`. If the three trigger buttons aren't authored per the canonical markup,
128
+ the cycle breaks visually even though the JS click handling still works.
129
+ - **Persisting without a stable key.** `data-ln-persist` with no value falls back to `el.id`; a
130
+ bare `<ul data-ln-sort data-ln-persist>` with no `id` silently skips persistence
131
+ (see `ln-core.persistGet`/`persistSet` — `console.warn`s once).
132
+ - **Assuming every item carries the declared field.** When `data-ln-sort-field`
133
+ is set and an item has no matching `[data-ln-field]`, that item's value is
134
+ empty — it never contributes to type detection and sorts as `0` in a numeric
135
+ column. Permanent non-data siblings (empty-state rows, totals rows) are the
136
+ usual case.
137
+
138
+ ---
139
+
140
+ ## 🔧 Internals
141
+
142
+ Source: `components/ln-sort/ln-sort.js`. One instance per `[data-ln-sort]`, stored at `element.lnSort`.
143
+
144
+ ### The three-step click (no cycle logic)
145
+
146
+ `_onClick` reads `data-ln-sort-dir` off the clicked button and sets `data-ln-sort-state` on the root,
147
+ which triggers `_syncAttribute` and `_apply(direction)`. `_apply()` updates `aria-sort` on parent `<th>`,
148
+ dispatches `ln-sort:change` on the target, and — only if not prevented — runs `_defaultSort`. There is no
149
+ `if (current === 'asc') then 'desc'` branch anywhere in this file; the SCSS cycle in
150
+ `theme/config/mixins/_sort.scss` decides which button is even clickable next.
151
+
152
+ ### Mutual exclusion & Multi-Control Sync
153
+
154
+ Each instance adds its `ln-sort:change` listener to the resolved **target** element. When an incoming
155
+ event's field/column matches this instance's own, duplicate controls sync their `data-ln-sort-state`
156
+ and `aria-sort`. When an incoming event belongs to a different column, the losing instance resets to `"none"`
157
+ and drops its persist key.
158
+
159
+ ### Default DOM behaviour
160
+
161
+ Only runs when the consumer does not call `preventDefault()`. Reorders `target.children` (or
162
+ `data-ln-sort-items` matches) by moving nodes into a `DocumentFragment` and re-appending — a single
163
+ reflow, not N individual moves. Value extraction: `readValue` on the `[data-ln-field="<field>"]`
164
+ descendant if `field` is set, else `readValue(item)` directly. The `column` index fallback is
165
+ **never** consulted here — it exists solely to populate the event payload for SSR/DOM table
166
+ consumers; see README "Field is optional" above.
167
+
168
+ ### Persistence & Boot
169
+
170
+ `data-ln-persist` on the root, restored in the constructor via `persistGet('sort', dom)` and
171
+ applied via `_apply(saved.direction, true)` through `queueBoot`. Authoring `data-ln-sort-state="asc"|"desc"`
172
+ is also applied safely at boot via `queueBoot`.
173
+
174
+ ### Destroy
175
+
176
+ Removes the click listener (root) and the `ln-sort:change` listener (target, if resolved). Does
177
+ not restore the DOM to its pre-sort order — that's a rendering side effect, not the component's
178
+ own teardown responsibility (same doctrine as `ln-search.destroy()`).
@@ -1,8 +1,8 @@
1
- @use '../../scss/config/mixins' as *;
2
-
3
- [data-ln-debug] {
4
- // 1. Sort Target Check: an empty target id resolves to getElementById('') → null, so the control is silently inert
5
- [data-ln-sort=""] {
6
- @include dev-inline-error("data-ln-sort target is empty");
7
- }
8
- }
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Sort Target Check: an empty target id resolves to getElementById('') → null, so the control is silently inert
5
+ [data-ln-sort=""] {
6
+ @include dev-inline-error("data-ln-sort target is empty");
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function V(){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 O(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let r=null;for(let o=1;o<t.length;o++)if(t[o]&&t[o].nodeType===1){r=t[o];break}if(!O(r))return}e.apply(console,t)}}function te(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:r}))}function W(e,t,n){const r=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,r);const o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:r});return e.dispatchEvent(o),o}function T(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){T(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}function L(e){return e.hasAttribute("data-ln-value")?e.getAttribute("data-ln-value"):e.tagName==="TIME"&&e.hasAttribute("datetime")?e.getAttribute("datetime"):e.tagName==="DATA"&&e.hasAttribute("value")?e.getAttribute("value"):e.textContent.trim()}const K={};function ne(e,t){K[e]=t}function re(e){return K[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=ne,window.lnCore.getDataMapper=re);function oe(e){const t=e?e.closest("[lang]"):null,n=(t?t.getAttribute("lang")||t.lang:null)||(typeof document<"u"&&document.documentElement?document.documentElement.getAttribute("lang")||document.documentElement.lang:null)||(typeof navigator<"u"?navigator.language:null);return n?n.trim():"en-US"}function ie(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,T(function(){new MutationObserver(function(){te(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const U={};function le(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];U[n]=t}function se(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return U[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=le,window.lnCore.getLocaleFallback=se,window.lnCore.ensureLocaleObserver=ie);const x={};function ue(e,t){x[e]||(x[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=x[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function P(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let l=0;l<n.length;l++){const u=n[l],a=u.getAttribute("data-ln-field");t[a]!=null&&(u.textContent=t[a])}const r=e.querySelectorAll("[data-ln-attr]");for(let l=0;l<r.length;l++){const u=r[l],a=u.getAttribute("data-ln-attr").split(",");for(let d=0;d<a.length;d++){const b=a[d].trim().split(":");if(b.length!==2)continue;const f=b[0].trim(),C=b[1].trim();t[C]!=null&&u.setAttribute(f,t[C])}}const o=e.querySelectorAll("[data-ln-show]");for(let l=0;l<o.length;l++){const u=o[l],a=u.getAttribute("data-ln-show");a in t&&u.classList.toggle("hidden",!t[a])}const i=e.querySelectorAll("[data-ln-class]");for(let l=0;l<i.length;l++){const u=i[l],a=u.getAttribute("data-ln-class").split(",");for(let d=0;d<a.length;d++){const b=a[d].trim().split(":");if(b.length!==2)continue;const f=b[0].trim(),C=b[1].trim();C in t&&u.classList.toggle(f,!!t[C])}}return e}function ae(e,t){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,t??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0})));const n=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let r=0;r<n.length;r++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[r],t??null),n[r].dispatchEvent(new CustomEvent("ln-fill",{detail:t??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)P(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 Q(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const i=n.currentNode;i.textContent.indexOf("{{")!==-1&&(i.textContent=i.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(l,u){return t[u]!==void 0?t[u]:""}))}const r=function(i,l){return t[l]!==void 0?t[l]:""},o=Array.from(e.querySelectorAll("*"));e.nodeType===1&&o.push(e);for(let i=0;i<o.length;i++){const l=o[i],u=l.attributes;for(let a=0;a<u.length;a++){const d=u[a];d.value.indexOf("{{")!==-1&&l.setAttribute(d.name,d.value.replace(/\{\{\s*(\w+)\s*\}\}/g,r))}}return e}function ce(e,t,n,r,o,i){const l={};for(let a=0;a<e.children.length;a++){const d=e.children[a],b=d.getAttribute("data-ln-render-key");b&&(l[b]=d)}const u=document.createDocumentFragment();for(let a=0;a<t.length;a++){const d=t[a],b=String(r(d));let f=l[b];if(f)o(f,d,a);else{const C=ue(n,i);if(!C||(Q(C,d),f=C.firstElementChild,!f))continue;f.setAttribute("data-ln-render-key",b),o(f,d,a)}u.appendChild(f)}e.textContent="",e.appendChild(u)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=Q,window.lnCore.fill=P,window.lnCore.lnFill=ae,window.lnCore.renderList=ce);function D(e,t,n){const r=e.getAttribute(t);return r===null?n:r}function de(e,t,n){const r=parseInt(e.getAttribute(t),10);return isNaN(r)?n:r}function fe(e,t,n=!1){const r=e.getAttribute(t);if(r===null)return!!n;const o=r.trim().toLowerCase();return!(o==="false"||o==="0")}function he(e,t){return(e.getAttribute(t)||"").split(",").map(n=>n.trim()).filter(Boolean)}const N=new Map;function we(e,t){const n=(e?e.join("|"):"")+"::"+t;if(N.has(n))return N.get(n);const r=new Set(e||[]),o=function(i,l){const u=i.getAttribute(l);return u!==null&&r.has(u)?u:t};return N.set(n,o),o}function be(e,t,n){const r=parseFloat(e.getAttribute(t));return isNaN(r)?n:r}function ge(e,t,n){const r=e.getAttribute(t);if(!r)return n;try{return JSON.parse(r)}catch{return n}}function z(e,t,n,r,o){if(!e||t===null)return!0;const i=r||"ln-component",l=e.type;if(l==="trigger"||l==="marker"||l==="string"||l==="list"||!l||t===""&&e.fallback!==void 0)return!0;const u=a=>{o?console.warn(a,o):console.warn(a)};if(l==="boolean"){const a=t.trim().toLowerCase();return a!==""&&a!=="true"&&a!=="false"&&a!=="1"&&a!=="0"?(u(`[${i}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(l==="enum"){const a=e.values||[];return a.includes(t)?!0:(u(`[${i}] Invalid value "${t}" for attribute "${n}". Allowed: ${a.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(l==="integer"){if(!/^-?\d+$/.test(t))return u(`[${i}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const a=parseInt(t,10);return e.min!==void 0&&a<e.min?(u(`[${i}] Value ${a} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&a>e.max?(u(`[${i}] Value ${a} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(l==="float")return isNaN(Number(t))?(u(`[${i}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(l==="json")try{return JSON.parse(t),!0}catch(a){return u(`[${i}] Invalid JSON for attribute "${n}": ${a.message}. Fallback: ${e.fallback}.`),!1}return!0}function pe(e,t,n){for(const r in n){const[o,i,l]=n[r];Object.defineProperty(e,r,{get:function(){return o(t,i,l)},enumerable:!0,configurable:!0})}return e}function me(e){const t={};for(const n in e){const r=e[n];if(!r||!r.prop)continue;let o=r.read;if(!o&&r.type)switch(r.type){case"enum":o=we(r.values,r.fallback);break;case"integer":o=de;break;case"float":o=be;break;case"boolean":o=fe;break;case"list":o=he;break;case"json":o=ge;break;case"string":default:o=D;break}o||(o=D),t[r.prop]=[o,n,r.fallback]}return t}function ye(e){const t={};for(const n in e){const r=e[n];r&&r.effect&&(t[n]=r.effect)}return Object.keys(t).length?t:null}function Ce(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 $(e,t,n,r){if(e.nodeType!==1)return;const i=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",l=Array.from(e.querySelectorAll(i));e.matches&&e.matches(i)&&l.push(e);for(const u of l)if(!u[n]){window.lnCore._persistSink&&u.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(u,t);try{u[n]=new r(u)}catch(a){console.error("["+n+"] init failed",u,a)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function Ae(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function q(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function J(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function _e(e){const t=J(),n=e.observed||[];for(let r=0;r<n.length;r++){const o=n[r];t.byAttr.has(o)||t.byAttr.set(o,[]),t.byAttr.get(o).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const r in e.attributes){t.byDeclaredAttr.has(r)||t.byDeclaredAttr.set(r,[]);const o=t.byDeclaredAttr.get(r);o.some(i=>i.componentTag===e.componentTag)||o.push({spec:e.attributes[r],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const r=Ce(e);if(r===null)t.reactiveWildcard.push(e);else for(const o of r)t.byReactive.has(o)||t.byReactive.set(o,[]),t.byReactive.get(o).push(e)}e.persist&&t.persist.push(e)}function G(e,t,n,r){for(let o=0;o<e.length;o++){const i=e[o];if(!t[i.attribute])continue;const l=i.effects&&i.effects[n];l?l(t,n,r):i.onAttrChange&&(!i.declared||i.declared.has(n))&&i.onAttrChange(t,n,r)}}function Se(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const r=J(),o=r.byAttr.get(n);if(V()&&r.byDeclaredAttr&&r.byDeclaredAttr.has(n)&&O(t)){const i=r.byDeclaredAttr.get(n),l=t.getAttribute(n);for(let u=0;u<i.length;u++)z(i[u].spec,l,n,i[u].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||o)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const i=r.byReactive.get(n);i&&G(i,t,n,e.oldValue),r.reactiveWildcard.length&&G(r.reactiveWildcard,t,n,e.oldValue)}if(o)for(let i=0;i<o.length;i++){const l=o[i];if(l.handler){l.handler(t,n,e.oldValue);continue}l.onAttributeChange&&t[l.attribute]?l.onAttributeChange(t,n):($(t,l.selector,l.attribute,l.ComponentFn),l.onInit&&l.onInit(t))}}function ve(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,T(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{Se(t[n])}catch(r){console.error("[ln-core] mutation handler failed",t[n].target,r)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function X(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function ke(e){const t=X();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const r=t[n];if(r.onSubtreeChange){const o=r.query,i=e.target.nodeType===1?e.target.matches(o)?e.target:e.target.closest(o):e.target.parentElement?e.target.parentElement.closest(o):null;i&&r.onSubtreeChange(i,e)}}for(let n=0;n<e.addedNodes.length;n++){const r=e.addedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o];$(r,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(r)}}for(let n=0;n<e.removedNodes.length;n++){const r=e.removedNodes[n];if(r.nodeType===1)for(let o=0;o<t.length;o++){const i=t[o],l=i.query,u=Array.from(r.querySelectorAll(l));r.matches&&r.matches(l)&&u.push(r);for(let a=0;a<u.length;a++){const d=u[a];if(!document.contains(d)){const b=d[i.attribute];if(b&&typeof b.destroy=="function")try{b.destroy()}catch(f){console.error("["+i.attribute+"] destroy failed",d,f)}delete d[i.attribute]}}}}}}function Ee(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,T(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const r=t[n];if(r.type==="childList")try{ke(r)}catch(o){console.error("[ln-core] lifecycle handler failed",r.target,o)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function Te(e,t,n,r,o={}){const i=o.extraAttributes||[],l=o.onAttributeChange||null,u=o.onSubtreeChange||null,a=o.onInit||null,d=o.onAttrChange||null,b=o.effects||null,f=o.attributes||null,C=f?ye(f):b,c=f?new Set(Object.keys(f)):null,m=o.persist||null;function s(S){const w=S||document.body;if($(w,e,t,n),f&&V())for(const A in f){const j=f[A],v=Array.from(w.querySelectorAll("["+A+"]"));w.matches&&w.matches("["+A+"]")&&v.push(w);for(let k=0;k<v.length;k++){const E=v[k];O(E)&&z(j,E.getAttribute(A),A,r,E)}}a&&a(w)}const p=[];if(e.indexOf("[")!==-1){const S=/\[([\w-]+)/g;let w;for(;(w=S.exec(e))!==null;)p.push(w[1])}else p.push(e);_e({selector:e,attribute:t,componentTag:r,attributes:f,ComponentFn:n,onInit:a,observed:p.concat(i),onAttributeChange:l,onAttrChange:d,effects:C,declared:c,persist:m}),ve();const g=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";X().push({selector:e,attribute:t,ComponentFn:n,onInit:a,onSubtreeChange:u,query:g}),Ee(),window[t]=s;function y(){Ae()>0?q(function(){s(document.body)}):s(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",y):y(),s}function Ie(e){let t=!1;for(let n=0;n<e.length;n++){const r=e[n];if(!(r===""||r==null)&&(t=!0,!Number.isFinite(Number(r))))return"string"}return t?"number":"string"}function Oe(e,t,n,r){if(n==="number"){const l=parseFloat(e),u=parseFloat(t);return(isNaN(l)?0:l)-(isNaN(u)?0:u)}const o=e!=null?String(e):"",i=t!=null?String(t):"";return r?r.compare(o,i):o<i?-1:o>i?1:0}function Y(e){return(e||"").replace(/^#/,"")}function R(e){const t=e===void 0?location.hash:e,n={},r=Y(t);if(!r)return n;const o=r.split("&");for(let i=0;i<o.length;i++){const l=o[i];if(!l)continue;const u=l.indexOf(":"),a=u>-1?l.slice(0,u):l,d=u>-1?l.slice(u+1):"";if(a)try{n[a]=decodeURIComponent(d)}catch{n[a]=d}}return n}function B(e){if(!e)return null;const t=R();return e in t?t[e]:null}function Z(e,t){if(!e)return;const n=R();t==null?delete n[e]:n[e]=String(t);const o=Object.keys(n).map(function(i){const l=n[i];return l===""?i:i+":"+encodeURIComponent(l)}).join("&");Y(location.hash)!==o&&(location.hash=o)}function Le(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function I(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const r=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return r?t?r+"-"+t:r:t||null}function ee(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function F(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 xe(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function De(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),r=e.slice(t+1),o=r?r.split(",").map(function(i){try{return decodeURIComponent(i)}catch{return i}}).filter(Boolean):[];return{key:n,values:o}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=R,window.lnCore.hashGet=B,window.lnCore.hashSet=Z,window.lnCore.hashLinkClick=Le,window.lnCore.resolveHashNamespace=I,window.lnCore.hashSortEncode=ee,window.lnCore.hashSortDecode=F,window.lnCore.hashFilterEncode=xe,window.lnCore.hashFilterDecode=De);function _(e){const t=String(e||"").trim().toLowerCase();return t==="asc"||t==="ascending"?"asc":t==="desc"||t==="descending"?"desc":"none"}function Ne(e){const t=_(e);return t==="asc"?"ascending":t==="desc"?"descending":"none"}function $e(e,t){return!e||!t?!1:e.field!==null&&e.field!==void 0&&t.field!==null&&t.field!==void 0?e.field===t.field:e.column!==null&&e.column!==void 0&&t.column!==null&&t.column!==void 0?String(e.column)===String(t.column):!1}function qe(e,t,n,r){const o=_(e);if(o==="none")return()=>0;const i=o==="desc"?-1:1,l=typeof r=="function"?r:u=>u;return function(u,a){const d=l(u),b=l(a);return Oe(d,b,t,n)*i}}function M(e){return!!(!e||typeof e!="object"||e.nodeType!==1||e.tagName==="TEMPLATE"||typeof e.hasAttribute=="function"&&(e.hasAttribute("data-ln-sort-exclude")||e.hasAttribute("hidden"))||e.classList&&e.classList.contains("hidden")||e.style&&e.style.display==="none"||typeof e.matches=="function"&&e.matches(".empty-state, .ln-table__empty, .ln-table__empty-state, [data-ln-empty], [data-ln-empty-state], [data-ln-table-empty]"))}function Re(e,t){if(!e||typeof e!="object"||e.nodeType!==1)return[];const n=t||(typeof e.getAttribute=="function"?e.getAttribute("data-ln-sort-items"):null)||null;if(n&&typeof e.querySelectorAll=="function")return Array.from(e.querySelectorAll(n));if(e.tagName==="TABLE"){const r=e.tBodies&&e.tBodies.length?e.tBodies[0]:typeof e.querySelector=="function"?e.querySelector("tbody"):null;if(r)return Array.from(r.children||[]);if(typeof e.querySelectorAll=="function")return Array.from(e.querySelectorAll("tbody tr, tr"))}return Array.from(e.children||[])}(function(){const e="data-ln-sort",t="lnSort",n="data-ln-sort-field",r="data-ln-sort-state",o="data-ln-sort-dir",i="data-ln-hash";if(window[t]!==void 0)return;function l(c,m){return c.getAttribute(m)||null}const u={"data-ln-sort":{prop:"targetId",type:"string",read:D,fallback:null,description:"Target table or list element ID to sort"},"data-ln-sort-field":{prop:"field",type:"string",read:l,effect:C,description:"Field name or column key to sort by"},"data-ln-sort-dir":{type:"enum",values:["asc","desc"],fallback:"asc",description:"Default or requested sort direction"},"data-ln-sort-items":{prop:"itemsSelector",type:"string",read:l,description:"CSS selector matching sortable child items"},"data-ln-sort-state":{type:"enum",values:["asc","desc","none"],fallback:"none",effect:C,description:"Active sort state applied to column or control"},"data-ln-hash":{type:"string",effect:C,description:"URL hash routing key for sort state persistence"}},a=me(u),d=new WeakMap;function b(c,m,s){if(m){const p=c.querySelector('[data-ln-field="'+m+'"]');return p?L(p):""}return s!=null&&c.cells&&c.cells[s]?L(c.cells[s]):L(c)}function f(c){this.dom=c,pe(this,c,a);const m=c.closest("th");this.column=!this.field&&m?m.cellIndex:null,this._state=_(c.getAttribute(r)),c.hasAttribute(r)||c.setAttribute(r,this._state),this._destroyed=!1,this.nsKey=I(c,"sort"),this.hashEnabled=!!this.nsKey;const s=this;this._onClick=function(h){const g=h.target.closest("["+o+"]");if(!g)return;const y=_(g.getAttribute(o));s._apply(y)},c.addEventListener("click",this._onClick),this._onSortChange=function(h){if(s._destroyed||!h.detail)return;const g=s._resolveTarget();if(!(g&&(h.target===g||g.contains(h.target))||h.detail.targetId&&h.detail.targetId===s.targetId))return;if($e({field:s.field,column:s.column},{field:h.detail.field,column:h.detail.column})){const w=_(h.detail.direction);w&&c.getAttribute(r)!==w&&(s._state=w,c.setAttribute(r,w),s._updateAriaSort(w));return}c.getAttribute(r)!=="none"&&(s._state="none",c.setAttribute(r,"none"),s._updateAriaSort("none"))},document.addEventListener("ln-sort:change",this._onSortChange),this._onHashChange=function(){if(s._destroyed||!s.hashEnabled)return;const h=B(s.nsKey),g=F(h);if(g)s.field!==null&&g.fieldOrColumn===s.field||s.column!==null&&String(s.column)===g.fieldOrColumn?s._state!==g.direction&&s._apply(g.direction,!0):s._state!=="none"&&(s._state="none",c.setAttribute(r,"none"),s._updateAriaSort("none"));else if(s._state!=="none"){s._state="none",c.setAttribute(r,"none"),s._updateAriaSort("none");const y=s._resolveTarget();y&&(W(y,"ln-sort:change",{field:s.field,column:s.column,direction:"none",targetId:s.targetId}).defaultPrevented||s._defaultSort(y,"none"))}},this.hashEnabled&&window.addEventListener("hashchange",this._onHashChange);let p=!1;if(this.hashEnabled){const h=B(this.nsKey),g=F(h);g&&((s.field!==null&&g.fieldOrColumn===s.field||s.column!==null&&String(s.column)===g.fieldOrColumn)&&q(function(){s._destroyed||s._apply(g.direction,!0)}),p=!0)}if(!p){const h=_(c.getAttribute(r));h&&h!=="none"&&q(function(){s._destroyed||s._apply(h,!0)})}return this}f.prototype._resolveTarget=function(){return document.getElementById(this.targetId)},f.prototype._updateAriaSort=function(c){const m=this.dom.closest("th");m&&m.setAttribute("aria-sort",Ne(c))},f.prototype._apply=function(c,m){if(this._destroyed)return;if(!this.field&&this.column===null){const y=this.dom.closest("th");y&&y.cellIndex!==void 0&&(this.column=y.cellIndex)}const s=_(c);this._state=s,this.dom.getAttribute(r)!==s&&this.dom.setAttribute(r,s),this._updateAriaSort(s);const p=this._resolveTarget();if(!p)return;const h={field:this.field,column:this.column,direction:s,targetId:this.targetId};if(!m&&this.hashEnabled){const y=ee(this.field!==null?this.field:this.column,s);Z(this.nsKey,y)}W(p,"ln-sort:change",h).defaultPrevented||this._defaultSort(p,s)},f.prototype._defaultSort=function(c,m){const s=Re(c,this.itemsSelector);if(!s.length)return;const p=s[0].parentNode,h=s.filter(function(w){return!M(w)});if(!h.length)return;d.has(c)||d.set(c,h.slice());let g;if(m==="none"){const w=d.get(c)||h;d.delete(c),g=w.filter(function(A){return A.parentNode===p&&!M(A)})}else{const w=this.field,A=this.column,j=h.map(function(H){return b(H,w,A)}),v=Ie(j),k=typeof Intl<"u"?new Intl.Collator(oe(this.dom),{sensitivity:"base",numeric:!0}):null,E=qe(m,v,k,function(H){return b(H,w,A)});g=h.slice().sort(E)}const y=document.createDocumentFragment();let S=0;for(let w=0;w<s.length;w++){const A=s[w];M(A)?y.appendChild(A):S<g.length&&y.appendChild(g[S++])}p.appendChild(y)},f.prototype.destroy=function(){this._destroyed||(this._destroyed=!0,this.dom.removeEventListener("click",this._onClick),document.removeEventListener("ln-sort:change",this._onSortChange),this.hashEnabled&&this._onHashChange&&window.removeEventListener("hashchange",this._onHashChange),delete this.dom[t])};function C(c,m){const s=c[t];if(!(!s||s._destroyed))if(m===n){const p=c.closest("th");s.column=!s.field&&p?p.cellIndex:null}else if(m===r){const p=_(c.getAttribute(r));p!==s._state&&s._apply(p)}else m===i&&(s.hashEnabled&&s._onHashChange&&window.removeEventListener("hashchange",s._onHashChange),s.nsKey=I(c,"sort"),s.hashEnabled=!!s.nsKey,s.hashEnabled&&window.addEventListener("hashchange",s._onHashChange))}Te(e,t,f,"ln-sort",{attributes:u,persist:{attr:r,hashActive:function(c){return!!I(c,"sort")}}})})()})();
@@ -0,0 +1,104 @@
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-sort",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-debug": {
7
+ "direction": null,
8
+ "sources": [
9
+ "ln-sort-dev.scss"
10
+ ]
11
+ },
12
+ "data-ln-empty": {
13
+ "direction": "author",
14
+ "sources": [
15
+ "src/sort-model.js"
16
+ ]
17
+ },
18
+ "data-ln-empty-state": {
19
+ "direction": "author",
20
+ "sources": [
21
+ "src/sort-model.js"
22
+ ]
23
+ },
24
+ "data-ln-field": {
25
+ "direction": "author",
26
+ "sources": [
27
+ "src/ln-sort.js"
28
+ ]
29
+ },
30
+ "data-ln-hash": {
31
+ "direction": null,
32
+ "sources": [
33
+ "src/ln-sort.js"
34
+ ],
35
+ "type": "string",
36
+ "description": "URL hash routing key for sort state persistence"
37
+ },
38
+ "data-ln-sort": {
39
+ "direction": null,
40
+ "sources": [
41
+ "ln-sort-dev.scss",
42
+ "src/ln-sort.js"
43
+ ],
44
+ "type": "string",
45
+ "description": "Target table or list element ID to sort"
46
+ },
47
+ "data-ln-sort-dir": {
48
+ "direction": "author",
49
+ "sources": [
50
+ "src/ln-sort.js"
51
+ ],
52
+ "type": "enum",
53
+ "values": [
54
+ "asc",
55
+ "desc"
56
+ ],
57
+ "fallback": "asc",
58
+ "description": "Default or requested sort direction"
59
+ },
60
+ "data-ln-sort-exclude": {
61
+ "direction": "author",
62
+ "sources": [
63
+ "src/sort-model.js"
64
+ ]
65
+ },
66
+ "data-ln-sort-field": {
67
+ "direction": null,
68
+ "sources": [
69
+ "src/ln-sort.js"
70
+ ],
71
+ "type": "string",
72
+ "description": "Field name or column key to sort by"
73
+ },
74
+ "data-ln-sort-items": {
75
+ "direction": "author",
76
+ "sources": [
77
+ "src/ln-sort.js",
78
+ "src/sort-model.js"
79
+ ],
80
+ "type": "string",
81
+ "description": "CSS selector matching sortable child items"
82
+ },
83
+ "data-ln-sort-state": {
84
+ "direction": "both",
85
+ "sources": [
86
+ "src/ln-sort.js"
87
+ ],
88
+ "type": "enum",
89
+ "values": [
90
+ "asc",
91
+ "desc",
92
+ "none"
93
+ ],
94
+ "fallback": "none",
95
+ "description": "Active sort state applied to column or control"
96
+ },
97
+ "data-ln-table-empty": {
98
+ "direction": "author",
99
+ "sources": [
100
+ "src/sort-model.js"
101
+ ]
102
+ }
103
+ }
104
+ }