@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,15 +1,49 @@
1
- import { registerComponent, dispatch, buildDict, serializeForm, resolveFormMethod } from '../../ln-core';
2
- import { normalizeDataQuery, selectDataSource } from './data-read-policy';
1
+ import { registerComponent, dispatch, buildDict, serializeForm, resolveFormMethod, createBatcher, attrSpec, defineAttrs } from '../../ln-core';
2
+ import { normalizeDataQuery, selectDataSource, composeQuery, needsRemoteSupersede } from './data-read-policy';
3
3
  import { MutationReceipts } from './mutation-receipts';
4
4
 
5
5
  (function () {
6
6
  const DOM_SELECTOR = 'data-ln-data-coordinator';
7
7
  const DOM_ATTRIBUTE = 'lnDataCoordinator';
8
- const DOM_ALIAS = 'lnCoordinator';
9
- const SCOPE_ATTR = 'data-ln-form-scope';
10
-
8
+ const SCOPE_ATTR = 'data-ln-data-coordinator-scope';
9
+ const SEARCH_ATTR = 'data-ln-data-coordinator-search';
10
+ const FILTERS_ATTR = 'data-ln-data-coordinator-filters';
11
+ const SORT_FIELD_ATTR = 'data-ln-data-coordinator-sort-field';
12
+ const SORT_DIR_ATTR = 'data-ln-data-coordinator-sort-direction';
11
13
  if (window[DOM_ATTRIBUTE] !== undefined) return;
12
14
 
15
+ // ─── Attribute Contract (SSOT) ──────────────────────────
16
+ function _applyMapper(el) {
17
+ const instance = el[DOM_ATTRIBUTE];
18
+ if (!instance) return;
19
+ instance.refreshMapper();
20
+ }
21
+
22
+ function _applyQuery(el) {
23
+ const instance = el[DOM_ATTRIBUTE];
24
+ if (!instance) return;
25
+ instance._queueQueryRefresh();
26
+ }
27
+
28
+ function _readName(el, name) {
29
+ return el.getAttribute(name) || el.id;
30
+ }
31
+
32
+ const ATTRIBUTES = {
33
+ 'data-ln-data-coordinator': { prop: '_name', type: 'string', read: _readName, description: 'Coordinator name or identifier for data routing' },
34
+ 'data-ln-data-coordinator-scope': { type: 'string', description: 'Scope name addressing the bound data store and connector' },
35
+ 'data-ln-data-coordinator-mapper': { type: 'string', effect: _applyMapper, description: 'Name of the registered data mapper transform' },
36
+ 'data-ln-data-coordinator-search': { type: 'string', effect: _applyQuery, description: 'Active search query term' },
37
+ 'data-ln-data-coordinator-filters': { type: 'string', effect: _applyQuery, description: 'Active encoded filter parameters' },
38
+ 'data-ln-data-coordinator-sort-field': { type: 'string', effect: _applyQuery, description: 'Active sort field property name' },
39
+ 'data-ln-data-coordinator-sort-direction': { type: 'enum', values: ['asc', 'desc'], fallback: 'asc', effect: _applyQuery, description: 'Sort direction' },
40
+ 'data-ln-data-coordinator-stale': { type: 'marker', description: 'Flag indicating data needs re-synchronization' },
41
+ 'data-ln-data-coordinator-no-autosync': { type: 'boolean', description: 'Disables automatic synchronization upon state changes' },
42
+ 'data-ln-data-coordinator-dict': { type: 'marker', description: 'Marks dictionary container for coordinator translatable messages' }
43
+ };
44
+
45
+ const ATTR_SPEC = attrSpec(ATTRIBUTES);
46
+
13
47
  // ─── Sync Orchestration Singleton ──────────────────────
14
48
 
15
49
  const _coordinators = new Set();
@@ -23,14 +57,14 @@ import { MutationReceipts } from './mutation-receipts';
23
57
  _globalSyncInstalled = true;
24
58
 
25
59
  _onlineHandler = function () {
26
- dispatch(document, 'ln-data-store:online', {});
60
+ dispatch(document, 'ln-data-coordinator:online', {});
27
61
  _coordinators.forEach(function (coord) {
28
62
  coord._maybeSync();
29
63
  });
30
64
  };
31
65
 
32
66
  _offlineHandler = function () {
33
- dispatch(document, 'ln-data-store:offline', {});
67
+ dispatch(document, 'ln-data-coordinator:offline', {});
34
68
  };
35
69
 
36
70
  _visibilityHandler = function () {
@@ -77,27 +111,42 @@ import { MutationReceipts } from './mutation-receipts';
77
111
  }
78
112
 
79
113
  // Connector response namespaces — generalized so writes work whether the
80
- // paired connector is ln-api-connector or ln-couchdb-connector.
81
- const CONNECTOR_RESPONSE_NAMESPACES = ['ln-api-connector', 'ln-couchdb-connector'];
114
+ // paired connector is ln-api-connector, ln-couchdb-connector or
115
+ // ln-websocket-connector. A socket beside a REST connector only pushes;
116
+ // its :fetched still arrives here.
117
+ const CONNECTOR_RESPONSE_NAMESPACES = ['ln-api-connector', 'ln-couchdb-connector', 'ln-websocket-connector'];
118
+
119
+ function _connectorNamespace(connectorEl) {
120
+ if (!connectorEl) return 'ln-api-connector';
121
+ if (connectorEl.hasAttribute('data-ln-couchdb-connector')) return 'ln-couchdb-connector';
122
+ if (connectorEl.hasAttribute('data-ln-websocket-connector')) return 'ln-websocket-connector';
123
+ return 'ln-api-connector';
124
+ }
82
125
 
83
126
  // ─── Component Constructor ─────────────────────────────
84
127
 
85
128
  function _component(dom) {
129
+ const self = this;
86
130
  this.dom = dom;
87
- this._name = dom.getAttribute(DOM_SELECTOR);
131
+ defineAttrs(this, dom, ATTR_SPEC);
132
+ if (!this._name) console.warn('[ln-data-coordinator] missing id — the coordinator cannot be addressed', dom);
88
133
  dom[DOM_ATTRIBUTE] = this;
89
- dom[DOM_ALIAS] = this;
90
134
 
135
+ this._destroyed = false;
91
136
  this.mapper = null;
92
137
  this._handlers = null;
93
138
  this._boundQueries = new WeakMap();
94
139
  this._boundDelivered = new WeakMap();
140
+ this._queryGens = new WeakMap();
95
141
  this._mutationReceipts = new MutationReceipts();
96
142
  this._dict = buildDict(dom, 'data-ln-data-coordinator-dict'); // flat key→string error-toast map; {} if none
97
143
 
98
- this._parseStaleAttributes();
144
+ this._queueQueryRefresh = createBatcher(function () {
145
+ if (self._destroyed) return;
146
+ self._refreshAll(null, true);
147
+ });
99
148
 
100
- this.refreshMapper();
149
+ this.refreshConfig();
101
150
  _bindEvents(this);
102
151
 
103
152
  _coordinators.add(this);
@@ -108,20 +157,31 @@ import { MutationReceipts } from './mutation-receipts';
108
157
  return this;
109
158
  }
110
159
 
111
- // ─── Stale / No-Autosync Attribute Parsing ──────────────
112
-
113
- _component.prototype._parseStaleAttributes = function () {
114
- const children = this.findChildren();
115
- const storeEl = children.storeEl;
160
+ // ─── Observable Live Attributes (Single Source of Truth) ──
161
+
162
+ Object.defineProperty(_component.prototype, '_staleThreshold', {
163
+ get: function () {
164
+ const children = this.findChildren();
165
+ const storeEl = children.storeEl;
166
+ const staleAttr = this.dom.getAttribute('data-ln-data-coordinator-stale')
167
+ || (storeEl ? storeEl.getAttribute('data-ln-data-store-stale') : null);
168
+ if (staleAttr === 'never' || staleAttr === '-1') return -1;
169
+ const parsed = parseInt(staleAttr, 10);
170
+ return isNaN(parsed) ? 300 : parsed;
171
+ }
172
+ });
116
173
 
117
- const staleAttr = this.dom.getAttribute('data-ln-data-coordinator-stale')
118
- || (storeEl ? storeEl.getAttribute('data-ln-data-store-stale') : null);
119
- const parsed = parseInt(staleAttr, 10);
120
- this._staleThreshold = (staleAttr === 'never' || staleAttr === '-1') ? -1 : (isNaN(parsed) ? 300 : parsed);
174
+ Object.defineProperty(_component.prototype, '_noAutosync', {
175
+ get: function () {
176
+ const children = this.findChildren();
177
+ const storeEl = children.storeEl;
178
+ return this.dom.hasAttribute('data-ln-data-coordinator-no-autosync')
179
+ || (storeEl ? storeEl.hasAttribute('data-ln-data-store-no-autosync') : false);
180
+ }
181
+ });
121
182
 
122
- const noAutosyncAttr = this.dom.hasAttribute('data-ln-data-coordinator-no-autosync')
123
- || (storeEl ? storeEl.hasAttribute('data-ln-data-store-no-autosync') : false);
124
- this._noAutosync = !!noAutosyncAttr;
183
+ _component.prototype.refreshConfig = function () {
184
+ this.refreshMapper();
125
185
  };
126
186
 
127
187
  _component.prototype._isStale = function () {
@@ -150,6 +210,7 @@ import { MutationReceipts } from './mutation-receipts';
150
210
  if (!store) return;
151
211
 
152
212
  Promise.resolve(store.ready).then(function () {
213
+ if (self._destroyed) return;
153
214
  const children = self.findChildren();
154
215
  const currentStore = children.store;
155
216
  if (currentStore && currentStore.initializationError) {
@@ -159,6 +220,7 @@ import { MutationReceipts } from './mutation-receipts';
159
220
  if (!currentStore || !children.connector || self._noAutosync || currentStore.isSyncing) return;
160
221
  if (!currentStore.hasCache || self._isStale()) currentStore.forceSync();
161
222
  }).catch(function (error) {
223
+ if (self._destroyed) return;
162
224
  self._reportReconciliationError('store-initialize', error, null);
163
225
  });
164
226
  };
@@ -175,7 +237,8 @@ import { MutationReceipts } from './mutation-receipts';
175
237
  }
176
238
 
177
239
  // 2. Resolve to registered external mapper
178
- const mapperName = this.dom.getAttribute('data-ln-data-mapper') || this.dom.getAttribute('data-ln-data-coordinator');
240
+ const mapperName = this.dom.getAttribute('data-ln-data-coordinator-mapper')
241
+ || this.dom.id;
179
242
  if (mapperName && window.lnCore && typeof window.lnCore.getDataMapper === 'function') {
180
243
  this.mapper = window.lnCore.getDataMapper(mapperName);
181
244
  }
@@ -198,15 +261,18 @@ import { MutationReceipts } from './mutation-receipts';
198
261
 
199
262
  _component.prototype.findChildren = function () {
200
263
  const storeEl = this.dom.querySelector('[data-ln-data-store]');
201
- const connectorEl = this.dom.querySelector('[data-ln-api-connector], [data-ln-couchdb-connector], [data-ln-websocket-connector], [data-ln-rest-connector]');
264
+ // Requests go to a REST/CouchDB connector when there is one; a socket
265
+ // takes them only when it is the sole transport.
266
+ const connectorEl = this.dom.querySelector('[data-ln-api-connector], [data-ln-couchdb-connector]')
267
+ || this.dom.querySelector('[data-ln-websocket-connector]');
202
268
  const queueEl = this.dom.querySelector('[data-ln-api-queue]');
203
269
 
204
270
  return {
205
271
  storeEl: storeEl,
206
272
  connectorEl: connectorEl,
207
273
  queueEl: queueEl,
208
- store: storeEl ? (storeEl.lnDataStore || storeEl.lnStore) : null,
209
- connector: connectorEl ? (connectorEl.lnConnector || connectorEl.lnApiConnector || connectorEl.lnCouchDbConnector) : null,
274
+ store: storeEl ? storeEl.lnDataStore : null,
275
+ connector: connectorEl ? (connectorEl.lnApiConnector || connectorEl.lnCouchDbConnector || connectorEl.lnWebsocketConnector) : null,
210
276
  queue: queueEl ? queueEl.lnApiQueue : null
211
277
  };
212
278
  };
@@ -215,8 +281,8 @@ import { MutationReceipts } from './mutation-receipts';
215
281
 
216
282
  _component.prototype._handleSubmitRecord = function (detail) {
217
283
  const children = this.findChildren();
218
- if (!children.storeEl) {
219
- console.warn('[ln-data-coordinator] form submit claimed but no [data-ln-data-store] child found in "' + (this._name || '') + '"');
284
+ if (!children.storeEl && !children.connectorEl) {
285
+ console.warn('[ln-data-coordinator] form submit claimed but neither [data-ln-data-store] nor a connector child found in "' + (this._name || '') + '"');
220
286
  return;
221
287
  }
222
288
 
@@ -242,7 +308,9 @@ import { MutationReceipts } from './mutation-receipts';
242
308
  this.refreshMapper();
243
309
  const tempId = '_temp_' + _uuid();
244
310
 
245
- dispatch(children.storeEl, 'ln-data-store:request-create', { tempId: tempId, data: data });
311
+ if (children.storeEl) {
312
+ dispatch(children.storeEl, 'ln-data-store:request-create', { tempId: tempId, data: data });
313
+ }
246
314
 
247
315
  if (children.queue) {
248
316
  dispatch(children.queueEl, 'ln-api-queue:request-enqueue', {
@@ -251,7 +319,7 @@ import { MutationReceipts } from './mutation-receipts';
251
319
  meta: { tempId: tempId, action: action }
252
320
  });
253
321
  } else if (children.connector) {
254
- dispatch(children.connectorEl, 'ln-api-connector:request-create', {
322
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-create', {
255
323
  data: this.mapper.egress(data), url: action,
256
324
  meta: { entryId: _uuid(), queued: false, op: 'create', tempId: tempId }
257
325
  });
@@ -261,7 +329,9 @@ import { MutationReceipts } from './mutation-receipts';
261
329
  _component.prototype._fanOutUpdate = function (children, id, data, expectedVersion, action) {
262
330
  this.refreshMapper();
263
331
 
264
- dispatch(children.storeEl, 'ln-data-store:request-update', { id: id, data: data });
332
+ if (children.storeEl) {
333
+ dispatch(children.storeEl, 'ln-data-store:request-update', { id: id, data: data });
334
+ }
265
335
 
266
336
  if (children.queue) {
267
337
  dispatch(children.queueEl, 'ln-api-queue:request-enqueue', {
@@ -270,7 +340,7 @@ import { MutationReceipts } from './mutation-receipts';
270
340
  meta: { id: id, action: action }
271
341
  });
272
342
  } else if (children.connector) {
273
- dispatch(children.connectorEl, 'ln-api-connector:request-update', {
343
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-update', {
274
344
  id: id, data: this.mapper.egress(data), expected_version: expectedVersion, url: action,
275
345
  meta: { entryId: _uuid(), queued: false, op: 'update', id: id }
276
346
  });
@@ -280,14 +350,16 @@ import { MutationReceipts } from './mutation-receipts';
280
350
  _component.prototype._fanOutDelete = function (children, id) {
281
351
  this.refreshMapper();
282
352
 
283
- dispatch(children.storeEl, 'ln-data-store:request-delete', { id: id });
353
+ if (children.storeEl) {
354
+ dispatch(children.storeEl, 'ln-data-store:request-delete', { id: id });
355
+ }
284
356
 
285
357
  if (children.queue) {
286
358
  dispatch(children.queueEl, 'ln-api-queue:request-enqueue', {
287
359
  chainKey: id, op: 'delete', targetId: id, payload: null, expectedVersion: null, meta: { id: id }
288
360
  });
289
361
  } else if (children.connector) {
290
- dispatch(children.connectorEl, 'ln-api-connector:request-delete', {
362
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-delete', {
291
363
  id: id, meta: { entryId: _uuid(), queued: false, op: 'delete', id: id }
292
364
  });
293
365
  }
@@ -297,14 +369,16 @@ import { MutationReceipts } from './mutation-receipts';
297
369
  this.refreshMapper();
298
370
  const bulkKey = ids.join(',');
299
371
 
300
- dispatch(children.storeEl, 'ln-data-store:request-bulk-delete', { ids: ids });
372
+ if (children.storeEl) {
373
+ dispatch(children.storeEl, 'ln-data-store:request-bulk-delete', { ids: ids });
374
+ }
301
375
 
302
376
  if (children.queue) {
303
377
  dispatch(children.queueEl, 'ln-api-queue:request-enqueue', {
304
378
  chainKey: bulkKey, op: 'bulk-delete', targetId: null, payload: { ids: ids }, expectedVersion: null, meta: { bulkKey: bulkKey, ids: ids }
305
379
  });
306
380
  } else if (children.connector) {
307
- dispatch(children.connectorEl, 'ln-api-connector:request-bulk-delete', {
381
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-bulk-delete', {
308
382
  ids: ids, meta: { entryId: _uuid(), queued: false, op: 'bulk-delete', bulkKey: bulkKey }
309
383
  });
310
384
  }
@@ -314,21 +388,17 @@ import { MutationReceipts } from './mutation-receipts';
314
388
 
315
389
  _component.prototype._toastFromMessage = function (message) {
316
390
  if (!message) return;
317
- window.dispatchEvent(new CustomEvent('ln-toast:enqueue', {
318
- detail: {
319
- type: message.type || 'success',
320
- title: message.title || '',
321
- message: message.body || ''
322
- }
323
- }));
391
+ dispatch(window, 'ln-toast:enqueue', {
392
+ type: message.type || 'success',
393
+ title: message.title || '',
394
+ message: message.body || ''
395
+ });
324
396
  };
325
397
 
326
398
  _component.prototype._toastFromDict = function (key) {
327
399
  const text = this._dict[key];
328
400
  if (!text) return;
329
- window.dispatchEvent(new CustomEvent('ln-toast:enqueue', {
330
- detail: { type: 'error', title: '', message: text }
331
- }));
401
+ dispatch(window, 'ln-toast:enqueue', { type: 'error', title: '', message: text });
332
402
  };
333
403
 
334
404
  _component.prototype._requestStoreMutation = function (children, action, detail) {
@@ -337,15 +407,12 @@ import { MutationReceipts } from './mutation-receipts';
337
407
 
338
408
  const requestId = _uuid();
339
409
  const receipt = this._mutationReceipts.wait(requestId);
340
- try {
341
- dispatch(storeEl, 'ln-data-store:request-' + action, Object.assign({}, detail, { requestId }));
342
- } catch (error) {
343
- this._mutationReceipts.reject({ requestId, error });
344
- }
410
+ dispatch(storeEl, 'ln-data-store:request-' + action, Object.assign({}, detail, { requestId }));
345
411
  return receipt;
346
412
  };
347
413
 
348
414
  _component.prototype._reportReconciliationError = function (operation, error, meta) {
415
+ if (this._destroyed) return;
349
416
  dispatch(this.dom, 'ln-data-coordinator:error', {
350
417
  operation,
351
418
  error,
@@ -364,30 +431,39 @@ import { MutationReceipts } from './mutation-receipts';
364
431
  console.warn('[ln-data-coordinator] Cannot sync: store or connector not found in subtree');
365
432
  return;
366
433
  }
367
- dispatch(children.connectorEl, 'ln-api-connector:request-sync', { since: e.detail.since, meta: { op: 'sync' } });
434
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-sync', { since: e.detail.since, meta: { op: 'sync' } });
435
+ },
436
+
437
+ requestPage: function (e) {
438
+ const children = self.findChildren();
439
+ if (!children.connectorEl) return;
440
+ const detail = e.detail || {};
441
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
442
+ query: Object.assign({}, detail.query, {
443
+ offset: detail.offset,
444
+ limit: detail.limit,
445
+ queryGen: detail.queryGen
446
+ })
447
+ });
368
448
  },
369
449
 
370
450
  reqCreate: function (e) {
371
451
  const children = self.findChildren();
372
- if (!children.storeEl) return;
373
452
  self._fanOutCreate(children, e.detail.data || {}, e.detail.action);
374
453
  },
375
454
 
376
455
  reqUpdate: function (e) {
377
456
  const children = self.findChildren();
378
- if (!children.storeEl) return;
379
457
  self._fanOutUpdate(children, e.detail.id, e.detail.data || {}, e.detail.expected_version, e.detail.action);
380
458
  },
381
459
 
382
460
  reqDelete: function (e) {
383
461
  const children = self.findChildren();
384
- if (!children.storeEl) return;
385
462
  self._fanOutDelete(children, e.detail.id);
386
463
  },
387
464
 
388
465
  reqBulkDelete: function (e) {
389
466
  const children = self.findChildren();
390
- if (!children.storeEl) return;
391
467
  self._fanOutBulkDelete(children, e.detail.ids || []);
392
468
  },
393
469
 
@@ -412,22 +488,22 @@ import { MutationReceipts } from './mutation-receipts';
412
488
  const idempotencyKey = detail.idempotencyKey || entryId;
413
489
 
414
490
  if (op === 'create') {
415
- dispatch(children.connectorEl, 'ln-api-connector:request-create', {
491
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-create', {
416
492
  data: payload, url: resourceUrl, idempotencyKey: idempotencyKey,
417
493
  meta: { entryId: entryId, queued: true, op: 'create', tempId: queueMeta.tempId }
418
494
  });
419
495
  } else if (op === 'update') {
420
- dispatch(children.connectorEl, 'ln-api-connector:request-update', {
496
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-update', {
421
497
  id: targetId, data: payload, expected_version: expectedVersion, url: resourceUrl, idempotencyKey: idempotencyKey,
422
498
  meta: { entryId: entryId, queued: true, op: 'update', id: targetId }
423
499
  });
424
500
  } else if (op === 'delete') {
425
- dispatch(children.connectorEl, 'ln-api-connector:request-delete', {
501
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-delete', {
426
502
  id: targetId, idempotencyKey: idempotencyKey,
427
503
  meta: { entryId: entryId, queued: true, op: 'delete', id: targetId }
428
504
  });
429
505
  } else if (op === 'bulk-delete') {
430
- dispatch(children.connectorEl, 'ln-api-connector:request-bulk-delete', {
506
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-bulk-delete', {
431
507
  ids: (payload && payload.ids) ? payload.ids : [],
432
508
  idempotencyKey: idempotencyKey,
433
509
  meta: { entryId: entryId, queued: true, op: 'bulk-delete', bulkKey: queueMeta.bulkKey }
@@ -447,7 +523,7 @@ import { MutationReceipts } from './mutation-receipts';
447
523
 
448
524
  let isMine;
449
525
  if (scopeAttr) {
450
- isMine = (scopeAttr === self._name);
526
+ isMine = self._owns(scopeAttr);
451
527
  } else {
452
528
  isMine = (form.closest('[data-ln-data-coordinator]') === self.dom);
453
529
  }
@@ -488,8 +564,9 @@ import { MutationReceipts } from './mutation-receipts';
488
564
  // 1. Pass fetched remote data strictly to ln-data-store (Single Source of Truth)
489
565
  if (children.store && !children.store.initializationError) {
490
566
  if (meta.kind) {
491
- if (meta.kind === 'table' || meta.kind === 'list') {
567
+ if (meta.kind === 'table' || meta.kind === 'list' || meta.kind === 'chart') {
492
568
  children.store.applyQuery(normalizedData, { total: e.detail.total }).then(function (decorated) {
569
+ if (meta.queryGen != null && !self._isCurrentGen(meta.targetEl, meta.queryGen)) return;
493
570
  dispatch(meta.targetEl, 'ln-' + meta.kind + ':set-loading', { loading: false });
494
571
  dispatch(meta.targetEl, 'ln-' + meta.kind + ':set-data', {
495
572
  data: decorated,
@@ -504,10 +581,12 @@ import { MutationReceipts } from './mutation-receipts';
504
581
  children.store.applyQuery(normalizedData, { total: e.detail.total }).then(function () {
505
582
  return children.store.getAll({});
506
583
  }).then(function (r) {
584
+ if (meta.queryGen != null && !self._isCurrentGen(meta.targetEl, meta.queryGen)) return;
507
585
  dispatch(meta.targetEl, 'ln-options:set-data', { data: r.data });
508
586
  });
509
587
  } else if (meta.kind === 'stat') {
510
588
  children.store.applyQuery(normalizedData, { total: e.detail.total }).then(function () {
589
+ if (meta.queryGen != null && !self._isCurrentGen(meta.targetEl, meta.queryGen)) return;
511
590
  const count = e.detail.filtered !== undefined
512
591
  ? e.detail.filtered
513
592
  : (e.detail.total !== undefined ? e.detail.total : normalizedData.length);
@@ -524,7 +603,7 @@ import { MutationReceipts } from './mutation-receipts';
524
603
  });
525
604
  }
526
605
  } else if (meta.targetEl && meta.kind) {
527
- if (meta.kind === 'table' || meta.kind === 'list') {
606
+ if (meta.kind === 'table' || meta.kind === 'list' || meta.kind === 'chart') {
528
607
  dispatch(meta.targetEl, 'ln-' + meta.kind + ':set-loading', { loading: false });
529
608
  dispatch(meta.targetEl, 'ln-' + meta.kind + ':set-data', {
530
609
  data: normalizedData,
@@ -547,11 +626,13 @@ import { MutationReceipts } from './mutation-receipts';
547
626
 
548
627
  connCreated: function (e) {
549
628
  const children = self.findChildren();
550
- if (!children.storeEl) return;
551
629
  const meta = e.detail.meta || {};
552
630
  const serverRecord = self.mapper.ingress(e.detail.record);
631
+ const reconciled = children.storeEl
632
+ ? self._requestStoreMutation(children, 'update', { id: meta.tempId, data: serverRecord })
633
+ : Promise.resolve();
553
634
 
554
- self._requestStoreMutation(children, 'update', { id: meta.tempId, data: serverRecord })
635
+ reconciled
555
636
  .then(function () {
556
637
  self._toastFromMessage(e.detail.message);
557
638
  if (meta.queued && children.queue) {
@@ -569,11 +650,13 @@ import { MutationReceipts } from './mutation-receipts';
569
650
 
570
651
  connUpdated: function (e) {
571
652
  const children = self.findChildren();
572
- if (!children.storeEl) return;
573
653
  const meta = e.detail.meta || {};
574
654
  const serverRecord = self.mapper.ingress(e.detail.record);
655
+ const reconciled = children.storeEl
656
+ ? self._requestStoreMutation(children, 'update', { id: meta.id, data: serverRecord })
657
+ : Promise.resolve();
575
658
 
576
- self._requestStoreMutation(children, 'update', { id: meta.id, data: serverRecord })
659
+ reconciled
577
660
  .then(function () {
578
661
  self._toastFromMessage(e.detail.message);
579
662
  if (meta.queued && children.queue) {
@@ -587,7 +670,6 @@ import { MutationReceipts } from './mutation-receipts';
587
670
 
588
671
  connDeleted: function (e) {
589
672
  const children = self.findChildren();
590
- if (!children.storeEl) return;
591
673
  const meta = e.detail.meta || {};
592
674
  // Optimistic delete already applied; no local reconciliation.
593
675
  self._toastFromMessage(e.detail.message); // null on 204 → silent
@@ -598,7 +680,6 @@ import { MutationReceipts } from './mutation-receipts';
598
680
 
599
681
  connBulkDeleted: function (e) {
600
682
  const children = self.findChildren();
601
- if (!children.storeEl) return;
602
683
  const meta = e.detail.meta || {};
603
684
  self._toastFromMessage(e.detail.message);
604
685
  if (meta.queued && children.queue) {
@@ -610,7 +691,7 @@ import { MutationReceipts } from './mutation-receipts';
610
691
  const detail = e.detail || {};
611
692
  const meta = detail.meta || {};
612
693
  const op = meta.op || detail.action;
613
- const status = detail.status || 0;
694
+ const status = detail.status || (detail.error && detail.error.status) || 0;
614
695
  const children = self.findChildren();
615
696
 
616
697
  if (op === 'sync') {
@@ -635,11 +716,9 @@ import { MutationReceipts } from './mutation-receipts';
635
716
  return;
636
717
  }
637
718
 
638
- if (!children.storeEl) return;
639
-
640
- const isAuth = status === 401 || status === 419;
719
+ const isAuth = status === 401 || status === 419;
641
720
  const isTransient = status === 0 || status >= 500;
642
- const isConflict = status === 409;
721
+ const isConflict = status === 409 || status === 412;
643
722
 
644
723
  // ── Auth: pause queue, keep local write ──
645
724
  if (isAuth) {
@@ -666,12 +745,14 @@ import { MutationReceipts } from './mutation-receipts';
666
745
  let reconciliation = Promise.resolve();
667
746
  if (isConflict && op === 'update') {
668
747
  const remote = detail.data && detail.data.remote ? self.mapper.ingress(detail.data.remote) : null;
669
- if (remote) {
748
+ if (remote && children.storeEl) {
670
749
  reconciliation = self._requestStoreMutation(children, 'update', { id: meta.id, data: remote });
671
750
  }
672
751
  self._toastFromDict('conflict');
673
752
  } else if (op === 'create') {
674
- reconciliation = self._requestStoreMutation(children, 'delete', { id: meta.tempId });
753
+ if (children.storeEl) {
754
+ reconciliation = self._requestStoreMutation(children, 'delete', { id: meta.tempId });
755
+ }
675
756
  self._toastFromDict('rejected');
676
757
  } else {
677
758
  // update/delete/bulk generic 4xx (incl. 404): leave local, next sync reconciles
@@ -705,25 +786,83 @@ import { MutationReceipts } from './mutation-receipts';
705
786
  }
706
787
  },
707
788
 
789
+ // A (re)opened socket may have missed pushes — catch up with a delta
790
+ // sync over whichever connector takes requests.
791
+ socketConnected: function () {
792
+ const children = self.findChildren();
793
+ const store = children.store;
794
+ if (!store || store.initializationError || !children.connector || self._noAutosync || !store.isInitialized || store.isSyncing) return;
795
+ store.forceSync();
796
+ },
797
+
708
798
  // ─── View Binder Handlers ─────────────────────────────
709
799
  reqTableData: function (e) { self._serveData(e, 'table'); },
710
- reqListData: function (e) { self._serveData(e, 'list'); },
711
- reqOptions: function (e) { self._serveOptions(e); },
712
- reqStat: function (e) { self._serveStat(e); },
800
+ reqListData: function (e) { self._serveData(e, 'list'); },
801
+ reqChartData: function (e) { self._serveData(e, 'chart'); },
802
+ reqOptions: function (e) { self._serveOptions(e); },
803
+ reqStat: function (e) { self._serveStat(e); },
804
+ refreshQuery: function () { self._refreshAll(null, true); },
713
805
  refresh: function (e) {
714
806
  self._mutationReceipts.resolve(e.detail);
715
- self._refreshAll();
807
+ self._refreshAll(null, false);
716
808
  },
717
809
  mutationError: function (e) {
718
810
  self._mutationReceipts.reject(e.detail);
719
811
  },
720
812
  refreshSynced: function (e) {
721
- if (e.detail && e.detail.changed) self._refreshAll(e.detail.meta);
813
+ if (e.detail && e.detail.changed) self._refreshAll(e.detail.meta, false);
814
+ },
815
+
816
+ searchChange: function (e) {
817
+ e.preventDefault();
818
+ const term = (e.detail && e.detail.term != null) ? e.detail.term : '';
819
+ if (term === (self.dom.getAttribute(SEARCH_ATTR) || '')) return;
820
+ self.dom.setAttribute(SEARCH_ATTR, term);
821
+ },
822
+
823
+ filterChange: function (e) {
824
+ e.preventDefault();
825
+ const key = e.detail && e.detail.key;
826
+ if (!key) return;
827
+ const values = (e.detail.values || []).slice();
828
+ const filters = self._currentQuery().filters;
829
+ const prev = filters[key];
830
+ const unchanged = prev
831
+ ? (prev.length === values.length && prev.every((v, i) => v === values[i]))
832
+ : !values.length;
833
+ if (unchanged) return;
834
+ if (values.length) filters[key] = values;
835
+ else delete filters[key];
836
+ const params = new URLSearchParams();
837
+ Object.keys(filters).forEach(function (k) {
838
+ filters[k].forEach(function (v) { params.append(k, v); });
839
+ });
840
+ const next = params.toString();
841
+ if (next) self.dom.setAttribute(FILTERS_ATTR, next);
842
+ else self.dom.removeAttribute(FILTERS_ATTR);
843
+ },
844
+
845
+ sortChange: function (e) {
846
+ e.preventDefault();
847
+ const field = e.detail && e.detail.field;
848
+ const direction = e.detail && e.detail.direction;
849
+ const next = (field && direction && direction !== 'none') ? { field: field, direction: direction } : null;
850
+ const prev = self._currentQuery().sort;
851
+ const unchanged = (!prev && !next) || (prev && next && prev.field === next.field && prev.direction === next.direction);
852
+ if (unchanged) return;
853
+ if (next) {
854
+ self.dom.setAttribute(SORT_FIELD_ATTR, next.field);
855
+ self.dom.setAttribute(SORT_DIR_ATTR, next.direction);
856
+ } else {
857
+ self.dom.removeAttribute(SORT_FIELD_ATTR);
858
+ self.dom.removeAttribute(SORT_DIR_ATTR);
859
+ }
722
860
  }
723
861
  };
724
862
 
725
863
  // Sync request bubbling up from the child store
726
864
  self.dom.addEventListener('ln-data-store:request-remote-sync', self._handlers.sync);
865
+ self.dom.addEventListener('ln-data-store:request-page', self._handlers.requestPage);
727
866
 
728
867
  // Coordinator-namespaced intake events (parallel fan-out)
729
868
  self.dom.addEventListener('ln-data-coordinator:request-create', self._handlers.reqCreate);
@@ -737,6 +876,7 @@ import { MutationReceipts } from './mutation-receipts';
737
876
 
738
877
  // Sync ownership — store initialization
739
878
  self.dom.addEventListener('ln-data-store:initialized', self._handlers.storeInitialized);
879
+ self.dom.addEventListener('ln-websocket-connector:connected', self._handlers.socketConnected);
740
880
 
741
881
  // Form write intake — native submit, document-level, bubble phase (never
742
882
  // capture: ln-validate's own submit gate on the form must run first)
@@ -754,34 +894,64 @@ import { MutationReceipts } from './mutation-receipts';
754
894
 
755
895
  // View binder — request handlers (document-level to reach tables/lists outside this subtree)
756
896
  document.addEventListener('ln-table:request-data', self._handlers.reqTableData);
757
- document.addEventListener('ln-list:request-data', self._handlers.reqListData);
897
+ document.addEventListener('ln-list:request-data', self._handlers.reqListData);
898
+ document.addEventListener('ln-chart:request-data', self._handlers.reqChartData);
758
899
  document.addEventListener('ln-options:request-data', self._handlers.reqOptions);
759
- document.addEventListener('ln-stat:request-count', self._handlers.reqStat);
900
+ document.addEventListener('ln-stat:request-count', self._handlers.reqStat);
760
901
 
761
902
  // Store-change refresh — attach to self.dom so bubbling store events are caught
762
- self.dom.addEventListener('ln-data-store:ready', self._handlers.refresh);
903
+ self.dom.addEventListener('ln-data-store:ready', self._handlers.refresh);
763
904
  self.dom.addEventListener('ln-data-store:created', self._handlers.refresh);
764
905
  self.dom.addEventListener('ln-data-store:updated', self._handlers.refresh);
765
906
  self.dom.addEventListener('ln-data-store:deleted', self._handlers.refresh);
766
907
  self.dom.addEventListener('ln-data-store:mutation-error', self._handlers.mutationError);
767
- self.dom.addEventListener('ln-data-store:synced', self._handlers.refreshSynced);
908
+ self.dom.addEventListener('ln-data-store:synced', self._handlers.refreshSynced);
909
+ self.dom.addEventListener('ln-data-store:query-changed', self._handlers.refreshQuery);
910
+
911
+ // Query state — owned by the coordinator, written from the axis events
912
+ self.dom.addEventListener('ln-search:change', self._handlers.searchChange);
913
+ self.dom.addEventListener('ln-filter:change', self._handlers.filterChange);
914
+ self.dom.addEventListener('ln-sort:change', self._handlers.sortChange);
768
915
  }
769
916
 
770
917
  // ─── Store↔View Binder ───────────────────────────────────
771
918
 
772
- _component.prototype._ownsStore = function (name) {
773
- const children = this.findChildren();
774
- if (children.store && children.store._name === name && name) return true;
775
- if (this._name === name && name) return true;
776
- return false;
919
+ _component.prototype._owns = function (name) {
920
+ return !!name && name === this._name;
921
+ };
922
+
923
+ // ─── Query State — owned by the coordinator, read live (§1 doctrine) ────
924
+
925
+ _component.prototype._currentQuery = function () {
926
+ const field = this.dom.getAttribute(SORT_FIELD_ATTR);
927
+ const direction = this.dom.getAttribute(SORT_DIR_ATTR);
928
+ const params = new URLSearchParams(this.dom.getAttribute(FILTERS_ATTR) || '');
929
+ const filters = {};
930
+ for (const key of new Set(params.keys())) filters[key] = params.getAll(key);
931
+ return {
932
+ search: this.dom.getAttribute(SEARCH_ATTR) || '',
933
+ filters: filters,
934
+ sort: (field && direction) ? { field: field, direction: direction } : null
935
+ };
936
+ };
937
+
938
+ _component.prototype._nextQueryGen = function (el) {
939
+ const gen = (this._queryGens.get(el) || 0) + 1;
940
+ this._queryGens.set(el, gen);
941
+ return gen;
942
+ };
943
+
944
+ _component.prototype._isCurrentGen = function (el, gen) {
945
+ return this._queryGens.get(el) === gen;
777
946
  };
778
947
 
779
948
  _component.prototype._serveData = function (e, kind) {
780
949
  const el = e.target;
781
- const attrName = kind === 'table' ? 'data-ln-table-store' : 'data-ln-list-store';
782
- const storeName = el.getAttribute(attrName) || el.getAttribute('data-ln-table-source') || el.getAttribute('data-ln-list-source');
950
+ const attrName = kind === 'table' ? 'data-ln-table-source'
951
+ : (kind === 'list' ? 'data-ln-list-source' : 'data-ln-chart-source');
952
+ const storeName = el.getAttribute(attrName);
783
953
  if (!storeName) return;
784
- if (!this._ownsStore(storeName)) return;
954
+ if (!this._owns(storeName)) return;
785
955
 
786
956
  const request = e.detail || {};
787
957
  const query = normalizeDataQuery(request);
@@ -789,17 +959,22 @@ import { MutationReceipts } from './mutation-receipts';
789
959
 
790
960
  const children = this.findChildren();
791
961
  const self = this;
792
- const isWindowed = request.offset != null;
793
962
  const store = children.store;
794
963
  const ready = store && store.ready ? store.ready : Promise.resolve();
795
964
 
796
965
  return ready.then(function () {
797
- const source = selectDataSource(store, children.connector, isWindowed);
966
+ if (self._destroyed) return;
967
+ const source = selectDataSource(store, children.connector);
968
+ const effective = composeQuery(query, self._currentQuery());
798
969
  if (source === 'remote') {
970
+ // The source owns search/filter/sort even when it holds no rows yet —
971
+ // the view only contributes the page window, so the server must be
972
+ // asked with the composed query, not the view's request as it arrived.
973
+ const gen = self._nextQueryGen(el);
799
974
  dispatch(el, 'ln-' + kind + ':set-loading', { loading: true });
800
- dispatch(children.connectorEl, 'ln-api-connector:request-query', {
801
- query: query,
802
- meta: { targetEl: el, kind: kind, offset: query.offset, limit: query.limit }
975
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
976
+ query: effective,
977
+ meta: { targetEl: el, kind: kind, offset: effective.offset, limit: effective.limit, queryGen: gen }
803
978
  });
804
979
  return;
805
980
  }
@@ -809,18 +984,33 @@ import { MutationReceipts } from './mutation-receipts';
809
984
  return;
810
985
  }
811
986
 
812
- return store.getAll(query).then(function (r) {
987
+ const supersede = needsRemoteSupersede(store, children.connector, source);
988
+ const gen = supersede ? self._nextQueryGen(el) : null;
989
+ if (supersede) {
990
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
991
+ query: effective,
992
+ meta: { targetEl: el, kind: kind, offset: effective.offset, limit: effective.limit, queryGen: gen }
993
+ });
994
+ }
995
+
996
+ return store.getAll(effective).then(function (r) {
997
+ if (self._destroyed || !self._boundDelivered) return;
998
+ if (supersede && !self._isCurrentGen(el, gen)) return; // a newer request has already superseded this one
813
999
  const detail = {
814
1000
  data: r.data,
815
1001
  total: r.total,
816
1002
  filtered: r.filtered,
817
1003
  offset: request.offset !== undefined ? request.offset : r.offset,
818
- queryGen: request.queryGen !== undefined ? request.queryGen : r.queryGen
1004
+ queryGen: request.queryGen !== undefined ? request.queryGen : r.queryGen,
1005
+ // The store answered from its own records while the server query
1006
+ // is still out; the view renders it but keeps the refresh showing.
1007
+ provisional: supersede || r.provisional === true
819
1008
  };
820
1009
  dispatch(el, 'ln-' + kind + ':set-data', detail);
821
1010
  self._boundDelivered.set(el, true);
822
1011
  });
823
1012
  }).catch(function (error) {
1013
+ if (self._destroyed) return;
824
1014
  dispatch(el, 'ln-' + kind + ':set-loading', { loading: false });
825
1015
  dispatch(self.dom, 'ln-data-coordinator:error', {
826
1016
  operation: 'query',
@@ -835,7 +1025,7 @@ import { MutationReceipts } from './mutation-receipts';
835
1025
  _component.prototype._serveOptions = function (e) {
836
1026
  const el = e.target;
837
1027
  const name = el.getAttribute('data-ln-options');
838
- if (!this._ownsStore(name)) return;
1028
+ if (!this._owns(name)) return;
839
1029
 
840
1030
  const children = this.findChildren();
841
1031
  const store = children.store;
@@ -843,19 +1033,34 @@ import { MutationReceipts } from './mutation-receipts';
843
1033
  const self = this;
844
1034
 
845
1035
  return ready.then(function () {
846
- const source = selectDataSource(store, children.connector, false);
1036
+ if (self._destroyed) return;
1037
+ const source = selectDataSource(store, children.connector);
847
1038
  if (source === 'remote') {
848
- dispatch(children.connectorEl, 'ln-api-connector:request-query', {
1039
+ const gen = self._nextQueryGen(el);
1040
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
849
1041
  query: {},
850
- meta: { targetEl: el, kind: 'options' }
1042
+ meta: { targetEl: el, kind: 'options', queryGen: gen }
851
1043
  });
852
1044
  return;
853
1045
  }
854
1046
  if (source !== 'store') return;
1047
+
1048
+ const supersede = needsRemoteSupersede(store, children.connector, source);
1049
+ const gen = supersede ? self._nextQueryGen(el) : null;
1050
+ if (supersede) {
1051
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
1052
+ query: {},
1053
+ meta: { targetEl: el, kind: 'options', queryGen: gen }
1054
+ });
1055
+ }
1056
+
855
1057
  return store.getAll({}).then(function (r) {
1058
+ if (self._destroyed) return;
1059
+ if (supersede && !self._isCurrentGen(el, gen)) return;
856
1060
  dispatch(el, 'ln-options:set-data', { data: r.data });
857
1061
  });
858
1062
  }).catch(function (error) {
1063
+ if (self._destroyed) return;
859
1064
  self._reportReconciliationError('options-query', error, { targetEl: el, kind: 'options' });
860
1065
  });
861
1066
  };
@@ -863,7 +1068,7 @@ import { MutationReceipts } from './mutation-receipts';
863
1068
  _component.prototype._serveStat = function (e) {
864
1069
  const el = e.target;
865
1070
  const name = el.getAttribute('data-ln-stat');
866
- if (!this._ownsStore(name)) return;
1071
+ if (!this._owns(name)) return;
867
1072
 
868
1073
  const filters = e.detail && e.detail.filters ? e.detail.filters : null;
869
1074
  const children = this.findChildren();
@@ -872,36 +1077,56 @@ import { MutationReceipts } from './mutation-receipts';
872
1077
  const self = this;
873
1078
 
874
1079
  return ready.then(function () {
875
- const source = selectDataSource(store, children.connector, false);
1080
+ if (self._destroyed) return;
1081
+ const hasFilters = filters && Object.keys(filters).length > 0;
1082
+ const requiresRemote = !!(children.connector && store && ((store.windowed && hasFilters) || store.noLocalQuery));
1083
+ const source = requiresRemote ? 'remote' : selectDataSource(store, children.connector);
876
1084
  if (source === 'remote') {
877
- dispatch(children.connectorEl, 'ln-api-connector:request-query', {
1085
+ const gen = self._nextQueryGen(el);
1086
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
878
1087
  query: { filters: filters },
879
- meta: { targetEl: el, kind: 'stat' }
1088
+ meta: { targetEl: el, kind: 'stat', queryGen: gen }
880
1089
  });
881
1090
  return;
882
1091
  }
883
1092
  if (source !== 'store') return;
1093
+
1094
+ const supersede = !requiresRemote && needsRemoteSupersede(store, children.connector, source);
1095
+ const gen = supersede ? self._nextQueryGen(el) : null;
1096
+ if (supersede) {
1097
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
1098
+ query: { filters: filters },
1099
+ meta: { targetEl: el, kind: 'stat', queryGen: gen }
1100
+ });
1101
+ }
1102
+
884
1103
  return store.count(filters).then(function (n) {
1104
+ if (self._destroyed) return;
1105
+ if (supersede && !self._isCurrentGen(el, gen)) return;
885
1106
  dispatch(el, 'ln-stat:set-count', { count: n });
886
1107
  });
887
1108
  }).catch(function (error) {
1109
+ if (self._destroyed) return;
888
1110
  self._reportReconciliationError('stat-query', error, { targetEl: el, kind: 'stat' });
889
1111
  });
890
1112
  };
891
1113
 
892
- _component.prototype._refreshAll = function (syncMeta) {
1114
+ _component.prototype._refreshAll = function (syncMeta, isQueryChange) {
893
1115
  const self = this;
894
- const allBound = document.querySelectorAll('[data-ln-table-store],[data-ln-list-store],[data-ln-options],[data-ln-stat]');
1116
+ const allBound = document.querySelectorAll('[data-ln-table-source],[data-ln-list-source],[data-ln-chart-source],[data-ln-options],[data-ln-stat]');
895
1117
  for (let i = 0; i < allBound.length; i++) {
896
1118
  const el = allBound[i];
897
1119
  let storeName, kind;
898
1120
 
899
- if (el.hasAttribute('data-ln-table-store')) {
900
- storeName = el.getAttribute('data-ln-table-store');
1121
+ if (el.hasAttribute('data-ln-table-source')) {
1122
+ storeName = el.getAttribute('data-ln-table-source');
901
1123
  kind = 'table';
902
- } else if (el.hasAttribute('data-ln-list-store')) {
903
- storeName = el.getAttribute('data-ln-list-store');
1124
+ } else if (el.hasAttribute('data-ln-list-source')) {
1125
+ storeName = el.getAttribute('data-ln-list-source');
904
1126
  kind = 'list';
1127
+ } else if (el.hasAttribute('data-ln-chart-source')) {
1128
+ storeName = el.getAttribute('data-ln-chart-source');
1129
+ kind = 'chart';
905
1130
  } else if (el.hasAttribute('data-ln-options')) {
906
1131
  storeName = el.getAttribute('data-ln-options');
907
1132
  kind = 'options';
@@ -910,36 +1135,71 @@ import { MutationReceipts } from './mutation-receipts';
910
1135
  kind = 'stat';
911
1136
  }
912
1137
 
913
- if (!this._ownsStore(storeName)) continue;
1138
+ if (!self._owns(storeName)) continue;
914
1139
 
915
- const store = this.findChildren().store;
1140
+ const children = self.findChildren();
1141
+ const store = children.store;
916
1142
 
917
1143
  if (kind === 'table' || kind === 'list') {
918
1144
  const windowAttr = kind === 'table' ? 'data-ln-table-window' : 'data-ln-list-window';
919
1145
  if (el.hasAttribute(windowAttr)) {
920
- dispatch(el, 'ln-' + kind + ':request-revalidate', {});
1146
+ // A windowed view holds no rows to re-serve — it is told to restart
1147
+ // its window (query change) or refresh it in place (post-mutation),
1148
+ // and pulls the pages back through request-data.
1149
+ dispatch(el, 'ln-' + kind + (isQueryChange ? ':request-invalidate' : ':request-revalidate'), {});
921
1150
  continue;
922
1151
  }
1152
+ }
1153
+ if (kind === 'table' || kind === 'list' || kind === 'chart') {
923
1154
  const cached = self._boundQueries.get(el) || { sort: null, filters: {}, search: '' };
924
- (function (capturedEl, capturedKind) {
925
- store.getAll(cached).then(function (r) {
1155
+ const effective = composeQuery(cached, self._currentQuery());
1156
+
1157
+ // Same read policy the view-initiated path applies. Without this the
1158
+ // store-change refresh would query the cache even when the store has
1159
+ // been told to leave queries to the server.
1160
+ if (selectDataSource(store, children.connector) === 'remote') {
1161
+ const gen = self._nextQueryGen(el);
1162
+ dispatch(el, 'ln-' + kind + ':set-loading', { loading: true });
1163
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
1164
+ query: effective,
1165
+ meta: { targetEl: el, kind: kind, offset: effective.offset, limit: effective.limit, queryGen: gen }
1166
+ });
1167
+ continue;
1168
+ }
1169
+
1170
+ const supersede = needsRemoteSupersede(store, children.connector, selectDataSource(store, children.connector));
1171
+ const gen = supersede ? self._nextQueryGen(el) : null;
1172
+ if (supersede) {
1173
+ dispatch(children.connectorEl, _connectorNamespace(children.connectorEl) + ':request-query', {
1174
+ query: effective,
1175
+ meta: { targetEl: el, kind: kind, offset: effective.offset, limit: effective.limit, queryGen: gen }
1176
+ });
1177
+ }
1178
+
1179
+ (function (capturedEl, capturedKind, capturedSupersede, capturedGen) {
1180
+ store.getAll(effective).then(function (r) {
1181
+ if (self._destroyed || !self._boundDelivered) return;
1182
+ if (capturedSupersede && !self._isCurrentGen(capturedEl, capturedGen)) return;
926
1183
  const detail = {
927
1184
  data: r.data,
928
1185
  total: (syncMeta && syncMeta.total !== undefined) ? syncMeta.total : r.total,
929
1186
  filtered: (syncMeta && syncMeta.filtered !== undefined) ? syncMeta.filtered : r.filtered,
930
- offset: (syncMeta && syncMeta.offset !== undefined) ? syncMeta.offset : cached.offset,
931
- queryGen: (syncMeta && syncMeta.queryGen !== undefined) ? syncMeta.queryGen : cached.queryGen
1187
+ offset: (r.offset !== undefined) ? r.offset
1188
+ : ((syncMeta && syncMeta.offset !== undefined) ? syncMeta.offset : cached.offset),
1189
+ queryGen: (r.queryGen !== undefined) ? r.queryGen
1190
+ : ((syncMeta && syncMeta.queryGen !== undefined) ? syncMeta.queryGen : cached.queryGen)
932
1191
  };
933
1192
  dispatch(capturedEl, 'ln-' + capturedKind + ':set-loading', { loading: false });
934
1193
  dispatch(capturedEl, 'ln-' + capturedKind + ':set-data', detail);
935
1194
  self._boundDelivered.set(capturedEl, true);
936
- });
937
- })(el, kind);
1195
+ }).catch(function () {});
1196
+ })(el, kind, supersede, gen);
938
1197
  } else if (kind === 'options') {
939
1198
  (function (capturedEl) {
940
1199
  store.getAll({}).then(function (r) {
1200
+ if (self._destroyed) return;
941
1201
  dispatch(capturedEl, 'ln-options:set-data', { data: r.data });
942
- });
1202
+ }).catch(function () {});
943
1203
  })(el);
944
1204
  } else if (kind === 'stat') {
945
1205
  const raw = el.getAttribute('data-ln-stat-filter');
@@ -947,16 +1207,19 @@ import { MutationReceipts } from './mutation-receipts';
947
1207
  if (raw) {
948
1208
  const colonIdx = raw.indexOf(':');
949
1209
  if (colonIdx !== -1) {
950
- const field = raw.slice(0, colonIdx);
951
- const val = raw.slice(colonIdx + 1);
952
- filters = {};
953
- filters[field] = [val];
1210
+ const field = raw.slice(0, colonIdx).trim();
1211
+ const val = raw.slice(colonIdx + 1).trim();
1212
+ if (field) {
1213
+ filters = {};
1214
+ filters[field] = [val];
1215
+ }
954
1216
  }
955
1217
  }
956
1218
  (function (capturedEl, capturedFilters) {
957
1219
  store.count(capturedFilters).then(function (n) {
1220
+ if (self._destroyed) return;
958
1221
  dispatch(capturedEl, 'ln-stat:set-count', { count: n });
959
- });
1222
+ }).catch(function () {});
960
1223
  })(el, filters);
961
1224
  }
962
1225
  }
@@ -967,9 +1230,11 @@ import { MutationReceipts } from './mutation-receipts';
967
1230
  _component.prototype.destroy = function () {
968
1231
  if (!this.dom[DOM_ATTRIBUTE]) return;
969
1232
 
1233
+ this._destroyed = true;
970
1234
  const self = this;
971
1235
  if (self._handlers) {
972
1236
  self.dom.removeEventListener('ln-data-store:request-remote-sync', self._handlers.sync);
1237
+ self.dom.removeEventListener('ln-data-store:request-page', self._handlers.requestPage);
973
1238
 
974
1239
  self.dom.removeEventListener('ln-data-coordinator:request-create', self._handlers.reqCreate);
975
1240
  self.dom.removeEventListener('ln-data-coordinator:request-update', self._handlers.reqUpdate);
@@ -979,6 +1244,7 @@ import { MutationReceipts } from './mutation-receipts';
979
1244
  self.dom.removeEventListener('ln-api-queue:send', self._handlers.queueSend);
980
1245
  self.dom.removeEventListener('ln-api-queue:failed', self._handlers.queueFailed);
981
1246
  self.dom.removeEventListener('ln-data-store:initialized', self._handlers.storeInitialized);
1247
+ self.dom.removeEventListener('ln-websocket-connector:connected', self._handlers.socketConnected);
982
1248
 
983
1249
  document.removeEventListener('submit', self._handlers.formSubmit);
984
1250
 
@@ -993,23 +1259,31 @@ import { MutationReceipts } from './mutation-receipts';
993
1259
 
994
1260
  // View binder — document-level listeners
995
1261
  document.removeEventListener('ln-table:request-data', self._handlers.reqTableData);
996
- document.removeEventListener('ln-list:request-data', self._handlers.reqListData);
1262
+ document.removeEventListener('ln-list:request-data', self._handlers.reqListData);
1263
+ document.removeEventListener('ln-chart:request-data', self._handlers.reqChartData);
997
1264
  document.removeEventListener('ln-options:request-data', self._handlers.reqOptions);
998
- document.removeEventListener('ln-stat:request-count', self._handlers.reqStat);
1265
+ document.removeEventListener('ln-stat:request-count', self._handlers.reqStat);
999
1266
 
1000
1267
  // Store-change listeners
1001
- self.dom.removeEventListener('ln-data-store:ready', self._handlers.refresh);
1268
+ self.dom.removeEventListener('ln-data-store:ready', self._handlers.refresh);
1002
1269
  self.dom.removeEventListener('ln-data-store:created', self._handlers.refresh);
1003
1270
  self.dom.removeEventListener('ln-data-store:updated', self._handlers.refresh);
1004
1271
  self.dom.removeEventListener('ln-data-store:deleted', self._handlers.refresh);
1005
1272
  self.dom.removeEventListener('ln-data-store:mutation-error', self._handlers.mutationError);
1006
- self.dom.removeEventListener('ln-data-store:synced', self._handlers.refreshSynced);
1273
+ self.dom.removeEventListener('ln-data-store:synced', self._handlers.refreshSynced);
1274
+ self.dom.removeEventListener('ln-data-store:query-changed', self._handlers.refreshQuery);
1275
+
1276
+ self.dom.removeEventListener('ln-search:change', self._handlers.searchChange);
1277
+ self.dom.removeEventListener('ln-filter:change', self._handlers.filterChange);
1278
+ self.dom.removeEventListener('ln-sort:change', self._handlers.sortChange);
1007
1279
 
1008
1280
  self._handlers = null;
1009
1281
  }
1010
1282
 
1011
1283
  self._boundQueries = null;
1012
1284
  self._boundDelivered = null;
1285
+ self._queryGens = null;
1286
+ self._queueQueryRefresh = null;
1013
1287
  self._mutationReceipts.close(new Error('Data coordinator destroyed'));
1014
1288
  self._mutationReceipts = null;
1015
1289
 
@@ -1017,26 +1291,9 @@ import { MutationReceipts } from './mutation-receipts';
1017
1291
  _uninstallGlobalSync();
1018
1292
 
1019
1293
  delete this.dom[DOM_ATTRIBUTE];
1020
- delete this.dom[DOM_ALIAS];
1021
1294
  };
1022
1295
 
1023
- // ─── Attribute Sync ────────────────────────────────────────
1024
-
1025
- function _syncAttribute(el, attrName) {
1026
- const instance = el[DOM_ATTRIBUTE];
1027
- if (!instance) return;
1028
-
1029
- if (attrName === 'data-ln-data-mapper') {
1030
- instance.refreshMapper();
1031
- }
1032
- }
1033
-
1034
- // ─── Registration ──────────────────────────────────────
1035
-
1036
1296
  registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-data-coordinator', {
1037
- extraAttributes: [
1038
- 'data-ln-data-mapper'
1039
- ],
1040
- onAttributeChange: _syncAttribute
1297
+ attributes: ATTRIBUTES
1041
1298
  });
1042
1299
  })();