@ds-mo/ui 13.6.1 → 13.8.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.
- package/dist/.package-ready.json +1 -1
- package/dist/agent-patterns.json +1 -1
- package/dist/agent.json +109 -371
- package/dist/angular/ds-card-chart.d.ts +18 -0
- package/dist/angular/{ds-card-data-viz.js → ds-card-chart.js} +10 -10
- package/dist/angular/ds-chart.d.ts +19 -0
- package/dist/angular/{ds-chart-donut.js → ds-chart.js} +15 -15
- package/dist/angular/ds-tooltip-chart.d.ts +12 -0
- package/dist/angular/{ds-chart-line.js → ds-tooltip-chart.js} +12 -12
- package/dist/angular/index.d.ts +1 -1
- package/dist/angular/index.js +3 -5
- package/dist/angular/proxies.d.ts +3 -5
- package/dist/angular/proxies.js +3 -5
- package/dist/components/ds-agent-activity.js +1 -1
- package/dist/components/ds-agent-response.js +1 -1
- package/dist/components/ds-agent-source-list.js +1 -1
- package/dist/components/ds-agent-tool-call.js +1 -1
- package/dist/components/ds-attachment-list.js +1 -1
- package/dist/components/ds-badge.js +1 -1
- package/dist/components/ds-banner.js +1 -1
- package/dist/components/ds-banner.js.map +1 -1
- package/dist/components/ds-bar-nav.js +1 -1
- package/dist/components/ds-bar-title.js +1 -1
- package/dist/components/ds-bar-workflow.js +1 -1
- package/dist/components/ds-breadcrumb.js +1 -1
- package/dist/components/ds-button-filled.js +1 -1
- package/dist/components/ds-button-unfilled.js +1 -1
- package/dist/components/{ds-chart-line.d.ts → ds-card-chart.d.ts} +4 -4
- package/dist/components/ds-card-chart.js +2 -0
- package/dist/components/ds-card-chart.js.map +1 -0
- package/dist/components/ds-card-overview.js +1 -1
- package/dist/components/ds-card-overview.js.map +1 -1
- package/dist/components/ds-card-setting.js +1 -1
- package/dist/components/ds-card-setting.js.map +1 -1
- package/dist/components/ds-chart-legend.js +1 -1
- package/dist/components/ds-chart-legend.js.map +1 -1
- package/dist/components/{ds-chart-bar.d.ts → ds-chart.d.ts} +4 -4
- package/dist/components/ds-chart.js +2 -0
- package/dist/components/ds-chart.js.map +1 -0
- package/dist/components/ds-checkbox.js +1 -1
- package/dist/components/ds-chip.js +1 -1
- package/dist/components/ds-code-block.js +1 -1
- package/dist/components/ds-conversation-list-item.js +1 -1
- package/dist/components/ds-conversation-list-section.js +1 -1
- package/dist/components/ds-conversation-list.js +1 -1
- package/dist/components/ds-divider.js +1 -1
- package/dist/components/ds-empty-state.js +1 -1
- package/dist/components/ds-field.js +1 -1
- package/dist/components/ds-input.js +1 -1
- package/dist/components/ds-markdown.js +1 -1
- package/dist/components/ds-menu.js +1 -1
- package/dist/components/ds-message-actions.js +1 -1
- package/dist/components/ds-message-bubble.js +1 -1
- package/dist/components/ds-message-composer.js +1 -1
- package/dist/components/ds-message-scroller.js +1 -1
- package/dist/components/ds-message.js +1 -1
- package/dist/components/ds-mobile-bar-nav.js +1 -1
- package/dist/components/ds-mobile-header.js +1 -1
- package/dist/components/ds-mobile-section-switcher.js +1 -1
- package/dist/components/ds-mobile-sheet-nav.js +1 -1
- package/dist/components/ds-modal.js +1 -1
- package/dist/components/ds-modal.js.map +1 -1
- package/dist/components/ds-panel-nav.js +1 -1
- package/dist/components/ds-panel-sub-nav.js +1 -1
- package/dist/components/ds-panel-tool-header.js +1 -1
- package/dist/components/ds-panel-tool-search.js +1 -1
- package/dist/components/ds-panel-tools.js +1 -1
- package/dist/components/ds-radio.js +1 -1
- package/dist/components/ds-scroll-overlay.js +1 -1
- package/dist/components/ds-select.js +1 -1
- package/dist/components/ds-select.js.map +1 -1
- package/dist/components/ds-shell-app.js +1 -1
- package/dist/components/ds-shell-page.js +1 -1
- package/dist/components/ds-shell-tools.js +1 -1
- package/dist/components/ds-skeleton.js +1 -1
- package/dist/components/ds-slider.js +1 -1
- package/dist/components/ds-swatch-picker.js +1 -1
- package/dist/components/ds-switch.js +1 -1
- package/dist/components/ds-tab-group.js +1 -1
- package/dist/components/ds-tag.js +1 -1
- package/dist/components/ds-text.js +1 -1
- package/dist/components/ds-toast.js +1 -1
- package/dist/components/ds-toast.js.map +1 -1
- package/dist/components/{ds-card-data-viz.d.ts → ds-tooltip-chart.d.ts} +4 -4
- package/dist/components/ds-tooltip-chart.js +2 -0
- package/dist/components/ds-tooltip.js +1 -1
- package/dist/components/ds-typing-indicator.js +1 -1
- package/dist/components/{p-iCjUWcp7.js → p-2inC7Jm-.js} +2 -2
- package/dist/components/{p-iCjUWcp7.js.map → p-2inC7Jm-.js.map} +1 -1
- package/dist/components/{p-DaqFuVKZ.js → p-B16OuqyJ.js} +2 -2
- package/dist/components/{p-BmNpPyzf.js → p-B4VlfBNc.js} +2 -2
- package/dist/components/{p-og48Kh2i.js → p-B6z51yBf.js} +2 -2
- package/dist/components/{p-Bb4f5SuF.js → p-BNiIyZCn.js} +2 -2
- package/dist/components/{p-BFfHIu6y.js → p-BP-EtYbR.js} +2 -2
- package/dist/components/{p-DHF5cLzr.js → p-BQ7Nzx9A.js} +2 -2
- package/dist/components/{p-B-kRByho.js → p-BQDi0v_h.js} +2 -2
- package/dist/components/{p-BBtjABBS.js → p-BYB0f1_F.js} +2 -2
- package/dist/components/p-BjIYFiuQ.js +2 -0
- package/dist/components/{p-D0bM3cKj.js.map → p-BjIYFiuQ.js.map} +1 -1
- package/dist/components/{p-DfvDNcoq.js → p-BjoyHNaM.js} +2 -2
- package/dist/components/{p-Bl6A5AJq.js → p-BkWhTEqe.js} +2 -2
- package/dist/components/p-BnGJrBPG.js +2 -0
- package/dist/components/{p-1S9AW2MA.js.map → p-BnGJrBPG.js.map} +1 -1
- package/dist/components/p-BtS5na9O.js +2 -0
- package/dist/components/{p-DX4c3kZ1.js.map → p-BtS5na9O.js.map} +1 -1
- package/dist/components/p-CCYgBUUS.js +2 -0
- package/dist/components/{p-D2cjoj2r.js.map → p-CCYgBUUS.js.map} +1 -1
- package/dist/components/p-CXduurfd.js +2 -0
- package/dist/components/p-CXduurfd.js.map +1 -0
- package/dist/components/{p-CxgMUvqg.js → p-CnbrksLl.js} +2 -2
- package/dist/components/{p-Eep5mxtv.js → p-Cog5NTLa.js} +2 -2
- package/dist/components/{p-Bmo4T96l.js → p-D2dQyoyK.js} +2 -2
- package/dist/components/{p-BPyymeuR.js → p-D6g070eY.js} +2 -2
- package/dist/components/{p-wOOAzVMC.js → p-D8qgVmHn.js} +2 -2
- package/dist/components/{p-BR_oMaas.js → p-DJ0XMH9E.js} +2 -2
- package/dist/components/{p-B6RrZQqw.js → p-DK4PGrHm.js} +2 -2
- package/dist/components/{p-DVuxzrCC.js → p-DLwM1RNL.js} +2 -2
- package/dist/components/p-DW9p068S.js +2 -0
- package/dist/components/{p-CuJ3J6OJ.js.map → p-DW9p068S.js.map} +1 -1
- package/dist/components/{p-Bsfpxto1.js → p-Dg9qiMDa.js} +2 -2
- package/dist/components/p-Dg9qiMDa.js.map +1 -0
- package/dist/components/{p-B4_0Ys4w.js → p-DgglLVCj.js} +2 -2
- package/dist/components/p-DmblUQ4D.js +2 -0
- package/dist/components/p-DmblUQ4D.js.map +1 -0
- package/dist/components/{p-6B411hQA.js → p-Dv8_hmUZ.js} +2 -2
- package/dist/components/{p-IYy-jODg.js → p-DwKC_9u2.js} +2 -2
- package/dist/components/p-GbTasczF.js +2 -0
- package/dist/components/{p-CzraIk34.js → p-HIg1XX62.js} +2 -2
- package/dist/components/{p-Di81y9Q2.js → p-KFHFBznh.js} +2 -2
- package/dist/components/{p-CKRJX0Y0.js → p-PQgDzzn9.js} +2 -2
- package/dist/components/{p-Ca6s5fLR.js → p-iyiOFsws.js} +2 -2
- package/dist/docs/components.json +180 -662
- package/dist/lib/utils/chart-grammar.d.ts +359 -0
- package/dist/lib/utils/index.d.ts +2 -0
- package/dist/lib/utils/index.js +504 -0
- package/dist/lib/utils/index.js.map +4 -4
- package/dist/lib/utils/token-defaults.d.ts +3 -0
- package/dist/mcp-data/registry/agent-activity.json +1 -1
- package/dist/mcp-data/registry/agent-response.json +1 -1
- package/dist/mcp-data/registry/agent-source-list.json +1 -1
- package/dist/mcp-data/registry/agent-tool-call.json +1 -1
- package/dist/mcp-data/registry/attachment-list.json +1 -1
- package/dist/mcp-data/registry/avatar.json +1 -1
- package/dist/mcp-data/registry/badge.json +1 -1
- package/dist/mcp-data/registry/banner.json +1 -1
- package/dist/mcp-data/registry/bar-nav.json +2 -2
- package/dist/mcp-data/registry/bar-title.json +1 -1
- package/dist/mcp-data/registry/bar-workflow.json +1 -1
- package/dist/mcp-data/registry/breadcrumb.json +1 -1
- package/dist/mcp-data/registry/button-filled.json +1 -1
- package/dist/mcp-data/registry/button-unfilled.json +1 -1
- package/dist/mcp-data/registry/card-chart.json +238 -0
- package/dist/mcp-data/registry/card-overview.json +7 -7
- package/dist/mcp-data/registry/card-setting.json +5 -5
- package/dist/mcp-data/registry/chart-legend.json +12 -19
- package/dist/mcp-data/registry/chart.json +258 -0
- package/dist/mcp-data/registry/checkbox.json +1 -1
- package/dist/mcp-data/registry/chip.json +1 -1
- package/dist/mcp-data/registry/code-block.json +1 -1
- package/dist/mcp-data/registry/conversation-list-item.json +1 -1
- package/dist/mcp-data/registry/conversation-list-section.json +1 -1
- package/dist/mcp-data/registry/conversation-list.json +1 -1
- package/dist/mcp-data/registry/divider.json +1 -1
- package/dist/mcp-data/registry/empty-state.json +1 -1
- package/dist/mcp-data/registry/field.json +1 -1
- package/dist/mcp-data/registry/icon.json +1 -1
- package/dist/mcp-data/registry/input.json +1 -1
- package/dist/mcp-data/registry/loader.json +1 -1
- package/dist/mcp-data/registry/markdown.json +1 -1
- package/dist/mcp-data/registry/menu.json +1 -1
- package/dist/mcp-data/registry/message-actions.json +1 -1
- package/dist/mcp-data/registry/message-bubble.json +1 -1
- package/dist/mcp-data/registry/message-composer.json +1 -1
- package/dist/mcp-data/registry/message-scroller.json +1 -1
- package/dist/mcp-data/registry/message.json +1 -1
- package/dist/mcp-data/registry/mobile-bar-nav.json +1 -1
- package/dist/mcp-data/registry/mobile-header.json +1 -1
- package/dist/mcp-data/registry/mobile-section-switcher.json +1 -1
- package/dist/mcp-data/registry/mobile-sheet-nav.json +1 -1
- package/dist/mcp-data/registry/modal.json +1 -1
- package/dist/mcp-data/registry/panel-nav.json +1 -1
- package/dist/mcp-data/registry/panel-sub-nav.json +1 -1
- package/dist/mcp-data/registry/panel-tool-header.json +1 -1
- package/dist/mcp-data/registry/panel-tool-search.json +1 -1
- package/dist/mcp-data/registry/panel-tools.json +1 -1
- package/dist/mcp-data/registry/radio.json +1 -1
- package/dist/mcp-data/registry/registry.json +109 -377
- package/dist/mcp-data/registry/scroll-overlay.json +1 -1
- package/dist/mcp-data/registry/select.json +1 -1
- package/dist/mcp-data/registry/shell-app.json +1 -1
- package/dist/mcp-data/registry/shell-page.json +1 -1
- package/dist/mcp-data/registry/shell-tools.json +1 -1
- package/dist/mcp-data/registry/skeleton.json +3 -3
- package/dist/mcp-data/registry/slider.json +1 -1
- package/dist/mcp-data/registry/swatch-picker.json +1 -1
- package/dist/mcp-data/registry/switch.json +1 -1
- package/dist/mcp-data/registry/tab-group.json +1 -1
- package/dist/mcp-data/registry/tag.json +1 -1
- package/dist/mcp-data/registry/text.json +1 -1
- package/dist/mcp-data/registry/toast.json +1 -1
- package/dist/mcp-data/registry/tooltip-chart.json +253 -0
- package/dist/mcp-data/registry/tooltip.json +3 -3
- package/dist/mcp-data/registry/typing-indicator.json +1 -1
- package/dist/react/components.d.ts +3 -5
- package/dist/react/components.js +3 -5
- package/dist/react/ds-card-chart.d.ts +12 -0
- package/dist/react/ds-card-chart.js +12 -0
- package/dist/react/ds-chart.d.ts +12 -0
- package/dist/react/ds-chart.js +12 -0
- package/dist/react/{ds-chart-bar.d.ts → ds-tooltip-chart.d.ts} +3 -3
- package/dist/react/ds-tooltip-chart.js +12 -0
- package/dist/types/components/{CardDataViz/CardDataViz.d.ts → CardChart/CardChart.d.ts} +9 -13
- package/dist/types/components/CardChart/index.d.ts +2 -0
- package/dist/types/components/CardOverview/CardOverview.d.ts +2 -0
- package/dist/types/components/Chart/Chart.d.ts +66 -0
- package/dist/types/components/Chart/index.d.ts +2 -0
- package/dist/types/components/ChartLegend/ChartLegend.d.ts +1 -1
- package/dist/types/components/{TooltipDataViz/TooltipDataViz.d.ts → TooltipChart/TooltipChart.d.ts} +15 -6
- package/dist/types/components/TooltipChart/index.d.ts +2 -0
- package/dist/types/components.d.ts +129 -294
- package/dist/types/utils/chart-grammar.d.ts +360 -0
- package/dist/types/utils/chart-scene.d.ts +128 -0
- package/dist/types/utils/chart-theme.d.ts +27 -0
- package/dist/types/utils/chart-types.d.ts +0 -13
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/token-defaults.d.ts +3 -0
- package/package.json +3 -3
- package/dist/angular/ds-card-data-viz.d.ts +0 -18
- package/dist/angular/ds-chart-bar.d.ts +0 -12
- package/dist/angular/ds-chart-bar.js +0 -38
- package/dist/angular/ds-chart-donut.d.ts +0 -19
- package/dist/angular/ds-chart-line.d.ts +0 -12
- package/dist/angular/ds-tooltip-data-viz.d.ts +0 -12
- package/dist/angular/ds-tooltip-data-viz.js +0 -38
- package/dist/components/ds-card-data-viz.js +0 -2
- package/dist/components/ds-card-data-viz.js.map +0 -1
- package/dist/components/ds-chart-bar.js +0 -2
- package/dist/components/ds-chart-bar.js.map +0 -1
- package/dist/components/ds-chart-donut.d.ts +0 -11
- package/dist/components/ds-chart-donut.js +0 -2
- package/dist/components/ds-chart-donut.js.map +0 -1
- package/dist/components/ds-chart-line.js +0 -2
- package/dist/components/ds-chart-line.js.map +0 -1
- package/dist/components/ds-tooltip-data-viz.d.ts +0 -11
- package/dist/components/ds-tooltip-data-viz.js +0 -2
- package/dist/components/p-1S9AW2MA.js +0 -2
- package/dist/components/p-B6Vuayt6.js +0 -2
- package/dist/components/p-B6Vuayt6.js.map +0 -1
- package/dist/components/p-BBpASejH.js +0 -2
- package/dist/components/p-BBpASejH.js.map +0 -1
- package/dist/components/p-Bsfpxto1.js.map +0 -1
- package/dist/components/p-BybqPD8K.js +0 -2
- package/dist/components/p-BybqPD8K.js.map +0 -1
- package/dist/components/p-C4uYvyq6.js +0 -2
- package/dist/components/p-C4uYvyq6.js.map +0 -1
- package/dist/components/p-CbjFUpOj.js +0 -2
- package/dist/components/p-CbjFUpOj.js.map +0 -1
- package/dist/components/p-CrCGIx8s.js +0 -2
- package/dist/components/p-CrCGIx8s.js.map +0 -1
- package/dist/components/p-CuJ3J6OJ.js +0 -2
- package/dist/components/p-D0bM3cKj.js +0 -2
- package/dist/components/p-D2cjoj2r.js +0 -2
- package/dist/components/p-DX4c3kZ1.js +0 -2
- package/dist/components/p-DXewjzkM.js +0 -2
- package/dist/components/p-DXewjzkM.js.map +0 -1
- package/dist/components/p-yhj1lIPx.js +0 -2
- package/dist/mcp-data/registry/card-data-viz.json +0 -241
- package/dist/mcp-data/registry/chart-bar.json +0 -245
- package/dist/mcp-data/registry/chart-donut.json +0 -344
- package/dist/mcp-data/registry/chart-line.json +0 -223
- package/dist/mcp-data/registry/tooltip-data-viz.json +0 -224
- package/dist/react/ds-card-data-viz.d.ts +0 -12
- package/dist/react/ds-card-data-viz.js +0 -12
- package/dist/react/ds-chart-bar.js +0 -12
- package/dist/react/ds-chart-donut.d.ts +0 -12
- package/dist/react/ds-chart-donut.js +0 -12
- package/dist/react/ds-chart-line.d.ts +0 -9
- package/dist/react/ds-chart-line.js +0 -12
- package/dist/react/ds-tooltip-data-viz.d.ts +0 -9
- package/dist/react/ds-tooltip-data-viz.js +0 -12
- package/dist/types/components/CardDataViz/index.d.ts +0 -2
- package/dist/types/components/ChartBar/ChartBar.d.ts +0 -31
- package/dist/types/components/ChartBar/index.d.ts +0 -3
- package/dist/types/components/ChartDonut/ChartDonut.d.ts +0 -72
- package/dist/types/components/ChartDonut/index.d.ts +0 -2
- package/dist/types/components/ChartLine/ChartLine.d.ts +0 -23
- package/dist/types/components/ChartLine/index.d.ts +0 -2
- package/dist/types/components/TooltipDataViz/index.d.ts +0 -2
- /package/dist/components/{ds-tooltip-data-viz.js.map → ds-tooltip-chart.js.map} +0 -0
- /package/dist/components/{p-DaqFuVKZ.js.map → p-B16OuqyJ.js.map} +0 -0
- /package/dist/components/{p-BmNpPyzf.js.map → p-B4VlfBNc.js.map} +0 -0
- /package/dist/components/{p-og48Kh2i.js.map → p-B6z51yBf.js.map} +0 -0
- /package/dist/components/{p-Bb4f5SuF.js.map → p-BNiIyZCn.js.map} +0 -0
- /package/dist/components/{p-BFfHIu6y.js.map → p-BP-EtYbR.js.map} +0 -0
- /package/dist/components/{p-DHF5cLzr.js.map → p-BQ7Nzx9A.js.map} +0 -0
- /package/dist/components/{p-B-kRByho.js.map → p-BQDi0v_h.js.map} +0 -0
- /package/dist/components/{p-BBtjABBS.js.map → p-BYB0f1_F.js.map} +0 -0
- /package/dist/components/{p-DfvDNcoq.js.map → p-BjoyHNaM.js.map} +0 -0
- /package/dist/components/{p-Bl6A5AJq.js.map → p-BkWhTEqe.js.map} +0 -0
- /package/dist/components/{p-CxgMUvqg.js.map → p-CnbrksLl.js.map} +0 -0
- /package/dist/components/{p-Eep5mxtv.js.map → p-Cog5NTLa.js.map} +0 -0
- /package/dist/components/{p-Bmo4T96l.js.map → p-D2dQyoyK.js.map} +0 -0
- /package/dist/components/{p-BPyymeuR.js.map → p-D6g070eY.js.map} +0 -0
- /package/dist/components/{p-wOOAzVMC.js.map → p-D8qgVmHn.js.map} +0 -0
- /package/dist/components/{p-BR_oMaas.js.map → p-DJ0XMH9E.js.map} +0 -0
- /package/dist/components/{p-B6RrZQqw.js.map → p-DK4PGrHm.js.map} +0 -0
- /package/dist/components/{p-DVuxzrCC.js.map → p-DLwM1RNL.js.map} +0 -0
- /package/dist/components/{p-B4_0Ys4w.js.map → p-DgglLVCj.js.map} +0 -0
- /package/dist/components/{p-6B411hQA.js.map → p-Dv8_hmUZ.js.map} +0 -0
- /package/dist/components/{p-IYy-jODg.js.map → p-DwKC_9u2.js.map} +0 -0
- /package/dist/components/{p-yhj1lIPx.js.map → p-GbTasczF.js.map} +0 -0
- /package/dist/components/{p-CzraIk34.js.map → p-HIg1XX62.js.map} +0 -0
- /package/dist/components/{p-Di81y9Q2.js.map → p-KFHFBznh.js.map} +0 -0
- /package/dist/components/{p-CKRJX0Y0.js.map → p-PQgDzzn9.js.map} +0 -0
- /package/dist/components/{p-Ca6s5fLR.js.map → p-iyiOFsws.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
3
|
"name": "chart-legend",
|
|
4
4
|
"title": "ChartLegend",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "External chart legend with swatches, labels, optional values and percentages, and explicit deep links.",
|
|
6
6
|
"type": "registry:ui",
|
|
7
7
|
"meta": {
|
|
8
8
|
"library": "@ds-mo/ui",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
"intentStatus": "complete",
|
|
13
13
|
"intent": {
|
|
14
14
|
"audience": "specialized",
|
|
15
|
-
"status": "
|
|
16
|
-
"summary": "
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "External chart legend with swatches, labels, optional values and percentages, and explicit deep links.",
|
|
17
17
|
"useWhen": [
|
|
18
18
|
"A chart needs the standard swatch, label, and optional numeric-detail legend.",
|
|
19
|
-
"
|
|
19
|
+
"Polar slices or Cartesian series need a continuously visible key beside or below the visualization.",
|
|
20
20
|
"A category has an explicit deep-link destination and should render as an accessible link."
|
|
21
21
|
],
|
|
22
22
|
"avoidWhen": [
|
|
@@ -31,25 +31,18 @@
|
|
|
31
31
|
"reason": "Compose product-owned semantic markup from the same chart data rather than forcing it into this row recipe."
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
|
-
"when": "
|
|
35
|
-
"component": "component:ds-card-
|
|
36
|
-
"reason": "The
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"when": "A static line chart and series key belong in the canonical dashboard card.",
|
|
40
|
-
"component": "component:ds-card-data-viz",
|
|
41
|
-
"reason": "The line variant places the horizontal legend and disables decorative local hover highlighting."
|
|
34
|
+
"when": "A chart and static series key belong in the canonical dashboard card.",
|
|
35
|
+
"component": "component:ds-card-chart",
|
|
36
|
+
"reason": "The chart variant places the external legend and disables decorative local hover highlighting."
|
|
42
37
|
}
|
|
43
38
|
],
|
|
44
39
|
"commonlyComposedWith": [
|
|
45
|
-
"component:ds-chart
|
|
46
|
-
"component:ds-chart
|
|
47
|
-
"component:ds-chart-line",
|
|
48
|
-
"component:ds-card-data-viz"
|
|
40
|
+
"component:ds-chart",
|
|
41
|
+
"component:ds-card-chart"
|
|
49
42
|
],
|
|
50
43
|
"accessibility": [
|
|
51
44
|
"Rows remain non-interactive unless the item supplies an actual link destination.",
|
|
52
|
-
"
|
|
45
|
+
"Optional highlight changes opacity only and never moves focus or announces selection.",
|
|
53
46
|
"Keep full labels available when visible text truncates, and retain stable tabular numeric columns.",
|
|
54
47
|
"Do not rely on swatch color alone; every entry requires a meaningful visible label."
|
|
55
48
|
],
|
|
@@ -274,7 +267,7 @@
|
|
|
274
267
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: items.",
|
|
275
268
|
"peerDependencies": {
|
|
276
269
|
"required": [
|
|
277
|
-
"@ds-mo/tokens ^5.1.
|
|
270
|
+
"@ds-mo/tokens ^5.1.2",
|
|
278
271
|
"@ds-mo/icons ^6.4.0"
|
|
279
272
|
],
|
|
280
273
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
@@ -296,7 +289,7 @@
|
|
|
296
289
|
},
|
|
297
290
|
{
|
|
298
291
|
"path": "src/wc/components/ChartLegend/ChartLegend.tsx",
|
|
299
|
-
"content": "import { Component, Prop, State, Event, EventEmitter, h, Host, Watch } from '@stencil/core';\nimport { categoryColor } from '../../utils/chart-colors';\nimport { formatCompactNumber, formatPercentage, resolveSafeUrl } from '../../utils';\nimport type { ChartLegendItem } from '../../utils/chart-types';\n\nexport type ChartLegendDirection = 'vertical' | 'horizontal';\nexport type ChartLegendPercentageDecimals = 1 | 2;\n\n/**\n * Base legend for `ds-chart-*` components. Webapp's Overview widgets each style\n * legends differently (list w/ values, compact chips, bare swatches, external\n * stat callouts) — this covers the common \"swatch + label + optional value\" case.\n * Consumers who need a different treatment skip this component entirely and\n * render their own markup against the same `
|
|
292
|
+
"content": "import { Component, Prop, State, Event, EventEmitter, h, Host, Watch } from '@stencil/core';\nimport { categoryColor } from '../../utils/chart-colors';\nimport { formatCompactNumber, formatPercentage, resolveSafeUrl } from '../../utils';\nimport type { ChartLegendItem } from '../../utils/chart-types';\n\nexport type ChartLegendDirection = 'vertical' | 'horizontal';\nexport type ChartLegendPercentageDecimals = 1 | 2;\n\n/**\n * Base legend for `ds-chart-*` components. Webapp's Overview widgets each style\n * legends differently (list w/ values, compact chips, bare swatches, external\n * stat callouts) — this covers the common \"swatch + label + optional value\" case.\n * Consumers who need a different treatment skip this component entirely and\n * render their own markup against the same `ChartLegendItem[]` data.\n */\n@Component({\n tag: 'ds-chart-legend',\n styleUrl: 'ChartLegend.css',\n scoped: true,\n})\nexport class ChartLegend {\n @Prop() locale: string | undefined;\n /** Legend entries. Set as a JS property (not an HTML attribute). */\n @Prop() items: ChartLegendItem[] = [];\n @Prop() direction: ChartLegendDirection = 'vertical';\n /** Show each item's share of the total (of items with a `value`) alongside its count. */\n @Prop() showPercentage: boolean = true;\n /** Fixed number of decimal places shown for percentages. */\n @Prop() percentageDecimals: ChartLegendPercentageDecimals = 1;\n /** Let local pointer/focus movement highlight one item and emit `dsItemHover`. */\n @Prop() highlightOnHover: boolean = true;\n /**\n * Externally controlled highlight, matched by `label` — e.g. drive this from a sibling\n * chart's `dsSliceHover` event to keep chart and legend hover in sync. Only dims the other\n * rows' opacity (like a chart dimming its other slices) — it never shows the hover-fill,\n * since that's reserved for a real pointer/keyboard interaction on this row.\n */\n @Prop() activeLabel: string | null = null;\n\n /** Own pointer/focus hover, kept separate from `activeLabel` so the hover-fill (a \"you can\n * click here\" affordance) never shows just because an external row was synced in. */\n @State() private hoveredLabel: string | null = null;\n\n /** Fires on row hover/focus with the item, or `null` on leave/blur. */\n @Event() dsItemHover!: EventEmitter<ChartLegendItem | null>;\n /** Fires when a deep-linkable row (`item.href` set) is activated. */\n @Event() dsItemClick!: EventEmitter<{ item: ChartLegendItem; originalEvent: MouseEvent }>;\n\n @Watch('highlightOnHover')\n handleHighlightOnHoverChange(enabled: boolean) {\n if (!enabled) this.hoveredLabel = null;\n }\n\n private handleHover(item: ChartLegendItem | null) {\n this.hoveredLabel = item?.label ?? null;\n this.dsItemHover.emit(item);\n }\n\n private handleClick = (item: ChartLegendItem, originalEvent: MouseEvent) => {\n this.dsItemClick.emit({ item, originalEvent });\n };\n\n render() {\n const total = this.items.reduce((sum, item) => sum + (item.value ?? 0), 0);\n // Highlight/dim from either this component's own hover or an externally-synced label\n // (e.g. an application-owned chart focus state).\n const highlightLabel = this.activeLabel ?? (this.highlightOnHover ? this.hoveredLabel : null);\n\n return (\n <Host\n class={{\n 'chart-legend': true,\n [`chart-legend--${this.direction}`]: true,\n 'chart-legend--show-percentage': this.showPercentage,\n }}\n >\n {/* mouseleave lives on the list, not each row — the row-gap between items is still\n inside the list's box, so crossing it while moving between rows never fires this.\n Only actually leaving the whole list does, and that clears with no delay. */}\n <ul\n class=\"chart-legend__list\"\n onMouseLeave={this.highlightOnHover ? () => this.handleHover(null) : undefined}\n >\n {this.items.map((item, i) => {\n const isDimmed = highlightLabel != null && item.label !== highlightLabel;\n const href = resolveSafeUrl(item.href);\n const RowTag = href ? 'a' : 'div';\n const percentage = item.value != null\n ? formatPercentage(total ? item.value / total : 0, this.percentageDecimals, this.locale)\n : '';\n\n return (\n <li class=\"chart-legend__list-item\" key={item.label}>\n <RowTag\n class={{\n 'chart-legend__item': true,\n 'chart-legend__item--interactive': !!href,\n 'chart-legend__item--dimmed': isDimmed,\n 'ds-control--md': true,\n 'ds-interaction-fill': this.highlightOnHover || !!href,\n 'ds-focus-ring-inset': !!href,\n }}\n href={href}\n onClick={href ? (e: MouseEvent) => this.handleClick(item, e) : undefined}\n onMouseEnter={\n this.highlightOnHover ? () => this.handleHover(item) : undefined\n }\n onFocus={this.highlightOnHover ? () => this.handleHover(item) : undefined}\n onBlur={this.highlightOnHover ? () => this.handleHover(null) : undefined}\n >\n <span class=\"chart-legend__swatch-box\">\n <span class=\"chart-legend__swatch\" style={{ backgroundColor: item.color ?? categoryColor(i) }} />\n </span>\n <ds-text\n class=\"chart-legend__label\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"secondary\"\n lineTruncation={1}\n title={item.label}\n >\n {item.label}\n </ds-text>\n {item.value != null && (\n <ds-text\n class=\"chart-legend__value\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"primary\"\n lineTruncation={1}\n align=\"right\"\n fontFeature=\"tabular-nums\"\n title={String(item.value)}\n >\n {formatCompactNumber(item.value, this.locale)}\n </ds-text>\n )}\n {item.value != null && this.showPercentage && (\n <ds-text\n class=\"chart-legend__percentage\"\n as=\"span\"\n variant=\"text-body-medium\"\n color=\"primary\"\n lineTruncation={1}\n align=\"right\"\n fontFeature=\"tabular-nums\"\n title={percentage}\n >\n {percentage}\n </ds-text>\n )}\n </RowTag>\n </li>\n );\n })}\n </ul>\n </Host>\n );\n }\n}\n",
|
|
300
293
|
"type": "registry:ui"
|
|
301
294
|
}
|
|
302
295
|
]
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "chart",
|
|
4
|
+
"title": "Chart",
|
|
5
|
+
"description": "Responsive chart grammar for Cartesian, polar, heatmap, and distribution compositions with one keyed scene and centralized focus.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"meta": {
|
|
8
|
+
"library": "@ds-mo/ui",
|
|
9
|
+
"distribution": "npm",
|
|
10
|
+
"storybook": "https://zainadeel.github.io/compomo/?path=/story/chart",
|
|
11
|
+
"source": "src/wc/components/Chart/Chart.tsx",
|
|
12
|
+
"intentStatus": "complete",
|
|
13
|
+
"intent": {
|
|
14
|
+
"audience": "specialized",
|
|
15
|
+
"status": "stable",
|
|
16
|
+
"summary": "Responsive chart grammar for Cartesian, polar, heatmap, and distribution compositions with one keyed scene and centralized focus.",
|
|
17
|
+
"useWhen": [
|
|
18
|
+
"A product needs a responsive line, area, bar, scatter, pie, donut, gauge, radar, heatmap, histogram, density, or boxplot visualization.",
|
|
19
|
+
"Several mark layers must share scales and semantic interaction points."
|
|
20
|
+
],
|
|
21
|
+
"avoidWhen": [
|
|
22
|
+
"The visualization needs maps, networks, brushing, zooming, selection, or Canvas."
|
|
23
|
+
],
|
|
24
|
+
"alternatives": [],
|
|
25
|
+
"commonlyComposedWith": [
|
|
26
|
+
"component:ds-chart-legend",
|
|
27
|
+
"component:ds-card-chart",
|
|
28
|
+
"component:ds-tooltip-chart"
|
|
29
|
+
],
|
|
30
|
+
"accessibility": [
|
|
31
|
+
"Provide the required label and an adjacent exact-value summary when exact values are essential.",
|
|
32
|
+
"The SVG is one tab stop; arrow keys, Home, End, and Escape control semantic focus.",
|
|
33
|
+
"Pointer and keyboard input resolve through the same interaction points and polite status output."
|
|
34
|
+
],
|
|
35
|
+
"states": [
|
|
36
|
+
"Definition compilation is renderer-independent and regenerates when data, locale, width, or height changes.",
|
|
37
|
+
"Stable mark and datum keys preserve focus across responsive recompilation and reordered data.",
|
|
38
|
+
"Tooltip rows are chart-owned while legends remain external and static.",
|
|
39
|
+
"Pie and donut pointer tooltips require direct slice containment and follow the pointer; keyboard tooltips remain anchored to the semantic slice.",
|
|
40
|
+
"Polar center content defaults to its authored total and may opt into focused content."
|
|
41
|
+
],
|
|
42
|
+
"responsiveBehavior": [
|
|
43
|
+
"The chart observes its container unless width is fixed and recompiles geometry at CSS pixel dimensions.",
|
|
44
|
+
"Height uses an authored height or aspectRatio, fills the chart region supplied by CardChart, and otherwise defaults to 320px.",
|
|
45
|
+
"Measured guide labels reserve margins and thin colliding ticks while retaining boundary ticks."
|
|
46
|
+
],
|
|
47
|
+
"frameworkCaveats": {
|
|
48
|
+
"customElements": [
|
|
49
|
+
"Assign definition as a JavaScript property and import D3 scale factories from granular D3 packages."
|
|
50
|
+
],
|
|
51
|
+
"react": [
|
|
52
|
+
"Pass the typed definition object as a property; do not serialize it to an attribute."
|
|
53
|
+
],
|
|
54
|
+
"angular": [
|
|
55
|
+
"Bind definition as a property so accessors and D3 factories remain intact."
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"references": [
|
|
59
|
+
{
|
|
60
|
+
"label": "Storybook examples",
|
|
61
|
+
"path": "src/wc/components/Chart/Chart.stories.ts"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"label": "Chart guide",
|
|
65
|
+
"path": "src/docs/Charts.mdx"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"api": {
|
|
70
|
+
"props": {
|
|
71
|
+
"aspectRatio": {
|
|
72
|
+
"type": "number",
|
|
73
|
+
"resolvedType": "number | undefined",
|
|
74
|
+
"attribute": "aspect-ratio",
|
|
75
|
+
"required": false,
|
|
76
|
+
"mutable": false,
|
|
77
|
+
"description": "Derives height from the current width when height is not supplied."
|
|
78
|
+
},
|
|
79
|
+
"definition": {
|
|
80
|
+
"type": "ChartDefinition",
|
|
81
|
+
"resolvedType": "ChartDefinition<unknown>",
|
|
82
|
+
"required": true,
|
|
83
|
+
"mutable": false,
|
|
84
|
+
"description": "Typed chart definition. Set as a JavaScript property, not an HTML attribute."
|
|
85
|
+
},
|
|
86
|
+
"description": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"resolvedType": "string | undefined",
|
|
89
|
+
"attribute": "description",
|
|
90
|
+
"required": false,
|
|
91
|
+
"mutable": false,
|
|
92
|
+
"description": "Optional longer accessible description."
|
|
93
|
+
},
|
|
94
|
+
"height": {
|
|
95
|
+
"type": "number",
|
|
96
|
+
"resolvedType": "number | undefined",
|
|
97
|
+
"attribute": "height",
|
|
98
|
+
"required": false,
|
|
99
|
+
"mutable": false,
|
|
100
|
+
"description": "Product-owned surface height. Wins over aspectRatio."
|
|
101
|
+
},
|
|
102
|
+
"label": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"resolvedType": "string",
|
|
105
|
+
"attribute": "label",
|
|
106
|
+
"required": true,
|
|
107
|
+
"mutable": false,
|
|
108
|
+
"description": "Required accessible name for the visualization."
|
|
109
|
+
},
|
|
110
|
+
"locale": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"resolvedType": "string | undefined",
|
|
113
|
+
"attribute": "locale",
|
|
114
|
+
"required": false,
|
|
115
|
+
"mutable": false,
|
|
116
|
+
"description": "Locale used by axes and default tooltip formatters."
|
|
117
|
+
},
|
|
118
|
+
"width": {
|
|
119
|
+
"type": "number",
|
|
120
|
+
"resolvedType": "number | undefined",
|
|
121
|
+
"attribute": "width",
|
|
122
|
+
"required": false,
|
|
123
|
+
"mutable": false,
|
|
124
|
+
"description": "Fixed surface width. Otherwise the chart observes its container."
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"events": [
|
|
128
|
+
{
|
|
129
|
+
"name": "dsChartFocusChange",
|
|
130
|
+
"detail": "ChartFocusChangeDetail",
|
|
131
|
+
"bubbles": true,
|
|
132
|
+
"cancelable": true,
|
|
133
|
+
"composed": true,
|
|
134
|
+
"description": "Fires whenever pointer or keyboard focus resolves to semantic chart points."
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"methods": [],
|
|
138
|
+
"slots": []
|
|
139
|
+
},
|
|
140
|
+
"props": {
|
|
141
|
+
"aspectRatio": {
|
|
142
|
+
"type": "number",
|
|
143
|
+
"resolvedType": "number | undefined",
|
|
144
|
+
"attribute": "aspect-ratio",
|
|
145
|
+
"required": false,
|
|
146
|
+
"mutable": false,
|
|
147
|
+
"description": "Derives height from the current width when height is not supplied."
|
|
148
|
+
},
|
|
149
|
+
"definition": {
|
|
150
|
+
"type": "ChartDefinition",
|
|
151
|
+
"resolvedType": "ChartDefinition<unknown>",
|
|
152
|
+
"required": true,
|
|
153
|
+
"mutable": false,
|
|
154
|
+
"description": "Typed chart definition. Set as a JavaScript property, not an HTML attribute."
|
|
155
|
+
},
|
|
156
|
+
"description": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"resolvedType": "string | undefined",
|
|
159
|
+
"attribute": "description",
|
|
160
|
+
"required": false,
|
|
161
|
+
"mutable": false,
|
|
162
|
+
"description": "Optional longer accessible description."
|
|
163
|
+
},
|
|
164
|
+
"height": {
|
|
165
|
+
"type": "number",
|
|
166
|
+
"resolvedType": "number | undefined",
|
|
167
|
+
"attribute": "height",
|
|
168
|
+
"required": false,
|
|
169
|
+
"mutable": false,
|
|
170
|
+
"description": "Product-owned surface height. Wins over aspectRatio."
|
|
171
|
+
},
|
|
172
|
+
"label": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"resolvedType": "string",
|
|
175
|
+
"attribute": "label",
|
|
176
|
+
"required": true,
|
|
177
|
+
"mutable": false,
|
|
178
|
+
"description": "Required accessible name for the visualization."
|
|
179
|
+
},
|
|
180
|
+
"locale": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"resolvedType": "string | undefined",
|
|
183
|
+
"attribute": "locale",
|
|
184
|
+
"required": false,
|
|
185
|
+
"mutable": false,
|
|
186
|
+
"description": "Locale used by axes and default tooltip formatters."
|
|
187
|
+
},
|
|
188
|
+
"width": {
|
|
189
|
+
"type": "number",
|
|
190
|
+
"resolvedType": "number | undefined",
|
|
191
|
+
"attribute": "width",
|
|
192
|
+
"required": false,
|
|
193
|
+
"mutable": false,
|
|
194
|
+
"description": "Fixed surface width. Otherwise the chart observes its container."
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
"events": [
|
|
198
|
+
{
|
|
199
|
+
"name": "dsChartFocusChange",
|
|
200
|
+
"detail": "ChartFocusChangeDetail",
|
|
201
|
+
"bubbles": true,
|
|
202
|
+
"cancelable": true,
|
|
203
|
+
"composed": true,
|
|
204
|
+
"description": "Fires whenever pointer or keyboard focus resolves to semantic chart points."
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
"methods": [],
|
|
208
|
+
"slots": [],
|
|
209
|
+
"exports": {
|
|
210
|
+
"customElement": "ds-chart",
|
|
211
|
+
"react": "DsChart",
|
|
212
|
+
"angular": "DsChart"
|
|
213
|
+
},
|
|
214
|
+
"consumption": {
|
|
215
|
+
"install": "npm install @ds-mo/ui @ds-mo/tokens @ds-mo/icons",
|
|
216
|
+
"cssSetup": "import '@ds-mo/tokens';\nimport '@ds-mo/tokens/reset';\nimport '@ds-mo/tokens/globals';",
|
|
217
|
+
"customElements": {
|
|
218
|
+
"import": "import '@ds-mo/ui/dist/components/ds-chart.js';",
|
|
219
|
+
"example": "<ds-chart></ds-chart>"
|
|
220
|
+
},
|
|
221
|
+
"react": {
|
|
222
|
+
"import": "import { DsChart } from '@ds-mo/ui/react';",
|
|
223
|
+
"example": "<DsChart />"
|
|
224
|
+
},
|
|
225
|
+
"angular": {
|
|
226
|
+
"import": "import { DsChart } from '@ds-mo/ui/angular/ds-chart';",
|
|
227
|
+
"example": "<ds-chart></ds-chart>"
|
|
228
|
+
},
|
|
229
|
+
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: definition.",
|
|
230
|
+
"peerDependencies": {
|
|
231
|
+
"required": [
|
|
232
|
+
"@ds-mo/tokens ^5.1.2",
|
|
233
|
+
"@ds-mo/icons ^6.4.0"
|
|
234
|
+
],
|
|
235
|
+
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"dependencies": [
|
|
240
|
+
"@ds-mo/ui",
|
|
241
|
+
"@ds-mo/tokens"
|
|
242
|
+
],
|
|
243
|
+
"registryDependencies": [
|
|
244
|
+
"tooltip-chart"
|
|
245
|
+
],
|
|
246
|
+
"files": [
|
|
247
|
+
{
|
|
248
|
+
"path": "src/wc/components/Chart/Chart.css",
|
|
249
|
+
"content": "@import '../../utils/forced-colors.css';\n\n:host {\n position: relative;\n display: block;\n width: var(--ds-chart-width, 100%);\n max-width: 100%;\n height: var(--ds-chart-height, 320px);\n font-family: var(--typography-font-family);\n}\n\n.chart__surface {\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n outline: none;\n touch-action: none;\n}\n\n.chart__surface:focus-visible {\n outline: var(--dimension-stroke-width-025) solid var(--color-border-focus);\n outline-offset: var(--dimension-space-025);\n}\n\n.chart__interaction-surface {\n fill: transparent;\n pointer-events: all;\n}\n\n.chart__grid,\n.chart__axis-line,\n.chart__plot-boundary,\n.chart__mark {\n vector-effect: non-scaling-stroke;\n}\n\n.chart__grid {\n stroke: var(--color-foreground-quaternary);\n stroke-width: var(--dimension-stroke-width-012);\n}\n\n.chart__plot-boundary {\n stroke: var(--color-foreground-quaternary);\n stroke-width: var(--dimension-stroke-width-012);\n}\n\n.chart__axis-line {\n stroke: var(--color-foreground-tertiary);\n stroke-width: var(--dimension-stroke-width-012);\n}\n\n.chart__tick,\n.chart__text-mark {\n fill: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-xs);\n font-weight: var(--typography-weight-medium);\n letter-spacing: var(--typography-letterspacing-positive);\n text-transform: uppercase;\n}\n\n.chart__axis-title {\n fill: var(--color-foreground-primary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-xs);\n font-weight: var(--typography-weight-semibold);\n letter-spacing: var(--typography-letterspacing-positive);\n text-transform: uppercase;\n}\n\n.chart__center-value {\n fill: var(--color-foreground-primary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-xl);\n font-weight: var(--typography-weight-semibold);\n letter-spacing: var(--typography-letterspacing-negative-double);\n}\n\n.chart__center-caption {\n fill: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.chart__focus {\n fill: none;\n stroke: var(--color-border-focus);\n stroke-width: var(--dimension-stroke-width-025);\n vector-effect: non-scaling-stroke;\n pointer-events: none;\n}\n\n.chart__focus-guide {\n stroke: var(--color-foreground-quaternary);\n stroke-width: var(--dimension-stroke-width-012);\n vector-effect: non-scaling-stroke;\n pointer-events: none;\n}\n\n.chart__focus-point {\n vector-effect: non-scaling-stroke;\n pointer-events: none;\n}\n\n.chart__description,\n.chart__status {\n position: absolute;\n width: var(--dimension-stroke-width-012);\n height: var(--dimension-stroke-width-012);\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@media (forced-colors: active) {\n .chart__mark,\n .chart__focus,\n .chart__focus-point {\n forced-color-adjust: none;\n }\n\n .chart__focus {\n stroke: var(--ds-forced-color-focus);\n }\n}\n",
|
|
250
|
+
"type": "registry:ui"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"path": "src/wc/components/Chart/Chart.tsx",
|
|
254
|
+
"content": "import {\n Component,\n Element,\n Event,\n EventEmitter,\n Prop,\n State,\n Watch,\n h,\n Host,\n} from '@stencil/core';\nimport type {\n ChartDefinition,\n ChartPoint,\n ChartTooltipItem,\n ChartTooltipOptions,\n} from '../../utils/chart-grammar';\nimport {\n compileChartScene,\n findChartFocus,\n type ChartScene,\n type ChartSceneNode,\n} from '../../utils/chart-scene';\nimport { resolveChartTheme } from '../../utils/chart-theme';\n\nconst DEFAULT_WIDTH = 480;\nconst DEFAULT_HEIGHT = 320;\n\nexport type ChartFocusSource = 'pointer' | 'keyboard';\n\nexport interface ChartFocusChangeDetail {\n primary: ChartPoint | null;\n points: readonly ChartPoint[];\n source: ChartFocusSource;\n}\n\ninterface FocusState {\n primary: ChartPoint;\n points: ChartPoint[];\n source: ChartFocusSource;\n}\n\ninterface PointerAnchor {\n x: number;\n y: number;\n}\n\n@Component({\n tag: 'ds-chart',\n styleUrl: 'Chart.css',\n scoped: true,\n})\nexport class Chart {\n @Element() el!: HTMLElement;\n\n /** Typed chart definition. Set as a JavaScript property, not an HTML attribute. */\n @Prop() definition!: ChartDefinition;\n /** Required accessible name for the visualization. */\n @Prop() label!: string;\n /** Optional longer accessible description. */\n @Prop() description?: string;\n /** Locale used by axes and default tooltip formatters. */\n @Prop() locale?: string;\n /** Fixed surface width. Otherwise the chart observes its container. */\n @Prop() width?: number;\n /** Product-owned surface height. Wins over aspectRatio. */\n @Prop() height?: number;\n /** Derives height from the current width when height is not supplied. */\n @Prop() aspectRatio?: number;\n\n /** Fires whenever pointer or keyboard focus resolves to semantic chart points. */\n @Event() dsChartFocusChange!: EventEmitter<ChartFocusChangeDetail>;\n\n @State() private surfaceWidth = DEFAULT_WIDTH;\n @State() private containerHeight?: number;\n @State() private scene?: ChartScene;\n @State() private focusState?: FocusState;\n @State() private pointerAnchor?: PointerAnchor;\n\n private resizeObserver?: ResizeObserver;\n private resizeFrame?: number;\n private compileFrame?: number;\n private warnedConflictingHeight = false;\n private measuredLabels = new Map<string, { width: number; height: number }>();\n private descriptionId = `ds-chart-description-${Math.random().toString(36).slice(2)}`;\n private clipId = `ds-chart-clip-${Math.random().toString(36).slice(2)}`;\n\n componentWillLoad() {\n if (this.width && this.width > 0) this.surfaceWidth = this.width;\n this.compile();\n }\n\n componentDidLoad() {\n this.connectResizeObserver();\n this.waitForFonts();\n }\n\n componentDidRender() {\n this.measureRenderedLabels();\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect();\n if (this.resizeFrame) cancelAnimationFrame(this.resizeFrame);\n if (this.compileFrame) cancelAnimationFrame(this.compileFrame);\n }\n\n @Watch('definition')\n @Watch('locale')\n @Watch('height')\n @Watch('aspectRatio')\n onDefinitionInputChange() {\n this.scheduleCompile();\n }\n\n @Watch('width')\n onWidthChange() {\n if (this.width && this.width > 0) this.surfaceWidth = this.width;\n else this.measureHost();\n this.scheduleCompile();\n }\n\n private get surfaceHeight(): number {\n if (this.height && this.height > 0) {\n if (this.aspectRatio && !this.warnedConflictingHeight) {\n this.warnedConflictingHeight = true;\n console.warn('[ds-chart] height wins when both height and aspectRatio are supplied.');\n }\n return this.height;\n }\n if (this.aspectRatio && this.aspectRatio > 0) {\n return Math.max(1, this.surfaceWidth / this.aspectRatio);\n }\n return this.containerHeight ?? DEFAULT_HEIGHT;\n }\n\n private connectResizeObserver() {\n this.resizeObserver?.disconnect();\n if (typeof ResizeObserver === 'undefined') return;\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0];\n if (!entry) return;\n if (this.resizeFrame) cancelAnimationFrame(this.resizeFrame);\n this.resizeFrame = requestAnimationFrame(() => {\n this.resizeFrame = undefined;\n this.applyMeasuredSize(entry.contentRect.width, entry.contentRect.height);\n });\n });\n this.resizeObserver.observe(this.el);\n this.measureHost();\n }\n\n private measureHost() {\n const bounds = this.el.getBoundingClientRect();\n this.applyMeasuredSize(bounds.width, bounds.height);\n }\n\n private applyMeasuredSize(width: number, height: number) {\n let changed = false;\n if (!this.width && width > 0) {\n const nextWidth = Math.round(width * 100) / 100;\n if (nextWidth !== this.surfaceWidth) {\n this.surfaceWidth = nextWidth;\n changed = true;\n }\n }\n if (!this.height && !this.aspectRatio && height > 0) {\n const nextHeight = Math.round(height * 100) / 100;\n if (nextHeight !== this.containerHeight) {\n this.containerHeight = nextHeight;\n changed = true;\n }\n }\n if (changed) {\n this.scheduleCompile();\n }\n }\n\n private scheduleCompile() {\n if (this.compileFrame) cancelAnimationFrame(this.compileFrame);\n this.compileFrame = requestAnimationFrame(() => {\n this.compileFrame = undefined;\n this.compile();\n });\n }\n\n private compile() {\n if (!this.definition || !(this.surfaceWidth > 0) || !(this.surfaceHeight > 0)) return;\n const previousKey = this.focusState?.primary.sceneKey;\n const scene = compileChartScene(\n this.definition,\n this.surfaceWidth,\n this.surfaceHeight,\n this.locale ?? document.documentElement.lang ?? 'en',\n text => this.measuredLabels.get(text) ?? { width: text.length * 7, height: 14 },\n resolveChartTheme(this.el),\n );\n this.scene = scene;\n if (previousKey) {\n const restored = scene.points.find(point => point.sceneKey === previousKey);\n if (restored && this.focusState) {\n const grouped = findChartFocus(\n scene.points,\n scene.focus,\n restored.x,\n restored.y,\n scene.maxFocusDistance,\n scene.arcHitRegions,\n );\n this.focusState = grouped\n ? { ...grouped, source: this.focusState.source }\n : undefined;\n } else {\n this.focusState = undefined;\n }\n }\n }\n\n private waitForFonts() {\n if (!document.fonts) return;\n void document.fonts.ready.then(() => {\n this.measuredLabels.clear();\n this.scheduleCompile();\n });\n }\n\n private measureRenderedLabels() {\n const labels = this.el.querySelectorAll<SVGTextElement>('[data-chart-measure]');\n let changed = false;\n labels.forEach(label => {\n const text = label.textContent ?? '';\n let box: DOMRect;\n try {\n box = label.getBBox();\n } catch {\n return;\n }\n const previous = this.measuredLabels.get(text);\n if (!previous || Math.abs(previous.width - box.width) > 0.5 || Math.abs(previous.height - box.height) > 0.5) {\n this.measuredLabels.set(text, { width: box.width, height: box.height });\n changed = true;\n }\n });\n if (changed) this.scheduleCompile();\n }\n\n private resolveFocus(x: number, y: number, source: ChartFocusSource) {\n if (!this.scene) return;\n const result = findChartFocus(\n this.scene.points,\n this.scene.focus,\n x,\n y,\n this.scene.maxFocusDistance,\n this.scene.arcHitRegions,\n );\n if (!result) {\n this.clearFocus(source);\n return;\n }\n if (\n this.focusState?.primary.sceneKey === result.primary.sceneKey &&\n this.focusState.source === source\n ) return;\n this.focusState = { ...result, source };\n this.dsChartFocusChange.emit({ ...result, source });\n }\n\n private clearFocus(source: ChartFocusSource) {\n if (!this.focusState) return;\n this.focusState = undefined;\n this.dsChartFocusChange.emit({ primary: null, points: [], source });\n }\n\n private onPointerMove = (event: PointerEvent) => {\n const svg = event.currentTarget as SVGSVGElement;\n const rect = svg.getBoundingClientRect();\n const x = event.clientX - rect.left;\n const y = event.clientY - rect.top;\n this.pointerAnchor = this.scene?.arcHitRegions.length ? { x, y } : undefined;\n this.resolveFocus(x, y, 'pointer');\n };\n\n private onPointerLeave = () => {\n this.pointerAnchor = undefined;\n this.clearFocus('pointer');\n };\n\n private keyboardPoints(): ChartPoint[] {\n if (!this.scene) return [];\n return [...this.scene.points].sort((a, b) => a.x - b.x || a.y - b.y);\n }\n\n private onKeyDown = (event: KeyboardEvent) => {\n const points = this.keyboardPoints();\n if (points.length === 0) return;\n if (event.key === 'Escape') {\n event.preventDefault();\n this.clearFocus('keyboard');\n return;\n }\n const current = this.focusState\n ? points.findIndex(point => point.sceneKey === this.focusState?.primary.sceneKey)\n : -1;\n let next: number | undefined;\n if (event.key === 'Home') next = 0;\n else if (event.key === 'End') next = points.length - 1;\n else if (event.key === 'ArrowRight' || event.key === 'ArrowDown') next = Math.min(points.length - 1, current + 1);\n else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') next = Math.max(0, current < 0 ? 0 : current - 1);\n else return;\n event.preventDefault();\n const point = points[next ?? 0] ?? points[0];\n this.resolveFocus(point.x, point.y, 'keyboard');\n };\n\n private renderNode(node: ChartSceneNode, className = 'chart__mark') {\n const resolvedClassName = node.className ?? className;\n const presentation = {\n fill: node.style.fill,\n stroke: node.style.stroke,\n opacity: node.style.opacity,\n 'fill-opacity': node.style.fillOpacity,\n 'stroke-opacity': node.style.strokeOpacity,\n 'stroke-width': node.style.strokeWidth,\n 'stroke-dasharray': node.style.strokeDasharray || undefined,\n };\n if (node.type === 'path') return <path key={node.key} class={resolvedClassName} d={node.d} transform={node.transform} {...presentation} />;\n if (node.type === 'rect') {\n return <rect key={node.key} class={resolvedClassName} x={node.x} y={node.y} width={node.width} height={node.height} rx={node.style.radius} {...presentation} />;\n }\n if (node.type === 'circle') return <circle key={node.key} class={resolvedClassName} cx={node.x} cy={node.y} r={node.radius} {...presentation} />;\n if (node.type === 'line') return <line key={node.key} class={resolvedClassName} x1={node.x1} x2={node.x2} y1={node.y1} y2={node.y2} {...presentation} />;\n return <text key={node.key} class={node.className ?? `${className} chart__text-mark`} data-chart-measure={node.measure ? '' : undefined} x={node.x} y={node.y} dx={node.dx} dy={node.dy} text-anchor={node.style.textAnchor} font-family={node.style.fontFamily} font-size={node.style.fontSize} font-weight={node.style.fontWeight} dominant-baseline={node.dominantBaseline ?? 'middle'} transform={node.rotate ? `rotate(${node.rotate} ${node.x} ${node.y})` : undefined} {...presentation}>{node.text}</text>;\n }\n\n private renderMarkLayers(scene: ChartScene) {\n const layers: Array<{ clipped: boolean; nodes: ChartSceneNode[] }> = [];\n scene.nodes.forEach(node => {\n const clipped = scene.clip && node.clipToPlot !== false;\n const current = layers[layers.length - 1];\n if (current?.clipped === clipped) current.nodes.push(node);\n else layers.push({ clipped, nodes: [node] });\n });\n return layers.map((layer, index) => (\n <g\n key={`marks-${index}`}\n class=\"chart__marks\"\n aria-hidden=\"true\"\n clip-path={layer.clipped ? `url(#${this.clipId})` : undefined}\n >\n {layer.nodes.map(node => this.renderNode(node))}\n </g>\n ));\n }\n\n private focusedDotPoints(scene: ChartScene): ChartPoint[] {\n if (!this.focusState) return [];\n const focusDotPointKeys = new Set(scene.focusDotPointKeys);\n return this.focusState.points.filter(point => focusDotPointKeys.has(point.sceneKey));\n }\n\n private tooltipContent(): { heading?: string; items: ChartTooltipItem[] } | undefined {\n if (!this.scene || !this.focusState || this.scene.tooltip === false) return undefined;\n const options: ChartTooltipOptions =\n typeof this.scene.tooltip === 'object' ? this.scene.tooltip : {};\n const locale = this.locale ?? document.documentElement.lang ?? 'en';\n const points = this.focusState.points;\n const formatValue = (point: ChartPoint) => {\n const value = point.value ?? point.yValue;\n return options.format?.(point, locale) ??\n (typeof value === 'number'\n ? new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }).format(value)\n : String(value));\n };\n return {\n heading:\n points.length > 1\n ? options.formatGroupHeading?.(points, locale) ?? String(this.focusState.primary.xValue)\n : undefined,\n items: points.map(point =>\n options.formatGroupItem?.(point, locale) ?? {\n label: point.groupLabel ?? point.markId,\n value: formatValue(point),\n color: point.color,\n },\n ),\n };\n }\n\n private statusText(): string {\n const content = this.tooltipContent();\n if (!content) return '';\n return [content.heading, ...content.items.map(item => `${item.label}: ${item.value}`)]\n .filter(Boolean)\n .join('. ');\n }\n\n render() {\n const scene = this.scene;\n const tooltip = this.tooltipContent();\n const focus = this.focusState?.primary;\n const tooltipAnchor = focus &&\n this.focusState?.source === 'pointer' &&\n scene?.arcHitRegions.length &&\n this.pointerAnchor\n ? this.pointerAnchor\n : focus;\n const focusedDotPoints = scene ? this.focusedDotPoints(scene) : [];\n const groupedPointFocus = scene?.coordinate === 'cartesian' &&\n scene.focus === 'group-x' &&\n focusedDotPoints.length > 1;\n const center = focus && scene?.center?.focused\n ? scene.center.focused(focus, this.locale ?? document.documentElement.lang ?? 'en')\n : scene?.center;\n return (\n <Host\n class=\"chart\"\n style={{\n '--ds-chart-width': this.width ? `${this.width}px` : '100%',\n '--ds-chart-height': this.height || this.aspectRatio\n ? `${this.surfaceHeight}px`\n : 'var(--ds-chart-container-height, 320px)',\n }}\n >\n {this.description && <span id={this.descriptionId} class=\"chart__description\">{this.description}</span>}\n {scene && (\n /* eslint-disable-next-line local/prefer-ds-icon -- SVG is the chart renderer. */\n <svg\n class=\"chart__surface\"\n width={scene.width}\n height={scene.height}\n viewBox={`0 0 ${scene.width} ${scene.height}`}\n role=\"img\"\n aria-label={this.label || 'Chart'}\n aria-describedby={this.description ? this.descriptionId : undefined}\n tabindex={0}\n onPointerMove={this.onPointerMove}\n onPointerLeave={this.onPointerLeave}\n onKeyDown={this.onKeyDown}\n >\n <defs aria-hidden=\"true\">\n <clipPath id={this.clipId}>\n <rect x={scene.plot.left} y={scene.plot.top} width={scene.plot.width} height={scene.plot.height} />\n </clipPath>\n </defs>\n <rect\n class=\"chart__interaction-surface\"\n x=\"0\"\n y=\"0\"\n width={scene.width}\n height={scene.height}\n aria-hidden=\"true\"\n />\n <g class=\"chart__guides\" aria-hidden=\"true\">\n {scene.guides.map(node => this.renderNode(node, 'chart__grid chart__polar-guide'))}\n {scene.yAxis.line && <line class=\"chart__axis-line\" x1={scene.plot.left} x2={scene.plot.left} y1={scene.plot.top} y2={scene.plot.bottom} />}\n {scene.yAxis.ticks.map(tick => (\n <g key={`y-${tick.key}`}>\n {scene.yAxis.grid && <line class=\"chart__grid\" x1={scene.plot.left} x2={scene.plot.right} y1={tick.position} y2={tick.position} />}\n <line class=\"chart__axis-line chart__tick-stub\" x1={scene.plot.left - scene.yAxis.tickSize} x2={scene.plot.left} y1={tick.position} y2={tick.position} />\n {tick.labelVisible && <text data-chart-measure class=\"chart__tick chart__tick--y\" x={scene.plot.left - scene.yAxis.tickSize - scene.yAxis.tickPadding} y={tick.position} text-anchor=\"end\" dominant-baseline=\"middle\">{tick.label}</text>}\n </g>\n ))}\n {scene.xAxis.line && <line class=\"chart__axis-line\" x1={scene.plot.left} x2={scene.plot.right} y1={scene.plot.bottom} y2={scene.plot.bottom} />}\n {scene.xAxis.ticks.map(tick => (\n <g key={`x-${tick.key}`}>\n {scene.xAxis.grid && <line class=\"chart__grid\" x1={tick.position} x2={tick.position} y1={scene.plot.top} y2={scene.plot.bottom} />}\n <line class=\"chart__axis-line chart__tick-stub\" x1={tick.position} x2={tick.position} y1={scene.plot.bottom} y2={scene.plot.bottom + scene.xAxis.tickSize} />\n {tick.labelVisible && <text data-chart-measure class=\"chart__tick chart__tick--x\" x={tick.position} y={scene.plot.bottom + scene.xAxis.tickSize + scene.xAxis.tickPadding} text-anchor=\"middle\" dominant-baseline=\"hanging\" transform={scene.xAxis.rotate ? `rotate(${scene.xAxis.rotate} ${tick.position} ${scene.plot.bottom + scene.xAxis.tickSize + scene.xAxis.tickPadding})` : undefined}>{tick.label}</text>}\n </g>\n ))}\n {scene.xAxis.label && <text class=\"chart__axis-title\" x={(scene.plot.left + scene.plot.right) / 2} y={scene.height - 4} text-anchor=\"middle\">{scene.xAxis.label}</text>}\n {scene.yAxis.label && <text class=\"chart__axis-title\" x=\"8\" y={(scene.plot.top + scene.plot.bottom) / 2} text-anchor=\"middle\" dominant-baseline=\"middle\" transform={`rotate(-90 8 ${(scene.plot.top + scene.plot.bottom) / 2})`}>{scene.yAxis.label}</text>}\n {scene.coordinate === 'cartesian' && (\n <g class=\"chart__plot-frame\">\n <line class=\"chart__plot-boundary chart__plot-boundary--top\" x1={scene.plot.left} x2={scene.plot.right} y1={scene.plot.top} y2={scene.plot.top} />\n <line class=\"chart__plot-boundary chart__plot-boundary--right\" x1={scene.plot.right} x2={scene.plot.right} y1={scene.plot.top} y2={scene.plot.bottom} />\n </g>\n )}\n </g>\n {this.renderMarkLayers(scene)}\n {groupedPointFocus && focus && (\n <line\n class=\"chart__focus-guide\"\n x1={focus.x}\n x2={focus.x}\n y1={scene.plot.top}\n y2={scene.plot.bottom}\n aria-hidden=\"true\"\n />\n )}\n {focusedDotPoints.map(point => (\n <circle\n key={`focus-${point.sceneKey}`}\n class=\"chart__focus-point\"\n cx={point.x}\n cy={point.y}\n r={scene.focusDotCircleRadius}\n fill={point.color}\n stroke=\"var(--color-background-primary)\"\n stroke-width={scene.focusDotHaloWidth}\n aria-hidden=\"true\"\n />\n ))}\n {focus && focusedDotPoints.length === 0 && <circle class=\"chart__focus\" cx={focus.x} cy={focus.y} r=\"6\" aria-hidden=\"true\" />}\n {center?.value && (\n <text\n class=\"chart__center-value\"\n x={(scene.plot.left + scene.plot.right) / 2}\n y={(scene.plot.top + scene.plot.bottom) / 2 - (center.caption ? 10 : 0)}\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n aria-hidden=\"true\"\n >\n {center.value}\n </text>\n )}\n {center?.caption && (\n <text\n class=\"chart__center-caption\"\n x={(scene.plot.left + scene.plot.right) / 2}\n y={(scene.plot.top + scene.plot.bottom) / 2 + 14}\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n aria-hidden=\"true\"\n >\n {center.caption}\n </text>\n )}\n </svg>\n )}\n {tooltip && tooltipAnchor && (\n <ds-tooltip-chart\n heading={tooltip.heading}\n items={tooltip.items}\n x={tooltipAnchor.x}\n y={tooltipAnchor.y}\n />\n )}\n <span class=\"chart__status\" aria-live=\"polite\" aria-atomic=\"true\">{this.statusText()}</span>\n </Host>\n );\n }\n}\n",
|
|
255
|
+
"type": "registry:ui"
|
|
256
|
+
}
|
|
257
|
+
]
|
|
258
|
+
}
|
|
@@ -453,7 +453,7 @@
|
|
|
453
453
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: anchor, items, sections.",
|
|
454
454
|
"peerDependencies": {
|
|
455
455
|
"required": [
|
|
456
|
-
"@ds-mo/tokens ^5.1.
|
|
456
|
+
"@ds-mo/tokens ^5.1.2",
|
|
457
457
|
"@ds-mo/icons ^6.4.0"
|
|
458
458
|
],
|
|
459
459
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: currentArea.",
|
|
300
300
|
"peerDependencies": {
|
|
301
301
|
"required": [
|
|
302
|
-
"@ds-mo/tokens ^5.1.
|
|
302
|
+
"@ds-mo/tokens ^5.1.2",
|
|
303
303
|
"@ds-mo/icons ^6.4.0"
|
|
304
304
|
],
|
|
305
305
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
"complexPropertyNote": "Assign these non-primitive values as JavaScript properties: sections, subsections.",
|
|
281
281
|
"peerDependencies": {
|
|
282
282
|
"required": [
|
|
283
|
-
"@ds-mo/tokens ^5.1.
|
|
283
|
+
"@ds-mo/tokens ^5.1.2",
|
|
284
284
|
"@ds-mo/icons ^6.4.0"
|
|
285
285
|
],
|
|
286
286
|
"frameworks": "Custom Elements; React 18/19 wrappers; Angular 19-22 standalone adapters."
|