@signalflare-ai/ui 1.1.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 +102 -5
- package/README.md +1 -1
- package/ai/component-registry.json +531 -79
- package/ai/component-registry.md +4067 -6
- package/ai/schemas.ts +6 -1
- package/dist/.build-complete +1 -1
- package/dist/ai/schemas.d.ts +76 -58
- package/dist/ai/schemas.d.ts.map +1 -1
- package/dist/{ai-actions-DSVeQn4e.js → ai-actions-CBfz5XEf.js} +4 -4
- package/dist/{ai-actions-DSVeQn4e.js.map → ai-actions-CBfz5XEf.js.map} +1 -1
- package/dist/{ai-agent-card-BXHwhWAU.js → ai-agent-card-CByAUe0q.js} +3 -3
- package/dist/ai-agent-card-CByAUe0q.js.map +1 -0
- package/dist/{ai-approval-aa0qvjFN.js → ai-approval-Ci8N70a7.js} +4 -3
- package/dist/{ai-approval-aa0qvjFN.js.map → ai-approval-Ci8N70a7.js.map} +1 -1
- package/dist/{ai-code-block-BgtIxtZZ.js → ai-code-block-P9TJHvaC.js} +37 -39
- package/dist/ai-code-block-P9TJHvaC.js.map +1 -0
- package/dist/ai-conversation-Qslfdi1t.js +228 -0
- package/dist/ai-conversation-Qslfdi1t.js.map +1 -0
- package/dist/{ai-info-banner-uFxHHwBA.js → ai-info-banner-B_9vtGK3.js} +8 -4
- package/dist/ai-info-banner-B_9vtGK3.js.map +1 -0
- package/dist/{ai-message-BjnFznXy.js → ai-message-Ci3gwM7G.js} +29 -10
- package/dist/ai-message-Ci3gwM7G.js.map +1 -0
- package/dist/{ai-mission-header-08__gULL.js → ai-mission-header-CaBc19-t.js} +2 -2
- package/dist/{ai-mission-header-08__gULL.js.map → ai-mission-header-CaBc19-t.js.map} +1 -1
- package/dist/{ai-part-group-DBtgTgAn.js → ai-part-group-Dx1Mr92B.js} +5 -4
- package/dist/ai-part-group-Dx1Mr92B.js.map +1 -0
- package/dist/{ai-prompt-input-CuluUzpf.js → ai-prompt-input-Bm4XoSj2.js} +44 -55
- package/dist/ai-prompt-input-Bm4XoSj2.js.map +1 -0
- package/dist/{ai-question-CHHoDJMg.js → ai-question-OyJovxGe.js} +4 -3
- package/dist/{ai-question-CHHoDJMg.js.map → ai-question-OyJovxGe.js.map} +1 -1
- package/dist/{ai-reasoning-CnL6ZSr5.js → ai-reasoning-BLfBXx3F.js} +9 -5
- package/dist/ai-reasoning-BLfBXx3F.js.map +1 -0
- package/dist/{ai-response-BEUg3xvd.js → ai-response-hbVCZJmo.js} +9 -4
- package/dist/ai-response-hbVCZJmo.js.map +1 -0
- package/dist/{ai-shimmer-By5_L05p.js → ai-shimmer-BamNMNK3.js} +2 -2
- package/dist/{ai-shimmer-By5_L05p.js.map → ai-shimmer-BamNMNK3.js.map} +1 -1
- package/dist/{ai-status-badge-BGYGWYF6.js → ai-status-badge-BZLczdkI.js} +2 -2
- package/dist/{ai-status-badge-BGYGWYF6.js.map → ai-status-badge-BZLczdkI.js.map} +1 -1
- package/dist/{ai-streaming-text-CMfoThV0.js → ai-streaming-text-DgYu64UH.js} +44 -16
- package/dist/ai-streaming-text-DgYu64UH.js.map +1 -0
- package/dist/{ai-subagent-DcPRqkAA.js → ai-subagent-p97AI1h9.js} +14 -6
- package/dist/ai-subagent-p97AI1h9.js.map +1 -0
- package/dist/{ai-suggestion-MgeCg5Ar.js → ai-suggestion-Bj6vF7CT.js} +3 -3
- package/dist/{ai-suggestion-MgeCg5Ar.js.map → ai-suggestion-Bj6vF7CT.js.map} +1 -1
- package/dist/{ai-task-list-Da9zIm00.js → ai-task-list-C_UQYpk9.js} +15 -6
- package/dist/ai-task-list-C_UQYpk9.js.map +1 -0
- package/dist/{ai-timeline-Cwu045IR.js → ai-timeline-CePL1LOU.js} +3 -3
- package/dist/ai-timeline-CePL1LOU.js.map +1 -0
- package/dist/{ai-tool-Cn1O4xjP.js → ai-tool-CfRcwmHT.js} +35 -16
- package/dist/ai-tool-CfRcwmHT.js.map +1 -0
- package/dist/{ai-usage-bar-DjS12DMp.js → ai-usage-bar-45pVRCGA.js} +2 -2
- package/dist/{ai-usage-bar-DjS12DMp.js.map → ai-usage-bar-45pVRCGA.js.map} +1 -1
- package/dist/{badge-D_eaA6wv.js → badge-Beb-6uut.js} +5 -5
- package/dist/{badge-D_eaA6wv.js.map → badge-Beb-6uut.js.map} +1 -1
- package/dist/{banner-B_6oBrsu.js → banner-CCEksxPg.js} +8 -3
- package/dist/banner-CCEksxPg.js.map +1 -0
- package/dist/{breadcrumbs-BlmeYfgq.js → breadcrumbs-HiTmgaZ4.js} +5 -5
- package/dist/{breadcrumbs-BlmeYfgq.js.map → breadcrumbs-HiTmgaZ4.js.map} +1 -1
- package/dist/{button-De0267YU.js → button-BHOgXJRU.js} +4 -4
- package/dist/{button-De0267YU.js.map → button-BHOgXJRU.js.map} +1 -1
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/{chart-BK3sVPnD.js → chart-B9FfZdKs.js} +7 -7
- package/dist/chart-B9FfZdKs.js.map +1 -0
- package/dist/{checkbox-DYhUmZNw.js → checkbox-Cy_OCyay.js} +3 -3
- package/dist/{checkbox-DYhUmZNw.js.map → checkbox-Cy_OCyay.js.map} +1 -1
- package/dist/{clipboard-text-ssybngLw.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-Cx-QSoOT.js → code-JsQz-0G_.js} +4 -4
- package/dist/{code-Cx-QSoOT.js.map → code-JsQz-0G_.js.map} +1 -1
- package/dist/{collapsible-DWsXeXmS.js → collapsible-1kOZ-89L.js} +2 -2
- package/dist/{collapsible-DWsXeXmS.js.map → collapsible-1kOZ-89L.js.map} +1 -1
- package/dist/{combobox-C0iW6a0r.js → combobox-CQwDmqgA.js} +4 -4
- package/dist/{combobox-C0iW6a0r.js.map → combobox-CQwDmqgA.js.map} +1 -1
- package/dist/command-line/cli.js +3 -3
- package/dist/{command-palette-DGzioeki.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 +2 -2
- 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 +2 -2
- 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-CG76N_hK.js → data-grid-DDSFMHud.js} +136 -53
- package/dist/data-grid-DDSFMHud.js.map +1 -0
- package/dist/{date-picker-Dqg9L4xu.js → date-picker-O34AqG3f.js} +2 -2
- package/dist/{date-picker-Dqg9L4xu.js.map → date-picker-O34AqG3f.js.map} +1 -1
- package/dist/{date-range-picker-D75LLINc.js → date-range-picker-YKYvum_r.js} +29 -39
- package/dist/{date-range-picker-D75LLINc.js.map → date-range-picker-YKYvum_r.js.map} +1 -1
- package/dist/{dialog-CyHEQXEY.js → dialog-DYqu4aDO.js} +3 -3
- package/dist/{dialog-CyHEQXEY.js.map → dialog-DYqu4aDO.js.map} +1 -1
- package/dist/{dist-1-gcEL2L.js → dist-6AtBsaJE.js} +153 -47
- package/dist/dist-6AtBsaJE.js.map +1 -0
- package/dist/{dropdown-qnEYRFXZ.js → dropdown-XzbnRLYR.js} +15 -5
- package/dist/dropdown-XzbnRLYR.js.map +1 -0
- package/dist/{echart-DURZEyai.js → echart-DGBIVAv1.js} +23 -57
- package/dist/{echart-DURZEyai.js.map → echart-DGBIVAv1.js.map} +1 -1
- package/dist/{empty-D2TypIId.js → empty-C1tAkawe.js} +12 -7
- package/dist/empty-C1tAkawe.js.map +1 -0
- package/dist/{field-Y_UK1_Cg.js → field-DBpFzzBS.js} +3 -3
- package/dist/{field-Y_UK1_Cg.js.map → field-DBpFzzBS.js.map} +1 -1
- package/dist/{filters-Bw_U6ZTx.js → filters-SmEl93za.js} +10 -10
- package/dist/filters-SmEl93za.js.map +1 -0
- package/dist/{flow-BRsYUCJa.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-qUAN9hFx.js → grid-CifjQL-5.js} +2 -2
- package/dist/{grid-qUAN9hFx.js.map → grid-CifjQL-5.js.map} +1 -1
- package/dist/{highlight-to-react-ClEfL81q.js → highlight-to-react-DN9dUCS2.js} +9 -15
- package/dist/highlight-to-react-DN9dUCS2.js.map +1 -0
- package/dist/index.js +72 -72
- package/dist/index.js.map +1 -1
- package/dist/{input-DddtBN-g.js → input-COmx2M_R.js} +5 -5
- package/dist/{input-DddtBN-g.js.map → input-COmx2M_R.js.map} +1 -1
- package/dist/{input-DXYUjGgD.js → input-GkfMQZC_.js} +3 -3
- package/dist/{input-DXYUjGgD.js.map → input-GkfMQZC_.js.map} +1 -1
- package/dist/{label-QtJxtJ4u.js → label-CiGZ464N.js} +3 -3
- package/dist/{label-QtJxtJ4u.js.map → label-CiGZ464N.js.map} +1 -1
- package/dist/{layer-card-BME0eljh.js → layer-card-8l8GuLQr.js} +2 -2
- package/dist/{layer-card-BME0eljh.js.map → layer-card-8l8GuLQr.js.map} +1 -1
- package/dist/layout-CWBE0qwx.js +6207 -0
- 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-CXkdw9Yr.js +305 -0
- package/dist/measured-text-CXkdw9Yr.js.map +1 -0
- package/dist/{menubar-C8NzAjfd.js → menubar-CoOr4ocj.js} +3 -3
- package/dist/{menubar-C8NzAjfd.js.map → menubar-CoOr4ocj.js.map} +1 -1
- package/dist/{meter-CpmTenEr.js → meter-Pf_VOl59.js} +2 -2
- package/dist/{meter-CpmTenEr.js.map → meter-Pf_VOl59.js.map} +1 -1
- package/dist/{pagination-BVqdlONY.js → pagination-DSY279Ta.js} +2 -2
- package/dist/{pagination-BVqdlONY.js.map → pagination-DSY279Ta.js.map} +1 -1
- package/dist/{popover-BRQZ2b6z.js → popover-BY-e9co1.js} +2 -2
- package/dist/{popover-BRQZ2b6z.js.map → popover-BY-e9co1.js.map} +1 -1
- package/dist/{radio-BNSwOt3B.js → radio-DZwL13j0.js} +2 -2
- package/dist/{radio-BNSwOt3B.js.map → radio-DZwL13j0.js.map} +1 -1
- package/dist/{select-1w2aebGQ.js → select-BFifYqHA.js} +6 -6
- package/dist/{select-1w2aebGQ.js.map → select-BFifYqHA.js.map} +1 -1
- package/dist/{sensitive-input-82Cez3vj.js → sensitive-input-DHLZcM73.js} +4 -4
- package/dist/{sensitive-input-82Cez3vj.js.map → sensitive-input-DHLZcM73.js.map} +1 -1
- package/dist/{sidebar-CAsCmSpM.js → sidebar-odGsdvG4.js} +6 -7
- package/dist/sidebar-odGsdvG4.js.map +1 -0
- package/dist/{signalflare-ai-logo-DDhxMJD6.js → signalflare-ai-logo-CNaDT_w8.js} +2 -2
- package/dist/{signalflare-ai-logo-DDhxMJD6.js.map → signalflare-ai-logo-CNaDT_w8.js.map} +1 -1
- package/dist/{skeleton-line-Do3UmGk9.js → skeleton-line-CxxYVTO2.js} +2 -2
- package/dist/{skeleton-line-Do3UmGk9.js.map → skeleton-line-CxxYVTO2.js.map} +1 -1
- package/dist/{sparkline-DdbeM4Ai.js → sparkline-BQ-4j2W2.js} +2 -2
- package/dist/{sparkline-DdbeM4Ai.js.map → sparkline-BQ-4j2W2.js.map} +1 -1
- package/dist/src/blocks/agent-harness/agent-harness.d.ts.map +1 -1
- package/dist/src/blocks/agent-harness/agent-harness.tsx +40 -16
- 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 +69 -37
- package/dist/src/components/ai-conversation/ai-conversation.d.ts.map +1 -1
- package/dist/src/components/ai-conversation/index.d.ts +2 -1
- package/dist/src/components/ai-conversation/index.d.ts.map +1 -1
- package/dist/src/components/ai-conversation/measurement-constants.d.ts +30 -0
- package/dist/src/components/ai-conversation/measurement-constants.d.ts.map +1 -0
- package/dist/src/components/ai-info-banner/ai-info-banner.d.ts.map +1 -1
- package/dist/src/components/ai-message/ai-message.d.ts +3 -0
- package/dist/src/components/ai-message/ai-message.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 +13 -3
- 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/index.d.ts +1 -1
- package/dist/src/components/ai-prompt-input/index.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 +12 -1
- package/dist/src/components/ai-response/ai-response.d.ts.map +1 -1
- package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts +27 -0
- package/dist/src/components/ai-streaming-text/ai-streaming-text.d.ts.map +1 -1
- package/dist/src/components/ai-streaming-text/index.d.ts +1 -1
- package/dist/src/components/ai-streaming-text/index.d.ts.map +1 -1
- package/dist/src/components/ai-subagent/ai-subagent.d.ts.map +1 -1
- package/dist/src/components/ai-task-list/ai-task-list.d.ts.map +1 -1
- package/dist/src/components/ai-tool/ai-tool.d.ts.map +1 -1
- package/dist/src/components/banner/banner.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/empty/empty.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/stat-card/stat-card.d.ts +5 -0
- package/dist/src/components/stat-card/stat-card.d.ts.map +1 -1
- package/dist/src/components/text/text.d.ts +36 -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/components/tooltip/tooltip.d.ts.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/utils/highlight-to-react.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +2 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/measured-text.d.ts +40 -0
- package/dist/src/utils/measured-text.d.ts.map +1 -0
- package/dist/src/utils/use-measured-text.d.ts +59 -0
- package/dist/src/utils/use-measured-text.d.ts.map +1 -0
- package/dist/{stat-card-CEZscNh8.js → stat-card-Bspk4XFr.js} +30 -12
- package/dist/stat-card-Bspk4XFr.js.map +1 -0
- package/dist/styles/sf-binding.css +1 -1
- package/dist/styles/sf-standalone.css +2 -2
- package/dist/styles/shadcn.css +1 -1
- package/dist/styles/theme-minimal.css +9 -9
- package/dist/styles/theme-sf.css +14 -20
- package/dist/styles/theme-vesper.css +91 -0
- package/dist/{surface-BduI7Ehl.js → surface-CWdSFVUx.js} +3 -3
- package/dist/{surface-BduI7Ehl.js.map → surface-CWdSFVUx.js.map} +1 -1
- package/dist/{switch-CzZBRBL7.js → switch-TA4cByCJ.js} +5 -5
- package/dist/switch-TA4cByCJ.js.map +1 -0
- package/dist/{table-Rv4JMy0B.js → table-BM8JBGBs.js} +3 -3
- package/dist/{table-Rv4JMy0B.js.map → table-BM8JBGBs.js.map} +1 -1
- package/dist/{tabs-1cHrYoel.js → tabs-bnH2vGLv.js} +2 -2
- package/dist/{tabs-1cHrYoel.js.map → tabs-bnH2vGLv.js.map} +1 -1
- package/dist/{text-KJmGkwnf.js → text-iQ0YUFNg.js} +27 -6
- package/dist/text-iQ0YUFNg.js.map +1 -0
- package/dist/{text-roll-BZ3I1umc.js → text-roll-C3U2jd2u.js} +5 -2
- package/dist/text-roll-C3U2jd2u.js.map +1 -0
- package/dist/{theme-toggle-Bhu681D7.js → theme-toggle-BTVxD-fD.js} +10 -9
- package/dist/theme-toggle-BTVxD-fD.js.map +1 -0
- package/dist/{toast-Nw28a5Cx.js → toast-CgZVaAkw.js} +3 -3
- package/dist/{toast-Nw28a5Cx.js.map → toast-CgZVaAkw.js.map} +1 -1
- package/dist/{tooltip-Cb7QW-7H.js → tooltip-uobk6Oh-.js} +9 -3
- package/dist/tooltip-uobk6Oh-.js.map +1 -0
- package/dist/{use-agent-harness-BMyF8pTq.js → use-agent-harness-Dl8w6X5O.js} +3 -3
- package/dist/{use-agent-harness-BMyF8pTq.js.map → use-agent-harness-Dl8w6X5O.js.map} +1 -1
- package/dist/utils.js +4 -3
- package/package.json +27 -24
- 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/css-build.ts +1 -1
- package/scripts/theme-generator/config.ts +28 -146
- package/scripts/theme-generator/generate-css.ts +1 -2
- package/scripts/theme-generator/index.ts +0 -1
- package/scripts/theme-generator/migrate.ts +3 -3
- package/dist/ai-agent-card-BXHwhWAU.js.map +0 -1
- package/dist/ai-code-block-BgtIxtZZ.js.map +0 -1
- package/dist/ai-conversation-CArP7C8K.js +0 -184
- package/dist/ai-conversation-CArP7C8K.js.map +0 -1
- package/dist/ai-info-banner-uFxHHwBA.js.map +0 -1
- package/dist/ai-message-BjnFznXy.js.map +0 -1
- package/dist/ai-part-group-DBtgTgAn.js.map +0 -1
- package/dist/ai-prompt-input-CuluUzpf.js.map +0 -1
- package/dist/ai-reasoning-CnL6ZSr5.js.map +0 -1
- package/dist/ai-response-BEUg3xvd.js.map +0 -1
- package/dist/ai-streaming-text-CMfoThV0.js.map +0 -1
- package/dist/ai-subagent-DcPRqkAA.js.map +0 -1
- package/dist/ai-task-list-Da9zIm00.js.map +0 -1
- package/dist/ai-timeline-Cwu045IR.js.map +0 -1
- package/dist/ai-tool-Cn1O4xjP.js.map +0 -1
- package/dist/banner-B_6oBrsu.js.map +0 -1
- package/dist/chart-BK3sVPnD.js.map +0 -1
- package/dist/clipboard-text-ssybngLw.js.map +0 -1
- package/dist/command-palette-DGzioeki.js.map +0 -1
- package/dist/data-grid-CG76N_hK.js.map +0 -1
- package/dist/dist-1-gcEL2L.js.map +0 -1
- package/dist/dropdown-qnEYRFXZ.js.map +0 -1
- package/dist/empty-D2TypIId.js.map +0 -1
- package/dist/filters-Bw_U6ZTx.js.map +0 -1
- package/dist/flow-BRsYUCJa.js.map +0 -1
- package/dist/highlight-to-react-ClEfL81q.js.map +0 -1
- package/dist/link-provider-BUZKXaNE.js.map +0 -1
- package/dist/sidebar-CAsCmSpM.js.map +0 -1
- package/dist/stat-card-CEZscNh8.js.map +0 -1
- package/dist/styles/theme-blue-tint.css +0 -98
- package/dist/switch-CzZBRBL7.js.map +0 -1
- package/dist/text-KJmGkwnf.js.map +0 -1
- package/dist/text-roll-BZ3I1umc.js.map +0 -1
- package/dist/theme-toggle-Bhu681D7.js.map +0 -1
- package/dist/tooltip-Cb7QW-7H.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-qnEYRFXZ.js","names":[],"sources":["../src/components/dropdown/dropdown.tsx"],"sourcesContent":["import { Menu as DropdownMenuPrimitive } from \"@base-ui/react/menu\";\nimport {\n CaretRightIcon as CaretRight,\n CheckIcon as Check,\n type Icon,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Checkbox } from \"../checkbox\";\n\n/** Dropdown item variant definitions (default and danger styles). */\nexport const SF_DROPDOWN_VARIANTS = {\n variant: {\n default: {\n classes: \"\",\n description: \"Default dropdown item appearance\",\n },\n danger: {\n classes:\n \"text-sf-danger data-highlighted:bg-sf-danger/5 data-highlighted:text-sf-danger\",\n description: \"Destructive action item\",\n },\n },\n} as const;\n\nexport const SF_DROPDOWN_DEFAULT_VARIANTS = {\n variant: \"default\",\n} as const;\n\n// Derived types from SF_DROPDOWN_VARIANTS\nexport type SFDropdownVariant = keyof typeof SF_DROPDOWN_VARIANTS.variant;\n\nexport interface SFDropdownVariantsProps {\n /**\n * Visual style of the dropdown item.\n * - `\"default\"` — Standard item appearance\n * - `\"danger\"` — Destructive action with red text\n * @default \"default\"\n */\n variant?: SFDropdownVariant;\n}\n\nexport function dropdownVariants({\n variant = SF_DROPDOWN_DEFAULT_VARIANTS.variant,\n}: SFDropdownVariantsProps = {}) {\n return cn(SF_DROPDOWN_VARIANTS.variant[variant].classes);\n}\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubmenuTrigger>,\n React.ComponentPropsWithoutRef<\n typeof DropdownMenuPrimitive.SubmenuTrigger\n > & {\n inset?: boolean;\n icon?: Icon;\n }\n>(({ className, inset, children, icon: IconComponent, ...props }, ref) => (\n <DropdownMenuPrimitive.SubmenuTrigger\n ref={ref}\n className={cn(\n \"flex cursor-default items-center rounded-sm text-base outline-hidden select-none\", // base styles\n \"px-2 py-1.5\", // spacing\n \"focus:bg-sf-tint\", // focus state\n \"data-[state=open]:bg-sf-tint\", // open state\n inset && \"pl-8\", // conditional inset\n className\n )}\n {...props}\n >\n {IconComponent && <IconComponent className=\"mr-2 h-4 w-4\" />}\n {children}\n <CaretRight className=\"ml-auto h-4 w-4\" />\n </DropdownMenuPrimitive.SubmenuTrigger>\n));\n\nDropdownMenuSubTrigger.displayName =\n DropdownMenuPrimitive.SubmenuTrigger.displayName;\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Positioner>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Positioner>\n>(({ className, sideOffset = 8, children, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Positioner\n ref={ref}\n sideOffset={sideOffset}\n {...props}\n >\n <DropdownMenuPrimitive.Popup\n className={cn(\n \"overflow-hidden bg-sf-control text-sf-default\", // background\n \"rounded-lg shadow-lg ring ring-sf-line\", // border part\n \"min-w-36 p-1.5\", // spacing\n \"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\", // open animation\n \"data-[side=bottom]:slide-in-from-top-2\", // bottom side animation\n \"data-[side=left]:slide-in-from-right-2\", // left side animation\n \"data-[side=right]:slide-in-from-left-2\", // right side animation\n \"data-[side=top]:slide-in-from-bottom-2\", // top side animation\n \"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\", // close animation\n className\n )}\n >\n {children}\n </DropdownMenuPrimitive.Popup>\n </DropdownMenuPrimitive.Positioner>\n </DropdownMenuPrimitive.Portal>\n));\n\nconst renderIconNode = (IconComponent?: Icon | React.ReactNode) => {\n if (!IconComponent) return null;\n if (React.isValidElement(IconComponent)) return IconComponent;\n const Comp = IconComponent as React.ComponentType<Record<string, unknown>>;\n return <Comp className=\"mr-2 h-4 w-4\" />;\n};\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n icon?: Icon | React.ReactNode;\n selected?: boolean;\n href?: string;\n variant?: \"default\" | \"danger\";\n }\n>(\n (\n {\n className,\n inset,\n icon: IconComponent,\n children,\n selected,\n render,\n href,\n variant = \"default\",\n ...props\n },\n ref\n ) => {\n const LinkComponent = useLinkComponent();\n const content = React.useMemo(() => {\n const innerContent = (\n <>\n {IconComponent && renderIconNode(IconComponent)}\n {children}\n {selected && (\n <span className=\"inline-flex\">\n <Check />\n </span>\n )}\n </>\n );\n\n if (!href) return innerContent;\n\n // Matches http://, https://, or protocol-relative //\n const isExternal = /^(https?:)?\\/\\//.test(href);\n const styles = cn(\n \"flex items-center\",\n variant === \"danger\" &&\n \"text-sf-danger data-highlighted:bg-sf-danger/5 data-highlighted:text-sf-danger\"\n );\n if (isExternal) {\n return (\n <a\n className={cn(styles, \"w-full text-inherit! no-underline!\")}\n href={href}\n target=\"_blank\"\n rel=\"noreferrer\"\n /**\n * For some reason we need this here to prevent the outer link\n * from being clicked (thereby going to the worker details\n * instead of visiting the link)\n */\n onClick={(e) => e.stopPropagation()}\n >\n {innerContent}\n </a>\n );\n }\n return (\n <LinkComponent\n className={cn(styles, \"w-full text-inherit! no-underline!\")}\n href={href}\n to={href}\n /**\n * For some reason we need this here to prevent the outer link\n * from being clicked (thereby going to the worker details\n * instead of visiting the link)\n */\n onClick={(e) => e.stopPropagation()}\n >\n {innerContent}\n </LinkComponent>\n );\n }, [href, IconComponent, children, selected, variant, LinkComponent]);\n\n // When href is provided, content already contains children via innerContent\n // When render prop is provided, caller controls children rendering\n // Only pass children directly when neither href nor render is used\n const useRenderProp = href || render;\n\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-default items-center rounded-md px-2 py-1.5 text-base outline-hidden select-none focus:text-sf-default data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-sf-overlay\",\n inset && \"pl-8\",\n dropdownVariants({ variant }),\n className\n )}\n render={href ? content : render}\n {...props}\n >\n {useRenderProp ? undefined : children}\n </DropdownMenuPrimitive.Item>\n );\n }\n);\n\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n \"relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-base outline-hidden transition-colors select-none focus:bg-sf-tint focus:text-sf-default data-disabled:pointer-events-none data-disabled:opacity-50\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center text-inherit\">\n <Checkbox checked={checked} />\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName =\n DropdownMenuPrimitive.CheckboxItem.displayName;\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.GroupLabel>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.GroupLabel> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.GroupLabel\n ref={ref}\n className={cn(\n \"px-2 py-1.5 text-base font-semibold\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.GroupLabel.displayName;\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-sf-line\", className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nconst DropdownMenuShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) => {\n return (\n <span\n className={cn(\"ml-auto text-xs tracking-widest opacity-60\", className)}\n {...props}\n />\n );\n};\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\";\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> & {\n inset?: boolean;\n icon?: Icon | React.ReactNode;\n }\n>(({ className, children, inset, icon: IconComponent, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-default items-center rounded-md px-2 py-1.5 text-base outline-hidden select-none\",\n \"data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-sf-tint\",\n inset && \"pl-8\",\n className\n )}\n {...props}\n >\n {IconComponent && renderIconNode(IconComponent)}\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = \"DropdownMenuRadioItem\";\n\nconst DropdownMenuRadioItemIndicator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItemIndicator>,\n React.ComponentPropsWithoutRef<\n typeof DropdownMenuPrimitive.RadioItemIndicator\n >\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItemIndicator\n ref={ref}\n className={cn(\"ml-auto\", className)}\n {...props}\n >\n {children ?? <Check className=\"h-4 w-4\" />}\n </DropdownMenuPrimitive.RadioItemIndicator>\n));\nDropdownMenuRadioItemIndicator.displayName = \"DropdownMenuRadioItemIndicator\";\n\n/**\n * Custom Trigger that converts a single child element to the `render` prop\n * to avoid nested button issues with base-ui's Menu.Trigger.\n */\nconst DropdownMenuTrigger = React.forwardRef<\n HTMLButtonElement,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>(({ children, render, ...props }, ref) => {\n // If render prop is provided, use it directly\n // Otherwise, convert single child element to render prop\n const childElement = React.isValidElement(children) ? children : null;\n const effectiveRender = render ?? childElement;\n\n return (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n {...props}\n {...(effectiveRender && {\n render: effectiveRender as React.ReactElement<Record<string, unknown>>,\n })}\n >\n {/* Only pass children if not using as render prop */}\n {childElement ? undefined : children}\n </DropdownMenuPrimitive.Trigger>\n );\n});\nDropdownMenuTrigger.displayName = \"DropdownMenuTrigger\";\n\n/**\n * DropdownMenu — accessible dropdown menu anchored to a trigger.\n *\n * Compound component: `DropdownMenu` (Root), `.Trigger`, `.Content`, `.Item`,\n * `.CheckboxItem`, `.RadioGroup`, `.RadioItem`, `.RadioItemIndicator`,\n * `.Sub`, `.SubTrigger`, `.SubContent`, `.Label`, `.Separator`, `.Shortcut`, `.Group`.\n *\n * Built on `@base-ui/react/menu`.\n *\n * @example\n * ```tsx\n * <DropdownMenu>\n * <DropdownMenu.Trigger>\n * <Button>Actions</Button>\n * </DropdownMenu.Trigger>\n * <DropdownMenu.Content>\n * <DropdownMenu.Item>Edit</DropdownMenu.Item>\n * <DropdownMenu.Item icon={CopyIcon}>Duplicate</DropdownMenu.Item>\n * <DropdownMenu.Separator />\n * <DropdownMenu.Item variant=\"danger\">Delete</DropdownMenu.Item>\n * </DropdownMenu.Content>\n * </DropdownMenu>\n * ```\n *\n * @see https://base-ui.com/react/components/menu\n */\nexport const DropdownMenu = Object.assign(DropdownMenuPrimitive.Root, {\n Trigger: DropdownMenuTrigger,\n Portal: DropdownMenuPrimitive.Portal,\n Sub: DropdownMenuPrimitive.SubmenuRoot,\n SubTrigger: DropdownMenuSubTrigger,\n SubContent: DropdownMenuContent,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n RadioGroup: DropdownMenuPrimitive.RadioGroup,\n RadioItem: DropdownMenuRadioItem,\n RadioItemIndicator: DropdownMenuRadioItemIndicator,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Shortcut: DropdownMenuShortcut,\n Group: DropdownMenuPrimitive.Group,\n});\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"empty-D2TypIId.js","names":[],"sources":["../src/components/empty/empty.tsx"],"sourcesContent":["import { CheckIcon, CopyIcon } from \"@phosphor-icons/react\";\nimport { useState } from \"react\";\n\nimport { Button } from \"../../components/button\";\nimport { cn } from \"../../utils/cn\";\n\n/** Empty state size variant definitions mapping sizes to their Tailwind classes. */\nexport const SF_EMPTY_VARIANTS = {\n size: {\n sm: {\n classes: \"px-6 py-8 gap-4\",\n description: \"Compact empty state for smaller containers\",\n },\n base: {\n classes: \"px-10 py-16 gap-6\",\n description: \"Default empty state size\",\n },\n lg: {\n classes: \"px-12 py-20 gap-8\",\n description: \"Large empty state for prominent placement\",\n },\n },\n} as const;\n\nexport const SF_EMPTY_DEFAULT_VARIANTS = {\n size: \"base\",\n} as const;\n\nexport type SFEmptySize = keyof typeof SF_EMPTY_VARIANTS.size;\n\nexport interface SFEmptyVariantsProps {\n /**\n * Size of the empty state container.\n * - `\"sm\"` — Compact empty state for smaller containers\n * - `\"base\"` — Default empty state size\n * - `\"lg\"` — Large empty state for prominent placement\n * @default \"base\"\n */\n size?: SFEmptySize;\n}\n\nexport function emptyVariants({\n size = SF_EMPTY_DEFAULT_VARIANTS.size,\n}: SFEmptyVariantsProps = {}) {\n return cn(\n \"flex w-full flex-col items-center rounded-xl border border-sf-fill bg-sf-control text-sf-default\",\n SF_EMPTY_VARIANTS.size[size].classes\n );\n}\n\n/**\n * Empty state component props.\n *\n * @example\n * ```tsx\n * <Empty\n * icon={<PackageIcon size={48} />}\n * title=\"No packages found\"\n * description=\"Get started by installing your first package.\"\n * commandLine=\"npm install @signalflare-ai/ui\"\n * />\n * ```\n */\nexport interface EmptyProps extends SFEmptyVariantsProps {\n /** Decorative icon displayed above the title (e.g. from `@phosphor-icons/react`). */\n icon?: React.ReactNode;\n /** Primary heading text for the empty state. */\n title: string;\n /** Secondary description text displayed below the title. */\n description?: string;\n /** Shell command displayed in a copyable code block. */\n commandLine?: string;\n /** Additional content (buttons, links) rendered below the description. */\n contents?: React.ReactNode;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n}\n\n/**\n * Placeholder shown when a list, table, or page has no content to display.\n *\n * @example\n * ```tsx\n * <Empty title=\"No results found\" description=\"Try adjusting your search.\" />\n * ```\n */\nexport function Empty({\n icon,\n title,\n description,\n commandLine,\n contents,\n size = \"base\",\n className,\n}: EmptyProps) {\n const [emptyStateCopied, setEmptyStateCopied] = useState<boolean>(false);\n\n return (\n <div className={cn(emptyVariants({ size }), className)}>\n {icon}\n <h2 className=\"text-2xl font-semibold\">{title}</h2>\n\n {description && (\n <p className=\"max-w-140 text-center text-sf-strong\">{description}</p>\n )}\n\n {commandLine && (\n <div\n className={cn(\n \"group/cmd relative inline-flex h-10 max-w-8/10 transform-gpu items-center gap-2 rounded-lg font-mono shadow-sm\",\n \"bg-sf-overlay pr-2 pl-3\",\n \"transition-all duration-300 hover:border-sf-interact/80 hover:shadow-md\",\n \"border border-sf-fill/60\"\n )}\n >\n <span className=\"text-xs text-sf-inactive select-none\">$</span>\n <span className=\"no-scrollbar overflow-scroll text-[14px] whitespace-nowrap text-sf-brand\">\n {commandLine}\n </span>\n <Button\n className=\"group\"\n size=\"sm\"\n variant=\"ghost\"\n shape=\"square\"\n aria-label=\"Copy command\"\n onClick={async () => {\n setEmptyStateCopied(true);\n setTimeout(() => {\n setEmptyStateCopied(false);\n }, 1000);\n await navigator.clipboard.writeText(commandLine);\n }}\n >\n {emptyStateCopied ? (\n <CheckIcon\n size={16}\n className=\"animate-bounce-in text-sf-success\"\n />\n ) : (\n <CopyIcon\n size={16}\n className=\"text-sf-inactive group-hover:text-sf-brand\"\n />\n )}\n </Button>\n </div>\n )}\n\n {contents}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,oBAAoB,EAC/B,MAAM;CACJ,IAAI;EACF,SAAS;EACT,aAAa;EACd;CACD,MAAM;EACJ,SAAS;EACT,aAAa;EACd;CACD,IAAI;EACF,SAAS;EACT,aAAa;EACd;CACF,EACF;AAED,IAAa,4BAA4B,EACvC,MAAM,QACP;AAeD,SAAgB,cAAc,EAC5B,OAAO,0BAA0B,SACT,EAAE,EAAE;AAC5B,QAAO,GACL,oGACA,kBAAkB,KAAK,MAAM,QAC9B;;;;;;;;;;AAuCH,SAAgB,MAAM,EACpB,MACA,OACA,aACA,aACA,UACA,OAAO,QACP,aACa;CACb,MAAM,CAAC,kBAAkB,uBAAuB,SAAkB,MAAM;AAExE,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,cAAc,EAAE,MAAM,CAAC,EAAE,UAAU;YAAtD;GACG;GACD,oBAAC,MAAD;IAAI,WAAU;cAA0B;IAAW,CAAA;GAElD,eACC,oBAAC,KAAD;IAAG,WAAU;cAAwC;IAAgB,CAAA;GAGtE,eACC,qBAAC,OAAD;IACE,WAAW,GACT,kHACA,2BACA,2EACA,2BACD;cANH;KAQE,oBAAC,QAAD;MAAM,WAAU;gBAAuC;MAAQ,CAAA;KAC/D,oBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACP,oBAAC,QAAD;MACE,WAAU;MACV,MAAK;MACL,SAAQ;MACR,OAAM;MACN,cAAW;MACX,SAAS,YAAY;AACnB,2BAAoB,KAAK;AACzB,wBAAiB;AACf,4BAAoB,MAAM;UACzB,IAAK;AACR,aAAM,UAAU,UAAU,UAAU,YAAY;;gBAGjD,mBACC,oBAAC,WAAD;OACE,MAAM;OACN,WAAU;OACV,CAAA,GAEF,oBAAC,UAAD;OACE,MAAM;OACN,WAAU;OACV,CAAA;MAEG,CAAA;KACL;;GAGP;GACG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"filters-Bw_U6ZTx.js","names":[],"sources":["../src/components/filters/helpers.ts","../src/components/filters/filters.tsx"],"sourcesContent":["import type {\n Filter,\n FilterFieldConfig,\n FilterOperator,\n FilterI18nConfig,\n} from \"./types\";\n\n// ============================================================================\n// Default Operators by Field Type\n// ============================================================================\n\n/** Default operators for text fields */\nexport const DEFAULT_TEXT_OPERATORS: FilterOperator[] = [\n { value: \"is\", label: \"is\" },\n { value: \"is_not\", label: \"is not\" },\n { value: \"contains\", label: \"contains\" },\n { value: \"does_not_contain\", label: \"does not contain\" },\n { value: \"starts_with\", label: \"starts with\" },\n { value: \"ends_with\", label: \"ends with\" },\n { value: \"is_empty\", label: \"is empty\" },\n { value: \"is_not_empty\", label: \"is not empty\" },\n];\n\n/** Default operators for select fields */\nexport const DEFAULT_SELECT_OPERATORS: FilterOperator[] = [\n { value: \"is\", label: \"is\" },\n { value: \"is_not\", label: \"is not\" },\n { value: \"is_empty\", label: \"is empty\" },\n { value: \"is_not_empty\", label: \"is not empty\" },\n];\n\n/** Default operators for multiselect fields */\nexport const DEFAULT_MULTISELECT_OPERATORS: FilterOperator[] = [\n { value: \"is_any_of\", label: \"is any of\" },\n { value: \"is_none_of\", label: \"is none of\" },\n { value: \"is_all_of\", label: \"is all of\" },\n { value: \"is_empty\", label: \"is empty\" },\n { value: \"is_not_empty\", label: \"is not empty\" },\n];\n\n/** Map of field types to their default operators */\nexport const DEFAULT_OPERATORS: Record<string, FilterOperator[]> = {\n text: DEFAULT_TEXT_OPERATORS,\n select: DEFAULT_SELECT_OPERATORS,\n multiselect: DEFAULT_MULTISELECT_OPERATORS,\n};\n\n/** Get default operator for a field type */\nexport function getDefaultOperatorForFieldType(\n type: string,\n operators?: FilterOperator[]\n): string {\n if (operators && operators.length > 0) {\n return operators[0].value;\n }\n const defaults = DEFAULT_OPERATORS[type];\n return defaults?.[0]?.value ?? \"is\";\n}\n\n// ============================================================================\n// Filter Factory Functions\n// ============================================================================\n\n/**\n * Create a new filter with a unique ID.\n *\n * @example\n * ```tsx\n * const filter = createFilter(\"status\", \"is\", [\"active\"]);\n * setFilters(prev => [...prev, filter]);\n * ```\n */\nexport function createFilter(\n field: string,\n operator: string,\n values: string[]\n): Filter {\n return {\n id: crypto.randomUUID(),\n field,\n operator,\n values,\n };\n}\n\n/**\n * Create multiple filters at once, each with a unique ID.\n *\n * @example\n * ```tsx\n * const filters = createFilterGroup([\n * { field: \"status\", operator: \"is\", values: [\"active\"] },\n * { field: \"priority\", operator: \"is_any_of\", values: [\"high\", \"critical\"] },\n * ]);\n * ```\n */\nexport function createFilterGroup(\n filters: Array<Omit<Filter, \"id\">>\n): Filter[] {\n return filters.map((f) => ({\n ...f,\n id: crypto.randomUUID(),\n }));\n}\n\n// ============================================================================\n// i18n Defaults\n// ============================================================================\n\n/** Default i18n configuration */\nexport const DEFAULT_I18N: FilterI18nConfig = {\n addFilter: \"Add Filter\",\n clearAll: \"Clear All\",\n searchPlaceholder: \"Search fields...\",\n noResults: \"No fields found\",\n operators: {\n is: \"is\",\n is_not: \"is not\",\n contains: \"contains\",\n does_not_contain: \"does not contain\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n is_any_of: \"is any of\",\n is_none_of: \"is none of\",\n is_all_of: \"is all of\",\n },\n valueLabel: \"Value\",\n noValueLabel: \"None\",\n removeFilter: \"Remove filter\",\n changeOperator: \"Change operator\",\n changeValue: \"Change value\",\n maxSelectionsReached: \"Maximum selections reached\",\n validationError: \"Invalid value\",\n};\n\n/** Merge partial i18n config with defaults */\nexport function mergeI18n(\n partial?: Partial<FilterI18nConfig>\n): FilterI18nConfig {\n return {\n ...DEFAULT_I18N,\n ...partial,\n operators: {\n ...DEFAULT_I18N.operators,\n ...partial?.operators,\n },\n };\n}\n\n// ============================================================================\n// Field Helpers\n// ============================================================================\n\n/** Get operators for a field, falling back to defaults */\nexport function getFieldOperators(field: FilterFieldConfig): FilterOperator[] {\n if (field.operators && field.operators.length > 0) {\n return field.operators;\n }\n return DEFAULT_OPERATORS[field.type] ?? DEFAULT_TEXT_OPERATORS;\n}\n\n/** Get field configuration by key */\nexport function getFieldByKey(\n fields: FilterFieldConfig[],\n key: string\n): FilterFieldConfig | undefined {\n return fields.find((f) => f.key === key);\n}\n\n/** Group fields by their group property */\nexport function groupFields(\n fields: FilterFieldConfig[]\n): Map<string, FilterFieldConfig[]> {\n const groups = new Map<string, FilterFieldConfig[]>();\n\n for (const field of fields) {\n const group = field.group ?? \"Fields\";\n if (!groups.has(group)) {\n groups.set(group, []);\n }\n groups.get(group)!.push(field);\n }\n\n return groups;\n}\n\n/** Check if a filter value is valid for a field */\nexport function validateFilterValue(\n field: FilterFieldConfig,\n values: string[]\n): { valid: boolean; message?: string } {\n // Check max selections\n if (field.maxSelections && values.length > field.maxSelections) {\n return {\n valid: false,\n message: `Maximum ${field.maxSelections} selections allowed`,\n };\n }\n\n // Run custom validation if provided\n if (field.validation) {\n const result = field.validation(values);\n if (typeof result === \"boolean\") {\n return { valid: result, message: result ? undefined : \"Invalid value\" };\n }\n return result;\n }\n\n return { valid: true };\n}\n\n// ============================================================================\n// Value Formatting\n// ============================================================================\n\n/** Format a filter value for display in the filter pill */\nexport function formatFilterValue(\n values: string[],\n field: FilterFieldConfig\n): string {\n // Use custom renderer if provided\n if (field.customValueRenderer) {\n // Return a placeholder - the actual rendering happens in React\n return \"...\";\n }\n\n // Empty values\n if (values.length === 0) {\n return \"None\";\n }\n\n // Single value\n if (values.length === 1) {\n const value = values[0];\n // Try to find option label\n if (field.options) {\n const option = field.options.find((o) => o.value === value);\n if (option) return option.label;\n }\n return String(value);\n }\n\n // Multiple values\n if (field.options) {\n const labels = values.map((v) => {\n const option = field.options!.find((o) => o.value === v);\n return option?.label ?? String(v);\n });\n return `${labels.slice(0, 2).join(\", \")}${labels.length > 2 ? ` +${labels.length - 2}` : \"\"}`;\n }\n\n return `${values.length} selected`;\n}\n\n/** Get the label for a filter value from options */\nexport function getValueLabel(\n value: string,\n options?: Array<{ value: string; label: string }>\n): string {\n if (!options) return String(value);\n const option = options.find((o) => o.value === value);\n return option?.label ?? String(value);\n}\n","\"use client\";\n\nimport {\n Check,\n CaretDown,\n MagnifyingGlass,\n Plus,\n X,\n} from \"@phosphor-icons/react\";\nimport {\n useCallback,\n useEffect,\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 { Input, InputGroup } from \"../input\";\nimport { Popover } from \"../popover\";\nimport {\n formatFilterValue,\n getDefaultOperatorForFieldType,\n getFieldOperators,\n groupFields,\n mergeI18n,\n validateFilterValue,\n createFilter,\n createFilterGroup,\n} from \"./helpers\";\nimport type {\n Filter,\n FilterCustomRendererProps,\n FilterFieldConfig,\n FiltersProps,\n FilterI18nConfig,\n} from \"./types\";\n\nexport { createFilter, createFilterGroup } from \"./helpers\";\n\nexport type {\n Filter,\n FilterFieldConfig,\n FilterOption,\n FilterOperator,\n FilterI18nConfig,\n FiltersProps,\n FilterCustomRendererProps,\n} from \"./types\";\n\n// ============================================================================\n// Variants\n// ============================================================================\n\n/** Filters size variant definitions */\nexport const SF_FILTERS_VARIANTS = {\n size: {\n sm: {\n classes: \"text-xs\",\n description: \"Small filter items\",\n },\n default: {\n classes: \"text-sm\",\n description: \"Default filter items\",\n },\n lg: {\n classes: \"text-base\",\n description: \"Large filter items\",\n },\n },\n} as const;\n\n/** Default variants for Filters */\nexport const SF_FILTERS_DEFAULT_VARIANTS = {\n size: \"default\",\n} as const;\n\n// ============================================================================\n// Filter Value Editor (internal)\n// ============================================================================\n\ninterface FilterValueEditorProps {\n field: FilterFieldConfig;\n values: string[];\n onChange: (values: string[]) => void;\n onClose: () => void;\n i18n: FilterI18nConfig;\n}\n\nfunction FilterValueEditor({\n field,\n values,\n onChange,\n onClose,\n i18n,\n}: FilterValueEditorProps) {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [localValues, setLocalValues] = useState<string[]>(values);\n const [validationError, setValidationError] = useState<string | null>(null);\n\n const options = useMemo(() => {\n if (!field.options) return [];\n if (!searchQuery) return field.options;\n return field.options.filter((o: { label: string }) =>\n o.label.toLowerCase().includes(searchQuery.toLowerCase())\n );\n }, [field.options, searchQuery]);\n\n const handleValueChange = useCallback(\n (newValues: string[]) => {\n setLocalValues(newValues);\n const validation = validateFilterValue(field, newValues);\n if (!validation.valid) {\n setValidationError(validation.message ?? i18n.validationError);\n } else {\n setValidationError(null);\n }\n },\n [field, i18n.validationError]\n );\n\n const handleApply = useCallback(() => {\n const validation = validateFilterValue(field, localValues);\n if (!validation.valid) {\n setValidationError(validation.message ?? i18n.validationError);\n return;\n }\n onChange(localValues);\n onClose();\n }, [field, localValues, onChange, onClose, i18n.validationError]);\n\n // Text type\n if (field.type === \"text\") {\n return (\n <div className=\"flex flex-col gap-2 p-2\">\n <Input\n value={String(localValues[0] ?? \"\")}\n onChange={(e) => handleValueChange([e.target.value])}\n placeholder={field.placeholder}\n className=\"w-full\"\n />\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onClose}>\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleApply}>\n Apply\n </Button>\n </div>\n </div>\n );\n }\n\n // Select type\n if (field.type === \"select\") {\n return (\n <div className=\"flex max-h-72 flex-col gap-2\">\n {field.searchable && (\n <div className=\"border-b border-sf-line pb-2\">\n <InputGroup className=\"w-full\">\n <InputGroup.Input\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={i18n.searchPlaceholder}\n className=\"w-full\"\n />\n <InputGroup.Button>\n <MagnifyingGlass className=\"size-4\" />\n </InputGroup.Button>\n </InputGroup>\n </div>\n )}\n <div className=\"flex flex-col gap-0.5 overflow-auto\">\n {options.map((option) => (\n <button\n key={String(option.value)}\n type=\"button\"\n onClick={() => {\n handleValueChange([option.value]);\n handleApply();\n }}\n className={cn(\n \"flex items-center justify-between rounded-md px-2 py-1.5 text-sm\",\n \"hover:bg-sf-tint\",\n localValues[0] === option.value && \"bg-sf-tint\"\n )}\n >\n <span className=\"flex items-center gap-2\">\n {option.icon}\n {option.label}\n </span>\n {localValues[0] === option.value && (\n <Check className=\"size-4 text-sf-brand\" />\n )}\n </button>\n ))}\n {options.length === 0 && (\n <div className=\"py-4 text-center text-sm text-sf-subtle\">\n {i18n.noResults}\n </div>\n )}\n </div>\n </div>\n );\n }\n\n // Multiselect type\n if (field.type === \"multiselect\") {\n const isMaxReached =\n field.maxSelections !== undefined &&\n localValues.length >= field.maxSelections;\n\n return (\n <div className=\"flex max-h-72 w-56 flex-col gap-2\">\n {field.searchable && (\n <div className=\"border-b border-sf-line pb-2\">\n <InputGroup className=\"w-full\">\n <InputGroup.Input\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={i18n.searchPlaceholder}\n className=\"w-full\"\n />\n <InputGroup.Button>\n <MagnifyingGlass className=\"size-4\" />\n </InputGroup.Button>\n </InputGroup>\n </div>\n )}\n <div className=\"flex flex-col gap-0.5 overflow-auto\">\n {options.map((option) => {\n const isSelected = localValues.includes(option.value);\n const isDisabled = isMaxReached && !isSelected;\n\n return (\n <label\n key={String(option.value)}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm\",\n !isDisabled && \"hover:bg-sf-tint\",\n isDisabled && \"cursor-not-allowed opacity-50\"\n )}\n >\n <Checkbox\n checked={isSelected}\n disabled={isDisabled}\n onCheckedChange={(checked) => {\n if (checked) {\n handleValueChange([...localValues, option.value]);\n } else {\n handleValueChange(\n localValues.filter((v) => v !== option.value)\n );\n }\n }}\n />\n <span className=\"flex items-center gap-2\">\n {option.icon}\n {option.label}\n </span>\n </label>\n );\n })}\n {options.length === 0 && (\n <div className=\"py-4 text-center text-sm text-sf-subtle\">\n {i18n.noResults}\n </div>\n )}\n </div>\n {isMaxReached && (\n <div className=\"text-xs text-sf-subtle\">\n {i18n.maxSelectionsReached}\n </div>\n )}\n {validationError && (\n <div className=\"text-xs text-sf-danger\">{validationError}</div>\n )}\n <div className=\"flex justify-end gap-2 border-t border-sf-line pt-2\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onClose}>\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleApply}>\n Apply\n </Button>\n </div>\n </div>\n );\n }\n\n // Custom type\n if (field.type === \"custom\" && field.customRenderer) {\n const props: FilterCustomRendererProps = {\n values: localValues,\n onChange: handleValueChange,\n options: field.options,\n placeholder: field.placeholder,\n field,\n operator: \"is\",\n };\n\n return (\n <div className=\"flex flex-col gap-2 p-2\">\n {field.customRenderer(props)}\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"secondary\" size=\"sm\" onClick={onClose}>\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleApply}>\n Apply\n </Button>\n </div>\n </div>\n );\n }\n\n return null;\n}\n\n// ============================================================================\n// Filter Item (internal)\n// ============================================================================\n\ninterface FilterItemProps {\n filter: Filter;\n field: FilterFieldConfig;\n size: \"sm\" | \"default\" | \"lg\";\n i18n: FilterI18nConfig;\n onRemove: () => void;\n onOperatorChange: (operator: string) => void;\n onValuesChange: (values: string[]) => void;\n}\n\nfunction FilterItem({\n filter,\n field,\n size,\n i18n,\n onRemove,\n onOperatorChange,\n onValuesChange,\n}: FilterItemProps) {\n const [valuePopoverOpen, setValuePopoverOpen] = useState(false);\n const operators = useMemo(() => getFieldOperators(field), [field]);\n\n const sizeClasses = {\n sm: \"text-xs py-0.5 px-1.5\",\n default: \"text-sm py-1 px-2\",\n lg: \"text-base py-1.5 px-2.5\",\n };\n\n const displayValue = useMemo(() => {\n if (field.customValueRenderer) {\n return field.customValueRenderer(filter.values, field.options);\n }\n return formatFilterValue(filter.values, field);\n }, [field, filter.values]);\n\n return (\n <div\n className={cn(\n \"inline-flex items-center gap-0 rounded-md border border-sf-line bg-sf-tint\",\n sizeClasses[size]\n )}\n >\n {/* Field label */}\n <span className=\"flex items-center gap-1 whitespace-nowrap font-medium text-sf-default\">\n {field.icon}\n {field.label}\n </span>\n\n {/* Separator */}\n <span className=\"mx-1.5 h-3 w-px bg-sf-fill\" />\n\n {/* Operator dropdown */}\n <DropdownMenu>\n <DropdownMenu.Trigger className=\"inline-flex cursor-pointer items-center gap-1 whitespace-nowrap text-sf-strong hover:text-sf-default\">\n {operators.find((o) => o.value === filter.operator)?.label ??\n filter.operator}\n <CaretDown className=\"size-3\" />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content>\n {operators.map((op) => (\n <DropdownMenu.Item\n key={op.value}\n selected={filter.operator === op.value}\n onClick={() => onOperatorChange(op.value)}\n >\n {op.label}\n </DropdownMenu.Item>\n ))}\n </DropdownMenu.Content>\n </DropdownMenu>\n\n {/* Separator */}\n <span className=\"mx-1.5 h-3 w-px bg-sf-fill\" />\n\n {/* Value editor popover */}\n <Popover open={valuePopoverOpen} onOpenChange={setValuePopoverOpen}>\n <Popover.Trigger\n asChild\n className=\"inline-flex cursor-pointer items-center gap-1 whitespace-nowrap text-sf-strong hover:text-sf-default\"\n >\n <button type=\"button\" aria-label={i18n.changeValue}>\n <span className=\"max-w-32 truncate\">\n {filter.values.length === 0 ? i18n.noValueLabel : displayValue}\n </span>\n <CaretDown className=\"size-3\" />\n </button>\n </Popover.Trigger>\n <Popover.Content className=\"w-auto min-w-48\">\n <FilterValueEditor\n field={field}\n values={filter.values}\n onChange={onValuesChange}\n onClose={() => setValuePopoverOpen(false)}\n i18n={i18n}\n />\n </Popover.Content>\n </Popover>\n\n {/* Remove button */}\n <button\n type=\"button\"\n onClick={onRemove}\n aria-label={i18n.removeFilter}\n className=\"ml-1.5 inline-flex items-center justify-center rounded p-0.5 text-sf-subtle hover:bg-sf-overlay hover:text-sf-default\"\n >\n <X className=\"size-3.5\" />\n </button>\n </div>\n );\n}\n\n// ============================================================================\n// Filter Add Menu (internal)\n// ============================================================================\n\ninterface FilterAddMenuProps {\n fields: FilterFieldConfig[];\n filters: Filter[];\n size: \"sm\" | \"default\" | \"lg\";\n trigger?: ReactNode;\n showSearchInput?: boolean;\n allowMultiple?: boolean;\n i18n: FilterI18nConfig;\n onAddFilter: (filter: Filter) => void;\n}\n\nfunction FilterAddMenu({\n fields,\n filters,\n size,\n trigger,\n showSearchInput = true,\n allowMultiple = true,\n i18n,\n onAddFilter,\n}: FilterAddMenuProps) {\n const [open, setOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(\"\");\n\n const groupedFields = useMemo(() => groupFields(fields), [fields]);\n\n const availableFields = useMemo(() => {\n if (allowMultiple) return fields;\n const usedKeys = new Set(filters.map((f) => f.field));\n return fields.filter((f) => !usedKeys.has(f.key));\n }, [fields, filters, allowMultiple]);\n\n const filteredGroups = useMemo(() => {\n if (!searchQuery) return groupedFields;\n\n const filtered = new Map<string, FilterFieldConfig[]>();\n for (const [group, groupFields] of groupedFields) {\n const matching = groupFields.filter(\n (f) =>\n f.label.toLowerCase().includes(searchQuery.toLowerCase()) ||\n f.key.toLowerCase().includes(searchQuery.toLowerCase())\n );\n if (matching.length > 0) {\n filtered.set(group, matching);\n }\n }\n return filtered;\n }, [groupedFields, searchQuery]);\n\n const handleFieldClick = useCallback(\n (field: FilterFieldConfig) => {\n const operator =\n field.defaultOperator ??\n getDefaultOperatorForFieldType(field.type, field.operators);\n const newFilter = createFilter(field.key, operator, []);\n onAddFilter(newFilter);\n setOpen(false);\n setSearchQuery(\"\");\n },\n [onAddFilter]\n );\n\n const sizeClasses = {\n sm: \"text-xs h-7 px-2\",\n default: \"text-sm h-8 px-2.5\",\n lg: \"text-base h-9 px-3\",\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <Popover.Trigger asChild>\n {trigger ?? (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n className={cn(\n \"inline-flex items-center gap-1 whitespace-nowrap\",\n sizeClasses[size]\n )}\n >\n <Plus className=\"size-3.5\" />\n {i18n.addFilter}\n </Button>\n )}\n </Popover.Trigger>\n <Popover.Content className=\"w-64 p-2\">\n {showSearchInput && (\n <div className=\"mb-2\">\n <InputGroup className=\"w-full\">\n <InputGroup.Input\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n placeholder={i18n.searchPlaceholder}\n className=\"w-full\"\n />\n <InputGroup.Button>\n <MagnifyingGlass className=\"size-4\" />\n </InputGroup.Button>\n </InputGroup>\n </div>\n )}\n <div className=\"max-h-72 overflow-auto\">\n {filteredGroups.size === 0 ? (\n <div className=\"py-4 text-center text-sm text-sf-subtle\">\n {i18n.noResults}\n </div>\n ) : (\n Array.from(filteredGroups.entries()).map(\n ([group, groupFields], groupIndex) => (\n <div key={group}>\n {groupIndex > 0 && <div className=\"my-2 h-px bg-sf-line\" />}\n <div className=\"mb-1 px-2 text-xs font-medium text-sf-subtle\">\n {group}\n </div>\n <div className=\"flex flex-col gap-0.5\">\n {groupFields.map((field) => (\n <button\n key={field.key}\n type=\"button\"\n onClick={() => handleFieldClick(field)}\n disabled={\n !availableFields.some((f) => f.key === field.key)\n }\n className={cn(\n \"flex items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm\",\n \"hover:bg-sf-tint\",\n \"disabled:cursor-not-allowed disabled:opacity-50\"\n )}\n >\n {field.icon}\n {field.label}\n </button>\n ))}\n </div>\n </div>\n )\n )\n )}\n </div>\n </Popover.Content>\n </Popover>\n );\n}\n\n// ============================================================================\n// Main Filters Component\n// ============================================================================\n\n/**\n * Filters component for building and managing active filter conditions.\n *\n * Supports multiple filter types (text, select, multiselect, custom), operators,\n * and an \"Add Filter\" popover interface. Designed to work standalone or with\n * DataGrid for advanced data filtering.\n *\n * @example\n * ```tsx\n * const [filters, setFilters] = useState<Filter[]>([]);\n * const fields: FilterFieldConfig[] = [\n * { key: \"status\", label: \"Status\", type: \"select\", options: [\n * { value: \"active\", label: \"Active\" },\n * { value: \"inactive\", label: \"Inactive\" },\n * ]},\n * { key: \"name\", label: \"Name\", type: \"text\" },\n * ];\n *\n * <Filters\n * filters={filters}\n * fields={fields}\n * onChange={setFilters}\n * />\n * ```\n */\nexport function Filters({\n filters,\n fields,\n onChange,\n size = SF_FILTERS_DEFAULT_VARIANTS.size,\n trigger,\n showSearchInput = true,\n allowMultiple = true,\n enableShortcut = true,\n shortcutKey = \"k\",\n shortcutLabel,\n i18n: i18nProp,\n className,\n menuPopupClassName,\n}: FiltersProps) {\n const i18n = useMemo(() => mergeI18n(i18nProp), [i18nProp]);\n\n // Keyboard shortcut\n useEffect(() => {\n if (!enableShortcut) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === shortcutKey) {\n e.preventDefault();\n // The add menu popover will need to be opened via a ref or state\n // For now, this is a placeholder for the shortcut behavior\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [enableShortcut, shortcutKey]);\n\n const handleAddFilter = useCallback(\n (filter: Filter) => {\n onChange([...filters, filter]);\n },\n [filters, onChange]\n );\n\n const handleRemoveFilter = useCallback(\n (id: string) => {\n onChange(filters.filter((f) => f.id !== id));\n },\n [filters, onChange]\n );\n\n const handleOperatorChange = useCallback(\n (id: string, operator: string) => {\n onChange(filters.map((f) => (f.id === id ? { ...f, operator } : f)));\n },\n [filters, onChange]\n );\n\n const handleValuesChange = useCallback(\n (id: string, values: string[]) => {\n onChange(filters.map((f) => (f.id === id ? { ...f, values } : f)));\n },\n [filters, onChange]\n );\n\n const handleClearAll = useCallback(() => {\n onChange([]);\n }, [onChange]);\n\n const fieldMap = useMemo(() => {\n const map = new Map<string, FilterFieldConfig>();\n for (const field of fields) {\n map.set(field.key, field);\n }\n return map;\n }, [fields]);\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-2\", className)}>\n {/* Active filter pills */}\n {filters.map((filter) => {\n const field = fieldMap.get(filter.field);\n if (!field) return null;\n\n return (\n <FilterItem\n key={filter.id}\n filter={filter}\n field={field}\n size={size}\n i18n={i18n}\n onRemove={() => handleRemoveFilter(filter.id)}\n onOperatorChange={(op) => handleOperatorChange(filter.id, op)}\n onValuesChange={(vals) => handleValuesChange(filter.id, vals)}\n />\n );\n })}\n\n {/* Add filter menu */}\n <div className={menuPopupClassName}>\n <FilterAddMenu\n fields={fields}\n filters={filters}\n size={size}\n trigger={trigger}\n showSearchInput={showSearchInput}\n allowMultiple={allowMultiple}\n i18n={i18n}\n onAddFilter={handleAddFilter}\n />\n </div>\n\n {/* Clear all button */}\n {filters.length > 0 && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleClearAll}\n className=\"text-sf-subtle hover:text-sf-default\"\n >\n {i18n.clearAll}\n </Button>\n )}\n\n {/* Keyboard shortcut hint */}\n {enableShortcut && shortcutLabel && (\n <span className=\"ml-auto text-xs text-sf-subtle\">{shortcutLabel}</span>\n )}\n </div>\n );\n}\n\nFilters.displayName = \"Filters\";\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"flow-BRsYUCJa.js","names":[],"sources":["../src/components/flow/connectors.tsx","../src/components/flow/use-children.tsx","../src/components/flow/diagram.tsx","../src/components/flow/node.tsx","../src/components/flow/parallel.tsx","../src/components/flow/index.ts"],"sourcesContent":["import { forwardRef, useId, type ReactNode } from \"react\";\n\nexport interface Connector {\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n isBottom?: boolean;\n}\n\ntype ConnectorsProps = {\n connectors: Connector[];\n children?: ReactNode;\n} & Omit<PathProps, \"isBottom\">;\n\ntype PathProps = Partial<{\n cornerRadius: number;\n midOffset: number;\n arrowheadOffset: number;\n isBottom: boolean;\n single: boolean;\n orientation: \"vertical\" | \"horizontal\";\n}>;\n\n/**\n * Maximum vertical/horizontal distance between anchor points where the line\n * would render as straight. Below this number, the line will be drawn as a\n * straight line.\n */\nconst FLAT_THRESHOLD = 2;\n\nexport function createRoundedPath(\n { x1, y1, x2, y2 }: { x1: number; y1: number; x2: number; y2: number },\n {\n cornerRadius: maxCornerRadius = 8,\n midOffset = 32,\n arrowheadOffset = 8,\n isBottom = false,\n single = false,\n orientation = \"vertical\",\n }: PathProps = {}\n) {\n /**\n * Cap the corner radius to half the vertical/horizontal distance between the\n * anchor points so the line still looks smooth even in small gaps.\n */\n const cornerRadius = Math.min(\n maxCornerRadius,\n Math.abs(orientation === \"horizontal\" ? (y2 - y1) / 2 : (x2 - x1) / 2)\n );\n if (orientation === \"horizontal\") {\n if (Math.abs(y2 - y1) <= FLAT_THRESHOLD)\n return `M ${x1} ${y1} L ${x2 - arrowheadOffset} ${y2}`;\n\n // Horizontal orientation: horizontal → vertical → horizontal\n // verticalOffset is used as horizontalOffset (distance from x2 where we turn)\n const verticalX = x2 - midOffset;\n const isGoingRight = x2 > x1;\n const horizontalSign = isGoingRight ? 1 : -1;\n const isGoingDown = y2 > y1;\n const verticalSign = isGoingDown ? 1 : -1;\n\n // First horizontal segment stops before the first corner\n const firstHorizontalEnd = verticalX - horizontalSign * cornerRadius;\n // Vertical segment starts after the first corner\n const verticalStart = y1 + verticalSign * cornerRadius;\n // Vertical segment stops before the second corner\n const verticalEnd = y2 - verticalSign * cornerRadius;\n // Second horizontal segment starts after the second corner\n const secondHorizontalStart = verticalX + horizontalSign * cornerRadius;\n // Path ends before the arrowhead (on x-axis)\n const pathEndX = x2 - horizontalSign * arrowheadOffset;\n\n const bottomCurveCommands = [\n `L ${firstHorizontalEnd} ${y1}`,\n `Q ${verticalX} ${y1} ${verticalX} ${verticalStart}`,\n `L ${verticalX} ${y2}`,\n ];\n\n const topCurveCommands = [\n single\n ? `L ${firstHorizontalEnd} ${y1} Q ${verticalX} ${y1} ${verticalX} ${verticalStart}`\n : `L ${verticalX} ${y1}`,\n `L ${verticalX} ${verticalEnd}`,\n `Q ${verticalX} ${y2} ${secondHorizontalStart} ${y2}`,\n ];\n\n const commands = [\n `M ${x1} ${y1}`,\n isBottom ? [...bottomCurveCommands] : [...topCurveCommands],\n `L ${pathEndX} ${y2}`,\n ];\n\n return commands.join(\" \");\n }\n\n if (Math.abs(x2 - x1) <= FLAT_THRESHOLD)\n return `M ${x1} ${y1} L ${x2} ${y2 - arrowheadOffset}`;\n\n // Vertical orientation: vertical → horizontal → vertical\n // Vertical offset before turning horizontally\n const horizontalY = y2 - midOffset;\n const isGoingRight = x2 > x1;\n const horizontalSign = isGoingRight ? 1 : -1;\n const isGoingDown = y2 > y1;\n const verticalSign = isGoingDown ? 1 : -1;\n\n // First vertical segment stops before the first corner (going down)\n const firstVerticalEnd = horizontalY - cornerRadius;\n // Horizontal segment starts after the first corner\n const horizontalStart = x1 + horizontalSign * cornerRadius;\n // Horizontal segment stops before the second corner\n const horizontalEnd = x2 - horizontalSign * cornerRadius;\n // Second vertical segment starts after the second corner (going down)\n const secondVerticalStart = horizontalY + cornerRadius;\n // Path ends before the arrowhead\n const pathEndY = y2 - verticalSign * arrowheadOffset;\n\n const bottomCurveCommands = [\n `L ${x1} ${firstVerticalEnd}`,\n `Q ${x1} ${horizontalY} ${horizontalStart} ${horizontalY}`,\n `L ${x2} ${horizontalY}`,\n ];\n\n const topCurveCommands = [\n `L ${x1} ${horizontalY}`,\n `L ${horizontalEnd} ${horizontalY}`,\n `Q ${x2} ${horizontalY} ${x2} ${secondVerticalStart}`,\n ];\n\n const commands = [\n `M ${x1} ${y1}`, // Move the cursor to the starting point\n isBottom ? [...bottomCurveCommands] : [...topCurveCommands],\n `L ${x2} ${pathEndY}`, // Draw the final line to the end point\n ];\n\n return commands.join(\" \");\n}\n\nexport const Connectors = forwardRef<SVGSVGElement, ConnectorsProps>(\n function Connectors({ connectors, children, ...pathProps }, svgRef) {\n const id = useId();\n return (\n <svg\n width=\"100%\"\n height=\"100%\"\n aria-hidden=\"true\"\n className=\"text-sf-inactive overflow-visible\"\n ref={svgRef}\n >\n <defs>\n <marker\n id={id}\n markerWidth=\"8\"\n markerHeight=\"8\"\n refX=\"0\"\n refY=\"4\"\n orient=\"auto\"\n markerUnits=\"userSpaceOnUse\"\n >\n <path\n d=\"M 0,1.5 Q 0,0 1.5,0 Q 3.5,1 5.8,3.2 Q 6.5,4 5.8,4.8 Q 3.5,7 1.5,8 Q 0,8 0,6.5 Z\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n </marker>\n </defs>\n {connectors.map((connector, index) => {\n const path = createRoundedPath(connector, {\n isBottom: connector.isBottom,\n ...pathProps,\n });\n return (\n <path\n key={index}\n d={path}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n markerEnd={`url(#${id})`}\n data-index={index}\n />\n );\n })}\n {children}\n </svg>\n );\n }\n);\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\n// ============================================================================\n// Types\n// ============================================================================\nexport type DescendantInfo<T = Record<string, unknown>> = {\n id: string;\n props: T;\n renderOrder: number;\n};\n\ntype DescendantsContextType<DescendantType = Record<string, unknown>> = {\n register: (\n id: string,\n renderOrder: number,\n props?: DescendantType\n ) => { unregister: () => void };\n descendants: DescendantInfo<DescendantType>[];\n claimRenderOrder: (id: string) => number;\n};\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst DescendantsContext = createContext<DescendantsContextType | null>(null);\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Hook that manages descendant registration and provides access to all registered descendants.\n * This hook contains all the logic for tracking and managing descendants.\n *\n * @returns The descendants context value with register function and descendants array\n */\nexport function useDescendants<\n DescendantType extends Record<string, unknown>,\n>(): DescendantsContextType<DescendantType> {\n const [registeredDescendants, setRegisteredDescendants] = useState<\n DescendantInfo<DescendantType>[]\n >([]);\n const descendantsRef = useRef<Map<string, DescendantInfo<DescendantType>>>(\n new Map()\n );\n\n // Track render order - resets each render cycle\n const renderOrderCounterRef = useRef(0);\n const renderOrderMapRef = useRef<Map<string, number>>(new Map());\n\n // Reset counter at the start of each render cycle\n // This runs synchronously during render, before any children claim their order\n renderOrderCounterRef.current = 0;\n renderOrderMapRef.current.clear();\n\n // Called during render to claim a slot in the render order\n const claimRenderOrder = useCallback((id: string): number => {\n if (!renderOrderMapRef.current.has(id)) {\n renderOrderMapRef.current.set(id, renderOrderCounterRef.current++);\n }\n return renderOrderMapRef.current.get(id) as number;\n }, []);\n\n const register = useCallback(\n (\n id: string,\n renderOrder: number,\n props: DescendantType = {} as DescendantType\n ) => {\n // Add descendant to the map with render order\n const descendantInfo: DescendantInfo<DescendantType> = {\n id,\n props,\n renderOrder,\n };\n descendantsRef.current.set(id, descendantInfo);\n\n // Update state with all descendants sorted by render order\n const sortedDescendants = Array.from(\n descendantsRef.current.values()\n ).sort((a, b) => a.renderOrder - b.renderOrder);\n setRegisteredDescendants(sortedDescendants);\n\n // Return unregister function\n const unregister = () => {\n descendantsRef.current.delete(id);\n const remainingDescendants = Array.from(\n descendantsRef.current.values()\n ).sort((a, b) => a.renderOrder - b.renderOrder);\n setRegisteredDescendants(remainingDescendants);\n };\n\n return { unregister };\n },\n []\n );\n\n const contextValue: DescendantsContextType<DescendantType> = useMemo(\n () => ({\n register,\n descendants: registeredDescendants,\n claimRenderOrder,\n }),\n [register, registeredDescendants, claimRenderOrder]\n );\n\n return contextValue;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\ntype DescendantsProviderProps<T extends Record<string, unknown>> = {\n value: DescendantsContextType<T>;\n children: ReactNode;\n};\n\nexport function DescendantsProvider<T extends Record<string, unknown>>({\n value,\n children,\n}: DescendantsProviderProps<T>) {\n return (\n <DescendantsContext.Provider\n value={value as unknown as DescendantsContextType}\n >\n {children}\n </DescendantsContext.Provider>\n );\n}\n\n// ============================================================================\n// Context Hook\n// ============================================================================\n\n/**\n * Hook to access the descendants context from within a DescendantsProvider.\n * This allows callers to access the descendants data and register function.\n *\n * @returns The descendants context value\n * @throws Error if used outside of DescendantsProvider\n */\nexport function useDescendantsContext<\n T extends Record<string, unknown>,\n>(): DescendantsContextType<T> {\n const context = useContext(DescendantsContext);\n\n if (!context) {\n throw new Error(\n \"useDescendantsContext must be used within DescendantsProvider\"\n );\n }\n\n return context as DescendantsContextType<T>;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\n/**\n * Hook that allows a descendant component to register itself with a parent\n * and returns the index of the descendant in the parent's list.\n *\n * @example\n * ```tsx\n * function Parent() {\n * return (\n * <DescendantsProvider>\n * <Descendant />\n * <Descendant />\n * <Descendant />\n * </DescendantsProvider>\n * );\n * }\n *\n * function Descendant() {\n * const index = useDescendantIndex();\n * return <div>I am descendant {index}</div>;\n * }\n *\n * // With props\n * function Descendant() {\n * const index = useDescendantIndex({ name: \"Descendant 1\", type: \"primary\" });\n * return <div>I am descendant {index}</div>;\n * }\n * ```\n */\n// ============================================================================\n// Descendant Index Hook\n// ============================================================================\n\nexport function useDescendantIndex<T extends Record<string, unknown>>(\n props?: T\n) {\n const context = useDescendantsContext<T>();\n const id = useId();\n\n // Claim render order during render (synchronously, not in useEffect)\n // This captures the order in which descendants render\n const renderOrder = context.claimRenderOrder(id);\n\n const unregisterRef = useRef<(() => void) | null>(null);\n const registerRef = useRef(context.register);\n\n // Keep refs in sync with context\n registerRef.current = context.register;\n\n useEffect(() => {\n // Register or update this descendant with its render order\n const { unregister } = registerRef.current(id, renderOrder, props);\n\n // Store unregister function if not already stored\n if (!unregisterRef.current) {\n unregisterRef.current = unregister;\n }\n\n // Cleanup: unregister when component unmounts\n return () => {\n if (unregisterRef.current) {\n unregisterRef.current();\n unregisterRef.current = null;\n }\n };\n }, [id, renderOrder, props]);\n\n // Derive index from sorted descendants array\n const index = useMemo(() => {\n return context.descendants.findIndex((descendant) => descendant.id === id);\n }, [context.descendants, id]);\n\n const getPrevious = useCallback((): DescendantInfo<T> | undefined => {\n if (index <= 0) return undefined;\n return context.descendants[index - 1];\n }, [context.descendants, index]);\n\n const getNext = useCallback((): DescendantInfo<T> | undefined => {\n if (index < 0 || index >= context.descendants.length - 1) return undefined;\n return context.descendants[index + 1];\n }, [context.descendants, index]);\n\n return { index, id, getPrevious, getNext };\n}\n","import {\n motion,\n useMotionTemplate,\n useMotionValue,\n useTransform,\n type MotionValue,\n type PanInfo,\n} from \"motion/react\";\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Connectors, type Connector } from \"./connectors\";\nimport {\n DescendantsProvider,\n useDescendantIndex,\n useDescendants,\n type DescendantInfo,\n} from \"./use-children\";\n\nconst PAN_SPACING = {\n y: 64,\n x: 16,\n};\n\n/** Minimum scrollbar thumb size in percentage to ensure visibility */\nconst MIN_SCROLLBAR_THUMB_SIZE = 10;\n\n// Vertical orientation is currently a no-op\ntype Orientation = \"horizontal\" | \"vertical\";\ntype Align = \"start\" | \"center\";\n\ninterface DiagramContextValue {\n orientation: Orientation;\n align: Align;\n x: MotionValue<number>;\n y: MotionValue<number>;\n /** Ref to the canvas viewport wrapper element */\n wrapperRef: React.RefObject<HTMLDivElement | null>;\n}\n\nconst DiagramContext = createContext<DiagramContextValue | null>(null);\n\nexport function useDiagramContext(): DiagramContextValue {\n const context = useContext(DiagramContext);\n if (context === null) {\n throw new Error(\"useDiagramContext must be used within a FlowDiagram\");\n }\n return context;\n}\n\ninterface FlowDiagramProps {\n orientation?: Orientation;\n /**\n * Controls vertical alignment of nodes in horizontal orientation.\n * - `start`: Nodes align to the top (default)\n * - `center`: Nodes are vertically centered\n */\n align?: Align;\n className?: string;\n children?: ReactNode;\n}\n\nexport function FlowDiagram({\n orientation = \"horizontal\",\n align = \"start\",\n className,\n children,\n}: FlowDiagramProps) {\n const wrapperRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n\n const x = useMotionValue(0);\n const y = useMotionValue(0);\n const [bounds, setBounds] = useState<{ x: number; y: number } | null>(null);\n const [dimensions, setDimensions] = useState<{\n viewportWidth: number;\n viewportHeight: number;\n contentWidth: number;\n contentHeight: number;\n } | null>(null);\n\n const [isPanning, setIsPanning] = useState(false);\n const [canPan, setCanPan] = useState(false);\n\n useEffect(() => {\n if (!wrapperRef.current || !contentRef.current) return;\n\n const measureBounds = () => {\n if (!wrapperRef.current || !contentRef.current) return;\n\n const wrapper = wrapperRef.current.getBoundingClientRect();\n const content = contentRef.current.getBoundingClientRect();\n\n const availableWidth = wrapper.width - PAN_SPACING.x * 2;\n const availableHeight = wrapper.height - PAN_SPACING.y * 2;\n\n setBounds({\n x: Math.min(0, availableWidth - content.width),\n y: Math.min(0, availableHeight - content.height),\n });\n\n setDimensions({\n viewportWidth: availableWidth,\n viewportHeight: availableHeight,\n contentWidth: content.width,\n contentHeight: content.height,\n });\n\n setCanPan(\n content.width > availableWidth || content.height > availableHeight\n );\n };\n\n measureBounds();\n\n const resizeObserver = new ResizeObserver(measureBounds);\n resizeObserver.observe(wrapperRef.current);\n resizeObserver.observe(contentRef.current);\n\n return () => resizeObserver.disconnect();\n }, []);\n\n useEffect(() => {\n if (!bounds) return;\n\n /**\n * It's possible for the content to resize after the user panned. If we're\n * at the edge of the pan and the content gets smaller, then we've \"panned\n * too far\". In this case, we transition the pan back to the new bounds.\n */\n if (x.get() < bounds.x) {\n x.set(bounds.x);\n }\n if (y.get() < bounds.y) {\n y.set(bounds.y);\n }\n }, [bounds, x, y]);\n\n useEffect(() => {\n return () => {\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n };\n }, []);\n\n // Handle wheel/scroll events for panning\n useEffect(() => {\n const wrapper = wrapperRef.current;\n if (!wrapper) return;\n\n const handleWheel = (e: WheelEvent) => {\n if (!bounds) return;\n\n const canScrollX = bounds.x < 0;\n const canScrollY = bounds.y < 0;\n\n if (!canScrollX && !canScrollY) return;\n\n e.preventDefault();\n\n if (canScrollY) {\n const newY = Math.max(bounds.y, Math.min(0, y.get() - e.deltaY));\n y.set(newY);\n }\n\n if (canScrollX) {\n const newX = Math.max(bounds.x, Math.min(0, x.get() - e.deltaX));\n x.set(newX);\n }\n };\n\n wrapper.addEventListener(\"wheel\", handleWheel, { passive: false });\n return () => wrapper.removeEventListener(\"wheel\", handleWheel);\n }, [bounds, x, y]);\n\n const handlePan = (_: PointerEvent, info: PanInfo) => {\n if (!bounds) return;\n x.set(Math.max(bounds.x, Math.min(0, x.get() + info.delta.x)));\n y.set(Math.max(bounds.y, Math.min(0, y.get() + info.delta.y)));\n };\n\n // Calculate scrollbar dimensions\n const canScrollX = bounds && bounds.x < 0;\n const canScrollY = bounds && bounds.y < 0;\n\n const scrollThumbWidth =\n dimensions && dimensions.contentWidth > 0 && dimensions.viewportWidth > 0\n ? Math.max(\n MIN_SCROLLBAR_THUMB_SIZE,\n (dimensions.viewportWidth / dimensions.contentWidth) * 100\n )\n : 0;\n const scrollThumbHeight =\n dimensions && dimensions.contentHeight > 0 && dimensions.viewportHeight > 0\n ? Math.max(\n MIN_SCROLLBAR_THUMB_SIZE,\n (dimensions.viewportHeight / dimensions.contentHeight) * 100\n )\n : 0;\n\n // Transform pan position to scrollbar thumb position (as percentage)\n const scrollbarXPercent = useTransform(\n x,\n [0, bounds?.x ?? 0],\n [0, 100 - scrollThumbWidth]\n );\n const scrollbarYPercent = useTransform(\n y,\n [0, bounds?.y ?? 0],\n [0, 100 - scrollThumbHeight]\n );\n\n const scrollTop = useMotionTemplate`${scrollbarYPercent}%`;\n const scrollLeft = useMotionTemplate`${scrollbarXPercent}%`;\n\n const contextValue = useMemo(\n () => ({ orientation, align, x, y, wrapperRef }),\n [orientation, align, x, y]\n );\n\n return (\n <DiagramContext.Provider value={contextValue}>\n <motion.div\n ref={wrapperRef}\n className={cn(\n \"relative overflow-hidden py-16 px-4 grow isolate group\",\n className\n )}\n style={{\n cursor: canPan && !isPanning ? \"grab\" : undefined,\n }}\n onPanStart={() => {\n setIsPanning(true);\n document.body.style.cursor = \"grabbing\";\n document.body.style.userSelect = \"none\";\n }}\n onPan={handlePan}\n onPanEnd={() => {\n setIsPanning(false);\n document.body.style.cursor = \"\";\n document.body.style.userSelect = \"\";\n }}\n >\n <motion.div ref={contentRef} className=\"w-max mx-auto\" style={{ x, y }}>\n <FlowNodeList>{children}</FlowNodeList>\n </motion.div>\n\n {/* Vertical scrollbar */}\n {canScrollY && (\n <div className=\"absolute right-1 top-4 bottom-4 w-1.5 rounded-full bg-sf-line/50 opacity-0 group-hover:opacity-100\">\n <motion.div\n className=\"absolute w-full rounded-full bg-sf-fill\"\n style={{\n height: `${scrollThumbHeight}%`,\n top: scrollTop,\n }}\n />\n </div>\n )}\n\n {/* Horizontal scrollbar */}\n {canScrollX && (\n <div className=\"absolute bottom-1 left-4 right-4 h-1.5 rounded-full bg-sf-line/50 opacity-0 group-hover:opacity-100\">\n <motion.div\n className=\"absolute h-full rounded-full bg-sf-fill\"\n style={{\n width: `${scrollThumbWidth}%`,\n left: scrollLeft,\n }}\n />\n </div>\n )}\n </motion.div>\n </DiagramContext.Provider>\n );\n}\n\n// ---\n\nexport type RectLike = {\n x: number;\n y: number;\n top: number;\n left: number;\n right: number;\n bottom: number;\n width: number;\n height: number;\n};\n\nexport type NodeData = {\n parallel?: boolean;\n start?: RectLike | null;\n end?: RectLike | null;\n};\n\nexport const useNodeGroup = () => useDescendants<NodeData>();\n\nexport const useNode = (props: NodeData) => useDescendantIndex<NodeData>(props);\n\nexport const getNodeRect = (\n node: DescendantInfo<NodeData> | undefined,\n { type = \"start\" }: { type?: \"start\" | \"end\" }\n): RectLike | null => {\n if (!node) return null;\n return node.props[type] ?? null;\n};\n\nexport function FlowNodeList({ children }: { children: ReactNode }) {\n const { orientation, align } = useDiagramContext();\n const descendants = useNodeGroup();\n const containerRef = useRef<HTMLDivElement>(null);\n\n const connectors = useMemo(() => {\n const edges: Connector[] = [];\n const nodes = descendants.descendants;\n const containerRect = containerRef.current?.getBoundingClientRect();\n\n const offsetX = containerRect?.left ?? 0;\n const offsetY = containerRect?.top ?? 0;\n\n for (let i = 0; i < nodes.length - 1; i++) {\n const currentRect = getNodeRect(nodes[i], { type: \"start\" });\n const nextRect = getNodeRect(nodes[i + 1], { type: \"end\" });\n\n if (currentRect && nextRect) {\n edges.push({\n x1: currentRect.left - offsetX + currentRect.width,\n y1: currentRect.top - offsetY + currentRect.height / 2,\n x2: nextRect.left - offsetX,\n y2: nextRect.top - offsetY + nextRect.height / 2,\n });\n }\n }\n\n return edges;\n }, [descendants.descendants]);\n\n return (\n <DescendantsProvider value={descendants}>\n <div className=\"relative\" ref={containerRef}>\n <ul\n className={cn(\n \"ml-0 list-none\",\n orientation === \"vertical\"\n ? \"grid auto-rows-min gap-16\"\n : \"flex gap-16\",\n orientation === \"horizontal\" &&\n (align === \"center\" ? \"items-center\" : \"items-start\")\n )}\n >\n {children}\n </ul>\n <div className=\"absolute inset-0 pointer-events-none\">\n <Connectors\n connectors={connectors}\n orientation={orientation}\n single\n />\n </div>\n </div>\n </DescendantsProvider>\n );\n}\n","import {\n cloneElement,\n createContext,\n forwardRef,\n isValidElement,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { useNode, type RectLike } from \"./diagram\";\n\n// Utility to merge refs\nfunction mergeRefs<T>(\n ...refs: (React.Ref<T> | undefined)[]\n): React.RefCallback<T> {\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref != null) {\n (ref as React.MutableRefObject<T | null>).current = value;\n }\n });\n };\n}\n\n/**\n * FlowNode component props.\n *\n * @example Default styling\n * ```tsx\n * <Flow.Node>Step 1</Flow.Node>\n * ```\n *\n * @example Custom render - completely replaces the default element\n * ```tsx\n * <Flow.Node render={<div className=\"custom-node\">Custom content</div>} />\n * ```\n */\nexport type FlowNodeProps = {\n /**\n * Custom element to render instead of the default styled node.\n * When provided, completely replaces the default element.\n */\n render?: ReactElement;\n children?: ReactNode;\n};\n\nexport const FlowNode = forwardRef<HTMLElement, FlowNodeProps>(\n function FlowNode({ render, children }, ref) {\n const nodeRef = useRef<HTMLElement>(null);\n const startAnchorRef = useRef<HTMLElement | null>(null);\n const endAnchorRef = useRef<HTMLElement | null>(null);\n const [measurements, setMeasurements] = useState<{\n start: RectLike | null;\n end: RectLike | null;\n }>({ start: null, end: null });\n\n const nodeProps = useMemo(\n () => ({\n parallel: false,\n ...measurements,\n }),\n [measurements]\n );\n\n const { index, id } = useNode(nodeProps);\n\n /**\n * This effect intentionally has no dependencies because we want it to run on\n * every render to ensure measurements are always up to date.\n */\n useEffect(() => {\n if (!nodeRef.current) return;\n\n const rect = nodeRef.current.getBoundingClientRect();\n const nodeRect = rect;\n\n let startRect: RectLike = nodeRect;\n let endRect: RectLike = nodeRect;\n\n if (startAnchorRef.current) {\n startRect = startAnchorRef.current.getBoundingClientRect();\n }\n\n if (endAnchorRef.current) {\n endRect = endAnchorRef.current.getBoundingClientRect();\n }\n\n setMeasurements((m) => {\n const newVal = { start: startRect, end: endRect };\n if (JSON.stringify(m) === JSON.stringify(newVal)) return m;\n return newVal;\n });\n });\n\n const mergedRef = mergeRefs(ref, nodeRef);\n\n let element: ReactElement;\n if (render && isValidElement(render)) {\n // When render prop is provided, clone it with ref and data attributes\n const renderProps = render.props as { children?: ReactNode };\n // oxlint-disable-next-line react/no-clone-element -- forwarding ref to render prop element\n element = cloneElement(render, {\n ref: mergedRef,\n \"data-node-index\": index,\n \"data-node-id\": id,\n children: renderProps.children ?? children,\n } as React.HTMLAttributes<HTMLElement> & { ref: React.Ref<HTMLElement> });\n } else {\n // Default element\n element = (\n <li\n ref={mergedRef}\n className=\"py-2 px-3 rounded-md shadow bg-sf-base ring ring-sf-line\"\n data-node-index={index}\n data-node-id={id}\n >\n {children}\n </li>\n );\n }\n\n return (\n <FlowNodeAnchorContext.Provider\n value={useMemo(\n () => ({\n registerStartAnchor: (anchorRef) => {\n startAnchorRef.current = anchorRef;\n },\n registerEndAnchor: (anchorRef) => {\n endAnchorRef.current = anchorRef;\n },\n }),\n []\n )}\n >\n {element}\n </FlowNodeAnchorContext.Provider>\n );\n }\n);\n\nFlowNode.displayName = \"Flow.Node\";\n\ntype FlowNodeAnchorContextType = {\n registerStartAnchor: (ref: HTMLElement | null) => void;\n registerEndAnchor: (ref: HTMLElement | null) => void;\n};\n\nconst FlowNodeAnchorContext = createContext<FlowNodeAnchorContextType | null>(\n null\n);\n\n/**\n * FlowAnchor component props.\n *\n * @example Default (unstyled div)\n * ```tsx\n * <Flow.Anchor type=\"start\">Anchor content</Flow.Anchor>\n * ```\n *\n * @example Custom render - completely replaces the default element\n * ```tsx\n * <Flow.Anchor type=\"end\" render={<span className=\"custom-anchor\">Custom anchor</span>} />\n * ```\n */\nexport type FlowAnchorProps = {\n /**\n * Determines if the anchor should serve as a \"start\" point for the\n * _next_ connector or the \"end\" point for the _previous_ connector.\n * When omitted, it serves as both the start and end points.\n */\n type?: \"start\" | \"end\";\n /**\n * Custom element to render instead of the default div.\n * When provided, completely replaces the default element.\n */\n render?: ReactElement;\n children?: ReactNode;\n};\n\nexport const FlowAnchor = forwardRef<HTMLElement, FlowAnchorProps>(\n function FlowAnchor({ type, render, children }, ref) {\n const context = useContext(FlowNodeAnchorContext);\n const anchorRef = useRef<HTMLElement>(null);\n\n if (!context) {\n throw new Error(\"Flow.Anchor must be used within Flow.Node\");\n }\n\n useEffect(() => {\n if (!anchorRef.current) {\n return;\n }\n\n if (type === \"start\" || type === undefined) {\n context.registerStartAnchor(anchorRef.current);\n }\n if (type === \"end\" || type === undefined) {\n context.registerEndAnchor(anchorRef.current);\n }\n\n return () => {\n if (type === \"start\" || type === undefined) {\n context.registerStartAnchor(null);\n }\n if (type === \"end\" || type === undefined) {\n context.registerEndAnchor(null);\n }\n };\n }, [type, context.registerStartAnchor, context.registerEndAnchor]);\n\n const mergedRef = mergeRefs(ref, anchorRef);\n\n if (render && isValidElement(render)) {\n // When render prop is provided, clone it with ref\n const renderProps = render.props as { children?: ReactNode };\n // oxlint-disable-next-line react/no-clone-element -- forwarding ref to render prop element\n return cloneElement(render, {\n ref: mergedRef,\n children: renderProps.children ?? children,\n } as React.HTMLAttributes<HTMLElement> & { ref: React.Ref<HTMLElement> });\n }\n\n // Default element\n return <div ref={mergedRef}>{children}</div>;\n }\n);\n\nFlowAnchor.displayName = \"Flow.Anchor\";\n","import { useMemo, useRef, type ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { Connectors, type Connector } from \"./connectors\";\nimport {\n getNodeRect,\n useDiagramContext,\n useNode,\n useNodeGroup,\n type RectLike,\n} from \"./diagram\";\nimport { DescendantsProvider } from \"./use-children\";\n\nfunction getStartAndEndPoints({\n container,\n previous,\n next,\n orientation,\n}: {\n container: RectLike;\n previous: RectLike | null;\n next: RectLike | null;\n orientation: \"vertical\" | \"horizontal\";\n}): {\n start: { x: number; y: number };\n end: { x: number; y: number };\n} {\n if (orientation === \"vertical\") {\n // we ignore previous/next calculations for vertical orientations for now\n return {\n start: {\n x: container.width / 2,\n y: 0,\n },\n end: {\n x: container.width / 2,\n y: container.height,\n },\n };\n }\n // Default to midpoints\n let start = {\n x: 0,\n y: container.height / 2,\n };\n let end = {\n x: container.width,\n y: container.height / 2,\n };\n if (previous) {\n start.y = previous.top - container.top + previous.height / 2;\n }\n if (next) {\n end.y = next.top - container.top + next.height / 2;\n }\n return { start, end };\n}\n\nexport function FlowParallelNode({ children }: { children: ReactNode }) {\n const { orientation } = useDiagramContext();\n const descendants = useNodeGroup();\n\n const containerRef = useRef<HTMLDivElement>(null);\n\n const { index, getPrevious, getNext } = useNode(\n useMemo(() => ({ parallel: true }), [])\n );\n\n const measure = () => {\n const container = containerRef.current;\n if (!container) return;\n\n const containerRect = container.getBoundingClientRect();\n\n const [prevNode, nextNode] = [getPrevious(), getNext()];\n const previousNodeRect = getNodeRect(prevNode, { type: \"start\" });\n const nextNodeRect = getNodeRect(nextNode, { type: \"end\" });\n\n const { start, end } = getStartAndEndPoints({\n container: containerRect,\n previous: previousNodeRect,\n next: nextNodeRect,\n orientation,\n });\n\n const newConnectors = descendants.descendants.flatMap(({ props }) => {\n const connectors: Connector[] = [];\n\n const [endAnchorRect, startAnchorRect] = [props.end, props.start];\n if (endAnchorRect) {\n let branchStart: { x: number; y: number };\n switch (orientation) {\n case \"vertical\": {\n const anchorCenter =\n endAnchorRect.left - containerRect.left + endAnchorRect.width / 2;\n branchStart = {\n x: anchorCenter,\n y: endAnchorRect.top - containerRect.top,\n };\n break;\n }\n case \"horizontal\": {\n const anchorCenter =\n endAnchorRect.top - containerRect.top + endAnchorRect.height / 2;\n branchStart = {\n x: endAnchorRect.left - containerRect.left,\n y: anchorCenter,\n };\n break;\n }\n default:\n throw new Error(`Unknown orientation: ${orientation as string}`);\n }\n connectors.push({\n x1: start.x,\n y1: start.y,\n x2: branchStart.x,\n y2: branchStart.y,\n isBottom: false,\n });\n }\n\n if (nextNodeRect && startAnchorRect) {\n let branchEnd: { x: number; y: number };\n switch (orientation) {\n case \"vertical\": {\n const anchorCenter =\n startAnchorRect.left -\n containerRect.left +\n startAnchorRect.width / 2;\n branchEnd = {\n x: anchorCenter,\n y: startAnchorRect.bottom - containerRect.top,\n };\n break;\n }\n case \"horizontal\": {\n const anchorCenter =\n startAnchorRect.top -\n containerRect.top +\n startAnchorRect.height / 2;\n branchEnd = {\n x: startAnchorRect.right - containerRect.left,\n y: anchorCenter,\n };\n break;\n }\n default:\n throw new Error(`Unknown orientation: ${orientation as string}`);\n }\n connectors.push({\n x1: branchEnd.x,\n y1: branchEnd.y,\n x2: end.x,\n y2: end.y,\n isBottom: true,\n });\n }\n\n return connectors;\n });\n\n return {\n connectors: newConnectors,\n junctions: {\n start: {\n x: orientation === \"vertical\" ? start.x : start.x + 32,\n y: orientation === \"vertical\" ? start.y + 32 : start.y,\n },\n end: nextNodeRect\n ? {\n x: orientation === \"vertical\" ? end.x : end.x - 32,\n y: orientation === \"vertical\" ? end.y - 32 : end.y,\n }\n : undefined,\n },\n containerRect,\n };\n };\n\n const links = measure();\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"relative\",\n orientation === \"horizontal\" ? \"px-16 -mx-16\" : \"py-16 -my-16\"\n )}\n data-node-index={index}\n >\n <div className=\"absolute inset-0 pointer-events-none z-10\">\n {links && (\n <Connectors connectors={links.connectors} orientation={orientation}>\n {links.junctions?.start && (\n <g\n transform={`translate(${links.junctions.start.x} ${links.junctions.start.y})`}\n >\n <JunctionBox />\n </g>\n )}\n {links.junctions?.end && (\n <g\n transform={`translate(${links.junctions.end.x} ${links.junctions.end.y})`}\n >\n <JunctionBox />\n </g>\n )}\n </Connectors>\n )}\n </div>\n <ul\n className={cn(\n \"gap-5 list-none flex items-start\",\n orientation === \"horizontal\" ? \"flex-col ml-0\" : \"gap-5 w-fit mx-auto\"\n )}\n >\n <DescendantsProvider value={descendants}>\n {children}\n </DescendantsProvider>\n </ul>\n </div>\n );\n}\n\nfunction JunctionBox({ size = 6 }) {\n const halfSize = size / 2;\n return (\n <rect\n x={-halfSize}\n y={-halfSize}\n width={size}\n height={size}\n fill=\"currentColor\"\n rx=\"1\"\n />\n );\n}\n","import { FlowDiagram, FlowNodeList } from \"./diagram\";\nimport { FlowNode, FlowAnchor } from \"./node\";\nimport { FlowParallelNode } from \"./parallel\";\n\n/**\n * Flow - Components for visualizing workflows and data flows.\n *\n * @example\n * ```tsx\n * <Flow>\n * <Flow.Node>Step 1</Flow.Node>\n * <Flow.Node>Step 2</Flow.Node>\n * <Flow.Parallel>\n * <Flow.Node>Branch A</Flow.Node>\n * <Flow.Node>Branch B</Flow.Node>\n * </Flow.Parallel>\n * <Flow.Node>Step 3</Flow.Node>\n * </Flow>\n * ```\n */\nconst Flow = Object.assign(FlowDiagram, {\n Node: FlowNode,\n Parallel: FlowParallelNode,\n List: FlowNodeList,\n Anchor: FlowAnchor,\n});\n\nexport { Flow };\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"highlight-to-react-ClEfL81q.js","names":[],"sources":["../src/utils/highlight-to-react.tsx"],"sourcesContent":["/**\n * Converts sugar-high HTML output into React elements without dangerouslySetInnerHTML.\n *\n * sugar-high produces:\n * <span class=\"sh__line\">\n * <span class=\"sh__token--keyword\" style=\"color:var(--sh-keyword)\">const</span>\n * ...\n * </span>\n *\n * This parser extracts each token span and returns typed React elements.\n */\n\nimport type { ReactElement, ReactNode } from \"react\";\nimport { createElement } from \"react\";\n\n// Matches a single token span: <span class=\"sh__token--X\" style=\"color:var(--sh-Y)\">content</span>\nconst TOKEN_RE =\n /<span class=\"(sh__[^\"]+)\" style=\"color:([^\"]+)\">([\\s\\S]*?)<\\/span>/g;\n\n// Matches a line wrapper: <span class=\"sh__line\">content</span>\nconst LINE_RE = /<span class=\"sh__line\">([\\s\\S]*?)<\\/span>/g;\n\n/** Parse the inner HTML of a line into token React elements. */\nfunction parseTokens(lineHtml: string): ReactNode[] {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n // Reset regex state\n TOKEN_RE.lastIndex = 0;\n\n while ((match = TOKEN_RE.exec(lineHtml)) !== null) {\n // Any text between tokens (shouldn't happen with well-formed sugar-high output, but be safe)\n if (match.index > lastIndex) {\n nodes.push(lineHtml.slice(lastIndex, match.index));\n }\n\n const [, className, color, text] = match;\n nodes.push(\n createElement(\n \"span\",\n { key: match.index, className, style: { color } },\n // Unescape HTML entities sugar-high may produce\n unescapeHtml(text)\n )\n );\n lastIndex = match.index + match[0].length;\n }\n\n // Any trailing text\n if (lastIndex < lineHtml.length) {\n nodes.push(lineHtml.slice(lastIndex));\n }\n\n return nodes.length > 0 ? nodes : [\" \"];\n}\n\nfunction unescapeHtml(str: string): string {\n return str\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll(\""\", '\"')\n .replaceAll(\"'\", \"'\");\n}\n\n/**\n * Parse sugar-high HTML output into an array of per-line React elements.\n * Each element is a `<span class=\"sh__line\">` containing token spans.\n */\nexport function highlightToLines(html: string): ReactElement[] {\n const lines: ReactElement[] = [];\n let match: RegExpExecArray | null;\n\n // Reset regex state\n LINE_RE.lastIndex = 0;\n\n let lineIndex = 0;\n while ((match = LINE_RE.exec(html)) !== null) {\n const [, lineHtml] = match;\n lines.push(\n createElement(\n \"span\",\n { key: lineIndex, className: \"sh__line\" },\n ...parseTokens(lineHtml)\n )\n );\n lineIndex++;\n }\n\n return lines;\n}\n\n/**\n * Parse sugar-high HTML output into a flat array of token React elements\n * (no line wrappers — for inline use inside a single `<code>` element).\n */\nexport function highlightToTokens(html: string): ReactNode[] {\n // Flatten: strip line wrappers (opening tags and their closing </span> at line boundaries)\n const stripped = html\n .replace(/<span class=\"sh__line\">/g, \"\")\n .replace(/<\\/span>\\s*(?=\\n|$)/g, \"\");\n return parseTokens(stripped);\n}\n"],"mappings":";;;AAgBA,IAAM,WACJ;AAGF,IAAM,UAAU;;AAGhB,SAAS,YAAY,UAA+B;CAClD,MAAM,QAAqB,EAAE;CAC7B,IAAI,YAAY;CAChB,IAAI;AAGJ,UAAS,YAAY;AAErB,SAAQ,QAAQ,SAAS,KAAK,SAAS,MAAM,MAAM;AAEjD,MAAI,MAAM,QAAQ,UAChB,OAAM,KAAK,SAAS,MAAM,WAAW,MAAM,MAAM,CAAC;EAGpD,MAAM,GAAG,WAAW,OAAO,QAAQ;AACnC,QAAM,KACJ,cACE,QACA;GAAE,KAAK,MAAM;GAAO;GAAW,OAAO,EAAE,OAAO;GAAE,EAEjD,aAAa,KAAK,CACnB,CACF;AACD,cAAY,MAAM,QAAQ,MAAM,GAAG;;AAIrC,KAAI,YAAY,SAAS,OACvB,OAAM,KAAK,SAAS,MAAM,UAAU,CAAC;AAGvC,QAAO,MAAM,SAAS,IAAI,QAAQ,CAAC,IAAI;;AAGzC,SAAS,aAAa,KAAqB;AACzC,QAAO,IACJ,WAAW,SAAS,IAAI,CACxB,WAAW,QAAQ,IAAI,CACvB,WAAW,QAAQ,IAAI,CACvB,WAAW,UAAU,KAAI,CACzB,WAAW,SAAS,IAAI;;;;;;AAO7B,SAAgB,iBAAiB,MAA8B;CAC7D,MAAM,QAAwB,EAAE;CAChC,IAAI;AAGJ,SAAQ,YAAY;CAEpB,IAAI,YAAY;AAChB,SAAQ,QAAQ,QAAQ,KAAK,KAAK,MAAM,MAAM;EAC5C,MAAM,GAAG,YAAY;AACrB,QAAM,KACJ,cACE,QACA;GAAE,KAAK;GAAW,WAAW;GAAY,EACzC,GAAG,YAAY,SAAS,CACzB,CACF;AACD;;AAGF,QAAO;;;;;;AAOT,SAAgB,kBAAkB,MAA2B;AAK3D,QAAO,YAHU,KACd,QAAQ,4BAA4B,GAAG,CACvC,QAAQ,wBAAwB,GAAG,CACV"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"link-provider-BUZKXaNE.js","names":[],"sources":["../src/utils/link-provider.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n type AnchorHTMLAttributes,\n type ForwardRefExoticComponent,\n type ReactNode,\n type RefAttributes,\n useContext,\n} from \"react\";\n\ntype LinkComponentProps = AnchorHTMLAttributes<HTMLAnchorElement> & {\n to?: string;\n};\n\nconst DefaultLinkComponent = forwardRef<HTMLAnchorElement, LinkComponentProps>(\n function DefaultAnchor({ to, href, ...rest }, ref) {\n // Children and other content props are passed via ...rest spread\n // oxlint-disable-next-line anchor-has-content\n return <a ref={ref} href={href ?? to ?? undefined} {...rest} />;\n }\n);\n\ntype ForwardLinkComponent = ForwardRefExoticComponent<\n LinkComponentProps & RefAttributes<HTMLAnchorElement>\n>;\n\nconst LinkComponentContext =\n createContext<ForwardLinkComponent>(DefaultLinkComponent);\n\nexport function useLinkComponent() {\n return useContext(LinkComponentContext);\n}\n\nexport function LinkProvider({\n component,\n children,\n}: {\n component?: ForwardLinkComponent;\n children: ReactNode;\n}) {\n return (\n <LinkComponentContext.Provider value={component ?? DefaultLinkComponent}>\n {children}\n </LinkComponentContext.Provider>\n );\n}\n\nexport type { LinkComponentProps };\n"],"mappings":";;;;AAcA,IAAM,uBAAuB,WAC3B,SAAS,cAAc,EAAE,IAAI,MAAM,GAAG,QAAQ,KAAK;AAGjD,QAAO,oBAAC,KAAD;EAAQ;EAAK,MAAM,QAAQ,MAAM,KAAA;EAAW,GAAI;EAAQ,CAAA;EAElE;AAMD,IAAM,uBACJ,cAAoC,qBAAqB;AAE3D,SAAgB,mBAAmB;AACjC,QAAO,WAAW,qBAAqB;;AAGzC,SAAgB,aAAa,EAC3B,WACA,YAIC;AACD,QACE,oBAAC,qBAAqB,UAAtB;EAA+B,OAAO,aAAa;EAChD;EAC6B,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-CAsCmSpM.js","names":[],"sources":["../src/components/sidebar/sidebar.tsx"],"sourcesContent":["import { Collapsible as CollapsibleBase } from \"@base-ui/react/collapsible\";\nimport { Dialog as DialogBase } from \"@base-ui/react/dialog\";\nimport {\n CaretRightIcon,\n MagnifyingGlassIcon,\n SidebarSimpleIcon,\n} from \"@phosphor-icons/react\";\nimport React, {\n type ComponentPropsWithoutRef,\n type CSSProperties,\n type ReactNode,\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { cn } from \"../../utils/cn\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Tooltip, TooltipProvider } from \"../tooltip\";\n\n// ============================================================================\n// Variants (required by SignalFlare convention)\n// ============================================================================\n\n/** Sidebar variant definitions mapping layout, collapse, and side options. */\nexport const SF_SIDEBAR_VARIANTS = {\n variant: {\n sidebar: {\n classes: \"\",\n description: \"Standard sidebar with border separator\",\n },\n floating: {\n classes: \"\",\n description: \"Floating sidebar with shadow and rounded corners\",\n },\n inset: {\n classes: \"\",\n description: \"Inset sidebar within the content area\",\n },\n },\n collapsible: {\n icon: {\n classes: \"\",\n description: \"Collapses to show icons only\",\n },\n offcanvas: {\n classes: \"\",\n description: \"Slides off screen when collapsed\",\n },\n none: {\n classes: \"\",\n description: \"Cannot be collapsed\",\n },\n },\n side: {\n left: {\n classes: \"\",\n description: \"Left-aligned sidebar\",\n },\n right: {\n classes: \"\",\n description: \"Right-aligned sidebar\",\n },\n },\n} as const;\n\nexport const SF_SIDEBAR_DEFAULT_VARIANTS = {\n variant: \"sidebar\",\n collapsible: \"icon\",\n side: \"left\",\n} as const;\n\nexport const SF_SIDEBAR_STYLING = {\n width: {\n expanded: \"16rem\",\n icon: \"3rem\",\n },\n mobile: {\n breakpoint: 768,\n },\n} as const;\n\nexport type SidebarSide = \"left\" | \"right\";\nexport type SidebarVariant = \"sidebar\" | \"floating\" | \"inset\";\nexport type SidebarCollapsible = \"icon\" | \"offcanvas\" | \"none\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SIDEBAR_WIDTH = \"16rem\";\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst MOBILE_BREAKPOINT = 768;\n\n// ============================================================================\n// Mobile detection hook\n// ============================================================================\n\nfunction useIsMobile() {\n const [isMobile, setIsMobile] = useState(false);\n\n useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => setIsMobile(mql.matches);\n mql.addEventListener(\"change\", onChange);\n setIsMobile(mql.matches);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return isMobile;\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nexport interface SidebarContextValue {\n state: \"expanded\" | \"collapsed\";\n open: boolean;\n setOpen: (open: boolean) => void;\n openMobile: boolean;\n setOpenMobile: (open: boolean) => void;\n isMobile: boolean;\n toggleSidebar: () => void;\n variant: \"sidebar\" | \"floating\" | \"inset\";\n side: \"left\" | \"right\";\n collapsible: \"icon\" | \"offcanvas\" | \"none\";\n width: number;\n resizable: boolean;\n minWidth: number;\n maxWidth: number;\n isResizing: boolean;\n setIsResizing: (resizing: boolean) => void;\n setWidth: (width: number) => void;\n}\n\nconst SidebarContext = createContext<SidebarContextValue | null>(null);\n\n/**\n * Hook to access sidebar state and actions from any descendant component.\n *\n * @example\n * ```tsx\n * const { state, open, toggleSidebar, isMobile } = useSidebar();\n * ```\n *\n * @throws Error if used outside a `Sidebar.Provider`.\n */\nexport function useSidebar() {\n const context = useContext(SidebarContext);\n if (!context) {\n throw new Error(\"useSidebar must be used within a Sidebar.Provider\");\n }\n return context;\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport interface SidebarProviderProps {\n /** Initial open state when uncontrolled. @default true */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Callback when open state changes (controlled mode). */\n onOpenChange?: (open: boolean) => void;\n /** Sidebar layout variant. @default \"sidebar\" */\n variant?: SidebarVariant;\n /** Which side the sidebar is on. @default \"left\" */\n side?: SidebarSide;\n collapsible?: \"icon\" | \"offcanvas\" | \"none\";\n /** Enable drag-to-resize on the sidebar edge. @default false */\n resizable?: boolean;\n /** Initial width in pixels when resizable. @default 256 */\n defaultWidth?: number;\n /** Minimum width in pixels when resizing. @default 200 */\n minWidth?: number;\n /** Maximum width in pixels when resizing. @default 480 */\n maxWidth?: number;\n /** Callback when width changes during resize. */\n onWidthChange?: (width: number) => void;\n /** Content — typically `<Sidebar>` + main content. */\n children: ReactNode;\n /** Additional CSS classes for the wrapper div. */\n className?: string;\n /** Inline styles for the wrapper div. */\n style?: CSSProperties;\n}\n\n/**\n * Sidebar context provider. Manages expand/collapse state and mobile detection.\n * Renders a flex wrapper div with CSS custom properties for sidebar width.\n *\n * @example\n * ```tsx\n * <Sidebar.Provider defaultOpen>\n * <Sidebar>{...}</Sidebar>\n * <main className=\"flex-1\">{...}</main>\n * </Sidebar.Provider>\n * ```\n */\nconst DEFAULT_WIDTH_PX = 256;\nconst MIN_WIDTH_PX = 200;\nconst MAX_WIDTH_PX = 480;\n\nfunction SidebarProvider({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n variant = SF_SIDEBAR_DEFAULT_VARIANTS.variant,\n side = SF_SIDEBAR_DEFAULT_VARIANTS.side,\n collapsible = SF_SIDEBAR_DEFAULT_VARIANTS.collapsible,\n resizable = false,\n defaultWidth = DEFAULT_WIDTH_PX,\n minWidth = MIN_WIDTH_PX,\n maxWidth = MAX_WIDTH_PX,\n onWidthChange,\n children,\n className,\n style,\n}: SidebarProviderProps) {\n const isMobile = useIsMobile();\n const [openMobile, setOpenMobile] = useState(false);\n const [width, setWidthState] = useState(defaultWidth);\n const [isResizing, setIsResizing] = useState(false);\n\n const setWidth = useCallback(\n (newWidth: number) => {\n const clamped = Math.min(maxWidth, Math.max(minWidth, newWidth));\n setWidthState(clamped);\n onWidthChange?.(clamped);\n },\n [minWidth, maxWidth, onWidthChange]\n );\n\n const [_open, _setOpen] = useState(defaultOpen);\n const open = openProp ?? _open;\n const setOpen = useCallback(\n (value: boolean | ((prev: boolean) => boolean)) => {\n const next = typeof value === \"function\" ? value(open) : value;\n setOpenProp?.(next);\n _setOpen(next);\n },\n [setOpenProp, open]\n );\n\n const toggleSidebar = useCallback(() => {\n if (isMobile) {\n setOpenMobile((prev) => !prev);\n } else {\n setOpen((prev: boolean) => !prev);\n }\n }, [isMobile, setOpen]);\n\n const state = open ? \"expanded\" : \"collapsed\";\n\n const sidebarWidthValue = resizable ? `${width}px` : SIDEBAR_WIDTH;\n\n const contextValue = useMemo<SidebarContextValue>(\n () => ({\n state,\n open,\n setOpen,\n openMobile,\n setOpenMobile,\n isMobile,\n toggleSidebar,\n variant,\n side,\n collapsible,\n width,\n resizable,\n minWidth,\n maxWidth,\n isResizing,\n setIsResizing,\n setWidth,\n }),\n [\n state,\n open,\n setOpen,\n openMobile,\n setOpenMobile,\n isMobile,\n toggleSidebar,\n variant,\n side,\n collapsible,\n width,\n resizable,\n minWidth,\n maxWidth,\n isResizing,\n setIsResizing,\n setWidth,\n ]\n );\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <div\n data-sidebar-wrapper=\"\"\n data-state={state}\n data-side={side}\n style={\n {\n \"--sidebar-width\": sidebarWidthValue,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-svh w-full\",\n \"has-data-[variant=inset]:bg-sf-recessed\",\n isResizing && \"select-none\",\n className\n )}\n >\n {children}\n </div>\n </SidebarContext.Provider>\n );\n}\n\nSidebarProvider.displayName = \"Sidebar.Provider\";\n\n// ============================================================================\n// Sidebar Root\n// ============================================================================\n\nexport interface SidebarRootProps extends ComponentPropsWithoutRef<\"aside\"> {\n /** Additional CSS classes for the sidebar element. */\n className?: string;\n /** Sidebar content — Header, Content, Footer, etc. */\n children: ReactNode;\n}\n\n/**\n * Main sidebar container. Renders as `<aside>` on desktop, Dialog sheet on mobile.\n * Must be used inside `Sidebar.Provider`.\n *\n * @example\n * ```tsx\n * <Sidebar.Provider>\n * <Sidebar>\n * <Sidebar.Header>...</Sidebar.Header>\n * <Sidebar.Content>...</Sidebar.Content>\n * <Sidebar.Footer>...</Sidebar.Footer>\n * </Sidebar>\n * </Sidebar.Provider>\n * ```\n */\nconst SidebarRoot = forwardRef<HTMLElement, SidebarRootProps>(\n ({ className, children, ...props }, ref) => {\n const {\n state,\n isMobile,\n openMobile,\n setOpenMobile,\n side,\n variant,\n collapsible,\n isResizing,\n resizable,\n width,\n } = useSidebar();\n\n if (collapsible === \"none\") {\n return (\n <aside\n ref={ref}\n data-state=\"expanded\"\n data-side={side}\n data-variant={variant}\n data-sidebar=\"sidebar\"\n style={{\n width: \"var(--sidebar-width)\",\n minWidth: \"var(--sidebar-width)\",\n maxWidth: \"var(--sidebar-width)\",\n }}\n className={cn(\n \"relative flex h-full shrink-0 grow-0 flex-col overflow-hidden bg-sf-base text-sf-default\",\n variant === \"sidebar\" &&\n (side === \"left\"\n ? \"border-r border-sf-line\"\n : \"border-l border-sf-line\"),\n variant === \"floating\" &&\n \"m-2 rounded-lg border border-sf-line shadow-lg\",\n className\n )}\n {...props}\n >\n {children}\n </aside>\n );\n }\n\n if (isMobile) {\n return (\n <DialogBase.Root open={openMobile} onOpenChange={setOpenMobile}>\n <DialogBase.Portal>\n <DialogBase.Backdrop className=\"fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\" />\n <DialogBase.Popup\n className={cn(\n \"fixed inset-y-0 z-50 flex w-[--sidebar-width] flex-col bg-sf-base p-0\",\n \"duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0\",\n side === \"left\" &&\n \"left-0 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full\",\n side === \"right\" &&\n \"right-0 data-[ending-style]:translate-x-full data-[starting-style]:translate-x-full\"\n )}\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n transitionProperty: \"transform, opacity\",\n transitionTimingFunction:\n \"var(--default-transition-timing-function)\",\n } as CSSProperties\n }\n >\n <div\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className={cn(\n \"flex h-full w-full flex-col bg-sf-base text-sf-default\",\n className\n )}\n >\n {children}\n </div>\n </DialogBase.Popup>\n </DialogBase.Portal>\n </DialogBase.Root>\n );\n }\n\n // Resolve the target width from CSS variables or literal values\n const collapsedWidth =\n collapsible === \"icon\" ? \"var(--sidebar-width-icon)\" : \"0px\";\n const expandedWidth = resizable ? `${width}px` : \"var(--sidebar-width)\";\n const targetWidth = state === \"expanded\" ? expandedWidth : collapsedWidth;\n\n return (\n <aside\n ref={ref}\n data-state={state}\n data-side={side}\n data-variant={variant}\n data-collapsible={collapsible}\n data-sidebar=\"sidebar\"\n style={{ width: targetWidth }}\n className={cn(\n \"group/sidebar relative flex h-full shrink-0 grow-0 flex-col\",\n // overflow-hidden makes flex min-width resolve to 0 (per spec),\n // preventing children from pushing the sidebar wider than its width\n \"min-w-0 overflow-hidden whitespace-nowrap\",\n \"bg-sf-base text-sf-default\",\n // Transition width — matches production SidebarNav curve exactly\n \"transition-[width] duration-250 ease-[cubic-bezier(0.77,0,0.175,1)] will-change-[width]\",\n \"motion-reduce:transition-none\",\n // Disable transition during resize drag\n isResizing && \"transition-none!\",\n variant === \"sidebar\" &&\n (side === \"left\"\n ? \"border-r border-sf-line\"\n : \"border-l border-sf-line\"),\n variant === \"floating\" &&\n \"m-2 rounded-lg border border-sf-line shadow-lg\",\n className\n )}\n {...props}\n >\n {/* TooltipProvider groups all collapsed-state tooltips so hovering\n between icons shows tooltips instantly (no repeated delay). */}\n <TooltipProvider>{children}</TooltipProvider>\n </aside>\n );\n }\n);\n\nSidebarRoot.displayName = \"Sidebar\";\n\n// ============================================================================\n// Sidebar Header\n// ============================================================================\n\n/**\n * Top section of the sidebar. Typically contains a logo, title, and action button.\n *\n * @example\n * ```tsx\n * <Sidebar.Header>\n * <Logo />\n * <span>Design Engineering</span>\n * <Button shape=\"square\" icon={CaretUpDownIcon} aria-label=\"Switch\" />\n * </Sidebar.Header>\n * ```\n */\nconst SidebarHeader = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\n \"flex items-center gap-2 border-b border-sf-line px-2 py-3\",\n \"overflow-hidden\",\n // Collapsed: just remove border, keep same height\n \"group-data-[state=collapsed]/sidebar:border-b-0\",\n className\n )}\n {...props}\n />\n));\n\nSidebarHeader.displayName = \"Sidebar.Header\";\n\n// ============================================================================\n// Sidebar Content\n// ============================================================================\n\n/**\n * Scrollable middle section of the sidebar. Contains nav groups and menus.\n *\n * @example\n * ```tsx\n * <Sidebar.Content>\n * <Sidebar.Group>...</Sidebar.Group>\n * </Sidebar.Content>\n * ```\n */\nconst SidebarContent = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"flex min-w-0 flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden px-2 py-2\",\n // Collapsed: flatten spacing so icons are evenly spaced\n \"group-data-[state=collapsed]/sidebar:gap-0 group-data-[state=collapsed]/sidebar:py-0\",\n \"group-data-[state=collapsed]/sidebar:overflow-x-hidden\",\n className\n )}\n {...props}\n />\n));\n\nSidebarContent.displayName = \"Sidebar.Content\";\n\n// ============================================================================\n// Sidebar Footer\n// ============================================================================\n\n/**\n * Bottom-pinned section of the sidebar. Typically contains toggle button and help actions.\n *\n * @example\n * ```tsx\n * <Sidebar.Footer>\n * <Sidebar.Trigger />\n * <Button shape=\"square\" icon={InfoIcon} aria-label=\"Help\" />\n * </Sidebar.Footer>\n * ```\n */\nconst SidebarFooter = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\n \"flex min-w-0 flex-col gap-2 border-t border-sf-line px-2 py-2\",\n // Collapsed: remove border, tighten padding\n \"group-data-[state=collapsed]/sidebar:border-t-0 group-data-[state=collapsed]/sidebar:py-1\",\n className\n )}\n {...props}\n />\n));\n\nSidebarFooter.displayName = \"Sidebar.Footer\";\n\n// ============================================================================\n// Sidebar Group\n// ============================================================================\n\n/** Context to signal children they're inside a collapsible group and provide open state. */\ninterface SidebarGroupCollapsibleContextValue {\n isCollapsible: boolean;\n isOpen: boolean;\n}\nconst SidebarGroupCollapsibleContext =\n createContext<SidebarGroupCollapsibleContextValue>({\n isCollapsible: false,\n isOpen: true,\n });\n\nexport interface SidebarGroupProps extends ComponentPropsWithoutRef<\"div\"> {\n /** When true, the group can be expanded/collapsed via its label. @default false */\n collapsible?: boolean;\n /** Initial open state when collapsible and uncontrolled. @default true */\n defaultOpen?: boolean;\n /** Controlled open state (collapsible mode only). */\n open?: boolean;\n /** Callback when open state changes (collapsible mode only). */\n onOpenChange?: (open: boolean) => void;\n}\n\n/**\n * Groups related menu items with an optional label.\n * When `collapsible` is set, wraps content with Base UI Collapsible for\n * animated expand/collapse via the group label.\n *\n * @example Non-collapsible group\n * ```tsx\n * <Sidebar.Group>\n * <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>\n * <Sidebar.Menu>...</Sidebar.Menu>\n * </Sidebar.Group>\n * ```\n *\n * @example Collapsible group (requires GroupContent for animation)\n * ```tsx\n * <Sidebar.Group collapsible defaultOpen>\n * <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>\n * <Sidebar.GroupContent>\n * <Sidebar.Menu>...</Sidebar.Menu>\n * </Sidebar.GroupContent>\n * </Sidebar.Group>\n * ```\n */\nconst SidebarGroup = forwardRef<HTMLDivElement, SidebarGroupProps>(\n (\n {\n className,\n collapsible = false,\n defaultOpen = true,\n open: openProp,\n onOpenChange,\n children,\n ...props\n },\n ref\n ) => {\n // Track internal open state for uncontrolled mode\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = openProp ?? internalOpen;\n\n const handleOpenChange = useCallback(\n (newOpen: boolean) => {\n setInternalOpen(newOpen);\n onOpenChange?.(newOpen);\n },\n [onOpenChange]\n );\n\n const contextValue = useMemo(\n () => ({ isCollapsible: collapsible, isOpen }),\n [collapsible, isOpen]\n );\n\n const content = (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\n \"flex min-w-0 flex-col gap-0.5\",\n // Collapsed: remove internal gap so icons stack uniformly\n \"group-data-[state=collapsed]/sidebar:gap-0\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n\n if (!collapsible) {\n return (\n <SidebarGroupCollapsibleContext.Provider value={contextValue}>\n {content}\n </SidebarGroupCollapsibleContext.Provider>\n );\n }\n\n return (\n <SidebarGroupCollapsibleContext.Provider value={contextValue}>\n <CollapsibleBase.Root\n defaultOpen={defaultOpen}\n open={openProp}\n onOpenChange={handleOpenChange}\n className=\"min-w-0\"\n >\n {content}\n </CollapsibleBase.Root>\n </SidebarGroupCollapsibleContext.Provider>\n );\n }\n);\n\nSidebarGroup.displayName = \"Sidebar.Group\";\n\n// ============================================================================\n// Sidebar GroupLabel\n// ============================================================================\n\n/**\n * Section label for a sidebar group (e.g., \"Build\", \"Protect & Connect\").\n * Hidden when the sidebar is collapsed to icon-only mode.\n *\n * When used inside a collapsible `Sidebar.Group`, renders as a\n * `Collapsible.Trigger` with an auto-rotating chevron.\n *\n * @example\n * ```tsx\n * <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>\n * ```\n */\nconst SidebarGroupLabel = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, children, ...props }, ref) => {\n const { isCollapsible } = useContext(SidebarGroupCollapsibleContext);\n\n if (isCollapsible) {\n return (\n <CollapsibleBase.Trigger\n ref={ref as React.Ref<HTMLButtonElement>}\n data-sidebar=\"group-label\"\n className={cn(\n \"group/group-label flex w-full cursor-pointer items-center px-3 py-1 text-xs font-medium text-sf-subtle\",\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n {...(props as ComponentPropsWithoutRef<\"button\">)}\n >\n <span className=\"flex-1 truncate text-left\">{children}</span>\n <CaretRightIcon\n className={cn(\n \"ml-auto size-3 shrink-0 text-sf-subtle transition-transform duration-200\",\n \"group-data-[panel-open]/group-label:rotate-90\"\n )}\n />\n </CollapsibleBase.Trigger>\n );\n }\n\n return (\n <div\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"truncate px-3 py-1 text-xs font-medium text-sf-subtle\",\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nSidebarGroupLabel.displayName = \"Sidebar.GroupLabel\";\n\n// ============================================================================\n// Sidebar GroupContent\n// ============================================================================\n\n/**\n * Animation wrapper for collapsible group content. Uses CSS grid-rows\n * for smooth height transitions when the group is expanded/collapsed.\n *\n * **Only needed for collapsible groups.** For non-collapsible groups,\n * place `Menu` directly inside `Group` — no wrapper required.\n *\n * @example Collapsible group (GroupContent required)\n * ```tsx\n * <Sidebar.Group collapsible defaultOpen>\n * <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>\n * <Sidebar.GroupContent>\n * <Sidebar.Menu>...</Sidebar.Menu>\n * </Sidebar.GroupContent>\n * </Sidebar.Group>\n * ```\n *\n * @example Non-collapsible group (no GroupContent needed)\n * ```tsx\n * <Sidebar.Group>\n * <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>\n * <Sidebar.Menu>...</Sidebar.Menu>\n * </Sidebar.Group>\n * ```\n */\nconst SidebarGroupContent = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, children, ...props }, ref) => {\n const { isCollapsible, isOpen } = useContext(SidebarGroupCollapsibleContext);\n\n if (isCollapsible) {\n return (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\n \"grid\",\n // Animate height via grid-rows — matches production NavGroup pattern\n \"transition-[grid-template-rows] duration-250 ease-[cubic-bezier(0.77,0,0.175,1)]\",\n \"motion-reduce:transition-none\",\n // Default: collapsed\n \"grid-rows-[0fr]\",\n // When sidebar is expanded, respect group open/close state\n isOpen\n ? \"group-data-[state=expanded]/sidebar:grid-rows-[1fr]\"\n : \"group-data-[state=expanded]/sidebar:grid-rows-[0fr]\",\n className\n )}\n {...props}\n >\n <div className=\"overflow-hidden\">{children}</div>\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"flex flex-col\", className)}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nSidebarGroupContent.displayName = \"Sidebar.GroupContent\";\n\n// ============================================================================\n// MenuItem / MenuSubItem auto-wrap contexts\n// ============================================================================\n\n/**\n * When `true`, indicates the component is already wrapped in a `MenuItem` `<li>`.\n * `MenuButton` checks this: if `false`, it auto-wraps itself in an `<li>`.\n */\nconst MenuItemContext = createContext(false);\n\n/**\n * When `true`, indicates the component is already wrapped in a `MenuSubItem` `<li>`.\n * `MenuSubButton` checks this: if `false`, it auto-wraps itself in an `<li>`.\n */\nconst MenuSubItemContext = createContext(false);\n\n// ============================================================================\n// Sidebar Menu\n// ============================================================================\n\n/**\n * Navigation menu list. Renders as `<ul>`.\n *\n * `MenuButton` auto-wraps in `<li>` so `MenuItem` is optional for simple items.\n *\n * @example Simple usage\n * ```tsx\n * <Sidebar.Menu>\n * <Sidebar.MenuButton icon={HouseIcon} active>Account home</Sidebar.MenuButton>\n * <Sidebar.MenuButton icon={GlobeIcon}>Domains</Sidebar.MenuButton>\n * </Sidebar.Menu>\n * ```\n *\n * @example With explicit MenuItem (needed for MenuAction sibling)\n * ```tsx\n * <Sidebar.Menu>\n * <Sidebar.MenuItem>\n * <Sidebar.MenuButton icon={GearIcon}>Settings</Sidebar.MenuButton>\n * <Sidebar.MenuAction><PencilIcon /></Sidebar.MenuAction>\n * </Sidebar.MenuItem>\n * </Sidebar.Menu>\n * ```\n */\nconst SidebarMenu = forwardRef<\n HTMLUListElement,\n ComponentPropsWithoutRef<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\n \"m-0 flex min-w-0 list-none flex-col gap-0.5 p-0\",\n \"group-data-[state=collapsed]/sidebar:gap-0\",\n className\n )}\n {...props}\n />\n));\n\nSidebarMenu.displayName = \"Sidebar.Menu\";\n\n// ============================================================================\n// Sidebar MenuItem\n// ============================================================================\n\n/**\n * Individual menu list item. Renders as `<li>`.\n *\n * **Optional when using `MenuButton` directly** — `MenuButton` auto-wraps\n * itself in a `<li>` when not already inside a `MenuItem`. Use `MenuItem`\n * explicitly when you need to place siblings (e.g., `MenuAction`) alongside\n * a `MenuButton`.\n *\n * @example Explicit usage (needed for MenuAction sibling)\n * ```tsx\n * <Sidebar.MenuItem>\n * <Sidebar.MenuButton icon={GearIcon}>Settings</Sidebar.MenuButton>\n * <Sidebar.MenuAction><PencilIcon /></Sidebar.MenuAction>\n * </Sidebar.MenuItem>\n * ```\n */\nconst SidebarMenuItem = forwardRef<\n HTMLLIElement,\n ComponentPropsWithoutRef<\"li\">\n>(({ className, children, ...props }, ref) => (\n <MenuItemContext.Provider value={true}>\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"relative\", className)}\n {...props}\n >\n {children}\n </li>\n </MenuItemContext.Provider>\n));\n\nSidebarMenuItem.displayName = \"Sidebar.MenuItem\";\n\n// ============================================================================\n// Sidebar MenuButton\n// ============================================================================\n\nexport type SidebarMenuButtonSize = \"base\" | \"sm\";\n\nexport interface SidebarMenuButtonProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"className\" | \"children\"\n> {\n icon?: React.ComponentType<{ className?: string }> | React.ReactNode;\n active?: boolean;\n /**\n * Button size.\n * - `\"base\"` — Standard nav item\n * - `\"sm\"` — Compact nav item\n * @default \"base\"\n */\n size?: SidebarMenuButtonSize;\n href?: string;\n tooltip?: string;\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Primary interactive element inside a menu item. Renders as a `<button>` or link.\n * Supports icons, active state, and auto-tooltip when the sidebar is collapsed.\n *\n * **Auto-wraps in `<li>`** when not already inside a `Sidebar.MenuItem`.\n * Use `MenuItem` explicitly only when you need siblings (e.g., `MenuAction`).\n *\n * When used as a `Collapsible.Trigger` via `render` prop, the expand/collapse chevron\n * auto-rotates thanks to Base UI's `data-panel-open` attribute combined with\n * `group/menu-button` CSS grouping.\n *\n * @example Simple usage (auto-wrapped in `<li>`)\n * ```tsx\n * <Sidebar.Menu>\n * <Sidebar.MenuButton icon={GlobeIcon} active>Domains</Sidebar.MenuButton>\n * <Sidebar.MenuButton icon={ClockIcon} href=\"/recents\">Recents</Sidebar.MenuButton>\n * </Sidebar.Menu>\n * ```\n *\n * @example With MenuAction sibling (explicit MenuItem needed)\n * ```tsx\n * <Sidebar.MenuItem>\n * <Sidebar.MenuButton icon={GearIcon}>Settings</Sidebar.MenuButton>\n * <Sidebar.MenuAction><PencilIcon /></Sidebar.MenuAction>\n * </Sidebar.MenuItem>\n * ```\n */\nconst SidebarMenuButton = forwardRef<HTMLButtonElement, SidebarMenuButtonProps>(\n (\n {\n className,\n icon: IconProp,\n active = false,\n size = \"base\",\n href,\n tooltip,\n children,\n ...props\n },\n ref\n ) => {\n const { state } = useSidebar();\n const LinkComponent = useLinkComponent();\n const isInsideMenuItem = useContext(MenuItemContext);\n\n // Render icon — supports both component types and React elements\n const iconNode = (() => {\n if (!IconProp) return null;\n if (React.isValidElement(IconProp)) return IconProp;\n const Comp = IconProp as React.ComponentType<{ className?: string }>;\n return (\n <Comp\n className={cn(\"shrink-0\", size === \"base\" ? \"size-3.5\" : \"size-3\")}\n />\n );\n })();\n\n const content = (\n <>\n {iconNode}\n <span\n className={cn(\n \"flex flex-1 items-center min-w-0 text-left overflow-hidden\",\n \"group-data-[state=collapsed]/sidebar:hidden\"\n )}\n >\n {children}\n </span>\n </>\n );\n\n const buttonClasses = cn(\n // Layout\n \"group/menu-button flex w-full min-w-0 items-center gap-2 rounded-lg outline-none cursor-pointer\",\n // Sizing\n size === \"base\" && \"min-h-[34px] px-3 py-1.5 text-sm font-medium\",\n size === \"sm\" && \"min-h-[28px] px-2 py-1 text-sm\",\n // Default state — transition includes padding so collapsed centering animates smoothly\n \"text-sf-default\",\n \"transition-[color,background-color,padding] duration-0 ease-[cubic-bezier(0.77,0,0.175,1)]\",\n // Icon color\n \"[&>svg]:text-sf-subtle\",\n !active && \"hover:bg-sf-tint\",\n // Active state\n active && \"bg-sf-tint\",\n // When a child sub-button is active, don't show active styling on the parent trigger\n \"has-[[data-active]]:bg-transparent has-[[data-active]]:hover:bg-sf-tint\",\n // Focus\n \"focus-visible:ring-1 focus-visible:ring-sf-ring\",\n // Collapsed: px-2 centers the icon (48px sidebar − 16px content padding = 32px;\n // 32px − 2×8px padding = 16px = icon size). Padding transition keeps it smooth.\n \"group-data-[state=collapsed]/sidebar:px-2\",\n className\n );\n\n let button: React.ReactNode = href ? (\n <LinkComponent\n ref={ref as React.Ref<HTMLAnchorElement>}\n className={cn(buttonClasses, \"no-underline!\")}\n href={href}\n to={href}\n data-active={active || undefined}\n data-sidebar=\"menu-button\"\n data-size={size}\n onClick={\n props.onClick as unknown as React.MouseEventHandler<HTMLAnchorElement>\n }\n >\n {content}\n </LinkComponent>\n ) : (\n <button\n ref={ref}\n type=\"button\"\n className={buttonClasses}\n data-active={active || undefined}\n data-sidebar=\"menu-button\"\n data-size={size}\n {...props}\n >\n {content}\n </button>\n );\n\n // Wrap in Tooltip when collapsed and tooltip text is provided.\n if (state === \"collapsed\" && tooltip) {\n button = (\n <Tooltip content={tooltip} side=\"right\">\n {button}\n </Tooltip>\n );\n }\n\n // Auto-wrap in <li> when not already inside a MenuItem\n if (!isInsideMenuItem) {\n return (\n <li data-sidebar=\"menu-item\" className=\"relative\">\n {button}\n </li>\n );\n }\n\n return button;\n }\n);\n\nSidebarMenuButton.displayName = \"Sidebar.MenuButton\";\n\n// ============================================================================\n// Sidebar MenuAction\n// ============================================================================\n\n/**\n * Right-aligned action button inside a menu item (e.g., settings gear, plus icon).\n * Positioned absolutely so it overlays the menu button.\n * Hidden when the sidebar is collapsed.\n */\nconst SidebarMenuAction = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<\"button\">\n>(({ className, ...props }, ref) => (\n <button\n ref={ref}\n type=\"button\"\n data-sidebar=\"menu-action\"\n className={cn(\n \"absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center justify-center rounded-md p-1\",\n \"text-sf-strong hover:bg-sf-overlay\",\n \"transition-colors duration-150\",\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n {...props}\n />\n));\n\nSidebarMenuAction.displayName = \"Sidebar.MenuAction\";\n\n// ============================================================================\n// Sidebar MenuBadge\n// ============================================================================\n\n/**\n * Badge pill displayed inside a menu button (e.g., \"Beta\", \"New\").\n * Uses dashed border styling.\n *\n * @example\n * ```tsx\n * <Sidebar.MenuSubButton>\n * Containers\n * <Sidebar.MenuBadge>Beta</Sidebar.MenuBadge>\n * </Sidebar.MenuSubButton>\n * ```\n */\nconst SidebarMenuBadge = forwardRef<\n HTMLSpanElement,\n ComponentPropsWithoutRef<\"span\">\n>(({ className, ...props }, ref) => (\n <span\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"inline-flex shrink-0 items-center rounded-full border border-dashed border-sf-line\",\n \"select-none px-1.5 py-0.5 text-[11px]/none font-medium text-sf-strong\",\n // Hidden when collapsed\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n {...props}\n />\n));\n\nSidebarMenuBadge.displayName = \"Sidebar.MenuBadge\";\n\n// ============================================================================\n// Sidebar MenuSub\n// ============================================================================\n\n/**\n * Indented sub-menu container for child navigation items. Renders as `<ul>` with\n * a left border accent for visual hierarchy.\n *\n * `MenuSubButton` auto-wraps in `<li>` so `MenuSubItem` is optional.\n *\n * @example\n * ```tsx\n * <Sidebar.MenuSub>\n * <Sidebar.MenuSubButton active>Workers & Pages</Sidebar.MenuSubButton>\n * <Sidebar.MenuSubButton>Durable Objects</Sidebar.MenuSubButton>\n * </Sidebar.MenuSub>\n * ```\n */\nconst SidebarMenuSub = forwardRef<\n HTMLUListElement,\n ComponentPropsWithoutRef<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"m-0 ml-3.5 flex min-w-0 list-none flex-col gap-0.5 border-l border-sf-line p-0 pl-2.5\",\n // Hidden when collapsed\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n {...props}\n />\n));\n\nSidebarMenuSub.displayName = \"Sidebar.MenuSub\";\n\n// ============================================================================\n// Sidebar MenuSubItem\n// ============================================================================\n\n/**\n * Individual item inside a sub-menu. Renders as `<li>`.\n *\n * **Optional when using `MenuSubButton` directly** — `MenuSubButton` auto-wraps\n * itself in a `<li>` when not already inside a `MenuSubItem`.\n */\nconst SidebarMenuSubItem = forwardRef<\n HTMLLIElement,\n ComponentPropsWithoutRef<\"li\">\n>(({ className, children, ...props }, ref) => (\n <MenuSubItemContext.Provider value={true}>\n <li\n ref={ref}\n data-sidebar=\"menu-sub-item\"\n className={cn(\"relative\", className)}\n {...props}\n >\n {children}\n </li>\n </MenuSubItemContext.Provider>\n));\n\nSidebarMenuSubItem.displayName = \"Sidebar.MenuSubItem\";\n\n// ============================================================================\n// Sidebar MenuSubButton\n// ============================================================================\n\nexport interface SidebarMenuSubButtonProps extends ComponentPropsWithoutRef<\"button\"> {\n /** Marks this sub-item as currently active/selected. @default false */\n active?: boolean;\n /** Navigation URL. When set, renders as a link via LinkProvider. */\n href?: string;\n}\n\n/**\n * Button inside a sub-menu item. Does not render an icon (sub-items are\n * indented instead). Supports active state and link rendering.\n *\n * **Auto-wraps in `<li>`** when not already inside a `Sidebar.MenuSubItem`.\n *\n * @example Simple usage (auto-wrapped in `<li>`)\n * ```tsx\n * <Sidebar.MenuSub>\n * <Sidebar.MenuSubButton active>Workers & Pages</Sidebar.MenuSubButton>\n * <Sidebar.MenuSubButton href=\"/observability\">Observability</Sidebar.MenuSubButton>\n * </Sidebar.MenuSub>\n * ```\n */\nconst SidebarMenuSubButton = forwardRef<\n HTMLButtonElement,\n SidebarMenuSubButtonProps\n>(({ className, active = false, href, children, ...props }, ref) => {\n const LinkComponent = useLinkComponent();\n const isInsideMenuSubItem = useContext(MenuSubItemContext);\n\n const buttonClasses = cn(\n \"flex w-full min-w-0 items-center gap-2 rounded-lg min-h-[34px] px-3 py-1 text-sm font-medium outline-none\",\n \"text-sf-default transition-colors duration-150\",\n !active && \"hover:bg-sf-tint\",\n active && \"bg-sf-tint\",\n \"focus-visible:ring-1 focus-visible:ring-sf-ring\",\n className\n );\n\n const content = <span className=\"flex-1 truncate text-left\">{children}</span>;\n\n const button: React.ReactNode = href ? (\n <LinkComponent\n ref={ref as React.Ref<HTMLAnchorElement>}\n className={cn(buttonClasses, \"no-underline!\")}\n href={href}\n to={href}\n data-active={active || undefined}\n data-sidebar=\"menu-sub-button\"\n onClick={\n props.onClick as unknown as React.MouseEventHandler<HTMLAnchorElement>\n }\n >\n {content}\n </LinkComponent>\n ) : (\n <button\n ref={ref}\n type=\"button\"\n className={buttonClasses}\n data-active={active || undefined}\n data-sidebar=\"menu-sub-button\"\n {...props}\n >\n {content}\n </button>\n );\n\n // Auto-wrap in <li> when not already inside a MenuSubItem\n if (!isInsideMenuSubItem) {\n return (\n <li data-sidebar=\"menu-sub-item\" className=\"relative\">\n {button}\n </li>\n );\n }\n\n return button;\n});\n\nSidebarMenuSubButton.displayName = \"Sidebar.MenuSubButton\";\n\n// ============================================================================\n// Sidebar Separator\n// ============================================================================\n\n/**\n * Horizontal divider line between sidebar sections.\n */\nconst SidebarSeparator = forwardRef<\n HTMLHRElement,\n ComponentPropsWithoutRef<\"hr\">\n>(({ className, ...props }, ref) => (\n <hr\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"mx-2 min-h-px h-px border-0 bg-sf-line\", className)}\n {...props}\n />\n));\n\nSidebarSeparator.displayName = \"Sidebar.Separator\";\n\n// ============================================================================\n// Sidebar Input\n// ============================================================================\n\nexport interface SidebarInputProps extends ComponentPropsWithoutRef<\"button\"> {\n /** Placeholder text displayed inside the search trigger. @default \"Search...\" */\n placeholder?: string;\n /** Keyboard shortcut hint (e.g., \"⌘K\"). */\n shortcut?: string;\n}\n\n/**\n * Search trigger button styled as an input. Typically opens a command palette.\n *\n * @example\n * ```tsx\n * <Sidebar.Input placeholder=\"Quick search...\" shortcut=\"⌘K\" onClick={openSearch} />\n * ```\n */\nconst SidebarInput = forwardRef<HTMLButtonElement, SidebarInputProps>(\n (\n { className, placeholder = \"Search...\", shortcut, children, ...props },\n ref\n ) => (\n <button\n ref={ref}\n type=\"button\"\n data-sidebar=\"input\"\n className={cn(\n \"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm\",\n \"bg-sf-base text-sf-subtle ring ring-sf-line\",\n \"transition-[color,background-color,padding,box-shadow] duration-250 ease-[cubic-bezier(0.77,0,0.175,1)]\",\n \"hover:bg-sf-overlay\",\n // Collapsed: icon-only, padding centers icon, ring fades via box-shadow transition\n \"group-data-[state=collapsed]/sidebar:px-2 group-data-[state=collapsed]/sidebar:ring-0\",\n className\n )}\n {...props}\n >\n <MagnifyingGlassIcon className=\"size-4 shrink-0 text-sf-subtle\" />\n <span className=\"flex-1 truncate text-left group-data-[state=collapsed]/sidebar:hidden\">\n {children ?? placeholder}\n </span>\n {shortcut && (\n <kbd className=\"ml-auto font-sans text-xs text-sf-subtle group-data-[state=collapsed]/sidebar:hidden\">\n {shortcut}\n </kbd>\n )}\n </button>\n )\n);\n\nSidebarInput.displayName = \"Sidebar.Input\";\n\n// ============================================================================\n// Sidebar Trigger\n// ============================================================================\n\n/**\n * Button that toggles the sidebar open/collapsed. Uses `toggleSidebar()` from context.\n * Defaults to a `SidebarSimpleIcon`, left-aligned.\n *\n * @example\n * ```tsx\n * <Sidebar.Trigger />\n * <Sidebar.Trigger><PanelLeftIcon className=\"size-5\" /></Sidebar.Trigger>\n * ```\n */\nconst SidebarTrigger = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<\"button\">\n>(({ className, children, onClick, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n ref={ref}\n type=\"button\"\n data-sidebar=\"trigger\"\n aria-label=\"Toggle sidebar\"\n className={cn(\n \"flex items-center rounded-md p-1.5\",\n \"text-sf-subtle hover:text-sf-strong hover:bg-sf-overlay\",\n \"transition-colors duration-150\",\n className\n )}\n onClick={(e) => {\n onClick?.(e);\n toggleSidebar();\n }}\n {...props}\n >\n {children ?? <SidebarSimpleIcon className=\"size-5\" />}\n </button>\n );\n});\n\nSidebarTrigger.displayName = \"Sidebar.Trigger\";\n\n// ============================================================================\n// Sidebar Rail\n// ============================================================================\n\n/**\n * Invisible interaction strip at the sidebar edge for click-to-toggle.\n * Renders a thin hover-sensitive area between the sidebar and main content.\n */\nconst SidebarRail = forwardRef<\n HTMLButtonElement,\n ComponentPropsWithoutRef<\"button\">\n>(({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar();\n\n return (\n <button\n ref={ref}\n type=\"button\"\n data-sidebar=\"rail\"\n aria-label=\"Toggle sidebar\"\n tabIndex={-1}\n className={cn(\n \"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 cursor-pointer transition-all\",\n \"after:absolute after:inset-y-0 after:left-1/2 after:w-0.5\",\n \"hover:after:bg-sf-brand/20\",\n \"group-data-[side=left]/sidebar-wrapper:right-0\",\n \"group-data-[side=right]/sidebar-wrapper:left-0\",\n \"sm:flex\",\n className\n )}\n onClick={toggleSidebar}\n {...props}\n />\n );\n});\n\nSidebarRail.displayName = \"Sidebar.Rail\";\n\n// ============================================================================\n// Sidebar ResizeHandle\n// ============================================================================\n\n/**\n * Drag handle for resizing the sidebar. Renders when `resizable` is true in\n * both expanded and collapsed states.\n *\n * - **Expanded → drag inward past `minWidth`**: auto-collapses to icon-only.\n * - **Collapsed → drag outward past `minWidth`**: auto-expands and begins\n * tracking width from `minWidth`.\n */\nconst SidebarResizeHandle = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => {\n const { side, resizable, setIsResizing, setWidth, setOpen, open, minWidth } =\n useSidebar();\n const startX = useRef(0);\n const startWidth = useRef(0);\n const wasCollapsed = useRef(false);\n\n if (!resizable) return null;\n\n const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n e.preventDefault();\n setIsResizing(true);\n startX.current = e.clientX;\n wasCollapsed.current = !open;\n\n const wrapper = (e.currentTarget as HTMLElement).closest(\n \"[data-sidebar-wrapper]\"\n );\n const sidebar = wrapper?.querySelector(\"[data-sidebar='sidebar']\");\n startWidth.current = sidebar?.getBoundingClientRect().width ?? 0;\n\n const cleanup = () => {\n setIsResizing(false);\n document.removeEventListener(\"pointermove\", handlePointerMove);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n\n const handlePointerMove = (moveEvent: PointerEvent) => {\n const delta =\n side === \"left\"\n ? moveEvent.clientX - startX.current\n : startX.current - moveEvent.clientX;\n const rawWidth = startWidth.current + delta;\n\n if (wasCollapsed.current) {\n // Dragging outward from collapsed — expand once past minWidth\n if (rawWidth >= minWidth) {\n wasCollapsed.current = false;\n setOpen(true);\n setWidth(rawWidth);\n }\n return;\n }\n\n // Dragging inward while expanded — collapse when below minWidth\n if (rawWidth < minWidth) {\n setOpen(false);\n wasCollapsed.current = true;\n return;\n }\n\n setWidth(rawWidth);\n };\n\n const handlePointerUp = () => {\n cleanup();\n };\n\n document.addEventListener(\"pointermove\", handlePointerMove);\n document.addEventListener(\"pointerup\", handlePointerUp);\n };\n\n return (\n <div\n ref={ref}\n data-sidebar=\"resize-handle\"\n className={cn(\n \"absolute inset-y-0 z-20 hidden w-1 cursor-col-resize transition-colors sm:block\",\n \"hover:bg-sf-brand/30 active:bg-sf-brand/50\",\n side === \"left\" && \"right-0\",\n side === \"right\" && \"left-0\",\n className\n )}\n onPointerDown={handlePointerDown}\n {...props}\n />\n );\n});\n\nSidebarResizeHandle.displayName = \"Sidebar.ResizeHandle\";\n\n// ============================================================================\n// Sidebar MenuChevron\n// ============================================================================\n\n/**\n * Auto-rotating chevron for collapsible menu items. When the parent\n * `MenuButton` is used as a `Collapsible.Trigger`, Base UI sets\n * `data-panel-open` on the trigger — the chevron rotates automatically via CSS.\n *\n * @example\n * ```tsx\n * <Sidebar.CollapsibleTrigger render={<Sidebar.MenuButton icon={ComputeIcon} />}>\n * Compute\n * <Sidebar.MenuChevron />\n * </Sidebar.CollapsibleTrigger>\n * ```\n */\nfunction SidebarMenuChevron({ className }: { className?: string }) {\n return (\n <CaretRightIcon\n className={cn(\n \"ml-auto size-4 shrink-0 text-sf-subtle transition-transform duration-200\",\n // Auto-rotate when inside an open Collapsible trigger\n \"group-data-[panel-open]/menu-button:rotate-90\",\n // Hidden when collapsed\n \"group-data-[state=collapsed]/sidebar:hidden\",\n className\n )}\n />\n );\n}\n\nSidebarMenuChevron.displayName = \"Sidebar.MenuChevron\";\n\n// ============================================================================\n// Collapsible re-exports\n// ============================================================================\n\n/**\n * Base UI Collapsible.Root for sidebar sub-menu expand/collapse.\n * @see https://base-ui.com/react/components/collapsible\n */\nconst SidebarCollapsible = CollapsibleBase.Root;\n\n/**\n * Base UI Collapsible.Trigger for sidebar sub-menu toggle.\n * Use `render` prop to compose with `Sidebar.MenuButton`.\n *\n * @example\n * ```tsx\n * <Sidebar.CollapsibleTrigger render={<Sidebar.MenuButton icon={DiamondIcon} />}>\n * Compute\n * <Sidebar.MenuChevron />\n * </Sidebar.CollapsibleTrigger>\n * ```\n */\nconst SidebarCollapsibleTrigger = CollapsibleBase.Trigger;\n\n/**\n * Animated collapsible panel for sidebar sub-menu content.\n * Wraps Base UI `Collapsible.Panel` with a height transition driven by the\n * `--collapsible-panel-height` CSS variable that Base UI measures automatically.\n *\n * Uses `keepMounted` by default so the exit animation plays before removal.\n *\n * Animation flow:\n * - **Opening**: `data-starting-style` (h=0) → `data-open` (h=measured) — transition up\n * - **Closing**: `data-open` removed + `data-ending-style` (h=0) — transition down\n * - **Closed**: `data-closed` (h=0) — stays collapsed while mounted\n */\nconst SidebarCollapsibleContent = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<typeof CollapsibleBase.Panel>\n>(({ className, keepMounted = true, ...props }, ref) => (\n <CollapsibleBase.Panel\n ref={ref}\n keepMounted={keepMounted}\n className={cn(\n \"overflow-hidden\",\n // Default: show at measured height (when data-open, no override matches)\n \"h-[var(--collapsible-panel-height)]\",\n // Transition height — matches production NavGroup easing\n \"transition-[height] duration-250 ease-[cubic-bezier(0.77,0,0.175,1)]\",\n \"motion-reduce:transition-none\",\n // Closed / animating in / animating out: height 0\n \"data-[closed]:h-0 data-[starting-style]:h-0 data-[ending-style]:h-0\",\n className\n )}\n {...props}\n />\n));\n\nSidebarCollapsibleContent.displayName = \"Sidebar.CollapsibleContent\";\n\n// ============================================================================\n// Compound Component Export\n// ============================================================================\n\n/**\n * Sidebar — responsive navigation panel with expand/collapse support.\n *\n * Compound component: `Sidebar` (root `<aside>`), `.Provider`, `.Header`,\n * `.Content`, `.Footer`, `.Group`, `.GroupLabel`, `.GroupContent`,\n * `.Menu`, `.MenuItem`, `.MenuButton`, `.MenuAction`, `.MenuBadge`,\n * `.MenuSub`, `.MenuSubItem`, `.MenuSubButton`, `.Separator`,\n * `.Input`, `.Trigger`, `.Rail`, `.MenuChevron`,\n * `.Collapsible`, `.CollapsibleTrigger`, `.CollapsibleContent`.\n *\n * Built on `@base-ui/react/collapsible` + `@base-ui/react/dialog`.\n *\n * @example\n * ```tsx\n * <Sidebar.Provider defaultOpen>\n * <Sidebar>\n * <Sidebar.Content>\n * <Sidebar.Group>\n * <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>\n * <Sidebar.Menu>\n * <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>\n * <Sidebar.MenuButton icon={GlobeIcon}>Domains</Sidebar.MenuButton>\n * </Sidebar.Menu>\n * </Sidebar.Group>\n * </Sidebar.Content>\n * <Sidebar.Footer>\n * <Sidebar.Trigger />\n * </Sidebar.Footer>\n * </Sidebar>\n * </Sidebar.Provider>\n * ```\n */\nexport const Sidebar = Object.assign(SidebarRoot, {\n Provider: SidebarProvider,\n Header: SidebarHeader,\n Content: SidebarContent,\n Footer: SidebarFooter,\n Group: SidebarGroup,\n GroupLabel: SidebarGroupLabel,\n GroupContent: SidebarGroupContent,\n Menu: SidebarMenu,\n MenuItem: SidebarMenuItem,\n MenuButton: SidebarMenuButton,\n MenuAction: SidebarMenuAction,\n MenuBadge: SidebarMenuBadge,\n MenuSub: SidebarMenuSub,\n MenuSubItem: SidebarMenuSubItem,\n MenuSubButton: SidebarMenuSubButton,\n Separator: SidebarSeparator,\n Input: SidebarInput,\n Trigger: SidebarTrigger,\n Rail: SidebarRail,\n ResizeHandle: SidebarResizeHandle,\n MenuChevron: SidebarMenuChevron,\n Collapsible: SidebarCollapsible,\n CollapsibleTrigger: SidebarCollapsibleTrigger,\n CollapsibleContent: SidebarCollapsibleContent,\n});\n\nexport {\n SidebarProvider,\n SidebarRoot,\n SidebarHeader,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupLabel,\n SidebarGroupContent,\n SidebarMenu,\n SidebarMenuItem,\n SidebarMenuButton,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuSub,\n SidebarMenuSubItem,\n SidebarMenuSubButton,\n SidebarSeparator,\n SidebarInput,\n SidebarTrigger,\n SidebarRail,\n SidebarResizeHandle,\n SidebarMenuChevron,\n SidebarCollapsible,\n SidebarCollapsibleTrigger,\n SidebarCollapsibleContent,\n};\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card-CEZscNh8.js","names":[],"sources":["../src/components/stat-card/stat-card.tsx"],"sourcesContent":["import { forwardRef, type ReactNode } from \"react\";\n\nimport { cn } from \"../../utils/cn\";\n\n/** StatCard variant definitions (indexed by variant name) */\nexport const SF_STAT_CARD_VARIANTS = {\n size: {\n sm: {\n root: \"gap-1 p-3\",\n label: \"text-xs\",\n value: \"text-xl\",\n hint: \"text-xs\",\n },\n base: {\n root: \"gap-2 p-4\",\n label: \"text-sm\",\n value: \"text-3xl\",\n hint: \"text-xs\",\n },\n },\n} as const;\n\n/** Default variants applied when none are passed explicitly. */\nexport const SF_STAT_CARD_DEFAULT_VARIANTS = {\n size: \"base\",\n} as const;\n\nexport interface SFStatCardVariantsProps {\n /** Size of the card. Defaults to `\"base\"`. */\n size?: keyof typeof SF_STAT_CARD_VARIANTS.size;\n}\n\n/** Describes a change in the metric over a comparison period. */\nexport interface StatCardDelta {\n /** Numeric delta (e.g. `0.12` for +12% or `-4` for a drop of 4). */\n value: number;\n /**\n * Override the arrow direction. By default, direction is inferred from the\n * sign of `value`.\n */\n direction?: \"up\" | \"down\" | \"flat\";\n /** Optional context label shown after the delta (e.g. `\"vs last week\"`). */\n label?: string;\n /**\n * Whether an \"up\" direction means \"good\" (green) or \"bad\" (red). For metrics\n * like error rate, set this to `\"down\"` so downward movement is coloured\n * green.\n * @default \"up\"\n */\n positiveDirection?: \"up\" | \"down\";\n}\n\n/** Props for `StatCard` */\nexport interface StatCardProps extends SFStatCardVariantsProps {\n /** Short descriptive label (e.g. `\"Active users\"`). */\n label: ReactNode;\n /** Primary value to emphasise (pre-formatted). */\n value: ReactNode;\n /** Optional secondary text displayed under the value. */\n hint?: ReactNode;\n /** Optional change indicator (arrow + number + label). */\n delta?: StatCardDelta;\n /**\n * Optional content slot rendered on the right-hand side — typically a\n * `Sparkline` showing the metric's trend.\n */\n trend?: ReactNode;\n /** When `true`, renders a skeleton placeholder in place of value + delta. */\n loading?: boolean;\n /** Additional CSS classes merged via `cn()`. */\n className?: string;\n}\n\nfunction inferDirection(\n value: number,\n override?: \"up\" | \"down\" | \"flat\"\n): \"up\" | \"down\" | \"flat\" {\n if (override) return override;\n if (value > 0) return \"up\";\n if (value < 0) return \"down\";\n return \"flat\";\n}\n\nfunction deltaColor(\n direction: \"up\" | \"down\" | \"flat\",\n positiveDirection: \"up\" | \"down\"\n): string {\n if (direction === \"flat\") return \"text-sf-subtle\";\n const isPositive = direction === positiveDirection;\n return isPositive ? \"text-sf-success\" : \"text-sf-danger\";\n}\n\nfunction deltaArrow(direction: \"up\" | \"down\" | \"flat\"): string {\n if (direction === \"up\") return \"↑\";\n if (direction === \"down\") return \"↓\";\n return \"→\";\n}\n\n/**\n * StatCard — compact KPI tile displaying a label, a primary value, and an\n * optional delta + trend sparkline.\n *\n * @example\n * ```tsx\n * <StatCard\n * label=\"Active users\"\n * value=\"12,384\"\n * delta={{ value: 0.12, label: \"vs last week\" }}\n * />\n * ```\n */\nexport const StatCard = forwardRef<HTMLDivElement, StatCardProps>(\n function StatCard(\n {\n label,\n value,\n hint,\n delta,\n trend,\n loading,\n size = SF_STAT_CARD_DEFAULT_VARIANTS.size,\n className,\n },\n ref\n ) {\n const sizeClasses = SF_STAT_CARD_VARIANTS.size[size];\n const direction = delta\n ? inferDirection(delta.value, delta.direction)\n : undefined;\n const deltaClass =\n delta && direction\n ? deltaColor(direction, delta.positiveDirection ?? \"up\")\n : undefined;\n\n return (\n <div\n ref={ref}\n className={cn(\n \"flex flex-col rounded-lg bg-sf-elevated text-sf-default\",\n sizeClasses.root,\n className\n )}\n >\n <div className={cn(\"text-sf-subtle\", sizeClasses.label)}>{label}</div>\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex flex-col gap-1\">\n {loading ? (\n <div\n aria-busy=\"true\"\n className={cn(\n \"h-8 w-24 animate-pulse rounded-md bg-sf-fill\",\n size === \"sm\" && \"h-6 w-20\"\n )}\n />\n ) : (\n <div\n className={cn(\n \"font-semibold tabular-nums text-sf-strong\",\n sizeClasses.value\n )}\n >\n {value}\n </div>\n )}\n {delta && !loading ? (\n <div\n className={cn(\n \"flex items-baseline gap-1 tabular-nums\",\n sizeClasses.hint\n )}\n >\n <span className={cn(\"font-medium\", deltaClass)}>\n {deltaArrow(direction!)}{\" \"}\n {Math.abs(delta.value).toLocaleString(undefined, {\n maximumFractionDigits: 2,\n })}\n </span>\n {delta.label ? (\n <span className=\"text-sf-subtle\">{delta.label}</span>\n ) : null}\n </div>\n ) : null}\n {hint && !loading ? (\n <div className={cn(\"text-sf-subtle\", sizeClasses.hint)}>\n {hint}\n </div>\n ) : null}\n </div>\n {trend ? (\n <div className=\"pointer-events-none shrink-0\">{trend}</div>\n ) : null}\n </div>\n </div>\n );\n }\n);\n\nStatCard.displayName = \"StatCard\";\n"],"mappings":";;;;;;AAKA,IAAa,wBAAwB,EACnC,MAAM;CACJ,IAAI;EACF,MAAM;EACN,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACD,MAAM;EACJ,MAAM;EACN,OAAO;EACP,OAAO;EACP,MAAM;EACP;CACF,EACF;;AAGD,IAAa,gCAAgC,EAC3C,MAAM,QACP;AAgDD,SAAS,eACP,OACA,UACwB;AACxB,KAAI,SAAU,QAAO;AACrB,KAAI,QAAQ,EAAG,QAAO;AACtB,KAAI,QAAQ,EAAG,QAAO;AACtB,QAAO;;AAGT,SAAS,WACP,WACA,mBACQ;AACR,KAAI,cAAc,OAAQ,QAAO;AAEjC,QADmB,cAAc,oBACb,oBAAoB;;AAG1C,SAAS,WAAW,WAA2C;AAC7D,KAAI,cAAc,KAAM,QAAO;AAC/B,KAAI,cAAc,OAAQ,QAAO;AACjC,QAAO;;;;;;;;;;;;;;;AAgBT,IAAa,WAAW,WACtB,SAAS,SACP,EACE,OACA,OACA,MACA,OACA,OACA,SACA,OAAO,8BAA8B,MACrC,aAEF,KACA;CACA,MAAM,cAAc,sBAAsB,KAAK;CAC/C,MAAM,YAAY,QACd,eAAe,MAAM,OAAO,MAAM,UAAU,GAC5C,KAAA;CACJ,MAAM,aACJ,SAAS,YACL,WAAW,WAAW,MAAM,qBAAqB,KAAK,GACtD,KAAA;AAEN,QACE,qBAAC,OAAD;EACO;EACL,WAAW,GACT,2DACA,YAAY,MACZ,UACD;YANH,CAQE,oBAAC,OAAD;GAAK,WAAW,GAAG,kBAAkB,YAAY,MAAM;aAAG;GAAY,CAAA,EACtE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,OAAD;IAAK,WAAU;cAAf;KACG,UACC,oBAAC,OAAD;MACE,aAAU;MACV,WAAW,GACT,gDACA,SAAS,QAAQ,WAClB;MACD,CAAA,GAEF,oBAAC,OAAD;MACE,WAAW,GACT,6CACA,YAAY,MACb;gBAEA;MACG,CAAA;KAEP,SAAS,CAAC,UACT,qBAAC,OAAD;MACE,WAAW,GACT,0CACA,YAAY,KACb;gBAJH,CAME,qBAAC,QAAD;OAAM,WAAW,GAAG,eAAe,WAAW;iBAA9C;QACG,WAAW,UAAW;QAAE;QACxB,KAAK,IAAI,MAAM,MAAM,CAAC,eAAe,KAAA,GAAW,EAC/C,uBAAuB,GACxB,CAAC;QACG;UACN,MAAM,QACL,oBAAC,QAAD;OAAM,WAAU;iBAAkB,MAAM;OAAa,CAAA,GACnD,KACA;UACJ;KACH,QAAQ,CAAC,UACR,oBAAC,OAAD;MAAK,WAAW,GAAG,kBAAkB,YAAY,KAAK;gBACnD;MACG,CAAA,GACJ;KACA;OACL,QACC,oBAAC,OAAD;IAAK,WAAU;cAAgC;IAAY,CAAA,GACzD,KACA;KACF;;EAGX;AAED,SAAS,cAAc"}
|