@exegia/corpora-ui 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist-lib/components/atoms/avatar/base.d.ts +2 -0
- package/dist-lib/components/atoms/avatar/index.d.ts +3 -0
- package/dist-lib/components/atoms/avatar/types.d.ts +20 -0
- package/dist-lib/components/atoms/avatar/utils.d.ts +13 -0
- package/dist-lib/components/atoms/background/texture.d.ts +2 -0
- package/dist-lib/components/atoms/background/type.d.ts +7 -0
- package/dist-lib/components/atoms/background/utils.d.ts +4 -0
- package/dist-lib/components/atoms/bubble/actions.d.ts +7 -0
- package/dist-lib/components/atoms/bubble/context.d.ts +4 -0
- package/dist-lib/components/atoms/bubble/default.d.ts +3 -0
- package/dist-lib/components/atoms/bubble/header.d.ts +8 -0
- package/dist-lib/components/atoms/bubble/index.d.ts +16 -0
- package/dist-lib/components/atoms/bubble/message.d.ts +3 -0
- package/dist-lib/components/atoms/bubble/reactions.d.ts +14 -0
- package/dist-lib/components/atoms/bubble/types.d.ts +62 -0
- package/dist-lib/components/atoms/bubble/utils.d.ts +13 -0
- package/dist-lib/components/atoms/field.d.ts +2 -0
- package/dist-lib/components/atoms/index.d.ts +10 -0
- package/dist-lib/components/atoms/loader.d.ts +2 -0
- package/dist-lib/components/atoms/reference.d.ts +7 -0
- package/dist-lib/components/atoms/text/default.d.ts +1144 -0
- package/dist-lib/components/atoms/text/heading.d.ts +2 -0
- package/dist-lib/components/atoms/text/index.d.ts +18 -0
- package/dist-lib/components/atoms/text/label.d.ts +2 -0
- package/dist-lib/components/atoms/text/paragraph.d.ts +2 -0
- package/dist-lib/components/atoms/text/span.d.ts +2 -0
- package/dist-lib/components/atoms/text/types.d.ts +27 -0
- package/dist-lib/components/atoms/text/utils.d.ts +4 -0
- package/dist-lib/components/atoms/text-selection/click-popover.d.ts +3 -0
- package/dist-lib/components/atoms/text-selection/index.d.ts +6 -0
- package/dist-lib/components/atoms/text-selection/popover.d.ts +3 -0
- package/dist-lib/components/atoms/text-selection/selection-atom.d.ts +30 -0
- package/dist-lib/components/atoms/text-selection/selection.d.ts +3 -0
- package/dist-lib/components/atoms/text-selection/types.d.ts +90 -0
- package/dist-lib/components/atoms/text-selection/use-selection.d.ts +7 -0
- package/dist-lib/components/atoms/types.d.ts +39 -0
- package/dist-lib/components/blocks/auth/auth-flow-atom.d.ts +76 -0
- package/dist-lib/components/blocks/auth/auth-flow-block.d.ts +41 -0
- package/dist-lib/components/blocks/auth/auth-session-atom.d.ts +28 -0
- package/dist-lib/components/blocks/auth/auth-shell.d.ts +55 -0
- package/dist-lib/components/blocks/auth/auth-state.d.ts +8 -0
- package/dist-lib/components/blocks/auth/code-auth-block.d.ts +3 -0
- package/dist-lib/components/blocks/auth/forgot-password-block.d.ts +3 -0
- package/dist-lib/components/blocks/auth/linked-accounts-block.d.ts +9 -0
- package/dist-lib/components/blocks/auth/login-block.d.ts +3 -0
- package/dist-lib/components/blocks/auth/onboarding/profile-step.d.ts +8 -0
- package/dist-lib/components/blocks/auth/onboarding-block.d.ts +15 -0
- package/dist-lib/components/blocks/auth/passkey-manager-block.d.ts +10 -0
- package/dist-lib/components/blocks/auth/passkey-sign-in-block.d.ts +8 -0
- package/dist-lib/components/blocks/auth/signup-block.d.ts +3 -0
- package/dist-lib/components/blocks/auth/type.d.ts +398 -0
- package/dist-lib/components/blocks/auth/update-password-block.d.ts +9 -0
- package/dist-lib/components/blocks/auth/use-auth-state.d.ts +52 -0
- package/dist-lib/components/blocks/chat/banners.d.ts +5 -0
- package/dist-lib/components/blocks/chat/base.d.ts +10 -0
- package/dist-lib/components/blocks/chat/header.d.ts +4 -0
- package/dist-lib/components/blocks/chat/index.d.ts +11 -0
- package/dist-lib/components/blocks/chat/recommendation-stack.d.ts +11 -0
- package/dist-lib/components/blocks/chat/suggested-prompts.d.ts +3 -0
- package/dist-lib/components/blocks/chat/type.d.ts +75 -0
- package/dist-lib/components/blocks/chat/version-history-record.d.ts +3 -0
- package/dist-lib/components/blocks/layout.d.ts +8 -0
- package/dist-lib/components/blocks/profile/index.d.ts +7 -0
- package/dist-lib/components/blocks/profile/profile-card-atom.d.ts +68 -0
- package/dist-lib/components/blocks/profile/profile-card-block.d.ts +66 -0
- package/dist-lib/components/blocks/profile/type.d.ts +80 -0
- package/dist-lib/components/blocks/profile/use-profile-card-state.d.ts +24 -0
- package/dist-lib/components/blocks/profile/use-profile-card.d.ts +34 -0
- package/dist-lib/components/blocks/scaffold/index.d.ts +29 -0
- package/dist-lib/components/blocks/scaffold/panel-close-button.d.ts +4 -0
- package/dist-lib/components/blocks/scaffold/panel-menu-button.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/scaffold-actions.d.ts +10 -0
- package/dist-lib/components/blocks/scaffold/scaffold-atom.d.ts +112 -0
- package/dist-lib/components/blocks/scaffold/scaffold-canvas.d.ts +9 -0
- package/dist-lib/components/blocks/scaffold/scaffold-context.d.ts +7 -0
- package/dist-lib/components/blocks/scaffold/scaffold-inspector.d.ts +9 -0
- package/dist-lib/components/blocks/scaffold/scaffold-main.d.ts +8 -0
- package/dist-lib/components/blocks/scaffold/scaffold-panel.d.ts +16 -0
- package/dist-lib/components/blocks/scaffold/scaffold-root.d.ts +17 -0
- package/dist-lib/components/blocks/scaffold/scaffold-sidebar.d.ts +7 -0
- package/dist-lib/components/blocks/scaffold/scaffold-sub-panel.d.ts +6 -0
- package/dist-lib/components/blocks/scaffold/scaffold-tab.d.ts +14 -0
- package/dist-lib/components/blocks/scaffold/type.d.ts +216 -0
- package/dist-lib/components/blocks/scaffold/use-scaffold-state.d.ts +26 -0
- package/dist-lib/components/blocks/scaffold/use-scaffold.d.ts +14 -0
- package/dist-lib/components/blocks/scaffold/utils.d.ts +38 -0
- package/dist-lib/components/blocks/shell/animated-panel-inset.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel-provider.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel-trigger.d.ts +2 -0
- package/dist-lib/components/blocks/shell/animated-panel.d.ts +2 -0
- package/dist-lib/components/blocks/shell/shell-fit-atom.d.ts +68 -0
- package/dist-lib/components/blocks/shell/shell-layout.d.ts +3 -0
- package/dist-lib/components/blocks/shell/shell-metrics.d.ts +72 -0
- package/dist-lib/components/blocks/shell/shell-panel-atom.d.ts +40 -0
- package/dist-lib/components/blocks/shell/type.d.ts +301 -0
- package/dist-lib/components/blocks/shell/use-shell-fit-state.d.ts +25 -0
- package/dist-lib/components/blocks/shell/use-shell-fit.d.ts +13 -0
- package/dist-lib/components/blocks/shell/use-shell-panels.d.ts +20 -0
- package/dist-lib/components/blocks/shell/utils.d.ts +72 -0
- package/dist-lib/components/blocks/sidebar/ai-sidebar-atom.d.ts +217 -0
- package/dist-lib/components/blocks/sidebar/ai-sidebar.d.ts +11 -0
- package/dist-lib/components/blocks/sidebar/index.d.ts +15 -0
- package/dist-lib/components/blocks/sidebar/sidebar-context.d.ts +3 -0
- package/dist-lib/components/blocks/sidebar/sidebar-icon.d.ts +2 -0
- package/dist-lib/components/blocks/sidebar/sidebar-row.d.ts +4 -0
- package/dist-lib/components/blocks/sidebar/type.d.ts +245 -0
- package/dist-lib/components/blocks/sidebar/use-ai-sidebar-state.d.ts +29 -0
- package/dist-lib/components/blocks/sidebar/use-ai-sidebar.d.ts +27 -0
- package/dist-lib/components/blocks/sidebar/utils.d.ts +21 -0
- package/dist-lib/components/blocks/types.d.ts +6 -0
- package/dist-lib/components/composed/action-bar/action.d.ts +18 -0
- package/dist-lib/components/composed/action-bar/emojis.d.ts +10 -0
- package/dist-lib/components/composed/action-bar/index.d.ts +12 -0
- package/dist-lib/components/composed/action-bar/toolbar.d.ts +2 -0
- package/dist-lib/components/composed/action-bar/types.d.ts +55 -0
- package/dist-lib/components/composed/action-bar/utils.d.ts +56 -0
- package/dist-lib/components/composed/ai/index.d.ts +17 -0
- package/dist-lib/components/composed/ai/message.d.ts +7 -0
- package/dist-lib/components/composed/ai/research-answer.d.ts +30 -0
- package/dist-lib/components/composed/ai/streaming-text.d.ts +26 -0
- package/dist-lib/components/composed/ai/suggested-prompt.d.ts +35 -0
- package/dist-lib/components/composed/ai/types.d.ts +68 -0
- package/dist-lib/components/composed/chat/attachment-file.d.ts +17 -0
- package/dist-lib/components/composed/chat/attachment.d.ts +60 -0
- package/dist-lib/components/composed/chat/chart/chart-atom.d.ts +30 -0
- package/dist-lib/components/composed/chat/chart/chart-context.d.ts +2 -0
- package/dist-lib/components/composed/chat/chart/chart-legend.d.ts +4 -0
- package/dist-lib/components/composed/chat/chart/chart-plot.d.ts +3 -0
- package/dist-lib/components/composed/chat/chart/chart-root.d.ts +3 -0
- package/dist-lib/components/composed/chat/chart/chart-tooltip.d.ts +4 -0
- package/dist-lib/components/composed/chat/chart/constants.d.ts +3 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-area-chart.d.ts +93 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-bar-chart.d.ts +86 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-brush.d.ts +65 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-chart.d.ts +44 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-dot.d.ts +16 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-legend.d.ts +24 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-line-chart.d.ts +90 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-pie-chart.d.ts +73 -0
- package/dist-lib/components/composed/chat/chart/evilcharts/echarts-tooltip.d.ts +29 -0
- package/dist-lib/components/composed/chat/chart/index.d.ts +6 -0
- package/dist-lib/components/composed/chat/chart/type.d.ts +80 -0
- package/dist-lib/components/composed/chat/chart/use-chart-state.d.ts +3 -0
- package/dist-lib/components/composed/chat/chart/use-chart.d.ts +6 -0
- package/dist-lib/components/composed/chat/chart/utils.d.ts +5 -0
- package/dist-lib/components/composed/chat/chart.d.ts +1 -0
- package/dist-lib/components/composed/chat/code-block.d.ts +26 -0
- package/dist-lib/components/composed/chat/composer/add-button.d.ts +5 -0
- package/dist-lib/components/composed/chat/composer/base.d.ts +5 -0
- package/dist-lib/components/composed/chat/composer/command-menu.d.ts +3 -0
- package/dist-lib/components/composed/chat/composer/hooks.d.ts +9 -0
- package/dist-lib/components/composed/chat/composer/send-button.d.ts +3 -0
- package/dist-lib/components/composed/chat/composer/suggestions.d.ts +7 -0
- package/dist-lib/components/composed/chat/composer/use-dictation.d.ts +8 -0
- package/dist-lib/components/composed/chat/composer/utils.d.ts +19 -0
- package/dist-lib/components/composed/chat/context-cards.d.ts +27 -0
- package/dist-lib/components/composed/chat/filter-table.d.ts +40 -0
- package/dist-lib/components/composed/chat/flowchart/chart-node.d.ts +2 -0
- package/dist-lib/components/composed/chat/flowchart/connector.d.ts +2 -0
- package/dist-lib/components/composed/chat/flowchart/default.d.ts +7 -0
- package/dist-lib/components/composed/chat/flowchart/edge-toolbar.d.ts +9 -0
- package/dist-lib/components/composed/chat/flowchart/hooks.d.ts +290 -0
- package/dist-lib/components/composed/chat/flowchart/step-body.d.ts +6 -0
- package/dist-lib/components/composed/chat/flowchart/types.d.ts +91 -0
- package/dist-lib/components/composed/chat/flowchart/utils.d.ts +64 -0
- package/dist-lib/components/composed/chat/index.d.ts +15 -0
- package/dist-lib/components/composed/chat/insight-cards.d.ts +54 -0
- package/dist-lib/components/composed/chat/markdown-atom.d.ts +7 -0
- package/dist-lib/components/composed/chat/markdown.d.ts +22 -0
- package/dist-lib/components/composed/chat/recommendation/checkbox.d.ts +9 -0
- package/dist-lib/components/composed/chat/recommendation/constant.d.ts +52 -0
- package/dist-lib/components/composed/chat/recommendation/default.d.ts +7 -0
- package/dist-lib/components/composed/chat/recommendation/group.d.ts +7 -0
- package/dist-lib/components/composed/chat/recommendation/index.d.ts +13 -0
- package/dist-lib/components/composed/chat/recommendation/item.d.ts +12 -0
- package/dist-lib/components/composed/chat/recommendation/types.d.ts +62 -0
- package/dist-lib/components/composed/chat/records-table.d.ts +40 -0
- package/dist-lib/components/composed/chat/type.d.ts +117 -0
- package/dist-lib/components/composed/logo.d.ts +34 -0
- package/dist-lib/components/composed/password-input.d.ts +30 -0
- package/dist-lib/components/composed/reader/applied-mark.d.ts +3 -0
- package/dist-lib/components/composed/reader/apply-toast.d.ts +3 -0
- package/dist-lib/components/composed/reader/selection-highlight.d.ts +3 -0
- package/dist-lib/components/composed/reader/selection-popover.d.ts +9 -0
- package/dist-lib/components/composed/reader/type.d.ts +40 -0
- package/dist-lib/components/composed/social-providers.d.ts +39 -0
- package/dist-lib/components/composed/tree/index.d.ts +6 -0
- package/dist-lib/components/composed/tree/tree-atom.d.ts +160 -0
- package/dist-lib/components/composed/tree/tree-context.d.ts +4 -0
- package/dist-lib/components/composed/tree/tree-node.d.ts +12 -0
- package/dist-lib/components/composed/tree/tree.d.ts +19 -0
- package/dist-lib/components/composed/tree/type.d.ts +290 -0
- package/dist-lib/components/composed/tree/use-tree-dnd.d.ts +18 -0
- package/dist-lib/components/composed/tree/use-tree-state.d.ts +25 -0
- package/dist-lib/components/composed/tree/use-tree.d.ts +24 -0
- package/dist-lib/components/composed/tree/utils.d.ts +28 -0
- package/dist-lib/components/composed/types.d.ts +7 -0
- package/dist-lib/components/composed/user/index.d.ts +11 -0
- package/dist-lib/components/composed/user/info.d.ts +12 -0
- package/dist-lib/components/composed/user/message.d.ts +2 -0
- package/dist-lib/components/composed/user/pill.d.ts +10 -0
- package/dist-lib/components/composed/user/types.d.ts +27 -0
- package/dist-lib/components/composed/verse/index.d.ts +2 -0
- package/dist-lib/components/composed/verse/types.d.ts +18 -0
- package/dist-lib/components/composed/verse/verse.d.ts +5 -0
- package/dist-lib/components/docs/atoms-gallery.d.ts +12 -0
- package/dist-lib/components/docs/block-demo-stage.d.ts +7 -0
- package/dist-lib/components/docs/blocks-gallery.d.ts +2 -0
- package/dist-lib/components/docs/browser.d.ts +10 -0
- package/dist-lib/components/docs/components-gallery.d.ts +2 -0
- package/dist-lib/components/docs/demo-controls.d.ts +21 -0
- package/dist-lib/components/icons/__tests__/icons.test.d.ts +1 -0
- package/dist-lib/components/icons/file-badge-cfm.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-corpus.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-pdf.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-tei.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-tf.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-txt.d.ts +7 -0
- package/dist-lib/components/icons/file-badge-xml.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-cfm.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-corpus.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-pdf.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-tei.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-tf.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-txt.d.ts +7 -0
- package/dist-lib/components/icons/file-wordmark-xml.d.ts +7 -0
- package/dist-lib/components/icons/index.d.ts +15 -0
- package/dist-lib/components/icons/types.d.ts +7 -0
- package/dist-lib/components/motion/index.d.ts +1 -0
- package/dist-lib/components/motion/overflow-actions.d.ts +35 -0
- package/dist-lib/components/motion/popover-morph.d.ts +35 -0
- package/dist-lib/components/motion/popover-position.d.ts +15 -0
- package/dist-lib/components/motion/shared-layout-bg.d.ts +13 -0
- package/dist-lib/components/stories/attachment.story.d.ts +11 -0
- package/dist-lib/components/stories/auth-flow-block.story.d.ts +2 -0
- package/dist-lib/components/stories/avatar.story.d.ts +6 -0
- package/dist-lib/components/stories/base.story.d.ts +6 -0
- package/dist-lib/components/stories/bubble.story.d.ts +6 -0
- package/dist-lib/components/stories/button.story.d.ts +9 -0
- package/dist-lib/components/stories/card.story.d.ts +8 -0
- package/dist-lib/components/stories/chart.story.d.ts +71 -0
- package/dist-lib/components/stories/checkbox.story.d.ts +6 -0
- package/dist-lib/components/stories/code-auth-block.story.d.ts +2 -0
- package/dist-lib/components/stories/code-block.story.d.ts +12 -0
- package/dist-lib/components/stories/composed-password-input.story.d.ts +6 -0
- package/dist-lib/components/stories/context-cards.story.d.ts +3 -0
- package/dist-lib/components/stories/emoji-action-bar.story.d.ts +3 -0
- package/dist-lib/components/stories/field.story.d.ts +6 -0
- package/dist-lib/components/stories/file-icons.story.d.ts +5 -0
- package/dist-lib/components/stories/filter-table.story.d.ts +21 -0
- package/dist-lib/components/stories/flowchart.story.d.ts +3 -0
- package/dist-lib/components/stories/forgot-password-block.story.d.ts +2 -0
- package/dist-lib/components/stories/frame.story.d.ts +8 -0
- package/dist-lib/components/stories/index.d.ts +54 -0
- package/dist-lib/components/stories/input-group.story.d.ts +6 -0
- package/dist-lib/components/stories/input.story.d.ts +8 -0
- package/dist-lib/components/stories/insight-cards.story.d.ts +74 -0
- package/dist-lib/components/stories/label.story.d.ts +8 -0
- package/dist-lib/components/stories/linked-accounts-block.story.d.ts +2 -0
- package/dist-lib/components/stories/login-block.story.d.ts +2 -0
- package/dist-lib/components/stories/logo.story.d.ts +3 -0
- package/dist-lib/components/stories/markdown.story.d.ts +7 -0
- package/dist-lib/components/stories/menu-command.story.d.ts +13 -0
- package/dist-lib/components/stories/onboarding-block.story.d.ts +2 -0
- package/dist-lib/components/stories/otp-field.story.d.ts +8 -0
- package/dist-lib/components/stories/passkey-manager-block.story.d.ts +2 -0
- package/dist-lib/components/stories/passkey-sign-in-block.story.d.ts +2 -0
- package/dist-lib/components/stories/password-input.story.d.ts +9 -0
- package/dist-lib/components/stories/profile-card-block.story.d.ts +6 -0
- package/dist-lib/components/stories/recommendation-card.story.d.ts +3 -0
- package/dist-lib/components/stories/records-table.story.d.ts +23 -0
- package/dist-lib/components/stories/reference.story.d.ts +9 -0
- package/dist-lib/components/stories/research-answer.story.d.ts +10 -0
- package/dist-lib/components/stories/scaffold-root.story.d.ts +6 -0
- package/dist-lib/components/stories/separator.story.d.ts +6 -0
- package/dist-lib/components/stories/shell-layout.story.d.ts +2 -0
- package/dist-lib/components/stories/signup-block.story.d.ts +2 -0
- package/dist-lib/components/stories/social-providers.story.d.ts +3 -0
- package/dist-lib/components/stories/streaming-text.story.d.ts +3 -0
- package/dist-lib/components/stories/text.story.d.ts +8 -0
- package/dist-lib/components/stories/textarea.story.d.ts +6 -0
- package/dist-lib/components/stories/tree.story.d.ts +4 -0
- package/dist-lib/components/stories/update-password-block.story.d.ts +2 -0
- package/dist-lib/components/stories/user.story.d.ts +8 -0
- package/dist-lib/components/stories/verse.story.d.ts +6 -0
- package/dist-lib/components/theme-provider.d.ts +15 -0
- package/dist-lib/components/types.d.ts +11 -0
- package/dist-lib/components/ui/badge.d.ts +14 -0
- package/dist-lib/components/ui/button.d.ts +28 -0
- package/dist-lib/components/ui/chat/__tests__/segmented-toggle.test.d.ts +1 -0
- package/dist-lib/components/ui/chat/avatar-handle.d.ts +13 -0
- package/dist-lib/components/ui/chat/badges.d.ts +29 -0
- package/dist-lib/components/ui/chat/buttons.d.ts +29 -0
- package/dist-lib/components/ui/chat/chips.d.ts +48 -0
- package/dist-lib/components/ui/chat/dot.d.ts +30 -0
- package/dist-lib/components/ui/chat/icon-button.d.ts +18 -0
- package/dist-lib/components/ui/chat/icon-tile.d.ts +20 -0
- package/dist-lib/components/ui/chat/index.d.ts +12 -0
- package/dist-lib/components/ui/chat/inline-source.d.ts +20 -0
- package/dist-lib/components/ui/chat/segmented-toggle.d.ts +20 -0
- package/dist-lib/components/ui/chat/tag.d.ts +21 -0
- package/dist-lib/components/ui/chat/thumbnail.d.ts +13 -0
- package/dist-lib/components/ui/chat/waveform.d.ts +13 -0
- package/dist-lib/components/ui/glasscn/glass-button-group.d.ts +7 -0
- package/dist-lib/components/ui/glasscn/glass-button.d.ts +6 -0
- package/dist-lib/components/ui/glasscn/glass-container.d.ts +8 -0
- package/dist-lib/components/ui/glasscn/glass-separator.d.ts +5 -0
- package/dist-lib/components/ui/glasscn/liquid-glass.d.ts +29 -0
- package/dist-lib/components/ui/input-group.d.ts +13 -0
- package/dist-lib/components/ui/input.d.ts +9 -0
- package/dist-lib/components/ui/menu-command.d.ts +36 -0
- package/dist-lib/components/ui/popover-glass.d.ts +32 -0
- package/dist-lib/components/ui/select.d.ts +30 -0
- package/dist-lib/components/ui/tabs.d.ts +15 -0
- package/dist-lib/components/ui/textarea.d.ts +8 -0
- package/dist-lib/components/ui/toast.d.ts +15 -0
- package/dist-lib/index.d.ts +65 -0
- package/dist-lib/index.js +11893 -6443
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/attachment-content.d.ts +4 -0
- package/dist-lib/lib/auth-accent.d.ts +33 -0
- package/dist-lib/lib/glass-variants.d.ts +9 -0
- package/dist-lib/lib/keyed-atom.d.ts +14 -0
- package/dist-lib/lib/segmented-control.d.ts +8 -0
- package/dist-lib/lib/specular.d.ts +12 -0
- package/dist-lib/lib/state/__tests__/sound.test.d.ts +1 -0
- package/dist-lib/lib/state/exegia-provider.d.ts +51 -0
- package/dist-lib/lib/state/index.d.ts +4 -0
- package/dist-lib/lib/state/store.d.ts +17 -0
- package/dist-lib/lib/use-motion-panel.d.ts +9 -0
- package/dist-lib/lib/utils.test.d.ts +1 -0
- package/package.json +6 -1
- package/src/components/atoms/avatar/base.tsx +3 -3
- package/src/components/atoms/avatar/index.ts +8 -1
- package/src/components/atoms/avatar/types.ts +9 -9
- package/src/components/atoms/avatar/utils.ts +6 -6
- package/src/components/atoms/background/texture.tsx +2 -2
- package/src/components/atoms/background/type.ts +3 -3
- package/src/components/atoms/background/utils.ts +2 -2
- package/src/components/atoms/bubble/__tests__/bubble.test.tsx +59 -4
- package/src/components/atoms/bubble/actions.tsx +2 -2
- package/src/components/atoms/bubble/context.ts +3 -3
- package/src/components/atoms/bubble/default.tsx +2 -2
- package/src/components/atoms/bubble/header.tsx +6 -4
- package/src/components/atoms/bubble/index.ts +9 -9
- package/src/components/atoms/bubble/message.tsx +12 -5
- package/src/components/atoms/bubble/reactions.tsx +11 -14
- package/src/components/atoms/bubble/types.ts +21 -18
- package/src/components/atoms/bubble/utils.ts +10 -10
- package/src/components/atoms/field.tsx +43 -0
- package/src/components/atoms/index.ts +0 -26
- package/src/components/atoms/loader.tsx +2 -11
- package/src/components/atoms/reference.tsx +2 -2
- package/src/components/atoms/text/default.tsx +5 -5
- package/src/components/atoms/text/heading.tsx +2 -5
- package/src/components/atoms/text/index.ts +8 -4
- package/src/components/atoms/text/label.tsx +2 -2
- package/src/components/atoms/text/paragraph.tsx +2 -5
- package/src/components/atoms/text/span.tsx +2 -3
- package/src/components/atoms/text/types.ts +18 -8
- package/src/components/atoms/text/utils.ts +2 -2
- package/src/components/atoms/text-selection/click-popover.tsx +3 -3
- package/src/components/atoms/text-selection/index.ts +12 -12
- package/src/components/atoms/text-selection/popover.tsx +7 -4
- package/src/components/atoms/text-selection/selection-atom.ts +7 -7
- package/src/components/atoms/text-selection/selection.tsx +2 -2
- package/src/components/atoms/text-selection/types.ts +24 -24
- package/src/components/atoms/text-selection/use-selection.ts +3 -3
- package/src/components/atoms/types.ts +30 -2
- package/src/components/blocks/auth/__tests__/auth-flow-block.test.tsx +4 -4
- package/src/components/blocks/auth/__tests__/auth-state-atom.test.tsx +2 -2
- package/src/components/blocks/auth/__tests__/linked-accounts-block.test.tsx +2 -2
- package/src/components/blocks/auth/__tests__/onboarding-block.test.tsx +39 -12
- package/src/components/blocks/auth/__tests__/passkey-manager-block.test.tsx +4 -4
- package/src/components/blocks/auth/auth-flow-atom.ts +27 -27
- package/src/components/blocks/auth/auth-flow-block.tsx +8 -93
- package/src/components/blocks/auth/auth-session-atom.ts +9 -9
- package/src/components/blocks/auth/auth-shell.tsx +2 -3
- package/src/components/blocks/auth/auth-state.ts +0 -12
- package/src/components/blocks/auth/code-auth-block.tsx +16 -30
- package/src/components/blocks/auth/forgot-password-block.tsx +4 -15
- package/src/components/blocks/auth/linked-accounts-block.tsx +10 -36
- package/src/components/blocks/auth/login-block.tsx +7 -29
- package/src/components/blocks/auth/onboarding/profile-step.tsx +14 -25
- package/src/components/blocks/auth/onboarding-block.tsx +39 -96
- package/src/components/blocks/auth/passkey-manager-block.tsx +8 -33
- package/src/components/blocks/auth/passkey-sign-in-block.tsx +6 -20
- package/src/components/blocks/auth/signup-block.tsx +7 -44
- package/src/components/blocks/auth/type.ts +436 -0
- package/src/components/blocks/auth/update-password-block.tsx +4 -22
- package/src/components/blocks/auth/use-auth-state.ts +12 -12
- package/src/components/blocks/chat/__tests__/ai-panel.test.tsx +2 -2
- package/src/components/blocks/chat/banners.tsx +5 -13
- package/src/components/blocks/chat/base.tsx +2 -2
- package/src/components/blocks/chat/header.tsx +2 -2
- package/src/components/blocks/chat/index.ts +17 -19
- package/src/components/blocks/chat/recommendation-stack.tsx +4 -6
- package/src/components/blocks/chat/suggested-prompts.tsx +2 -2
- package/src/components/blocks/chat/type.ts +96 -0
- package/src/components/blocks/chat/version-history-record.tsx +2 -2
- package/src/components/blocks/profile/__tests__/profile-card-block.test.tsx +6 -6
- package/src/components/blocks/profile/index.ts +2 -2
- package/src/components/blocks/profile/profile-card-atom.ts +29 -29
- package/src/components/blocks/profile/profile-card-block.tsx +30 -30
- package/src/components/blocks/profile/type.ts +17 -17
- package/src/components/blocks/profile/use-profile-card-state.ts +7 -7
- package/src/components/blocks/profile/use-profile-card.ts +18 -18
- package/src/components/blocks/scaffold/__tests__/scaffold-atom.test.tsx +28 -2
- package/src/components/blocks/scaffold/__tests__/scaffold.test.tsx +90 -11
- package/src/components/blocks/scaffold/index.ts +4 -0
- package/src/components/blocks/scaffold/panel-close-button.tsx +2 -2
- package/src/components/blocks/scaffold/panel-menu-button.tsx +2 -2
- package/src/components/blocks/scaffold/scaffold-actions.tsx +2 -2
- package/src/components/blocks/scaffold/scaffold-atom.ts +76 -32
- package/src/components/blocks/scaffold/scaffold-canvas.tsx +89 -10
- package/src/components/blocks/scaffold/scaffold-context.ts +3 -3
- package/src/components/blocks/scaffold/scaffold-inspector.tsx +56 -7
- package/src/components/blocks/scaffold/scaffold-main.tsx +2 -2
- package/src/components/blocks/scaffold/scaffold-panel.tsx +119 -71
- package/src/components/blocks/scaffold/scaffold-root.tsx +14 -12
- package/src/components/blocks/scaffold/scaffold-sidebar.tsx +2 -2
- package/src/components/blocks/scaffold/scaffold-sub-panel.tsx +82 -13
- package/src/components/blocks/scaffold/scaffold-tab.tsx +2 -2
- package/src/components/blocks/scaffold/type.ts +46 -28
- package/src/components/blocks/scaffold/use-scaffold-state.ts +35 -8
- package/src/components/blocks/scaffold/use-scaffold.ts +3 -3
- package/src/components/blocks/scaffold/utils.ts +9 -6
- package/src/components/blocks/shell/__tests__/shell-fit-atom.test.tsx +7 -5
- package/src/components/blocks/shell/__tests__/shell-layout.test.tsx +154 -9
- package/src/components/blocks/shell/__tests__/shell-metrics.test.ts +56 -10
- package/src/components/blocks/shell/animated-panel-inset.tsx +9 -9
- package/src/components/blocks/shell/animated-panel-provider.tsx +76 -30
- package/src/components/blocks/shell/animated-panel-trigger.tsx +10 -5
- package/src/components/blocks/shell/animated-panel.tsx +76 -128
- package/src/components/blocks/shell/shell-fit-atom.ts +29 -26
- package/src/components/blocks/shell/shell-layout.tsx +29 -13
- package/src/components/blocks/shell/shell-metrics.ts +69 -15
- package/src/components/blocks/shell/shell-panel-atom.ts +74 -0
- package/src/components/blocks/shell/type.ts +78 -69
- package/src/components/blocks/shell/use-shell-fit-state.ts +4 -4
- package/src/components/blocks/shell/use-shell-fit.ts +52 -34
- package/src/components/blocks/shell/use-shell-panels.ts +32 -66
- package/src/components/blocks/shell/utils.ts +8 -4
- package/src/components/blocks/sidebar/__tests__/ai-sidebar-atom.test.tsx +325 -0
- package/src/components/blocks/sidebar/__tests__/ai-sidebar.test.tsx +93 -0
- package/src/components/blocks/sidebar/__tests__/use-ai-sidebar.test.tsx +310 -0
- package/src/components/blocks/sidebar/ai-sidebar-atom.ts +698 -0
- package/src/components/blocks/sidebar/ai-sidebar.tsx +150 -0
- package/src/components/blocks/sidebar/index.ts +67 -0
- package/src/components/blocks/sidebar/sidebar-context.ts +15 -0
- package/src/components/blocks/sidebar/sidebar-icon.tsx +14 -0
- package/src/components/blocks/sidebar/sidebar-row.tsx +378 -0
- package/src/components/blocks/sidebar/type.ts +294 -0
- package/src/components/blocks/sidebar/use-ai-sidebar-state.ts +123 -0
- package/src/components/blocks/sidebar/use-ai-sidebar.ts +619 -0
- package/src/components/blocks/sidebar/utils.ts +170 -0
- package/src/components/blocks/types.ts +6 -0
- package/src/components/composed/action-bar/__tests__/action-bar.test.tsx +43 -14
- package/src/components/composed/action-bar/action.tsx +9 -12
- package/src/components/composed/action-bar/emojis.tsx +4 -4
- package/src/components/composed/action-bar/index.ts +8 -8
- package/src/components/composed/action-bar/toolbar.tsx +36 -30
- package/src/components/composed/action-bar/types.ts +14 -14
- package/src/components/composed/action-bar/utils.ts +17 -18
- package/src/components/composed/ai/__tests__/ai.test.tsx +3 -4
- package/src/components/composed/ai/avatar.tsx +4 -5
- package/src/components/composed/ai/index.ts +7 -7
- package/src/components/composed/ai/message.tsx +7 -7
- package/src/components/composed/ai/research-answer.tsx +7 -7
- package/src/components/composed/ai/streaming-text.tsx +5 -5
- package/src/components/composed/ai/suggested-prompt.tsx +4 -4
- package/src/components/composed/ai/types.ts +30 -30
- package/src/components/composed/chat/__tests__/attachment.test.tsx +182 -9
- package/src/components/composed/chat/__tests__/chart.test.tsx +295 -8
- package/src/components/composed/chat/__tests__/composer-attachments.test.tsx +51 -3
- package/src/components/composed/chat/__tests__/composer.test.tsx +173 -6
- package/src/components/composed/chat/__tests__/flowchart.test.tsx +15 -2
- package/src/components/composed/chat/__tests__/glass-material.test.tsx +54 -0
- package/src/components/composed/chat/__tests__/menu-command.test.tsx +35 -0
- package/src/components/composed/chat/__tests__/recommendation.test.tsx +43 -0
- package/src/components/composed/chat/attachment-file.ts +82 -0
- package/src/components/composed/chat/attachment.tsx +387 -117
- package/src/components/composed/chat/chart/chart-atom.ts +55 -0
- package/src/components/composed/chat/chart/chart-context.ts +9 -0
- package/src/components/composed/chat/chart/chart-legend.tsx +75 -0
- package/src/components/composed/chat/chart/chart-plot.tsx +233 -0
- package/src/components/composed/chat/chart/chart-root.tsx +229 -0
- package/src/components/composed/chat/chart/chart-tooltip.tsx +74 -0
- package/src/components/composed/chat/chart/constants.ts +8 -0
- package/src/components/composed/chat/chart/evilcharts/LICENSE +21 -0
- package/src/components/composed/chat/chart/evilcharts/README.md +8 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-area-chart.tsx +2358 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-bar-chart.tsx +2261 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-brush.tsx +233 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-chart.tsx +214 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-dot.tsx +111 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-legend.tsx +131 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-line-chart.tsx +2112 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-pie-chart.tsx +1235 -0
- package/src/components/composed/chat/chart/evilcharts/echarts-tooltip.tsx +125 -0
- package/src/components/composed/chat/chart/index.ts +13 -0
- package/src/components/composed/chat/chart/type.ts +88 -0
- package/src/components/composed/chat/chart/use-chart-state.ts +22 -0
- package/src/components/composed/chat/chart/use-chart.ts +70 -0
- package/src/components/composed/chat/chart/utils.ts +47 -0
- package/src/components/composed/chat/chart.tsx +2 -192
- package/src/components/composed/chat/code-block.tsx +5 -5
- package/src/components/composed/chat/composer/BEAUTIFUL-UI-LICENSE.txt +23 -0
- package/src/components/composed/chat/composer/add-button.tsx +24 -17
- package/src/components/composed/chat/composer/base.tsx +503 -178
- package/src/components/composed/chat/composer/command-menu.tsx +31 -26
- package/src/components/composed/chat/composer/hooks.ts +3 -3
- package/src/components/composed/chat/composer/send-button.tsx +20 -28
- package/src/components/composed/chat/composer/suggestions.tsx +2 -2
- package/src/components/composed/chat/composer/use-dictation.ts +123 -0
- package/src/components/composed/chat/composer/utils.ts +4 -4
- package/src/components/composed/chat/context-cards.tsx +5 -5
- package/src/components/composed/chat/filter-table.tsx +8 -8
- package/src/components/composed/chat/flowchart/chart-node.tsx +2 -2
- package/src/components/composed/chat/flowchart/connector.tsx +2 -2
- package/src/components/composed/chat/flowchart/default.tsx +3 -3
- package/src/components/composed/chat/flowchart/edge-toolbar.tsx +5 -5
- package/src/components/composed/chat/flowchart/hooks.tsx +28 -26
- package/src/components/composed/chat/flowchart/step-body.tsx +2 -2
- package/src/components/composed/chat/flowchart/types.ts +14 -14
- package/src/components/composed/chat/flowchart/utils.ts +15 -15
- package/src/components/composed/chat/index.ts +35 -39
- package/src/components/composed/chat/insight-cards.tsx +17 -17
- package/src/components/composed/chat/markdown-atom.ts +2 -2
- package/src/components/composed/chat/markdown.tsx +10 -10
- package/src/components/composed/chat/recommendation/checkbox.tsx +3 -14
- package/src/components/composed/chat/recommendation/constant.ts +35 -6
- package/src/components/composed/chat/recommendation/default.tsx +2 -2
- package/src/components/composed/chat/recommendation/group.tsx +4 -11
- package/src/components/composed/chat/recommendation/index.ts +0 -2
- package/src/components/composed/chat/recommendation/item.tsx +35 -64
- package/src/components/composed/chat/recommendation/types.ts +26 -15
- package/src/components/composed/chat/records-table.tsx +20 -18
- package/src/components/composed/chat/type.ts +56 -25
- package/src/components/composed/logo.tsx +4 -4
- package/src/components/composed/password-input.tsx +2 -2
- package/src/components/composed/reader/applied-mark.tsx +2 -2
- package/src/components/composed/reader/apply-toast.tsx +2 -2
- package/src/components/composed/reader/selection-highlight.tsx +2 -2
- package/src/components/composed/reader/selection-popover.tsx +2 -2
- package/src/components/composed/reader/type.ts +8 -8
- package/src/components/composed/social-providers.tsx +26 -18
- package/src/components/composed/tree/CLAUDE.md +3 -3
- package/src/components/composed/tree/__tests__/tree-atom.test.tsx +8 -8
- package/src/components/composed/tree/__tests__/tree.test.tsx +10 -10
- package/src/components/composed/tree/__tests__/use-tree.test.tsx +10 -10
- package/src/components/composed/tree/index.ts +12 -12
- package/src/components/composed/tree/tree-atom.ts +46 -46
- package/src/components/composed/tree/tree-context.ts +3 -3
- package/src/components/composed/tree/tree-node.tsx +7 -7
- package/src/components/composed/tree/tree.tsx +13 -13
- package/src/components/composed/tree/type.ts +56 -56
- package/src/components/composed/tree/use-tree-dnd.ts +10 -10
- package/src/components/composed/tree/use-tree-state.ts +3 -3
- package/src/components/composed/tree/use-tree.ts +10 -10
- package/src/components/composed/tree/utils.ts +21 -21
- package/src/components/composed/types.ts +9 -0
- package/src/components/composed/user/__tests__/message.test.tsx +79 -0
- package/src/components/composed/user/index.ts +2 -2
- package/src/components/composed/user/info.tsx +15 -7
- package/src/components/composed/user/message.tsx +33 -7
- package/src/components/composed/user/pill.tsx +3 -3
- package/src/components/composed/user/types.ts +34 -14
- package/src/components/composed/verse/index.ts +1 -1
- package/src/components/composed/verse/types.ts +8 -8
- package/src/components/composed/verse/verse.tsx +6 -6
- package/src/components/docs/atoms-gallery.tsx +45 -45
- package/src/components/docs/block-demo-stage.tsx +38 -0
- package/src/components/docs/blocks-gallery.tsx +224 -0
- package/src/components/docs/browser.tsx +87 -0
- package/src/components/docs/components-gallery.tsx +182 -0
- package/src/components/docs/demo-controls.tsx +6 -7
- package/src/components/docs/hero.tsx +2 -3
- package/src/components/icons/file-badge-cfm.tsx +2 -2
- package/src/components/icons/file-badge-corpus.tsx +2 -2
- package/src/components/icons/file-badge-pdf.tsx +2 -2
- package/src/components/icons/file-badge-tei.tsx +2 -2
- package/src/components/icons/file-badge-tf.tsx +2 -2
- package/src/components/icons/file-badge-txt.tsx +2 -2
- package/src/components/icons/file-badge-xml.tsx +2 -2
- package/src/components/icons/file-wordmark-cfm.tsx +2 -2
- package/src/components/icons/file-wordmark-corpus.tsx +2 -2
- package/src/components/icons/file-wordmark-pdf.tsx +2 -2
- package/src/components/icons/file-wordmark-tei.tsx +2 -2
- package/src/components/icons/file-wordmark-tf.tsx +2 -2
- package/src/components/icons/file-wordmark-txt.tsx +2 -2
- package/src/components/icons/file-wordmark-xml.tsx +2 -2
- package/src/components/icons/index.ts +1 -1
- package/src/components/icons/types.ts +1 -1
- package/src/components/motion/overflow-actions.tsx +19 -19
- package/src/components/motion/popover-morph.tsx +15 -15
- package/src/components/motion/popover-position.ts +4 -4
- package/src/components/motion/shared-layout-bg.tsx +2 -2
- package/src/components/stories/attachment.story.tsx +40 -0
- package/src/components/stories/auth-flow-block.story.tsx +17 -0
- package/src/components/stories/avatar.story.tsx +33 -0
- package/src/components/stories/base.story.tsx +70 -0
- package/src/components/stories/bubble.story.tsx +61 -0
- package/src/components/stories/button.story.tsx +32 -0
- package/src/components/stories/card.story.tsx +36 -0
- package/src/components/stories/chart.story.tsx +69 -0
- package/src/components/stories/checkbox.story.tsx +30 -0
- package/src/components/stories/code-auth-block.story.tsx +20 -0
- package/src/components/stories/code-block.story.tsx +35 -0
- package/src/components/stories/composed-password-input.story.tsx +41 -0
- package/src/components/stories/context-cards.story.tsx +30 -0
- package/src/components/stories/emoji-action-bar.story.tsx +20 -0
- package/src/components/stories/field.story.tsx +32 -0
- package/src/components/stories/file-icons.story.tsx +20 -0
- package/src/components/stories/filter-table.story.tsx +63 -0
- package/src/components/stories/flowchart.story.tsx +25 -0
- package/src/components/stories/forgot-password-block.story.tsx +14 -0
- package/src/components/stories/frame.story.tsx +32 -0
- package/src/components/stories/index.ts +57 -0
- package/src/components/stories/input-group.story.tsx +27 -0
- package/src/components/stories/input.story.tsx +42 -0
- package/src/components/stories/insight-cards.story.tsx +67 -0
- package/src/components/stories/label.story.tsx +32 -0
- package/src/components/stories/linked-accounts-block.story.tsx +23 -0
- package/src/components/stories/login-block.story.tsx +18 -0
- package/src/components/stories/logo.story.tsx +21 -0
- package/src/components/stories/markdown.story.tsx +31 -0
- package/src/components/stories/menu-command.story.tsx +43 -0
- package/src/components/stories/onboarding-block.story.tsx +14 -0
- package/src/components/stories/otp-field.story.tsx +35 -0
- package/src/components/stories/passkey-manager-block.story.tsx +18 -0
- package/src/components/stories/passkey-sign-in-block.story.tsx +15 -0
- package/src/components/stories/password-input.story.tsx +35 -0
- package/src/components/stories/profile-card-block.story.tsx +29 -0
- package/src/components/stories/recommendation-card.story.tsx +37 -0
- package/src/components/stories/records-table.story.tsx +52 -0
- package/src/components/stories/reference.story.tsx +33 -0
- package/src/components/stories/research-answer.story.tsx +32 -0
- package/src/components/stories/scaffold-root.story.tsx +47 -0
- package/src/components/stories/separator.story.tsx +37 -0
- package/src/components/stories/shell-layout.story.tsx +19 -0
- package/src/components/stories/signup-block.story.tsx +18 -0
- package/src/components/stories/social-providers.story.tsx +26 -0
- package/src/components/stories/streaming-text.story.tsx +25 -0
- package/src/components/stories/text.story.tsx +31 -0
- package/src/components/stories/textarea.story.tsx +34 -0
- package/src/components/stories/tree.story.tsx +31 -0
- package/src/components/stories/update-password-block.story.tsx +14 -0
- package/src/components/stories/user.story.tsx +32 -0
- package/src/components/stories/verse.story.tsx +42 -0
- package/src/components/theme-provider.tsx +16 -16
- package/src/components/types.ts +106 -0
- package/src/components/ui/badge.tsx +3 -3
- package/src/components/ui/button.tsx +15 -22
- package/src/components/ui/chat/avatar-handle.tsx +2 -2
- package/src/components/ui/chat/badges.tsx +2 -2
- package/src/components/ui/chat/buttons.tsx +7 -7
- package/src/components/ui/chat/chips.tsx +11 -11
- package/src/components/ui/chat/dot.tsx +8 -8
- package/src/components/ui/chat/icon-button.tsx +3 -3
- package/src/components/ui/chat/icon-tile.tsx +2 -2
- package/src/components/ui/chat/index.ts +12 -12
- package/src/components/ui/chat/inline-source.tsx +2 -2
- package/src/components/ui/chat/segmented-toggle.tsx +4 -4
- package/src/components/ui/chat/tag.tsx +10 -10
- package/src/components/ui/chat/thumbnail.tsx +2 -2
- package/src/components/ui/chat/waveform.tsx +2 -2
- package/src/components/ui/field.tsx +3 -3
- package/src/components/ui/glasscn/glass-button-group.tsx +7 -21
- package/src/components/ui/glasscn/glass-button.tsx +5 -5
- package/src/components/ui/glasscn/glass-container.tsx +10 -47
- package/src/components/ui/glasscn/glass-separator.tsx +6 -6
- package/src/components/ui/glasscn/liquid-glass.tsx +43 -300
- package/src/components/ui/input-group.tsx +4 -4
- package/src/components/ui/input.tsx +2 -2
- package/src/components/ui/menu-command.tsx +103 -75
- package/src/components/ui/popover-glass.tsx +11 -14
- package/src/components/ui/select.tsx +2 -2
- package/src/components/ui/tabs.tsx +12 -12
- package/src/components/ui/textarea.tsx +2 -2
- package/src/components/ui/toast.tsx +13 -13
- package/src/index.css +3 -1
- package/src/index.ts +3 -1
- package/src/lib/attachment-content.ts +28 -0
- package/src/lib/auth-accent.ts +2 -2
- package/src/lib/glass-variants.ts +19 -54
- package/src/lib/keyed-atom.ts +3 -3
- package/src/lib/segmented-control.ts +2 -2
- package/src/lib/specular.ts +10 -0
- package/src/lib/state/__tests__/sound.test.tsx +53 -0
- package/src/lib/state/exegia-provider.tsx +6 -6
- package/src/lib/state/index.ts +2 -2
- package/src/lib/state/store.ts +2 -2
- package/src/lib/use-motion-panel.ts +78 -0
- package/src/lib/utils.ts +7 -5
- package/src/specular.js +4 -0
- package/dist-lib/src/components/atoms/avatar/base.d.ts +0 -2
- package/dist-lib/src/components/atoms/avatar/index.d.ts +0 -2
- package/dist-lib/src/components/atoms/avatar/types.d.ts +0 -20
- package/dist-lib/src/components/atoms/avatar/utils.d.ts +0 -13
- package/dist-lib/src/components/atoms/background/texture.d.ts +0 -2
- package/dist-lib/src/components/atoms/background/type.d.ts +0 -7
- package/dist-lib/src/components/atoms/background/utils.d.ts +0 -4
- package/dist-lib/src/components/atoms/bubble/actions.d.ts +0 -7
- package/dist-lib/src/components/atoms/bubble/context.d.ts +0 -4
- package/dist-lib/src/components/atoms/bubble/default.d.ts +0 -3
- package/dist-lib/src/components/atoms/bubble/header.d.ts +0 -8
- package/dist-lib/src/components/atoms/bubble/index.d.ts +0 -16
- package/dist-lib/src/components/atoms/bubble/message.d.ts +0 -3
- package/dist-lib/src/components/atoms/bubble/reactions.d.ts +0 -14
- package/dist-lib/src/components/atoms/bubble/types.d.ts +0 -59
- package/dist-lib/src/components/atoms/bubble/utils.d.ts +0 -13
- package/dist-lib/src/components/atoms/index.d.ts +0 -15
- package/dist-lib/src/components/atoms/loader.d.ts +0 -8
- package/dist-lib/src/components/atoms/reference.d.ts +0 -7
- package/dist-lib/src/components/atoms/text/default.d.ts +0 -1144
- package/dist-lib/src/components/atoms/text/heading.d.ts +0 -4
- package/dist-lib/src/components/atoms/text/index.d.ts +0 -21
- package/dist-lib/src/components/atoms/text/label.d.ts +0 -2
- package/dist-lib/src/components/atoms/text/paragraph.d.ts +0 -4
- package/dist-lib/src/components/atoms/text/span.d.ts +0 -3
- package/dist-lib/src/components/atoms/text/types.d.ts +0 -23
- package/dist-lib/src/components/atoms/text/utils.d.ts +0 -4
- package/dist-lib/src/components/atoms/text-selection/click-popover.d.ts +0 -3
- package/dist-lib/src/components/atoms/text-selection/index.d.ts +0 -6
- package/dist-lib/src/components/atoms/text-selection/popover.d.ts +0 -3
- package/dist-lib/src/components/atoms/text-selection/selection-atom.d.ts +0 -30
- package/dist-lib/src/components/atoms/text-selection/selection.d.ts +0 -3
- package/dist-lib/src/components/atoms/text-selection/types.d.ts +0 -90
- package/dist-lib/src/components/atoms/text-selection/use-selection.d.ts +0 -7
- package/dist-lib/src/components/atoms/types.d.ts +0 -17
- package/dist-lib/src/components/blocks/auth/auth-flow-atom.d.ts +0 -77
- package/dist-lib/src/components/blocks/auth/auth-flow-block.d.ts +0 -123
- package/dist-lib/src/components/blocks/auth/auth-flow-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/auth-session-atom.d.ts +0 -28
- package/dist-lib/src/components/blocks/auth/auth-shell.d.ts +0 -56
- package/dist-lib/src/components/blocks/auth/auth-state-type.d.ts +0 -71
- package/dist-lib/src/components/blocks/auth/auth-state.d.ts +0 -9
- package/dist-lib/src/components/blocks/auth/code-auth-block.d.ts +0 -22
- package/dist-lib/src/components/blocks/auth/code-auth-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/forgot-password-block.d.ts +0 -15
- package/dist-lib/src/components/blocks/auth/forgot-password-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/linked-accounts-block.d.ts +0 -36
- package/dist-lib/src/components/blocks/auth/linked-accounts-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/login-block.d.ts +0 -25
- package/dist-lib/src/components/blocks/auth/login-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/onboarding/profile-step.d.ts +0 -19
- package/dist-lib/src/components/blocks/auth/onboarding-block.d.ts +0 -69
- package/dist-lib/src/components/blocks/auth/onboarding-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/passkey-manager-block.d.ts +0 -38
- package/dist-lib/src/components/blocks/auth/passkey-manager-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/passkey-sign-in-block.d.ts +0 -27
- package/dist-lib/src/components/blocks/auth/passkey-sign-in-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/signup-block.d.ts +0 -41
- package/dist-lib/src/components/blocks/auth/signup-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/update-password-block.d.ts +0 -28
- package/dist-lib/src/components/blocks/auth/update-password-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/auth/use-auth-state.d.ts +0 -52
- package/dist-lib/src/components/blocks/chat/banners.d.ts +0 -13
- package/dist-lib/src/components/blocks/chat/base.d.ts +0 -10
- package/dist-lib/src/components/blocks/chat/base.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/chat/header.d.ts +0 -4
- package/dist-lib/src/components/blocks/chat/index.d.ts +0 -13
- package/dist-lib/src/components/blocks/chat/recommendation-stack.d.ts +0 -13
- package/dist-lib/src/components/blocks/chat/suggested-prompts.d.ts +0 -3
- package/dist-lib/src/components/blocks/chat/types.d.ts +0 -64
- package/dist-lib/src/components/blocks/chat/version-history-record.d.ts +0 -3
- package/dist-lib/src/components/blocks/layout.d.ts +0 -8
- package/dist-lib/src/components/blocks/nav/sidebar/ai-sidebar-atom.d.ts +0 -217
- package/dist-lib/src/components/blocks/nav/sidebar/ai-sidebar.d.ts +0 -11
- package/dist-lib/src/components/blocks/nav/sidebar/index.d.ts +0 -15
- package/dist-lib/src/components/blocks/nav/sidebar/sidebar-context.d.ts +0 -3
- package/dist-lib/src/components/blocks/nav/sidebar/sidebar-icon.d.ts +0 -2
- package/dist-lib/src/components/blocks/nav/sidebar/sidebar-row.d.ts +0 -4
- package/dist-lib/src/components/blocks/nav/sidebar/type.d.ts +0 -245
- package/dist-lib/src/components/blocks/nav/sidebar/use-ai-sidebar-state.d.ts +0 -29
- package/dist-lib/src/components/blocks/nav/sidebar/use-ai-sidebar.d.ts +0 -27
- package/dist-lib/src/components/blocks/nav/sidebar/utils.d.ts +0 -21
- package/dist-lib/src/components/blocks/profile/index.d.ts +0 -7
- package/dist-lib/src/components/blocks/profile/profile-card-atom.d.ts +0 -68
- package/dist-lib/src/components/blocks/profile/profile-card-block.d.ts +0 -66
- package/dist-lib/src/components/blocks/profile/profile-card-block.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/profile/type.d.ts +0 -80
- package/dist-lib/src/components/blocks/profile/use-profile-card-state.d.ts +0 -24
- package/dist-lib/src/components/blocks/profile/use-profile-card.d.ts +0 -34
- package/dist-lib/src/components/blocks/scaffold/index.d.ts +0 -29
- package/dist-lib/src/components/blocks/scaffold/panel-close-button.d.ts +0 -4
- package/dist-lib/src/components/blocks/scaffold/panel-menu-button.d.ts +0 -8
- package/dist-lib/src/components/blocks/scaffold/scaffold-actions.d.ts +0 -10
- package/dist-lib/src/components/blocks/scaffold/scaffold-atom.d.ts +0 -97
- package/dist-lib/src/components/blocks/scaffold/scaffold-canvas.d.ts +0 -9
- package/dist-lib/src/components/blocks/scaffold/scaffold-context.d.ts +0 -7
- package/dist-lib/src/components/blocks/scaffold/scaffold-inspector.d.ts +0 -9
- package/dist-lib/src/components/blocks/scaffold/scaffold-main.d.ts +0 -8
- package/dist-lib/src/components/blocks/scaffold/scaffold-panel.d.ts +0 -10
- package/dist-lib/src/components/blocks/scaffold/scaffold-root.d.ts +0 -17
- package/dist-lib/src/components/blocks/scaffold/scaffold-root.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/scaffold/scaffold-sidebar.d.ts +0 -7
- package/dist-lib/src/components/blocks/scaffold/scaffold-sub-panel.d.ts +0 -3
- package/dist-lib/src/components/blocks/scaffold/scaffold-tab.d.ts +0 -14
- package/dist-lib/src/components/blocks/scaffold/type.d.ts +0 -203
- package/dist-lib/src/components/blocks/scaffold/use-scaffold-state.d.ts +0 -26
- package/dist-lib/src/components/blocks/scaffold/use-scaffold.d.ts +0 -14
- package/dist-lib/src/components/blocks/scaffold/utils.d.ts +0 -37
- package/dist-lib/src/components/blocks/shell/animated-panel-inset.d.ts +0 -2
- package/dist-lib/src/components/blocks/shell/animated-panel-provider.d.ts +0 -2
- package/dist-lib/src/components/blocks/shell/animated-panel-trigger.d.ts +0 -2
- package/dist-lib/src/components/blocks/shell/animated-panel.d.ts +0 -2
- package/dist-lib/src/components/blocks/shell/shell-fit-atom.d.ts +0 -68
- package/dist-lib/src/components/blocks/shell/shell-layout.d.ts +0 -3
- package/dist-lib/src/components/blocks/shell/shell-layout.story.d.ts +0 -3
- package/dist-lib/src/components/blocks/shell/shell-metrics.d.ts +0 -54
- package/dist-lib/src/components/blocks/shell/type.d.ts +0 -292
- package/dist-lib/src/components/blocks/shell/use-shell-fit-state.d.ts +0 -25
- package/dist-lib/src/components/blocks/shell/use-shell-fit.d.ts +0 -14
- package/dist-lib/src/components/blocks/shell/use-shell-panels.d.ts +0 -20
- package/dist-lib/src/components/blocks/shell/utils.d.ts +0 -70
- package/dist-lib/src/components/composed/action-bar/action.d.ts +0 -18
- package/dist-lib/src/components/composed/action-bar/emojis.d.ts +0 -10
- package/dist-lib/src/components/composed/action-bar/index.d.ts +0 -12
- package/dist-lib/src/components/composed/action-bar/toolbar.d.ts +0 -2
- package/dist-lib/src/components/composed/action-bar/types.d.ts +0 -55
- package/dist-lib/src/components/composed/action-bar/utils.d.ts +0 -57
- package/dist-lib/src/components/composed/ai/index.d.ts +0 -17
- package/dist-lib/src/components/composed/ai/message.d.ts +0 -7
- package/dist-lib/src/components/composed/ai/research-answer.d.ts +0 -30
- package/dist-lib/src/components/composed/ai/streaming-text.d.ts +0 -26
- package/dist-lib/src/components/composed/ai/suggested-prompt.d.ts +0 -35
- package/dist-lib/src/components/composed/ai/types.d.ts +0 -66
- package/dist-lib/src/components/composed/attachment.story.d.ts +0 -3
- package/dist-lib/src/components/composed/chart.story.d.ts +0 -3
- package/dist-lib/src/components/composed/chat/attachment.d.ts +0 -62
- package/dist-lib/src/components/composed/chat/chart.d.ts +0 -41
- package/dist-lib/src/components/composed/chat/code-block.d.ts +0 -26
- package/dist-lib/src/components/composed/chat/composer/add-button.d.ts +0 -5
- package/dist-lib/src/components/composed/chat/composer/base.d.ts +0 -8
- package/dist-lib/src/components/composed/chat/composer/command-menu.d.ts +0 -2
- package/dist-lib/src/components/composed/chat/composer/hooks.d.ts +0 -9
- package/dist-lib/src/components/composed/chat/composer/send-button.d.ts +0 -3
- package/dist-lib/src/components/composed/chat/composer/suggestions.d.ts +0 -7
- package/dist-lib/src/components/composed/chat/composer/utils.d.ts +0 -19
- package/dist-lib/src/components/composed/chat/context-cards.d.ts +0 -27
- package/dist-lib/src/components/composed/chat/filter-table.d.ts +0 -40
- package/dist-lib/src/components/composed/chat/flowchart/chart-node.d.ts +0 -2
- package/dist-lib/src/components/composed/chat/flowchart/connector.d.ts +0 -2
- package/dist-lib/src/components/composed/chat/flowchart/default.d.ts +0 -7
- package/dist-lib/src/components/composed/chat/flowchart/edge-toolbar.d.ts +0 -9
- package/dist-lib/src/components/composed/chat/flowchart/hooks.d.ts +0 -290
- package/dist-lib/src/components/composed/chat/flowchart/step-body.d.ts +0 -6
- package/dist-lib/src/components/composed/chat/flowchart/types.d.ts +0 -91
- package/dist-lib/src/components/composed/chat/flowchart/utils.d.ts +0 -64
- package/dist-lib/src/components/composed/chat/index.d.ts +0 -15
- package/dist-lib/src/components/composed/chat/insight-cards.d.ts +0 -54
- package/dist-lib/src/components/composed/chat/markdown-atom.d.ts +0 -7
- package/dist-lib/src/components/composed/chat/markdown.d.ts +0 -22
- package/dist-lib/src/components/composed/chat/recommendation/checkbox.d.ts +0 -15
- package/dist-lib/src/components/composed/chat/recommendation/constant.d.ts +0 -6
- package/dist-lib/src/components/composed/chat/recommendation/default.d.ts +0 -7
- package/dist-lib/src/components/composed/chat/recommendation/group.d.ts +0 -8
- package/dist-lib/src/components/composed/chat/recommendation/index.d.ts +0 -15
- package/dist-lib/src/components/composed/chat/recommendation/item.d.ts +0 -12
- package/dist-lib/src/components/composed/chat/recommendation/types.d.ts +0 -53
- package/dist-lib/src/components/composed/chat/records-table.d.ts +0 -38
- package/dist-lib/src/components/composed/chat/type.d.ts +0 -86
- package/dist-lib/src/components/composed/code-block.story.d.ts +0 -3
- package/dist-lib/src/components/composed/context-cards.story.d.ts +0 -3
- package/dist-lib/src/components/composed/emoji-action-bar.story.d.ts +0 -3
- package/dist-lib/src/components/composed/filter-table.story.d.ts +0 -7
- package/dist-lib/src/components/composed/flowchart.story.d.ts +0 -3
- package/dist-lib/src/components/composed/insight-cards.story.d.ts +0 -3
- package/dist-lib/src/components/composed/logo.d.ts +0 -34
- package/dist-lib/src/components/composed/logo.story.d.ts +0 -3
- package/dist-lib/src/components/composed/markdown.story.d.ts +0 -3
- package/dist-lib/src/components/composed/password-input.d.ts +0 -30
- package/dist-lib/src/components/composed/password-input.story.d.ts +0 -7
- package/dist-lib/src/components/composed/reader/applied-mark.d.ts +0 -3
- package/dist-lib/src/components/composed/reader/apply-toast.d.ts +0 -3
- package/dist-lib/src/components/composed/reader/selection-highlight.d.ts +0 -3
- package/dist-lib/src/components/composed/reader/selection-popover.d.ts +0 -9
- package/dist-lib/src/components/composed/reader/type.d.ts +0 -40
- package/dist-lib/src/components/composed/recommendation-card.story.d.ts +0 -3
- package/dist-lib/src/components/composed/records-table.story.d.ts +0 -3
- package/dist-lib/src/components/composed/research-answer.story.d.ts +0 -3
- package/dist-lib/src/components/composed/social-providers.d.ts +0 -37
- package/dist-lib/src/components/composed/social-providers.story.d.ts +0 -3
- package/dist-lib/src/components/composed/streaming-text.story.d.ts +0 -3
- package/dist-lib/src/components/composed/tree/index.d.ts +0 -6
- package/dist-lib/src/components/composed/tree/tree-atom.d.ts +0 -160
- package/dist-lib/src/components/composed/tree/tree-context.d.ts +0 -4
- package/dist-lib/src/components/composed/tree/tree-node.d.ts +0 -12
- package/dist-lib/src/components/composed/tree/tree.d.ts +0 -19
- package/dist-lib/src/components/composed/tree/type.d.ts +0 -290
- package/dist-lib/src/components/composed/tree/use-tree-dnd.d.ts +0 -18
- package/dist-lib/src/components/composed/tree/use-tree-state.d.ts +0 -25
- package/dist-lib/src/components/composed/tree/use-tree.d.ts +0 -24
- package/dist-lib/src/components/composed/tree/utils.d.ts +0 -28
- package/dist-lib/src/components/composed/tree.story.d.ts +0 -6
- package/dist-lib/src/components/composed/user/index.d.ts +0 -11
- package/dist-lib/src/components/composed/user/info.d.ts +0 -12
- package/dist-lib/src/components/composed/user/message.d.ts +0 -3
- package/dist-lib/src/components/composed/user/pill.d.ts +0 -10
- package/dist-lib/src/components/composed/user/types.d.ts +0 -20
- package/dist-lib/src/components/composed/user.story.d.ts +0 -8
- package/dist-lib/src/components/composed/verse/index.d.ts +0 -2
- package/dist-lib/src/components/composed/verse/types.d.ts +0 -18
- package/dist-lib/src/components/composed/verse/verse.d.ts +0 -5
- package/dist-lib/src/components/composed/verse.story.d.ts +0 -3
- package/dist-lib/src/components/docs/atoms-gallery.d.ts +0 -3
- package/dist-lib/src/components/docs/demo-controls.d.ts +0 -21
- package/dist-lib/src/components/icons/file-badge-cfm.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-corpus.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-pdf.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-tei.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-tf.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-txt.d.ts +0 -7
- package/dist-lib/src/components/icons/file-badge-xml.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-cfm.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-corpus.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-pdf.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-tei.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-tf.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-txt.d.ts +0 -7
- package/dist-lib/src/components/icons/file-wordmark-xml.d.ts +0 -7
- package/dist-lib/src/components/icons/index.d.ts +0 -15
- package/dist-lib/src/components/icons/types.d.ts +0 -7
- package/dist-lib/src/components/motion/overflow-actions.d.ts +0 -35
- package/dist-lib/src/components/motion/popover-morph.d.ts +0 -35
- package/dist-lib/src/components/motion/popover-position.d.ts +0 -15
- package/dist-lib/src/components/motion/shared-layout-bg.d.ts +0 -13
- package/dist-lib/src/components/theme-provider.d.ts +0 -15
- package/dist-lib/src/components/ui/avatar.story.d.ts +0 -7
- package/dist-lib/src/components/ui/badge.d.ts +0 -14
- package/dist-lib/src/components/ui/bubble.story.d.ts +0 -7
- package/dist-lib/src/components/ui/button.d.ts +0 -28
- package/dist-lib/src/components/ui/button.story.d.ts +0 -8
- package/dist-lib/src/components/ui/card.story.d.ts +0 -9
- package/dist-lib/src/components/ui/chat/avatar-handle.d.ts +0 -13
- package/dist-lib/src/components/ui/chat/badges.d.ts +0 -29
- package/dist-lib/src/components/ui/chat/buttons.d.ts +0 -29
- package/dist-lib/src/components/ui/chat/chips.d.ts +0 -48
- package/dist-lib/src/components/ui/chat/dot.d.ts +0 -30
- package/dist-lib/src/components/ui/chat/icon-button.d.ts +0 -18
- package/dist-lib/src/components/ui/chat/icon-tile.d.ts +0 -20
- package/dist-lib/src/components/ui/chat/index.d.ts +0 -12
- package/dist-lib/src/components/ui/chat/inline-source.d.ts +0 -20
- package/dist-lib/src/components/ui/chat/segmented-toggle.d.ts +0 -20
- package/dist-lib/src/components/ui/chat/tag.d.ts +0 -21
- package/dist-lib/src/components/ui/chat/thumbnail.d.ts +0 -13
- package/dist-lib/src/components/ui/chat/waveform.d.ts +0 -13
- package/dist-lib/src/components/ui/chat-atoms.story.d.ts +0 -22
- package/dist-lib/src/components/ui/checkbox.story.d.ts +0 -7
- package/dist-lib/src/components/ui/field.story.d.ts +0 -7
- package/dist-lib/src/components/ui/file-icons.story.d.ts +0 -7
- package/dist-lib/src/components/ui/frame.story.d.ts +0 -9
- package/dist-lib/src/components/ui/glasscn/glass-button-group.d.ts +0 -5
- package/dist-lib/src/components/ui/glasscn/glass-button.d.ts +0 -6
- package/dist-lib/src/components/ui/glasscn/glass-container.d.ts +0 -12
- package/dist-lib/src/components/ui/glasscn/glass-separator.d.ts +0 -5
- package/dist-lib/src/components/ui/glasscn/liquid-glass.d.ts +0 -31
- package/dist-lib/src/components/ui/input-group.d.ts +0 -13
- package/dist-lib/src/components/ui/input-group.story.d.ts +0 -7
- package/dist-lib/src/components/ui/input.d.ts +0 -9
- package/dist-lib/src/components/ui/input.story.d.ts +0 -9
- package/dist-lib/src/components/ui/label.story.d.ts +0 -9
- package/dist-lib/src/components/ui/menu-command.d.ts +0 -37
- package/dist-lib/src/components/ui/menu-command.story.d.ts +0 -7
- package/dist-lib/src/components/ui/otp-field.story.d.ts +0 -9
- package/dist-lib/src/components/ui/password-input.story.d.ts +0 -3
- package/dist-lib/src/components/ui/popover-glass.d.ts +0 -32
- package/dist-lib/src/components/ui/reference.story.d.ts +0 -10
- package/dist-lib/src/components/ui/select.d.ts +0 -30
- package/dist-lib/src/components/ui/separator.story.d.ts +0 -7
- package/dist-lib/src/components/ui/tabs.d.ts +0 -15
- package/dist-lib/src/components/ui/text.story.d.ts +0 -9
- package/dist-lib/src/components/ui/textarea.d.ts +0 -8
- package/dist-lib/src/components/ui/textarea.story.d.ts +0 -7
- package/dist-lib/src/components/ui/toast.d.ts +0 -15
- package/dist-lib/src/index.d.ts +0 -63
- package/dist-lib/src/lib/auth-accent.d.ts +0 -33
- package/dist-lib/src/lib/glass-variants.d.ts +0 -6
- package/dist-lib/src/lib/keyed-atom.d.ts +0 -14
- package/dist-lib/src/lib/segmented-control.d.ts +0 -8
- package/dist-lib/src/lib/state/exegia-provider.d.ts +0 -51
- package/dist-lib/src/lib/state/index.d.ts +0 -4
- package/dist-lib/src/lib/state/store.d.ts +0 -17
- package/src/components/blocks/auth/auth-flow-block.story.tsx +0 -17
- package/src/components/blocks/auth/auth-state-type.ts +0 -97
- package/src/components/blocks/auth/code-auth-block.story.tsx +0 -20
- package/src/components/blocks/auth/forgot-password-block.story.tsx +0 -14
- package/src/components/blocks/auth/linked-accounts-block.story.tsx +0 -23
- package/src/components/blocks/auth/login-block.story.tsx +0 -18
- package/src/components/blocks/auth/onboarding-block.story.tsx +0 -14
- package/src/components/blocks/auth/passkey-manager-block.story.tsx +0 -18
- package/src/components/blocks/auth/passkey-sign-in-block.story.tsx +0 -14
- package/src/components/blocks/auth/signup-block.story.tsx +0 -18
- package/src/components/blocks/auth/update-password-block.story.tsx +0 -14
- package/src/components/blocks/chat/base.story.tsx +0 -19
- package/src/components/blocks/chat/types.ts +0 -81
- package/src/components/blocks/nav/sidebar/__tests__/ai-sidebar-atom.test.tsx +0 -325
- package/src/components/blocks/nav/sidebar/__tests__/ai-sidebar.test.tsx +0 -93
- package/src/components/blocks/nav/sidebar/__tests__/use-ai-sidebar.test.tsx +0 -310
- package/src/components/blocks/nav/sidebar/ai-sidebar-atom.ts +0 -698
- package/src/components/blocks/nav/sidebar/ai-sidebar.tsx +0 -150
- package/src/components/blocks/nav/sidebar/index.ts +0 -67
- package/src/components/blocks/nav/sidebar/sidebar-context.ts +0 -15
- package/src/components/blocks/nav/sidebar/sidebar-icon.tsx +0 -14
- package/src/components/blocks/nav/sidebar/sidebar-row.tsx +0 -378
- package/src/components/blocks/nav/sidebar/type.ts +0 -294
- package/src/components/blocks/nav/sidebar/use-ai-sidebar-state.ts +0 -123
- package/src/components/blocks/nav/sidebar/use-ai-sidebar.ts +0 -619
- package/src/components/blocks/nav/sidebar/utils.ts +0 -170
- package/src/components/blocks/profile/profile-card-block.story.tsx +0 -18
- package/src/components/blocks/scaffold/scaffold-root.story.tsx +0 -36
- package/src/components/blocks/shell/shell-layout.story.tsx +0 -19
- package/src/components/composed/attachment.story.tsx +0 -52
- package/src/components/composed/chart.story.tsx +0 -46
- package/src/components/composed/code-block.story.tsx +0 -29
- package/src/components/composed/context-cards.story.tsx +0 -30
- package/src/components/composed/emoji-action-bar.story.tsx +0 -20
- package/src/components/composed/filter-table.story.tsx +0 -58
- package/src/components/composed/flowchart.story.tsx +0 -24
- package/src/components/composed/insight-cards.story.tsx +0 -51
- package/src/components/composed/logo.story.tsx +0 -21
- package/src/components/composed/markdown.story.tsx +0 -21
- package/src/components/composed/password-input.story.tsx +0 -38
- package/src/components/composed/recommendation-card.story.tsx +0 -32
- package/src/components/composed/records-table.story.tsx +0 -46
- package/src/components/composed/research-answer.story.tsx +0 -32
- package/src/components/composed/social-providers.story.tsx +0 -24
- package/src/components/composed/streaming-text.story.tsx +0 -24
- package/src/components/composed/tree.story.tsx +0 -31
- package/src/components/composed/user.story.tsx +0 -29
- package/src/components/composed/verse.story.tsx +0 -21
- package/src/components/ui/avatar.story.tsx +0 -33
- package/src/components/ui/bubble.story.tsx +0 -45
- package/src/components/ui/button.story.tsx +0 -62
- package/src/components/ui/card.story.tsx +0 -36
- package/src/components/ui/chat-atoms.story.tsx +0 -94
- package/src/components/ui/checkbox.story.tsx +0 -30
- package/src/components/ui/field.story.tsx +0 -32
- package/src/components/ui/file-icons.story.tsx +0 -24
- package/src/components/ui/frame.story.tsx +0 -32
- package/src/components/ui/input-group.story.tsx +0 -27
- package/src/components/ui/input.story.tsx +0 -42
- package/src/components/ui/label.story.tsx +0 -32
- package/src/components/ui/menu-command.story.tsx +0 -37
- package/src/components/ui/otp-field.story.tsx +0 -35
- package/src/components/ui/password-input.story.tsx +0 -19
- package/src/components/ui/reference.story.tsx +0 -33
- package/src/components/ui/separator.story.tsx +0 -34
- package/src/components/ui/text.story.tsx +0 -31
- package/src/components/ui/textarea.story.tsx +0 -34
- /package/dist-lib/{src/components → components}/atoms/_layout.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/avatar/verified.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/background/index.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/bubble/__tests__/bubble.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/bubble/__tests__/reference.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/chip.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/text/__tests__/text.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/text-selection/__tests__/click-popover.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/atoms/text-selection/__tests__/text-selection.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/auth-flow-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/auth-state-atom.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/linked-accounts-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/onboarding-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/passkey-manager-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/passkey-sign-in-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/signup-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/auth/__tests__/update-password-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/chat/__tests__/ai-panel.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/chat/shared.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/profile/__tests__/profile-card-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/scaffold/__tests__/scaffold-atom.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/scaffold/__tests__/scaffold.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/scaffold/constants.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/shell/__tests__/shell-fit-atom.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/shell/__tests__/shell-layout.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/shell/__tests__/shell-metrics.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/blocks/shell/index.d.ts +0 -0
- /package/dist-lib/{src/components/blocks/nav → components/blocks}/sidebar/__tests__/ai-sidebar-atom.test.d.ts +0 -0
- /package/dist-lib/{src/components/blocks/nav → components/blocks}/sidebar/__tests__/ai-sidebar.test.d.ts +0 -0
- /package/dist-lib/{src/components/blocks/nav → components/blocks}/sidebar/__tests__/use-ai-sidebar.test.d.ts +0 -0
- /package/dist-lib/{src/components/blocks/nav → components/blocks}/sidebar/marquee-label.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/__tests__/logo.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/action-bar/__tests__/action-bar.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/ai/__tests__/ai.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/ai/avatar.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/ai/shared.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/attachment.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/beautiful-ui.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/chart.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/code-block.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/composer-attachments.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/composer.test.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/__tests__/flowchart.test.d.ts +0 -0
- /package/dist-lib/{src/components/composed/chat/__tests__/markdown.test.d.ts → components/composed/chat/__tests__/glass-material.test.d.ts} +0 -0
- /package/dist-lib/{src/components/composed/tree/__tests__/tree-atom.test.d.ts → components/composed/chat/__tests__/markdown.test.d.ts} +0 -0
- /package/dist-lib/{src/components/composed/tree/__tests__/tree.test.d.ts → components/composed/chat/__tests__/menu-command.test.d.ts} +0 -0
- /package/dist-lib/{src/components/composed/tree/__tests__/use-tree.test.d.ts → components/composed/chat/__tests__/recommendation.test.d.ts} +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/actions.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/composer/index.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/flowchart/constant.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/flowchart/index.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/chat/hint.d.ts +0 -0
- /package/dist-lib/{src/components → components}/composed/reader/index.d.ts +0 -0
- /package/dist-lib/{src/components/composed/user/utils.d.ts → components/composed/tree/__tests__/tree-atom.test.d.ts} +0 -0
- /package/dist-lib/{src/components/composed/verse/__tests__/verse.test.d.ts → components/composed/tree/__tests__/tree.test.d.ts} +0 -0
- /package/dist-lib/{src/components/icons/__tests__/icons.test.d.ts → components/composed/tree/__tests__/use-tree.test.d.ts} +0 -0
- /package/dist-lib/{src/components → components}/composed/tree/constants.d.ts +0 -0
- /package/dist-lib/{src/components/motion/index.d.ts → components/composed/user/__tests__/message.test.d.ts} +0 -0
- /package/dist-lib/{src/components/ui/chat/__tests__/segmented-toggle.test.d.ts → components/composed/user/utils.d.ts} +0 -0
- /package/dist-lib/{src/lib/utils.test.d.ts → components/composed/verse/__tests__/verse.test.d.ts} +0 -0
- /package/dist-lib/{src/components → components}/docs/code-block.d.ts +0 -0
- /package/dist-lib/{src/components → components}/docs/hero.d.ts +0 -0
- /package/dist-lib/{src/components → components}/docs/mermaid.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/accordion.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/alert-dialog.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/alert.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/autocomplete.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/avatar.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/button-group.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/card.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/checkbox.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/collapsible.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/command.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/context-menu.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/emoji-picker.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/field.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/frame.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/group.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/kbd.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/label.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/menu.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/otp-field.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/popover.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/preview-card.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/scroll-area.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/separator.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/sheet.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/skeleton.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/spinner.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/switch.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/toolbar.d.ts +0 -0
- /package/dist-lib/{src/components → components}/ui/tooltip.d.ts +0 -0
- /package/dist-lib/{src/lib → lib}/ease.d.ts +0 -0
- /package/dist-lib/{src/lib → lib}/hooks/use-hover-capable.d.ts +0 -0
- /package/dist-lib/{src/lib → lib}/hooks/use-mobile.d.ts +0 -0
- /package/dist-lib/{src/lib → lib}/sound.d.ts +0 -0
- /package/dist-lib/{src/lib → lib}/utils.d.ts +0 -0
- /package/src/components/blocks/{nav/sidebar → sidebar}/marquee-label.tsx +0 -0
|
@@ -0,0 +1,2358 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_ECHARTS_RENDERER,
|
|
5
|
+
buildChartCss,
|
|
6
|
+
flattenColor,
|
|
7
|
+
getColorsCount,
|
|
8
|
+
resolveColors,
|
|
9
|
+
seriesPaint,
|
|
10
|
+
withAlpha,
|
|
11
|
+
type ChartConfig,
|
|
12
|
+
type EChartsRenderer,
|
|
13
|
+
type ResolvedColors,
|
|
14
|
+
} from "./echarts-chart";
|
|
15
|
+
import {
|
|
16
|
+
tooltipBaseOption,
|
|
17
|
+
tooltipIndicatorHtml,
|
|
18
|
+
tooltipRow,
|
|
19
|
+
tooltipShell,
|
|
20
|
+
type TooltipPosition,
|
|
21
|
+
type TooltipRoundness,
|
|
22
|
+
type TooltipVariant,
|
|
23
|
+
} from "./echarts-tooltip";
|
|
24
|
+
import {
|
|
25
|
+
Brush,
|
|
26
|
+
buildBrushDataZoom,
|
|
27
|
+
syncBrushOverlay,
|
|
28
|
+
type BrushGeometry,
|
|
29
|
+
type BrushOverlayElements,
|
|
30
|
+
type BrushProps,
|
|
31
|
+
type BrushRange,
|
|
32
|
+
} from "./echarts-brush";
|
|
33
|
+
import {
|
|
34
|
+
DataZoomComponent,
|
|
35
|
+
GridComponent,
|
|
36
|
+
TooltipComponent,
|
|
37
|
+
type DataZoomComponentOption,
|
|
38
|
+
type GridComponentOption,
|
|
39
|
+
type TooltipComponentOption,
|
|
40
|
+
} from "echarts/components";
|
|
41
|
+
import {
|
|
42
|
+
Children,
|
|
43
|
+
isValidElement,
|
|
44
|
+
useCallback,
|
|
45
|
+
useEffect,
|
|
46
|
+
useId,
|
|
47
|
+
useMemo,
|
|
48
|
+
useRef,
|
|
49
|
+
useState,
|
|
50
|
+
type CSSProperties,
|
|
51
|
+
type FC,
|
|
52
|
+
type ReactNode,
|
|
53
|
+
} from "react";
|
|
54
|
+
import { dotItemStyle, dotStyle, sampleGradient, type DotVariant } from "./echarts-dot";
|
|
55
|
+
import { LegendOverlay, type LegendVariant } from "./echarts-legend";
|
|
56
|
+
import type { ComposeOption, ImagePatternObject } from "echarts/core";
|
|
57
|
+
import { LineChart, type LineSeriesOption } from "echarts/charts";
|
|
58
|
+
import { motion, useReducedMotion } from "motion/react";
|
|
59
|
+
import * as echarts from "echarts/core";
|
|
60
|
+
|
|
61
|
+
// Re-export the shared types that were previously declared inline here, so
|
|
62
|
+
// existing consumers/examples keep importing them from the chart module.
|
|
63
|
+
export type {
|
|
64
|
+
ChartConfig,
|
|
65
|
+
DotVariant,
|
|
66
|
+
EChartsRenderer,
|
|
67
|
+
LegendVariant,
|
|
68
|
+
TooltipPosition,
|
|
69
|
+
TooltipRoundness,
|
|
70
|
+
TooltipVariant,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Modular registration keeps the bundle lean — only the pieces this chart needs.
|
|
74
|
+
// `DataZoomComponent` bundles both the slider (brush footer) and inside (wheel/drag)
|
|
75
|
+
// zoom. The brush's frame/handles/labels are raw zrender elements, not the
|
|
76
|
+
// graphic component — see syncBrushOverlay.
|
|
77
|
+
echarts.use([LineChart, GridComponent, TooltipComponent, DataZoomComponent]);
|
|
78
|
+
|
|
79
|
+
type EChartsInstance = ReturnType<typeof echarts.init>;
|
|
80
|
+
|
|
81
|
+
// The exact option surface this chart uses — line series, grid, tooltip, and
|
|
82
|
+
// dataZoom, plus the axis options they pull in as dependencies. Narrower than
|
|
83
|
+
// echarts' full EChartsOption, so a misspelled key fails the compile instead of
|
|
84
|
+
// silently reaching setOption.
|
|
85
|
+
type EChartsOption = ComposeOption<
|
|
86
|
+
LineSeriesOption | GridComponentOption | TooltipComponentOption | DataZoomComponentOption
|
|
87
|
+
>;
|
|
88
|
+
|
|
89
|
+
// Single-entry views of the composed option's array-or-single fields — the
|
|
90
|
+
// modular entry points don't export the axis option types directly.
|
|
91
|
+
type ArrayItem<T> = T extends readonly (infer U)[] ? U : T;
|
|
92
|
+
type XAxisOption = ArrayItem<NonNullable<EChartsOption["xAxis"]>>;
|
|
93
|
+
type YAxisOption = ArrayItem<NonNullable<EChartsOption["yAxis"]>>;
|
|
94
|
+
|
|
95
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
// Constants
|
|
97
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
const STROKE_WIDTH = 0.8; // default series stroke — <Area strokeWidth> overrides it
|
|
100
|
+
const LOADING_ANIMATION_DURATION = 2000; // shimmer loop, in milliseconds
|
|
101
|
+
const REVEAL_DURATION = 1000; // intro draw-in length, in milliseconds
|
|
102
|
+
// NOTE: the intro draw-in runs ECharts' RAW default entrance animation. Custom
|
|
103
|
+
// easing was tried and abandoned — ECharts hardcodes the line-entrance clip to
|
|
104
|
+
// linear and ignores animationEasing at every level (verified empirically).
|
|
105
|
+
const LOADING_DEFAULT_POINTS = 14;
|
|
106
|
+
// Buffer line: the last segment's stroke renders as this dash while the rest of
|
|
107
|
+
// the area stays solid, echoing the Recharts twin's 4px dash / 3px gap forecast
|
|
108
|
+
// tail. Ported from the line-chart twin.
|
|
109
|
+
const BUFFER_DASH: [number, number] = [4, 3];
|
|
110
|
+
|
|
111
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
112
|
+
// Theme knobs — every neutral line in the chart draws from these. Base colors
|
|
113
|
+
// come from the consumer's CSS tokens (resolved from the live DOM), so only the
|
|
114
|
+
// opacity factors live here. Factors MULTIPLY the token's own alpha — a border
|
|
115
|
+
// token that is already 10%-white stays subtle. Tune here, not in the builder.
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
// Recharts draws its grid at border/50, but SVG dashes render pixel-crisp while
|
|
118
|
+
// canvas at 2× DPR spreads a 1px line across device pixels — roughly halving
|
|
119
|
+
// perceived intensity. Using the border token's full alpha lands both engines at
|
|
120
|
+
// the same apparent brightness.
|
|
121
|
+
const GRID_LINE_OPACITY = 1; // dashed y-axis split lines, × border alpha
|
|
122
|
+
const AXIS_POINTER_OPACITY = 1; // tooltip cursor line, × border alpha
|
|
123
|
+
// The skeleton is CLIPPED to a small sweeping window — only the wave section
|
|
124
|
+
// inside it exists (stroke + fill), everything outside is fully transparent,
|
|
125
|
+
// like a clip-path sliding across the chart.
|
|
126
|
+
const LOADING_STROKE_OPACITY = 0.5; // outline inside the window, × foreground alpha
|
|
127
|
+
const LOADING_SHIMMER_MAX_OPACITY = 0.03; // fill inside the window, × foreground alpha
|
|
128
|
+
const LOADING_SHIMMER_BAND = 0.2; // window half-width, fraction of chart width
|
|
129
|
+
const LOADING_SHIMMER_FEATHER = 0.2; // eased edge softening of the clip window
|
|
130
|
+
const BRUSH_STROKE_OPACITY = 0.5; // mini-chart series stroke
|
|
131
|
+
const BRUSH_FILL_OPACITY = 0.15; // mini-chart series fade, at the top stop
|
|
132
|
+
const BRUSH_FILLER_OPACITY = 0; // selected-range wash — evil-brush draws none
|
|
133
|
+
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
// Public types
|
|
136
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
export type AreaVariant =
|
|
139
|
+
| "gradient"
|
|
140
|
+
| "gradient-reverse"
|
|
141
|
+
| "solid"
|
|
142
|
+
| "dotted"
|
|
143
|
+
| "lines"
|
|
144
|
+
| "hatched"
|
|
145
|
+
| "none"; // stroke only — no fill at all
|
|
146
|
+
export type StrokeVariant = "solid" | "dashed" | "animated-dashed";
|
|
147
|
+
export type StackType = "default" | "stacked" | "expanded";
|
|
148
|
+
export type AreaAnimationType =
|
|
149
|
+
| "none"
|
|
150
|
+
| "left-to-right"
|
|
151
|
+
| "right-to-left"
|
|
152
|
+
| "center-out"
|
|
153
|
+
| "edges-in";
|
|
154
|
+
export type CurveType =
|
|
155
|
+
| "linear"
|
|
156
|
+
| "smooth"
|
|
157
|
+
| "bump"
|
|
158
|
+
| "monotone"
|
|
159
|
+
| "monotoneX"
|
|
160
|
+
| "monotoneY"
|
|
161
|
+
| "natural"
|
|
162
|
+
| "step";
|
|
163
|
+
// DotVariant, TooltipVariant, TooltipRoundness, LegendVariant, and ChartConfig
|
|
164
|
+
// now live in the shared ./echarts/* modules and are imported +
|
|
165
|
+
// re-exported at the top of this file.
|
|
166
|
+
|
|
167
|
+
export interface EChartsAreaChartProps<TData extends Record<string, unknown>> {
|
|
168
|
+
data: TData[]; // rows rendered by the chart
|
|
169
|
+
config: ChartConfig; // series colors + labels
|
|
170
|
+
renderer?: EChartsRenderer; // rendering engine — defaults to canvas
|
|
171
|
+
xDataKey?: keyof TData & string; // x category key — falls back to the <XAxis> dataKey / first free column
|
|
172
|
+
className?: string; // extra classes for the chart container
|
|
173
|
+
curveType?: CurveType; // default curve interpolation each <Area> inherits
|
|
174
|
+
stackType?: StackType; // how multiple areas combine
|
|
175
|
+
animation?: boolean; // master switch for the intro draw-in — false renders instantly
|
|
176
|
+
animationType?: AreaAnimationType; // default intro reveal (first <Area> overrides)
|
|
177
|
+
enableHoverHighlight?: boolean; // hovering a series dims the others, like a temporary selection
|
|
178
|
+
enableHoverReveal?: boolean; // hovering colors each area up to the pointer's x and mutes the rest
|
|
179
|
+
defaultSelectedDataKey?: string | null; // series selected on first render
|
|
180
|
+
selectedDataKey?: string | null; // controlled selection — overrides internal state when set
|
|
181
|
+
onSelectionChange?: (key: string | null) => void; // fires when the selected series changes
|
|
182
|
+
isLoading?: boolean; // shows the animated loading skeleton
|
|
183
|
+
loadingPoints?: number; // number of points in the loading skeleton
|
|
184
|
+
chartOptions?: Record<string, unknown>; // escape hatch merged over the built ECharts option
|
|
185
|
+
children?: ReactNode; // declarative config — <Area>, <XAxis>, <Grid>, <Tooltip>, <Legend>, <Brush>, …
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
189
|
+
// Composible parts — DECLARATIVE CONFIG. Every part renders `null`; the root
|
|
190
|
+
// walks `children` by reference (child.type === Area, …) to collect its props.
|
|
191
|
+
// Presence semantics mirror the Recharts twin: omit a child and that part does
|
|
192
|
+
// not render. These are never mounted into the tree — they only carry props.
|
|
193
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
export interface AreaProps {
|
|
196
|
+
dataKey: string; // series key — must exist on the data + config
|
|
197
|
+
variant?: AreaVariant; // fill style for this area only
|
|
198
|
+
strokeVariant?: StrokeVariant; // stroke style for this area
|
|
199
|
+
strokeWidth?: number; // stroke thickness in pixels for this area
|
|
200
|
+
curveType?: CurveType; // curve interpolation — falls back to the root curveType
|
|
201
|
+
animationType?: AreaAnimationType; // intro reveal — first area drives the wrapper wipe
|
|
202
|
+
connectNulls?: boolean; // join segments across null/missing values
|
|
203
|
+
isClickable?: boolean; // lets this area be selected by clicking it
|
|
204
|
+
enableBufferLine?: boolean; // renders this area's last segment as a dashed, fill-less buffer
|
|
205
|
+
children?: ReactNode; // optional <Dot> and <ActiveDot> config
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A single area series. Declares its own fill/stroke/curve/clickability and,
|
|
210
|
+
* optionally, resting/active point markers via composed <Dot> / <ActiveDot>.
|
|
211
|
+
* Renders nothing — the root reads these props to build the ECharts series.
|
|
212
|
+
*/
|
|
213
|
+
const Area: FC<AreaProps> = () => null;
|
|
214
|
+
|
|
215
|
+
export interface DotProps {
|
|
216
|
+
variant?: DotVariant; // visual style of the point marker
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Declares the resting point marker for the enclosing <Area>. Renders nothing. */
|
|
220
|
+
const Dot: FC<DotProps> = () => null;
|
|
221
|
+
|
|
222
|
+
/** Declares the hovered/active point marker for the enclosing <Area>. Renders nothing. */
|
|
223
|
+
const ActiveDot: FC<DotProps> = () => null;
|
|
224
|
+
|
|
225
|
+
export interface XAxisProps {
|
|
226
|
+
dataKey?: string; // x category key — overrides the root xDataKey
|
|
227
|
+
// Category-axis values are always stringified, so the formatter sees a string —
|
|
228
|
+
// letting examples share `(value) => value.substring(0, 3)` with the Recharts twin.
|
|
229
|
+
tickFormatter?: (value: string, index: number) => string; // formats x tick labels
|
|
230
|
+
label?: string; // axis title, centered below the tick labels
|
|
231
|
+
hideDots?: boolean; // hides the tick dots beside this axis's labels
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Presence shows the x-axis category labels. Renders nothing. */
|
|
235
|
+
const XAxis: FC<XAxisProps> = () => null;
|
|
236
|
+
|
|
237
|
+
export interface YAxisProps {
|
|
238
|
+
dataKey?: string; // reserved for parity with the Recharts twin
|
|
239
|
+
tickFormatter?: (value: number, index: number) => string; // formats y tick labels
|
|
240
|
+
label?: string; // axis title, rotated alongside the tick labels
|
|
241
|
+
hideDots?: boolean; // hides the tick dots beside this axis's labels
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Presence shows the y value axis. Renders nothing. */
|
|
245
|
+
const YAxis: FC<YAxisProps> = () => null;
|
|
246
|
+
|
|
247
|
+
/** Presence shows the dashed horizontal split lines. Renders nothing. */
|
|
248
|
+
const Grid: FC = () => null;
|
|
249
|
+
|
|
250
|
+
export interface TooltipProps {
|
|
251
|
+
variant?: TooltipVariant; // visual style of the tooltip surface
|
|
252
|
+
roundness?: TooltipRoundness; // border-radius of the tooltip
|
|
253
|
+
cursor?: boolean; // whether the vertical cursor line follows the pointer
|
|
254
|
+
position?: TooltipPosition; // "variable" follows both axes (default); "fixed" pins the tooltip near the top and tracks the pointer's X
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Presence enables the hover tooltip. Renders nothing. */
|
|
258
|
+
const Tooltip: FC<TooltipProps> = () => null;
|
|
259
|
+
|
|
260
|
+
export interface LegendProps {
|
|
261
|
+
variant?: LegendVariant; // visual style of the legend indicators
|
|
262
|
+
align?: "left" | "center" | "right"; // horizontal placement
|
|
263
|
+
verticalAlign?: "top" | "middle" | "bottom"; // vertical placement
|
|
264
|
+
isClickable?: boolean; // lets each entry toggle selection of its series
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Presence enables the HTML legend overlay. Renders nothing. */
|
|
268
|
+
const Legend: FC<LegendProps> = () => null;
|
|
269
|
+
|
|
270
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
271
|
+
// Children collection — walk the declarative config into plain objects the
|
|
272
|
+
// option builder consumes. <Dot> / <ActiveDot> are read from each <Area>'s own
|
|
273
|
+
// children; a missing dot child means that marker does not render.
|
|
274
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
type AreaSeriesConfig = {
|
|
277
|
+
dataKey: string;
|
|
278
|
+
variant: AreaVariant;
|
|
279
|
+
strokeVariant: StrokeVariant;
|
|
280
|
+
strokeWidth: number;
|
|
281
|
+
curveType?: CurveType;
|
|
282
|
+
animationType?: AreaAnimationType;
|
|
283
|
+
connectNulls: boolean;
|
|
284
|
+
isClickable: boolean;
|
|
285
|
+
enableBufferLine: boolean;
|
|
286
|
+
dotVariant: DotVariant; // "none" when no <Dot> child is present
|
|
287
|
+
activeDotVariant: DotVariant; // "none" when no <ActiveDot> child is present
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
type XAxisSlot = {
|
|
291
|
+
present: boolean;
|
|
292
|
+
dataKey?: string;
|
|
293
|
+
tickFormatter?: (value: string, index: number) => string;
|
|
294
|
+
label?: string;
|
|
295
|
+
hideDots: boolean;
|
|
296
|
+
};
|
|
297
|
+
type YAxisSlot = {
|
|
298
|
+
present: boolean;
|
|
299
|
+
dataKey?: string;
|
|
300
|
+
tickFormatter?: (value: number, index: number) => string;
|
|
301
|
+
label?: string;
|
|
302
|
+
hideDots: boolean;
|
|
303
|
+
};
|
|
304
|
+
type TooltipSlot = {
|
|
305
|
+
present: boolean;
|
|
306
|
+
variant: TooltipVariant;
|
|
307
|
+
roundness: TooltipRoundness;
|
|
308
|
+
cursor: boolean;
|
|
309
|
+
position: TooltipPosition;
|
|
310
|
+
};
|
|
311
|
+
type LegendSlot = {
|
|
312
|
+
present: boolean;
|
|
313
|
+
variant: LegendVariant;
|
|
314
|
+
align: "left" | "center" | "right";
|
|
315
|
+
verticalAlign: "top" | "middle" | "bottom";
|
|
316
|
+
isClickable: boolean;
|
|
317
|
+
};
|
|
318
|
+
type BrushSlot = {
|
|
319
|
+
present: boolean; // a <Brush> child was passed — replaces the old showBrush prop
|
|
320
|
+
height?: number;
|
|
321
|
+
formatLabel?: (value: string, index: number) => string;
|
|
322
|
+
onChange?: (range: { startIndex: number; endIndex: number }) => void;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
type CollectedConfig = {
|
|
326
|
+
areas: AreaSeriesConfig[];
|
|
327
|
+
xAxis: XAxisSlot;
|
|
328
|
+
yAxis: YAxisSlot;
|
|
329
|
+
showGrid: boolean;
|
|
330
|
+
tooltip: TooltipSlot;
|
|
331
|
+
legend: LegendSlot;
|
|
332
|
+
brush: BrushSlot;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
function collectConfig(children: ReactNode): CollectedConfig {
|
|
336
|
+
const areas: AreaSeriesConfig[] = [];
|
|
337
|
+
let xAxis: XAxisSlot = { present: false, hideDots: false };
|
|
338
|
+
let yAxis: YAxisSlot = { present: false, hideDots: false };
|
|
339
|
+
let showGrid = false;
|
|
340
|
+
let tooltip: TooltipSlot = {
|
|
341
|
+
present: false,
|
|
342
|
+
variant: "default",
|
|
343
|
+
roundness: "lg",
|
|
344
|
+
cursor: true,
|
|
345
|
+
position: "variable",
|
|
346
|
+
};
|
|
347
|
+
let legend: LegendSlot = {
|
|
348
|
+
present: false,
|
|
349
|
+
variant: "rounded-square",
|
|
350
|
+
align: "right",
|
|
351
|
+
verticalAlign: "top",
|
|
352
|
+
isClickable: false,
|
|
353
|
+
};
|
|
354
|
+
let brush: BrushSlot = { present: false };
|
|
355
|
+
|
|
356
|
+
Children.forEach(children, (child) => {
|
|
357
|
+
if (!isValidElement(child)) return;
|
|
358
|
+
const type = child.type;
|
|
359
|
+
|
|
360
|
+
if (type === Area) {
|
|
361
|
+
const props = child.props as AreaProps;
|
|
362
|
+
let dotVariant: DotVariant = "none";
|
|
363
|
+
let activeDotVariant: DotVariant = "none";
|
|
364
|
+
Children.forEach(props.children, (dotChild) => {
|
|
365
|
+
if (!isValidElement(dotChild)) return;
|
|
366
|
+
if (dotChild.type === Dot) {
|
|
367
|
+
dotVariant = (dotChild.props as DotProps).variant ?? "default";
|
|
368
|
+
} else if (dotChild.type === ActiveDot) {
|
|
369
|
+
activeDotVariant = (dotChild.props as DotProps).variant ?? "default";
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
areas.push({
|
|
373
|
+
dataKey: props.dataKey,
|
|
374
|
+
variant: props.variant ?? "gradient",
|
|
375
|
+
strokeVariant: props.strokeVariant ?? "dashed",
|
|
376
|
+
strokeWidth: props.strokeWidth ?? STROKE_WIDTH,
|
|
377
|
+
curveType: props.curveType,
|
|
378
|
+
animationType: props.animationType,
|
|
379
|
+
connectNulls: props.connectNulls ?? false,
|
|
380
|
+
isClickable: props.isClickable ?? false,
|
|
381
|
+
enableBufferLine: props.enableBufferLine ?? false,
|
|
382
|
+
dotVariant,
|
|
383
|
+
activeDotVariant,
|
|
384
|
+
});
|
|
385
|
+
} else if (type === XAxis) {
|
|
386
|
+
const props = child.props as XAxisProps;
|
|
387
|
+
xAxis = {
|
|
388
|
+
present: true,
|
|
389
|
+
dataKey: props.dataKey,
|
|
390
|
+
tickFormatter: props.tickFormatter,
|
|
391
|
+
label: props.label,
|
|
392
|
+
hideDots: props.hideDots ?? false,
|
|
393
|
+
};
|
|
394
|
+
} else if (type === YAxis) {
|
|
395
|
+
const props = child.props as YAxisProps;
|
|
396
|
+
yAxis = {
|
|
397
|
+
present: true,
|
|
398
|
+
dataKey: props.dataKey,
|
|
399
|
+
tickFormatter: props.tickFormatter,
|
|
400
|
+
label: props.label,
|
|
401
|
+
hideDots: props.hideDots ?? false,
|
|
402
|
+
};
|
|
403
|
+
} else if (type === Grid) {
|
|
404
|
+
showGrid = true;
|
|
405
|
+
} else if (type === Tooltip) {
|
|
406
|
+
const props = child.props as TooltipProps;
|
|
407
|
+
tooltip = {
|
|
408
|
+
present: true,
|
|
409
|
+
variant: props.variant ?? "default",
|
|
410
|
+
roundness: props.roundness ?? "lg",
|
|
411
|
+
cursor: props.cursor ?? true,
|
|
412
|
+
position: props.position ?? "variable",
|
|
413
|
+
};
|
|
414
|
+
} else if (type === Legend) {
|
|
415
|
+
const props = child.props as LegendProps;
|
|
416
|
+
legend = {
|
|
417
|
+
present: true,
|
|
418
|
+
variant: props.variant ?? "rounded-square",
|
|
419
|
+
align: props.align ?? "right",
|
|
420
|
+
verticalAlign: props.verticalAlign ?? "top",
|
|
421
|
+
isClickable: props.isClickable ?? false,
|
|
422
|
+
};
|
|
423
|
+
} else if (type === Brush) {
|
|
424
|
+
const props = child.props as BrushProps;
|
|
425
|
+
brush = {
|
|
426
|
+
present: true,
|
|
427
|
+
height: props.height,
|
|
428
|
+
formatLabel: props.formatLabel,
|
|
429
|
+
onChange: props.onChange,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
return { areas, xAxis, yAxis, showGrid, tooltip, legend, brush };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Color plumbing (ChartConfig, getColorsCount, distributeColors, buildChartCss,
|
|
438
|
+
// normalizeColor, withAlpha, ResolvedColors, resolveColors, seriesPaint) now
|
|
439
|
+
// lives in ./echarts-chart and is imported at the top of this file.
|
|
440
|
+
|
|
441
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
442
|
+
// Fill paints — the ECharts analogue of the Recharts fill variants (§1.1).
|
|
443
|
+
// The first three are alpha fades; the last three are tiling canvas patterns.
|
|
444
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
445
|
+
|
|
446
|
+
// Tiling texture fills, tinted with the series' first color. Stripes are drawn
|
|
447
|
+
// STRAIGHT (trivially seamless) and the pattern itself is rotated — zrender
|
|
448
|
+
// applies pattern transforms the same way ECharts decals do. Baking a diagonal
|
|
449
|
+
// into a square tile clips the stroke at the corners, which reads as periodic
|
|
450
|
+
// gaps once tiled. Tiles render at devicePixelRatio and scale back down so the
|
|
451
|
+
// texture stays crisp on retina canvases.
|
|
452
|
+
function patternFill(
|
|
453
|
+
kind: "dotted" | "lines" | "hatched" | "stripe",
|
|
454
|
+
color: string,
|
|
455
|
+
): ImagePatternObject | null {
|
|
456
|
+
if (typeof document === "undefined") return null;
|
|
457
|
+
const dpr = Math.max(window.devicePixelRatio || 1, 1);
|
|
458
|
+
const canvas = document.createElement("canvas");
|
|
459
|
+
const ctx = canvas.getContext("2d");
|
|
460
|
+
if (!ctx) return null;
|
|
461
|
+
|
|
462
|
+
const size = (width: number, height: number) => {
|
|
463
|
+
canvas.width = width * dpr;
|
|
464
|
+
canvas.height = height * dpr;
|
|
465
|
+
ctx.scale(dpr, dpr);
|
|
466
|
+
};
|
|
467
|
+
const pattern = (rotation = 0): ImagePatternObject => ({
|
|
468
|
+
image: canvas,
|
|
469
|
+
repeat: "repeat",
|
|
470
|
+
rotation,
|
|
471
|
+
scaleX: 1 / dpr,
|
|
472
|
+
scaleY: 1 / dpr,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
if (kind === "dotted") {
|
|
476
|
+
size(6, 6);
|
|
477
|
+
// Slightly larger dots at 0.7 — the vertical fade + areaStyle opacity temper
|
|
478
|
+
// them, so at the old 0.5/r0.6 they washed out (especially on shorter areas).
|
|
479
|
+
ctx.fillStyle = withAlpha(color, 0.7);
|
|
480
|
+
ctx.beginPath();
|
|
481
|
+
ctx.arc(3, 3, 0.85, 0, Math.PI * 2);
|
|
482
|
+
ctx.fill();
|
|
483
|
+
return pattern();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (kind === "lines" || kind === "stripe") {
|
|
487
|
+
// Vertical 1px line every 5px, rotated 45° by the pattern transform.
|
|
488
|
+
size(5, 5);
|
|
489
|
+
ctx.strokeStyle = withAlpha(color, 0.3);
|
|
490
|
+
ctx.lineWidth = 1;
|
|
491
|
+
ctx.beginPath();
|
|
492
|
+
ctx.moveTo(2.5, -1);
|
|
493
|
+
ctx.lineTo(2.5, 6);
|
|
494
|
+
ctx.stroke();
|
|
495
|
+
return pattern(-Math.PI / 4);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// hatched: bold two-tone stripes leaning ~20°, echoing the Recharts
|
|
499
|
+
// gradient-edged stripe fill.
|
|
500
|
+
size(20, 20);
|
|
501
|
+
ctx.fillStyle = withAlpha(color, 0.06);
|
|
502
|
+
ctx.fillRect(0, 0, 10, 20);
|
|
503
|
+
ctx.fillStyle = withAlpha(color, 0.22);
|
|
504
|
+
ctx.fillRect(10, 0, 10, 20);
|
|
505
|
+
return pattern((20 * Math.PI) / 180);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Canvas can't express "multi-stop color horizontally × alpha fade vertically"
|
|
509
|
+
// as one gradient, so multi-color fills composite the two on an offscreen canvas
|
|
510
|
+
// sized to the chart: paint the horizontal color run, then mask it with a
|
|
511
|
+
// vertical alpha ramp via destination-in. Regenerated on resize (patterns anchor
|
|
512
|
+
// to the renderer's origin at natural pixel size).
|
|
513
|
+
function gradientFillTexture(
|
|
514
|
+
slots: string[],
|
|
515
|
+
width: number,
|
|
516
|
+
height: number,
|
|
517
|
+
reverse: boolean,
|
|
518
|
+
): HTMLCanvasElement | null {
|
|
519
|
+
if (typeof document === "undefined" || width < 1 || height < 1) return null;
|
|
520
|
+
|
|
521
|
+
const canvas = document.createElement("canvas");
|
|
522
|
+
canvas.width = Math.ceil(width);
|
|
523
|
+
canvas.height = Math.ceil(height);
|
|
524
|
+
const ctx = canvas.getContext("2d");
|
|
525
|
+
if (!ctx) return null;
|
|
526
|
+
|
|
527
|
+
const colors = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
|
528
|
+
slots.forEach((color, i) => colors.addColorStop(i / (slots.length - 1), color));
|
|
529
|
+
ctx.fillStyle = colors;
|
|
530
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
531
|
+
|
|
532
|
+
const fade = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
|
533
|
+
fade.addColorStop(0, `rgba(0, 0, 0, ${reverse ? 0 : 0.1})`);
|
|
534
|
+
fade.addColorStop(1, `rgba(0, 0, 0, ${reverse ? 0.1 : 0})`);
|
|
535
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
536
|
+
ctx.fillStyle = fade;
|
|
537
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
538
|
+
|
|
539
|
+
return canvas;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// A pattern fill (dotted/lines/hatched) faded vertically — opaque at the top
|
|
543
|
+
// (near the line), transparent toward the baseline — so it reads like the
|
|
544
|
+
// gradient variant instead of a flat wall of pattern. The tiling pattern can't
|
|
545
|
+
// itself carry an alpha ramp, so bake it into a plot-sized texture: tile the
|
|
546
|
+
// pattern (reusing patternFill's tile + rotation), then mask it with a vertical
|
|
547
|
+
// alpha ramp via destination-in.
|
|
548
|
+
function patternFadeTexture(
|
|
549
|
+
kind: "dotted" | "lines" | "hatched",
|
|
550
|
+
color: string,
|
|
551
|
+
width: number,
|
|
552
|
+
height: number,
|
|
553
|
+
): HTMLCanvasElement | null {
|
|
554
|
+
const patternObj = patternFill(kind, color);
|
|
555
|
+
if (!patternObj || typeof document === "undefined" || width < 1 || height < 1) return null;
|
|
556
|
+
const tile = patternObj.image;
|
|
557
|
+
if (!(tile instanceof HTMLCanvasElement)) return null;
|
|
558
|
+
const rotation = patternObj.rotation ?? 0;
|
|
559
|
+
const tileScale = patternObj.scaleX ?? 1; // patternFill draws the tile at dpr; 1/dpr scales it back
|
|
560
|
+
|
|
561
|
+
const w = Math.ceil(width);
|
|
562
|
+
const h = Math.ceil(height);
|
|
563
|
+
const canvas = document.createElement("canvas");
|
|
564
|
+
canvas.width = w;
|
|
565
|
+
canvas.height = h;
|
|
566
|
+
const ctx = canvas.getContext("2d");
|
|
567
|
+
if (!ctx) return null;
|
|
568
|
+
|
|
569
|
+
const pat = ctx.createPattern(tile, "repeat");
|
|
570
|
+
if (!pat) return null;
|
|
571
|
+
// Replicate the ECharts ImagePattern transform (rotate the tiling, scale the
|
|
572
|
+
// dpr tile back to CSS size) so the baked texture matches the plain pattern.
|
|
573
|
+
if (typeof pat.setTransform === "function") {
|
|
574
|
+
const m = new DOMMatrix();
|
|
575
|
+
m.rotateSelf((rotation * 180) / Math.PI);
|
|
576
|
+
m.scaleSelf(tileScale, tileScale);
|
|
577
|
+
pat.setTransform(m);
|
|
578
|
+
}
|
|
579
|
+
ctx.fillStyle = pat;
|
|
580
|
+
ctx.fillRect(0, 0, w, h);
|
|
581
|
+
|
|
582
|
+
const fade = ctx.createLinearGradient(0, 0, 0, h);
|
|
583
|
+
fade.addColorStop(0, "rgba(0, 0, 0, 1)"); // top: keep the pattern
|
|
584
|
+
fade.addColorStop(1, "rgba(0, 0, 0, 0)"); // baseline: fade it out
|
|
585
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
586
|
+
ctx.fillStyle = fade;
|
|
587
|
+
ctx.fillRect(0, 0, w, h);
|
|
588
|
+
|
|
589
|
+
return canvas;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Resolves the area fill for a variant into an ECharts color value. `size` is the
|
|
593
|
+
// full renderer size, used to bake 2D gradients for multi-color series.
|
|
594
|
+
function fillPaint(
|
|
595
|
+
variant: AreaVariant,
|
|
596
|
+
showUnselected: boolean,
|
|
597
|
+
slots: string[],
|
|
598
|
+
size: { width: number; height: number },
|
|
599
|
+
): string | echarts.graphic.LinearGradient | ImagePatternObject {
|
|
600
|
+
const base = slots[0] ?? "rgba(120, 120, 120, 1)";
|
|
601
|
+
const multi = slots.length > 1;
|
|
602
|
+
|
|
603
|
+
// "none" — stroke only, no fill (even when unselected).
|
|
604
|
+
if (variant === "none") return "transparent";
|
|
605
|
+
|
|
606
|
+
// A non-selected area in a clickable chart recedes as a 45° stripe texture.
|
|
607
|
+
if (showUnselected) {
|
|
608
|
+
return patternFill("stripe", base) ?? withAlpha(base, 0.1);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
switch (variant) {
|
|
612
|
+
case "gradient":
|
|
613
|
+
case "gradient-reverse": {
|
|
614
|
+
const reverse = variant === "gradient-reverse";
|
|
615
|
+
if (multi) {
|
|
616
|
+
const texture = gradientFillTexture(slots, size.width, size.height, reverse);
|
|
617
|
+
if (texture) return { image: texture, repeat: "no-repeat" };
|
|
618
|
+
}
|
|
619
|
+
return new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
620
|
+
{ offset: 0, color: withAlpha(base, reverse ? 0 : 0.1) },
|
|
621
|
+
{ offset: 1, color: withAlpha(base, reverse ? 0.1 : 0) },
|
|
622
|
+
]);
|
|
623
|
+
}
|
|
624
|
+
case "solid": {
|
|
625
|
+
// Uniform alpha, so the horizontal color run survives as one gradient.
|
|
626
|
+
if (multi) {
|
|
627
|
+
return new echarts.graphic.LinearGradient(
|
|
628
|
+
0,
|
|
629
|
+
0,
|
|
630
|
+
1,
|
|
631
|
+
0,
|
|
632
|
+
slots.map((color, i) => ({
|
|
633
|
+
offset: i / (slots.length - 1),
|
|
634
|
+
color: withAlpha(color, 0.1),
|
|
635
|
+
})),
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
return withAlpha(base, 0.1);
|
|
639
|
+
}
|
|
640
|
+
case "dotted":
|
|
641
|
+
case "lines":
|
|
642
|
+
case "hatched": {
|
|
643
|
+
// Faded by default (opaque near the line, transparent at the baseline).
|
|
644
|
+
const texture = patternFadeTexture(variant, base, size.width, size.height);
|
|
645
|
+
if (texture) return { image: texture, repeat: "no-repeat" };
|
|
646
|
+
return patternFill(variant, base) ?? withAlpha(base, 0.1);
|
|
647
|
+
}
|
|
648
|
+
default:
|
|
649
|
+
return withAlpha(base, 0.1);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
654
|
+
// Curve mapping — linear → straight, step → step:"end", everything else → smooth.
|
|
655
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
656
|
+
|
|
657
|
+
function curveConfig(curveType: CurveType): { smooth: boolean; step: "middle" | false } {
|
|
658
|
+
// Recharts "step" is d3's curveStep: the transition happens at the MIDPOINT
|
|
659
|
+
// between points, so each dot sits centered on its plateau.
|
|
660
|
+
if (curveType === "step") return { smooth: false, step: "middle" };
|
|
661
|
+
if (curveType === "linear") return { smooth: false, step: false };
|
|
662
|
+
return { smooth: true, step: false };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
666
|
+
// Selection opacities (§4.3) — dims a series only when another one is selected.
|
|
667
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
668
|
+
|
|
669
|
+
function getOpacity(selected: string | null, key: string) {
|
|
670
|
+
if (selected === null || selected === key) return { fill: 0.8, stroke: 1, dot: 1 };
|
|
671
|
+
return { fill: 0.1, stroke: 0.3, dot: 0.3 };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
675
|
+
// Loading skeleton helpers
|
|
676
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
677
|
+
|
|
678
|
+
// Skeleton data as a smooth random walk in a comfortable band — reads like a
|
|
679
|
+
// resting chart instead of raw noise spikes.
|
|
680
|
+
function getLoadingData(points: number): number[] {
|
|
681
|
+
const rows: number[] = [];
|
|
682
|
+
let value = 30 + Math.random() * 20;
|
|
683
|
+
for (let i = 0; i < points; i++) {
|
|
684
|
+
value = Math.min(58, Math.max(16, value + (Math.random() - 0.5) * 16));
|
|
685
|
+
rows.push(Math.round(value));
|
|
686
|
+
}
|
|
687
|
+
return rows;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Gradient stops forming a hard clip window around `center`: full `peak` alpha
|
|
691
|
+
// inside, zero outside, with a small feather so the edge isn't aliased.
|
|
692
|
+
// `center` may run outside [0, 1] so the window fully enters and exits the frame.
|
|
693
|
+
function shimmerWindowStops(center: number, color: string, peak: number) {
|
|
694
|
+
const half = LOADING_SHIMMER_BAND;
|
|
695
|
+
const feather = LOADING_SHIMMER_FEATHER;
|
|
696
|
+
|
|
697
|
+
const alphaAt = (x: number) => {
|
|
698
|
+
const dist = Math.abs(x - center);
|
|
699
|
+
if (dist <= half - feather) return peak;
|
|
700
|
+
if (dist >= half) return 0;
|
|
701
|
+
// Sine-eased falloff — a linear ramp still reads as a hard cut.
|
|
702
|
+
return peak * Math.sin(((1 - (dist - (half - feather)) / feather) * Math.PI) / 2);
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const offsets = [
|
|
706
|
+
0,
|
|
707
|
+
center - half,
|
|
708
|
+
center - half + feather,
|
|
709
|
+
center,
|
|
710
|
+
center + half - feather,
|
|
711
|
+
center + half,
|
|
712
|
+
1,
|
|
713
|
+
]
|
|
714
|
+
.filter((x) => x >= 0 && x <= 1)
|
|
715
|
+
.sort((a, b) => a - b);
|
|
716
|
+
|
|
717
|
+
const stops: { offset: number; color: string }[] = [];
|
|
718
|
+
for (const offset of offsets) {
|
|
719
|
+
if (stops.length === 0 || offset - stops[stops.length - 1].offset > 1e-4) {
|
|
720
|
+
stops.push({ offset, color: withAlpha(color, alphaAt(offset)) });
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return stops;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// The `__buffer-` prefix marks the dashed forecast overlay of a buffer area; it
|
|
727
|
+
// carries the SAME key's value, so the tooltip recovers the key from it (see
|
|
728
|
+
// createTooltipFormatter). Every other `__`-prefixed series (mini chart, loading
|
|
729
|
+
// skeleton, hover-reveal base) is truly internal and never surfaces.
|
|
730
|
+
const BUFFER_PREFIX = "__buffer-";
|
|
731
|
+
// The fill-only patch under a buffer area's dashed tail (the main area's fill
|
|
732
|
+
// stops one point early). Internal, so the tooltip drops it.
|
|
733
|
+
const BUFFERFILL_PREFIX = "__bufferfill-";
|
|
734
|
+
// The `__reveal-` prefix marks the muted base layer of a hover-reveal area — see
|
|
735
|
+
// buildAreaSeries. Internal, so the tooltip drops it like the mini/loading rows.
|
|
736
|
+
const REVEAL_PREFIX = "__reveal-";
|
|
737
|
+
|
|
738
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
739
|
+
// Option builders — pure functions from a snapshot context to ECharts option
|
|
740
|
+
// fragments. The component reads its refs and renderer size ONCE per build into
|
|
741
|
+
// this context; nothing below touches React state or the chart instance, so
|
|
742
|
+
// each fragment can be reasoned about (and tested) in isolation.
|
|
743
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
744
|
+
|
|
745
|
+
type OptionBuildContext = {
|
|
746
|
+
data: Record<string, unknown>[];
|
|
747
|
+
config: ChartConfig;
|
|
748
|
+
areas: AreaSeriesConfig[];
|
|
749
|
+
seriesKeys: string[];
|
|
750
|
+
curveType: CurveType;
|
|
751
|
+
isStacked: boolean;
|
|
752
|
+
isExpanded: boolean;
|
|
753
|
+
selectedDataKey: string | null;
|
|
754
|
+
hasSelection: boolean;
|
|
755
|
+
showGrid: boolean;
|
|
756
|
+
xAxisSlot: XAxisSlot;
|
|
757
|
+
yAxisSlot: YAxisSlot;
|
|
758
|
+
tooltipSlot: TooltipSlot;
|
|
759
|
+
legendSlot: LegendSlot;
|
|
760
|
+
isLoading: boolean;
|
|
761
|
+
loadingData: () => number[];
|
|
762
|
+
showBrush: boolean;
|
|
763
|
+
brushHeight: number;
|
|
764
|
+
enableHoverHighlight: boolean;
|
|
765
|
+
enableHoverReveal: boolean; // hover colors each area up to the pointer, mutes the rest
|
|
766
|
+
revealIndex: number | null; // pointer's x-index while revealing (null = idle → chart looks normal)
|
|
767
|
+
revealSink: Record<string, unknown[]>; // buildAreaSeries writes each area's full per-datum points here for the hover handler
|
|
768
|
+
resolved: ResolvedColors;
|
|
769
|
+
rendererSize: { width: number; height: number }; // 2D gradient textures bake at renderer size
|
|
770
|
+
categories: string[];
|
|
771
|
+
brushRange: BrushRange; // zoom window carried through rebuilds
|
|
772
|
+
getHoveredKey: () => string | null; // read per tooltip render — hover never repushes the option
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// Grid insets plus the footer band reserved for the brush. ECharts 6 contains
|
|
776
|
+
// axis labels automatically (the legacy `containLabel` flag now only triggers a
|
|
777
|
+
// deprecation warning).
|
|
778
|
+
function buildChartLayout({ legendSlot, xAxisSlot, showBrush, brushHeight }: OptionBuildContext): {
|
|
779
|
+
grid: GridComponentOption;
|
|
780
|
+
brushBottom: number;
|
|
781
|
+
} {
|
|
782
|
+
const legendTop = legendSlot.present && legendSlot.verticalAlign === "top";
|
|
783
|
+
const legendBottom = legendSlot.present && legendSlot.verticalAlign === "bottom";
|
|
784
|
+
// Clearance covers the x-axis labels plus the same breathing room the
|
|
785
|
+
// Recharts twin leaves between them and the brush. An x-axis TITLE renders
|
|
786
|
+
// below the labels (nameGap), so it needs its own band above the brush frame.
|
|
787
|
+
const brushGap = showBrush ? brushHeight + 30 + (xAxisSlot.label ? 22 : 0) : 0;
|
|
788
|
+
|
|
789
|
+
return {
|
|
790
|
+
grid: {
|
|
791
|
+
left: 8,
|
|
792
|
+
right: 8,
|
|
793
|
+
top: legendTop ? 42 : 16,
|
|
794
|
+
bottom: 8 + brushGap + (legendBottom ? 34 : 0),
|
|
795
|
+
},
|
|
796
|
+
brushBottom: legendBottom ? 34 : 6,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function buildMainAxes(ctx: OptionBuildContext): { xAxis: XAxisOption; yAxis: YAxisOption } {
|
|
801
|
+
const { xAxisSlot, yAxisSlot, showGrid, isLoading, isExpanded, categories, loadingData } = ctx;
|
|
802
|
+
const { tokens } = ctx.resolved;
|
|
803
|
+
|
|
804
|
+
const axisLabelColor = tokens.mutedForeground;
|
|
805
|
+
const splitLineColor = withAlpha(tokens.border, GRID_LINE_OPACITY);
|
|
806
|
+
// Gridline gray as an opaque color — see flattenColor.
|
|
807
|
+
const tickDotColor = flattenColor(splitLineColor, tokens.background);
|
|
808
|
+
|
|
809
|
+
const xTickFormatter = xAxisSlot.tickFormatter;
|
|
810
|
+
const yTickFormatter = yAxisSlot.tickFormatter;
|
|
811
|
+
|
|
812
|
+
const xAxis: XAxisOption = {
|
|
813
|
+
type: "category",
|
|
814
|
+
boundaryGap: false,
|
|
815
|
+
show: true,
|
|
816
|
+
data: isLoading ? loadingData().map((_, i) => i) : categories,
|
|
817
|
+
// Axis title — same size/color as the tick labels, pushed clear of them.
|
|
818
|
+
name: isLoading ? undefined : xAxisSlot.label,
|
|
819
|
+
nameLocation: "middle",
|
|
820
|
+
nameGap: 30,
|
|
821
|
+
nameTextStyle: { color: axisLabelColor, fontSize: 10 },
|
|
822
|
+
axisLine: { show: false },
|
|
823
|
+
// Tick DOTS: a near-zero-length tick whose round caps form a true circle,
|
|
824
|
+
// in the gridline gray (flattened opaque so the caps don't stack).
|
|
825
|
+
axisTick: {
|
|
826
|
+
show: !isLoading && xAxisSlot.present && !xAxisSlot.hideDots,
|
|
827
|
+
// Ticks default to the BOUNDARY between categories, which on a boundaryGap
|
|
828
|
+
// axis drops the dot in the gap instead of under its label. A no-op here
|
|
829
|
+
// (boundaryGap is false), kept for parity with the bar/composed charts.
|
|
830
|
+
alignWithLabel: true,
|
|
831
|
+
length: 0.5,
|
|
832
|
+
lineStyle: { color: tickDotColor, width: 3, cap: "round" },
|
|
833
|
+
},
|
|
834
|
+
splitLine: { show: false },
|
|
835
|
+
axisLabel: {
|
|
836
|
+
show: !isLoading && xAxisSlot.present,
|
|
837
|
+
color: axisLabelColor,
|
|
838
|
+
fontSize: 10,
|
|
839
|
+
margin: 8,
|
|
840
|
+
formatter: xTickFormatter
|
|
841
|
+
? (value: string, index: number) => xTickFormatter(value, index)
|
|
842
|
+
: undefined,
|
|
843
|
+
},
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
// An ECharts axis with `show: false` hides its splitLines too, but Recharts'
|
|
847
|
+
// <CartesianGrid> draws with or without a visible <YAxis>. Keep the axis on
|
|
848
|
+
// whenever <Grid/> is present and gate the LABELS on <YAxis/> instead.
|
|
849
|
+
const yAxis: YAxisOption = {
|
|
850
|
+
type: "value",
|
|
851
|
+
show: yAxisSlot.present || showGrid,
|
|
852
|
+
max: isExpanded ? 1 : undefined,
|
|
853
|
+
// Axis title — rendered rotated alongside the tick labels, same styling.
|
|
854
|
+
name: isLoading ? undefined : yAxisSlot.label,
|
|
855
|
+
nameLocation: "middle",
|
|
856
|
+
nameGap: 38,
|
|
857
|
+
nameTextStyle: { color: axisLabelColor, fontSize: 10 },
|
|
858
|
+
axisLine: { show: false },
|
|
859
|
+
// Same tick dots as the x-axis, beside each value label. No alignWithLabel
|
|
860
|
+
// here: ECharts types it on the CATEGORY axis only, and a value axis already
|
|
861
|
+
// puts its ticks on the labels.
|
|
862
|
+
axisTick: {
|
|
863
|
+
show: yAxisSlot.present && !isLoading && !yAxisSlot.hideDots,
|
|
864
|
+
length: 0.5,
|
|
865
|
+
lineStyle: { color: tickDotColor, width: 3, cap: "round" },
|
|
866
|
+
},
|
|
867
|
+
splitLine: {
|
|
868
|
+
// Hidden while loading — the skeleton floats on a clean canvas.
|
|
869
|
+
show: showGrid && !isLoading,
|
|
870
|
+
lineStyle: { color: splitLineColor, type: [3, 3] as [number, number], width: 1 },
|
|
871
|
+
},
|
|
872
|
+
axisLabel: {
|
|
873
|
+
// Hidden while loading — skeleton values are meaningless, and the
|
|
874
|
+
// Recharts YAxis unmounts during loading too.
|
|
875
|
+
show: yAxisSlot.present && !isLoading,
|
|
876
|
+
color: axisLabelColor,
|
|
877
|
+
fontSize: 10,
|
|
878
|
+
margin: 8,
|
|
879
|
+
formatter: isExpanded
|
|
880
|
+
? (value: number) => `${Math.round(value * 100)}%`
|
|
881
|
+
: yTickFormatter
|
|
882
|
+
? (value: number, index: number) => yTickFormatter(value, index)
|
|
883
|
+
: undefined,
|
|
884
|
+
},
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
return { xAxis, yAxis };
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Tooltip HTML builder, closed over the build context. `getHoveredKey` is read
|
|
891
|
+
// per invocation — ECharts calls the formatter at hover time, and syncing hover
|
|
892
|
+
// through an option push instead would reset the native blur state mid-hover.
|
|
893
|
+
function createTooltipFormatter(ctx: OptionBuildContext) {
|
|
894
|
+
const { config, selectedDataKey, tooltipSlot, getHoveredKey } = ctx;
|
|
895
|
+
|
|
896
|
+
return (params: unknown): string => {
|
|
897
|
+
const rows = Array.isArray(params) ? params : [params];
|
|
898
|
+
if (!rows.length) return "";
|
|
899
|
+
|
|
900
|
+
const first = rows[0] as { axisValue?: string | number; name?: string };
|
|
901
|
+
// Label shows the RAW axis value — matches ChartTooltipContent (no tick formatter).
|
|
902
|
+
const axisValue = first.axisValue ?? first.name ?? "";
|
|
903
|
+
const label = String(axisValue);
|
|
904
|
+
|
|
905
|
+
// Dedupe by effective key: a buffer area contributes both its solid part
|
|
906
|
+
// (id=key) and its dashed overlay (id=`__buffer-{key}`) at the shared
|
|
907
|
+
// second-to-last point. Keep the first non-null value seen per key so the
|
|
908
|
+
// final point (only the overlay has data there) still shows its number.
|
|
909
|
+
const seen = new Set<string>();
|
|
910
|
+
const body = rows
|
|
911
|
+
.map((param) => {
|
|
912
|
+
const p = param as {
|
|
913
|
+
seriesId?: string;
|
|
914
|
+
seriesName?: string;
|
|
915
|
+
value?: number | string | null;
|
|
916
|
+
};
|
|
917
|
+
const rawId = String(p.seriesId ?? "");
|
|
918
|
+
// Map the dashed buffer overlay back onto its series; drop every other
|
|
919
|
+
// internal series (mini chart, loading skeleton, hover-reveal base).
|
|
920
|
+
const key = rawId.startsWith(BUFFER_PREFIX)
|
|
921
|
+
? rawId.slice(BUFFER_PREFIX.length)
|
|
922
|
+
: rawId.startsWith("__")
|
|
923
|
+
? ""
|
|
924
|
+
: (p.seriesId ?? p.seriesName ?? "");
|
|
925
|
+
if (!key) return "";
|
|
926
|
+
// A null value means this series does not reach the hovered x (a buffer
|
|
927
|
+
// area's solid part stops before the last point, a revealed series stops
|
|
928
|
+
// at the cursor) — skip it, letting another row for the key stand in.
|
|
929
|
+
if (p.value === null || p.value === undefined) return "";
|
|
930
|
+
if (seen.has(key)) return "";
|
|
931
|
+
seen.add(key);
|
|
932
|
+
|
|
933
|
+
const item = config[key];
|
|
934
|
+
const colorsCount = item ? getColorsCount(item) : 1;
|
|
935
|
+
const labelText = typeof item?.label === "string" ? item.label : (p.seriesName ?? key);
|
|
936
|
+
const hovered = getHoveredKey();
|
|
937
|
+
const dimmed =
|
|
938
|
+
(selectedDataKey != null && selectedDataKey !== key) ||
|
|
939
|
+
(hovered != null && hovered !== key)
|
|
940
|
+
? " opacity-30"
|
|
941
|
+
: "";
|
|
942
|
+
const value =
|
|
943
|
+
typeof p.value === "number" ? p.value.toLocaleString() : String(p.value ?? "");
|
|
944
|
+
|
|
945
|
+
return tooltipRow({
|
|
946
|
+
indicatorHtml: tooltipIndicatorHtml(key, colorsCount),
|
|
947
|
+
labelText,
|
|
948
|
+
valueText: value,
|
|
949
|
+
dimmed,
|
|
950
|
+
});
|
|
951
|
+
})
|
|
952
|
+
.join("");
|
|
953
|
+
|
|
954
|
+
return tooltipShell({
|
|
955
|
+
label,
|
|
956
|
+
body,
|
|
957
|
+
roundness: tooltipSlot.roundness,
|
|
958
|
+
variant: tooltipSlot.variant,
|
|
959
|
+
});
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function buildTooltipOption(ctx: OptionBuildContext): TooltipComponentOption {
|
|
964
|
+
const { tooltipSlot, isLoading } = ctx;
|
|
965
|
+
const { tokens } = ctx.resolved;
|
|
966
|
+
|
|
967
|
+
return {
|
|
968
|
+
...tooltipBaseOption({
|
|
969
|
+
present: tooltipSlot.present && !isLoading,
|
|
970
|
+
cursor: tooltipSlot.cursor,
|
|
971
|
+
tokens,
|
|
972
|
+
position: tooltipSlot.position,
|
|
973
|
+
axisPointerColor: withAlpha(tokens.border, AXIS_POINTER_OPACITY),
|
|
974
|
+
strokeWidth: STROKE_WIDTH,
|
|
975
|
+
}),
|
|
976
|
+
formatter: createTooltipFormatter(ctx),
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// ── Brush — the evil-brush look, canvas-style: a real mini chart of the full
|
|
981
|
+
// data in a second grid, with a transparent slider dataZoom laid over it. Both
|
|
982
|
+
// zoom entries target only the MAIN x-axis, so the mini chart never filters
|
|
983
|
+
// itself. Only called when `showBrush` is set.
|
|
984
|
+
function buildBrushOption(
|
|
985
|
+
ctx: OptionBuildContext,
|
|
986
|
+
brushBottom: number,
|
|
987
|
+
): {
|
|
988
|
+
miniGrid: GridComponentOption;
|
|
989
|
+
miniXAxis: XAxisOption;
|
|
990
|
+
miniYAxis: YAxisOption;
|
|
991
|
+
miniSeries: LineSeriesOption[];
|
|
992
|
+
dataZoom: DataZoomComponentOption[];
|
|
993
|
+
} {
|
|
994
|
+
const { data, areas, curveType, isStacked, selectedDataKey, brushHeight, categories } = ctx;
|
|
995
|
+
const { tokens } = ctx.resolved;
|
|
996
|
+
|
|
997
|
+
const miniGrid: GridComponentOption = {
|
|
998
|
+
left: 8,
|
|
999
|
+
right: 8,
|
|
1000
|
+
bottom: brushBottom,
|
|
1001
|
+
height: brushHeight,
|
|
1002
|
+
// No visible axes here — opt out of label containment so the mini chart
|
|
1003
|
+
// spans the full brush frame.
|
|
1004
|
+
outerBoundsMode: "none",
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
const miniXAxis: XAxisOption = {
|
|
1008
|
+
type: "category",
|
|
1009
|
+
gridIndex: 1,
|
|
1010
|
+
boundaryGap: false,
|
|
1011
|
+
show: false,
|
|
1012
|
+
data: categories,
|
|
1013
|
+
axisPointer: { show: false },
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
const miniYAxis: YAxisOption = { type: "value", gridIndex: 1, show: false };
|
|
1017
|
+
|
|
1018
|
+
const miniSeries: LineSeriesOption[] = areas.map((area) => {
|
|
1019
|
+
const key = area.dataKey;
|
|
1020
|
+
const base = (ctx.resolved.series[key] ?? [])[0] ?? "rgba(120, 120, 120, 1)";
|
|
1021
|
+
const curve = curveConfig(area.curveType ?? curveType);
|
|
1022
|
+
|
|
1023
|
+
// The mini chart mirrors the click selection: unselected series recede
|
|
1024
|
+
// by the same ratios as the main plot.
|
|
1025
|
+
// Dim ratios normalize against the base opacities (stroke 1, fill 0.8).
|
|
1026
|
+
const opacity = getOpacity(selectedDataKey, key);
|
|
1027
|
+
const strokeDim = opacity.stroke;
|
|
1028
|
+
const fillDim = opacity.fill / 0.8;
|
|
1029
|
+
|
|
1030
|
+
return {
|
|
1031
|
+
id: `__mini-${key}`,
|
|
1032
|
+
type: "line",
|
|
1033
|
+
xAxisIndex: 1,
|
|
1034
|
+
yAxisIndex: 1,
|
|
1035
|
+
data: data.map((row) => Number(row[key]) || 0),
|
|
1036
|
+
stack: isStacked ? "__mini-total" : undefined,
|
|
1037
|
+
smooth: curve.smooth,
|
|
1038
|
+
step: curve.step,
|
|
1039
|
+
connectNulls: area.connectNulls,
|
|
1040
|
+
silent: true,
|
|
1041
|
+
showSymbol: false,
|
|
1042
|
+
emphasis: { disabled: true },
|
|
1043
|
+
tooltip: { show: false },
|
|
1044
|
+
lineStyle: { color: base, width: 1, opacity: BRUSH_STROKE_OPACITY * strokeDim },
|
|
1045
|
+
areaStyle: {
|
|
1046
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
1047
|
+
{ offset: 0, color: withAlpha(base, BRUSH_FILL_OPACITY * fillDim) },
|
|
1048
|
+
{ offset: 1, color: withAlpha(base, 0) },
|
|
1049
|
+
]),
|
|
1050
|
+
},
|
|
1051
|
+
z: 0,
|
|
1052
|
+
};
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
const dataZoom = buildBrushDataZoom({
|
|
1056
|
+
brushBottom,
|
|
1057
|
+
brushHeight,
|
|
1058
|
+
brushRange: ctx.brushRange,
|
|
1059
|
+
fillerColor: withAlpha(tokens.foreground, BRUSH_FILLER_OPACITY),
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
return { miniGrid, miniXAxis, miniYAxis, miniSeries, dataZoom };
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Loading skeleton — ONE gray wave regardless of declared areas (Recharts
|
|
1066
|
+
// parity: its skeleton is a single LoadingArea), swept by the shimmer rAF.
|
|
1067
|
+
function buildLoadingOption(
|
|
1068
|
+
ctx: OptionBuildContext,
|
|
1069
|
+
frame: { grid: GridComponentOption; xAxis: XAxisOption; yAxis: YAxisOption },
|
|
1070
|
+
): EChartsOption {
|
|
1071
|
+
const { tokens } = ctx.resolved;
|
|
1072
|
+
const curve = curveConfig(ctx.curveType);
|
|
1073
|
+
|
|
1074
|
+
return {
|
|
1075
|
+
animation: false,
|
|
1076
|
+
grid: frame.grid,
|
|
1077
|
+
xAxis: frame.xAxis,
|
|
1078
|
+
yAxis: frame.yAxis,
|
|
1079
|
+
tooltip: { show: false },
|
|
1080
|
+
series: [
|
|
1081
|
+
{
|
|
1082
|
+
id: "__loading",
|
|
1083
|
+
type: "line",
|
|
1084
|
+
data: ctx.loadingData(),
|
|
1085
|
+
smooth: curve.smooth,
|
|
1086
|
+
step: curve.step,
|
|
1087
|
+
showSymbol: false,
|
|
1088
|
+
silent: true,
|
|
1089
|
+
// Invisible until the first shimmer tick positions the clip window.
|
|
1090
|
+
lineStyle: { color: withAlpha(tokens.foreground, 0), width: 1 },
|
|
1091
|
+
areaStyle: { color: withAlpha(tokens.foreground, 0) },
|
|
1092
|
+
z: 1,
|
|
1093
|
+
},
|
|
1094
|
+
],
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function buildAreaSeries(ctx: OptionBuildContext): LineSeriesOption[] {
|
|
1099
|
+
const {
|
|
1100
|
+
data,
|
|
1101
|
+
config,
|
|
1102
|
+
areas,
|
|
1103
|
+
seriesKeys,
|
|
1104
|
+
curveType,
|
|
1105
|
+
isStacked,
|
|
1106
|
+
isExpanded,
|
|
1107
|
+
selectedDataKey,
|
|
1108
|
+
hasSelection,
|
|
1109
|
+
enableHoverHighlight,
|
|
1110
|
+
enableHoverReveal,
|
|
1111
|
+
revealIndex,
|
|
1112
|
+
revealSink,
|
|
1113
|
+
resolved,
|
|
1114
|
+
rendererSize,
|
|
1115
|
+
} = ctx;
|
|
1116
|
+
|
|
1117
|
+
// Optional per-row normalization for the expanded (100%) stack.
|
|
1118
|
+
const rowTotals = isExpanded
|
|
1119
|
+
? data.map((row) => seriesKeys.reduce((sum, key) => sum + (Number(row[key]) || 0), 0))
|
|
1120
|
+
: [];
|
|
1121
|
+
|
|
1122
|
+
return areas.flatMap((area): LineSeriesOption[] => {
|
|
1123
|
+
const key = area.dataKey;
|
|
1124
|
+
const slots = resolved.series[key] ?? ["rgba(120, 120, 120, 1)"];
|
|
1125
|
+
const paint = seriesPaint(slots);
|
|
1126
|
+
const isSelected = selectedDataKey === key;
|
|
1127
|
+
const showUnselected = hasSelection && !isSelected;
|
|
1128
|
+
const opacity = getOpacity(selectedDataKey, key);
|
|
1129
|
+
const curve = curveConfig(area.curveType ?? curveType);
|
|
1130
|
+
|
|
1131
|
+
const values = data.map((row, i) => {
|
|
1132
|
+
const value = Number(row[key]) || 0;
|
|
1133
|
+
if (!isExpanded) return value;
|
|
1134
|
+
const total = rowTotals[i];
|
|
1135
|
+
return total ? value / total : 0;
|
|
1136
|
+
});
|
|
1137
|
+
const n = values.length;
|
|
1138
|
+
// Hover-reveal is a root-level mode and owns the whole area rendering, so it
|
|
1139
|
+
// takes precedence over a per-area buffer tail when both are set.
|
|
1140
|
+
const reveal = enableHoverReveal;
|
|
1141
|
+
const buffer = !reveal && area.enableBufferLine && n >= 2;
|
|
1142
|
+
const revealActive = reveal && revealIndex !== null;
|
|
1143
|
+
|
|
1144
|
+
const restingDot = dotStyle(area.dotVariant, paint, resolved.tokens.background);
|
|
1145
|
+
const activeDot = dotStyle(area.activeDotVariant, paint, resolved.tokens.background);
|
|
1146
|
+
const restingVisible = area.dotVariant !== "none";
|
|
1147
|
+
const dotOpacity = opacity.dot;
|
|
1148
|
+
const multiColor = slots.length > 1;
|
|
1149
|
+
|
|
1150
|
+
// The reveal truncates the line to the cursor, which would COMPRESS a
|
|
1151
|
+
// bbox-relative stroke gradient into the shorter span — misaligning it from
|
|
1152
|
+
// the plot-anchored fill texture and the index-sampled dots. Anchor the
|
|
1153
|
+
// stroke to the plot in absolute pixels so every x keeps its own color.
|
|
1154
|
+
const strokePaint =
|
|
1155
|
+
reveal && multiColor
|
|
1156
|
+
? new echarts.graphic.LinearGradient(
|
|
1157
|
+
8,
|
|
1158
|
+
0,
|
|
1159
|
+
Math.max(rendererSize.width - 8, 9),
|
|
1160
|
+
0,
|
|
1161
|
+
slots.map((color, i) => ({ offset: i / (slots.length - 1), color })),
|
|
1162
|
+
true,
|
|
1163
|
+
)
|
|
1164
|
+
: paint;
|
|
1165
|
+
|
|
1166
|
+
// Turn a value list into ECharts data — attaching per-datum symbol colors
|
|
1167
|
+
// for multi-color areas (like the Recharts dots; the line/fill keep the full
|
|
1168
|
+
// gradient), and passing `null` gaps through so a buffer area's two parts
|
|
1169
|
+
// each draw only their own segment.
|
|
1170
|
+
type AreaPoint =
|
|
1171
|
+
| number
|
|
1172
|
+
| null
|
|
1173
|
+
| {
|
|
1174
|
+
value: number;
|
|
1175
|
+
itemStyle: Record<string, unknown>;
|
|
1176
|
+
emphasis: { itemStyle: Record<string, unknown> };
|
|
1177
|
+
};
|
|
1178
|
+
const toPoints = (vals: (number | null)[]): AreaPoint[] =>
|
|
1179
|
+
!multiColor
|
|
1180
|
+
? vals
|
|
1181
|
+
: vals.map((value, i): AreaPoint => {
|
|
1182
|
+
if (value === null) return null;
|
|
1183
|
+
const t = vals.length > 1 ? i / (vals.length - 1) : 0;
|
|
1184
|
+
const pointColor = sampleGradient(slots, t);
|
|
1185
|
+
return {
|
|
1186
|
+
value,
|
|
1187
|
+
itemStyle: {
|
|
1188
|
+
...dotItemStyle(
|
|
1189
|
+
restingVisible ? area.dotVariant : area.activeDotVariant,
|
|
1190
|
+
pointColor,
|
|
1191
|
+
resolved.tokens.background,
|
|
1192
|
+
),
|
|
1193
|
+
opacity: dotOpacity,
|
|
1194
|
+
},
|
|
1195
|
+
emphasis: {
|
|
1196
|
+
itemStyle: {
|
|
1197
|
+
...dotItemStyle(
|
|
1198
|
+
area.activeDotVariant === "none" ? "default" : area.activeDotVariant,
|
|
1199
|
+
pointColor,
|
|
1200
|
+
resolved.tokens.background,
|
|
1201
|
+
),
|
|
1202
|
+
opacity: 1,
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
};
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
// Buffer area: the solid MAIN part drops the last point (its final segment —
|
|
1209
|
+
// both fill and stroke — becomes the dashed, fill-less overlay); the overlay
|
|
1210
|
+
// carries only the last two points. Reveal instead TRUNCATES the real series
|
|
1211
|
+
// at the cursor's x-index (points beyond it null'd), so its line + fill stop
|
|
1212
|
+
// there and the muted base layer shows through past it. When idle
|
|
1213
|
+
// (revealIndex null) the real series carries its full data — the chart looks
|
|
1214
|
+
// completely normal.
|
|
1215
|
+
// Snapshot the FULL per-datum points (with the multi-color dot itemStyle) so
|
|
1216
|
+
// the reveal hover handler can slice them without losing each dot's sampled
|
|
1217
|
+
// gradient color — plain values would fall back to the default palette.
|
|
1218
|
+
if (reveal) revealSink[key] = toPoints(values);
|
|
1219
|
+
|
|
1220
|
+
const mainValues: (number | null)[] = buffer
|
|
1221
|
+
? values.map((v, i) => (i === n - 1 ? null : v))
|
|
1222
|
+
: revealActive
|
|
1223
|
+
? sliceToNull(values, revealIndex as number)
|
|
1224
|
+
: values;
|
|
1225
|
+
|
|
1226
|
+
// A buffer area keeps its body solid and dashes only the tail overlay, so
|
|
1227
|
+
// the main stroke is always solid regardless of strokeVariant (matches the
|
|
1228
|
+
// Recharts twin, which suppresses the base dasharray while buffering).
|
|
1229
|
+
const mainDash: "solid" | [number, number] =
|
|
1230
|
+
buffer || area.strokeVariant === "solid" ? "solid" : ([3, 3] as [number, number]);
|
|
1231
|
+
|
|
1232
|
+
const z = isSelected ? 3 : hasSelection ? 1 : 2;
|
|
1233
|
+
|
|
1234
|
+
const mainSeries: LineSeriesOption = {
|
|
1235
|
+
id: key,
|
|
1236
|
+
name: typeof config[key]?.label === "string" ? config[key]?.label : key,
|
|
1237
|
+
type: "line",
|
|
1238
|
+
data: toPoints(mainValues),
|
|
1239
|
+
stack: isStacked ? "total" : undefined,
|
|
1240
|
+
smooth: curve.smooth,
|
|
1241
|
+
step: curve.step,
|
|
1242
|
+
connectNulls: area.connectNulls,
|
|
1243
|
+
cursor: area.isClickable ? "pointer" : "default",
|
|
1244
|
+
// By default ECharts only fires mouse events on the symbols — this makes
|
|
1245
|
+
// the line AND the filled area clickable, like the Recharts <Area>.
|
|
1246
|
+
// (`true` covers both; the deprecated `triggerLineEvent` did the same.)
|
|
1247
|
+
triggerEvent: area.isClickable,
|
|
1248
|
+
showSymbol: restingVisible,
|
|
1249
|
+
symbol: "circle",
|
|
1250
|
+
symbolSize: restingVisible ? restingDot.size : activeDot.size,
|
|
1251
|
+
z,
|
|
1252
|
+
lineStyle: {
|
|
1253
|
+
color: strokePaint,
|
|
1254
|
+
width: area.strokeWidth,
|
|
1255
|
+
opacity: opacity.stroke,
|
|
1256
|
+
type: mainDash,
|
|
1257
|
+
dashOffset: 0,
|
|
1258
|
+
},
|
|
1259
|
+
itemStyle: multiColor
|
|
1260
|
+
? { opacity: dotOpacity }
|
|
1261
|
+
: {
|
|
1262
|
+
...(restingVisible ? restingDot.itemStyle : activeDot.itemStyle),
|
|
1263
|
+
opacity: dotOpacity,
|
|
1264
|
+
},
|
|
1265
|
+
areaStyle: {
|
|
1266
|
+
color: fillPaint(area.variant, showUnselected, slots, rendererSize),
|
|
1267
|
+
opacity: opacity.fill,
|
|
1268
|
+
},
|
|
1269
|
+
emphasis: {
|
|
1270
|
+
// focus "series" blurs every other series in this grid while one is
|
|
1271
|
+
// hovered — the hover twin of the click selection. Suppressed entirely
|
|
1272
|
+
// while a series is click-selected: the selection dim owns the canvas,
|
|
1273
|
+
// so hover highlighting stops until the selection clears (the option
|
|
1274
|
+
// rebuilds on selection change, making this a build-time conditional).
|
|
1275
|
+
// Reveal owns the hover visual, so native focus-blur stands down when it
|
|
1276
|
+
// is on (they must not blend).
|
|
1277
|
+
focus: enableHoverHighlight && !enableHoverReveal && !hasSelection ? "series" : "none",
|
|
1278
|
+
scale: restingVisible ? activeDot.size / Math.max(restingDot.size, 1) : 1,
|
|
1279
|
+
...(multiColor ? {} : { itemStyle: { ...activeDot.itemStyle, opacity: 1 } }),
|
|
1280
|
+
},
|
|
1281
|
+
// Blur styling mirrors the click-selection dim (fill 0.1 / stroke 0.3 / dot 0.3).
|
|
1282
|
+
blur: {
|
|
1283
|
+
lineStyle: { opacity: 0.3 },
|
|
1284
|
+
areaStyle: { opacity: 0.1 },
|
|
1285
|
+
itemStyle: { opacity: 0.3 },
|
|
1286
|
+
},
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// Hover-reveal: a muted gray BASE layer of the FULL series sits one z below
|
|
1290
|
+
// the real one. It is invisible while idle (opacity 0 → the chart looks
|
|
1291
|
+
// normal) and fades in only while hovering, so the region PAST the cursor —
|
|
1292
|
+
// where the truncated real series has stopped — shows as neutral gray.
|
|
1293
|
+
if (reveal) {
|
|
1294
|
+
const muted = resolved.tokens.mutedForeground;
|
|
1295
|
+
const revealBase: LineSeriesOption = {
|
|
1296
|
+
id: `${REVEAL_PREFIX}${key}`,
|
|
1297
|
+
type: "line",
|
|
1298
|
+
// Only the region FROM the cursor onward (null before it), so the gray
|
|
1299
|
+
// never sits under the colored part — the two meet exactly at the
|
|
1300
|
+
// pointer and their colors can't mix.
|
|
1301
|
+
data: revealActive ? sliceFrom(values, revealIndex as number) : values,
|
|
1302
|
+
// Its OWN stack, not "total" — a second series in the real stack would
|
|
1303
|
+
// double every key's contribution (broken geometry). This mirror stack
|
|
1304
|
+
// reproduces the same cumulative shape in a separate layer.
|
|
1305
|
+
stack: isStacked ? "__reveal-total" : undefined,
|
|
1306
|
+
smooth: curve.smooth,
|
|
1307
|
+
step: curve.step,
|
|
1308
|
+
connectNulls: false,
|
|
1309
|
+
silent: true,
|
|
1310
|
+
showSymbol: false,
|
|
1311
|
+
symbol: "circle",
|
|
1312
|
+
z: z - 1,
|
|
1313
|
+
// Neutral gray, NO fill, SAME dash pattern as the colored line.
|
|
1314
|
+
lineStyle: {
|
|
1315
|
+
color: muted,
|
|
1316
|
+
width: area.strokeWidth,
|
|
1317
|
+
type: mainDash,
|
|
1318
|
+
opacity: revealActive ? 0.3 : 0,
|
|
1319
|
+
},
|
|
1320
|
+
emphasis: { disabled: true },
|
|
1321
|
+
blur: { lineStyle: { opacity: revealActive ? 0.3 : 0 } },
|
|
1322
|
+
tooltip: { show: false },
|
|
1323
|
+
};
|
|
1324
|
+
return [revealBase, mainSeries];
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
if (!buffer) return [mainSeries];
|
|
1328
|
+
|
|
1329
|
+
// Dashed forecast overlay — draws ONLY the last segment's stroke, with NO
|
|
1330
|
+
// fill (matching the line twin's fill-less buffer). Silent, so it never
|
|
1331
|
+
// intercepts clicks/hover; it still feeds the axis tooltip (silent series
|
|
1332
|
+
// are aggregated by axis), which is why the last point keeps its number.
|
|
1333
|
+
const bufferValues: (number | null)[] = values.map((v, i) => (i >= n - 2 ? v : null));
|
|
1334
|
+
const bufferSeries: LineSeriesOption = {
|
|
1335
|
+
id: `${BUFFER_PREFIX}${key}`,
|
|
1336
|
+
type: "line",
|
|
1337
|
+
data: toPoints(bufferValues),
|
|
1338
|
+
// Own mirror stack — a second series in "total" would double the last
|
|
1339
|
+
// points' stacked height (buffer drawn too high). Same values in the same
|
|
1340
|
+
// order give the identical cumulative height, so the dash lines up.
|
|
1341
|
+
stack: isStacked ? "__buffer-total" : undefined,
|
|
1342
|
+
smooth: curve.smooth,
|
|
1343
|
+
step: curve.step,
|
|
1344
|
+
connectNulls: true,
|
|
1345
|
+
silent: true,
|
|
1346
|
+
showSymbol: restingVisible,
|
|
1347
|
+
symbol: "circle",
|
|
1348
|
+
symbolSize: restingVisible ? restingDot.size : activeDot.size,
|
|
1349
|
+
z,
|
|
1350
|
+
lineStyle: {
|
|
1351
|
+
color: paint,
|
|
1352
|
+
width: area.strokeWidth,
|
|
1353
|
+
opacity: opacity.stroke,
|
|
1354
|
+
type: BUFFER_DASH,
|
|
1355
|
+
},
|
|
1356
|
+
itemStyle: multiColor
|
|
1357
|
+
? { opacity: dotOpacity }
|
|
1358
|
+
: {
|
|
1359
|
+
...(restingVisible ? restingDot.itemStyle : activeDot.itemStyle),
|
|
1360
|
+
opacity: dotOpacity,
|
|
1361
|
+
},
|
|
1362
|
+
// The dashed tail is a separate silent series, so focus:"series" on its
|
|
1363
|
+
// parent would blur it apart from the area it belongs to. The root
|
|
1364
|
+
// dispatch-links this id (companionIdsByKey) so it focuses WITH its parent;
|
|
1365
|
+
// these styles give it the parent's look while focused and the
|
|
1366
|
+
// click-selection dim while another series is hovered.
|
|
1367
|
+
emphasis: {
|
|
1368
|
+
focus: "none",
|
|
1369
|
+
scale: false,
|
|
1370
|
+
lineStyle: { opacity: opacity.stroke },
|
|
1371
|
+
itemStyle: { opacity: dotOpacity },
|
|
1372
|
+
},
|
|
1373
|
+
blur: { lineStyle: { opacity: 0.3 }, itemStyle: { opacity: 0.3 } },
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// Fill patch — the main area drops its last point (so the tail stroke can be
|
|
1377
|
+
// the dashed overlay), which also removes the FILL under that last segment.
|
|
1378
|
+
// This fill-only layer (no stroke, no dots) fills just that segment so the
|
|
1379
|
+
// area reads as full under the dashed tail. Its OWN mirror stack keeps the
|
|
1380
|
+
// stacked geometry right — a second series in "total" would double the last
|
|
1381
|
+
// points' cumulative height (same reason as the reveal base + mini chart).
|
|
1382
|
+
const bufferFillSeries: LineSeriesOption = {
|
|
1383
|
+
id: `${BUFFERFILL_PREFIX}${key}`,
|
|
1384
|
+
type: "line",
|
|
1385
|
+
data: toPoints(bufferValues),
|
|
1386
|
+
stack: isStacked ? "__bufferfill-total" : undefined,
|
|
1387
|
+
smooth: curve.smooth,
|
|
1388
|
+
step: curve.step,
|
|
1389
|
+
connectNulls: true,
|
|
1390
|
+
silent: true,
|
|
1391
|
+
showSymbol: false,
|
|
1392
|
+
z: z - 1,
|
|
1393
|
+
lineStyle: { opacity: 0 },
|
|
1394
|
+
areaStyle: {
|
|
1395
|
+
color: fillPaint(area.variant, showUnselected, slots, rendererSize),
|
|
1396
|
+
opacity: opacity.fill,
|
|
1397
|
+
},
|
|
1398
|
+
emphasis: { disabled: true },
|
|
1399
|
+
blur: { areaStyle: { opacity: 0.1 } },
|
|
1400
|
+
tooltip: { show: false },
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
return [mainSeries, bufferSeries, bufferFillSeries];
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Copy a value list with everything AFTER `idx` nulled — the hover-reveal cut:
|
|
1408
|
+
// the colored real series keeps its data up to the cursor and drops the rest, so
|
|
1409
|
+
// (with connectNulls false) its line and fill stop dead at the pointer.
|
|
1410
|
+
function sliceToNull<T>(vals: readonly T[], idx: number): (T | null)[] {
|
|
1411
|
+
return vals.map((v, i) => (i > idx ? null : v));
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// Copy a value list with everything BEFORE `idx` nulled — the reveal's gray tail.
|
|
1415
|
+
// The muted base keeps only the region from the cursor onward, so it never sits
|
|
1416
|
+
// under the colored part; both include `idx` so they meet at the pointer.
|
|
1417
|
+
// Generic so it preserves per-datum point objects (multi-color dot itemStyle).
|
|
1418
|
+
function sliceFrom<T>(vals: readonly T[], idx: number): (T | null)[] {
|
|
1419
|
+
return vals.map((v, i) => (i < idx ? null : v));
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Per-series PLOTTED top value per category index — expanded normalization and
|
|
1423
|
+
// stack accumulation applied — so pointer hit-testing can reason in data space.
|
|
1424
|
+
function computePlottedTops(ctx: OptionBuildContext): Record<string, number[]> {
|
|
1425
|
+
const { data, areas, seriesKeys, isStacked, isExpanded } = ctx;
|
|
1426
|
+
const rowTotals = isExpanded
|
|
1427
|
+
? data.map((row) => seriesKeys.reduce((sum, key) => sum + (Number(row[key]) || 0), 0))
|
|
1428
|
+
: [];
|
|
1429
|
+
const running = new Array(data.length).fill(0);
|
|
1430
|
+
const tops: Record<string, number[]> = {};
|
|
1431
|
+
for (const area of areas) {
|
|
1432
|
+
const key = area.dataKey;
|
|
1433
|
+
tops[key] = data.map((row, i) => {
|
|
1434
|
+
let value = Number(row[key]) || 0;
|
|
1435
|
+
if (isExpanded) value = rowTotals[i] ? value / rowTotals[i] : 0;
|
|
1436
|
+
return isStacked ? (running[i] += value) : value;
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
return tops;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// Overlapping area polygons all contain the same pixel, so ECharts' native hit
|
|
1443
|
+
// test lands on whichever series drew topmost — not the band the user SEES.
|
|
1444
|
+
// Resolve the intended series geometrically: a plotted line within grab
|
|
1445
|
+
// distance of the pointer wins outright; otherwise the point belongs to the
|
|
1446
|
+
// nearest line ABOVE it (the boundary of the band the pointer is inside).
|
|
1447
|
+
// Returns null when the pointer is outside the grid or above every line.
|
|
1448
|
+
function resolveAreaAtPixel(
|
|
1449
|
+
chart: EChartsInstance,
|
|
1450
|
+
tops: Record<string, number[]>,
|
|
1451
|
+
keys: string[],
|
|
1452
|
+
x: number,
|
|
1453
|
+
y: number,
|
|
1454
|
+
): string | null {
|
|
1455
|
+
if (keys.length < 2) return null;
|
|
1456
|
+
if (!chart.containPixel({ gridIndex: 0 }, [x, y])) return null;
|
|
1457
|
+
const [rawIndex] = chart.convertFromPixel({ gridIndex: 0 }, [x, y]);
|
|
1458
|
+
const index = Math.round(rawIndex);
|
|
1459
|
+
|
|
1460
|
+
let nearest: string | null = null;
|
|
1461
|
+
let nearestDist = Infinity;
|
|
1462
|
+
let above: string | null = null;
|
|
1463
|
+
let abovePixelY = -Infinity;
|
|
1464
|
+
for (const key of keys) {
|
|
1465
|
+
const value = tops[key]?.[index];
|
|
1466
|
+
if (value === undefined) continue;
|
|
1467
|
+
const pixelY = chart.convertToPixel({ gridIndex: 0 }, [index, value])[1];
|
|
1468
|
+
const dist = Math.abs(pixelY - y);
|
|
1469
|
+
if (dist < nearestDist) {
|
|
1470
|
+
nearestDist = dist;
|
|
1471
|
+
nearest = key;
|
|
1472
|
+
}
|
|
1473
|
+
// Pixel y grows downward: a line above the pointer has the larger pixelY
|
|
1474
|
+
// among those ≤ the pointer's.
|
|
1475
|
+
if (pixelY <= y && pixelY > abovePixelY) {
|
|
1476
|
+
abovePixelY = pixelY;
|
|
1477
|
+
above = key;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return nearestDist <= 10 ? nearest : above;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1484
|
+
// Live imperative state — everything the ECharts event handlers, rAF loops, and
|
|
1485
|
+
// theme/resize repushes read or write OUTSIDE the React render cycle, grouped in
|
|
1486
|
+
// one ref-stable object so the whole imperative surface is visible at a glance.
|
|
1487
|
+
// None of it is render output, which is exactly why it is not React state.
|
|
1488
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1489
|
+
|
|
1490
|
+
type LiveState = {
|
|
1491
|
+
resolved: ResolvedColors | null; // colors read off the live DOM — feeds builds and rAF loops
|
|
1492
|
+
hoveredKey: string | null; // tooltip's view of hover — the legend's twin lives in React state
|
|
1493
|
+
hasRevealed: boolean; // the intro draw-in already played on this chart instance
|
|
1494
|
+
revealEndsAt: number; // performance.now() timestamp when the entrance settles
|
|
1495
|
+
loadingRows: number[] | null; // skeleton data, lazily rolled and re-rolled per shimmer sweep
|
|
1496
|
+
categories: string[]; // x labels of the last build, for the brush label pills
|
|
1497
|
+
dataLength: number; // row count, for the datazoom index math
|
|
1498
|
+
plottedTops: Record<string, number[]>; // per-series plotted line value per index, for pointer hit-testing
|
|
1499
|
+
seriesKeyByIndex: (string | undefined)[]; // built series order → key, so a polygon click's seriesIndex recovers its key past interleaved buffer/reveal/mini series
|
|
1500
|
+
companionIdsByKey: Map<string, string[]>; // per-key silent companion series ids (buffer tail, reveal base) — highlighted/downplayed with their parent
|
|
1501
|
+
revealIndex: number | null; // hover-reveal pointer x-index (null = idle); read by builds and the reveal hover handler
|
|
1502
|
+
revealValues: Record<string, unknown[]>; // per-area FULL per-datum points (with dot itemStyle), sliced to the cursor on hover without a rebuild
|
|
1503
|
+
brushRange: BrushRange; // live zoom window — carried through every rebuild
|
|
1504
|
+
brushGeom: BrushGeometry | null; // brush footer layout of the last build
|
|
1505
|
+
brushOverlay: BrushOverlayElements | null; // zrender elements, owned by syncBrushOverlay
|
|
1506
|
+
brushHover: { inside: boolean; left: boolean; right: boolean };
|
|
1507
|
+
// Latest callbacks/flags for the imperative ECharts event handlers.
|
|
1508
|
+
handlers: {
|
|
1509
|
+
onBrushChange?: (range: { startIndex: number; endIndex: number }) => void;
|
|
1510
|
+
onSelectionChange?: (key: string | null) => void;
|
|
1511
|
+
clickableKeys: Set<string>;
|
|
1512
|
+
selectedDataKey: string | null;
|
|
1513
|
+
brushFormatLabel?: (value: string, index: number) => string;
|
|
1514
|
+
seriesKeys: string[];
|
|
1515
|
+
enableHoverHighlight: boolean;
|
|
1516
|
+
enableHoverReveal: boolean;
|
|
1517
|
+
};
|
|
1518
|
+
// Update-style re-push for paths that bypass React entirely (theme flips,
|
|
1519
|
+
// resizes) — set by the sync effect.
|
|
1520
|
+
repush: () => void;
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1524
|
+
// Component
|
|
1525
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* Apache ECharts port of the EvilCharts area chart, exposing a compound-as-config
|
|
1529
|
+
* API so its JSX reads identically to the Recharts twin. The root owns the data,
|
|
1530
|
+
* config, selection state, loading skeleton, intro reveal, and optional zoom
|
|
1531
|
+
* brush; every visual part — `<Area>`, `<XAxis>`, `<YAxis>`, `<Grid>`,
|
|
1532
|
+
* `<Tooltip>`, `<Legend>` — is composed as a declarative child that renders
|
|
1533
|
+
* nothing. The root walks those children by reference and drives a single
|
|
1534
|
+
* imperative ECharts instance. Fully self-contained: its only dependencies are
|
|
1535
|
+
* `react` and `echarts`.
|
|
1536
|
+
*/
|
|
1537
|
+
export function EChartsAreaChart<TData extends Record<string, unknown>>({
|
|
1538
|
+
data,
|
|
1539
|
+
config,
|
|
1540
|
+
renderer = DEFAULT_ECHARTS_RENDERER,
|
|
1541
|
+
xDataKey,
|
|
1542
|
+
className,
|
|
1543
|
+
curveType = "linear",
|
|
1544
|
+
stackType = "default",
|
|
1545
|
+
animation = true,
|
|
1546
|
+
animationType = "left-to-right",
|
|
1547
|
+
enableHoverHighlight = false,
|
|
1548
|
+
enableHoverReveal = false,
|
|
1549
|
+
defaultSelectedDataKey = null,
|
|
1550
|
+
selectedDataKey: selectedDataKeyProp,
|
|
1551
|
+
onSelectionChange,
|
|
1552
|
+
isLoading = false,
|
|
1553
|
+
loadingPoints = LOADING_DEFAULT_POINTS,
|
|
1554
|
+
chartOptions,
|
|
1555
|
+
children,
|
|
1556
|
+
}: EChartsAreaChartProps<TData>) {
|
|
1557
|
+
const rawId = useId();
|
|
1558
|
+
const chartId = `chart-${rawId.replace(/:/g, "")}`;
|
|
1559
|
+
|
|
1560
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
1561
|
+
const mountRef = useRef<HTMLDivElement>(null);
|
|
1562
|
+
const echartsRef = useRef<EChartsInstance | null>(null);
|
|
1563
|
+
|
|
1564
|
+
// The single imperative surface (see LiveState). `resolved` lives here rather
|
|
1565
|
+
// than in state: as state it forced an extra render pass and an effect whose
|
|
1566
|
+
// only job was to trigger the option push — the "chain of computations"
|
|
1567
|
+
// react.dev/learn/you-might-not-need-an-effect warns about. The object
|
|
1568
|
+
// identity is stable for the component's lifetime.
|
|
1569
|
+
const live = useRef<LiveState>({
|
|
1570
|
+
resolved: null,
|
|
1571
|
+
hoveredKey: null,
|
|
1572
|
+
hasRevealed: false,
|
|
1573
|
+
revealEndsAt: 0,
|
|
1574
|
+
loadingRows: null,
|
|
1575
|
+
categories: [],
|
|
1576
|
+
dataLength: 0,
|
|
1577
|
+
plottedTops: {},
|
|
1578
|
+
seriesKeyByIndex: [],
|
|
1579
|
+
companionIdsByKey: new Map<string, string[]>(),
|
|
1580
|
+
revealIndex: null,
|
|
1581
|
+
revealValues: {},
|
|
1582
|
+
brushRange: { start: 0, end: 100 },
|
|
1583
|
+
brushGeom: null,
|
|
1584
|
+
brushOverlay: null,
|
|
1585
|
+
brushHover: { inside: false, left: false, right: false },
|
|
1586
|
+
handlers: {
|
|
1587
|
+
onBrushChange: undefined, // set per-render from the <Brush> child's onChange
|
|
1588
|
+
onSelectionChange,
|
|
1589
|
+
clickableKeys: new Set<string>(),
|
|
1590
|
+
selectedDataKey: defaultSelectedDataKey,
|
|
1591
|
+
brushFormatLabel: undefined, // set per-render from the <Brush> child's formatLabel
|
|
1592
|
+
seriesKeys: [],
|
|
1593
|
+
enableHoverHighlight,
|
|
1594
|
+
enableHoverReveal,
|
|
1595
|
+
},
|
|
1596
|
+
repush: () => {},
|
|
1597
|
+
}).current;
|
|
1598
|
+
|
|
1599
|
+
// Skeleton rows roll lazily on first use — an impure useRef initializer would
|
|
1600
|
+
// re-roll Math.random() on every render.
|
|
1601
|
+
const loadingData = useCallback(
|
|
1602
|
+
() => (live.loadingRows ??= getLoadingData(loadingPoints)),
|
|
1603
|
+
[live, loadingPoints],
|
|
1604
|
+
);
|
|
1605
|
+
const shouldReduceMotion = useReducedMotion();
|
|
1606
|
+
|
|
1607
|
+
// Selection is controlled when the `selectedDataKey` prop is provided;
|
|
1608
|
+
// otherwise the internal state (seeded by defaultSelectedDataKey) drives it.
|
|
1609
|
+
const [internalSelectedKey, setSelectedDataKey] = useState<string | null>(defaultSelectedDataKey);
|
|
1610
|
+
const selectedDataKey =
|
|
1611
|
+
selectedDataKeyProp !== undefined ? selectedDataKeyProp : internalSelectedKey;
|
|
1612
|
+
|
|
1613
|
+
// Hover-highlight mirrors into the legend (React state) and tooltip
|
|
1614
|
+
// (live.hoveredKey — its formatter runs on every hover, and pushing an option
|
|
1615
|
+
// to sync it would reset ECharts' blur state mid-hover).
|
|
1616
|
+
const [hoveredDataKey, setHoveredDataKey] = useState<string | null>(null);
|
|
1617
|
+
|
|
1618
|
+
// ── Declarative config, collected from children by reference ─────────────────
|
|
1619
|
+
const collected = useMemo(() => collectConfig(children), [children]);
|
|
1620
|
+
const {
|
|
1621
|
+
areas,
|
|
1622
|
+
xAxis: xAxisSlot,
|
|
1623
|
+
yAxis: yAxisSlot,
|
|
1624
|
+
showGrid,
|
|
1625
|
+
tooltip: tooltipSlot,
|
|
1626
|
+
legend: legendSlot,
|
|
1627
|
+
brush: brushSlot,
|
|
1628
|
+
} = collected;
|
|
1629
|
+
// Brush is a <Brush> child now (not props): presence turns it on, its props
|
|
1630
|
+
// carry height/formatLabel/onChange.
|
|
1631
|
+
const showBrush = brushSlot.present;
|
|
1632
|
+
const brushHeight = brushSlot.height ?? 56;
|
|
1633
|
+
|
|
1634
|
+
const seriesKeys = useMemo(() => areas.map((area) => area.dataKey), [areas]);
|
|
1635
|
+
|
|
1636
|
+
// x category key: <XAxis dataKey> → root xDataKey → first data column no <Area> claims.
|
|
1637
|
+
const xCategoryKey = useMemo(() => {
|
|
1638
|
+
if (xAxisSlot.dataKey) return xAxisSlot.dataKey;
|
|
1639
|
+
if (xDataKey) return xDataKey as string;
|
|
1640
|
+
const firstRow = data[0];
|
|
1641
|
+
if (firstRow) {
|
|
1642
|
+
const claimed = new Set(seriesKeys);
|
|
1643
|
+
const found = Object.keys(firstRow).find((key) => !claimed.has(key));
|
|
1644
|
+
if (found) return found;
|
|
1645
|
+
}
|
|
1646
|
+
return "";
|
|
1647
|
+
}, [xAxisSlot.dataKey, xDataKey, data, seriesKeys]);
|
|
1648
|
+
|
|
1649
|
+
// The intro draw-in follows the first area's setting, falling back to the root default.
|
|
1650
|
+
const effectiveAnimation = areas[0]?.animationType ?? animationType;
|
|
1651
|
+
|
|
1652
|
+
const css = useMemo(() => buildChartCss(chartId, config), [chartId, config]);
|
|
1653
|
+
|
|
1654
|
+
const hasSelection = selectedDataKey !== null;
|
|
1655
|
+
const isExpanded = stackType === "expanded";
|
|
1656
|
+
const isStacked = stackType === "stacked" || isExpanded;
|
|
1657
|
+
|
|
1658
|
+
// Which series may be clicked to toggle selection (consulted by the click handler).
|
|
1659
|
+
const clickableKeys = useMemo(
|
|
1660
|
+
() => new Set(areas.filter((area) => area.isClickable).map((area) => area.dataKey)),
|
|
1661
|
+
[areas],
|
|
1662
|
+
);
|
|
1663
|
+
|
|
1664
|
+
// Refresh the handlers' snapshot of the latest callbacks/flags every render.
|
|
1665
|
+
live.handlers = {
|
|
1666
|
+
onBrushChange: brushSlot.onChange,
|
|
1667
|
+
onSelectionChange,
|
|
1668
|
+
clickableKeys,
|
|
1669
|
+
selectedDataKey,
|
|
1670
|
+
brushFormatLabel: brushSlot.formatLabel,
|
|
1671
|
+
seriesKeys,
|
|
1672
|
+
enableHoverHighlight,
|
|
1673
|
+
enableHoverReveal,
|
|
1674
|
+
};
|
|
1675
|
+
live.dataLength = data.length;
|
|
1676
|
+
|
|
1677
|
+
// Reads the CURRENT selection through live.handlers so the identity stays
|
|
1678
|
+
// stable for the init effect's click closure, and stays correct when the
|
|
1679
|
+
// selection is controlled from outside.
|
|
1680
|
+
const toggleSelection = useCallback(
|
|
1681
|
+
(key: string) => {
|
|
1682
|
+
const next = live.handlers.selectedDataKey === key ? null : key;
|
|
1683
|
+
// Making a selection hands the canvas to the selection dim — clear any
|
|
1684
|
+
// active hover highlight at that moment so it doesn't linger in the
|
|
1685
|
+
// legend/tooltip or fight the notMerge rebuild that follows.
|
|
1686
|
+
if (next !== null && live.hoveredKey !== null) {
|
|
1687
|
+
const previous = live.hoveredKey;
|
|
1688
|
+
live.hoveredKey = null;
|
|
1689
|
+
setHoveredDataKey(null);
|
|
1690
|
+
echartsRef.current?.dispatchAction({
|
|
1691
|
+
type: "downplay",
|
|
1692
|
+
seriesIndex: live.handlers.seriesKeys.indexOf(previous),
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
setSelectedDataKey(next);
|
|
1696
|
+
live.handlers.onSelectionChange?.(next);
|
|
1697
|
+
},
|
|
1698
|
+
[live],
|
|
1699
|
+
);
|
|
1700
|
+
|
|
1701
|
+
// Reposition the brush overlays from the live refs — safe to call from drag
|
|
1702
|
+
// events, hover tracking, and pushes alike, since it never touches setOption.
|
|
1703
|
+
const syncBrushOverlayNow = useCallback(() => {
|
|
1704
|
+
const chart = echartsRef.current;
|
|
1705
|
+
if (!chart) return;
|
|
1706
|
+
|
|
1707
|
+
const geom = live.brushGeom;
|
|
1708
|
+
const tokens = live.resolved?.tokens;
|
|
1709
|
+
if (!geom || !tokens) {
|
|
1710
|
+
syncBrushOverlay(chart, live, null);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
const range = live.brushRange;
|
|
1715
|
+
const categories = live.categories;
|
|
1716
|
+
const format = live.handlers.brushFormatLabel;
|
|
1717
|
+
const lastIndex = Math.max(categories.length - 1, 0);
|
|
1718
|
+
const startIndex = Math.round((range.start / 100) * lastIndex);
|
|
1719
|
+
const endIndex = Math.round((range.end / 100) * lastIndex);
|
|
1720
|
+
const labels =
|
|
1721
|
+
format && categories.length
|
|
1722
|
+
? {
|
|
1723
|
+
start: format(categories[startIndex] ?? "", startIndex),
|
|
1724
|
+
end: format(categories[endIndex] ?? "", endIndex),
|
|
1725
|
+
}
|
|
1726
|
+
: null;
|
|
1727
|
+
|
|
1728
|
+
syncBrushOverlay(chart, live, {
|
|
1729
|
+
range,
|
|
1730
|
+
geom,
|
|
1731
|
+
size: { width: chart.getWidth(), height: chart.getHeight() },
|
|
1732
|
+
tokens,
|
|
1733
|
+
labels,
|
|
1734
|
+
showLabels: live.brushHover.inside,
|
|
1735
|
+
hover: live.brushHover,
|
|
1736
|
+
});
|
|
1737
|
+
}, [live]);
|
|
1738
|
+
|
|
1739
|
+
// ── Option builder ─────────────────────────────────────────────────────────
|
|
1740
|
+
// Thin orchestrator over the pure builders above: snapshot the imperative
|
|
1741
|
+
// surface (refs, renderer size) into an OptionBuildContext, then assemble.
|
|
1742
|
+
const buildOption = useCallback((): EChartsOption => {
|
|
1743
|
+
const resolved = live.resolved;
|
|
1744
|
+
if (!resolved) return {};
|
|
1745
|
+
|
|
1746
|
+
const categories = data.map((row) => String(row[xCategoryKey]));
|
|
1747
|
+
live.categories = categories;
|
|
1748
|
+
|
|
1749
|
+
// buildAreaSeries fills this with each area's full per-datum points (with the
|
|
1750
|
+
// multi-color dot itemStyle) so the reveal hover handler slices real data.
|
|
1751
|
+
const revealSink: Record<string, unknown[]> = {};
|
|
1752
|
+
|
|
1753
|
+
const ctx: OptionBuildContext = {
|
|
1754
|
+
data,
|
|
1755
|
+
config,
|
|
1756
|
+
areas,
|
|
1757
|
+
seriesKeys,
|
|
1758
|
+
curveType,
|
|
1759
|
+
isStacked,
|
|
1760
|
+
isExpanded,
|
|
1761
|
+
selectedDataKey,
|
|
1762
|
+
hasSelection,
|
|
1763
|
+
showGrid,
|
|
1764
|
+
xAxisSlot,
|
|
1765
|
+
yAxisSlot,
|
|
1766
|
+
tooltipSlot,
|
|
1767
|
+
legendSlot,
|
|
1768
|
+
isLoading,
|
|
1769
|
+
loadingData,
|
|
1770
|
+
showBrush,
|
|
1771
|
+
brushHeight,
|
|
1772
|
+
enableHoverHighlight,
|
|
1773
|
+
enableHoverReveal,
|
|
1774
|
+
revealIndex: live.revealIndex,
|
|
1775
|
+
resolved,
|
|
1776
|
+
rendererSize: {
|
|
1777
|
+
width: echartsRef.current?.getWidth() ?? mountRef.current?.clientWidth ?? 0,
|
|
1778
|
+
height: echartsRef.current?.getHeight() ?? mountRef.current?.clientHeight ?? 0,
|
|
1779
|
+
},
|
|
1780
|
+
categories,
|
|
1781
|
+
brushRange: live.brushRange,
|
|
1782
|
+
getHoveredKey: () => live.hoveredKey,
|
|
1783
|
+
revealSink,
|
|
1784
|
+
};
|
|
1785
|
+
|
|
1786
|
+
live.plottedTops = computePlottedTops(ctx);
|
|
1787
|
+
|
|
1788
|
+
const { grid, brushBottom } = buildChartLayout(ctx);
|
|
1789
|
+
live.brushGeom = showBrush ? { bottom: brushBottom, height: brushHeight } : null;
|
|
1790
|
+
|
|
1791
|
+
const { xAxis, yAxis } = buildMainAxes(ctx);
|
|
1792
|
+
|
|
1793
|
+
if (isLoading) return buildLoadingOption(ctx, { grid, xAxis, yAxis });
|
|
1794
|
+
|
|
1795
|
+
const brush = showBrush ? buildBrushOption(ctx, brushBottom) : null;
|
|
1796
|
+
|
|
1797
|
+
const series = [...buildAreaSeries(ctx), ...(brush?.miniSeries ?? [])];
|
|
1798
|
+
// buildAreaSeries has now filled revealSink with each area's full per-datum
|
|
1799
|
+
// points — hand them to the hover handler for slicing.
|
|
1800
|
+
if (enableHoverReveal) live.revealValues = revealSink;
|
|
1801
|
+
// Record the exact series order so an area-polygon click (which reports only
|
|
1802
|
+
// a seriesIndex) can recover its key — buffer/reveal/mini/loading series
|
|
1803
|
+
// break the "index === key position" shortcut, so map each index to its id.
|
|
1804
|
+
live.seriesKeyByIndex = series.map((s) => {
|
|
1805
|
+
const id = String(s.id ?? "");
|
|
1806
|
+
return id && !id.startsWith("__") ? id : undefined;
|
|
1807
|
+
});
|
|
1808
|
+
// Map each key to its silent companion series ids (buffer tail, hover-reveal
|
|
1809
|
+
// base), mirroring exactly what buildAreaSeries emits — the hover handlers
|
|
1810
|
+
// highlight/downplay these with the parent so focus:"series" never strands
|
|
1811
|
+
// an area's own forecast tail or muted reveal base apart from it.
|
|
1812
|
+
const companionIdsByKey = new Map<string, string[]>();
|
|
1813
|
+
for (const area of areas) {
|
|
1814
|
+
const ids: string[] = [];
|
|
1815
|
+
if (area.enableBufferLine && data.length >= 2) {
|
|
1816
|
+
ids.push(`${BUFFER_PREFIX}${area.dataKey}`, `${BUFFERFILL_PREFIX}${area.dataKey}`);
|
|
1817
|
+
}
|
|
1818
|
+
if (enableHoverReveal) ids.push(`${REVEAL_PREFIX}${area.dataKey}`);
|
|
1819
|
+
if (ids.length) companionIdsByKey.set(area.dataKey, ids);
|
|
1820
|
+
}
|
|
1821
|
+
live.companionIdsByKey = companionIdsByKey;
|
|
1822
|
+
|
|
1823
|
+
return {
|
|
1824
|
+
animation: false,
|
|
1825
|
+
grid: brush ? [grid, brush.miniGrid] : grid,
|
|
1826
|
+
xAxis: brush ? [xAxis, brush.miniXAxis] : xAxis,
|
|
1827
|
+
yAxis: brush ? [yAxis, brush.miniYAxis] : yAxis,
|
|
1828
|
+
tooltip: buildTooltipOption(ctx),
|
|
1829
|
+
dataZoom: brush?.dataZoom,
|
|
1830
|
+
series,
|
|
1831
|
+
};
|
|
1832
|
+
}, [
|
|
1833
|
+
live,
|
|
1834
|
+
data,
|
|
1835
|
+
config,
|
|
1836
|
+
areas,
|
|
1837
|
+
seriesKeys,
|
|
1838
|
+
xCategoryKey,
|
|
1839
|
+
curveType,
|
|
1840
|
+
isStacked,
|
|
1841
|
+
isExpanded,
|
|
1842
|
+
selectedDataKey,
|
|
1843
|
+
hasSelection,
|
|
1844
|
+
showGrid,
|
|
1845
|
+
xAxisSlot,
|
|
1846
|
+
yAxisSlot,
|
|
1847
|
+
tooltipSlot,
|
|
1848
|
+
legendSlot,
|
|
1849
|
+
isLoading,
|
|
1850
|
+
loadingData,
|
|
1851
|
+
showBrush,
|
|
1852
|
+
brushHeight,
|
|
1853
|
+
enableHoverHighlight,
|
|
1854
|
+
enableHoverReveal,
|
|
1855
|
+
]);
|
|
1856
|
+
|
|
1857
|
+
// ── Init + resize + theme observer (per renderer instance) ──────────────────
|
|
1858
|
+
useEffect(() => {
|
|
1859
|
+
const mount = mountRef.current;
|
|
1860
|
+
const container = containerRef.current;
|
|
1861
|
+
if (!mount || !container) return;
|
|
1862
|
+
|
|
1863
|
+
// Pointer state belongs to the renderer instance. A renderer switch disposes
|
|
1864
|
+
// that surface without emitting mouseout/globalout, so do not carry a hover,
|
|
1865
|
+
// reveal slice, or brush-hover treatment into the replacement instance.
|
|
1866
|
+
// Keep brushRange: the selected zoom window should survive the switch.
|
|
1867
|
+
live.hoveredKey = null;
|
|
1868
|
+
live.revealIndex = null;
|
|
1869
|
+
live.brushHover = { inside: false, left: false, right: false };
|
|
1870
|
+
setHoveredDataKey(null);
|
|
1871
|
+
|
|
1872
|
+
const chart = echarts.init(mount, null, { renderer });
|
|
1873
|
+
echartsRef.current = chart;
|
|
1874
|
+
|
|
1875
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
1876
|
+
// Observers always fire once right after observe(). Repushing on that
|
|
1877
|
+
// no-op fire would land one frame into the intro and stomp the line's
|
|
1878
|
+
// reveal clip — only react when the renderer size actually changed.
|
|
1879
|
+
if (mount.clientWidth === chart.getWidth() && mount.clientHeight === chart.getHeight()) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
chart.resize();
|
|
1883
|
+
// 2D gradient textures are baked at renderer size — rebuild them to fit.
|
|
1884
|
+
live.repush();
|
|
1885
|
+
});
|
|
1886
|
+
resizeObserver.observe(mount);
|
|
1887
|
+
|
|
1888
|
+
// Light/dark flips change no React state — re-resolve and push directly.
|
|
1889
|
+
const themeObserver = new MutationObserver(() => {
|
|
1890
|
+
live.repush();
|
|
1891
|
+
});
|
|
1892
|
+
themeObserver.observe(document.documentElement, {
|
|
1893
|
+
attributes: true,
|
|
1894
|
+
attributeFilter: ["class"],
|
|
1895
|
+
});
|
|
1896
|
+
|
|
1897
|
+
chart.on("click", (params) => {
|
|
1898
|
+
const { clickableKeys: clickable, seriesKeys: keys } = live.handlers;
|
|
1899
|
+
const p = params as {
|
|
1900
|
+
seriesId?: string;
|
|
1901
|
+
seriesIndex?: number;
|
|
1902
|
+
event?: { offsetX?: number; offsetY?: number };
|
|
1903
|
+
};
|
|
1904
|
+
// Symbol clicks carry seriesId; area-polygon clicks (triggerEvent)
|
|
1905
|
+
// only carry seriesIndex — recover the key from the last build's index map,
|
|
1906
|
+
// which accounts for the extra `__buffer-`/`__reveal-`/`__mini-` series
|
|
1907
|
+
// interleaved between the main ones (a raw seriesKeys lookup would miss).
|
|
1908
|
+
let id =
|
|
1909
|
+
p.seriesId ??
|
|
1910
|
+
(typeof p.seriesIndex === "number" ? live.seriesKeyByIndex[p.seriesIndex] : undefined);
|
|
1911
|
+
// Overlapping polygons: the native hit is the topmost series, not the
|
|
1912
|
+
// band the pointer is visually inside — resolve geometrically.
|
|
1913
|
+
if (typeof p.event?.offsetX === "number" && typeof p.event?.offsetY === "number") {
|
|
1914
|
+
const resolved = resolveAreaAtPixel(
|
|
1915
|
+
chart,
|
|
1916
|
+
live.plottedTops,
|
|
1917
|
+
keys,
|
|
1918
|
+
p.event.offsetX,
|
|
1919
|
+
p.event.offsetY,
|
|
1920
|
+
);
|
|
1921
|
+
if (resolved) id = resolved;
|
|
1922
|
+
}
|
|
1923
|
+
if (typeof id === "string" && clickable.has(id)) toggleSelection(id);
|
|
1924
|
+
});
|
|
1925
|
+
|
|
1926
|
+
// Hover-highlight is POINTER-driven, not series-mouseover-driven:
|
|
1927
|
+
// overlapping polygons would pin the native hover on the topmost series and
|
|
1928
|
+
// never re-fire while the pointer moves within it. A zrender mousemove
|
|
1929
|
+
// tracker resolves the visually-hovered band, drives the canvas emphasis
|
|
1930
|
+
// via dispatchAction, and mirrors into the HTML legend (React state) and
|
|
1931
|
+
// tooltip (live.hoveredKey — its formatter runs per pointer move).
|
|
1932
|
+
const applyHoverKey = (key: string | null) => {
|
|
1933
|
+
if (live.hoveredKey === key) return;
|
|
1934
|
+
const previous = live.hoveredKey;
|
|
1935
|
+
live.hoveredKey = key;
|
|
1936
|
+
setHoveredDataKey(key);
|
|
1937
|
+
// Dispatch by seriesId (buffer/reveal series shift the numeric indices), and
|
|
1938
|
+
// link each key's silent companions (buffer tail, reveal base) so
|
|
1939
|
+
// focus:"series" never strands them apart from their parent.
|
|
1940
|
+
if (previous) {
|
|
1941
|
+
chart.dispatchAction({ type: "downplay", seriesId: previous });
|
|
1942
|
+
for (const id of live.companionIdsByKey.get(previous) ?? [])
|
|
1943
|
+
chart.dispatchAction({ type: "downplay", seriesId: id });
|
|
1944
|
+
}
|
|
1945
|
+
if (key) {
|
|
1946
|
+
chart.dispatchAction({ type: "highlight", seriesId: key });
|
|
1947
|
+
for (const id of live.companionIdsByKey.get(key) ?? [])
|
|
1948
|
+
chart.dispatchAction({ type: "highlight", seriesId: id });
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
|
|
1952
|
+
// Hover-reveal: color each area up to the pointer's x-index, mute the rest.
|
|
1953
|
+
// Purely TARGETED series updates (real series data + muted base opacity) — we
|
|
1954
|
+
// NEVER rebuild the whole option on mousemove, which would replay transitions
|
|
1955
|
+
// and fight the tooltip's axis pointer.
|
|
1956
|
+
const pushReveal = (idx: number | null) => {
|
|
1957
|
+
const keys = live.handlers.seriesKeys;
|
|
1958
|
+
const on = idx !== null;
|
|
1959
|
+
chart.setOption(
|
|
1960
|
+
{
|
|
1961
|
+
series: keys.flatMap((key) => [
|
|
1962
|
+
{
|
|
1963
|
+
id: key,
|
|
1964
|
+
data: on
|
|
1965
|
+
? sliceToNull(live.revealValues[key] ?? [], idx)
|
|
1966
|
+
: (live.revealValues[key] ?? []),
|
|
1967
|
+
},
|
|
1968
|
+
{
|
|
1969
|
+
id: `${REVEAL_PREFIX}${key}`,
|
|
1970
|
+
// Gray tail keeps only the region from the cursor onward.
|
|
1971
|
+
data: on
|
|
1972
|
+
? sliceFrom(live.revealValues[key] ?? [], idx)
|
|
1973
|
+
: (live.revealValues[key] ?? []),
|
|
1974
|
+
lineStyle: { opacity: on ? 0.3 : 0 },
|
|
1975
|
+
},
|
|
1976
|
+
]),
|
|
1977
|
+
},
|
|
1978
|
+
// NOT lazy: the highlight dispatched just below re-draws the active dot
|
|
1979
|
+
// the setOption wipes, so the option must be committed first — a queued
|
|
1980
|
+
// (lazy) update would land after the dispatch and erase the dot again.
|
|
1981
|
+
{ silent: true },
|
|
1982
|
+
);
|
|
1983
|
+
// The per-frame setOption above cancels the axis tooltip's transient hover
|
|
1984
|
+
// symbol, so the <ActiveDot> never lands at the cursor. Re-assert it:
|
|
1985
|
+
// highlighting a real series at the cursor index draws its emphasis symbol
|
|
1986
|
+
// (the active dot) even with showSymbol:false; downplay clears it on exit.
|
|
1987
|
+
for (const key of keys) {
|
|
1988
|
+
chart.dispatchAction(
|
|
1989
|
+
on
|
|
1990
|
+
? { type: "highlight", seriesId: key, dataIndex: idx as number }
|
|
1991
|
+
: { type: "downplay", seriesId: key },
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
const applyReveal = (event: { offsetX?: number; offsetY?: number }) => {
|
|
1996
|
+
const len = live.dataLength;
|
|
1997
|
+
if (len < 1) return;
|
|
1998
|
+
const x = event.offsetX ?? -1;
|
|
1999
|
+
const y = event.offsetY ?? -1;
|
|
2000
|
+
if (!chart.containPixel({ gridIndex: 0 }, [x, y])) {
|
|
2001
|
+
clearReveal();
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
const raw = chart.convertFromPixel({ gridIndex: 0 }, [x, y])[0];
|
|
2005
|
+
const idx = Math.max(0, Math.min(len - 1, Math.round(raw)));
|
|
2006
|
+
if (idx === live.revealIndex) return;
|
|
2007
|
+
live.revealIndex = idx;
|
|
2008
|
+
pushReveal(idx);
|
|
2009
|
+
};
|
|
2010
|
+
const clearReveal = () => {
|
|
2011
|
+
if (live.revealIndex === null) return;
|
|
2012
|
+
live.revealIndex = null;
|
|
2013
|
+
pushReveal(null);
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
const zrHover = chart.getZr();
|
|
2017
|
+
const onZrHoverMove = (event: { offsetX?: number; offsetY?: number }) => {
|
|
2018
|
+
// Reveal is a standalone hover mode and takes precedence over highlight.
|
|
2019
|
+
if (live.handlers.enableHoverReveal) {
|
|
2020
|
+
applyReveal(event);
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
if (!live.handlers.enableHoverHighlight) return;
|
|
2024
|
+
// A click selection owns the canvas dim — hover highlighting stops
|
|
2025
|
+
// entirely while one exists and resumes once it clears.
|
|
2026
|
+
if (live.handlers.selectedDataKey !== null) return;
|
|
2027
|
+
applyHoverKey(
|
|
2028
|
+
resolveAreaAtPixel(
|
|
2029
|
+
chart,
|
|
2030
|
+
live.plottedTops,
|
|
2031
|
+
live.handlers.seriesKeys,
|
|
2032
|
+
event.offsetX ?? -1,
|
|
2033
|
+
event.offsetY ?? -1,
|
|
2034
|
+
),
|
|
2035
|
+
);
|
|
2036
|
+
};
|
|
2037
|
+
const onZrHoverOut = () => {
|
|
2038
|
+
if (live.handlers.enableHoverReveal) clearReveal();
|
|
2039
|
+
else if (live.handlers.enableHoverHighlight) applyHoverKey(null);
|
|
2040
|
+
};
|
|
2041
|
+
zrHover.on("mousemove", onZrHoverMove);
|
|
2042
|
+
zrHover.on("globalout", onZrHoverOut);
|
|
2043
|
+
|
|
2044
|
+
// The native hover still emphasizes whichever element the pointer entered —
|
|
2045
|
+
// cancel it whenever it disagrees with the tracker's resolved key.
|
|
2046
|
+
chart.on("mouseover", (params) => {
|
|
2047
|
+
const { enableHoverHighlight: hoverOn, enableHoverReveal: revealOn } = live.handlers;
|
|
2048
|
+
if (!hoverOn || revealOn) return;
|
|
2049
|
+
// While a selection is active, hover highlighting is disabled — never
|
|
2050
|
+
// dispatch emphasis/downplay so the selection dim is the only dimming.
|
|
2051
|
+
if (live.handlers.selectedDataKey !== null) return;
|
|
2052
|
+
const p = params as { seriesIndex?: number; componentType?: string };
|
|
2053
|
+
if (p.componentType !== "series" || typeof p.seriesIndex !== "number") return;
|
|
2054
|
+
const key = live.seriesKeyByIndex[p.seriesIndex];
|
|
2055
|
+
if (!key || key.startsWith("__")) return;
|
|
2056
|
+
if (key !== live.hoveredKey) {
|
|
2057
|
+
chart.dispatchAction({ type: "downplay", seriesIndex: p.seriesIndex });
|
|
2058
|
+
if (live.hoveredKey) {
|
|
2059
|
+
chart.dispatchAction({ type: "highlight", seriesId: live.hoveredKey });
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
|
|
2064
|
+
chart.on("datazoom", () => {
|
|
2065
|
+
const option = chart.getOption() as { dataZoom?: { start?: number; end?: number }[] };
|
|
2066
|
+
const zoom = option.dataZoom?.[0];
|
|
2067
|
+
if (!zoom) return;
|
|
2068
|
+
|
|
2069
|
+
// Ride the selection — pure zrender updates, so the drag stays 1:1.
|
|
2070
|
+
live.brushRange = { start: zoom.start ?? 0, end: zoom.end ?? 100 };
|
|
2071
|
+
syncBrushOverlayNow();
|
|
2072
|
+
|
|
2073
|
+
const { onBrushChange: onChange } = live.handlers;
|
|
2074
|
+
if (!onChange) return;
|
|
2075
|
+
const len = live.dataLength;
|
|
2076
|
+
const startIndex = Math.round(((zoom.start ?? 0) / 100) * (len - 1));
|
|
2077
|
+
const endIndex = Math.round(((zoom.end ?? 100) / 100) * (len - 1));
|
|
2078
|
+
onChange({ startIndex, endIndex });
|
|
2079
|
+
});
|
|
2080
|
+
|
|
2081
|
+
// Hover tracking for the overlay: labels show while the pointer is over the
|
|
2082
|
+
// brush, and each pill brightens when the pointer is near its edge.
|
|
2083
|
+
const zr = chart.getZr();
|
|
2084
|
+
const applyHover = (next: { inside: boolean; left: boolean; right: boolean }) => {
|
|
2085
|
+
const prev = live.brushHover;
|
|
2086
|
+
if (prev.inside === next.inside && prev.left === next.left && prev.right === next.right) {
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
live.brushHover = next;
|
|
2090
|
+
syncBrushOverlayNow();
|
|
2091
|
+
};
|
|
2092
|
+
const onZrMove = (event: { offsetX?: number; offsetY?: number }) => {
|
|
2093
|
+
const geom = live.brushGeom;
|
|
2094
|
+
if (!geom) return;
|
|
2095
|
+
const x = event.offsetX ?? -1;
|
|
2096
|
+
const y = event.offsetY ?? -1;
|
|
2097
|
+
const top = chart.getHeight() - geom.bottom - geom.height;
|
|
2098
|
+
const inside = y >= top - 4 && y <= top + geom.height + 4;
|
|
2099
|
+
const trackLeft = 8;
|
|
2100
|
+
const trackWidth = Math.max(chart.getWidth() - 16, 1);
|
|
2101
|
+
const { start, end } = live.brushRange;
|
|
2102
|
+
const selectionLeft = trackLeft + (trackWidth * start) / 100;
|
|
2103
|
+
const selectionRight = trackLeft + (trackWidth * end) / 100;
|
|
2104
|
+
applyHover({
|
|
2105
|
+
inside,
|
|
2106
|
+
left: inside && Math.abs(x - selectionLeft) <= 8,
|
|
2107
|
+
right: inside && Math.abs(x - selectionRight) <= 8,
|
|
2108
|
+
});
|
|
2109
|
+
};
|
|
2110
|
+
const onZrOut = () => applyHover({ inside: false, left: false, right: false });
|
|
2111
|
+
zr.on("mousemove", onZrMove);
|
|
2112
|
+
zr.on("globalout", onZrOut);
|
|
2113
|
+
|
|
2114
|
+
return () => {
|
|
2115
|
+
zrHover.off("mousemove", onZrHoverMove);
|
|
2116
|
+
zrHover.off("globalout", onZrHoverOut);
|
|
2117
|
+
zr.off("mousemove", onZrMove);
|
|
2118
|
+
zr.off("globalout", onZrOut);
|
|
2119
|
+
resizeObserver.disconnect();
|
|
2120
|
+
themeObserver.disconnect();
|
|
2121
|
+
chart.dispose();
|
|
2122
|
+
echartsRef.current = null;
|
|
2123
|
+
// The overlay elements died with the zrender instance.
|
|
2124
|
+
live.brushOverlay = null;
|
|
2125
|
+
// The reveal guard belongs to the chart instance it guarded. Without this
|
|
2126
|
+
// reset, StrictMode's dev-only mount→unmount→remount plays the entrance on
|
|
2127
|
+
// the throwaway instance and the surviving one renders without it.
|
|
2128
|
+
live.hasRevealed = false;
|
|
2129
|
+
};
|
|
2130
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2131
|
+
}, [renderer]);
|
|
2132
|
+
|
|
2133
|
+
// ── Sync ECharts with props/theme/selection — resolve, build, push ────────────
|
|
2134
|
+
useEffect(() => {
|
|
2135
|
+
const chart = echartsRef.current;
|
|
2136
|
+
const container = containerRef.current;
|
|
2137
|
+
if (!chart || !container) return;
|
|
2138
|
+
|
|
2139
|
+
// Colors come from the <style> committed just before this effect ran — read
|
|
2140
|
+
// them here, right before the push, rather than round-tripping through state.
|
|
2141
|
+
live.resolved = resolveColors(container, config, seriesKeys);
|
|
2142
|
+
|
|
2143
|
+
const push = (withEntrance: boolean) => {
|
|
2144
|
+
const option = buildOption();
|
|
2145
|
+
const merged = chartOptions ? { ...option, ...chartOptions } : option;
|
|
2146
|
+
Object.assign(merged, {
|
|
2147
|
+
animation: withEntrance,
|
|
2148
|
+
animationDuration: REVEAL_DURATION,
|
|
2149
|
+
animationDurationUpdate: 0,
|
|
2150
|
+
});
|
|
2151
|
+
// chartOptions is an untyped escape hatch — the spread erases the option's
|
|
2152
|
+
// shape, so re-assert it. The only cast in the file.
|
|
2153
|
+
chart.setOption(merged as EChartsOption, { notMerge: true });
|
|
2154
|
+
// Overlays live outside the option — reposition them after every push.
|
|
2155
|
+
syncBrushOverlayNow();
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
// Intro reveal — ECharts' native progressive draw, enabled only for the first
|
|
2159
|
+
// real render: the line traces in, dots pop up as its front passes. Every
|
|
2160
|
+
// later push (selection, theme, zoom) applies instantly, since notMerge would
|
|
2161
|
+
// otherwise replay the entrance on each of them. A loading cycle re-arms it:
|
|
2162
|
+
// the Recharts twin unmounts its <Area>s while loading and replays the intro
|
|
2163
|
+
// on remount, so data → loading → data draws in again here too.
|
|
2164
|
+
if (isLoading) live.hasRevealed = false;
|
|
2165
|
+
const shouldReveal = !live.hasRevealed && !isLoading;
|
|
2166
|
+
if (shouldReveal) live.hasRevealed = true;
|
|
2167
|
+
const revealEnabled =
|
|
2168
|
+
animation && shouldReveal && effectiveAnimation !== "none" && !shouldReduceMotion;
|
|
2169
|
+
if (revealEnabled) live.revealEndsAt = performance.now() + REVEAL_DURATION;
|
|
2170
|
+
push(revealEnabled);
|
|
2171
|
+
|
|
2172
|
+
// Theme flips and resizes re-enter here without touching React: re-read the
|
|
2173
|
+
// tokens (the .dark class changed, or textures need renderer-sized rebakes)
|
|
2174
|
+
// and push an update-style option.
|
|
2175
|
+
live.repush = () => {
|
|
2176
|
+
live.resolved = resolveColors(container, config, seriesKeys);
|
|
2177
|
+
push(false);
|
|
2178
|
+
};
|
|
2179
|
+
}, [
|
|
2180
|
+
renderer,
|
|
2181
|
+
live,
|
|
2182
|
+
buildOption,
|
|
2183
|
+
chartOptions,
|
|
2184
|
+
isLoading,
|
|
2185
|
+
animation,
|
|
2186
|
+
effectiveAnimation,
|
|
2187
|
+
shouldReduceMotion,
|
|
2188
|
+
config,
|
|
2189
|
+
seriesKeys,
|
|
2190
|
+
syncBrushOverlayNow,
|
|
2191
|
+
]);
|
|
2192
|
+
|
|
2193
|
+
// ── Animated dashed stroke — rAF sweeps the dash offset while unselected ─────
|
|
2194
|
+
useEffect(() => {
|
|
2195
|
+
const chart = echartsRef.current;
|
|
2196
|
+
if (!chart || isLoading) return;
|
|
2197
|
+
const animatedKeys = areas
|
|
2198
|
+
.filter((area) => area.strokeVariant === "animated-dashed" && !area.enableBufferLine)
|
|
2199
|
+
.map((area) => area.dataKey);
|
|
2200
|
+
if (animatedKeys.length === 0 || hasSelection) return;
|
|
2201
|
+
|
|
2202
|
+
let raf = 0;
|
|
2203
|
+
let delayTimer: ReturnType<typeof setTimeout> | undefined;
|
|
2204
|
+
const begin = () => {
|
|
2205
|
+
const loopStart = performance.now();
|
|
2206
|
+
const tick = (now: number) => {
|
|
2207
|
+
const offset = -(((now - loopStart) / 1000) % 1) * 6; // 0 → -6 per second
|
|
2208
|
+
chart.setOption(
|
|
2209
|
+
{ series: animatedKeys.map((id) => ({ id, lineStyle: { dashOffset: offset } })) },
|
|
2210
|
+
{ silent: true, lazyUpdate: true },
|
|
2211
|
+
);
|
|
2212
|
+
raf = requestAnimationFrame(tick);
|
|
2213
|
+
};
|
|
2214
|
+
raf = requestAnimationFrame(tick);
|
|
2215
|
+
};
|
|
2216
|
+
|
|
2217
|
+
// Per-frame setOption churn fights the intro draw-in (each update pass
|
|
2218
|
+
// recomputes the reveal clip, crawling it to a standstill) — hold the dash
|
|
2219
|
+
// sweep until the entrance has finished.
|
|
2220
|
+
const delay = Math.max(0, live.revealEndsAt - performance.now());
|
|
2221
|
+
if (delay > 0) delayTimer = setTimeout(begin, delay + 50);
|
|
2222
|
+
else begin();
|
|
2223
|
+
|
|
2224
|
+
return () => {
|
|
2225
|
+
if (delayTimer !== undefined) clearTimeout(delayTimer);
|
|
2226
|
+
cancelAnimationFrame(raf);
|
|
2227
|
+
};
|
|
2228
|
+
}, [renderer, live, areas, hasSelection, isLoading]);
|
|
2229
|
+
|
|
2230
|
+
// ── Loading shimmer — rAF sweeps a bright band, regenerating data off-screen ─
|
|
2231
|
+
useEffect(() => {
|
|
2232
|
+
const chart = echartsRef.current;
|
|
2233
|
+
if (!chart || !isLoading) return;
|
|
2234
|
+
|
|
2235
|
+
let raf = 0;
|
|
2236
|
+
let lastPhase = 0;
|
|
2237
|
+
const start = performance.now();
|
|
2238
|
+
const tick = (now: number) => {
|
|
2239
|
+
const phase = ((((now - start) / LOADING_ANIMATION_DURATION) % 1) + 1) % 1;
|
|
2240
|
+
// Wrapped past 1 → the band is off-screen; swap in fresh random data.
|
|
2241
|
+
if (phase < lastPhase) live.loadingRows = getLoadingData(loadingPoints);
|
|
2242
|
+
lastPhase = phase;
|
|
2243
|
+
|
|
2244
|
+
// Read tokens per frame, so a theme flip mid-loading retints the shimmer.
|
|
2245
|
+
const foreground = live.resolved?.tokens.foreground ?? "rgba(120, 120, 120, 1)";
|
|
2246
|
+
// Sweep the clip window from fully off-screen left to fully off-screen
|
|
2247
|
+
// right, leaned 45°. The gradient uses ABSOLUTE pixel coordinates shared
|
|
2248
|
+
// by stroke and fill — bbox-relative coords put the window at different
|
|
2249
|
+
// positions for the line vs the area polygon (their bounding boxes
|
|
2250
|
+
// differ), which made the line trail the fill near the sweep's end.
|
|
2251
|
+
const w = chart.getWidth();
|
|
2252
|
+
const h = chart.getHeight();
|
|
2253
|
+
if (!w || !h) {
|
|
2254
|
+
raf = requestAnimationFrame(tick);
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
// Farthest plot corner projected onto the 45° axis — keeps the sweep
|
|
2258
|
+
// tight instead of dawdling off-plot at the end of each loop.
|
|
2259
|
+
const maxT = (w + h) / (2 * w);
|
|
2260
|
+
const center = phase * (maxT + 2 * LOADING_SHIMMER_BAND) - LOADING_SHIMMER_BAND;
|
|
2261
|
+
const clip = (peak: number) =>
|
|
2262
|
+
new echarts.graphic.LinearGradient(
|
|
2263
|
+
0,
|
|
2264
|
+
0,
|
|
2265
|
+
w,
|
|
2266
|
+
w,
|
|
2267
|
+
shimmerWindowStops(center, foreground, peak),
|
|
2268
|
+
true,
|
|
2269
|
+
);
|
|
2270
|
+
chart.setOption(
|
|
2271
|
+
{
|
|
2272
|
+
series: [
|
|
2273
|
+
{
|
|
2274
|
+
id: "__loading",
|
|
2275
|
+
data: loadingData(),
|
|
2276
|
+
lineStyle: { color: clip(LOADING_STROKE_OPACITY), width: 1 },
|
|
2277
|
+
areaStyle: { color: clip(LOADING_SHIMMER_MAX_OPACITY) },
|
|
2278
|
+
},
|
|
2279
|
+
],
|
|
2280
|
+
},
|
|
2281
|
+
{ silent: true, lazyUpdate: true },
|
|
2282
|
+
);
|
|
2283
|
+
raf = requestAnimationFrame(tick);
|
|
2284
|
+
};
|
|
2285
|
+
raf = requestAnimationFrame(tick);
|
|
2286
|
+
return () => cancelAnimationFrame(raf);
|
|
2287
|
+
}, [renderer, live, isLoading, loadingPoints, loadingData]);
|
|
2288
|
+
|
|
2289
|
+
// ── Legend overlay position ──────────────────────────────────────────────────
|
|
2290
|
+
// Insets match the Recharts legend's breathing room inside the plot frame.
|
|
2291
|
+
const legendStyle: CSSProperties = {
|
|
2292
|
+
position: "absolute",
|
|
2293
|
+
left: 16,
|
|
2294
|
+
right: 16,
|
|
2295
|
+
pointerEvents: "auto",
|
|
2296
|
+
...(legendSlot.verticalAlign === "top"
|
|
2297
|
+
? { top: 12 }
|
|
2298
|
+
: legendSlot.verticalAlign === "bottom"
|
|
2299
|
+
? { bottom: showBrush ? brushHeight + 16 : 12 }
|
|
2300
|
+
: { top: "50%", transform: "translateY(-50%)" }),
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
return (
|
|
2304
|
+
<div
|
|
2305
|
+
ref={containerRef}
|
|
2306
|
+
data-chart={chartId}
|
|
2307
|
+
className={`relative flex flex-col text-xs ${className ?? ""}`}
|
|
2308
|
+
>
|
|
2309
|
+
<style dangerouslySetInnerHTML={{ __html: css }} />
|
|
2310
|
+
|
|
2311
|
+
<div className="relative min-h-0 w-full flex-1">
|
|
2312
|
+
<div ref={mountRef} className="h-full min-h-0 w-full" />
|
|
2313
|
+
</div>
|
|
2314
|
+
|
|
2315
|
+
{legendSlot.present && !isLoading && (
|
|
2316
|
+
<LegendOverlay
|
|
2317
|
+
seriesKeys={seriesKeys}
|
|
2318
|
+
config={config}
|
|
2319
|
+
variant={legendSlot.variant}
|
|
2320
|
+
align={legendSlot.align}
|
|
2321
|
+
verticalAlign={legendSlot.verticalAlign}
|
|
2322
|
+
selectedKey={selectedDataKey}
|
|
2323
|
+
hoveredKey={hoveredDataKey}
|
|
2324
|
+
isClickable={legendSlot.isClickable}
|
|
2325
|
+
onToggle={toggleSelection}
|
|
2326
|
+
style={legendStyle}
|
|
2327
|
+
/>
|
|
2328
|
+
)}
|
|
2329
|
+
|
|
2330
|
+
{isLoading && (
|
|
2331
|
+
<div className="pointer-events-none absolute inset-0 z-20 flex items-center justify-center">
|
|
2332
|
+
<motion.div
|
|
2333
|
+
initial={shouldReduceMotion ? false : { opacity: 0, scale: 0.92 }}
|
|
2334
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
2335
|
+
transition={{ duration: 0.25, ease: "easeOut" }}
|
|
2336
|
+
className="text-primary bg-background flex items-center justify-center gap-2 rounded-md border px-2 py-0.5 text-sm"
|
|
2337
|
+
>
|
|
2338
|
+
<div className="border-border border-t-primary h-3 w-3 animate-spin rounded-full border" />
|
|
2339
|
+
<span>Loading</span>
|
|
2340
|
+
</motion.div>
|
|
2341
|
+
</div>
|
|
2342
|
+
)}
|
|
2343
|
+
</div>
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// Compound API: every part hangs off the root as a static member, so a consumer
|
|
2348
|
+
// writes <EChartsAreaChart.Area/>, <EChartsAreaChart.Tooltip/>, … from a single
|
|
2349
|
+
// import — no colliding named marker exports when several charts share one file.
|
|
2350
|
+
EChartsAreaChart.Area = Area;
|
|
2351
|
+
EChartsAreaChart.Dot = Dot;
|
|
2352
|
+
EChartsAreaChart.ActiveDot = ActiveDot;
|
|
2353
|
+
EChartsAreaChart.XAxis = XAxis;
|
|
2354
|
+
EChartsAreaChart.YAxis = YAxis;
|
|
2355
|
+
EChartsAreaChart.Grid = Grid;
|
|
2356
|
+
EChartsAreaChart.Tooltip = Tooltip;
|
|
2357
|
+
EChartsAreaChart.Legend = Legend;
|
|
2358
|
+
EChartsAreaChart.Brush = Brush;
|