@livenetworks/ashlar 1.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +202 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/components/ln-confirm/README.md +216 -0
  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 -167
  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/README.md +0 -162
  414. package/js/ln-confirm/_ln-confirm.scss +0 -24
  415. package/js/ln-confirm/ln-confirm.js +0 -1
  416. package/js/ln-core/crypto.js +0 -83
  417. package/js/ln-core/helpers.js +0 -788
  418. package/js/ln-core/index.js +0 -7
  419. package/js/ln-core/persist.js +0 -97
  420. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  421. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  422. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  423. package/js/ln-data-store/ln-data-store.js +0 -1
  424. package/js/ln-date/ln-date.js +0 -1
  425. package/js/ln-date/src/ln-date.js +0 -603
  426. package/js/ln-debug/README.md +0 -58
  427. package/js/ln-debug/ln-debug.js +0 -1
  428. package/js/ln-debug/src/ln-debug.js +0 -19
  429. package/js/ln-dropdown/ln-dropdown.js +0 -1
  430. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  431. package/js/ln-editor/ln-editor.js +0 -1
  432. package/js/ln-editor/ln-editor.scss +0 -43
  433. package/js/ln-external-links/ln-external-links.js +0 -1
  434. package/js/ln-fill/ln-fill.js +0 -1
  435. package/js/ln-filter/README.md +0 -189
  436. package/js/ln-filter/ln-filter-dev.scss +0 -8
  437. package/js/ln-filter/ln-filter.js +0 -1
  438. package/js/ln-filter/src/ln-filter.js +0 -419
  439. package/js/ln-form/ln-form.js +0 -1
  440. package/js/ln-http/ln-http.js +0 -1
  441. package/js/ln-link/ln-link.js +0 -1
  442. package/js/ln-list/README.md +0 -129
  443. package/js/ln-list/ln-list.js +0 -1
  444. package/js/ln-list/ln-list.scss +0 -28
  445. package/js/ln-modal/ln-modal-dev.scss +0 -13
  446. package/js/ln-modal/ln-modal.js +0 -1
  447. package/js/ln-modal/ln-modal.scss +0 -35
  448. package/js/ln-modal-coordinator/README.md +0 -99
  449. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  450. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  451. package/js/ln-nav/ln-nav.js +0 -1
  452. package/js/ln-number/README.md +0 -149
  453. package/js/ln-number/ln-number.js +0 -1
  454. package/js/ln-number/src/ln-number.js +0 -443
  455. package/js/ln-options/ln-options.js +0 -1
  456. package/js/ln-popover/ln-popover.js +0 -1
  457. package/js/ln-progress/ln-progress.js +0 -1
  458. package/js/ln-progress/src/ln-progress.js +0 -98
  459. package/js/ln-router/ln-router.js +0 -1
  460. package/js/ln-search/README.md +0 -158
  461. package/js/ln-search/ln-search-dev.scss +0 -8
  462. package/js/ln-search/ln-search.js +0 -1
  463. package/js/ln-search/src/ln-search.js +0 -117
  464. package/js/ln-slug/ln-slug.js +0 -1
  465. package/js/ln-sort/ln-sort.js +0 -1
  466. package/js/ln-sort/src/ln-sort.js +0 -178
  467. package/js/ln-sortable/ln-sortable.js +0 -1
  468. package/js/ln-stat/ln-stat.js +0 -1
  469. package/js/ln-table/ln-table.js +0 -1
  470. package/js/ln-table/ln-table.scss +0 -11
  471. package/js/ln-table-coordinator/README.md +0 -73
  472. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  473. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  474. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  475. package/js/ln-tabs/ln-tabs.js +0 -1
  476. package/js/ln-tabs/src/ln-tabs.js +0 -188
  477. package/js/ln-time/ln-time.js +0 -1
  478. package/js/ln-toast/ln-toast.js +0 -1
  479. package/js/ln-toggle/ln-toggle.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.js +0 -1
  481. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  482. package/js/ln-translations/ln-translations.js +0 -1
  483. package/js/ln-upload/README.md +0 -255
  484. package/js/ln-upload/ln-upload.js +0 -1
  485. package/js/ln-upload/ln-upload.scss +0 -20
  486. package/js/ln-upload/src/ln-upload.js +0 -423
  487. package/js/ln-validate/ln-validate.js +0 -1
  488. package/scss/components/_button.scss +0 -20
  489. package/scss/components/_confirm.scss +0 -5
  490. package/scss/components/_form.scss +0 -107
  491. package/scss/components/_status-badge.scss +0 -4
  492. package/scss/components/_toast.scss +0 -68
  493. package/scss/components/_upload.scss +0 -51
  494. package/scss/config/_theme.scss +0 -216
  495. package/scss/config/mixins/_btn.scss +0 -179
  496. package/scss/config/mixins/_confirm.scss +0 -31
  497. package/scss/config/mixins/_dropdown.scss +0 -141
  498. package/scss/config/mixins/_popover.scss +0 -64
  499. package/scss/ln-ashlar-dev.scss +0 -39
  500. /package/{js → components}/ln-core/positioning.js +0 -0
  501. /package/{js → components}/ln-core/reactive.js +0 -0
  502. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  503. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  504. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  505. /package/{js → components}/ln-search/ln-search.scss +0 -0
  506. /package/{js → components}/ln-toast/template.html +0 -0
  507. /package/{scss → theme}/components/_accordion.scss +0 -0
  508. /package/{scss → theme}/components/_ajax.scss +0 -0
  509. /package/{scss → theme}/components/_alert.scss +0 -0
  510. /package/{scss → theme}/components/_avatar.scss +0 -0
  511. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  512. /package/{scss → theme}/components/_card.scss +0 -0
  513. /package/{scss → theme}/components/_chip.scss +0 -0
  514. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  515. /package/{scss → theme}/components/_editor.scss +0 -0
  516. /package/{scss → theme}/components/_empty-state.scss +0 -0
  517. /package/{scss → theme}/components/_filter.scss +0 -0
  518. /package/{scss → theme}/components/_link.scss +0 -0
  519. /package/{scss → theme}/components/_loader.scss +0 -0
  520. /package/{scss → theme}/components/_modal.scss +0 -0
  521. /package/{scss → theme}/components/_nav.scss +0 -0
  522. /package/{scss → theme}/components/_page-header.scss +0 -0
  523. /package/{scss → theme}/components/_popover.scss +0 -0
  524. /package/{scss → theme}/components/_progress.scss +0 -0
  525. /package/{scss → theme}/components/_prose.scss +0 -0
  526. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  527. /package/{scss → theme}/components/_sections.scss +0 -0
  528. /package/{scss → theme}/components/_sidebar.scss +0 -0
  529. /package/{scss → theme}/components/_stat-card.scss +0 -0
  530. /package/{scss → theme}/components/_stepper.scss +0 -0
  531. /package/{scss → theme}/components/_timeline.scss +0 -0
  532. /package/{scss → theme}/components/_tooltip.scss +0 -0
  533. /package/{scss → theme}/components/_translations.scss +0 -0
  534. /package/{scss → theme}/config/_icons.scss +0 -0
  535. /package/{scss → theme}/config/_mixins.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  559. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  560. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -0,0 +1,176 @@
1
+ import { cloneTemplateScoped, dispatch, fillTemplate, formatNumber, getLocale, registerComponent, defineAttrs, attrSpec, attrStr } from '../../ln-core';
2
+ import { buildChartModel, parseChartSort, parseChartViewBox } from './chart-model.js';
3
+
4
+ (function () {
5
+ const DOM_SELECTOR = 'data-ln-chart';
6
+ const DOM_ATTRIBUTE = 'lnChart';
7
+ const DEFAULT_VIEW_BOX = { x: 0, y: 0, width: 1000, height: 320 };
8
+ if (window[DOM_ATTRIBUTE] !== undefined) return;
9
+
10
+ // ─── Attribute Contract (SSOT) ──────────────────────────
11
+ function _requestData(el) {
12
+ const instance = el[DOM_ATTRIBUTE];
13
+ if (!instance) return;
14
+ instance.requestData();
15
+ }
16
+
17
+ function _renderChart(el) {
18
+ const instance = el[DOM_ATTRIBUTE];
19
+ if (!instance) return;
20
+ instance._render();
21
+ }
22
+
23
+ const ATTRIBUTES = {
24
+ 'data-ln-chart': { prop: 'name', type: 'string', read: attrStr, fallback: '', effect: _renderChart, description: 'Chart instance name or identifier' },
25
+ 'data-ln-chart-source': { type: 'string', effect: _requestData, description: 'Source store or coordinator identifier' },
26
+ 'data-ln-chart-sort': { type: 'string', effect: _requestData, description: 'Field name to sort chart series data by' },
27
+ 'data-ln-chart-type': { type: 'enum', values: ['line', 'bar', 'area', 'scatter'], fallback: 'line', effect: _renderChart, description: 'Visual chart render type' },
28
+ 'data-ln-chart-x': { type: 'string', effect: _renderChart, description: 'Field name mapping to X axis' },
29
+ 'data-ln-chart-y': { type: 'string', effect: _renderChart, description: 'Field name mapping to Y axis' },
30
+ 'data-ln-chart-padding': { type: 'integer', fallback: 20, min: 0, effect: _renderChart, description: 'Internal plot padding in pixels' },
31
+ 'data-ln-chart-zero': { type: 'boolean', effect: _renderChart, description: 'Forces Y axis scale to start at zero' }
32
+ };
33
+ const ATTR_SPEC = attrSpec(ATTRIBUTES);
34
+
35
+ function setText(element, value) {
36
+ if (element) element.textContent = value;
37
+ }
38
+
39
+ function _component(dom) {
40
+ this.dom = dom;
41
+ defineAttrs(this, dom, ATTR_SPEC);
42
+ this.source = dom.getAttribute('data-ln-chart-source') || this.name;
43
+ this.plot = dom.querySelector('[data-ln-chart-plot]');
44
+ this.line = dom.querySelector('[data-ln-chart-line]');
45
+ this.area = dom.querySelector('[data-ln-chart-area]');
46
+ this.labels = dom.querySelector('[data-ln-chart-labels]');
47
+ this.empty = dom.querySelector('[data-ln-chart-empty]');
48
+ this.minimum = dom.querySelector('[data-ln-chart-min]');
49
+ this.maximum = dom.querySelector('[data-ln-chart-max]');
50
+ this.count = dom.querySelector('[data-ln-chart-count]');
51
+ this._data = [];
52
+ this.model = null;
53
+ this.isLoaded = false;
54
+
55
+ const self = this;
56
+ this._onSetData = function (event) {
57
+ const detail = event.detail || {};
58
+ self._data = Array.isArray(detail.data) ? detail.data : [];
59
+ self.isLoaded = true;
60
+ self._setLoading(false);
61
+ self._render();
62
+ };
63
+ this._onSetLoading = function (event) {
64
+ self._setLoading(!!(event.detail && event.detail.loading));
65
+ };
66
+ this._onRefresh = function () {
67
+ self.requestData();
68
+ };
69
+
70
+ dom.addEventListener('ln-chart:set-data', this._onSetData);
71
+ dom.addEventListener('ln-chart:set-loading', this._onSetLoading);
72
+ dom.addEventListener('ln-chart:request-refresh', this._onRefresh);
73
+
74
+ this.requestData();
75
+ return this;
76
+ }
77
+
78
+ _component.prototype._readOptions = function () {
79
+ const rawPadding = this.dom.getAttribute('data-ln-chart-padding');
80
+ const parsedPadding = rawPadding === null ? NaN : Number(rawPadding);
81
+ const type = (this.dom.getAttribute('data-ln-chart-type') || 'line').toLowerCase();
82
+ return {
83
+ xField: this.dom.getAttribute('data-ln-chart-x') || 'label',
84
+ yField: this.dom.getAttribute('data-ln-chart-y') || 'value',
85
+ includeZero: this.dom.getAttribute('data-ln-chart-zero') !== 'false',
86
+ padding: Number.isFinite(parsedPadding) && parsedPadding >= 0 ? parsedPadding : 16,
87
+ type: type === 'area' || type === 'polygon' ? 'area' : 'line',
88
+ viewBox: this.plot ? (parseChartViewBox(this.plot.getAttribute('viewBox')) || DEFAULT_VIEW_BOX) : DEFAULT_VIEW_BOX
89
+ };
90
+ };
91
+
92
+ _component.prototype._setLoading = function (loading) {
93
+ this.dom.classList.toggle('ln-chart--loading', loading);
94
+ this.dom.setAttribute('aria-busy', loading ? 'true' : 'false');
95
+ };
96
+
97
+ _component.prototype._renderLabels = function (model) {
98
+ if (!this.labels) return;
99
+ this.labels.replaceChildren();
100
+ if (model.count === 0) return;
101
+
102
+ const templateName = this.name + '-label';
103
+ const selector = '[data-ln-template="' + templateName + '"]';
104
+ if (!this.dom.querySelector(selector) && !document.querySelector(selector)) return;
105
+
106
+ const prototype = cloneTemplateScoped(this.dom, templateName, 'ln-chart');
107
+ if (!prototype) return;
108
+
109
+ const locale = getLocale(this.dom);
110
+ for (const point of model.points) {
111
+ const clone = prototype.cloneNode(true);
112
+ fillTemplate(clone, {
113
+ label: point.label,
114
+ value: formatNumber(point.value, locale)
115
+ });
116
+ this.labels.appendChild(clone);
117
+ }
118
+ };
119
+
120
+ _component.prototype._render = function () {
121
+ const options = this._readOptions();
122
+ const model = buildChartModel(this._data, options);
123
+ this.model = model;
124
+
125
+ if (this.line) {
126
+ this.line.setAttribute('points', model.linePoints);
127
+ this.line.toggleAttribute('hidden', model.count === 0);
128
+ }
129
+ if (this.area) {
130
+ this.area.setAttribute('points', model.areaPoints);
131
+ this.area.toggleAttribute('hidden', model.count === 0 || options.type !== 'area');
132
+ }
133
+
134
+ const isEmpty = model.count === 0;
135
+ this.dom.classList.toggle('ln-chart--empty', isEmpty);
136
+ if (this.empty) this.empty.toggleAttribute('hidden', !isEmpty);
137
+
138
+ const locale = getLocale(this.dom);
139
+ setText(this.minimum, formatNumber(model.min, locale));
140
+ setText(this.maximum, formatNumber(model.max, locale));
141
+ setText(this.count, formatNumber(model.count, locale));
142
+ this._renderLabels(model);
143
+
144
+ dispatch(this.dom, 'ln-chart:rendered', {
145
+ chart: this.name,
146
+ count: model.count,
147
+ min: model.min,
148
+ max: model.max
149
+ });
150
+ };
151
+
152
+ _component.prototype.requestData = function () {
153
+ this.source = this.dom.getAttribute('data-ln-chart-source') || this.name;
154
+ dispatch(this.dom, 'ln-chart:request-data', {
155
+ chart: this.name,
156
+ source: this.source,
157
+ sort: parseChartSort(this.dom.getAttribute('data-ln-chart-sort')),
158
+ filters: {},
159
+ search: ''
160
+ });
161
+ };
162
+
163
+ _component.prototype.destroy = function () {
164
+ if (!this.dom[DOM_ATTRIBUTE]) return;
165
+ this.dom.removeEventListener('ln-chart:set-data', this._onSetData);
166
+ this.dom.removeEventListener('ln-chart:set-loading', this._onSetLoading);
167
+ this.dom.removeEventListener('ln-chart:request-refresh', this._onRefresh);
168
+ this._data = [];
169
+ this.model = null;
170
+ delete this.dom[DOM_ATTRIBUTE];
171
+ };
172
+
173
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _component, 'ln-chart', {
174
+ attributes: ATTRIBUTES
175
+ });
176
+ })();
@@ -1,7 +1,8 @@
1
1
  # ln-circular-progress
2
2
 
3
- > Attribute-driven SVG ring renderer. Set `data-ln-circular-progress="75"` on an
4
- > empty host; the component builds the SVG, watches the attribute, and redraws on every change. The attribute IS the state — no imperative setter.
3
+ > Applied to an empty host element via `data-ln-circular-progress="value"`. On initialization, it injects an inline `<svg>`
4
+ > ring and a `<strong>` percentage label. An attribute observer watches `data-ln-circular-progress`: it calculates the circle's
5
+ > `stroke-dashoffset` from the clamped percentage, updates `strong.textContent`, and synchronizes `aria-valuenow` on the host.
5
6
 
6
7
  ## Quick start
7
8
 
@@ -26,7 +27,7 @@ The host element must be empty at init — the constructor `appendChild`s the SV
26
27
 
27
28
  **Size variants** — apply a class to the host: `.sm` (2.5 rem), default (4 rem), `.lg` (6 rem), `.xl` (8 rem). Each rebinds the label font-size.
28
29
 
29
- **Colour variants** — `.success`, `.warning`, `.error` override the fill stroke via `scss/components/_circular-progress.scss`. The default fill reads `--color-accent` and follows the active theme.
30
+ **Colour variants** — `.success`, `.warning`, `.error` override the fill stroke via `theme/components/_circular-progress.scss`. The default fill reads `--color-accent` and follows the active theme.
30
31
 
31
32
  ## Events
32
33
 
@@ -71,7 +72,7 @@ The component sets `aria-hidden="true"` on the constructed `<svg>` — screen re
71
72
  | `trackCircle` | `SVGCircleElement` | Background circle (stroke = `--color-border`) |
72
73
  | `progressCircle` | `SVGCircleElement` | Fill circle — `stroke-dashoffset` carries the progress |
73
74
  | `labelEl` | `HTMLElement` | The `<strong>` element holding the percentage or custom label text |
74
- | `destroy()` | method | Disconnects the attribute observer, removes the SVG and label from DOM, deletes `el.lnCircularProgress`. Leaves the value attribute, colour class, and size class in place — setting the value attribute again re-instantiates. |
75
+ | `destroy()` | method | Removes the SVG and label from DOM, deletes `el.lnCircularProgress`. Leaves the value attribute, colour class, and size class in place — setting the value attribute again re-instantiates. |
75
76
 
76
77
  `window.lnCircularProgress(root)` re-runs the init scan over `root`. The shared `registerComponent` observer already covers AJAX inserts; call this manually only for Shadow DOM roots or foreign documents the observer cannot reach.
77
78
 
@@ -135,16 +136,16 @@ Include the main Ashlar bundle in your HTML. This bundle registers all Ashlar co
135
136
  ### 2. Standalone (Zero-Dependency IIFE)
136
137
  If you only need circular progress indicators without the rest of the Ashlar library, load the component's standalone IIFE script:
137
138
  ```html
138
- <script src="js/ln-circular-progress/ln-circular-progress.js" defer></script>
139
+ <script src="components/ln-circular-progress/ln-circular-progress.js" defer></script>
139
140
  ```
140
141
 
141
142
  ### 3. Source & Reference
142
- * **Active Development Source**: [js/ln-circular-progress/src/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-circular-progress/src/ln-circular-progress.js) — The source of truth containing the ES module implementation.
143
- * **Compiled Standalone Release**: [js/ln-circular-progress/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-circular-progress/ln-circular-progress.js) — The compiled IIFE distribution file.
143
+ * **Active Development Source**: [components/ln-circular-progress/src/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/components/ln-circular-progress/src/ln-circular-progress.js) — The source of truth containing the ES module implementation.
144
+ * **Compiled Standalone Release**: [components/ln-circular-progress/ln-circular-progress.js](file:///c:/laragon/www/ln-ashlar/components/ln-circular-progress/ln-circular-progress.js) — The compiled IIFE distribution file.
144
145
 
145
146
  ## 🔧 Internals
146
147
 
147
- Source: `js/ln-circular-progress/ln-circular-progress.js`. Passive renderer — owns no data; `_render` re-reads all three attributes fresh on every call, no cached value.
148
+ Source: `components/ln-circular-progress/ln-circular-progress.js`. Passive renderer — owns no data; `_render` re-reads all three attributes fresh on every call, no cached value.
148
149
 
149
150
  ### SVG geometry
150
151
 
@@ -156,20 +157,20 @@ Fixed module-level constants: `VIEW_SIZE = 36`, `RADIUS = 16`, `CIRCUMFERENCE =
156
157
  - Clamping happens at the percentage level, not the attribute level: `detail.value` stays the raw unclamped number, `detail.percentage` is the 0–100 clamp used for the arc — this is how a consumer distinguishes overshoot from the visible fill.
157
158
  - ARIA (`role`, `aria-valuemin/max/now`, `aria-valuetext`) is rewritten on every render on the host element itself, not the SVG (which is `aria-hidden`).
158
159
 
159
- ### What the per-instance observer watches
160
+ ### Attribute observation
160
161
 
161
- `_listenValues` filters to the three state attributes only — deliberately excluding `class`. Colour/size variants are pure CSS; nothing about the SVG geometry depends on the class, so no JS reaction is needed.
162
+ Attribute mutations (`data-ln-circular-progress`, `-max`, `-label`) are handled by `registerComponent`'s shared observer via `onAttributeChange`, calling `_render` directly without any per-instance MutationObserver. Colour/size variants are pure CSS, so no JS reaction to class changes is needed.
162
163
 
163
164
  ### Destroy
164
165
 
165
- Disconnects the attribute observer, removes the SVG and label nodes, removes the ARIA attributes it added, deletes `el.lnCircularProgress`. Leaves the value attribute and any classes in place — a later write to the value attribute re-triggers `registerComponent`'s shared observer and rebuilds the instance from scratch (`_buildSvg` is not idempotent; it always appends new nodes).
166
+ Removes the SVG and label nodes, deletes `el.lnCircularProgress`. Leaves the host attributes in place — a later write to the value attribute re-triggers `registerComponent`'s shared observer and rebuilds the instance from scratch (`_buildSvg` is not idempotent; it always appends new nodes).
166
167
 
167
168
  ---
168
169
 
169
170
  ## See also
170
171
 
171
- - `@mixin circular-progress` (`scss/config/mixins/_circular-progress.scss`) — SVG layout, fill/track stroke tokens, motion-safe transition, size variant mixins.
172
- - `scss/components/_circular-progress.scss` — default selector application and the `.success` / `.warning` / `.error` colour variant rules.
172
+ - `@mixin circular-progress` (`theme/config/mixins/_circular-progress.scss`) — SVG layout, fill/track stroke tokens, motion-safe transition, size variant mixins.
173
+ - `theme/components/_circular-progress.scss` — default selector application and the `.success` / `.warning` / `.error` colour variant rules.
173
174
  - [`ln-progress`](../ln-progress/README.md) — linear sibling. Same contract shape (`data-ln-progress` + `-max`); supports parent-track shared max.
174
- - `@mixin loader` (`scss/config/mixins/_loader.scss`) — indeterminate spinner for operations with no known fraction.
175
+ - `@mixin loader` (`theme/config/mixins/_loader.scss`) — indeterminate spinner for operations with no known fraction.
175
176
  - [`docs/architecture/data-flow.md`](../../docs/architecture/data-flow.md) — passive renderer pattern used by this component.
@@ -0,0 +1,8 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Identification Check: circular progress should have an id for identification/linking
5
+ [data-ln-circular-progress]:not([id]) {
6
+ @include dev-dom-error("[data-ln-circular-progress] is missing an 'id' attribute.");
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function L(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function y(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const t=e.closest("[data-ln-debug]");return!(!t||typeof document<"u"&&t===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...t){if(typeof t[0]=="string"&&(t[0].startsWith("[ln-")||t[0].startsWith("[lnCore"))){let o=null;for(let l=1;l<t.length;l++)if(t[l]&&t[l].nodeType===1){o=t[l];break}if(!y(o))return}e.apply(console,t)}}function $(e,t,n){const o=n||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",t,e,o),e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:o}))}function m(e,t){if(!document.body){document.addEventListener("DOMContentLoaded",function(){m(e,t)}),console.warn("["+t+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const I={};function W(e,t){I[e]=t}function j(e){return I[e]||{ingress:t=>t,egress:t=>t}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=W,window.lnCore.getDataMapper=j);function H(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,m(function(){new MutationObserver(function(){$(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const T={};function U(e,t){if(!e||typeof t!="object")return;const n=e.toLowerCase().split("-")[0];T[n]=t}function Q(e){if(!e)return null;const t=e.toLowerCase().split("-")[0];return T[t]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=U,window.lnCore.getLocaleFallback=Q,window.lnCore.ensureLocaleObserver=H);const A={};function K(e,t){A[e]||(A[e]=document.querySelector('[data-ln-template="'+e+'"]'));const n=A[e];return n?n.content.cloneNode(!0):(console.warn("["+(t||"ln-core")+'] Template "'+e+'" not found'),null)}function D(e,t){if(!e||!t)return e;const n=e.querySelectorAll("[data-ln-field]");for(let i=0;i<n.length;i++){const c=n[i],s=c.getAttribute("data-ln-field");t[s]!=null&&(c.textContent=t[s])}const o=e.querySelectorAll("[data-ln-attr]");for(let i=0;i<o.length;i++){const c=o[i],s=c.getAttribute("data-ln-attr").split(",");for(let a=0;a<s.length;a++){const d=s[a].trim().split(":");if(d.length!==2)continue;const u=d[0].trim(),f=d[1].trim();t[f]!=null&&c.setAttribute(u,t[f])}}const l=e.querySelectorAll("[data-ln-show]");for(let i=0;i<l.length;i++){const c=l[i],s=c.getAttribute("data-ln-show");s in t&&c.classList.toggle("hidden",!t[s])}const r=e.querySelectorAll("[data-ln-class]");for(let i=0;i<r.length;i++){const c=r[i],s=c.getAttribute("data-ln-class").split(",");for(let a=0;a<s.length;a++){const d=s[a].trim().split(":");if(d.length!==2)continue;const u=d[0].trim(),f=d[1].trim();f in t&&c.classList.toggle(u,!!t[f])}}return e}function P(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 o=0;o<n.length;o++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",n[o],t??null),n[o].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)D(e.target,e.detail);else{const t=e.target.querySelectorAll("[data-ln-field]");for(let n=0;n<t.length;n++)t[n].textContent=""}})));function M(e,t){if(!e||!t)return e;const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;n.nextNode();){const r=n.currentNode;r.textContent.indexOf("{{")!==-1&&(r.textContent=r.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(i,c){return t[c]!==void 0?t[c]:""}))}const o=function(r,i){return t[i]!==void 0?t[i]:""},l=Array.from(e.querySelectorAll("*"));e.nodeType===1&&l.push(e);for(let r=0;r<l.length;r++){const i=l[r],c=i.attributes;for(let s=0;s<c.length;s++){const a=c[s];a.value.indexOf("{{")!==-1&&i.setAttribute(a.name,a.value.replace(/\{\{\s*(\w+)\s*\}\}/g,o))}}return e}function G(e,t,n,o,l,r){const i={};for(let s=0;s<e.children.length;s++){const a=e.children[s],d=a.getAttribute("data-ln-render-key");d&&(i[d]=a)}const c=document.createDocumentFragment();for(let s=0;s<t.length;s++){const a=t[s],d=String(o(a));let u=i[d];if(u)l(u,a,s);else{const f=K(n,r);if(!f||(M(f,a),u=f.firstElementChild,!u))continue;u.setAttribute("data-ln-render-key",d),l(u,a,s)}c.appendChild(u)}e.textContent="",e.appendChild(c)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=M,window.lnCore.fill=D,window.lnCore.lnFill=P,window.lnCore.renderList=G);function R(e,t,n,o,l){if(!e||t===null)return!0;const r=o||"ln-component",i=e.type;if(i==="trigger"||i==="marker"||i==="string"||i==="list"||!i||t===""&&e.fallback!==void 0)return!0;const c=s=>{l?console.warn(s,l):console.warn(s)};if(i==="boolean"){const s=t.trim().toLowerCase();return s!==""&&s!=="true"&&s!=="false"&&s!=="1"&&s!=="0"?(c(`[${r}] Invalid value "${t}" for boolean attribute "${n}". Allowed: "true", "false", or presence-only.`),!1):!0}if(i==="enum"){const s=e.values||[];return s.includes(t)?!0:(c(`[${r}] Invalid value "${t}" for attribute "${n}". Allowed: ${s.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(i==="integer"){if(!/^-?\d+$/.test(t))return c(`[${r}] Invalid integer "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1;const s=parseInt(t,10);return e.min!==void 0&&s<e.min?(c(`[${r}] Value ${s} for attribute "${n}" is less than min (${e.min}).`),!1):e.max!==void 0&&s>e.max?(c(`[${r}] Value ${s} for attribute "${n}" is greater than max (${e.max}).`),!1):!0}if(i==="float")return isNaN(Number(t))?(c(`[${r}] Invalid float "${t}" for attribute "${n}". Fallback: ${e.fallback}.`),!1):!0;if(i==="json")try{return JSON.parse(t),!0}catch(s){return c(`[${r}] Invalid JSON for attribute "${n}": ${s.message}. Fallback: ${e.fallback}.`),!1}return!0}function J(e){const t={};for(const n in e){const o=e[n];o&&o.effect&&(t[n]=o.effect)}return Object.keys(t).length?t:null}function X(e){if(e.onAttrChange&&!e.declared)return null;const t=new Set;if(e.effects)for(const n in e.effects)t.add(n);if(e.onAttrChange&&e.declared)for(const n of e.declared)t.add(n);return t}function v(e,t,n,o){if(e.nodeType!==1)return;const r=t.indexOf("[")!==-1||t.indexOf(".")!==-1||t.indexOf("#")!==-1?t:"["+t+"]",i=Array.from(e.querySelectorAll(r));e.matches&&e.matches(r)&&i.push(e);for(const c of i)if(!c[n]){window.lnCore._persistSink&&c.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(c,t);try{c[n]=new o(c)}catch(s){console.error("["+n+"] init failed",c,s)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function Z(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function z(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function F(){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 Y(e){const t=F(),n=e.observed||[];for(let o=0;o<n.length;o++){const l=n[o];t.byAttr.has(l)||t.byAttr.set(l,[]),t.byAttr.get(l).push(e)}if(t.byDeclaredAttr=t.byDeclaredAttr||new Map,e.attributes)for(const o in e.attributes){t.byDeclaredAttr.has(o)||t.byDeclaredAttr.set(o,[]);const l=t.byDeclaredAttr.get(o);l.some(r=>r.componentTag===e.componentTag)||l.push({spec:e.attributes[o],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){t.reactive.push(e);const o=X(e);if(o===null)t.reactiveWildcard.push(e);else for(const l of o)t.byReactive.has(l)||t.byReactive.set(l,[]),t.byReactive.get(l).push(e)}e.persist&&t.persist.push(e)}function q(e,t,n,o){for(let l=0;l<e.length;l++){const r=e[l];if(!t[r.attribute])continue;const i=r.effects&&r.effects[n];i?i(t,n,o):r.onAttrChange&&(!r.declared||r.declared.has(n))&&r.onAttrChange(t,n,o)}}function ee(e){const t=e.target,n=e.attributeName;if(e.oldValue===t.getAttribute(n))return;const o=F(),l=o.byAttr.get(n);if(L()&&o.byDeclaredAttr&&o.byDeclaredAttr.has(n)&&y(t)){const r=o.byDeclaredAttr.get(n),i=t.getAttribute(n);for(let c=0;c<r.length;c++)R(r[c].spec,i,n,r[c].componentTag,t)}if(window.lnCore._debugSink&&(n.indexOf("data-ln-")===0||l)&&window.lnCore._debugSink("attr",n,t,{oldValue:e.oldValue,newValue:t.getAttribute(n)}),n.indexOf("data-ln-")===0){const r=o.byReactive.get(n);r&&q(r,t,n,e.oldValue),o.reactiveWildcard.length&&q(o.reactiveWildcard,t,n,e.oldValue)}if(l)for(let r=0;r<l.length;r++){const i=l[r];if(i.handler){i.handler(t,n,e.oldValue);continue}i.onAttributeChange&&t[i.attribute]?i.onAttributeChange(t,n):(v(t,i.selector,i.attribute,i.ComponentFn),i.onInit&&i.onInit(t))}}function te(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++)try{ee(t[n])}catch(o){console.error("[ln-core] mutation handler failed",t[n].target,o)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function B(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function ne(e){const t=B();if(t.length){if(e.target)for(let n=0;n<t.length;n++){const o=t[n];if(o.onSubtreeChange){const l=o.query,r=e.target.nodeType===1?e.target.matches(l)?e.target:e.target.closest(l):e.target.parentElement?e.target.parentElement.closest(l):null;r&&o.onSubtreeChange(r,e)}}for(let n=0;n<e.addedNodes.length;n++){const o=e.addedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const r=t[l];v(o,r.selector,r.attribute,r.ComponentFn),r.onInit&&r.onInit(o)}}for(let n=0;n<e.removedNodes.length;n++){const o=e.removedNodes[n];if(o.nodeType===1)for(let l=0;l<t.length;l++){const r=t[l],i=r.query,c=Array.from(o.querySelectorAll(i));o.matches&&o.matches(i)&&c.push(o);for(let s=0;s<c.length;s++){const a=c[s];if(!document.contains(a)){const d=a[r.attribute];if(d&&typeof d.destroy=="function")try{d.destroy()}catch(u){console.error("["+r.attribute+"] destroy failed",a,u)}delete a[r.attribute]}}}}}}function re(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,m(function(){new MutationObserver(function(t){for(let n=0;n<t.length;n++){const o=t[n];if(o.type==="childList")try{ne(o)}catch(l){console.error("[ln-core] lifecycle handler failed",o.target,l)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function oe(e,t,n,o,l={}){const r=l.extraAttributes||[],i=l.onAttributeChange||null,c=l.onSubtreeChange||null,s=l.onInit||null,a=l.onAttrChange||null,d=l.effects||null,u=l.attributes||null,f=u?J(u):d,b=u?new Set(Object.keys(u)):null,w=l.persist||null;function g(k){const h=k||document.body;if(v(h,e,t,n),u&&L())for(const C in u){const be=u[C],x=Array.from(h.querySelectorAll("["+C+"]"));h.matches&&h.matches("["+C+"]")&&x.push(h);for(let E=0;E<x.length;E++){const O=x[E];y(O)&&R(be,O.getAttribute(C),C,o,O)}}s&&s(h)}const p=[];if(e.indexOf("[")!==-1){const k=/\[([\w-]+)/g;let h;for(;(h=k.exec(e))!==null;)p.push(h[1])}else p.push(e);Y({selector:e,attribute:t,componentTag:o,attributes:u,ComponentFn:n,onInit:s,observed:p.concat(r),onAttributeChange:i,onAttrChange:a,effects:f,declared:b,persist:w}),te();const he=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";B().push({selector:e,attribute:t,ComponentFn:n,onInit:s,onSubtreeChange:c,query:he}),re(),window[t]=g;function N(){Z()>0?z(function(){g(document.body)}):g(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",N):N(),g}function V(e){return(e||"").replace(/^#/,"")}function _(e){const t=e===void 0?location.hash:e,n={},o=V(t);if(!o)return n;const l=o.split("&");for(let r=0;r<l.length;r++){const i=l[r];if(!i)continue;const c=i.indexOf(":"),s=c>-1?i.slice(0,c):i,a=c>-1?i.slice(c+1):"";if(s)try{n[s]=decodeURIComponent(a)}catch{n[s]=a}}return n}function ie(e){if(!e)return null;const t=_();return e in t?t[e]:null}function le(e,t){if(!e)return;const n=_();t==null?delete n[e]:n[e]=String(t);const l=Object.keys(n).map(function(r){const i=n[r];return i===""?r:r+":"+encodeURIComponent(i)}).join("&");V(location.hash)!==l&&(location.hash=l)}function se(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function ce(e,t){if(!e||!e.hasAttribute("data-ln-hash"))return null;const n=e.getAttribute("data-ln-hash");if(n&&n.trim()!=="")return n.trim();const o=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return o?t?o+"-"+t:o:t||null}function ae(e,t){return!t||t==="none"||e===null||e===void 0?null:String(e)+"."+t}function ue(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 de(e,t){return!e||!Array.isArray(t)||t.length===0?null:e+":"+t.map(encodeURIComponent).join(",")}function fe(e){if(!e||typeof e!="string")return null;const t=e.indexOf(":");if(t===-1)return null;const n=e.slice(0,t),o=e.slice(t+1),l=o?o.split(",").map(function(r){try{return decodeURIComponent(r)}catch{return r}}).filter(Boolean):[];return{key:n,values:l}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=_,window.lnCore.hashGet=ie,window.lnCore.hashSet=le,window.lnCore.hashLinkClick=se,window.lnCore.resolveHashNamespace=ce,window.lnCore.hashSortEncode=ae,window.lnCore.hashSortDecode=ue,window.lnCore.hashFilterEncode=de,window.lnCore.hashFilterDecode=fe);function we(e,t=100,n=0){const o=parseFloat(String(e))||0,l=parseFloat(String(t))||100,r=parseFloat(String(n))||0,i=Math.max(r,Math.min(o,l)),c=l-r;let s=0;return c>0&&(s=(i-r)/c*100),s=Math.max(0,Math.min(100,s)),{value:o,min:r,max:l,clampedValue:i,percentage:s}}(function(){const e="data-ln-circular-progress",t="lnCircularProgress";if(window[t]!==void 0)return;function n(f){const b=f[t];b&&u.call(b)}const o={"data-ln-circular-progress":{type:"float",fallback:0,min:0,effect:n,description:"Current circular progress value"},"data-ln-circular-progress-max":{type:"float",fallback:100,min:0,effect:n,description:"Maximum circular progress scale value"},"data-ln-circular-progress-label":{type:"string",effect:n,description:"Text label format or template inside circular progress"}},l="http://www.w3.org/2000/svg",r=36,i=16,c=2*Math.PI*i;function s(f){return this.dom=f,this.svg=null,this.trackCircle=null,this.progressCircle=null,this.labelEl=null,d.call(this),u.call(this),this}s.prototype.destroy=function(){this.dom[t]&&(this.svg&&this.svg.remove(),this.labelEl&&this.labelEl.remove(),delete this.dom[t])};function a(f,b){const w=document.createElementNS(l,f);for(const[g,p]of Object.entries(b))w.setAttribute(g,p);return w}function d(){this.svg=a("svg",{viewBox:"0 0 "+r+" "+r,width:r,height:r}),this.svg.classList.add("ln-circular-progress__svg"),this.trackCircle=a("circle",{cx:r/2,cy:r/2,r:i,fill:"none","stroke-width":"3"}),this.trackCircle.classList.add("ln-circular-progress__track"),this.progressCircle=a("circle",{cx:r/2,cy:r/2,r:i,fill:"none","stroke-width":"3","stroke-linecap":"round","stroke-dasharray":c,"stroke-dashoffset":c,transform:"rotate(-90 "+r/2+" "+r/2+")"}),this.progressCircle.classList.add("ln-circular-progress__fill"),this.svg.appendChild(this.trackCircle),this.svg.appendChild(this.progressCircle),this.labelEl=document.createElement("strong"),this.labelEl.classList.add("ln-circular-progress__label"),this.dom.appendChild(this.svg),this.dom.appendChild(this.labelEl)}function u(){const f=this.dom.getAttribute("data-ln-circular-progress"),b=this.dom.getAttribute("data-ln-circular-progress-max"),w=we(f,b||100),g=c-w.percentage/100*c;this.progressCircle.setAttribute("stroke-dashoffset",g);const p=this.dom.getAttribute("data-ln-circular-progress-label"),S=p!==null?p:Math.round(w.percentage)+"%";this.labelEl.textContent=S,this.dom.setAttribute("role","progressbar"),this.dom.setAttribute("aria-valuemin",String(w.min)),this.dom.setAttribute("aria-valuemax",String(w.max)),this.dom.setAttribute("aria-valuenow",String(w.clampedValue)),this.dom.setAttribute("aria-valuetext",S),$(this.dom,"ln-circular-progress:change",{target:this.dom,value:w.value,max:w.max,percentage:w.percentage})}oe(e,t,s,"ln-circular-progress",{attributes:o})})()})();
@@ -0,0 +1,42 @@
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-circular-progress",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-circular-progress": {
7
+ "direction": "author",
8
+ "sources": [
9
+ "ln-circular-progress-dev.scss",
10
+ "src/ln-circular-progress.js"
11
+ ],
12
+ "type": "float",
13
+ "fallback": 0,
14
+ "min": 0,
15
+ "description": "Current circular progress value"
16
+ },
17
+ "data-ln-circular-progress-label": {
18
+ "direction": "author",
19
+ "sources": [
20
+ "src/ln-circular-progress.js"
21
+ ],
22
+ "type": "string",
23
+ "description": "Text label format or template inside circular progress"
24
+ },
25
+ "data-ln-circular-progress-max": {
26
+ "direction": "author",
27
+ "sources": [
28
+ "src/ln-circular-progress.js"
29
+ ],
30
+ "type": "float",
31
+ "fallback": 100,
32
+ "min": 0,
33
+ "description": "Maximum circular progress scale value"
34
+ },
35
+ "data-ln-debug": {
36
+ "direction": null,
37
+ "sources": [
38
+ "ln-circular-progress-dev.scss"
39
+ ]
40
+ }
41
+ }
42
+ }
@@ -1,4 +1,4 @@
1
- import { dispatch, registerComponent } from '../../ln-core';
1
+ import { calculateProgress, dispatch, registerComponent } from '../../ln-core';
2
2
 
3
3
  (function () {
4
4
  const DOM_SELECTOR = 'data-ln-circular-progress';
@@ -6,6 +6,18 @@ import { dispatch, registerComponent } from '../../ln-core';
6
6
 
7
7
  if (window[DOM_ATTRIBUTE] !== undefined) return;
8
8
 
9
+ function _syncAttribute(el) {
10
+ const inst = el[DOM_ATTRIBUTE];
11
+ if (inst) _render.call(inst);
12
+ }
13
+
14
+ // ─── Attribute Contract (SSOT) ──────────────────────────
15
+ const ATTRIBUTES = {
16
+ 'data-ln-circular-progress': { type: 'float', fallback: 0, min: 0, effect: _syncAttribute, description: 'Current circular progress value' },
17
+ 'data-ln-circular-progress-max': { type: 'float', fallback: 100, min: 0, effect: _syncAttribute, description: 'Maximum circular progress scale value' },
18
+ 'data-ln-circular-progress-label': { type: 'string', effect: _syncAttribute, description: 'Text label format or template inside circular progress' }
19
+ };
20
+
9
21
  const SVG_NS = 'http://www.w3.org/2000/svg';
10
22
  const VIEW_SIZE = 36;
11
23
  const RADIUS = 16;
@@ -17,36 +29,26 @@ import { dispatch, registerComponent } from '../../ln-core';
17
29
  this.trackCircle = null;
18
30
  this.progressCircle = null;
19
31
  this.labelEl = null;
20
- this._attrObserver = null;
21
32
  _buildSvg.call(this);
22
33
  _render.call(this);
23
- _listenValues.call(this);
24
34
  return this;
25
35
  }
26
36
 
27
37
  _constructor.prototype.destroy = function () {
28
38
  if (!this.dom[DOM_ATTRIBUTE]) return;
29
- if (this._attrObserver) {
30
- this._attrObserver.disconnect();
31
- }
32
39
  if (this.svg) {
33
40
  this.svg.remove();
34
41
  }
35
42
  if (this.labelEl) {
36
43
  this.labelEl.remove();
37
44
  }
38
- this.dom.removeAttribute('role');
39
- this.dom.removeAttribute('aria-valuemin');
40
- this.dom.removeAttribute('aria-valuemax');
41
- this.dom.removeAttribute('aria-valuenow');
42
- this.dom.removeAttribute('aria-valuetext');
43
45
  delete this.dom[DOM_ATTRIBUTE];
44
46
  };
45
47
 
46
48
  function _createSvgElement(tag, attrs) {
47
49
  const el = document.createElementNS(SVG_NS, tag);
48
- for (const key in attrs) {
49
- el.setAttribute(key, attrs[key]);
50
+ for (const [key, val] of Object.entries(attrs)) {
51
+ el.setAttribute(key, val);
50
52
  }
51
53
  return el;
52
54
  }
@@ -54,8 +56,10 @@ import { dispatch, registerComponent } from '../../ln-core';
54
56
  function _buildSvg() {
55
57
  this.svg = _createSvgElement('svg', {
56
58
  viewBox: '0 0 ' + VIEW_SIZE + ' ' + VIEW_SIZE,
57
- 'aria-hidden': 'true'
59
+ width: VIEW_SIZE,
60
+ height: VIEW_SIZE
58
61
  });
62
+ this.svg.classList.add('ln-circular-progress__svg');
59
63
 
60
64
  this.trackCircle = _createSvgElement('circle', {
61
65
  cx: VIEW_SIZE / 2,
@@ -89,58 +93,36 @@ import { dispatch, registerComponent } from '../../ln-core';
89
93
  this.dom.appendChild(this.labelEl);
90
94
  }
91
95
 
92
- function _listenValues() {
93
- const self = this;
94
- const observer = new MutationObserver(function (mutations) {
95
- for (const mutation of mutations) {
96
- if (mutation.attributeName === 'data-ln-circular-progress' ||
97
- mutation.attributeName === 'data-ln-circular-progress-max' ||
98
- mutation.attributeName === 'data-ln-circular-progress-label') {
99
- _render.call(self);
100
- }
101
- }
102
- });
103
-
104
- observer.observe(this.dom, {
105
- attributes: true,
106
- attributeFilter: ['data-ln-circular-progress', 'data-ln-circular-progress-max', 'data-ln-circular-progress-label']
107
- });
108
-
109
- this._attrObserver = observer;
110
- }
111
-
112
96
  function _render() {
113
- const value = parseFloat(this.dom.getAttribute('data-ln-circular-progress')) || 0;
114
- const max = parseFloat(this.dom.getAttribute('data-ln-circular-progress-max')) || 100;
115
- let percentage = (max > 0) ? (value / max) * 100 : 0;
116
-
117
- if (percentage < 0) percentage = 0;
118
- if (percentage > 100) percentage = 100;
97
+ const rawVal = this.dom.getAttribute('data-ln-circular-progress');
98
+ const rawMax = this.dom.getAttribute('data-ln-circular-progress-max');
99
+ const result = calculateProgress(rawVal, rawMax || 100);
119
100
 
120
- const offset = CIRCUMFERENCE - (percentage / 100) * CIRCUMFERENCE;
101
+ const offset = CIRCUMFERENCE - (result.percentage / 100) * CIRCUMFERENCE;
121
102
  this.progressCircle.setAttribute('stroke-dashoffset', offset);
122
103
 
123
104
  const label = this.dom.getAttribute('data-ln-circular-progress-label');
124
- const labelText = label !== null ? label : Math.round(percentage) + '%';
105
+ const labelText = label !== null ? label : Math.round(result.percentage) + '%';
125
106
  this.labelEl.textContent = labelText;
126
107
 
127
108
  // Sync ARIA properties
128
109
  this.dom.setAttribute('role', 'progressbar');
129
- this.dom.setAttribute('aria-valuemin', '0');
130
- this.dom.setAttribute('aria-valuemax', String(max));
131
- const clampedValue = Math.max(0, Math.min(value, max));
132
- this.dom.setAttribute('aria-valuenow', String(clampedValue));
110
+ this.dom.setAttribute('aria-valuemin', String(result.min));
111
+ this.dom.setAttribute('aria-valuemax', String(result.max));
112
+ this.dom.setAttribute('aria-valuenow', String(result.clampedValue));
133
113
  this.dom.setAttribute('aria-valuetext', labelText);
134
114
 
135
115
  dispatch(this.dom, 'ln-circular-progress:change', {
136
116
  target: this.dom,
137
- value: value,
138
- max: max,
139
- percentage: percentage
117
+ value: result.value,
118
+ max: result.max,
119
+ percentage: result.percentage
140
120
  });
141
121
  }
142
122
 
143
123
  // ─── Init ──────────────────────────────────────────────────
144
124
 
145
- registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _constructor, 'ln-circular-progress');
125
+ registerComponent(DOM_SELECTOR, DOM_ATTRIBUTE, _constructor, 'ln-circular-progress', {
126
+ attributes: ATTRIBUTES
127
+ });
146
128
  })();