@signalflare-ai/ui 1.2.0 → 1.3.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/CHANGELOG.md +77 -1
- package/ai/component-registry.json +208 -550
- package/ai/component-registry.md +3042 -3115
- package/ai/schemas.ts +504 -96
- package/dist/.build-complete +1 -1
- package/dist/ai/schemas.d.ts.map +1 -1
- package/dist/{ai-actions-BdUZI3Gk.js → ai-actions-CBfz5XEf.js} +4 -4
- package/dist/{ai-actions-BdUZI3Gk.js.map → ai-actions-CBfz5XEf.js.map} +1 -1
- package/dist/{ai-agent-card-BR2NIYhi.js → ai-agent-card-CByAUe0q.js} +3 -3
- package/dist/ai-agent-card-CByAUe0q.js.map +1 -0
- package/dist/{ai-approval-Ba7mrKba.js → ai-approval-Ci8N70a7.js} +4 -3
- package/dist/{ai-approval-Ba7mrKba.js.map → ai-approval-Ci8N70a7.js.map} +1 -1
- package/dist/{ai-code-block-CZtoL73R.js → ai-code-block-P9TJHvaC.js} +37 -39
- package/dist/ai-code-block-P9TJHvaC.js.map +1 -0
- package/dist/{ai-conversation-Cc7WlaBg.js → ai-conversation-Qslfdi1t.js} +28 -42
- package/dist/ai-conversation-Qslfdi1t.js.map +1 -0
- package/dist/{ai-info-banner-C7EWPBj7.js → ai-info-banner-B_9vtGK3.js} +3 -3
- package/dist/{ai-info-banner-C7EWPBj7.js.map → ai-info-banner-B_9vtGK3.js.map} +1 -1
- package/dist/{ai-message-Bp7L68U_.js → ai-message-Ci3gwM7G.js} +6 -6
- package/dist/{ai-message-Bp7L68U_.js.map → ai-message-Ci3gwM7G.js.map} +1 -1
- package/dist/{ai-mission-header-TiCJfTNt.js → ai-mission-header-CaBc19-t.js} +2 -2
- package/dist/{ai-mission-header-TiCJfTNt.js.map → ai-mission-header-CaBc19-t.js.map} +1 -1
- package/dist/{ai-part-group-DNb9I446.js → ai-part-group-Dx1Mr92B.js} +5 -4
- package/dist/ai-part-group-Dx1Mr92B.js.map +1 -0
- package/dist/{ai-prompt-input-BVvov_KF.js → ai-prompt-input-Bm4XoSj2.js} +19 -17
- package/dist/ai-prompt-input-Bm4XoSj2.js.map +1 -0
- package/dist/{ai-question-GPPMk7YM.js → ai-question-OyJovxGe.js} +4 -3
- package/dist/{ai-question-GPPMk7YM.js.map → ai-question-OyJovxGe.js.map} +1 -1
- package/dist/{ai-reasoning-_feFjk56.js → ai-reasoning-BLfBXx3F.js} +9 -5
- package/dist/ai-reasoning-BLfBXx3F.js.map +1 -0
- package/dist/{ai-response-CvjV3WhV.js → ai-response-hbVCZJmo.js} +2 -2
- package/dist/{ai-response-CvjV3WhV.js.map → ai-response-hbVCZJmo.js.map} +1 -1
- package/dist/{ai-shimmer-j6lKIrjj.js → ai-shimmer-BamNMNK3.js} +2 -2
- package/dist/{ai-shimmer-j6lKIrjj.js.map → ai-shimmer-BamNMNK3.js.map} +1 -1
- package/dist/{ai-status-badge-CSU_QOdz.js → ai-status-badge-BZLczdkI.js} +2 -2
- package/dist/{ai-status-badge-CSU_QOdz.js.map → ai-status-badge-BZLczdkI.js.map} +1 -1
- package/dist/{ai-streaming-text-IWW1BhvZ.js → ai-streaming-text-DgYu64UH.js} +1 -1
- package/dist/{ai-streaming-text-IWW1BhvZ.js.map → ai-streaming-text-DgYu64UH.js.map} +1 -1
- package/dist/{ai-subagent-JA4iIMW3.js → ai-subagent-p97AI1h9.js} +3 -3
- package/dist/{ai-subagent-JA4iIMW3.js.map → ai-subagent-p97AI1h9.js.map} +1 -1
- package/dist/{ai-suggestion-BdO6MBuH.js → ai-suggestion-Bj6vF7CT.js} +3 -3
- package/dist/{ai-suggestion-BdO6MBuH.js.map → ai-suggestion-Bj6vF7CT.js.map} +1 -1
- package/dist/{ai-task-list-DYw4R1FA.js → ai-task-list-C_UQYpk9.js} +6 -4
- package/dist/{ai-task-list-DYw4R1FA.js.map → ai-task-list-C_UQYpk9.js.map} +1 -1
- package/dist/{ai-timeline-C42tOUT8.js → ai-timeline-CePL1LOU.js} +3 -3
- package/dist/ai-timeline-CePL1LOU.js.map +1 -0
- package/dist/{ai-tool-03jOTwUI.js → ai-tool-CfRcwmHT.js} +17 -11
- package/dist/ai-tool-CfRcwmHT.js.map +1 -0
- package/dist/{ai-usage-bar-BRf5LC_b.js → ai-usage-bar-45pVRCGA.js} +2 -2
- package/dist/{ai-usage-bar-BRf5LC_b.js.map → ai-usage-bar-45pVRCGA.js.map} +1 -1
- package/dist/{badge-BheXjMc8.js → badge-Beb-6uut.js} +5 -5
- package/dist/{badge-BheXjMc8.js.map → badge-Beb-6uut.js.map} +1 -1
- package/dist/{banner-CcsjunJg.js → banner-CCEksxPg.js} +3 -3
- package/dist/{banner-CcsjunJg.js.map → banner-CCEksxPg.js.map} +1 -1
- package/dist/{breadcrumbs-CouSyy3H.js → breadcrumbs-HiTmgaZ4.js} +5 -5
- package/dist/{breadcrumbs-CouSyy3H.js.map → breadcrumbs-HiTmgaZ4.js.map} +1 -1
- package/dist/{button-CO6-qPax.js → button-BHOgXJRU.js} +4 -4
- package/dist/{button-CO6-qPax.js.map → button-BHOgXJRU.js.map} +1 -1
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/{chart-Dg0qUeSc.js → chart-B9FfZdKs.js} +7 -7
- package/dist/chart-B9FfZdKs.js.map +1 -0
- package/dist/{checkbox-D7p4QKsC.js → checkbox-Cy_OCyay.js} +3 -3
- package/dist/{checkbox-D7p4QKsC.js.map → checkbox-Cy_OCyay.js.map} +1 -1
- package/dist/{clipboard-text-kLaMogs3.js → clipboard-text-CKSvNp9L.js} +6 -5
- package/dist/clipboard-text-CKSvNp9L.js.map +1 -0
- package/dist/{cn-YROP2_ox.js → cn-CmAOpn49.js} +2 -2
- package/dist/{cn-YROP2_ox.js.map → cn-CmAOpn49.js.map} +1 -1
- package/dist/{code-BN8InC0G.js → code-JsQz-0G_.js} +4 -4
- package/dist/{code-BN8InC0G.js.map → code-JsQz-0G_.js.map} +1 -1
- package/dist/{collapsible-D_ueZ0jz.js → collapsible-1kOZ-89L.js} +2 -2
- package/dist/{collapsible-D_ueZ0jz.js.map → collapsible-1kOZ-89L.js.map} +1 -1
- package/dist/{combobox-B7TOK0U2.js → combobox-CQwDmqgA.js} +4 -4
- package/dist/{combobox-B7TOK0U2.js.map → combobox-CQwDmqgA.js.map} +1 -1
- package/dist/command-line/cli.js +3 -3
- package/dist/{command-palette-CuNUyJca.js → command-palette-Bkuv3e6o.js} +20 -5
- package/dist/command-palette-Bkuv3e6o.js.map +1 -0
- package/dist/components/ai-actions.js +1 -1
- package/dist/components/ai-agent-card.js +1 -1
- package/dist/components/ai-approval.js +1 -1
- package/dist/components/ai-code-block.js +1 -1
- package/dist/components/ai-conversation.js +1 -1
- package/dist/components/ai-info-banner.js +1 -1
- package/dist/components/ai-message.js +1 -1
- package/dist/components/ai-mission-header.js +1 -1
- package/dist/components/ai-part-group.js +1 -1
- package/dist/components/ai-prompt-input.js +1 -1
- package/dist/components/ai-question.js +1 -1
- package/dist/components/ai-reasoning.js +1 -1
- package/dist/components/ai-response.js +1 -1
- package/dist/components/ai-shimmer.js +1 -1
- package/dist/components/ai-status-badge.js +1 -1
- package/dist/components/ai-streaming-text.js +1 -1
- package/dist/components/ai-subagent.js +1 -1
- package/dist/components/ai-suggestion.js +1 -1
- package/dist/components/ai-task-list.js +1 -1
- package/dist/components/ai-timeline.js +1 -1
- package/dist/components/ai-tool.js +1 -1
- package/dist/components/ai-usage-bar.js +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/banner.js +1 -1
- package/dist/components/breadcrumbs.js +1 -1
- package/dist/components/button.js +1 -1
- package/dist/components/chart.js +2 -2
- package/dist/components/checkbox.js +1 -1
- package/dist/components/clipboard-text.js +1 -1
- package/dist/components/code.js +1 -1
- package/dist/components/collapsible.js +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/command-palette.js +1 -1
- package/dist/components/data-grid.js +1 -1
- package/dist/components/date-picker.js +1 -1
- package/dist/components/date-range-picker.js +1 -1
- package/dist/components/dialog.js +1 -1
- package/dist/components/dropdown.js +1 -1
- package/dist/components/empty.js +1 -1
- package/dist/components/field.js +1 -1
- package/dist/components/filters.js +1 -1
- package/dist/components/flow.js +1 -1
- package/dist/components/grid.js +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/label.js +1 -1
- package/dist/components/layer-card.js +1 -1
- package/dist/components/link.js +3 -3
- package/dist/components/link.js.map +1 -1
- package/dist/components/loader.js +2 -2
- package/dist/components/menubar.js +1 -1
- package/dist/components/meter.js +1 -1
- package/dist/components/pagination.js +1 -1
- package/dist/components/popover.js +1 -1
- package/dist/components/radio.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/sensitive-input.js +1 -1
- package/dist/components/sidebar.js +1 -1
- package/dist/components/signalflare-ai-logo.js +1 -1
- package/dist/components/sparkline.js +1 -1
- package/dist/components/stat-card.js +1 -1
- package/dist/components/surface.js +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/table.js +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/text-roll.js +1 -1
- package/dist/components/text.js +1 -1
- package/dist/components/theme-toggle.js +1 -1
- package/dist/components/toast.js +1 -1
- package/dist/components/tooltip.js +1 -1
- package/dist/components/use-agent-harness.js +1 -1
- package/dist/{data-grid-DGHmU0w3.js → data-grid-DDSFMHud.js} +136 -53
- package/dist/data-grid-DDSFMHud.js.map +1 -0
- package/dist/{date-picker--ox89RBy.js → date-picker-O34AqG3f.js} +2 -2
- package/dist/{date-picker--ox89RBy.js.map → date-picker-O34AqG3f.js.map} +1 -1
- package/dist/{date-range-picker-DVa7QBqE.js → date-range-picker-YKYvum_r.js} +29 -39
- package/dist/{date-range-picker-DVa7QBqE.js.map → date-range-picker-YKYvum_r.js.map} +1 -1
- package/dist/{dialog-Bv1oSFOd.js → dialog-DYqu4aDO.js} +3 -3
- package/dist/{dialog-Bv1oSFOd.js.map → dialog-DYqu4aDO.js.map} +1 -1
- package/dist/{dist-B6iWiWwp.js → dist-6AtBsaJE.js} +153 -47
- package/dist/dist-6AtBsaJE.js.map +1 -0
- package/dist/{dropdown-B_nrGXjV.js → dropdown-XzbnRLYR.js} +15 -5
- package/dist/dropdown-XzbnRLYR.js.map +1 -0
- package/dist/{echart-CdOUaT-r.js → echart-DGBIVAv1.js} +23 -57
- package/dist/{echart-CdOUaT-r.js.map → echart-DGBIVAv1.js.map} +1 -1
- package/dist/{empty-DZnN0zKX.js → empty-C1tAkawe.js} +6 -6
- package/dist/{empty-DZnN0zKX.js.map → empty-C1tAkawe.js.map} +1 -1
- package/dist/{field-B_yVof52.js → field-DBpFzzBS.js} +3 -3
- package/dist/{field-B_yVof52.js.map → field-DBpFzzBS.js.map} +1 -1
- package/dist/{filters-cpJCY21R.js → filters-SmEl93za.js} +10 -10
- package/dist/filters-SmEl93za.js.map +1 -0
- package/dist/{flow-B4v198ot.js → flow-BLzgbq1T.js} +6 -6
- package/dist/flow-BLzgbq1T.js.map +1 -0
- package/dist/genui.js +2 -2
- package/dist/genui.js.map +1 -1
- package/dist/{grid-CEd64Lnh.js → grid-CifjQL-5.js} +2 -2
- package/dist/{grid-CEd64Lnh.js.map → grid-CifjQL-5.js.map} +1 -1
- package/dist/{highlight-to-react-D0Yav4jk.js → highlight-to-react-DN9dUCS2.js} +9 -15
- package/dist/highlight-to-react-DN9dUCS2.js.map +1 -0
- package/dist/index.js +71 -71
- package/dist/index.js.map +1 -1
- package/dist/{input-ClB_E4Lb.js → input-COmx2M_R.js} +5 -5
- package/dist/{input-ClB_E4Lb.js.map → input-COmx2M_R.js.map} +1 -1
- package/dist/{input-B2bbijRh.js → input-GkfMQZC_.js} +3 -3
- package/dist/{input-B2bbijRh.js.map → input-GkfMQZC_.js.map} +1 -1
- package/dist/{label-DUv_urO1.js → label-CiGZ464N.js} +3 -3
- package/dist/{label-DUv_urO1.js.map → label-CiGZ464N.js.map} +1 -1
- package/dist/{layer-card-BK7eYfwn.js → layer-card-8l8GuLQr.js} +2 -2
- package/dist/{layer-card-BK7eYfwn.js.map → layer-card-8l8GuLQr.js.map} +1 -1
- package/dist/{layout-DJHMMap2.js → layout-CWBE0qwx.js} +258 -154
- package/dist/layout-CWBE0qwx.js.map +1 -0
- package/dist/{link-provider-BUZKXaNE.js → link-provider-BSn8YJon.js} +2 -2
- package/dist/link-provider-BSn8YJon.js.map +1 -0
- package/dist/{loader-DAcc-Uag.js → loader-BEMz8pJO.js} +1 -1
- package/dist/{loader-DAcc-Uag.js.map → loader-BEMz8pJO.js.map} +1 -1
- package/dist/{measured-text-BI3dTJmH.js → measured-text-CXkdw9Yr.js} +45 -30
- package/dist/measured-text-CXkdw9Yr.js.map +1 -0
- package/dist/{menubar-Cxf3xeAt.js → menubar-CoOr4ocj.js} +3 -3
- package/dist/{menubar-Cxf3xeAt.js.map → menubar-CoOr4ocj.js.map} +1 -1
- package/dist/{meter-BFFe9l5b.js → meter-Pf_VOl59.js} +2 -2
- package/dist/{meter-BFFe9l5b.js.map → meter-Pf_VOl59.js.map} +1 -1
- package/dist/{pagination-yS372Tr4.js → pagination-DSY279Ta.js} +2 -2
- package/dist/{pagination-yS372Tr4.js.map → pagination-DSY279Ta.js.map} +1 -1
- package/dist/{popover-SRoJaCZr.js → popover-BY-e9co1.js} +2 -2
- package/dist/{popover-SRoJaCZr.js.map → popover-BY-e9co1.js.map} +1 -1
- package/dist/{radio-BcwhwYNB.js → radio-DZwL13j0.js} +2 -2
- package/dist/{radio-BcwhwYNB.js.map → radio-DZwL13j0.js.map} +1 -1
- package/dist/{select-DMhdoHMa.js → select-BFifYqHA.js} +6 -6
- package/dist/{select-DMhdoHMa.js.map → select-BFifYqHA.js.map} +1 -1
- package/dist/{sensitive-input-CJUpIRal.js → sensitive-input-DHLZcM73.js} +4 -4
- package/dist/{sensitive-input-CJUpIRal.js.map → sensitive-input-DHLZcM73.js.map} +1 -1
- package/dist/{sidebar-D4zrlYpn.js → sidebar-odGsdvG4.js} +6 -7
- package/dist/sidebar-odGsdvG4.js.map +1 -0
- package/dist/{signalflare-ai-logo-Bipogceq.js → signalflare-ai-logo-CNaDT_w8.js} +2 -2
- package/dist/{signalflare-ai-logo-Bipogceq.js.map → signalflare-ai-logo-CNaDT_w8.js.map} +1 -1
- package/dist/{skeleton-line-CH1-h6e2.js → skeleton-line-CxxYVTO2.js} +2 -2
- package/dist/{skeleton-line-CH1-h6e2.js.map → skeleton-line-CxxYVTO2.js.map} +1 -1
- package/dist/{sparkline-DHmgj1d0.js → sparkline-BQ-4j2W2.js} +2 -2
- package/dist/{sparkline-DHmgj1d0.js.map → sparkline-BQ-4j2W2.js.map} +1 -1
- package/dist/src/blocks/agent-harness/agent-harness.tsx +11 -11
- package/dist/src/blocks/commander/commander.tsx +15 -15
- package/dist/src/blocks/map-block/map-block.d.ts.map +1 -1
- package/dist/src/blocks/map-block/map-block.tsx +11 -7
- package/dist/src/components/ai-approval/ai-approval.d.ts.map +1 -1
- package/dist/src/components/ai-code-block/ai-code-block.d.ts +14 -13
- package/dist/src/components/ai-code-block/ai-code-block.d.ts.map +1 -1
- package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
- package/dist/src/components/ai-part-group/ai-part-group.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
- package/dist/src/components/ai-question/ai-question.d.ts.map +1 -1
- package/dist/src/components/ai-reasoning/ai-reasoning.d.ts.map +1 -1
- package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
- package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
- package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
- package/dist/src/components/chart/echart.d.ts.map +1 -1
- package/dist/src/components/clipboard-text/clipboard-text.d.ts.map +1 -1
- package/dist/src/components/data-grid/data-grid.d.ts +2 -1
- package/dist/src/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/src/components/data-grid/features.d.ts +20 -0
- package/dist/src/components/data-grid/features.d.ts.map +1 -0
- package/dist/src/components/data-grid/types.d.ts +38 -7
- package/dist/src/components/data-grid/types.d.ts.map +1 -1
- package/dist/src/components/filters/filters.d.ts.map +1 -1
- package/dist/src/components/flow/use-children.d.ts +1 -1
- package/dist/src/components/link/link.d.ts.map +1 -1
- package/dist/src/components/sidebar/sidebar.d.ts +1 -1
- package/dist/src/components/signalflare-ai-logo/signalflare-ai-logo.d.ts.map +1 -1
- package/dist/src/components/text/text.d.ts +2 -1
- package/dist/src/components/text/text.d.ts.map +1 -1
- package/dist/src/components/text-roll/text-roll.d.ts.map +1 -1
- package/dist/src/components/theme-toggle/theme-toggle.d.ts.map +1 -1
- package/dist/src/components/toast/toast.d.ts.map +1 -1
- package/dist/src/utils/highlight-to-react.d.ts.map +1 -1
- package/dist/src/utils/measured-text.d.ts.map +1 -1
- package/dist/src/utils/use-measured-text.d.ts.map +1 -1
- package/dist/{stat-card-Ew-ofzEm.js → stat-card-Bspk4XFr.js} +4 -4
- package/dist/stat-card-Bspk4XFr.js.map +1 -0
- package/dist/styles/sf-standalone.css +1 -1
- package/dist/styles/theme-fedramp.css +3 -12
- package/dist/styles/theme-minimal.css +26 -104
- package/dist/styles/theme-sf.css +37 -142
- package/dist/{surface-DGwRlC0o.js → surface-CWdSFVUx.js} +3 -3
- package/dist/{surface-DGwRlC0o.js.map → surface-CWdSFVUx.js.map} +1 -1
- package/dist/{switch-BxAMfHdt.js → switch-TA4cByCJ.js} +5 -5
- package/dist/switch-TA4cByCJ.js.map +1 -0
- package/dist/{table-BBeAtYVZ.js → table-BM8JBGBs.js} +3 -3
- package/dist/{table-BBeAtYVZ.js.map → table-BM8JBGBs.js.map} +1 -1
- package/dist/{tabs-CeHu7Scn.js → tabs-bnH2vGLv.js} +2 -2
- package/dist/{tabs-CeHu7Scn.js.map → tabs-bnH2vGLv.js.map} +1 -1
- package/dist/{text-Cqryz7rk.js → text-iQ0YUFNg.js} +4 -5
- package/dist/{text-Cqryz7rk.js.map → text-iQ0YUFNg.js.map} +1 -1
- package/dist/{text-roll-Ch52hcQj.js → text-roll-C3U2jd2u.js} +5 -2
- package/dist/text-roll-C3U2jd2u.js.map +1 -0
- package/dist/{theme-toggle-LDfIKEqx.js → theme-toggle-BTVxD-fD.js} +10 -9
- package/dist/theme-toggle-BTVxD-fD.js.map +1 -0
- package/dist/{toast-CaFQNYng.js → toast-CgZVaAkw.js} +3 -3
- package/dist/{toast-CaFQNYng.js.map → toast-CgZVaAkw.js.map} +1 -1
- package/dist/{tooltip-g9lFsvcT.js → tooltip-uobk6Oh-.js} +3 -3
- package/dist/{tooltip-g9lFsvcT.js.map → tooltip-uobk6Oh-.js.map} +1 -1
- package/dist/{use-agent-harness-BTcNJdw4.js → use-agent-harness-Dl8w6X5O.js} +3 -3
- package/dist/{use-agent-harness-BTcNJdw4.js.map → use-agent-harness-Dl8w6X5O.js.map} +1 -1
- package/dist/utils.js +3 -3
- package/package.json +27 -25
- package/scripts/component-registry/discovery.ts +11 -10
- package/scripts/component-registry/example-cleanup.ts +8 -8
- package/scripts/component-registry/index.ts +6 -6
- package/scripts/component-registry/schema-generator.ts +1 -1
- package/scripts/component-registry/sub-components.ts +35 -23
- package/scripts/component-registry/utils.ts +11 -11
- package/scripts/component-registry/variant-parser.ts +17 -15
- package/scripts/convert-demos-to-stories.ts +5 -5
- package/scripts/theme-generator/config.ts +1 -5
- package/scripts/theme-generator/generate-css.ts +1 -1
- package/scripts/theme-generator/migrate.ts +3 -3
- package/dist/ai-agent-card-BR2NIYhi.js.map +0 -1
- package/dist/ai-code-block-CZtoL73R.js.map +0 -1
- package/dist/ai-conversation-Cc7WlaBg.js.map +0 -1
- package/dist/ai-part-group-DNb9I446.js.map +0 -1
- package/dist/ai-prompt-input-BVvov_KF.js.map +0 -1
- package/dist/ai-reasoning-_feFjk56.js.map +0 -1
- package/dist/ai-timeline-C42tOUT8.js.map +0 -1
- package/dist/ai-tool-03jOTwUI.js.map +0 -1
- package/dist/chart-Dg0qUeSc.js.map +0 -1
- package/dist/clipboard-text-kLaMogs3.js.map +0 -1
- package/dist/command-palette-CuNUyJca.js.map +0 -1
- package/dist/data-grid-DGHmU0w3.js.map +0 -1
- package/dist/dist-B6iWiWwp.js.map +0 -1
- package/dist/dropdown-B_nrGXjV.js.map +0 -1
- package/dist/filters-cpJCY21R.js.map +0 -1
- package/dist/flow-B4v198ot.js.map +0 -1
- package/dist/highlight-to-react-D0Yav4jk.js.map +0 -1
- package/dist/layout-DJHMMap2.js.map +0 -1
- package/dist/link-provider-BUZKXaNE.js.map +0 -1
- package/dist/measured-text-BI3dTJmH.js.map +0 -1
- package/dist/sidebar-D4zrlYpn.js.map +0 -1
- package/dist/stat-card-Ew-ofzEm.js.map +0 -1
- package/dist/switch-BxAMfHdt.js.map +0 -1
- package/dist/text-roll-Ch52hcQj.js.map +0 -1
- package/dist/theme-toggle-LDfIKEqx.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ai-tool-03jOTwUI.js","names":[],"sources":["../src/components/ai-tool/ai-tool.tsx"],"sourcesContent":["\"use client\";\n\nimport { Collapsible as BaseCollapsible } from \"@base-ui/react/collapsible\";\nimport {\n CaretDownIcon,\n CheckIcon,\n CircleIcon,\n CodeIcon,\n DatabaseIcon,\n FileTextIcon,\n GearIcon,\n GlobeIcon,\n ImageIcon,\n LightningIcon,\n MagnifyingGlassIcon,\n ChatCircleIcon,\n SpinnerGapIcon,\n TerminalIcon,\n WarningCircleIcon,\n XCircleIcon,\n ShieldWarningIcon,\n BrainIcon,\n} from \"@phosphor-icons/react\";\nimport type { ComponentProps, ElementType } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { AiStatusBadge, type AiStatusBadgeStatus } from \"../ai-status-badge\";\nimport { Button } from \"../button\";\nimport { Text } from \"../text\";\nimport { Tooltip } from \"../tooltip\";\n\n// ─── Variants ────────────────────────────────────────────────────────────────\n\nexport const SF_AI_TOOL_VARIANTS = {\n variant: {\n default: {\n classes: \"\",\n description: \"Expandable card with collapsible input/output sections\",\n },\n inline: {\n classes: \"\",\n description: \"Compact single-line display with left accent border\",\n },\n minimal: {\n classes: \"\",\n description: \"Pill-shaped status badge\",\n },\n ephemeral: {\n classes: \"\",\n description:\n \"Single-status row that auto-dismisses after the call completes. Persists on error or when the user expands it.\",\n },\n },\n} as const;\n\nexport const SF_AI_TOOL_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\n/** Tool execution state — matches Vercel AI SDK ToolUIPart[\"state\"]. */\nexport type AiToolState =\n | \"input-streaming\"\n | \"input-available\"\n | \"approval-requested\"\n | \"approval-responded\"\n | \"output-available\"\n | \"output-error\"\n | \"output-denied\";\n\n/** Display variant for tool call rendering. */\nexport type AiToolCallVariant = keyof typeof SF_AI_TOOL_VARIANTS.variant;\n\n/** Approval state for tools requiring user confirmation. */\nexport type AiToolApprovalState =\n | \"awaiting\"\n | \"approved\"\n | \"declined\"\n | undefined;\n\n/**\n * Structured tool part — mirrors the shape of Vercel AI SDK's `ToolUIPart`\n * without requiring the `ai` package as a dependency.\n */\nexport interface AiToolPart {\n /** Tool call identifier. */\n toolCallId: string;\n /** Tool name or type identifier. */\n toolName: string;\n /** Current execution state. */\n state: AiToolState;\n /** Tool input parameters. */\n input?: unknown;\n /** Tool output result. */\n output?: unknown;\n /** Error text when state is \"output-error\". */\n errorText?: string;\n}\n\nexport type AiToolCallProps = Omit<ComponentProps<\"div\">, \"part\"> & {\n /** Structured tool part data. */\n part: AiToolPart;\n /** Display variant. @default \"default\" */\n variant?: AiToolCallVariant;\n /** Duration in milliseconds. */\n duration?: number;\n /** Summary text shown alongside the tool name. */\n summary?: string;\n /** Custom icon override. */\n icon?: ElementType;\n /** Approval state for tools requiring confirmation. */\n approvalState?: AiToolApprovalState;\n /** Called when user approves a tool call (only relevant when `approvalState=\"awaiting\"`). */\n onApprove?: () => void;\n /** Called when user rejects a tool call (only relevant when `approvalState=\"awaiting\"`). */\n onReject?: () => void;\n /** Default expanded state for the collapsible. @default false */\n defaultExpanded?: boolean;\n /**\n * For `variant=\"ephemeral\"`: ms to keep the completed row visible before\n * fading out. Set to `0` to disable auto-dismiss. @default 800\n */\n dismissDelay?: number;\n /**\n * For `variant=\"ephemeral\"`: when `true`, the row never auto-dismisses.\n * Errors and user-expanded rows are pinned automatically.\n */\n persist?: boolean;\n /** Called once the ephemeral row has finished its dismiss animation. */\n onDismiss?: () => void;\n};\n\nexport type AiToolCallGroupProps = Omit<ComponentProps<\"div\">, \"part\"> & {\n /** Array of tool parts. */\n parts: AiToolPart[];\n /** Group label. */\n label?: string;\n /** Duration map keyed by toolCallId. */\n durations?: Record<string, number>;\n /** Summary map keyed by toolCallId. */\n summaries?: Record<string, string>;\n /** Approval states map keyed by toolCallId. */\n approvalStates?: Record<string, AiToolApprovalState>;\n /** Called when user approves a tool call. Receives the `toolCallId`. */\n onApprove?: (toolCallId: string) => void;\n /** Called when user rejects a tool call. Receives the `toolCallId`. */\n onReject?: (toolCallId: string) => void;\n /** Default expanded state. @default false */\n defaultExpanded?: boolean;\n};\n\nexport type AiToolCallTimelineProps = ComponentProps<\"div\"> & {\n /** Array of tool parts. */\n parts: AiToolPart[];\n /** Duration map keyed by toolCallId. */\n durations?: Record<string, number>;\n};\n\n// ─── Icon Map ────────────────────────────────────────────────────────────────\n\nconst TOOL_ICONS: Record<string, ElementType> = {\n generateSuggestions: BrainIcon,\n searchDatabase: DatabaseIcon,\n searchWeb: GlobeIcon,\n search: MagnifyingGlassIcon,\n readFile: FileTextIcon,\n writeFile: FileTextIcon,\n executeQuery: LightningIcon,\n analyzeContent: BrainIcon,\n generateImage: ImageIcon,\n chat: ChatCircleIcon,\n runCode: TerminalIcon,\n settings: GearIcon,\n code: CodeIcon,\n};\n\n// ─── Utilities ───────────────────────────────────────────────────────────────\n\n/** Extract the display-friendly tool name from a tool part. */\nexport function getToolDisplayName(part: AiToolPart): string {\n return part.toolName\n .replace(\"tool-\", \"\")\n .replace(\"Tool\", \"\")\n .replace(/([A-Z])/g, \" $1\")\n .replace(/_/g, \" \")\n .replace(/^./, (s) => s.toUpperCase())\n .trim();\n}\n\n/** Get the best-matching icon for a tool part. */\nexport function getToolIcon(part: AiToolPart): ElementType {\n if (TOOL_ICONS[part.toolName]) return TOOL_ICONS[part.toolName];\n\n const lower = part.toolName.toLowerCase();\n for (const [key, icon] of Object.entries(TOOL_ICONS)) {\n if (lower.includes(key.toLowerCase())) return icon;\n }\n\n return MagnifyingGlassIcon;\n}\n\n/** Whether the tool is still loading (input phase). */\nexport function isToolLoading(part: AiToolPart): boolean {\n return part.state === \"input-streaming\" || part.state === \"input-available\";\n}\n\n/** Whether the tool has produced a result. */\nexport function hasToolResult(part: AiToolPart): boolean {\n return part.state === \"output-available\";\n}\n\n/** Whether the output itself contains an error flag. */\nexport function hasToolOutputError(part: AiToolPart): boolean {\n if (part.state !== \"output-available\") return false;\n const output = part.output as Record<string, unknown> | undefined;\n return output?.error === true;\n}\n\n/** Whether the tool is in any error state. */\nexport function hasToolError(part: AiToolPart): boolean {\n return part.state === \"output-error\" || hasToolOutputError(part);\n}\n\n/** Extract error text from either error state or output.error. */\nexport function getToolErrorText(part: AiToolPart): string | undefined {\n if (part.state === \"output-error\") return part.errorText;\n if (hasToolOutputError(part)) {\n const output = part.output as Record<string, unknown>;\n return (\n (output.message as string) ||\n (output.error as string) ||\n \"Tool returned an error\"\n );\n }\n return undefined;\n}\n\n// ─── State Config ────────────────────────────────────────────────────────────\n\ninterface StateConfig {\n colorClass: string;\n bgClass: string;\n icon: ElementType;\n spin: boolean;\n label: string;\n /** Solid dot color for the minimalist status indicator (ephemeral variant). */\n dotClass: string;\n /** Whether the dot should pulse (running / streaming states). */\n dotPulse: boolean;\n}\n\nfunction getStateConfig(\n part: AiToolPart,\n approvalState?: AiToolApprovalState\n): StateConfig {\n if (approvalState === \"awaiting\") {\n return {\n colorClass: \"text-sf-warning\",\n bgClass: \"bg-sf-warning/10\",\n icon: ShieldWarningIcon,\n spin: false,\n label: \"Awaiting Approval\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n }\n\n if (approvalState === \"declined\") {\n return {\n colorClass: \"text-sf-danger\",\n bgClass: \"bg-sf-danger/10\",\n icon: XCircleIcon,\n spin: false,\n label: \"Declined\",\n dotClass: \"bg-sf-danger\",\n dotPulse: false,\n };\n }\n\n if (hasToolError(part)) {\n return {\n colorClass: \"text-sf-danger\",\n bgClass: \"bg-sf-danger/10\",\n icon: WarningCircleIcon,\n spin: false,\n label: \"Failed\",\n dotClass: \"bg-sf-danger\",\n dotPulse: false,\n };\n }\n\n switch (part.state) {\n case \"input-streaming\":\n return {\n colorClass: \"text-sf-brand\",\n bgClass: \"bg-sf-brand/10\",\n icon: CircleIcon,\n spin: false,\n label: \"Streaming\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n case \"input-available\":\n return {\n colorClass: \"text-sf-brand\",\n bgClass: \"bg-sf-brand/10\",\n icon: SpinnerGapIcon,\n spin: true,\n label: \"Running\",\n dotClass: \"bg-sf-warning\",\n dotPulse: true,\n };\n case \"output-available\":\n return {\n colorClass: \"text-sf-success\",\n bgClass: \"bg-sf-tint\",\n icon: CheckIcon,\n spin: false,\n label: \"Complete\",\n dotClass: \"bg-sf-success\",\n dotPulse: false,\n };\n default:\n return {\n colorClass: \"text-sf-subtle\",\n bgClass: \"bg-sf-tint\",\n icon: CircleIcon,\n spin: false,\n label: \"Pending\",\n dotClass: \"bg-sf-subtle/40\",\n dotPulse: false,\n };\n }\n}\n\n// ─── AiToolCall ──────────────────────────────────────────────────────────────\n\n/**\n * Renders a single tool call. Supports three display variants:\n * - `\"default\"` — expandable card with collapsible input/output\n * - `\"inline\"` — compact single-line with left accent border\n * - `\"minimal\"` — pill-shaped status badge\n *\n * @example\n * ```tsx\n * <AiToolCall\n * part={{ toolCallId: \"1\", toolName: \"web_search\", state: \"output-available\", input: { q: \"ts\" }, output: \"...\" }}\n * />\n * ```\n */\nexport function AiToolCall({\n part,\n variant = \"default\",\n duration,\n summary,\n icon: CustomIcon,\n approvalState,\n onApprove,\n onReject,\n defaultExpanded = false,\n dismissDelay = 800,\n persist = false,\n onDismiss,\n className,\n ...props\n}: AiToolCallProps) {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const IconComponent = CustomIcon ?? getToolIcon(part);\n const displayName = getToolDisplayName(part);\n const config = getStateConfig(part, approvalState);\n const StatusIconEl = config.icon;\n const errorText = getToolErrorText(part);\n\n // ── Ephemeral ──────────────────────────────────────────────────────────────\n\n if (variant === \"ephemeral\") {\n return (\n <AiToolCallEphemeral\n part={part}\n approvalState={approvalState}\n duration={duration}\n summary={summary}\n icon={IconComponent}\n displayName={displayName}\n config={config}\n errorText={errorText}\n dismissDelay={dismissDelay}\n persist={persist}\n onDismiss={onDismiss}\n onApprove={onApprove}\n onReject={onReject}\n className={className}\n {...props}\n />\n );\n }\n\n // ── Minimal ────────────────────────────────────────────────────────────────\n\n if (variant === \"minimal\") {\n const handleGetToolStatus = (): AiStatusBadgeStatus => {\n if (hasToolError(part)) return \"error\";\n if (approvalState === \"awaiting\") return \"idle\";\n if (isToolLoading(part)) return \"running\";\n if (hasToolResult(part)) return \"success\";\n return \"idle\";\n };\n\n return (\n <AiStatusBadge\n className={cn(\n \"my-1.5 py-1\",\n hasToolError(part) && \"bg-sf-danger/10\",\n className\n )}\n icon={IconComponent}\n label={displayName}\n status={handleGetToolStatus()}\n {...props}\n />\n );\n }\n\n // ── Inline ─────────────────────────────────────────────────────────────────\n\n if (variant === \"inline\") {\n return (\n <div\n className={cn(\n \"my-1 flex w-full items-center gap-2 py-1.5 pl-1\",\n \"animate-in fade-in-0 slide-in-from-bottom-1 duration-200\",\n className\n )}\n {...props}\n >\n <div className={cn(\"shrink-0 rounded-md p-1\", config.bgClass)}>\n <IconComponent className={cn(\"size-3.5\", config.colorClass)} />\n </div>\n <Text\n size=\"sm\"\n variant={hasToolError(part) ? \"error\" : \"secondary\"}\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className=\"min-w-0 grow truncate\"\n >\n {displayName}\n </Text>\n {/* Right-aligned meta group */}\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {duration && hasToolResult(part) ? (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {duration}ms\n </span>\n ) : null}\n {summary && !hasToolError(part) ? (\n <Text\n size=\"xs\"\n variant=\"secondary\"\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className=\"max-w-[160px] truncate\"\n >\n {summary}\n </Text>\n ) : null}\n {errorText ? (\n <Text\n size=\"xs\"\n variant=\"error\"\n wrap=\"shrink\"\n as=\"span\"\n DANGEROUS_className=\"max-w-[160px] truncate\"\n title={errorText}\n >\n {errorText}\n </Text>\n ) : null}\n {approvalState === \"awaiting\" && (onApprove || onReject) ? (\n <>\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </>\n ) : (\n <StatusIconEl\n className={cn(\n \"size-3.5\",\n config.colorClass,\n config.spin && \"animate-spin\"\n )}\n />\n )}\n </div>\n </div>\n );\n }\n\n // ── Default (compact expandable row) ─────────────────────────────────────\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={setIsExpanded}>\n <div\n className={cn(\n \"my-0.5 flex w-full max-w-sm flex-col\",\n \"animate-in fade-in-0 duration-150\",\n className\n )}\n {...props}\n >\n {/* Trigger — content-width: name + meta + caret */}\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n className={cn(\n \"flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left\",\n \"transition-colors hover:bg-sf-tint\"\n )}\n />\n }\n >\n <span className=\"min-w-0 truncate text-sm text-sf-subtle\">\n {displayName}\n </span>\n {/* Meta group — part of the content-width trigger */}\n <div className=\"flex shrink-0 items-center gap-1.5\">\n {duration && hasToolResult(part) && !isExpanded ? (\n <span className=\"text-xs tabular-nums text-sf-subtle\">\n {duration}ms\n </span>\n ) : null}\n {summary && !isExpanded && !errorText ? (\n <span className=\"max-w-[120px] truncate text-xs text-sf-subtle\">\n {summary}\n </span>\n ) : null}\n <StatusIconEl\n className={cn(\n \"size-3.5\",\n config.colorClass,\n config.spin && \"animate-spin\"\n )}\n />\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n {/* Detail panel */}\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"mt-1 space-y-2 rounded-lg bg-sf-tint/50 px-3 py-2\">\n {/* Input */}\n {part.input &&\n typeof part.input === \"object\" &&\n Object.keys(part.input as object).length > 0 ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Input\n </p>\n <pre className=\"overflow-x-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {JSON.stringify(\n part.input as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {/* Output */}\n {hasToolResult(part) &&\n part.output !== undefined &&\n !hasToolError(part) ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Output\n </p>\n <pre className=\"max-h-28 overflow-x-auto overflow-y-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {typeof part.output === \"string\"\n ? part.output\n : JSON.stringify(\n part.output as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {/* Error */}\n {errorText ? (\n <div>\n <p className=\"mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wider text-sf-danger\">\n <WarningCircleIcon className=\"size-3\" />\n Error\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded bg-sf-danger/10 p-1.5 font-mono text-xs text-sf-danger\">\n {errorText}\n </pre>\n </div>\n ) : null}\n\n {/* Approval actions */}\n {approvalState === \"awaiting\" && (onApprove || onReject) && (\n <div className=\"flex items-center justify-end gap-2 border-t border-sf-line/20 pt-2\">\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </div>\n )}\n\n {/* Tool call ID */}\n <p className=\"font-mono text-[10px] text-sf-subtle/50\">\n {part.toolCallId}\n </p>\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallEphemeral ─────────────────────────────────────────────────────\n\ninterface AiToolCallEphemeralInternalProps extends Omit<\n ComponentProps<\"div\">,\n \"part\"\n> {\n part: AiToolPart;\n approvalState?: AiToolApprovalState;\n duration?: number;\n summary?: string;\n icon: ElementType;\n displayName: string;\n config: StateConfig;\n errorText: string | undefined;\n dismissDelay: number;\n persist: boolean;\n onDismiss?: () => void;\n onApprove?: () => void;\n onReject?: () => void;\n}\n\n// Note: `icon` (tool icon) is accepted for API symmetry with other variants\n// but the ephemeral row leads with the status icon for at-a-glance state.\n\nconst FADE_OUT_MS = 200;\n\n/**\n * Internal renderer for `variant=\"ephemeral\"`. Shows a single-status row\n * that auto-dismisses once the tool call completes. Pins on error or when\n * the user expands the row.\n */\nfunction AiToolCallEphemeral({\n part,\n approvalState,\n duration,\n summary,\n icon: _icon,\n displayName,\n config,\n errorText,\n dismissDelay,\n persist,\n onDismiss,\n onApprove,\n onReject,\n className,\n ...props\n}: AiToolCallEphemeralInternalProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [phase, setPhase] = useState<\"live\" | \"fading\" | \"gone\">(\"live\");\n const userExpandedRef = useRef(false);\n\n const hasError = hasToolError(part);\n const isComplete = hasToolResult(part);\n const isAwaitingApproval = approvalState === \"awaiting\";\n\n // Pin the row when: errored, user expanded, persist requested, or awaiting approval.\n const pinned =\n persist || hasError || userExpandedRef.current || isAwaitingApproval;\n\n // Auto-dismiss when complete (and not pinned).\n useEffect(() => {\n if (phase !== \"live\") return;\n if (!isComplete) return;\n if (pinned) return;\n if (dismissDelay <= 0) return;\n\n const settleTimer = setTimeout(() => {\n setPhase(\"fading\");\n }, dismissDelay);\n\n return () => clearTimeout(settleTimer);\n }, [phase, isComplete, pinned, dismissDelay]);\n\n // After fade animation, unmount.\n useEffect(() => {\n if (phase !== \"fading\") return;\n const fadeTimer = setTimeout(() => {\n setPhase(\"gone\");\n onDismiss?.();\n }, FADE_OUT_MS);\n return () => clearTimeout(fadeTimer);\n }, [phase, onDismiss]);\n\n if (phase === \"gone\") return null;\n\n const handleToggle = (next: boolean) => {\n if (next) userExpandedRef.current = true;\n setIsExpanded(next);\n };\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={handleToggle}>\n <div\n className={cn(\n \"my-0.5 flex w-full max-w-sm flex-col overflow-hidden\",\n \"transition-[opacity,max-height,margin] duration-200 ease-out\",\n phase === \"live\"\n ? \"max-h-40 opacity-100\"\n : \"pointer-events-none my-0 max-h-0 opacity-0\",\n phase === \"live\" && \"animate-in fade-in-0 duration-150\",\n className\n )}\n data-phase={phase}\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n className={cn(\n \"flex w-fit items-center gap-1.5 rounded px-1 py-0.5 text-left\",\n \"transition-colors hover:bg-sf-tint\"\n )}\n />\n }\n >\n <span\n aria-label={config.label}\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n config.dotClass,\n config.dotPulse && \"animate-pulse\"\n )}\n role=\"status\"\n />\n <span\n className={cn(\n \"min-w-0 truncate text-sm\",\n hasError ? \"text-sf-danger\" : \"text-sf-subtle\"\n )}\n >\n {displayName}\n </span>\n {duration && isComplete && !isExpanded ? (\n <span className=\"text-xs tabular-nums text-sf-subtle/60\">\n {duration}ms\n </span>\n ) : null}\n {summary && !isExpanded && !errorText ? (\n <span className=\"max-w-[120px] truncate text-xs text-sf-subtle/60\">\n {summary}\n </span>\n ) : null}\n <CaretDownIcon\n className={cn(\n \"size-3 text-sf-subtle/60 transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"mt-1 space-y-2 rounded-lg bg-sf-tint/50 px-3 py-2\">\n {part.input &&\n typeof part.input === \"object\" &&\n Object.keys(part.input as object).length > 0 ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Input\n </p>\n <pre className=\"overflow-x-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {JSON.stringify(\n part.input as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {hasToolResult(part) && part.output !== undefined && !hasError ? (\n <div>\n <p className=\"mb-1 text-[10px] uppercase tracking-wider text-sf-subtle\">\n Output\n </p>\n <pre className=\"max-h-28 overflow-x-auto overflow-y-auto rounded bg-sf-recessed p-1.5 font-mono text-xs text-sf-subtle\">\n {typeof part.output === \"string\"\n ? part.output\n : JSON.stringify(\n part.output as Record<string, unknown>,\n null,\n 2\n )}\n </pre>\n </div>\n ) : null}\n\n {errorText ? (\n <div>\n <p className=\"mb-1 flex items-center gap-1 text-[10px] uppercase tracking-wider text-sf-danger\">\n <WarningCircleIcon className=\"size-3\" />\n Error\n </p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded bg-sf-danger/10 p-1.5 font-mono text-xs text-sf-danger\">\n {errorText}\n </pre>\n </div>\n ) : null}\n\n {isAwaitingApproval && (onApprove || onReject) && (\n <div className=\"flex items-center justify-end gap-2 border-t border-sf-line/20 pt-2\">\n {onReject && (\n <Button\n onClick={onReject}\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n >\n Reject\n </Button>\n )}\n {onApprove && (\n <Button\n onClick={onApprove}\n size=\"sm\"\n type=\"button\"\n variant=\"primary\"\n >\n Approve\n </Button>\n )}\n </div>\n )}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallGroup ─────────────────────────────────────────────────────────\n\n/**\n * Collapsible group of tool calls. Shows a summary header with progress\n * counter and stacked tool icons when collapsed. Renders a single tool\n * directly if only one part is provided.\n *\n * @example\n * ```tsx\n * <AiToolCallGroup parts={toolParts} label=\"3 tools ran\" />\n * ```\n */\nexport function AiToolCallGroup({\n parts,\n label,\n durations = {},\n summaries = {},\n approvalStates = {},\n onApprove,\n onReject,\n defaultExpanded = false,\n className,\n ...props\n}: AiToolCallGroupProps) {\n const [isExpanded, setIsExpanded] = useState(defaultExpanded);\n\n const completedCount = parts.filter(\n (p) => p.state === \"output-available\"\n ).length;\n const errorCount = parts.filter(hasToolError).length;\n const totalCount = parts.length;\n\n // Single item — render directly\n if (parts.length === 1 && parts[0]) {\n const singlePart = parts[0];\n // Strip `part` from div props spread to avoid collision with HTML `part` attribute\n const { part: _part, ...restProps } = props as typeof props & {\n part?: unknown;\n };\n return (\n <AiToolCall\n part={singlePart}\n duration={durations[singlePart.toolCallId]}\n summary={summaries[singlePart.toolCallId]}\n approvalState={approvalStates[singlePart.toolCallId]}\n onApprove={\n onApprove ? () => onApprove(singlePart.toolCallId) : undefined\n }\n onReject={onReject ? () => onReject(singlePart.toolCallId) : undefined}\n className={className}\n {...restProps}\n />\n );\n }\n\n return (\n <BaseCollapsible.Root open={isExpanded} onOpenChange={setIsExpanded}>\n <div\n className={cn(\n \"my-1.5 w-full overflow-hidden rounded-lg bg-sf-tint/50\",\n \"animate-in fade-in-0 slide-in-from-bottom-2 duration-200\",\n className\n )}\n {...props}\n >\n <BaseCollapsible.Trigger\n render={\n <button\n type=\"button\"\n className=\"flex w-fit items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-sf-tint\"\n />\n }\n >\n <span className=\"text-sm font-medium text-sf-subtle\">\n {label ?? `${totalCount} tool calls`}\n </span>\n <span className=\"text-xs text-sf-subtle\">\n {completedCount}/{totalCount}\n {errorCount > 0 ? (\n <span className=\"ml-1 text-sf-danger\">({errorCount} errors)</span>\n ) : null}\n </span>\n\n <div className=\"flex items-center gap-2\">\n {/* Tool icon stack when collapsed */}\n {!isExpanded ? (\n <div className=\"flex -space-x-1\">\n {parts.slice(0, 5).map((p) => {\n const Icon = getToolIcon(p);\n return (\n <div\n key={p.toolCallId}\n className={cn(\n \"flex size-6 items-center justify-center rounded-full border-2 border-sf-elevated\",\n p.state === \"output-available\" &&\n !hasToolError(p) &&\n \"bg-sf-tint\",\n isToolLoading(p) && \"bg-sf-tint\",\n hasToolError(p) && \"bg-sf-danger/20\"\n )}\n >\n <Icon className=\"size-3\" />\n </div>\n );\n })}\n {parts.length > 5 ? (\n <div className=\"flex size-6 items-center justify-center rounded-full border-2 border-sf-elevated bg-sf-tint text-[10px] text-sf-subtle\">\n +{parts.length - 5}\n </div>\n ) : null}\n </div>\n ) : null}\n <CaretDownIcon\n className={cn(\n \"size-4 text-sf-subtle transition-transform duration-200\",\n !isExpanded && \"-rotate-90\"\n )}\n />\n </div>\n </BaseCollapsible.Trigger>\n\n <BaseCollapsible.Panel className=\"overflow-hidden\">\n <div className=\"space-y-2 border-t border-sf-line/20 px-3 pt-1 pb-3\">\n {parts.map((p) => (\n <AiToolCall\n key={p.toolCallId}\n part={p}\n variant=\"inline\"\n duration={durations[p.toolCallId]}\n summary={summaries[p.toolCallId]}\n approvalState={approvalStates[p.toolCallId]}\n onApprove={\n onApprove ? () => onApprove(p.toolCallId) : undefined\n }\n onReject={onReject ? () => onReject(p.toolCallId) : undefined}\n />\n ))}\n </div>\n </BaseCollapsible.Panel>\n </div>\n </BaseCollapsible.Root>\n );\n}\n\n// ─── AiToolCallTimeline ──────────────────────────────────────────────────────\n\n/**\n * Horizontal timeline view of tool calls. Each tool is shown as a small\n * icon pill with a tooltip showing its name, duration, and any errors.\n *\n * @example\n * ```tsx\n * <AiToolCallTimeline parts={toolParts} durations={{ id1: 230, id2: 540 }} />\n * ```\n */\nexport function AiToolCallTimeline({\n parts,\n durations = {},\n className,\n ...props\n}: AiToolCallTimelineProps) {\n return (\n <div\n className={cn(\"flex flex-wrap items-center gap-1 py-2\", className)}\n {...props}\n >\n {parts.map((part, index) => {\n const IconComponent = getToolIcon(part);\n const displayName = getToolDisplayName(part);\n const config = getStateConfig(part);\n const duration = durations[part.toolCallId];\n const errorText = getToolErrorText(part);\n\n const tooltipLines = [\n displayName,\n duration ? `${duration}ms` : null,\n errorText ?? null,\n ]\n .filter(Boolean)\n .join(\" · \");\n\n return (\n <div key={part.toolCallId} className=\"flex items-center\">\n <Tooltip content={tooltipLines}>\n <div\n className={cn(\n \"flex cursor-default items-center gap-1.5 rounded-md px-2 py-1 transition-all\",\n config.bgClass\n )}\n >\n <IconComponent className={cn(\"size-3.5\", config.colorClass)} />\n {part.state === \"input-streaming\" ? (\n <CircleIcon\n className={cn(\"size-3 animate-pulse\", config.colorClass)}\n />\n ) : null}\n {part.state === \"input-available\" ? (\n <SpinnerGapIcon\n className={cn(\"size-3 animate-spin\", config.colorClass)}\n />\n ) : null}\n {hasToolResult(part) && !hasToolError(part) ? (\n <CheckIcon className={cn(\"size-3\", config.colorClass)} />\n ) : null}\n {hasToolError(part) ? (\n <WarningCircleIcon\n className={cn(\"size-3\", config.colorClass)}\n />\n ) : null}\n </div>\n </Tooltip>\n\n {/* Connector line */}\n {index < parts.length - 1 ? (\n <div\n className={cn(\n \"mx-0.5 h-px w-3\",\n (() => {\n const nextPart = parts[index + 1];\n if (!nextPart) return \"bg-sf-line\";\n return hasToolResult(nextPart) || isToolLoading(nextPart)\n ? \"bg-sf-success/50\"\n : \"bg-sf-line\";\n })()\n )}\n />\n ) : null}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chart-Dg0qUeSc.js","names":[],"sources":["../src/components/chart/timeseries-chart.tsx","../src/components/chart/legend.tsx","../src/components/chart/bar-chart.tsx","../src/components/chart/stacked-bar-chart.tsx","../src/components/chart/pie-chart.tsx","../src/components/chart/area-chart.tsx","../src/components/chart/scatter-chart.tsx"],"sourcesContent":["import type { EChartsOption } from \"echarts\";\nimport type { LineSeriesOption, BarSeriesOption } from \"echarts/charts\";\nimport type * as echarts from \"echarts/core\";\nimport { useEffect, useMemo, useRef } from \"react\";\n\nimport { ChartPalette } from \"./color\";\nimport { Chart, ChartEvents } from \"./echart\";\n\n/** A single data series rendered on a `TimeseriesChart` */\nexport interface TimeseriesData {\n /** Display name shown in tooltips and legends */\n name: string;\n /** Array of `[timestamp_ms, value]` tuples ordered by time */\n data: [number, number][];\n /** Hex color string used for this series' line, bars, and legend dot */\n color: string;\n}\n\n/** Props for `TimeseriesChart` */\nexport interface TimeseriesChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Visual style of each series. Defaults to `\"line\"`. */\n type?: \"line\" | \"bar\";\n /** Array of time series data to display on the chart */\n data: TimeseriesData[];\n /** Label for the x-axis (time axis) */\n xAxisName?: string;\n /** Number of ticks to display on the x-axis */\n xAxisTickCount?: number;\n /**\n * Custom formatter for x-axis tick labels.\n * Receives the raw timestamp in milliseconds and returns a display string,\n * overriding ECharts' built-in time formatting.\n */\n xAxisTickFormat?: (value: number) => string;\n /**\n * Custom formatter for y-axis tick labels.\n * Receives the raw value and returns a display string.\n * When omitted, ECharts' built-in formatter is used.\n */\n yAxisTickFormat?: (value: number) => string;\n /**\n * @deprecated Use `tooltipValueFormat` instead. This prop formats tooltip\n * values, not y-axis tick labels. It will be removed in a future major version.\n */\n yAxisTickLabelFormat?: (value: number) => string;\n /** Label for the y-axis (value axis) */\n yAxisName?: string;\n /** Number of ticks to display on the y-axis */\n yAxisTickCount?: number;\n /**\n * Custom formatter for tooltip values.\n * Receives the raw y-value and returns a display string.\n * When omitted, the raw value is shown. Takes precedence over the\n * deprecated `yAxisTickLabelFormat` prop.\n */\n tooltipValueFormat?: (value: number) => string;\n /** Indicates incomplete data periods with optional before/after timestamps in ms */\n incomplete?: { before?: number; after?: number };\n /** Height of the chart in pixels. Defaults to `350`. */\n height?: number;\n /** Callback fired when user selects a time range via brush selection */\n onTimeRangeChange?: (from: number, to: number) => void;\n /** When `true`, switches the chart to ECharts' built-in dark theme */\n isDarkMode?: boolean;\n /**\n * When `true`, renders a vertical gradient fill beneath each line series.\n * The gradient fades from the series' color at the top to transparent at the bottom.\n * Has no effect when `type` is `\"bar\"`.\n */\n gradient?: boolean;\n /**\n * When `true`, hides the chart and displays an animated sine-wave skeleton\n * that oscillates back and forth to indicate that data is being fetched.\n */\n loading?: boolean;\n /**\n * Accessible description for screen readers. When provided, it is passed to\n * ECharts' `aria.label.description` and announced when the chart receives\n * focus. Consumers are responsible for writing a meaningful description —\n * see the W3C guidance on complex images for recommendations.\n *\n * @see https://www.w3.org/WAI/tutorials/images/complex/\n * @see https://echarts.apache.org/handbook/en/best-practices/aria/\n */\n ariaDescription?: string;\n}\n\n/**\n * TimeseriesChart — a time-series line or bar chart.\n *\n * Built on `Chart` (Apache ECharts) with opinionated defaults for time-series data:\n * a time-typed x-axis, dashed lines for incomplete data periods, brush-based\n * time range selection, and automatic tooltip deduplication.\n *\n * @example\n * ```tsx\n * import * as echarts from \"echarts/core\";\n * import { LineChart } from \"echarts/charts\";\n * import { GridComponent, TooltipComponent, BrushComponent, ToolboxComponent } from \"echarts/components\";\n * import { CanvasRenderer } from \"echarts/renderers\";\n *\n * echarts.use([LineChart, GridComponent, TooltipComponent, BrushComponent, ToolboxComponent, CanvasRenderer]);\n *\n * const [range, setRange] = useState<[number, number]>();\n *\n * <TimeseriesChart\n * echarts={echarts}\n * data={[{ name: \"Requests\", data: [[Date.now(), 42]], color: \"#086FFF\" }]}\n * xAxisName=\"Time\"\n * xAxisTickFormat={(ts) => new Date(ts).toLocaleTimeString()}\n * yAxisName=\"Count\"\n * yAxisTickFormat={(value) => `${value / 1000}k`}\n * tooltipValueFormat={(value) => `${value.toFixed(2)} req/s`}\n * onTimeRangeChange={(from, to) => setRange([from, to])}\n * />\n * ```\n */\nexport function TimeseriesChart({\n echarts,\n type = \"line\",\n data,\n xAxisName,\n xAxisTickCount,\n xAxisTickFormat,\n yAxisTickFormat,\n yAxisTickLabelFormat,\n yAxisName,\n yAxisTickCount,\n tooltipValueFormat,\n onTimeRangeChange,\n height = 350,\n incomplete,\n isDarkMode,\n gradient,\n loading,\n ariaDescription,\n}: TimeseriesChartProps) {\n const chartRef = useRef<echarts.ECharts | null>(null);\n const incompleteBefore = incomplete?.before;\n const incompleteAfter = incomplete?.after;\n\n const options = useMemo(() => {\n const transformSeries: Array<LineSeriesOption | BarSeriesOption> = [];\n\n const seriesType =\n type === \"bar\"\n ? ({ type: \"bar\", stack: \"total\" } as const)\n : ({ type: \"line\", showSymbol: false } as const);\n\n for (const s of data) {\n const incompleteBeforePoints =\n incompleteBefore && type === \"line\"\n ? s.data.filter((point) => point[0] <= incompleteBefore)\n : [];\n\n const incompleteAfterPoints =\n incompleteAfter && type === \"line\"\n ? s.data.filter((point) => point[0] >= incompleteAfter)\n : [];\n\n const completePoints =\n incompleteBeforePoints.length > 0 || incompleteAfterPoints.length > 0\n ? s.data.slice(\n Math.max(0, incompleteBeforePoints.length - 1),\n Math.max(0, s.data.length - incompleteAfterPoints.length + 1)\n )\n : s.data;\n\n // Main complete data series\n const areaStyle =\n gradient && type === \"line\"\n ? {\n color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [\n { offset: 0, color: colorWithOpacity(s.color, 0.4) },\n { offset: 1, color: colorWithOpacity(s.color, 0) },\n ]),\n }\n : undefined;\n\n transformSeries.push({\n data: completePoints,\n color: s.color,\n name: s.name,\n emphasis: { focus: \"series\" },\n ...(areaStyle ? { areaStyle } : {}),\n ...seriesType,\n });\n\n // Incomplete data series with dashed lines\n const incompleteSeriesConfig = {\n color: s.color,\n name: s.name,\n type: \"line\" as const,\n lineStyle: { type: \"dashed\" as const },\n showSymbol: false,\n emphasis: { focus: \"series\" as const },\n };\n\n if (incompleteBeforePoints.length > 0) {\n transformSeries.push({\n ...incompleteSeriesConfig,\n data: incompleteBeforePoints,\n });\n }\n\n if (incompleteAfterPoints.length > 0) {\n transformSeries.push({\n ...incompleteSeriesConfig,\n data: incompleteAfterPoints,\n });\n }\n }\n\n return {\n aria: {\n enabled: true,\n ...(ariaDescription && { label: { description: ariaDescription } }),\n },\n brush: {\n snapToData: true,\n xAxisIndex: \"all\" as const,\n brushType: \"lineX\" as const,\n brushMode: \"single\" as const,\n outOfBrush: {\n colorAlpha: 0.3,\n },\n brushStyle: {\n borderWidth: 1,\n color: \"rgba(120,140,180,0.3)\",\n borderColor: \"rgba(120,140,180,0.8)\",\n },\n },\n tooltip: {\n trigger: \"axis\" as const,\n axisPointer: { type: \"shadow\" as const },\n formatter: (params: any) => {\n const items = Array.isArray(params) ? params : [params];\n\n // Track seen series names to avoid duplicates in tooltip\n // This is needed because incomplete data series (dashed lines) and complete data series\n // can overlap at the same timestamp, causing duplicate entries in the tooltip\n const seenNames = new Set<string>();\n const filteredParams = items.filter((param: any) => {\n if (seenNames.has(param.seriesName)) return false;\n seenNames.add(param.seriesName);\n return true;\n });\n\n const first = filteredParams[0];\n const ts = first?.value?.[0] ?? first?.axisValue;\n const header =\n ts != null\n ? `<div style=\"font-weight:600;margin-bottom:4px;\">${formatTimestamp(ts)}</div>`\n : \"\";\n\n const rows = filteredParams\n .map((param: any) => {\n const value = param?.value?.[1];\n const formatFn = tooltipValueFormat ?? yAxisTickLabelFormat;\n return `${param.marker} ${param.seriesName}: <strong>${formatFn ? formatFn(value) : value}</strong>`;\n })\n .join(\"<br/>\");\n\n return `${header}${rows}`;\n },\n },\n backgroundColor: \"transparent\",\n toolbox: { show: false },\n xAxis: {\n name: xAxisName,\n nameLocation: \"middle\" as const,\n nameGap: 30,\n type: \"time\" as const,\n splitLine: {\n show: true,\n lineStyle: {\n color: ChartPalette.axisLine(isDarkMode),\n type: \"dashed\" as const,\n width: 1,\n },\n },\n axisLine: { show: false },\n splitNumber: xAxisTickCount ?? 5,\n ...(xAxisTickFormat && {\n axisLabel: {\n formatter: (value: number) => xAxisTickFormat(value),\n },\n }),\n },\n yAxis: {\n name: yAxisName,\n nameLocation: \"middle\" as const,\n nameGap: 40,\n type: \"value\" as const,\n axisLine: { show: false },\n axisTick: { show: false },\n axisLabel: {\n margin: 15,\n ...(yAxisTickFormat && {\n formatter: (value: number) => yAxisTickFormat(value),\n }),\n },\n splitLine: {\n show: true,\n lineStyle: {\n color: ChartPalette.axisLine(isDarkMode),\n type: \"dashed\" as const,\n width: 1,\n },\n },\n splitNumber: yAxisTickCount,\n },\n grid: {\n left: yAxisName ? 30 : 24,\n right: 24,\n top: 24,\n bottom: xAxisName ? 30 : 24,\n },\n series: transformSeries,\n };\n }, [\n data,\n xAxisName,\n xAxisTickCount,\n xAxisTickFormat,\n yAxisTickFormat,\n yAxisTickLabelFormat,\n yAxisName,\n yAxisTickCount,\n tooltipValueFormat,\n incompleteBefore,\n incompleteAfter,\n type,\n gradient,\n echarts,\n ariaDescription,\n ]);\n\n const events = useMemo<Partial<ChartEvents>>(() => {\n if (!onTimeRangeChange) return {};\n\n return {\n brushend: (params) => {\n const range = params.areas[0].coordRange;\n onTimeRangeChange(range[0], range[1]);\n chartRef.current?.dispatchAction({ type: \"brush\", areas: [] });\n },\n };\n }, [onTimeRangeChange]);\n\n // Activate the lineX brush cursor when a time-range callback is provided,\n // and deactivate it on cleanup so the cursor resets when the prop is removed.\n const hasTimeRangeCallback = !!onTimeRangeChange;\n useEffect(() => {\n const chart = chartRef.current;\n if (chart && hasTimeRangeCallback) {\n chart.dispatchAction({\n type: \"takeGlobalCursor\",\n key: \"brush\",\n brushOption: {\n brushType: \"lineX\" as const,\n brushMode: \"single\" as const,\n },\n });\n\n return () => {\n chart.dispatchAction({\n type: \"takeGlobalCursor\",\n key: \"brush\",\n brushOption: {\n brushType: false,\n },\n });\n };\n }\n // `loading` controls whether <Chart> is mounted. When it flips to false,\n // chartRef.current becomes available and the brush cursor must be activated.\n // Without this dep, the effect won't re-run after Chart mounts.\n }, [chartRef, hasTimeRangeCallback, loading]);\n\n return (\n <div className=\"relative w-full\" style={{ height }}>\n {loading && <ChartWaveLoader height={height} isDarkMode={isDarkMode} />}\n {!loading && (\n <Chart\n echarts={echarts}\n ref={chartRef}\n options={options as EChartsOption}\n height={height}\n isDarkMode={isDarkMode}\n onEvents={events}\n />\n )}\n </div>\n );\n}\n\n/**\n * Animated sine-wave skeleton shown while `TimeseriesChart` is in `loading` state.\n * Renders multiple staggered wave paths that sweep continuously left-to-right,\n * mimicking the motion of live time-series data being drawn.\n */\nfunction ChartWaveLoader({\n height,\n isDarkMode,\n}: {\n height: number;\n isDarkMode?: boolean;\n}) {\n const mid = height / 2;\n const amp = Math.min(height * 0.12, 28);\n const period = 400;\n const steps = 120;\n\n const points: string[] = [];\n for (let i = 0; i <= steps; i++) {\n const x = -period + (i / steps) * period * 3;\n const y = mid + Math.sin((i / steps) * 2 * Math.PI * 3) * amp;\n points.push(`${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`);\n }\n const d = points.join(\" \");\n\n const strokeColor = isDarkMode ? \"rgba(255,255,255,0.5)\" : \"rgba(0,0,0,0.2)\";\n\n return (\n <div\n aria-hidden=\"true\"\n className=\"absolute inset-0 overflow-hidden\"\n style={{ height }}\n >\n <style>{`@keyframes sf-chart-wave{from{transform:translateX(0)}to{transform:translateX(${period}px)}}`}</style>\n <svg\n width=\"100%\"\n height={height}\n viewBox={`0 0 ${period} ${height}`}\n preserveAspectRatio=\"none\"\n className=\"w-full animate-pulse\"\n >\n <path\n d={d}\n fill=\"none\"\n stroke={strokeColor}\n strokeWidth=\"2\"\n style={{\n animation: `sf-chart-wave 2.4s linear infinite`,\n transformOrigin: \"0 0\",\n }}\n />\n </svg>\n </div>\n );\n}\n\n/**\n * Returns an `rgba(r, g, b, alpha)` string for any hex or rgb(a) color input,\n * replacing whatever opacity was already present with the given `alpha` (0–1).\n *\n * Handles:\n * - 6-digit hex: `#RRGGBB`\n * - 8-digit hex: `#RRGGBBAA` ← strips existing alpha\n * - 3-digit hex: `#RGB`\n * - `rgb(r, g, b)`\n * - `rgba(r, g, b, a)` ← replaces existing alpha\n */\nfunction colorWithOpacity(color: string, alpha: number): string {\n const a = Math.max(0, Math.min(1, alpha));\n\n // rgb / rgba\n const rgbMatch = /rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)/i.exec(\n color\n );\n if (rgbMatch) {\n return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${a})`;\n }\n\n // hex — strip leading #\n let hex = color.replace(/^#/, \"\");\n\n // expand 3-digit → 6-digit\n if (hex.length === 3) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n }\n\n // strip 8-digit alpha → keep only 6\n if (hex.length === 8) {\n hex = hex.slice(0, 6);\n }\n\n const r = Number.parseInt(hex.slice(0, 2), 16);\n const g = Number.parseInt(hex.slice(2, 4), 16);\n const b = Number.parseInt(hex.slice(4, 6), 16);\n\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\n/** Zero-pads a number to two digits (e.g. `5` → `\"05\"`) */\nfunction pad(n: number) {\n return n.toString().padStart(2, \"0\");\n}\n\n/**\n * Formats a timestamp as `\"YYYY-MM-DD HH:mm:ss\"` for use in chart tooltips.\n * Accepts a Unix timestamp in milliseconds, an ISO date string, or a `Date` object.\n */\nfunction formatTimestamp(ts: number | string | Date): string {\n const d = new Date(ts);\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;\n}\n","import { cn } from \"../../utils\";\n\n/** Shared props for both legend item variants */\ninterface LegendItemProps {\n /** Series name shown as a label */\n name: string;\n /** Hex color string for the series indicator dot */\n color: string;\n /** Formatted value string to display */\n value: string;\n /** Optional unit label shown after the value (e.g. `\"ms\"`, `\"%\"`) */\n unit?: string;\n /** When `true`, renders the item at 50% opacity to indicate a deselected state */\n inactive?: boolean;\n}\n\n/**\n * Large legend item — stacked layout with a colored dot + series name on top\n * and a large value with an optional small unit below. Use for prominent\n * single-metric displays such as dashboard cards.\n */\nfunction LargeItem({ color, value, name, unit, inactive }: LegendItemProps) {\n return (\n <div className=\"inline-flex flex-col gap-2 min-w-42 py-2\">\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\"size-2 rounded-full inline-block\", {\n \"opacity-50\": inactive,\n })}\n style={{ backgroundColor: color }}\n />\n <span className={cn(\"text-xs\", { \"opacity-50\": inactive })}>\n {name}\n </span>\n </div>\n <div className=\"flex items-baseline gap-0.5\">\n <span\n className={cn(\"text-lg font-medium leading-none\", {\n \"opacity-50\": inactive,\n })}\n >\n {value}\n </span>\n {unit && (\n <span\n className={cn(\"text-xs text-sf-subtle leading-none\", {\n \"opacity-50\": inactive,\n })}\n >\n {unit}\n </span>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Small legend item — inline layout with a colored dot, series name, and value\n * on a single row. Use for compact legends below or beside a chart.\n */\nfunction SmallItem({ color, value, name, inactive }: LegendItemProps) {\n return (\n <div className=\"inline-flex items-center gap-2\">\n <span\n className={cn(\"size-2 rounded-full inline-block\", {\n \"opacity-50\": inactive,\n })}\n style={{ backgroundColor: color }}\n />\n <span className={cn(\"text-xs\", { \"opacity-50\": inactive })}>{name}</span>\n <span className={cn(\"text-xs font-medium\", { \"opacity-50\": inactive })}>\n {value}\n </span>\n </div>\n );\n}\n\n/**\n * ChartLegend — pre-built legend item components for use alongside a chart.\n *\n * - `ChartLegend.SmallItem` — compact inline layout; suited for multi-series legends\n * - `ChartLegend.LargeItem` — stacked layout with a large value; suited for single-metric cards\n *\n * @example\n * ```tsx\n * <ChartLegend.SmallItem name=\"Requests\" color=\"#086FFF\" value=\"1,234\" />\n * <ChartLegend.LargeItem name=\"Latency\" color=\"#CF7EE9\" value=\"42\" unit=\"ms\" inactive />\n * ```\n */\nexport const ChartLegend = {\n SmallItem,\n LargeItem,\n};\n","import type { EChartsOption } from \"echarts\";\nimport type * as echarts from \"echarts/core\";\nimport { useMemo } from \"react\";\n\nimport { ChartPalette, CHART_DARK_COLORS, CHART_LIGHT_COLORS } from \"./color\";\nimport { Chart } from \"./echart\";\n\n/** A single bar value on a `BarChart` */\nexport interface BarChartDatum {\n /** Category name shown on the axis and in tooltips */\n name: string;\n /** Numeric value represented by the bar */\n value: number;\n /** Optional override color for this bar; defaults to the categorical palette */\n color?: string;\n}\n\n/** Props for `BarChart` */\nexport interface BarChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Array of bars to display */\n data: BarChartDatum[];\n /** Axis orientation. `\"vertical\"` draws bars upward from the x-axis. */\n orientation?: \"vertical\" | \"horizontal\";\n /** Height of the chart in pixels. Defaults to `320`. */\n height?: number;\n /** Custom formatter for value-axis tick labels */\n yAxisTickFormat?: (value: number) => string;\n /** Custom formatter for tooltip values */\n tooltipValueFormat?: (value: number) => string;\n /** When `true`, switches ECharts to its built-in dark theme */\n isDarkMode?: boolean;\n /**\n * When `true`, hides the chart and displays a neutral skeleton placeholder\n * to indicate data is being fetched.\n */\n loading?: boolean;\n /** Callback fired when a bar is clicked (receives the bar's `name`) */\n onBarClick?: (name: string) => void;\n /** Additional CSS classes applied to the chart container */\n className?: string;\n}\n\n/**\n * BarChart — categorical bar chart for comparing discrete values.\n *\n * Consumer must register `BarChart`, `GridComponent`, `TooltipComponent`, and a\n * renderer (e.g. `CanvasRenderer`) on the passed-in `echarts` instance.\n *\n * @example\n * ```tsx\n * <BarChart\n * echarts={echarts}\n * data={[\n * { name: \"Mon\", value: 120 },\n * { name: \"Tue\", value: 200 },\n * { name: \"Wed\", value: 150 },\n * ]}\n * />\n * ```\n */\nexport function BarChart({\n echarts,\n data,\n orientation = \"vertical\",\n height = 320,\n yAxisTickFormat,\n tooltipValueFormat,\n isDarkMode,\n loading,\n onBarClick,\n className,\n}: BarChartProps) {\n const options = useMemo<EChartsOption>(() => {\n const palette = isDarkMode ? CHART_DARK_COLORS : CHART_LIGHT_COLORS;\n const axis = ChartPalette.axisDefaults(isDarkMode);\n const categoryAxis = {\n ...axis,\n type: \"category\" as const,\n data: data.map((d) => d.name),\n };\n const valueAxis = {\n ...axis,\n type: \"value\" as const,\n axisLabel: yAxisTickFormat\n ? { formatter: (v: number) => yAxisTickFormat(v) }\n : undefined,\n };\n\n return {\n grid: { left: 40, right: 16, top: 24, bottom: 32, containLabel: true },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n valueFormatter: tooltipValueFormat\n ? (value: unknown) =>\n typeof value === \"number\"\n ? tooltipValueFormat(value)\n : String(value)\n : undefined,\n },\n xAxis: orientation === \"vertical\" ? categoryAxis : valueAxis,\n yAxis: orientation === \"vertical\" ? valueAxis : categoryAxis,\n series: [\n {\n type: \"bar\",\n data: data.map((d, i) => ({\n value: d.value,\n itemStyle: { color: d.color ?? palette[i % palette.length] },\n })),\n },\n ],\n };\n }, [data, orientation, yAxisTickFormat, tooltipValueFormat, isDarkMode]);\n\n const onEvents = useMemo(\n () =>\n onBarClick\n ? {\n click: (params: { name?: string }) => {\n if (params.name) onBarClick(params.name);\n },\n }\n : undefined,\n [onBarClick]\n );\n\n if (loading) {\n return (\n <div\n aria-busy=\"true\"\n className={className}\n style={{ height }}\n data-sf-skeleton=\"\"\n >\n <div className=\"h-full w-full animate-pulse rounded-md bg-sf-fill\" />\n </div>\n );\n }\n\n return (\n <Chart\n echarts={echarts}\n options={options}\n height={height}\n isDarkMode={isDarkMode}\n className={className}\n onEvents={onEvents}\n />\n );\n}\n","import type { EChartsOption } from \"echarts\";\nimport type * as echarts from \"echarts/core\";\nimport { useMemo } from \"react\";\n\nimport { ChartPalette, CHART_DARK_COLORS, CHART_LIGHT_COLORS } from \"./color\";\nimport { Chart } from \"./echart\";\n\n/** A single series on a `StackedBarChart`. */\nexport interface StackedBarSeries {\n /** Series name displayed in tooltips and legends */\n name: string;\n /** Values aligned 1:1 with the parent `categories` array */\n data: number[];\n /** Optional override color; defaults to the categorical palette */\n color?: string;\n}\n\n/** Props for `StackedBarChart` */\nexport interface StackedBarChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Category labels rendered on the non-value axis */\n categories: string[];\n /** Array of stacked series */\n series: StackedBarSeries[];\n /** Axis orientation. `\"vertical\"` draws bars upward from the x-axis. */\n orientation?: \"vertical\" | \"horizontal\";\n /**\n * When `true`, renders as a 100%-stacked chart where each category sums to 100.\n * @default false\n */\n percent?: boolean;\n /** Height of the chart in pixels. Defaults to `320`. */\n height?: number;\n /** Custom formatter for value-axis tick labels */\n yAxisTickFormat?: (value: number) => string;\n /** Custom formatter for tooltip values */\n tooltipValueFormat?: (value: number) => string;\n /** When `true`, switches ECharts to its built-in dark theme */\n isDarkMode?: boolean;\n /** When `true`, hides the chart and displays a neutral skeleton placeholder */\n loading?: boolean;\n /** Additional CSS classes applied to the chart container */\n className?: string;\n}\n\n/**\n * StackedBarChart — stacked categorical bar chart for part-to-whole comparisons.\n *\n * @example\n * ```tsx\n * <StackedBarChart\n * echarts={echarts}\n * categories={[\"Mon\", \"Tue\", \"Wed\"]}\n * series={[\n * { name: \"Desktop\", data: [100, 120, 90] },\n * { name: \"Mobile\", data: [60, 80, 110] },\n * ]}\n * />\n * ```\n */\nexport function StackedBarChart({\n echarts,\n categories,\n series,\n orientation = \"vertical\",\n percent = false,\n height = 320,\n yAxisTickFormat,\n tooltipValueFormat,\n isDarkMode,\n loading,\n className,\n}: StackedBarChartProps) {\n const options = useMemo<EChartsOption>(() => {\n const palette = isDarkMode ? CHART_DARK_COLORS : CHART_LIGHT_COLORS;\n\n // For percent mode, normalize each position across all series to sum to 100.\n const resolvedSeries = percent\n ? series.map((s) => ({\n ...s,\n data: s.data.map((v, i) => {\n const total = series.reduce(\n (acc, other) => acc + (other.data[i] ?? 0),\n 0\n );\n return total === 0 ? 0 : (v / total) * 100;\n }),\n }))\n : series;\n\n const axis = ChartPalette.axisDefaults(isDarkMode);\n const categoryAxis = {\n ...axis,\n type: \"category\" as const,\n data: categories,\n };\n const valueAxis = {\n ...axis,\n type: \"value\" as const,\n max: percent ? 100 : undefined,\n axisLabel: yAxisTickFormat\n ? { formatter: (v: number) => yAxisTickFormat(v) }\n : percent\n ? { formatter: \"{value}%\" }\n : undefined,\n };\n\n return {\n legend: { bottom: 0 },\n grid: { left: 40, right: 16, top: 24, bottom: 48, containLabel: true },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n valueFormatter: tooltipValueFormat\n ? (value: unknown) =>\n typeof value === \"number\"\n ? tooltipValueFormat(value)\n : String(value)\n : undefined,\n },\n xAxis: orientation === \"vertical\" ? categoryAxis : valueAxis,\n yAxis: orientation === \"vertical\" ? valueAxis : categoryAxis,\n series: resolvedSeries.map((s, i) => ({\n name: s.name,\n type: \"bar\" as const,\n stack: \"total\",\n data: s.data,\n itemStyle: { color: s.color ?? palette[i % palette.length] },\n })),\n };\n }, [\n categories,\n series,\n orientation,\n percent,\n yAxisTickFormat,\n tooltipValueFormat,\n isDarkMode,\n ]);\n\n if (loading) {\n return (\n <div aria-busy=\"true\" className={className} style={{ height }}>\n <div className=\"h-full w-full animate-pulse rounded-md bg-sf-fill\" />\n </div>\n );\n }\n\n return (\n <Chart\n echarts={echarts}\n options={options}\n height={height}\n isDarkMode={isDarkMode}\n className={className}\n />\n );\n}\n","import type { EChartsOption } from \"echarts\";\nimport type * as echarts from \"echarts/core\";\nimport type { ReactNode } from \"react\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { CHART_DARK_COLORS, CHART_LIGHT_COLORS } from \"./color\";\nimport { Chart } from \"./echart\";\n\n/** A single slice of a `PieChart` */\nexport interface PieChartDatum {\n /** Display name shown in tooltips and legends */\n name: string;\n /** Numeric value determining the slice's arc size */\n value: number;\n /** Optional override color; defaults to the categorical palette */\n color?: string;\n}\n\n/** Props for `PieChart` */\nexport interface PieChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Array of slice data */\n data: PieChartDatum[];\n /**\n * Visual variant. `\"donut\"` renders with a hollow center suitable for\n * overlaying a centered label via `centerSlot`.\n * @default \"pie\"\n */\n variant?: \"pie\" | \"donut\";\n /** Height of the chart in pixels. Defaults to `320`. */\n height?: number;\n /** When `true`, shows a legend below the chart. */\n showLegend?: boolean;\n /** Custom formatter for tooltip values */\n tooltipValueFormat?: (value: number) => string;\n /** When `true`, switches ECharts to its built-in dark theme */\n isDarkMode?: boolean;\n /** When `true`, hides the chart and displays a neutral skeleton placeholder */\n loading?: boolean;\n /**\n * Optional content rendered absolutely centered over the chart. Intended for\n * donut variants — displays a KPI (e.g. total value) in the hole.\n */\n centerSlot?: ReactNode;\n /** Additional CSS classes applied to the chart container */\n className?: string;\n}\n\n/**\n * PieChart — single-series pie or donut chart for part-to-whole relationships.\n *\n * @example\n * ```tsx\n * <PieChart\n * echarts={echarts}\n * variant=\"donut\"\n * data={[\n * { name: \"Prod\", value: 60 },\n * { name: \"Staging\", value: 30 },\n * { name: \"Dev\", value: 10 },\n * ]}\n * centerSlot={<span className=\"text-sf-strong\">100</span>}\n * />\n * ```\n */\nexport function PieChart({\n echarts,\n data,\n variant = \"pie\",\n height = 320,\n showLegend,\n tooltipValueFormat,\n isDarkMode,\n loading,\n centerSlot,\n className,\n}: PieChartProps) {\n const options = useMemo<EChartsOption>(() => {\n const palette = isDarkMode ? CHART_DARK_COLORS : CHART_LIGHT_COLORS;\n return {\n legend: showLegend ? { bottom: 0 } : undefined,\n tooltip: {\n trigger: \"item\",\n valueFormatter: tooltipValueFormat\n ? (value: unknown) =>\n typeof value === \"number\"\n ? tooltipValueFormat(value)\n : String(value)\n : undefined,\n },\n series: [\n {\n type: \"pie\",\n radius: variant === \"donut\" ? [\"55%\", \"80%\"] : \"75%\",\n avoidLabelOverlap: true,\n label: {\n show: variant !== \"donut\",\n color: isDarkMode ? \"#e5e5e5\" : \"#1a1a1a\",\n textBorderWidth: 0,\n textBorderColor: \"transparent\",\n },\n labelLine: {\n show: variant !== \"donut\",\n lineStyle: { color: isDarkMode ? \"#e5e5e5\" : \"#1a1a1a\" },\n },\n data: data.map((d, i) => ({\n name: d.name,\n value: d.value,\n itemStyle: { color: d.color ?? palette[i % palette.length] },\n })),\n },\n ],\n };\n }, [data, variant, showLegend, tooltipValueFormat, isDarkMode]);\n\n if (loading) {\n return (\n <div aria-busy=\"true\" className={className} style={{ height }}>\n <div className=\"h-full w-full animate-pulse rounded-full bg-sf-fill\" />\n </div>\n );\n }\n\n return (\n <div className={cn(\"relative\", className)} style={{ height }}>\n <Chart\n echarts={echarts}\n options={options}\n height={height}\n isDarkMode={isDarkMode}\n />\n {centerSlot !== undefined && variant === \"donut\" ? (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n {centerSlot}\n </div>\n ) : null}\n </div>\n );\n}\n","import type { EChartsOption } from \"echarts\";\nimport type * as echarts from \"echarts/core\";\nimport { useMemo } from \"react\";\n\nimport { ChartPalette, CHART_DARK_COLORS, CHART_LIGHT_COLORS } from \"./color\";\nimport { Chart } from \"./echart\";\n\n/** A single data series rendered on an `AreaChart` */\nexport interface AreaChartSeries {\n /** Display name shown in tooltips and legends */\n name: string;\n /**\n * Series data. For `\"time\"` x-axis: `[timestampMs, value][]`.\n * For `\"category\"` x-axis: a numeric array aligned with the parent `categories`.\n */\n data: [number, number][] | number[];\n /** Optional override color; defaults to the categorical palette */\n color?: string;\n}\n\n/** Props for `AreaChart` */\nexport interface AreaChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Array of series to display */\n series: AreaChartSeries[];\n /**\n * X-axis type. `\"time\"` plots `[ts, value]` tuples; `\"category\"` plots\n * numeric arrays indexed against `categories`.\n * @default \"time\"\n */\n xAxisType?: \"time\" | \"category\";\n /** Required when `xAxisType === \"category\"` — labels rendered on the x-axis */\n categories?: string[];\n /** Height of the chart in pixels. Defaults to `320`. */\n height?: number;\n /** Label for the x-axis */\n xAxisName?: string;\n /** Label for the y-axis */\n yAxisName?: string;\n /** Custom formatter for y-axis tick labels */\n yAxisTickFormat?: (value: number) => string;\n /** Custom formatter for tooltip values */\n tooltipValueFormat?: (value: number) => string;\n /** When `true`, switches ECharts to its built-in dark theme */\n isDarkMode?: boolean;\n /** When `true`, stacks all series on top of one another */\n stacked?: boolean;\n /** When `true`, hides the chart and displays a neutral skeleton placeholder */\n loading?: boolean;\n /** Additional CSS classes applied to the chart container */\n className?: string;\n}\n\n/**\n * AreaChart — filled line chart with gradient fill, suitable for trend\n * visualisations on either a time or category x-axis.\n *\n * Unlike `TimeseriesChart`, this component also accepts a category x-axis,\n * making it useful for cohort or bucketed trend displays.\n *\n * @example\n * ```tsx\n * <AreaChart\n * echarts={echarts}\n * xAxisType=\"category\"\n * categories={[\"W1\", \"W2\", \"W3\", \"W4\"]}\n * series={[{ name: \"Signups\", data: [120, 180, 240, 200] }]}\n * />\n * ```\n */\nexport function AreaChart({\n echarts,\n series,\n xAxisType = \"time\",\n categories,\n height = 320,\n xAxisName,\n yAxisName,\n yAxisTickFormat,\n tooltipValueFormat,\n isDarkMode,\n stacked,\n loading,\n className,\n}: AreaChartProps) {\n const options = useMemo<EChartsOption>(() => {\n const palette = isDarkMode ? CHART_DARK_COLORS : CHART_LIGHT_COLORS;\n\n return {\n grid: { left: 40, right: 16, top: 24, bottom: 32, containLabel: true },\n legend: series.length > 1 ? { bottom: 0 } : undefined,\n tooltip: {\n trigger: \"axis\",\n valueFormatter: tooltipValueFormat\n ? (value: unknown) =>\n typeof value === \"number\"\n ? tooltipValueFormat(value)\n : String(value)\n : undefined,\n },\n xAxis:\n xAxisType === \"category\"\n ? {\n ...ChartPalette.axisDefaults(isDarkMode),\n type: \"category\" as const,\n name: xAxisName,\n data: categories,\n boundaryGap: false,\n }\n : {\n ...ChartPalette.axisDefaults(isDarkMode),\n type: \"time\" as const,\n name: xAxisName,\n },\n yAxis: {\n ...ChartPalette.axisDefaults(isDarkMode),\n type: \"value\",\n name: yAxisName,\n axisLabel: yAxisTickFormat\n ? { formatter: (v: number) => yAxisTickFormat(v) }\n : undefined,\n },\n series: series.map((s, i) => {\n const color = s.color ?? palette[i % palette.length];\n return {\n name: s.name,\n type: \"line\" as const,\n smooth: true,\n showSymbol: false,\n stack: stacked ? \"total\" : undefined,\n data: s.data,\n itemStyle: { color },\n lineStyle: { color },\n areaStyle: {\n color: {\n type: \"linear\" as const,\n x: 0,\n y: 0,\n x2: 0,\n y2: 1,\n colorStops: [\n { offset: 0, color },\n { offset: 1, color: \"rgba(0,0,0,0)\" },\n ],\n },\n opacity: 0.4,\n },\n };\n }),\n };\n }, [\n series,\n xAxisType,\n categories,\n xAxisName,\n yAxisName,\n yAxisTickFormat,\n tooltipValueFormat,\n stacked,\n isDarkMode,\n ]);\n\n if (loading) {\n return (\n <div aria-busy=\"true\" className={className} style={{ height }}>\n <div className=\"h-full w-full animate-pulse rounded-md bg-sf-fill\" />\n </div>\n );\n }\n\n return (\n <Chart\n echarts={echarts}\n options={options}\n height={height}\n isDarkMode={isDarkMode}\n className={className}\n />\n );\n}\n","import type { EChartsOption } from \"echarts\";\nimport type * as echarts from \"echarts/core\";\nimport { useMemo } from \"react\";\n\nimport { ChartPalette, CHART_DARK_COLORS, CHART_LIGHT_COLORS } from \"./color\";\nimport { Chart } from \"./echart\";\n\n/** A single scatter series (point cloud) */\nexport interface ScatterSeries {\n /** Display name shown in tooltips and legends */\n name: string;\n /** Array of `[x, y]` point tuples */\n data: [number, number][];\n /** Optional override color; defaults to the categorical palette */\n color?: string;\n /** Optional marker size in pixels (default 8) */\n symbolSize?: number;\n}\n\n/** Props for `ScatterChart` */\nexport interface ScatterChartProps {\n /**\n * The ECharts core instance imported by the consumer.\n * Passed in rather than imported directly so the consumer controls which\n * ECharts modules are bundled (tree-shaking).\n */\n echarts: typeof echarts;\n /** Array of scatter series */\n series: ScatterSeries[];\n /** Height of the chart in pixels. Defaults to `320`. */\n height?: number;\n /** Label for the x-axis */\n xAxisName?: string;\n /** Label for the y-axis */\n yAxisName?: string;\n /** Custom formatter for x-axis tick labels */\n xAxisTickFormat?: (value: number) => string;\n /** Custom formatter for y-axis tick labels */\n yAxisTickFormat?: (value: number) => string;\n /** When `true`, switches ECharts to its built-in dark theme */\n isDarkMode?: boolean;\n /** When `true`, hides the chart and displays a neutral skeleton placeholder */\n loading?: boolean;\n /** Additional CSS classes applied to the chart container */\n className?: string;\n}\n\n/**\n * ScatterChart — multi-series scatter/point cloud for correlation visualisations.\n *\n * @example\n * ```tsx\n * <ScatterChart\n * echarts={echarts}\n * series={[\n * { name: \"Group A\", data: [[1, 2], [2, 3], [3, 4]] },\n * { name: \"Group B\", data: [[1.5, 4], [2.5, 5]] },\n * ]}\n * xAxisName=\"Cost\"\n * yAxisName=\"Value\"\n * />\n * ```\n */\nexport function ScatterChart({\n echarts,\n series,\n height = 320,\n xAxisName,\n yAxisName,\n xAxisTickFormat,\n yAxisTickFormat,\n isDarkMode,\n loading,\n className,\n}: ScatterChartProps) {\n const options = useMemo<EChartsOption>(() => {\n const palette = isDarkMode ? CHART_DARK_COLORS : CHART_LIGHT_COLORS;\n\n return {\n grid: { left: 40, right: 16, top: 24, bottom: 40, containLabel: true },\n legend: series.length > 1 ? { bottom: 0 } : undefined,\n tooltip: { trigger: \"item\" },\n xAxis: {\n ...ChartPalette.axisDefaults(isDarkMode),\n type: \"value\",\n name: xAxisName,\n axisLabel: xAxisTickFormat\n ? { formatter: (v: number) => xAxisTickFormat(v) }\n : undefined,\n },\n yAxis: {\n ...ChartPalette.axisDefaults(isDarkMode),\n type: \"value\",\n name: yAxisName,\n axisLabel: yAxisTickFormat\n ? { formatter: (v: number) => yAxisTickFormat(v) }\n : undefined,\n },\n series: series.map((s, i) => ({\n name: s.name,\n type: \"scatter\" as const,\n data: s.data,\n symbolSize: s.symbolSize ?? 8,\n itemStyle: { color: s.color ?? palette[i % palette.length] },\n })),\n };\n }, [\n series,\n xAxisName,\n yAxisName,\n xAxisTickFormat,\n yAxisTickFormat,\n isDarkMode,\n ]);\n\n if (loading) {\n return (\n <div aria-busy=\"true\" className={className} style={{ height }}>\n <div className=\"h-full w-full animate-pulse rounded-md bg-sf-fill\" />\n </div>\n );\n }\n\n return (\n <Chart\n echarts={echarts}\n options={options}\n height={height}\n isDarkMode={isDarkMode}\n className={className}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2HA,SAAgB,gBAAgB,EAC9B,SACA,OAAO,QACP,MACA,WACA,gBACA,iBACA,iBACA,sBACA,WACA,gBACA,oBACA,mBACA,SAAS,KACT,YACA,YACA,UACA,SACA,mBACuB;CACvB,MAAM,WAAW,OAA+B,KAAK;CACrD,MAAM,mBAAmB,YAAY;CACrC,MAAM,kBAAkB,YAAY;CAEpC,MAAM,UAAU,cAAc;EAC5B,MAAM,kBAA6D,EAAE;EAErE,MAAM,aACJ,SAAS,QACJ;GAAE,MAAM;GAAO,OAAO;GAAS,GAC/B;GAAE,MAAM;GAAQ,YAAY;GAAO;AAE1C,OAAK,MAAM,KAAK,MAAM;GACpB,MAAM,yBACJ,oBAAoB,SAAS,SACzB,EAAE,KAAK,QAAQ,UAAU,MAAM,MAAM,iBAAiB,GACtD,EAAE;GAER,MAAM,wBACJ,mBAAmB,SAAS,SACxB,EAAE,KAAK,QAAQ,UAAU,MAAM,MAAM,gBAAgB,GACrD,EAAE;GAER,MAAM,iBACJ,uBAAuB,SAAS,KAAK,sBAAsB,SAAS,IAChE,EAAE,KAAK,MACL,KAAK,IAAI,GAAG,uBAAuB,SAAS,EAAE,EAC9C,KAAK,IAAI,GAAG,EAAE,KAAK,SAAS,sBAAsB,SAAS,EAAE,CAC9D,GACD,EAAE;GAGR,MAAM,YACJ,YAAY,SAAS,SACjB,EACE,OAAO,IAAI,QAAQ,QAAQ,eAAe,GAAG,GAAG,GAAG,GAAG,CACpD;IAAE,QAAQ;IAAG,OAAO,iBAAiB,EAAE,OAAO,GAAI;IAAE,EACpD;IAAE,QAAQ;IAAG,OAAO,iBAAiB,EAAE,OAAO,EAAE;IAAE,CACnD,CAAC,EACH,GACD,KAAA;AAEN,mBAAgB,KAAK;IACnB,MAAM;IACN,OAAO,EAAE;IACT,MAAM,EAAE;IACR,UAAU,EAAE,OAAO,UAAU;IAC7B,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;IAClC,GAAG;IACJ,CAAC;GAGF,MAAM,yBAAyB;IAC7B,OAAO,EAAE;IACT,MAAM,EAAE;IACR,MAAM;IACN,WAAW,EAAE,MAAM,UAAmB;IACtC,YAAY;IACZ,UAAU,EAAE,OAAO,UAAmB;IACvC;AAED,OAAI,uBAAuB,SAAS,EAClC,iBAAgB,KAAK;IACnB,GAAG;IACH,MAAM;IACP,CAAC;AAGJ,OAAI,sBAAsB,SAAS,EACjC,iBAAgB,KAAK;IACnB,GAAG;IACH,MAAM;IACP,CAAC;;AAIN,SAAO;GACL,MAAM;IACJ,SAAS;IACT,GAAI,mBAAmB,EAAE,OAAO,EAAE,aAAa,iBAAiB,EAAE;IACnE;GACD,OAAO;IACL,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,WAAW;IACX,YAAY,EACV,YAAY,IACb;IACD,YAAY;KACV,aAAa;KACb,OAAO;KACP,aAAa;KACd;IACF;GACD,SAAS;IACP,SAAS;IACT,aAAa,EAAE,MAAM,UAAmB;IACxC,YAAY,WAAgB;KAC1B,MAAM,QAAQ,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,OAAO;KAKvD,MAAM,4BAAY,IAAI,KAAa;KACnC,MAAM,iBAAiB,MAAM,QAAQ,UAAe;AAClD,UAAI,UAAU,IAAI,MAAM,WAAW,CAAE,QAAO;AAC5C,gBAAU,IAAI,MAAM,WAAW;AAC/B,aAAO;OACP;KAEF,MAAM,QAAQ,eAAe;KAC7B,MAAM,KAAK,OAAO,QAAQ,MAAM,OAAO;AAcvC,YAAO,GAZL,MAAM,OACF,mDAAmD,gBAAgB,GAAG,CAAC,UACvE,KAEO,eACV,KAAK,UAAe;MACnB,MAAM,QAAQ,OAAO,QAAQ;MAC7B,MAAM,WAAW,sBAAsB;AACvC,aAAO,GAAG,MAAM,OAAO,GAAG,MAAM,WAAW,YAAY,WAAW,SAAS,MAAM,GAAG,MAAM;OAC1F,CACD,KAAK,QAAQ;;IAInB;GACD,iBAAiB;GACjB,SAAS,EAAE,MAAM,OAAO;GACxB,OAAO;IACL,MAAM;IACN,cAAc;IACd,SAAS;IACT,MAAM;IACN,WAAW;KACT,MAAM;KACN,WAAW;MACT,OAAO,aAAa,SAAS,WAAW;MACxC,MAAM;MACN,OAAO;MACR;KACF;IACD,UAAU,EAAE,MAAM,OAAO;IACzB,aAAa,kBAAkB;IAC/B,GAAI,mBAAmB,EACrB,WAAW,EACT,YAAY,UAAkB,gBAAgB,MAAM,EACrD,EACF;IACF;GACD,OAAO;IACL,MAAM;IACN,cAAc;IACd,SAAS;IACT,MAAM;IACN,UAAU,EAAE,MAAM,OAAO;IACzB,UAAU,EAAE,MAAM,OAAO;IACzB,WAAW;KACT,QAAQ;KACR,GAAI,mBAAmB,EACrB,YAAY,UAAkB,gBAAgB,MAAM,EACrD;KACF;IACD,WAAW;KACT,MAAM;KACN,WAAW;MACT,OAAO,aAAa,SAAS,WAAW;MACxC,MAAM;MACN,OAAO;MACR;KACF;IACD,aAAa;IACd;GACD,MAAM;IACJ,MAAM,YAAY,KAAK;IACvB,OAAO;IACP,KAAK;IACL,QAAQ,YAAY,KAAK;IAC1B;GACD,QAAQ;GACT;IACA;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,SAAS,cAAoC;AACjD,MAAI,CAAC,kBAAmB,QAAO,EAAE;AAEjC,SAAO,EACL,WAAW,WAAW;GACpB,MAAM,QAAQ,OAAO,MAAM,GAAG;AAC9B,qBAAkB,MAAM,IAAI,MAAM,GAAG;AACrC,YAAS,SAAS,eAAe;IAAE,MAAM;IAAS,OAAO,EAAE;IAAE,CAAC;KAEjE;IACA,CAAC,kBAAkB,CAAC;CAIvB,MAAM,uBAAuB,CAAC,CAAC;AAC/B,iBAAgB;EACd,MAAM,QAAQ,SAAS;AACvB,MAAI,SAAS,sBAAsB;AACjC,SAAM,eAAe;IACnB,MAAM;IACN,KAAK;IACL,aAAa;KACX,WAAW;KACX,WAAW;KACZ;IACF,CAAC;AAEF,gBAAa;AACX,UAAM,eAAe;KACnB,MAAM;KACN,KAAK;KACL,aAAa,EACX,WAAW,OACZ;KACF,CAAC;;;IAML;EAAC;EAAU;EAAsB;EAAQ,CAAC;AAE7C,QACE,qBAAC,OAAD;EAAK,WAAU;EAAkB,OAAO,EAAE,QAAQ;YAAlD,CACG,WAAW,oBAAC,iBAAD;GAAyB;GAAoB;GAAc,CAAA,EACtE,CAAC,WACA,oBAAC,OAAD;GACW;GACT,KAAK;GACI;GACD;GACI;GACZ,UAAU;GACV,CAAA,CAEA;;;;;;;;AASV,SAAS,gBAAgB,EACvB,QACA,cAIC;CACD,MAAM,MAAM,SAAS;CACrB,MAAM,MAAM,KAAK,IAAI,SAAS,KAAM,GAAG;CACvC,MAAM,SAAS;CACf,MAAM,QAAQ;CAEd,MAAM,SAAmB,EAAE;AAC3B,MAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;EAC/B,MAAM,IAAI,CAAC,SAAU,IAAI,QAAS,SAAS;EAC3C,MAAM,IAAI,MAAM,KAAK,IAAK,IAAI,QAAS,IAAI,KAAK,KAAK,EAAE,GAAG;AAC1D,SAAO,KAAK,GAAG,MAAM,IAAI,MAAM,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG;;CAEtE,MAAM,IAAI,OAAO,KAAK,IAAI;CAE1B,MAAM,cAAc,aAAa,0BAA0B;AAE3D,QACE,qBAAC,OAAD;EACE,eAAY;EACZ,WAAU;EACV,OAAO,EAAE,QAAQ;YAHnB,CAKE,oBAAC,SAAD,EAAA,UAAQ,iFAAiF,OAAO,QAAe,CAAA,EAC/G,oBAAC,OAAD;GACE,OAAM;GACE;GACR,SAAS,OAAO,OAAO,GAAG;GAC1B,qBAAoB;GACpB,WAAU;aAEV,oBAAC,QAAD;IACK;IACH,MAAK;IACL,QAAQ;IACR,aAAY;IACZ,OAAO;KACL,WAAW;KACX,iBAAiB;KAClB;IACD,CAAA;GACE,CAAA,CACF;;;;;;;;;;;;;;AAeV,SAAS,iBAAiB,OAAe,OAAuB;CAC9D,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC;CAGzC,MAAM,WAAW,oDAAoD,KACnE,MACD;AACD,KAAI,SACF,QAAO,QAAQ,SAAS,GAAG,IAAI,SAAS,GAAG,IAAI,SAAS,GAAG,IAAI,EAAE;CAInE,IAAI,MAAM,MAAM,QAAQ,MAAM,GAAG;AAGjC,KAAI,IAAI,WAAW,EACjB,OAAM,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI;AAIzD,KAAI,IAAI,WAAW,EACjB,OAAM,IAAI,MAAM,GAAG,EAAE;AAOvB,QAAO,QAJG,OAAO,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,CAI7B,IAHP,OAAO,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,CAGvB,IAFb,OAAO,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,CAEjB,IAAI,EAAE;;;AAIrC,SAAS,IAAI,GAAW;AACtB,QAAO,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI;;;;;;AAOtC,SAAS,gBAAgB,IAAoC;CAC3D,MAAM,IAAI,IAAI,KAAK,GAAG;AACtB,QAAO,GAAG,EAAE,aAAa,CAAC,GAAG,IAAI,EAAE,UAAU,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC;;;;;;;;;AC3e3I,SAAS,UAAU,EAAE,OAAO,OAAO,MAAM,MAAM,YAA6B;AAC1E,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IACE,WAAW,GAAG,oCAAoC,EAChD,cAAc,UACf,CAAC;IACF,OAAO,EAAE,iBAAiB,OAAO;IACjC,CAAA,EACF,oBAAC,QAAD;IAAM,WAAW,GAAG,WAAW,EAAE,cAAc,UAAU,CAAC;cACvD;IACI,CAAA,CACH;MACN,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD;IACE,WAAW,GAAG,oCAAoC,EAChD,cAAc,UACf,CAAC;cAED;IACI,CAAA,EACN,QACC,oBAAC,QAAD;IACE,WAAW,GAAG,uCAAuC,EACnD,cAAc,UACf,CAAC;cAED;IACI,CAAA,CAEL;KACF;;;;;;;AAQV,SAAS,UAAU,EAAE,OAAO,OAAO,MAAM,YAA6B;AACpE,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,oBAAC,QAAD;IACE,WAAW,GAAG,oCAAoC,EAChD,cAAc,UACf,CAAC;IACF,OAAO,EAAE,iBAAiB,OAAO;IACjC,CAAA;GACF,oBAAC,QAAD;IAAM,WAAW,GAAG,WAAW,EAAE,cAAc,UAAU,CAAC;cAAG;IAAY,CAAA;GACzE,oBAAC,QAAD;IAAM,WAAW,GAAG,uBAAuB,EAAE,cAAc,UAAU,CAAC;cACnE;IACI,CAAA;GACH;;;;;;;;;;;;;;;AAgBV,IAAa,cAAc;CACzB;CACA;CACD;;;;;;;;;;;;;;;;;;;;;AC3BD,SAAgB,SAAS,EACvB,SACA,MACA,cAAc,YACd,SAAS,KACT,iBACA,oBACA,YACA,SACA,YACA,aACgB;CAChB,MAAM,UAAU,cAA6B;EAC3C,MAAM,UAAU,aAAa,oBAAoB;EACjD,MAAM,OAAO,aAAa,aAAa,WAAW;EAClD,MAAM,eAAe;GACnB,GAAG;GACH,MAAM;GACN,MAAM,KAAK,KAAK,MAAM,EAAE,KAAK;GAC9B;EACD,MAAM,YAAY;GAChB,GAAG;GACH,MAAM;GACN,WAAW,kBACP,EAAE,YAAY,MAAc,gBAAgB,EAAE,EAAE,GAChD,KAAA;GACL;AAED,SAAO;GACL,MAAM;IAAE,MAAM;IAAI,OAAO;IAAI,KAAK;IAAI,QAAQ;IAAI,cAAc;IAAM;GACtE,SAAS;IACP,SAAS;IACT,aAAa,EAAE,MAAM,UAAU;IAC/B,gBAAgB,sBACX,UACC,OAAO,UAAU,WACb,mBAAmB,MAAM,GACzB,OAAO,MAAM,GACnB,KAAA;IACL;GACD,OAAO,gBAAgB,aAAa,eAAe;GACnD,OAAO,gBAAgB,aAAa,YAAY;GAChD,QAAQ,CACN;IACE,MAAM;IACN,MAAM,KAAK,KAAK,GAAG,OAAO;KACxB,OAAO,EAAE;KACT,WAAW,EAAE,OAAO,EAAE,SAAS,QAAQ,IAAI,QAAQ,SAAS;KAC7D,EAAE;IACJ,CACF;GACF;IACA;EAAC;EAAM;EAAa;EAAiB;EAAoB;EAAW,CAAC;CAExE,MAAM,WAAW,cAEb,aACI,EACE,QAAQ,WAA8B;AACpC,MAAI,OAAO,KAAM,YAAW,OAAO,KAAK;IAE3C,GACD,KAAA,GACN,CAAC,WAAW,CACb;AAED,KAAI,QACF,QACE,oBAAC,OAAD;EACE,aAAU;EACC;EACX,OAAO,EAAE,QAAQ;EACjB,oBAAiB;YAEjB,oBAAC,OAAD,EAAK,WAAU,qDAAsD,CAAA;EACjE,CAAA;AAIV,QACE,oBAAC,OAAD;EACW;EACA;EACD;EACI;EACD;EACD;EACV,CAAA;;;;;;;;;;;;;;;;;;;ACxFN,SAAgB,gBAAgB,EAC9B,SACA,YACA,QACA,cAAc,YACd,UAAU,OACV,SAAS,KACT,iBACA,oBACA,YACA,SACA,aACuB;CACvB,MAAM,UAAU,cAA6B;EAC3C,MAAM,UAAU,aAAa,oBAAoB;EAGjD,MAAM,iBAAiB,UACnB,OAAO,KAAK,OAAO;GACjB,GAAG;GACH,MAAM,EAAE,KAAK,KAAK,GAAG,MAAM;IACzB,MAAM,QAAQ,OAAO,QAClB,KAAK,UAAU,OAAO,MAAM,KAAK,MAAM,IACxC,EACD;AACD,WAAO,UAAU,IAAI,IAAK,IAAI,QAAS;KACvC;GACH,EAAE,GACH;EAEJ,MAAM,OAAO,aAAa,aAAa,WAAW;EAClD,MAAM,eAAe;GACnB,GAAG;GACH,MAAM;GACN,MAAM;GACP;EACD,MAAM,YAAY;GAChB,GAAG;GACH,MAAM;GACN,KAAK,UAAU,MAAM,KAAA;GACrB,WAAW,kBACP,EAAE,YAAY,MAAc,gBAAgB,EAAE,EAAE,GAChD,UACE,EAAE,WAAW,YAAY,GACzB,KAAA;GACP;AAED,SAAO;GACL,QAAQ,EAAE,QAAQ,GAAG;GACrB,MAAM;IAAE,MAAM;IAAI,OAAO;IAAI,KAAK;IAAI,QAAQ;IAAI,cAAc;IAAM;GACtE,SAAS;IACP,SAAS;IACT,aAAa,EAAE,MAAM,UAAU;IAC/B,gBAAgB,sBACX,UACC,OAAO,UAAU,WACb,mBAAmB,MAAM,GACzB,OAAO,MAAM,GACnB,KAAA;IACL;GACD,OAAO,gBAAgB,aAAa,eAAe;GACnD,OAAO,gBAAgB,aAAa,YAAY;GAChD,QAAQ,eAAe,KAAK,GAAG,OAAO;IACpC,MAAM,EAAE;IACR,MAAM;IACN,OAAO;IACP,MAAM,EAAE;IACR,WAAW,EAAE,OAAO,EAAE,SAAS,QAAQ,IAAI,QAAQ,SAAS;IAC7D,EAAE;GACJ;IACA;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,QACF,QACE,oBAAC,OAAD;EAAK,aAAU;EAAkB;EAAW,OAAO,EAAE,QAAQ;YAC3D,oBAAC,OAAD,EAAK,WAAU,qDAAsD,CAAA;EACjE,CAAA;AAIV,QACE,oBAAC,OAAD;EACW;EACA;EACD;EACI;EACD;EACX,CAAA;;;;;;;;;;;;;;;;;;;;;ACzFN,SAAgB,SAAS,EACvB,SACA,MACA,UAAU,OACV,SAAS,KACT,YACA,oBACA,YACA,SACA,YACA,aACgB;CAChB,MAAM,UAAU,cAA6B;EAC3C,MAAM,UAAU,aAAa,oBAAoB;AACjD,SAAO;GACL,QAAQ,aAAa,EAAE,QAAQ,GAAG,GAAG,KAAA;GACrC,SAAS;IACP,SAAS;IACT,gBAAgB,sBACX,UACC,OAAO,UAAU,WACb,mBAAmB,MAAM,GACzB,OAAO,MAAM,GACnB,KAAA;IACL;GACD,QAAQ,CACN;IACE,MAAM;IACN,QAAQ,YAAY,UAAU,CAAC,OAAO,MAAM,GAAG;IAC/C,mBAAmB;IACnB,OAAO;KACL,MAAM,YAAY;KAClB,OAAO,aAAa,YAAY;KAChC,iBAAiB;KACjB,iBAAiB;KAClB;IACD,WAAW;KACT,MAAM,YAAY;KAClB,WAAW,EAAE,OAAO,aAAa,YAAY,WAAW;KACzD;IACD,MAAM,KAAK,KAAK,GAAG,OAAO;KACxB,MAAM,EAAE;KACR,OAAO,EAAE;KACT,WAAW,EAAE,OAAO,EAAE,SAAS,QAAQ,IAAI,QAAQ,SAAS;KAC7D,EAAE;IACJ,CACF;GACF;IACA;EAAC;EAAM;EAAS;EAAY;EAAoB;EAAW,CAAC;AAE/D,KAAI,QACF,QACE,oBAAC,OAAD;EAAK,aAAU;EAAkB;EAAW,OAAO,EAAE,QAAQ;YAC3D,oBAAC,OAAD,EAAK,WAAU,uDAAwD,CAAA;EACnE,CAAA;AAIV,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,YAAY,UAAU;EAAE,OAAO,EAAE,QAAQ;YAA5D,CACE,oBAAC,OAAD;GACW;GACA;GACD;GACI;GACZ,CAAA,EACD,eAAe,KAAA,KAAa,YAAY,UACvC,oBAAC,OAAD;GAAK,WAAU;aACZ;GACG,CAAA,GACJ,KACA;;;;;;;;;;;;;;;;;;;;;;ACnEV,SAAgB,UAAU,EACxB,SACA,QACA,YAAY,QACZ,YACA,SAAS,KACT,WACA,WACA,iBACA,oBACA,YACA,SACA,SACA,aACiB;CACjB,MAAM,UAAU,cAA6B;EAC3C,MAAM,UAAU,aAAa,oBAAoB;AAEjD,SAAO;GACL,MAAM;IAAE,MAAM;IAAI,OAAO;IAAI,KAAK;IAAI,QAAQ;IAAI,cAAc;IAAM;GACtE,QAAQ,OAAO,SAAS,IAAI,EAAE,QAAQ,GAAG,GAAG,KAAA;GAC5C,SAAS;IACP,SAAS;IACT,gBAAgB,sBACX,UACC,OAAO,UAAU,WACb,mBAAmB,MAAM,GACzB,OAAO,MAAM,GACnB,KAAA;IACL;GACD,OACE,cAAc,aACV;IACE,GAAG,aAAa,aAAa,WAAW;IACxC,MAAM;IACN,MAAM;IACN,MAAM;IACN,aAAa;IACd,GACD;IACE,GAAG,aAAa,aAAa,WAAW;IACxC,MAAM;IACN,MAAM;IACP;GACP,OAAO;IACL,GAAG,aAAa,aAAa,WAAW;IACxC,MAAM;IACN,MAAM;IACN,WAAW,kBACP,EAAE,YAAY,MAAc,gBAAgB,EAAE,EAAE,GAChD,KAAA;IACL;GACD,QAAQ,OAAO,KAAK,GAAG,MAAM;IAC3B,MAAM,QAAQ,EAAE,SAAS,QAAQ,IAAI,QAAQ;AAC7C,WAAO;KACL,MAAM,EAAE;KACR,MAAM;KACN,QAAQ;KACR,YAAY;KACZ,OAAO,UAAU,UAAU,KAAA;KAC3B,MAAM,EAAE;KACR,WAAW,EAAE,OAAO;KACpB,WAAW,EAAE,OAAO;KACpB,WAAW;MACT,OAAO;OACL,MAAM;OACN,GAAG;OACH,GAAG;OACH,IAAI;OACJ,IAAI;OACJ,YAAY,CACV;QAAE,QAAQ;QAAG;QAAO,EACpB;QAAE,QAAQ;QAAG,OAAO;QAAiB,CACtC;OACF;MACD,SAAS;MACV;KACF;KACD;GACH;IACA;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,QACF,QACE,oBAAC,OAAD;EAAK,aAAU;EAAkB;EAAW,OAAO,EAAE,QAAQ;YAC3D,oBAAC,OAAD,EAAK,WAAU,qDAAsD,CAAA;EACjE,CAAA;AAIV,QACE,oBAAC,OAAD;EACW;EACA;EACD;EACI;EACD;EACX,CAAA;;;;;;;;;;;;;;;;;;;;ACvHN,SAAgB,aAAa,EAC3B,SACA,QACA,SAAS,KACT,WACA,WACA,iBACA,iBACA,YACA,SACA,aACoB;CACpB,MAAM,UAAU,cAA6B;EAC3C,MAAM,UAAU,aAAa,oBAAoB;AAEjD,SAAO;GACL,MAAM;IAAE,MAAM;IAAI,OAAO;IAAI,KAAK;IAAI,QAAQ;IAAI,cAAc;IAAM;GACtE,QAAQ,OAAO,SAAS,IAAI,EAAE,QAAQ,GAAG,GAAG,KAAA;GAC5C,SAAS,EAAE,SAAS,QAAQ;GAC5B,OAAO;IACL,GAAG,aAAa,aAAa,WAAW;IACxC,MAAM;IACN,MAAM;IACN,WAAW,kBACP,EAAE,YAAY,MAAc,gBAAgB,EAAE,EAAE,GAChD,KAAA;IACL;GACD,OAAO;IACL,GAAG,aAAa,aAAa,WAAW;IACxC,MAAM;IACN,MAAM;IACN,WAAW,kBACP,EAAE,YAAY,MAAc,gBAAgB,EAAE,EAAE,GAChD,KAAA;IACL;GACD,QAAQ,OAAO,KAAK,GAAG,OAAO;IAC5B,MAAM,EAAE;IACR,MAAM;IACN,MAAM,EAAE;IACR,YAAY,EAAE,cAAc;IAC5B,WAAW,EAAE,OAAO,EAAE,SAAS,QAAQ,IAAI,QAAQ,SAAS;IAC7D,EAAE;GACJ;IACA;EACD;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,QACF,QACE,oBAAC,OAAD;EAAK,aAAU;EAAkB;EAAW,OAAO,EAAE,QAAQ;YAC3D,oBAAC,OAAD,EAAK,WAAU,qDAAsD,CAAA;EACjE,CAAA;AAIV,QACE,oBAAC,OAAD;EACW;EACA;EACD;EACI;EACD;EACX,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard-text-kLaMogs3.js","names":[],"sources":["../src/components/clipboard-text/clipboard-text.tsx"],"sourcesContent":["import { Toast } from \"@base-ui/react/toast\";\nimport { Tooltip } from \"@base-ui/react/tooltip\";\nimport { CheckIcon, CopyIcon } from \"@phosphor-icons/react\";\nimport { forwardRef, useCallback, useRef, useState } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { inputVariants } from \"../input\";\n\n// Create a toast manager for anchored \"Copied\" toasts\nconst clipboardToastManager = Toast.createToastManager();\n\n/** ClipboardText size variant definitions mapping sizes to their Tailwind classes. */\nexport const SF_CLIPBOARD_TEXT_VARIANTS = {\n size: {\n sm: {\n classes: \"text-xs\",\n buttonSize: \"sm\" as const,\n description: \"Small clipboard text for compact UIs\",\n },\n base: {\n classes: \"text-sm\",\n buttonSize: \"base\" as const,\n description: \"Default clipboard text size\",\n },\n lg: {\n classes: \"text-sm\",\n buttonSize: \"lg\" as const,\n description: \"Large clipboard text for prominent display\",\n },\n },\n} as const;\n\nexport const SF_CLIPBOARD_TEXT_DEFAULT_VARIANTS = {\n size: \"lg\",\n} as const;\n\nconst clipboardTextAnimations = {\n slide: {\n initial:\n \"pointer-events-none absolute inset-0 flex items-center justify-center opacity-0 translate-y-full\",\n animate: \"translate-y-0 opacity-100\",\n end: \"pointer-events-none absolute inset-0 flex items-center justify-center opacity-0 -translate-y-full\",\n },\n} as const;\n\n// Derived types from SF_CLIPBOARD_TEXT_VARIANTS\nexport type SFClipboardTextSize = keyof typeof SF_CLIPBOARD_TEXT_VARIANTS.size;\n\nexport interface SFClipboardTextVariantsProps {\n /**\n * Size of the clipboard text field.\n * - `\"sm\"` — Small clipboard text for compact UIs\n * - `\"base\"` — Default clipboard text size\n * - `\"lg\"` — Large clipboard text for prominent display\n * @default \"lg\"\n */\n size?: SFClipboardTextSize;\n}\n\nexport function clipboardTextVariants({\n size = SF_CLIPBOARD_TEXT_DEFAULT_VARIANTS.size,\n}: SFClipboardTextVariantsProps = {}) {\n return cn(\n // Base styles\n \"flex items-center overflow-hidden bg-sf-base px-0 font-mono\",\n // Apply size styles from SF_CLIPBOARD_TEXT_VARIANTS\n SF_CLIPBOARD_TEXT_VARIANTS.size[size].classes\n );\n}\n\n// Legacy type alias for backwards compatibility\nexport type ClipboardTextSize = SFClipboardTextSize;\n\n/**\n * ClipboardText component props.\n *\n * @example\n * ```tsx\n * <ClipboardText text=\"sk_live_abc123\" />\n * <ClipboardText text=\"npm install @signalflare-ai/ui\" size=\"sm\" />\n * ```\n */\nexport interface ClipboardTextProps extends SFClipboardTextVariantsProps {\n /** The text to display and copy to clipboard. */\n text: string;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n /** Callback fired after text is copied to clipboard. */\n onCopy?: () => void;\n /**\n * Tooltip config. Shows tooltip on hover, anchored toast on click.\n * @example\n * ```tsx\n * <ClipboardText\n * text=\"abc123\"\n * tooltip={{ text: \"Copy\", copiedText: \"Copied!\", side: \"top\" }}\n * />\n * ```\n */\n tooltip?: {\n /** Text shown in tooltip on hover. @default \"Copy\" */\n text?: string;\n /** Text shown in toast after copying. @default \"Copied\" */\n copiedText?: string;\n /** Tooltip/toast placement. @default \"top\" */\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n };\n /** Accessible labels for i18n. */\n labels?: {\n /** @default \"Copy to clipboard\" */\n copyAction?: string;\n };\n}\n\n/**\n * Anchored toasts viewport - renders \"Copied\" toasts anchored to buttons\n */\nfunction AnchoredToasts() {\n const { toasts } = Toast.useToastManager();\n return (\n <Toast.Viewport className=\"pointer-events-none fixed inset-0 z-50\">\n {toasts.map((toast) => (\n <Toast.Positioner key={toast.id} toast={toast} className=\"absolute\">\n <Toast.Root\n toast={toast}\n className={cn(\n \"flex origin-[var(--transform-origin)] flex-col rounded-md bg-sf-base px-3 py-1.5 text-xs text-sf-default font-sans\",\n \"shadow-lg shadow-sf-tip-shadow outline outline-sf-fill\"\n )}\n >\n <Toast.Description />\n </Toast.Root>\n </Toast.Positioner>\n ))}\n </Toast.Viewport>\n );\n}\n\n/**\n * Internal wrapper that provides Toast context when tooltip is enabled.\n */\nfunction TooltipWrapper({ children }: { children: React.ReactNode }) {\n return (\n <Tooltip.Provider>\n <Toast.Provider toastManager={clipboardToastManager}>\n <AnchoredToasts />\n {children}\n </Toast.Provider>\n </Tooltip.Provider>\n );\n}\n\n/**\n * Read-only text field with a one-click copy-to-clipboard button.\n *\n * @example\n * ```tsx\n * <ClipboardText text=\"0c239dd2\" />\n * ```\n */\nexport const ClipboardText = forwardRef<HTMLDivElement, ClipboardTextProps>(\n (\n {\n text,\n className,\n size = SF_CLIPBOARD_TEXT_DEFAULT_VARIANTS.size,\n onCopy,\n tooltip,\n labels: { copyAction = \"Copy to clipboard\" } = {},\n },\n ref\n ) => {\n const [copied, setCopied] = useState(false);\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const sizeConfig = SF_CLIPBOARD_TEXT_VARIANTS.size[size];\n\n // Destructure tooltip config with defaults\n const {\n text: tooltipText = \"Copy\",\n copiedText = \"Copied\",\n side: tooltipSide = \"top\",\n } = tooltip ?? {};\n\n const copyToClipboard = useCallback(async () => {\n try {\n if (\n typeof navigator !== \"undefined\" &&\n navigator.clipboard &&\n typeof navigator.clipboard.writeText === \"function\"\n ) {\n await navigator.clipboard.writeText(text);\n } else if (typeof document !== \"undefined\") {\n // Fallback for older browsers\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"absolute\";\n textarea.style.left = \"-9999px\";\n document.body.appendChild(textarea);\n const selection = document.getSelection();\n const previousRange = selection?.rangeCount\n ? selection.getRangeAt(0)\n : null;\n textarea.select();\n try {\n document.execCommand(\"copy\");\n } finally {\n document.body.removeChild(textarea);\n if (previousRange) {\n selection?.removeAllRanges();\n selection?.addRange(previousRange);\n }\n }\n }\n\n setCopied(true);\n\n // Show anchored toast if tooltip mode is enabled\n if (tooltip) {\n clipboardToastManager.add({\n description: copiedText,\n positionerProps: {\n anchor: buttonRef.current,\n side: tooltipSide,\n sideOffset: 8,\n },\n timeout: 1500,\n onClose() {\n setCopied(false);\n },\n });\n } else {\n // Reset copied state after delay when no tooltip\n setTimeout(() => setCopied(false), 1500);\n }\n\n onCopy?.();\n } catch (error) {\n console.warn(\"Clipboard copy failed\", error);\n }\n }, [text, onCopy, tooltip, copiedText, tooltipSide]);\n\n const copyButton = (\n <Button\n ref={buttonRef}\n size={sizeConfig.buttonSize}\n variant=\"ghost\"\n className=\"rounded-none border-l! border-sf-line! px-3 relative overflow-hidden transition-all duration-200\"\n onClick={copyToClipboard}\n aria-label={copyAction}\n >\n <span\n className={cn(\n \"flex items-center gap-1 transition-all duration-200\",\n copied\n ? clipboardTextAnimations.slide.animate\n : clipboardTextAnimations.slide.initial\n )}\n >\n <CheckIcon />\n </span>\n <span\n className={cn(\n \"flex items-center justify-center transition-all duration-200\",\n copied\n ? clipboardTextAnimations.slide.end\n : clipboardTextAnimations.slide.animate\n )}\n >\n <CopyIcon />\n </span>\n </Button>\n );\n\n return (\n <div\n ref={ref}\n className={cn(\n inputVariants({ size: sizeConfig.buttonSize }),\n clipboardTextVariants({ size }),\n className\n )}\n >\n <span className=\"grow truncate ps-4 pe-2\">{text}</span>\n {tooltip ? (\n <TooltipWrapper>\n <Tooltip.Root\n disabled={copied}\n onOpenChange={(open, eventDetails) => {\n // Prevent tooltip from closing when button is clicked\n if (eventDetails.reason === \"trigger-press\") {\n eventDetails.cancel();\n }\n }}\n >\n <Tooltip.Trigger render={copyButton} />\n <Tooltip.Portal>\n <Tooltip.Positioner side={tooltipSide} sideOffset={8}>\n <Tooltip.Popup\n className={cn(\n \"flex origin-[var(--transform-origin)] flex-col rounded-md bg-sf-base px-3 py-1.5 text-xs text-sf-default\",\n \"shadow-lg shadow-sf-tip-shadow outline outline-sf-fill\"\n )}\n >\n {tooltipText}\n </Tooltip.Popup>\n </Tooltip.Positioner>\n </Tooltip.Portal>\n </Tooltip.Root>\n </TooltipWrapper>\n ) : (\n copyButton\n )}\n <span className=\"sr-only\" aria-live=\"polite\">\n {copied ? copiedText : \"\"}\n </span>\n </div>\n );\n }\n);\n\nClipboardText.displayName = \"ClipboardText\";\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"command-palette-CuNUyJca.js","names":[],"sources":["../src/components/command-palette/command-palette.tsx"],"sourcesContent":["import { Autocomplete } from \"@base-ui/react/autocomplete\";\nimport { Dialog as DialogBase } from \"@base-ui/react/dialog\";\nimport {\n MagnifyingGlassIcon,\n ArrowRightIcon,\n ArrowSquareOutIcon,\n CaretRightIcon,\n} from \"@phosphor-icons/react\";\nimport {\n useRef,\n useCallback,\n createContext,\n useContext,\n forwardRef,\n type CSSProperties,\n} from \"react\";\n\nimport { cn } from \"../../utils\";\nimport { Loader } from \"../loader\";\nimport { Surface } from \"../surface\";\nimport type {\n HighlightRange,\n CommandPaletteRootProps,\n CommandPaletteListProps,\n CommandPaletteGroupProps,\n CommandPaletteGroupLabelProps,\n CommandPaletteItemProps,\n CommandPaletteEmptyProps,\n CommandPaletteLoadingProps,\n CommandPaletteFooterProps,\n CommandPaletteResultItemProps,\n} from \"./types\";\n\n/**\n * CommandPalette - A composable command palette component for SignalFlare\n *\n * Uses @base-ui/react/autocomplete primitives for accessible command palette functionality.\n *\n * Keyboard navigation is built-in:\n * - Arrow keys (up/down) move highlight between items\n * - Enter selects the highlighted item (calls onSelect with newTab: false)\n * - Cmd+Enter (Mac) / Ctrl+Enter (Windows/Linux) selects with newTab: true\n * - First item is auto-highlighted when results change\n * - Escape closes the dialog\n *\n * Usage:\n * ```tsx\n * <CommandPalette.Root\n * open={open}\n * onOpenChange={setOpen}\n * items={results}\n * value={searchTerm}\n * onValueChange={setSearchTerm}\n * itemToStringValue={(group) => group.label}\n * onSelect={(item, { newTab }) => handleSelect(item, newTab)}\n * getSelectableItems={(groups) => groups.flatMap(g => g.items)}\n * >\n * <CommandPalette.Input placeholder=\"Search...\" />\n * <CommandPalette.List>\n * <Autocomplete.List>\n * {(group) => (\n * <CommandPalette.Group items={group.items}>\n * <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>\n * <Autocomplete.Collection>\n * {(item) => (\n * <CommandPalette.Item value={item} onClick={(e) => handleSelect(item, e.metaKey || e.ctrlKey)}>\n * {item.title}\n * </CommandPalette.Item>\n * )}\n * </Autocomplete.Collection>\n * </CommandPalette.Group>\n * )}\n * </Autocomplete.List>\n * <CommandPalette.Empty>No results found</CommandPalette.Empty>\n * </CommandPalette.List>\n * <CommandPalette.Footer />\n * </CommandPalette.Root>\n * ```\n */\n\n/**\n * Dialog context for passing close handler to children\n */\ninterface DialogContextValue {\n onClose?: () => void;\n}\n\nconst DialogContext = createContext<DialogContextValue>({});\n\n/**\n * Props for the Dialog component\n */\ninterface DialogProps {\n /** Whether the dialog is open */\n open: boolean;\n /** Callback when the open state changes */\n onOpenChange: (open: boolean) => void;\n /**\n * Optional callback when backdrop is clicked.\n * Receives the mouse event for position tracking (e.g., for ripple effects).\n * If not provided, backdrop click calls onOpenChange(false).\n */\n onBackdropClick?: (e: React.MouseEvent) => void;\n /** Child content - typically one or more Panel components */\n children: React.ReactNode;\n}\n\n/**\n * Dialog component - Modal wrapper for command palette content.\n *\n * Use this when you need a dialog that can swap between different Panel contents\n * without re-mounting (e.g., drill-down navigation).\n *\n * @example\n * ```tsx\n * <CommandPalette.Dialog open={open} onOpenChange={setOpen}>\n * {showDrillDown ? (\n * <ZonePicker />\n * ) : (\n * <CommandPalette.Panel items={results} ...>\n * ...\n * </CommandPalette.Panel>\n * )}\n * </CommandPalette.Dialog>\n * ```\n */\nfunction Dialog({\n open,\n onOpenChange,\n onBackdropClick,\n children,\n}: DialogProps) {\n const handleBackdropClick = (e: React.MouseEvent) => {\n if (onBackdropClick) {\n onBackdropClick(e);\n } else {\n onOpenChange(false);\n }\n };\n\n const handleClose = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n return (\n <DialogBase.Root open={open} onOpenChange={onOpenChange} modal>\n <DialogBase.Portal>\n <DialogBase.Backdrop\n className=\"fixed inset-0 bg-sf-overlay opacity-80 transition-all duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\"\n onClick={handleBackdropClick}\n />\n <Surface\n as={DialogBase.Popup}\n className={cn(\n \"fixed top-[10vh] left-1/2 w-full max-w-2xl -translate-x-1/2\",\n \"overflow-hidden rounded-lg\",\n \"duration-150 data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[starting-style]:scale-90 data-[starting-style]:opacity-0\"\n )}\n style={\n {\n transitionProperty: \"scale, opacity\",\n transitionTimingFunction:\n \"var(--default-transition-timing-function)\",\n } as CSSProperties\n }\n >\n <DialogContext.Provider value={{ onClose: handleClose }}>\n {children}\n </DialogContext.Provider>\n </Surface>\n </DialogBase.Portal>\n </DialogBase.Root>\n );\n}\n\n/**\n * Root component - Dialog + Panel combined for simple use cases.\n *\n * For cases where you need to swap content inside the dialog without\n * re-mounting (e.g., drill-down navigation), use Dialog + Panel separately.\n *\n * Keyboard navigation is always enabled:\n * - Arrow keys (up/down) move highlight\n * - Enter selects highlighted item\n * - Cmd/Ctrl+Enter selects with newTab: true\n * - First item is auto-highlighted when results change\n */\nfunction Root<TGroup, TItem = TGroup>({\n open,\n onOpenChange,\n onBackdropClick,\n children,\n items,\n value,\n onValueChange,\n onItemHighlighted,\n itemToStringValue,\n filter,\n onSelect,\n getSelectableItems,\n}: CommandPaletteRootProps<TGroup, TItem>) {\n return (\n <Dialog\n open={open}\n onOpenChange={onOpenChange}\n onBackdropClick={onBackdropClick}\n >\n <Panel\n items={items}\n value={value}\n onValueChange={onValueChange}\n onItemHighlighted={onItemHighlighted}\n itemToStringValue={itemToStringValue}\n filter={filter}\n open={open}\n onSelect={onSelect}\n getSelectableItems={getSelectableItems}\n >\n {children}\n </Panel>\n </Dialog>\n );\n}\n\n/**\n * InputHeader component - Internal styled container for search input.\n */\nfunction InputHeader({\n children,\n leading,\n trailing,\n}: {\n children: React.ReactNode;\n leading?: React.ReactNode;\n trailing?: React.ReactNode;\n}) {\n return (\n <div className=\"flex items-center gap-3 bg-sf-elevated px-4 py-3\">\n {leading ?? (\n <MagnifyingGlassIcon className=\"h-4 w-4 text-sf-subtle\" weight=\"bold\" />\n )}\n {children}\n {trailing}\n </div>\n );\n}\n\n/**\n * List component - Scrollable results container\n *\n * Wrapper div with proper styling, contains Autocomplete.List internally.\n * Supports ref forwarding for scroll control.\n */\nconst List = forwardRef<\n HTMLDivElement,\n CommandPaletteListProps & { className?: string }\n>(function List({ children, className }, ref) {\n return (\n <div\n ref={ref}\n className={cn(\n \"z-10 min-h-0 flex-1 overflow-y-auto rounded-b-lg bg-sf-elevated px-2 py-2 ring-1 ring-sf-line\",\n className\n )}\n >\n {children}\n </div>\n );\n});\n\nList.displayName = \"CommandPalette.List\";\n\n/**\n * Group component - Category grouping\n *\n * Re-export of Autocomplete.Group with default styling.\n */\nfunction Group({\n children,\n className,\n ...props\n}: CommandPaletteGroupProps & {\n className?: string;\n items?: unknown[];\n}) {\n return (\n <Autocomplete.Group className={cn(\"space-y-0.5\", className)} {...props}>\n {children}\n </Autocomplete.Group>\n );\n}\n\n/**\n * GroupLabel component - Section header text\n *\n * Re-export of Autocomplete.GroupLabel with styling matching SectionHeader.\n */\nfunction GroupLabel({\n children,\n className,\n}: CommandPaletteGroupLabelProps & { className?: string }) {\n return (\n <Autocomplete.GroupLabel\n className={cn(\n \"mb-2 px-2 pt-1 text-xs font-semibold text-sf-strong\",\n className\n )}\n >\n {children}\n </Autocomplete.GroupLabel>\n );\n}\n\n/**\n * Item component - Individual result item\n *\n * Re-export of Autocomplete.Item with styling matching SearchResultItem.\n * Note: Use onClick for selection handling, matching the existing CommandPalette pattern.\n */\nfunction Item<T>({\n value,\n disabled,\n children,\n className,\n onClick,\n}: CommandPaletteItemProps<T> & {\n className?: string;\n}) {\n return (\n <Autocomplete.Item\n value={value}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n \"group flex w-full items-center gap-3 px-2 py-1.5 text-left transition-colors\",\n \"cursor-pointer data-[highlighted]:bg-sf-overlay\",\n \"rounded-lg\",\n disabled && \"cursor-default opacity-50\",\n className\n )}\n >\n {children}\n </Autocomplete.Item>\n );\n}\n\n/**\n * Empty component - Empty state when no results\n *\n * Re-export of Autocomplete.Empty with default styling.\n */\nfunction Empty({ children }: CommandPaletteEmptyProps) {\n return (\n <Autocomplete.Empty>\n <div className=\"p-8 text-center\">\n <p className=\"text-sf-strong\">{children ?? \"No results found\"}</p>\n </div>\n </Autocomplete.Empty>\n );\n}\n\n/**\n * Loading component - Loading spinner state\n *\n * Centered loading spinner using SignalFlare Loader.\n */\nfunction Loading({ children }: CommandPaletteLoadingProps) {\n return (\n <div className=\"flex items-center justify-center p-8\">\n {children ?? <Loader size={24} />}\n </div>\n );\n}\n\n/**\n * Footer component - Styled container for keyboard hints or other footer content.\n *\n * Children are required - this is just a styled container.\n * Consumers should provide their own keyboard hints with proper i18n.\n */\nfunction Footer({ children }: CommandPaletteFooterProps) {\n return (\n <div className=\"flex items-center justify-between rounded-b-lg bg-sf-elevated px-4 py-3 text-xs text-sf-strong\">\n {children}\n </div>\n );\n}\n\n/**\n * HighlightedText - Renders text with highlighted portions based on match indices.\n * Highlighted text is shown with a background color to indicate matches.\n */\nfunction HighlightedText({\n text,\n highlights,\n className,\n}: {\n text: string;\n highlights?: HighlightRange[];\n className?: string;\n}) {\n if (!highlights || highlights.length === 0) {\n return <span className={className}>{text}</span>;\n }\n\n // Sort highlights by start index and merge overlapping ranges\n const sortedHighlights = [...highlights].sort((a, b) => a[0] - b[0]);\n const mergedHighlights: HighlightRange[] = [];\n\n for (const range of sortedHighlights) {\n const last = mergedHighlights[mergedHighlights.length - 1];\n if (last && range[0] <= last[1] + 1) {\n // Merge overlapping or adjacent ranges\n last[1] = Math.max(last[1], range[1]);\n } else {\n mergedHighlights.push([...range]);\n }\n }\n\n const parts: React.ReactNode[] = [];\n let lastIndex = 0;\n\n mergedHighlights.forEach((range, i) => {\n const [start, end] = range;\n\n // Add non-highlighted text before this match\n if (start > lastIndex) {\n parts.push(<span key={`text-${i}`}>{text.slice(lastIndex, start)}</span>);\n }\n\n // Add highlighted text (end index is inclusive)\n parts.push(\n <mark\n key={`highlight-${i}`}\n className=\"rounded-sm bg-sf-warning/50 text-sf-default\"\n >\n {text.slice(start, end + 1)}\n </mark>\n );\n\n lastIndex = end + 1;\n });\n\n // Add remaining non-highlighted text\n if (lastIndex < text.length) {\n parts.push(<span key=\"text-end\">{text.slice(lastIndex)}</span>);\n }\n\n return <span className={className}>{parts}</span>;\n}\n\n/**\n * ResultItem - Rich item component with breadcrumbs, highlights, icons, and external indicators.\n *\n * Use this for search result items that need breadcrumb navigation, text highlighting,\n * or external link indicators. For simple items, use Item instead.\n */\nfunction ResultItem<T>({\n title,\n breadcrumbs,\n titleHighlights,\n breadcrumbHighlights,\n description,\n icon,\n value,\n onClick,\n showArrow = true,\n external = false,\n nonInteractive = false,\n}: CommandPaletteResultItemProps<T>) {\n return (\n <Autocomplete.Item\n value={value}\n onClick={nonInteractive ? undefined : (e: React.MouseEvent) => onClick(e)}\n className={cn(\n \"group flex w-full items-center gap-3 rounded-lg px-2 py-1.5 text-left transition-colors\",\n nonInteractive\n ? \"cursor-default\"\n : \"cursor-pointer data-[highlighted]:bg-sf-overlay\"\n )}\n >\n {icon && (\n <div className=\"flex flex-shrink-0 items-center text-sf-subtle\">\n {icon}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 truncate\">\n {breadcrumbs?.map((crumb, index) => (\n <span key={index} className=\"flex items-center gap-2\">\n <HighlightedText\n text={crumb}\n highlights={breadcrumbHighlights?.[index]}\n className=\"text-base text-sf-default\"\n />\n <CaretRightIcon\n className=\"h-3 w-3 flex-shrink-0 text-sf-subtle\"\n weight=\"bold\"\n />\n </span>\n ))}\n <HighlightedText\n text={title}\n highlights={titleHighlights}\n className=\"text-base text-sf-default\"\n />\n {external && (\n <ArrowSquareOutIcon className=\"h-3.5 w-3.5 flex-shrink-0 text-sf-subtle\" />\n )}\n {description && (\n <>\n <span className=\"text-sf-strong\">—</span>\n <span className=\"truncate text-sm text-sf-strong\">\n {description}\n </span>\n </>\n )}\n </div>\n </div>\n {showArrow && !external && !nonInteractive && (\n <ArrowRightIcon className=\"h-4 w-4 flex-shrink-0 text-sf-subtle opacity-0 transition-opacity group-data-[highlighted]:opacity-100\" />\n )}\n </Autocomplete.Item>\n );\n}\n\n/**\n * Container component - Internal styled wrapper.\n */\nfunction Container({\n children,\n className,\n}: {\n children: React.ReactNode;\n className?: string;\n}) {\n return (\n <div\n className={cn(\n \"flex max-h-[60vh] flex-col overflow-hidden rounded-lg bg-sf-elevated\",\n className\n )}\n >\n {children}\n </div>\n );\n}\n\n/**\n * Panel context for passing handlers from Panel to children\n */\ninterface PanelContextValue {\n onInputKeyDown?: (e: React.KeyboardEvent) => void;\n}\n\nconst PanelContext = createContext<PanelContextValue>({});\n\n/**\n * Props for the Panel component\n */\ninterface PanelProps<TGroup, TItem = TGroup> {\n /** Child components (Input, List, Footer, etc.) */\n children: React.ReactNode;\n /** Items for the autocomplete */\n items: TGroup[];\n /** Controlled input value */\n value?: string;\n /** Callback when input value changes */\n onValueChange?: (value: string) => void;\n /** Callback when an item is highlighted */\n onItemHighlighted?: (\n item: TGroup | undefined,\n details: { reason: string; event: Event; index: number }\n ) => void;\n /** Convert item to string for accessibility */\n itemToStringValue?: (item: TGroup) => string;\n /** Custom filter function */\n filter?: (item: TGroup, query: string) => boolean;\n /** Whether the panel is active/open (for autocomplete state) */\n open?: boolean;\n /** Optional className for the container */\n className?: string;\n /**\n * Callback when an item is selected via Cmd/Ctrl+Enter.\n * Requires getSelectableItems to be provided.\n */\n onSelect?: (item: TItem, options: { newTab: boolean }) => void;\n /**\n * Function to get flat list of selectable items from groups.\n * Required when items are grouped and onSelect is used.\n */\n getSelectableItems?: (items: TGroup[]) => TItem[];\n}\n\n/**\n * Panel component - Command palette without dialog wrapper.\n *\n * Use this when you need to render command palette content inside an existing dialog\n * (e.g., for drill-down navigation where the dialog stays open but content changes).\n *\n * Combines Container + Autocomplete functionality with a clean API.\n *\n * @example\n * ```tsx\n * <DialogRoot open={open} onOpenChange={setOpen}>\n * <Dialog>\n * <CommandPalette.Panel\n * items={results}\n * value={searchTerm}\n * onValueChange={setSearchTerm}\n * itemToStringValue={(group) => group.label}\n * >\n * <CommandPalette.Input placeholder=\"Search...\" />\n * <CommandPalette.List>\n * <CommandPalette.Results>\n * {(group) => (\n * <CommandPalette.Group items={group.items}>\n * <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>\n * <CommandPalette.Items>\n * {(item) => <CommandPalette.ResultItem ... />}\n * </CommandPalette.Items>\n * </CommandPalette.Group>\n * )}\n * </CommandPalette.Results>\n * <CommandPalette.Empty>No results</CommandPalette.Empty>\n * </CommandPalette.List>\n * <CommandPalette.Footer>...</CommandPalette.Footer>\n * </CommandPalette.Panel>\n * </Dialog>\n * </DialogRoot>\n * ```\n */\nconst defaultFilter = () => true;\n\nfunction Panel<TGroup, TItem = TGroup>({\n children,\n items,\n value,\n onValueChange,\n onItemHighlighted,\n itemToStringValue,\n filter = defaultFilter,\n open = true,\n className,\n onSelect,\n getSelectableItems,\n}: PanelProps<TGroup, TItem>) {\n const highlightedIndexRef = useRef<number>(-1);\n\n const handleItemHighlighted = useCallback(\n (\n item: TGroup | undefined,\n details: { reason: string; event: Event; index: number }\n ) => {\n highlightedIndexRef.current = details.index;\n onItemHighlighted?.(item, details);\n },\n [onItemHighlighted]\n );\n\n // Handle Cmd/Ctrl+Enter for new tab selection\n const handleInputKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n const isEnter = e.key === \"Enter\";\n const withModifier = e.metaKey || e.ctrlKey;\n\n if (isEnter && withModifier && onSelect && getSelectableItems) {\n const selectableItems = getSelectableItems(items);\n const highlightedItem = selectableItems[highlightedIndexRef.current];\n\n if (highlightedItem !== undefined) {\n e.preventDefault();\n onSelect(highlightedItem, { newTab: true });\n }\n }\n },\n [items, onSelect, getSelectableItems]\n );\n\n return (\n <Container className={className}>\n <Autocomplete.Root\n items={items}\n value={value}\n onValueChange={onValueChange}\n onItemHighlighted={handleItemHighlighted}\n itemToStringValue={itemToStringValue}\n filter={filter}\n autoHighlight=\"always\"\n keepHighlight\n open={open}\n >\n <PanelContext.Provider value={{ onInputKeyDown: handleInputKeyDown }}>\n {children}\n </PanelContext.Provider>\n </Autocomplete.Root>\n </Container>\n );\n}\n\n/**\n * PanelInput component - Input that works inside Panel.\n *\n * Similar to Input but designed for use with Panel instead of Root.\n * Automatically wires up Cmd/Ctrl+Enter handling from Panel.\n */\nfunction PanelInput({\n autoFocus = true,\n placeholder,\n className,\n onKeyDown: onKeyDownProp,\n leading,\n trailing,\n ...props\n}: {\n autoFocus?: boolean;\n placeholder?: string;\n className?: string;\n onKeyDown?: (e: React.KeyboardEvent) => void;\n /** Optional leading content (e.g., back button) */\n leading?: React.ReactNode;\n /** Optional trailing content (e.g., Esc button) */\n trailing?: React.ReactNode;\n}) {\n const { onInputKeyDown } = useContext(PanelContext);\n const { onClose } = useContext(DialogContext);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n // Let consumer handle first (e.g., for custom Escape/Backspace behavior)\n onKeyDownProp?.(e);\n if (e.defaultPrevented) return;\n\n // Handle Escape to close the dialog\n if (e.key === \"Escape\" && onClose) {\n e.preventDefault();\n onClose();\n return;\n }\n onInputKeyDown?.(e);\n },\n [onInputKeyDown, onKeyDownProp, onClose]\n );\n\n return (\n <InputHeader leading={leading} trailing={trailing}>\n <Autocomplete.Input\n placeholder={placeholder}\n className={cn(\n \"flex-1 border-none bg-transparent text-base placeholder:text-sf-subtle\",\n \"outline-none focus-visible:outline-none\",\n className\n )}\n onKeyDown={handleKeyDown}\n // oxlint-disable-next-line no-autofocus -- Command palette input should autofocus for keyboard-driven UX\n autoFocus={autoFocus}\n {...props}\n />\n </InputHeader>\n );\n}\n\n/**\n * Render prop iterators - wrap base-ui primitives with cleaner names.\n */\n\n/**\n * Results component - Render prop iterator for groups.\n *\n * Wraps Autocomplete.List with default spacing between groups.\n */\nfunction Results({\n children,\n className,\n}: React.ComponentProps<typeof Autocomplete.List> & { className?: string }) {\n return (\n <Autocomplete.List className={cn(\"space-y-3\", className)}>\n {children}\n </Autocomplete.List>\n );\n}\n\nconst Items = Autocomplete.Collection;\n\n/** CommandPalette variant definitions (no user-facing variants; structure reserved for future use). */\nexport const SF_COMMAND_PALETTE_VARIANTS = {} as const;\n\nexport const SF_COMMAND_PALETTE_DEFAULT_VARIANTS = {} as const;\n\n/**\n * CommandPalette — accessible command palette / spotlight search overlay.\n *\n * Compound component: `CommandPalette.Root` (or `.Dialog` + `.Panel`),\n * `.Input`, `.List`, `.Results`, `.Items`, `.Group`, `.GroupLabel`,\n * `.Item`, `.ResultItem`, `.HighlightedText`, `.Empty`, `.Loading`, `.Footer`.\n *\n * Built on `@base-ui/react/autocomplete` + `@base-ui/react/dialog`.\n *\n * @example\n * ```tsx\n * <CommandPalette.Root\n * open={open}\n * onOpenChange={setOpen}\n * items={results}\n * value={query}\n * onValueChange={setQuery}\n * itemToStringValue={(g) => g.label}\n * onSelect={(item, { newTab }) => navigate(item, newTab)}\n * getSelectableItems={(groups) => groups.flatMap((g) => g.items)}\n * >\n * <CommandPalette.Input placeholder=\"Search…\" />\n * <CommandPalette.List>\n * <CommandPalette.Results>\n * {(group) => (\n * <CommandPalette.Group items={group.items}>\n * <CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>\n * <CommandPalette.Items>\n * {(item) => (\n * <CommandPalette.ResultItem title={item.title} value={item} onClick={…} />\n * )}\n * </CommandPalette.Items>\n * </CommandPalette.Group>\n * )}\n * </CommandPalette.Results>\n * <CommandPalette.Empty>No results found</CommandPalette.Empty>\n * </CommandPalette.List>\n * <CommandPalette.Footer>…keyboard hints…</CommandPalette.Footer>\n * </CommandPalette.Root>\n * ```\n */\nexport const CommandPalette = {\n /** Modal dialog wrapper - use with Panel for content that can swap */\n Dialog,\n /** Dialog + Panel combined - for simple single-view command palettes */\n Root,\n /** Autocomplete panel without dialog - use inside Dialog for swappable content */\n Panel,\n /** Input for use inside Panel */\n Input: PanelInput,\n /** Scrollable results container */\n List,\n /** Category grouping */\n Group,\n /** Section header text */\n GroupLabel,\n /** Basic item */\n Item,\n /** Rich item with breadcrumbs, highlights, icons */\n ResultItem,\n /** Text with highlighted portions */\n HighlightedText,\n /** Empty state */\n Empty,\n /** Loading state */\n Loading,\n /** Footer for keyboard hints */\n Footer,\n /** Render prop iterator for groups */\n Results,\n /** Render prop iterator for items within a group */\n Items,\n};\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-DGHmU0w3.js","names":[],"sources":["../src/components/data-grid/data-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n CaretDownIcon,\n CaretUpIcon,\n CaretUpDownIcon,\n ColumnsIcon,\n FadersHorizontalIcon,\n} from \"@phosphor-icons/react\";\nimport {\n useReactTable,\n getCoreRowModel,\n getSortedRowModel,\n getPaginationRowModel,\n getFilteredRowModel,\n flexRender,\n type SortingState,\n type VisibilityState,\n type RowSelectionState,\n type PaginationState,\n} from \"@tanstack/react-table\";\nimport {\n createContext,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../button\";\nimport { Checkbox } from \"../checkbox\";\nimport { DropdownMenu } from \"../dropdown\";\nimport { Empty } from \"../empty\";\nimport { Filters } from \"../filters\";\nimport { Loader } from \"../loader\";\nimport { Pagination } from \"../pagination\";\nimport { Table } from \"../table\";\nimport type {\n DataGridProps,\n DataGridColumn,\n DataGridContextValue,\n DataGridToolbarProps,\n DataGridContentProps,\n DataGridPaginationProps,\n DataGridColumnToggleProps,\n DataGridEmptyProps,\n} from \"./types\";\n\n// ============================================================================\n// Variants\n// ============================================================================\n\n/** DataGrid layout variant definitions */\nexport const SF_DATA_GRID_VARIANTS = {\n layout: {\n auto: {\n classes: \"\",\n description: \"Auto column sizing - columns resize based on content\",\n },\n fixed: {\n classes: \"table-fixed\",\n description: \"Fixed column sizing - columns have equal width\",\n },\n },\n} as const;\n\n/** Default variants for DataGrid */\nexport const SF_DATA_GRID_DEFAULT_VARIANTS = {\n layout: \"auto\",\n} as const;\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst DataGridContext = createContext<DataGridContextValue | null>(null);\n\nfunction useDataGridContext<TData>() {\n const context = useContext(DataGridContext);\n if (!context) {\n throw new Error(\n \"DataGrid compound components must be used within a DataGrid\"\n );\n }\n return context as DataGridContextValue<TData>;\n}\n\n// ============================================================================\n// Sort Icon Component\n// ============================================================================\n\nfunction SortIcon({ sorted }: { sorted: false | \"asc\" | \"desc\" }) {\n if (sorted === \"asc\") {\n return <CaretUpIcon className=\"size-3.5 text-sf-brand\" />;\n }\n if (sorted === \"desc\") {\n return <CaretDownIcon className=\"size-3.5 text-sf-brand\" />;\n }\n return <CaretUpDownIcon className=\"size-3.5 text-sf-subtle\" />;\n}\n\n// ============================================================================\n// DataGrid Toolbar\n// ============================================================================\n\n/**\n * Toolbar component for DataGrid.\n * Shows filters, column visibility toggle, and custom actions.\n */\nfunction DataGridToolbar({ className, children }: DataGridToolbarProps) {\n return (\n <div\n className={cn(\n \"flex items-center gap-2 border-b border-sf-line p-3\",\n className\n )}\n >\n {children}\n </div>\n );\n}\n\nDataGridToolbar.displayName = \"DataGrid.Toolbar\";\n\n// ============================================================================\n// DataGrid Column Toggle\n// ============================================================================\n\n/**\n * Column visibility toggle dropdown.\n * Shows checkboxes for each hideable column.\n */\nfunction DataGridColumnToggle({\n className,\n trigger,\n label = \"Columns\",\n}: DataGridColumnToggleProps) {\n const { table } = useDataGridContext();\n\n const hideableColumns = useMemo(() => {\n return table\n .getAllColumns()\n .filter(\n (col) =>\n col.getCanHide() &&\n (col.columnDef.enableHiding !== false || col.getCanHide())\n );\n }, [table]);\n\n if (hideableColumns.length === 0) return null;\n\n return (\n <DropdownMenu>\n <DropdownMenu.Trigger>\n {trigger ?? (\n <Button variant=\"secondary\" size=\"sm\" className={className}>\n <ColumnsIcon className=\"mr-1.5 size-4\" />\n {label}\n </Button>\n )}\n </DropdownMenu.Trigger>\n <DropdownMenu.Content className=\"w-48\">\n <div className=\"flex flex-col gap-1 p-1\">\n {hideableColumns.map((column) => (\n <label\n key={column.id}\n className=\"flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-sf-tint\"\n >\n <Checkbox\n checked={column.getIsVisible()}\n onCheckedChange={(checked) =>\n column.toggleVisibility(!!checked)\n }\n />\n <span className=\"flex-1\">\n {typeof column.columnDef.header === \"string\"\n ? column.columnDef.header\n : column.id}\n </span>\n </label>\n ))}\n {hideableColumns.length > 0 && (\n <>\n <div className=\"my-1 h-px bg-sf-line\" />\n <DropdownMenu.Item\n onClick={() =>\n hideableColumns.forEach((col) => col.toggleVisibility(true))\n }\n >\n Show all\n </DropdownMenu.Item>\n <DropdownMenu.Item\n onClick={() =>\n hideableColumns.forEach((col) => col.toggleVisibility(false))\n }\n >\n Hide all\n </DropdownMenu.Item>\n </>\n )}\n </div>\n </DropdownMenu.Content>\n </DropdownMenu>\n );\n}\n\nDataGridColumnToggle.displayName = \"DataGrid.ColumnToggle\";\n\n// ============================================================================\n// DataGrid Content\n// ============================================================================\n\n/**\n * Main table content component.\n * Renders the table with headers, rows, and cells.\n */\nfunction DataGridContent({ className }: DataGridContentProps) {\n const { table, loading, loadingRows, enableColumnResizing } =\n useDataGridContext();\n\n if (loading) {\n return (\n <div className=\"p-4\">\n <div className=\"space-y-2\">\n {Array.from({ length: loadingRows ?? 5 }).map((_, i) => (\n <Loader key={i} className=\"h-10 w-full\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <Table className={className}>\n <Table.Header>\n {table.getHeaderGroups().map((headerGroup) => (\n <Table.Row key={headerGroup.id}>\n {headerGroup.headers.map((header) => {\n const canSort = header.column.getCanSort();\n const sorted = header.column.getIsSorted();\n const canResize =\n enableColumnResizing && header.column.getCanResize();\n const isResizing = header.column.getIsResizing();\n\n return (\n <Table.Head\n key={header.id}\n className={cn(\n \"bg-sf-base relative group\",\n canSort && \"cursor-pointer select-none hover:bg-sf-overlay\",\n (\n header.column.columnDef as unknown as Record<\n string,\n unknown\n >\n ).headerClassName as string\n )}\n style={canResize ? { width: header.getSize() } : undefined}\n onClick={\n canSort ? () => header.column.toggleSorting() : undefined\n }\n >\n <div className=\"flex items-center gap-1.5\">\n {header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext()\n )}\n {canSort && <SortIcon sorted={sorted} />}\n </div>\n {canResize && (\n <Table.ResizeHandle\n onMouseDown={header.getResizeHandler()}\n onTouchStart={header.getResizeHandler()}\n onDoubleClick={() => header.column.resetSize()}\n className={cn(isResizing && \"visible bg-sf-brand/20\")}\n />\n )}\n </Table.Head>\n );\n })}\n </Table.Row>\n ))}\n </Table.Header>\n <Table.Body>\n {table.getRowModel().rows.map((row) => (\n <Table.Row\n key={row.id}\n variant={row.getIsSelected() ? \"selected\" : \"default\"}\n className={cn(\n row.getIsSelected() && \"bg-sf-tint\",\n \"hover:bg-sf-overlay\"\n )}\n >\n {row.getVisibleCells().map((cell) => (\n <Table.Cell\n key={cell.id}\n className={\n (cell.column.columnDef as unknown as Record<string, unknown>)\n .cellClassName as string\n }\n style={\n enableColumnResizing\n ? { width: cell.column.getSize() }\n : undefined\n }\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </Table.Cell>\n ))}\n </Table.Row>\n ))}\n </Table.Body>\n </Table>\n );\n}\n\nDataGridContent.displayName = \"DataGrid.Content\";\n\n// ============================================================================\n// DataGrid Pagination\n// ============================================================================\n\n/**\n * Pagination controls for DataGrid.\n * Wraps the Pagination component with table state.\n */\nfunction DataGridPagination({ className }: DataGridPaginationProps) {\n const {\n table,\n pageSize: _pageSize,\n pageIndex: _pageIndex,\n totalCount,\n manualPagination,\n } = useDataGridContext();\n\n // Get pagination state\n const paginationState = table.getState().pagination;\n const currentPageIndex = paginationState.pageIndex;\n const currentPageSize = paginationState.pageSize;\n\n // Calculate row range\n const startRow = currentPageIndex * currentPageSize + 1;\n const endRow = Math.min(\n (currentPageIndex + 1) * currentPageSize,\n manualPagination ? totalCount : table.getCoreRowModel().rows.length\n );\n\n if (table.getPageCount() <= 1 && !manualPagination) return null;\n\n return (\n <div\n className={cn(\n \"flex items-center justify-between border-t border-sf-line p-3\",\n className\n )}\n >\n <div className=\"text-sm text-sf-subtle\">\n Showing {startRow}–{endRow} of{\" \"}\n {manualPagination ? totalCount : table.getCoreRowModel().rows.length}\n </div>\n <Pagination\n page={currentPageIndex + 1}\n setPage={(page) => table.setPageIndex(page - 1)}\n perPage={currentPageSize}\n totalCount={\n manualPagination ? totalCount : table.getCoreRowModel().rows.length\n }\n text={({ pageShowingRange, totalCount: total }) =>\n `Showing ${pageShowingRange} of ${total}`\n }\n />\n </div>\n );\n}\n\nDataGridPagination.displayName = \"DataGrid.Pagination\";\n\n// ============================================================================\n// DataGrid Empty\n// ============================================================================\n\n/**\n * Empty state component for DataGrid.\n * Shown when there are no rows to display.\n */\nfunction DataGridEmpty({\n className,\n children,\n title = \"No results\",\n description = \"No data to display.\",\n}: DataGridEmptyProps) {\n const { table } = useDataGridContext();\n\n // Only show if there are no rows\n if (table.getCoreRowModel().rows.length > 0) return null;\n\n return (\n <div className={cn(\"p-8\", className)}>\n {children ?? (\n <Empty\n icon={<FadersHorizontalIcon className=\"size-10 text-sf-subtle\" />}\n title={title}\n description={description}\n />\n )}\n </div>\n );\n}\n\nDataGridEmpty.displayName = \"DataGrid.Empty\";\n\n// ============================================================================\n// DataGrid Root\n// ============================================================================\n\n/**\n * DataGrid root component.\n * Sets up the react-table instance and provides context to child components.\n */\nfunction DataGridRoot<TData>({\n data,\n columns,\n sorting: controlledSorting,\n onSortingChange,\n enableSorting = true,\n columnVisibility: controlledColumnVisibility,\n onColumnVisibilityChange,\n enableColumnVisibility = true,\n rowSelection: controlledRowSelection,\n onRowSelectionChange,\n enableRowSelection,\n pageSize = 10,\n pageIndex = 0,\n onPaginationChange,\n totalCount,\n manualPagination = false,\n pageSizeOptions: _pageSizeOptions = [10, 25, 50, 100],\n filters,\n onFiltersChange,\n filterFields,\n enableFiltering,\n layout: _layout = SF_DATA_GRID_DEFAULT_VARIANTS.layout,\n className,\n tableClassName,\n emptyState,\n loading,\n loadingRows,\n toolbar,\n showToolbar = true,\n showPagination = true,\n pagination: customPagination,\n enableColumnResizing,\n columnResizeMode = \"onEnd\",\n children,\n}: DataGridProps<TData> & { children?: ReactNode }) {\n // Internal state for uncontrolled mode\n const [internalSorting, setInternalSorting] = useState<SortingState>([]);\n const [internalColumnVisibility, setInternalColumnVisibility] =\n useState<VisibilityState>({});\n const [internalRowSelection, setInternalRowSelection] =\n useState<RowSelectionState>({});\n const [internalPagination, setInternalPagination] = useState<PaginationState>(\n {\n pageIndex,\n pageSize,\n }\n );\n\n // Determine controlled vs uncontrolled\n const sorting = controlledSorting ?? internalSorting;\n const columnVisibility =\n controlledColumnVisibility ?? internalColumnVisibility;\n const rowSelection = controlledRowSelection ?? internalRowSelection;\n const pagination = internalPagination;\n\n // Handle sorting change\n const handleSortingChange = (\n updater: SortingState | ((old: SortingState) => SortingState)\n ) => {\n const newSorting =\n typeof updater === \"function\" ? updater(sorting) : updater;\n if (onSortingChange) {\n onSortingChange(newSorting);\n } else {\n setInternalSorting(newSorting);\n }\n };\n\n // Handle column visibility change\n const handleColumnVisibilityChange = (\n updater: VisibilityState | ((old: VisibilityState) => VisibilityState)\n ) => {\n const newVisibility =\n typeof updater === \"function\" ? updater(columnVisibility) : updater;\n if (onColumnVisibilityChange) {\n onColumnVisibilityChange(newVisibility);\n } else {\n setInternalColumnVisibility(newVisibility);\n }\n };\n\n // Handle row selection change\n const handleRowSelectionChange = (\n updater: RowSelectionState | ((old: RowSelectionState) => RowSelectionState)\n ) => {\n const newSelection =\n typeof updater === \"function\" ? updater(rowSelection) : updater;\n if (onRowSelectionChange) {\n onRowSelectionChange(newSelection);\n } else {\n setInternalRowSelection(newSelection);\n }\n };\n\n // Handle pagination change\n const handlePaginationChange = (\n updater: PaginationState | ((old: PaginationState) => PaginationState)\n ) => {\n const newPagination =\n typeof updater === \"function\" ? updater(pagination) : updater;\n setInternalPagination(newPagination);\n if (onPaginationChange) {\n onPaginationChange({\n pageIndex: newPagination.pageIndex,\n pageSize: newPagination.pageSize,\n });\n }\n };\n\n // Create table instance\n const table = useReactTable({\n data,\n columns: columns as import(\"@tanstack/react-table\").ColumnDef<\n TData,\n unknown\n >[],\n state: {\n sorting,\n columnVisibility,\n rowSelection,\n pagination,\n },\n onSortingChange: handleSortingChange,\n onColumnVisibilityChange: handleColumnVisibilityChange,\n onRowSelectionChange: handleRowSelectionChange,\n onPaginationChange: handlePaginationChange,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: enableSorting ? getSortedRowModel() : undefined,\n getPaginationRowModel: getPaginationRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n enableSorting,\n enableRowSelection: enableRowSelection !== false,\n enableColumnResizing,\n columnResizeMode,\n manualPagination,\n pageCount: manualPagination\n ? Math.ceil((totalCount ?? data.length) / pageSize)\n : undefined,\n enableMultiRowSelection: true,\n });\n\n // Provide context to children\n const contextValue: DataGridContextValue<TData> = {\n table,\n pageSize,\n pageIndex,\n totalCount: totalCount ?? data.length,\n manualPagination,\n loading,\n loadingRows,\n enableColumnResizing,\n columnResizeMode,\n };\n\n return (\n <DataGridContext.Provider value={contextValue as DataGridContextValue}>\n <div\n className={cn(\n \"flex flex-col overflow-hidden rounded-lg border border-sf-line bg-sf-base\",\n className\n )}\n >\n {children || (\n // Auto mode - DataGrid renders everything based on props\n <>\n {/* Toolbar */}\n {showToolbar && (\n <DataGridToolbar>\n {toolbar ?? (\n <>\n {enableFiltering && filterFields && (\n <Filters\n filters={filters ?? []}\n fields={filterFields}\n onChange={onFiltersChange ?? (() => {})}\n size=\"sm\"\n />\n )}\n <div className=\"ml-auto flex items-center gap-2\">\n {enableColumnVisibility && <DataGridColumnToggle />}\n </div>\n </>\n )}\n </DataGridToolbar>\n )}\n\n {/* Table content */}\n <div className=\"flex-1 overflow-auto\">\n <DataGridContent className={tableClassName} />\n <DataGridEmpty>{emptyState}</DataGridEmpty>\n </div>\n\n {/* Pagination */}\n {showPagination && (customPagination ?? <DataGridPagination />)}\n </>\n )}\n </div>\n </DataGridContext.Provider>\n );\n}\n\nDataGridRoot.displayName = \"DataGrid\";\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport const DataGrid = Object.assign(DataGridRoot, {\n Toolbar: DataGridToolbar,\n Content: DataGridContent,\n Pagination: DataGridPagination,\n ColumnToggle: DataGridColumnToggle,\n Empty: DataGridEmpty,\n});\n\nexport type {\n DataGridProps,\n DataGridColumn,\n DataGridToolbarProps,\n DataGridContentProps,\n DataGridPaginationProps,\n DataGridColumnToggleProps,\n DataGridEmptyProps,\n} from \"./types\";\n"],"mappings":"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"}
|