decantr 0.9.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 (382) hide show
  1. package/AGENTS.md +868 -0
  2. package/CHANGELOG.md +255 -0
  3. package/CLAUDE.md +178 -0
  4. package/LICENSE +21 -0
  5. package/README.md +229 -0
  6. package/cli/art.js +127 -0
  7. package/cli/commands/a11y.js +61 -0
  8. package/cli/commands/audit.js +225 -0
  9. package/cli/commands/build.js +38 -0
  10. package/cli/commands/dev.js +18 -0
  11. package/cli/commands/doctor.js +197 -0
  12. package/cli/commands/figma-sync.js +48 -0
  13. package/cli/commands/figma-tokens.js +55 -0
  14. package/cli/commands/generate.js +26 -0
  15. package/cli/commands/init.js +116 -0
  16. package/cli/commands/lint.js +209 -0
  17. package/cli/commands/mcp.js +530 -0
  18. package/cli/commands/migrate.js +175 -0
  19. package/cli/commands/test.js +38 -0
  20. package/cli/commands/validate.js +354 -0
  21. package/cli/index.js +113 -0
  22. package/package.json +95 -0
  23. package/reference/atoms.md +517 -0
  24. package/reference/behaviors.md +384 -0
  25. package/reference/build-tooling.md +275 -0
  26. package/reference/color-guidelines.md +965 -0
  27. package/reference/component-lifecycle.md +137 -0
  28. package/reference/compound-spacing.md +95 -0
  29. package/reference/decantation-process.md +499 -0
  30. package/reference/dev-server-routes.md +93 -0
  31. package/reference/form-system.md +253 -0
  32. package/reference/i18n.md +336 -0
  33. package/reference/icons.md +576 -0
  34. package/reference/llm-primer.md +953 -0
  35. package/reference/plugins.md +252 -0
  36. package/reference/registry-consumption.md +76 -0
  37. package/reference/router.md +217 -0
  38. package/reference/shells.md +116 -0
  39. package/reference/spatial-guidelines.md +541 -0
  40. package/reference/ssr.md +234 -0
  41. package/reference/state-data.md +215 -0
  42. package/reference/state-patterns.md +166 -0
  43. package/reference/state.md +194 -0
  44. package/reference/style-system.md +110 -0
  45. package/reference/tokens.md +460 -0
  46. package/src/app.js +19 -0
  47. package/src/chart/_animate.js +266 -0
  48. package/src/chart/_base.js +109 -0
  49. package/src/chart/_data.js +209 -0
  50. package/src/chart/_format.js +106 -0
  51. package/src/chart/_interact.js +364 -0
  52. package/src/chart/_palette.js +105 -0
  53. package/src/chart/_renderer.js +52 -0
  54. package/src/chart/_scene.js +262 -0
  55. package/src/chart/_shared.js +371 -0
  56. package/src/chart/index.js +637 -0
  57. package/src/chart/layouts/_layout-base.js +328 -0
  58. package/src/chart/layouts/cartesian.js +148 -0
  59. package/src/chart/layouts/hierarchy.js +562 -0
  60. package/src/chart/layouts/polar.js +101 -0
  61. package/src/chart/renderers/canvas.js +179 -0
  62. package/src/chart/renderers/svg.js +256 -0
  63. package/src/chart/renderers/webgpu.js +715 -0
  64. package/src/chart/types/_type-base.js +26 -0
  65. package/src/chart/types/area.js +134 -0
  66. package/src/chart/types/bar.js +173 -0
  67. package/src/chart/types/box-plot.js +125 -0
  68. package/src/chart/types/bubble.js +63 -0
  69. package/src/chart/types/candlestick.js +115 -0
  70. package/src/chart/types/chord.js +85 -0
  71. package/src/chart/types/combination.js +108 -0
  72. package/src/chart/types/funnel.js +68 -0
  73. package/src/chart/types/gauge.js +163 -0
  74. package/src/chart/types/heatmap.js +98 -0
  75. package/src/chart/types/histogram.js +71 -0
  76. package/src/chart/types/line.js +111 -0
  77. package/src/chart/types/org-chart.js +93 -0
  78. package/src/chart/types/pie.js +81 -0
  79. package/src/chart/types/radar.js +96 -0
  80. package/src/chart/types/radial.js +68 -0
  81. package/src/chart/types/range-area.js +55 -0
  82. package/src/chart/types/range-bar.js +61 -0
  83. package/src/chart/types/sankey.js +73 -0
  84. package/src/chart/types/scatter.js +66 -0
  85. package/src/chart/types/sparkline.js +81 -0
  86. package/src/chart/types/sunburst.js +69 -0
  87. package/src/chart/types/swimlane.js +88 -0
  88. package/src/chart/types/treemap.js +62 -0
  89. package/src/chart/types/waterfall.js +100 -0
  90. package/src/components/_base.js +1658 -0
  91. package/src/components/_behaviors.js +1140 -0
  92. package/src/components/_primitives.js +534 -0
  93. package/src/components/_qr-encoder.js +539 -0
  94. package/src/components/accordion.js +207 -0
  95. package/src/components/affix.js +62 -0
  96. package/src/components/alert-dialog.js +75 -0
  97. package/src/components/alert.js +47 -0
  98. package/src/components/aspect-ratio.js +24 -0
  99. package/src/components/avatar-group.js +55 -0
  100. package/src/components/avatar.js +38 -0
  101. package/src/components/back-top.js +75 -0
  102. package/src/components/badge.js +74 -0
  103. package/src/components/banner.js +68 -0
  104. package/src/components/breadcrumb.js +162 -0
  105. package/src/components/button.js +115 -0
  106. package/src/components/calendar.js +131 -0
  107. package/src/components/card.js +192 -0
  108. package/src/components/carousel.js +98 -0
  109. package/src/components/cascader.js +261 -0
  110. package/src/components/checkbox.js +80 -0
  111. package/src/components/chip.js +81 -0
  112. package/src/components/code-block.js +82 -0
  113. package/src/components/collapsible.js +50 -0
  114. package/src/components/color-palette.js +438 -0
  115. package/src/components/color-picker.js +314 -0
  116. package/src/components/combobox.js +181 -0
  117. package/src/components/command.js +174 -0
  118. package/src/components/comment.js +206 -0
  119. package/src/components/context-menu.js +76 -0
  120. package/src/components/data-table.js +724 -0
  121. package/src/components/date-picker.js +217 -0
  122. package/src/components/date-range-picker.js +244 -0
  123. package/src/components/datetime-picker.js +271 -0
  124. package/src/components/descriptions.js +68 -0
  125. package/src/components/drawer.js +179 -0
  126. package/src/components/dropdown.js +88 -0
  127. package/src/components/empty.js +41 -0
  128. package/src/components/float-button.js +90 -0
  129. package/src/components/form.js +106 -0
  130. package/src/components/hover-card.js +49 -0
  131. package/src/components/icon.js +87 -0
  132. package/src/components/image.js +97 -0
  133. package/src/components/index.js +117 -0
  134. package/src/components/input-group.js +75 -0
  135. package/src/components/input-number.js +155 -0
  136. package/src/components/input-otp.js +178 -0
  137. package/src/components/input.js +91 -0
  138. package/src/components/kbd.js +36 -0
  139. package/src/components/label.js +25 -0
  140. package/src/components/list.js +118 -0
  141. package/src/components/masked-input.js +236 -0
  142. package/src/components/mentions.js +165 -0
  143. package/src/components/menu.js +259 -0
  144. package/src/components/message.js +80 -0
  145. package/src/components/modal.js +147 -0
  146. package/src/components/navigation-menu.js +166 -0
  147. package/src/components/notification.js +84 -0
  148. package/src/components/pagination.js +104 -0
  149. package/src/components/placeholder.js +132 -0
  150. package/src/components/popconfirm.js +70 -0
  151. package/src/components/popover.js +58 -0
  152. package/src/components/progress.js +61 -0
  153. package/src/components/qrcode.js +251 -0
  154. package/src/components/radiogroup.js +120 -0
  155. package/src/components/range-slider.js +176 -0
  156. package/src/components/rate.js +186 -0
  157. package/src/components/resizable.js +83 -0
  158. package/src/components/result.js +57 -0
  159. package/src/components/scroll-area.js +43 -0
  160. package/src/components/segmented.js +97 -0
  161. package/src/components/select.js +165 -0
  162. package/src/components/separator.js +31 -0
  163. package/src/components/shell.js +407 -0
  164. package/src/components/skeleton.js +39 -0
  165. package/src/components/slider.js +141 -0
  166. package/src/components/sortable-list.js +176 -0
  167. package/src/components/space.js +42 -0
  168. package/src/components/spinner.js +112 -0
  169. package/src/components/splitter.js +147 -0
  170. package/src/components/statistic.js +136 -0
  171. package/src/components/steps.js +99 -0
  172. package/src/components/switch.js +95 -0
  173. package/src/components/table.js +44 -0
  174. package/src/components/tabs.js +216 -0
  175. package/src/components/tag.js +115 -0
  176. package/src/components/textarea.js +82 -0
  177. package/src/components/time-picker.js +153 -0
  178. package/src/components/time-range-picker.js +170 -0
  179. package/src/components/timeline.js +226 -0
  180. package/src/components/toast.js +71 -0
  181. package/src/components/toggle.js +213 -0
  182. package/src/components/tooltip.js +57 -0
  183. package/src/components/tour.js +159 -0
  184. package/src/components/transfer.js +163 -0
  185. package/src/components/tree-select.js +274 -0
  186. package/src/components/tree.js +141 -0
  187. package/src/components/typography.js +136 -0
  188. package/src/components/upload.js +118 -0
  189. package/src/components/visually-hidden.js +20 -0
  190. package/src/components/watermark.js +124 -0
  191. package/src/core/index.js +539 -0
  192. package/src/core/lifecycle.js +69 -0
  193. package/src/css/atoms.js +651 -0
  194. package/src/css/components.js +940 -0
  195. package/src/css/derive.js +1296 -0
  196. package/src/css/index.js +265 -0
  197. package/src/css/runtime.js +268 -0
  198. package/src/css/styles/addons/bioluminescent.js +93 -0
  199. package/src/css/styles/addons/clay.js +70 -0
  200. package/src/css/styles/addons/clean.js +57 -0
  201. package/src/css/styles/addons/command-center.js +143 -0
  202. package/src/css/styles/addons/dopamine.js +83 -0
  203. package/src/css/styles/addons/editorial.js +80 -0
  204. package/src/css/styles/addons/glassmorphism.js +99 -0
  205. package/src/css/styles/addons/liquid-glass.js +105 -0
  206. package/src/css/styles/addons/prismatic.js +100 -0
  207. package/src/css/styles/addons/retro.js +63 -0
  208. package/src/css/styles/auradecantism.js +96 -0
  209. package/src/css/theme-registry.js +444 -0
  210. package/src/data/entity.js +281 -0
  211. package/src/data/index.js +13 -0
  212. package/src/data/persist.js +225 -0
  213. package/src/data/query.js +839 -0
  214. package/src/data/realtime.js +299 -0
  215. package/src/data/url.js +177 -0
  216. package/src/data/worker.js +134 -0
  217. package/src/explorer/archetypes.js +243 -0
  218. package/src/explorer/atoms.js +228 -0
  219. package/src/explorer/charts.js +497 -0
  220. package/src/explorer/components.js +129 -0
  221. package/src/explorer/foundations.js +949 -0
  222. package/src/explorer/icons.js +178 -0
  223. package/src/explorer/patterns.js +247 -0
  224. package/src/explorer/recipes.js +194 -0
  225. package/src/explorer/shared/pattern-examples.js +1337 -0
  226. package/src/explorer/shared/showcase-renderer.js +958 -0
  227. package/src/explorer/shared/spec-table.js +41 -0
  228. package/src/explorer/shared/usage-links.js +87 -0
  229. package/src/explorer/shell-config.js +10 -0
  230. package/src/explorer/shells.js +551 -0
  231. package/src/explorer/styles.js +161 -0
  232. package/src/explorer/tokens.js +262 -0
  233. package/src/explorer/tools.js +525 -0
  234. package/src/form/index.js +804 -0
  235. package/src/i18n/index.js +251 -0
  236. package/src/icons/essential.js +479 -0
  237. package/src/icons/index.js +53 -0
  238. package/src/plugins/index.js +282 -0
  239. package/src/registry/archetypes/content-site.json +71 -0
  240. package/src/registry/archetypes/docs-explorer.json +23 -0
  241. package/src/registry/archetypes/ecommerce.json +104 -0
  242. package/src/registry/archetypes/financial-dashboard.json +77 -0
  243. package/src/registry/archetypes/index.json +41 -0
  244. package/src/registry/archetypes/portfolio.json +82 -0
  245. package/src/registry/archetypes/recipe-community.json +159 -0
  246. package/src/registry/archetypes/saas-dashboard.json +86 -0
  247. package/src/registry/architect/cross-cutting.json +45 -0
  248. package/src/registry/architect/domains/ecommerce.json +294 -0
  249. package/src/registry/architect/domains/financial-services.json +302 -0
  250. package/src/registry/architect/index.json +26 -0
  251. package/src/registry/architect/traits.json +379 -0
  252. package/src/registry/atoms.json +16 -0
  253. package/src/registry/chart-showcase.json +160 -0
  254. package/src/registry/chart.json +136 -0
  255. package/src/registry/components.json +8616 -0
  256. package/src/registry/core.json +216 -0
  257. package/src/registry/css.json +319 -0
  258. package/src/registry/data.json +135 -0
  259. package/src/registry/foundations.json +11 -0
  260. package/src/registry/icons.json +463 -0
  261. package/src/registry/index.json +101 -0
  262. package/src/registry/patterns/activity-feed.json +37 -0
  263. package/src/registry/patterns/article-content.json +27 -0
  264. package/src/registry/patterns/auth-form.json +37 -0
  265. package/src/registry/patterns/author-card.json +20 -0
  266. package/src/registry/patterns/card-grid.json +127 -0
  267. package/src/registry/patterns/category-nav.json +26 -0
  268. package/src/registry/patterns/chart-grid.json +36 -0
  269. package/src/registry/patterns/chat-interface.json +37 -0
  270. package/src/registry/patterns/checklist-card.json +55 -0
  271. package/src/registry/patterns/comparison-panel.json +27 -0
  272. package/src/registry/patterns/component-showcase.json +24 -0
  273. package/src/registry/patterns/contact-form.json +31 -0
  274. package/src/registry/patterns/cta-section.json +20 -0
  275. package/src/registry/patterns/data-table.json +37 -0
  276. package/src/registry/patterns/detail-header.json +83 -0
  277. package/src/registry/patterns/detail-panel.json +27 -0
  278. package/src/registry/patterns/explorer-shell.json +22 -0
  279. package/src/registry/patterns/filter-bar.json +33 -0
  280. package/src/registry/patterns/filter-sidebar.json +27 -0
  281. package/src/registry/patterns/form-sections.json +110 -0
  282. package/src/registry/patterns/goal-tracker.json +27 -0
  283. package/src/registry/patterns/hero.json +107 -0
  284. package/src/registry/patterns/index.json +47 -0
  285. package/src/registry/patterns/kpi-grid.json +36 -0
  286. package/src/registry/patterns/media-gallery.json +20 -0
  287. package/src/registry/patterns/order-history.json +20 -0
  288. package/src/registry/patterns/pagination.json +19 -0
  289. package/src/registry/patterns/photo-to-recipe.json +36 -0
  290. package/src/registry/patterns/pipeline-tracker.json +28 -0
  291. package/src/registry/patterns/post-list.json +27 -0
  292. package/src/registry/patterns/pricing-table.json +32 -0
  293. package/src/registry/patterns/scorecard.json +28 -0
  294. package/src/registry/patterns/search-bar.json +20 -0
  295. package/src/registry/patterns/specimen-grid.json +19 -0
  296. package/src/registry/patterns/stat-card.json +55 -0
  297. package/src/registry/patterns/stats-bar.json +55 -0
  298. package/src/registry/patterns/steps-card.json +55 -0
  299. package/src/registry/patterns/table-of-contents.json +19 -0
  300. package/src/registry/patterns/testimonials.json +21 -0
  301. package/src/registry/patterns/timeline.json +27 -0
  302. package/src/registry/patterns/token-inspector.json +21 -0
  303. package/src/registry/patterns/wizard.json +27 -0
  304. package/src/registry/recipe-auradecantism.json +69 -0
  305. package/src/registry/recipe-clean.json +65 -0
  306. package/src/registry/recipe-command-center.json +78 -0
  307. package/src/registry/router.json +73 -0
  308. package/src/registry/schema/README.md +197 -0
  309. package/src/registry/skeletons.json +259 -0
  310. package/src/registry/state.json +137 -0
  311. package/src/registry/tokens.json +40 -0
  312. package/src/router/hash.js +17 -0
  313. package/src/router/history.js +18 -0
  314. package/src/router/index.js +598 -0
  315. package/src/ssr/index.js +922 -0
  316. package/src/state/arrays.js +181 -0
  317. package/src/state/devtools.js +647 -0
  318. package/src/state/index.js +498 -0
  319. package/src/state/middleware.js +288 -0
  320. package/src/state/scheduler.js +206 -0
  321. package/src/state/store.js +300 -0
  322. package/src/tags/index.js +19 -0
  323. package/src/tannins/auth.js +396 -0
  324. package/src/test/dom.js +352 -0
  325. package/src/test/index.js +62 -0
  326. package/src/test/state.js +306 -0
  327. package/tools/a11y-audit.js +487 -0
  328. package/tools/analyzer.js +315 -0
  329. package/tools/audit.js +706 -0
  330. package/tools/builder.js +1422 -0
  331. package/tools/css-extract.js +188 -0
  332. package/tools/dev-server.js +316 -0
  333. package/tools/dts-gen.js +1260 -0
  334. package/tools/figma-components.js +329 -0
  335. package/tools/figma-patterns.js +516 -0
  336. package/tools/figma-plugin/code.js +453 -0
  337. package/tools/figma-plugin/manifest.json +14 -0
  338. package/tools/figma-plugin/ui.html +268 -0
  339. package/tools/figma-render.js +293 -0
  340. package/tools/figma-tokens.js +712 -0
  341. package/tools/figma-upload.js +318 -0
  342. package/tools/generate.js +738 -0
  343. package/tools/icons.js +133 -0
  344. package/tools/init-templates.js +265 -0
  345. package/tools/install-hooks.sh +5 -0
  346. package/tools/migrations/0.5.0.js +53 -0
  347. package/tools/migrations/0.6.0.js +95 -0
  348. package/tools/minify.js +170 -0
  349. package/tools/pre-commit +4 -0
  350. package/tools/registry.js +662 -0
  351. package/tools/reset-playground.js +61 -0
  352. package/tools/starter-templates/content-site/app.js +49 -0
  353. package/tools/starter-templates/content-site/essence.js +19 -0
  354. package/tools/starter-templates/content-site/pages.js +31 -0
  355. package/tools/starter-templates/ecommerce/app.js +50 -0
  356. package/tools/starter-templates/ecommerce/essence.js +19 -0
  357. package/tools/starter-templates/ecommerce/pages.js +31 -0
  358. package/tools/starter-templates/landing-page/app.js +38 -0
  359. package/tools/starter-templates/landing-page/essence.js +18 -0
  360. package/tools/starter-templates/landing-page/pages.js +21 -0
  361. package/tools/starter-templates/portfolio/app.js +45 -0
  362. package/tools/starter-templates/portfolio/essence.js +19 -0
  363. package/tools/starter-templates/portfolio/pages.js +33 -0
  364. package/tools/starter-templates/saas-dashboard/app.js +70 -0
  365. package/tools/starter-templates/saas-dashboard/essence.js +19 -0
  366. package/tools/starter-templates/saas-dashboard/pages.js +31 -0
  367. package/tools/verify-pack.js +203 -0
  368. package/types/chart.d.ts +77 -0
  369. package/types/components.d.ts +587 -0
  370. package/types/core.d.ts +89 -0
  371. package/types/css.d.ts +149 -0
  372. package/types/data.d.ts +238 -0
  373. package/types/form.d.ts +164 -0
  374. package/types/i18n.d.ts +51 -0
  375. package/types/icons.d.ts +27 -0
  376. package/types/index.d.ts +13 -0
  377. package/types/router.d.ts +116 -0
  378. package/types/ssr.d.ts +102 -0
  379. package/types/state.d.ts +83 -0
  380. package/types/tags.d.ts +62 -0
  381. package/types/tannins.d.ts +63 -0
  382. package/types/test.d.ts +48 -0
@@ -0,0 +1,637 @@
1
+ /**
2
+ * decantr/chart — Declarative charting module.
3
+ * Public API: Chart(), Sparkline(), chartSpec(), createStream(), colorScale(), resolvePalette()
4
+ * @module chart
5
+ */
6
+
7
+ import { createEffect, createSignal } from '../state/index.js';
8
+ import { onDestroy } from '../core/index.js';
9
+ import { getAnimations } from '../css/theme-registry.js';
10
+ import { injectChartBase } from './_base.js';
11
+ import { resolve } from './_shared.js';
12
+ import { render } from './_renderer.js';
13
+ import { animate } from './_animate.js';
14
+
15
+ // --- Layout imports ---
16
+ import { layoutLine } from './types/line.js';
17
+ import { layoutBar } from './types/bar.js';
18
+ import { layoutArea } from './types/area.js';
19
+ import { layoutPie } from './types/pie.js';
20
+ import { layoutSparkline } from './types/sparkline.js';
21
+ import { layoutScatter } from './types/scatter.js';
22
+ import { layoutBubble } from './types/bubble.js';
23
+ import { layoutHistogram } from './types/histogram.js';
24
+ import { layoutBoxPlot } from './types/box-plot.js';
25
+ import { layoutCandlestick } from './types/candlestick.js';
26
+ import { layoutWaterfall } from './types/waterfall.js';
27
+ import { layoutRangeBar } from './types/range-bar.js';
28
+ import { layoutRangeArea } from './types/range-area.js';
29
+ import { layoutHeatmap } from './types/heatmap.js';
30
+ import { layoutCombination } from './types/combination.js';
31
+ import { layoutRadar } from './types/radar.js';
32
+ import { layoutRadial } from './types/radial.js';
33
+ import { layoutGauge } from './types/gauge.js';
34
+ import { layoutFunnel } from './types/funnel.js';
35
+ import { layoutTreemap } from './types/treemap.js';
36
+ import { layoutSunburst } from './types/sunburst.js';
37
+ import { layoutSankey } from './types/sankey.js';
38
+ import { layoutChord } from './types/chord.js';
39
+ import { layoutSwimlane } from './types/swimlane.js';
40
+ import { layoutOrgChart } from './types/org-chart.js';
41
+
42
+ // All 25 chart type layouts
43
+ const LAYOUT_MAP = {
44
+ line: layoutLine,
45
+ bar: layoutBar,
46
+ area: layoutArea,
47
+ pie: layoutPie,
48
+ scatter: layoutScatter,
49
+ bubble: layoutBubble,
50
+ histogram: layoutHistogram,
51
+ 'box-plot': layoutBoxPlot,
52
+ candlestick: layoutCandlestick,
53
+ waterfall: layoutWaterfall,
54
+ 'range-bar': layoutRangeBar,
55
+ 'range-area': layoutRangeArea,
56
+ heatmap: layoutHeatmap,
57
+ combination: layoutCombination,
58
+ radar: layoutRadar,
59
+ radial: layoutRadial,
60
+ gauge: layoutGauge,
61
+ funnel: layoutFunnel,
62
+ treemap: layoutTreemap,
63
+ sunburst: layoutSunburst,
64
+ sankey: layoutSankey,
65
+ chord: layoutChord,
66
+ swimlane: layoutSwimlane,
67
+ 'org-chart': layoutOrgChart
68
+ };
69
+
70
+ /**
71
+ * Register a chart type layout function.
72
+ * @param {string} type
73
+ * @param {Function} layoutFn
74
+ */
75
+ export function registerChartType(type, layoutFn) {
76
+ LAYOUT_MAP[type] = layoutFn;
77
+ }
78
+
79
+ const DEFAULTS = {
80
+ type: 'line',
81
+ tooltip: true,
82
+ legend: true,
83
+ grid: true,
84
+ stacked: false,
85
+ smooth: true,
86
+ dots: true,
87
+ axisLine: false,
88
+ animate: true,
89
+ renderer: 'auto',
90
+ height: '300px',
91
+ donut: true,
92
+ tableAlt: true
93
+ };
94
+
95
+ /**
96
+ * Validate and fill defaults for a chart spec.
97
+ * @param {Object} overrides
98
+ * @returns {Object}
99
+ */
100
+ export function chartSpec(overrides = {}) {
101
+ const spec = { ...DEFAULTS, ...overrides };
102
+ if (!spec.data) spec.data = [];
103
+ if (!spec.x && spec.type !== 'sparkline') spec.x = 'x';
104
+ if (!spec.y && spec.type !== 'sparkline') spec.y = 'y';
105
+ return spec;
106
+ }
107
+
108
+ /**
109
+ * Create a chart DOM element from a declarative spec.
110
+ * @param {Object} specInput
111
+ * @returns {HTMLElement}
112
+ */
113
+ export function Chart(specInput) {
114
+ injectChartBase();
115
+ const spec = chartSpec(specInput);
116
+
117
+ const container = document.createElement('div');
118
+ container.className = 'd-chart' + (spec.live ? ' d-chart-live' : '') + (spec.class ? ' ' + spec.class : '');
119
+ container.setAttribute('role', 'img');
120
+ if (spec['aria-label']) container.setAttribute('aria-label', spec['aria-label']);
121
+ else if (spec.title) container.setAttribute('aria-label', spec.title);
122
+
123
+ const heightStr = spec.height || '300px';
124
+ const heightPx = parseInt(heightStr, 10) || 300;
125
+
126
+ // Title
127
+ if (spec.title) {
128
+ const titleEl = document.createElement('div');
129
+ titleEl.className = 'd-chart-title';
130
+ titleEl.textContent = resolve(spec.title);
131
+ container.appendChild(titleEl);
132
+ }
133
+
134
+ // Chart inner container
135
+ const inner = document.createElement('div');
136
+ inner.className = 'd-chart-inner';
137
+ inner.style.height = heightStr;
138
+ container.appendChild(inner);
139
+
140
+ const layoutFn = LAYOUT_MAP[spec.type];
141
+ if (!layoutFn) {
142
+ inner.textContent = `Unknown chart type: ${spec.type}`;
143
+ return container;
144
+ }
145
+
146
+ let currentLegend = null;
147
+ let prevScene = null;
148
+ let firstRender = true;
149
+ let pendingRender = false;
150
+
151
+ const isReactive = typeof specInput.data === 'function';
152
+
153
+ function renderFn(sceneGraph) {
154
+ return render(sceneGraph, spec);
155
+ }
156
+
157
+ function renderChart() {
158
+ if (pendingRender) return;
159
+ pendingRender = true;
160
+ requestAnimationFrame(() => {
161
+ pendingRender = false;
162
+ doRender();
163
+ });
164
+ }
165
+
166
+ function doRender() {
167
+ const width = inner.offsetWidth || 600;
168
+ const height = heightPx;
169
+ const resolvedSpec = { ...spec, data: resolve(spec.data) };
170
+
171
+ const sceneGraph = layoutFn(resolvedSpec, width, height);
172
+
173
+ if (spec.live && !firstRender) {
174
+ // Live mode: in-place SVG updates so CSS d transitions can morph paths smoothly
175
+ doLiveUpdate(sceneGraph);
176
+ } else if (firstRender && spec.animate !== false) {
177
+ firstRender = false;
178
+ const zeroScene = createZeroScene(sceneGraph);
179
+ animate(inner, zeroScene, sceneGraph, renderFn, { duration: 750, easing: 'decelerate' }).then(() => {
180
+ postRender(sceneGraph);
181
+ // Line draw animation for SVG paths
182
+ applyLineDrawAnimation(inner);
183
+ });
184
+ } else if (prevScene && spec.animate !== false) {
185
+ animate(inner, prevScene, sceneGraph, renderFn, { duration: 300, easing: 'decelerate' }).then(() => {
186
+ postRender(sceneGraph);
187
+ });
188
+ } else {
189
+ const svgEl = render(sceneGraph, spec);
190
+ inner.textContent = '';
191
+ inner.appendChild(svgEl);
192
+ postRender(sceneGraph);
193
+ }
194
+
195
+ prevScene = sceneGraph;
196
+ }
197
+
198
+ /**
199
+ * Live update: patch the existing SVG in place.
200
+ * Paths with matching data-key get their `d` attribute updated (CSS transition morphs them).
201
+ * Axes, grid, and labels are swapped via a lightweight group replacement.
202
+ */
203
+ function doLiveUpdate(sceneGraph) {
204
+ const existingSvg = inner.querySelector('.d-chart-svg');
205
+ const newSvg = render(sceneGraph, spec);
206
+
207
+ if (!existingSvg) {
208
+ inner.textContent = '';
209
+ inner.appendChild(newSvg);
210
+ postRender(sceneGraph);
211
+ return;
212
+ }
213
+
214
+ // Build map of existing keyed elements
215
+ const existingKeyed = new Map();
216
+ for (const el of existingSvg.querySelectorAll('[data-key]')) {
217
+ existingKeyed.set(el.getAttribute('data-key'), el);
218
+ }
219
+
220
+ // Build map of new keyed elements and collect their attributes
221
+ const newKeyed = new Map();
222
+ for (const el of newSvg.querySelectorAll('[data-key]')) {
223
+ newKeyed.set(el.getAttribute('data-key'), el);
224
+ }
225
+
226
+ // Update existing keyed elements in place (paths get CSS-transitioned `d`)
227
+ for (const [key, newEl] of newKeyed) {
228
+ const existing = existingKeyed.get(key);
229
+ if (existing && existing.tagName === newEl.tagName) {
230
+ for (const attr of newEl.attributes) {
231
+ if (attr.name !== 'data-key') {
232
+ existing.setAttribute(attr.name, attr.value);
233
+ }
234
+ }
235
+ existingKeyed.delete(key);
236
+ }
237
+ }
238
+
239
+ // Swap non-keyed content (axes, grid, labels) by replacing the inner group
240
+ const existingGroup = existingSvg.querySelector('g');
241
+ const newGroup = newSvg.querySelector('g');
242
+ if (existingGroup && newGroup) {
243
+ // Collect keyed elements we updated in place
244
+ const preservedEls = new Map();
245
+ for (const [key, el] of newKeyed) {
246
+ const existing = existingSvg.querySelector(`[data-key="${key}"]`);
247
+ if (existing) preservedEls.set(key, existing);
248
+ }
249
+
250
+ // Replace group children, reinserting preserved keyed elements
251
+ const newChildren = [...newGroup.childNodes];
252
+ existingGroup.textContent = '';
253
+ for (const child of newChildren) {
254
+ const key = child.getAttribute?.('data-key');
255
+ if (key && preservedEls.has(key)) {
256
+ existingGroup.appendChild(preservedEls.get(key));
257
+ } else {
258
+ existingGroup.appendChild(child);
259
+ }
260
+ }
261
+ }
262
+
263
+ // Update viewBox if dimensions changed
264
+ const vb = newSvg.getAttribute('viewBox');
265
+ if (vb) existingSvg.setAttribute('viewBox', vb);
266
+
267
+ postRender(sceneGraph);
268
+ }
269
+
270
+ function postRender(sceneGraph) {
271
+ // Legend
272
+ if (currentLegend) {
273
+ currentLegend.remove();
274
+ currentLegend = null;
275
+ }
276
+ const meta = sceneGraph.meta || {};
277
+ if (spec.legend !== false && meta.series) {
278
+ currentLegend = buildLegend(meta.series, spec);
279
+ container.appendChild(currentLegend);
280
+ }
281
+
282
+ // Tooltip
283
+ const svgEl = inner.querySelector('svg');
284
+ if (spec.tooltip && svgEl) {
285
+ attachTooltip(inner, svgEl, meta, spec);
286
+ }
287
+
288
+ // Click handler
289
+ if (spec.onClick && svgEl) {
290
+ attachClickHandler(svgEl, meta, spec);
291
+ }
292
+ }
293
+
294
+ // ResizeObserver for responsive re-render
295
+ if (typeof ResizeObserver !== 'undefined') {
296
+ const ro = new ResizeObserver(() => renderChart());
297
+ ro.observe(inner);
298
+ onDestroy(() => ro.disconnect());
299
+ } else if (isReactive) {
300
+ // Fallback: double-rAF for initial sizing
301
+ requestAnimationFrame(() => requestAnimationFrame(doRender));
302
+ }
303
+
304
+ if (isReactive) {
305
+ createEffect(() => {
306
+ resolve(specInput.data);
307
+ renderChart();
308
+ });
309
+ } else {
310
+ // Initial render triggered by ResizeObserver or fallback
311
+ if (typeof ResizeObserver === 'undefined') {
312
+ requestAnimationFrame(() => requestAnimationFrame(doRender));
313
+ }
314
+ }
315
+
316
+ // Accessible data table fallback
317
+ if (spec.tableAlt) {
318
+ const details = document.createElement('details');
319
+ const summary = document.createElement('summary');
320
+ summary.className = 'd-chart-sr';
321
+ summary.textContent = 'View data table';
322
+ details.appendChild(summary);
323
+ details.addEventListener('toggle', () => {
324
+ if (details.open && !details.querySelector('table')) {
325
+ details.appendChild(buildDataTable(spec));
326
+ }
327
+ });
328
+ container.appendChild(details);
329
+ }
330
+
331
+ return container;
332
+ }
333
+
334
+ /**
335
+ * Create an inline sparkline element.
336
+ * @param {Object} specInput
337
+ * @returns {HTMLElement}
338
+ */
339
+ export function Sparkline(specInput) {
340
+ injectChartBase();
341
+ const spec = { ...specInput };
342
+ const heightPx = parseInt(spec.height || '32', 10) || 32;
343
+ const widthPx = parseInt(spec.width || '120', 10) || 120;
344
+
345
+ const container = document.createElement('span');
346
+ container.className = 'd-chart-spark' + (spec.class ? ' ' + spec.class : '');
347
+ container.setAttribute('role', 'img');
348
+ container.setAttribute('aria-label', spec['aria-label'] || 'Sparkline');
349
+
350
+ const isReactive = typeof specInput.data === 'function';
351
+
352
+ function renderSpark() {
353
+ const data = resolve(spec.data);
354
+ const sceneGraph = layoutSparkline({ ...spec, data }, widthPx, heightPx);
355
+ const svgEl = render(sceneGraph, { renderer: 'svg' });
356
+ container.textContent = '';
357
+ container.appendChild(svgEl);
358
+ }
359
+
360
+ if (isReactive) {
361
+ createEffect(() => {
362
+ resolve(specInput.data);
363
+ renderSpark();
364
+ });
365
+ } else {
366
+ renderSpark();
367
+ }
368
+
369
+ return container;
370
+ }
371
+
372
+ // --- Streaming API ---
373
+
374
+ /**
375
+ * Create a streaming data source for live charts.
376
+ * @param {Object} [opts]
377
+ * @param {number} [opts.maxPoints=500] — rolling buffer size
378
+ * @returns {{ append: Function, data: Function, window: Function, destroy: Function }}
379
+ */
380
+ export function createStream(opts = {}) {
381
+ const maxPoints = opts.maxPoints || 500;
382
+ const buffer = [];
383
+ const [data, setData] = createSignal([]);
384
+
385
+ function append(point) {
386
+ if (Array.isArray(point)) {
387
+ buffer.push(...point);
388
+ } else {
389
+ buffer.push(point);
390
+ }
391
+ // Trim to maxPoints
392
+ while (buffer.length > maxPoints) buffer.shift();
393
+ setData([...buffer]);
394
+ }
395
+
396
+ function window(start, end) {
397
+ return buffer.slice(start, end);
398
+ }
399
+
400
+ function destroy() {
401
+ buffer.length = 0;
402
+ setData([]);
403
+ }
404
+
405
+ return { append, data, window, destroy };
406
+ }
407
+
408
+ // --- Extended Palette ---
409
+
410
+ /**
411
+ * Continuous color scale for heatmaps/treemaps.
412
+ * @param {number[]} domain — [min, max] or [min, mid, max]
413
+ * @param {string[]} stops — hex color stops
414
+ * @returns {(value: number) => string}
415
+ */
416
+ export function colorScale(domain, stops) {
417
+ if (!stops || stops.length < 2) return () => stops?.[0] || '#000';
418
+ const segments = stops.length - 1;
419
+
420
+ return function(value) {
421
+ const t = Math.max(0, Math.min(1, (value - domain[0]) / ((domain[domain.length - 1] || 1) - domain[0])));
422
+ const seg = Math.min(Math.floor(t * segments), segments - 1);
423
+ const localT = (t * segments) - seg;
424
+ return interpolateHex(stops[seg], stops[seg + 1], localT);
425
+ };
426
+ }
427
+
428
+ function interpolateHex(hex1, hex2, t) {
429
+ const r1 = parseInt(hex1.slice(1, 3), 16), g1 = parseInt(hex1.slice(3, 5), 16), b1 = parseInt(hex1.slice(5, 7), 16);
430
+ const r2 = parseInt(hex2.slice(1, 3), 16), g2 = parseInt(hex2.slice(3, 5), 16), b2 = parseInt(hex2.slice(5, 7), 16);
431
+ const r = Math.round(r1 + (r2 - r1) * t);
432
+ const g = Math.round(g1 + (g2 - g1) * t);
433
+ const b = Math.round(b1 + (b2 - b1) * t);
434
+ return '#' + [r, g, b].map(c => Math.max(0, Math.min(255, c)).toString(16).padStart(2, '0')).join('');
435
+ }
436
+
437
+ /**
438
+ * Resolve palette to hex values for Canvas/WebGPU rendering.
439
+ * @param {number} count — number of colors needed
440
+ * @param {HTMLElement} [el] — element for getComputedStyle
441
+ * @returns {string[]}
442
+ */
443
+ export function resolvePalette(count, el) {
444
+ const colors = [];
445
+ const target = el || document.documentElement;
446
+ const style = typeof getComputedStyle === 'function' ? getComputedStyle(target) : null;
447
+
448
+ for (let i = 0; i < count; i++) {
449
+ const prop = i < 8 ? `--d-chart-${i}` : `--d-chart-${i % 8}-ext-${Math.floor((i - 8) / 8) + 1}`;
450
+ let color = style ? style.getPropertyValue(prop).trim() : '';
451
+ if (!color) color = FALLBACK_COLORS[i % FALLBACK_COLORS.length];
452
+ colors.push(color);
453
+ }
454
+
455
+ return colors;
456
+ }
457
+
458
+ const FALLBACK_COLORS = ['#1366D9', '#7c3aed', '#0891b2', '#22c55e', '#f59e0b', '#ef4444', '#3b82f6', '#71717a'];
459
+
460
+ // --- Animation helpers ---
461
+
462
+ /**
463
+ * Create a zero-state scene graph for entrance animation.
464
+ * Deep-clones the target scene with zeroed data values.
465
+ * @param {Object} targetScene
466
+ * @returns {Object}
467
+ */
468
+ function createZeroScene(targetScene) {
469
+ if (!targetScene) return targetScene;
470
+ return {
471
+ ...targetScene,
472
+ children: targetScene.children ? targetScene.children.map(zeroNode) : []
473
+ };
474
+ }
475
+
476
+ function zeroNode(node) {
477
+ if (!node) return node;
478
+ const result = { ...node };
479
+
480
+ switch (node.type) {
481
+ case 'rect':
482
+ // Bars: collapse to baseline
483
+ if (result.h != null) {
484
+ const fullH = result.h;
485
+ result.y = (result.y || 0) + fullH;
486
+ result.h = 0;
487
+ }
488
+ break;
489
+ case 'circle':
490
+ // Dots: scale from zero
491
+ result.r = 0;
492
+ break;
493
+ case 'path':
494
+ // Lines/areas: fade in
495
+ result.opacity = 0;
496
+ break;
497
+ case 'arc':
498
+ // Pie slices: grow from zero
499
+ result.endAngle = result.startAngle;
500
+ break;
501
+ case 'group':
502
+ if (result.children) {
503
+ result.children = result.children.map(zeroNode);
504
+ }
505
+ break;
506
+ }
507
+
508
+ return result;
509
+ }
510
+
511
+ /**
512
+ * Apply CSS stroke-dashoffset draw animation to SVG line paths.
513
+ * @param {HTMLElement} inner
514
+ */
515
+ function applyLineDrawAnimation(inner) {
516
+ if (typeof window === 'undefined') return;
517
+ const paths = inner.querySelectorAll('.d-chart-line');
518
+ for (const p of paths) {
519
+ if (typeof p.getTotalLength === 'function') {
520
+ const len = p.getTotalLength();
521
+ p.style.setProperty('--d-path-len', String(len));
522
+ p.setAttribute('data-animate', '');
523
+ }
524
+ }
525
+ }
526
+
527
+ // --- Internal helpers ---
528
+
529
+ function buildLegend(series, spec) {
530
+ const legend = document.createElement('div');
531
+ legend.className = 'd-chart-legend';
532
+
533
+ for (const s of series) {
534
+ const item = document.createElement('span');
535
+ item.className = 'd-chart-legend-item';
536
+
537
+ const swatch = document.createElement('span');
538
+ swatch.className = 'd-chart-legend-swatch';
539
+ swatch.style.background = s.color;
540
+ item.appendChild(swatch);
541
+ item.appendChild(document.createTextNode(s.key));
542
+
543
+ // Legend toggle
544
+ let disabled = false;
545
+ item.addEventListener('click', () => {
546
+ disabled = !disabled;
547
+ item.classList.toggle('d-chart-legend-disabled', disabled);
548
+ if (spec.onLegendToggle) {
549
+ spec.onLegendToggle({ series: s.key, visible: !disabled });
550
+ }
551
+ });
552
+
553
+ legend.appendChild(item);
554
+ }
555
+
556
+ return legend;
557
+ }
558
+
559
+ function attachTooltip(inner, svgEl, meta, spec) {
560
+ const tooltip = document.createElement('div');
561
+ tooltip.className = 'd-chart-tooltip';
562
+ inner.appendChild(tooltip);
563
+
564
+ const elements = svgEl.querySelectorAll('[data-series],[data-label]');
565
+ for (const el of elements) {
566
+ el.addEventListener('mouseenter', () => {
567
+ const value = el.getAttribute('data-value') || '';
568
+ const label = el.getAttribute('data-label') || el.getAttribute('data-series') || '';
569
+ if (typeof spec.tooltip === 'function') {
570
+ tooltip.textContent = '';
571
+ const result = spec.tooltip({ label, value, element: el });
572
+ if (typeof result === 'string') tooltip.textContent = result;
573
+ else if (result) tooltip.appendChild(result);
574
+ } else {
575
+ tooltip.textContent = label + (value ? ': ' + value : '');
576
+ }
577
+ tooltip.classList.add('d-chart-tooltip-visible');
578
+ });
579
+ el.addEventListener('mousemove', (e) => {
580
+ const r = inner.getBoundingClientRect();
581
+ tooltip.style.left = (e.clientX - r.left + 12) + 'px';
582
+ tooltip.style.top = (e.clientY - r.top - 8) + 'px';
583
+ });
584
+ el.addEventListener('mouseleave', () => {
585
+ tooltip.classList.remove('d-chart-tooltip-visible');
586
+ });
587
+ }
588
+ }
589
+
590
+ function attachClickHandler(svgEl, meta, spec) {
591
+ const elements = svgEl.querySelectorAll('[data-series],[data-label]');
592
+ for (const el of elements) {
593
+ el.style.cursor = 'pointer';
594
+ el.addEventListener('click', (e) => {
595
+ const series = el.getAttribute('data-series') || '';
596
+ const value = el.getAttribute('data-value') || '';
597
+ const label = el.getAttribute('data-label') || '';
598
+ spec.onClick({ point: { label, value }, series, event: e });
599
+ });
600
+ }
601
+ }
602
+
603
+ function buildDataTable(spec) {
604
+ const data = resolve(spec.data);
605
+ if (!data || !data.length) return document.createTextNode('No data');
606
+
607
+ const table = document.createElement('table');
608
+ table.className = 'd-chart-table';
609
+
610
+ const fields = [spec.x, ...(Array.isArray(spec.y) ? spec.y : [spec.y])].filter(Boolean);
611
+ const thead = document.createElement('thead');
612
+ const headerRow = document.createElement('tr');
613
+ for (const f of fields) {
614
+ const th = document.createElement('th');
615
+ th.textContent = f;
616
+ headerRow.appendChild(th);
617
+ }
618
+ thead.appendChild(headerRow);
619
+ table.appendChild(thead);
620
+
621
+ const tbody = document.createElement('tbody');
622
+ for (const row of data) {
623
+ const tr = document.createElement('tr');
624
+ for (const f of fields) {
625
+ const td = document.createElement('td');
626
+ td.textContent = row[f] != null ? String(row[f]) : '';
627
+ tr.appendChild(td);
628
+ }
629
+ tbody.appendChild(tr);
630
+ }
631
+ table.appendChild(tbody);
632
+ return table;
633
+ }
634
+
635
+ // Re-export utilities
636
+ export { chartColor, PALETTE_SIZE } from './layouts/_layout-base.js';
637
+ export { registerRenderer } from './_renderer.js';