@langchain/macaw-components 1.0.0-beta.1 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{LoadingIndicator-DgtU9aqD.js → LoadingIndicator-CDcArsiM.js} +13 -12
- package/dist/_chunks/LoadingIndicator-CDcArsiM.js.map +1 -0
- package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js +270 -0
- package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js.map +1 -0
- package/dist/catalog.json +527 -408
- package/dist/components/Attachment/Attachment.d.ts +21 -0
- package/dist/components/Attachment/Attachment.js +165 -0
- package/dist/components/Attachment/Attachment.js.map +1 -0
- package/dist/components/Attachment/AttachmentList.d.ts +5 -0
- package/dist/components/Attachment/AttachmentList.js +32 -0
- package/dist/components/Attachment/AttachmentList.js.map +1 -0
- package/dist/components/Attachment/index.d.ts +4 -0
- package/dist/components/Attachment/index.js +3 -0
- package/dist/components/Attachment/render-attachment-icon.d.ts +3 -0
- package/dist/components/Attachment/render-attachment-icon.js +2 -0
- package/dist/components/Avatar/Avatar.js +2 -2
- package/dist/components/Badge/Badge.d.ts +2 -0
- package/dist/components/Badge/Badge.js +12 -1
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Banner/Banner.js +1 -1
- package/dist/components/Button/Button.js +1 -1
- package/dist/components/Button/constants.js +2 -2
- package/dist/components/Button/constants.js.map +1 -1
- package/dist/components/ChartCard/ChartCard.d.ts +1 -4
- package/dist/components/ChartCard/ChartCard.js +2 -2
- package/dist/components/ChartCard/ChartCard.js.map +1 -1
- package/dist/components/ChartCard/ChartCardSkeleton.d.ts +1 -1
- package/dist/components/ChartCard/ChartCardSkeleton.js +37 -0
- package/dist/components/ChartCard/ChartCardSkeleton.js.map +1 -1
- package/dist/components/ChartLegend/ChartLegend.js +2 -2
- package/dist/components/ChartLegend/ChartLegendItemRenderer.js +2 -2
- package/dist/components/ChartLegend/ChartLegendItemRenderer.js.map +1 -1
- package/dist/components/Code/Code.utils.d.ts +3 -2
- package/dist/components/Code/Code.utils.js +7 -3
- package/dist/components/Code/Code.utils.js.map +1 -1
- package/dist/components/Code/CodeLanguageSelect.d.ts +7 -0
- package/dist/components/Code/CodeLanguageSelect.js +29 -0
- package/dist/components/Code/CodeLanguageSelect.js.map +1 -0
- package/dist/components/Code/CodeLite.js +3 -0
- package/dist/components/Code/CodeLite.js.map +1 -1
- package/dist/components/Code/CodeToolbar.d.ts +7 -0
- package/dist/components/Code/CodeToolbar.js +19 -0
- package/dist/components/Code/CodeToolbar.js.map +1 -0
- package/dist/components/Code/codeLanguages.d.ts +7 -0
- package/dist/components/Code/codeLanguages.js +59 -0
- package/dist/components/Code/codeLanguages.js.map +1 -0
- package/dist/components/Code/index.d.ts +15 -2
- package/dist/components/Code/index.js +70 -50
- package/dist/components/Code/index.js.map +1 -1
- package/dist/components/Code/types.d.ts +1 -1
- package/dist/components/Command/Command.d.ts +10 -13
- package/dist/components/Command/Command.js +7 -2
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/Command/CommandMenu.d.ts +7 -1
- package/dist/components/Command/CommandMenu.js +17 -9
- package/dist/components/Command/CommandMenu.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.d.ts +6 -0
- package/dist/components/ContextMenu/ContextMenu.js +3 -2
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/CopyButton/CopyButton.js +2 -2
- package/dist/components/Dialog/index.js +2 -2
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +11 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +5 -4
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/ErrorMessage/ErrorMessage.js +1 -1
- package/dist/components/GroupedTabs/GroupedTabs.d.ts +5 -2
- package/dist/components/GroupedTabs/GroupedTabs.js +33 -26
- package/dist/components/GroupedTabs/GroupedTabs.js.map +1 -1
- package/dist/components/IconButton/IconButton.js +2 -2
- package/dist/components/Input/Input.js +1 -1
- package/dist/components/LineChart/LineChart.d.ts +2 -2
- package/dist/components/LineChart/LineChart.js +3 -1
- package/dist/components/LineChart/LineChart.js.map +1 -1
- package/dist/components/LineChart/LineChart.types.d.ts +16 -0
- package/dist/components/LineChart/LineChartPlot.d.ts +3 -2
- package/dist/components/LineChart/LineChartPlot.js +8 -1
- package/dist/components/LineChart/LineChartPlot.js.map +1 -1
- package/dist/components/LineChart/LineChartVerticalMarkers.d.ts +8 -0
- package/dist/components/LineChart/LineChartVerticalMarkers.js +47 -0
- package/dist/components/LineChart/LineChartVerticalMarkers.js.map +1 -0
- package/dist/components/LineChart/index.d.ts +1 -1
- package/dist/components/Logo/index.js +11 -6
- package/dist/components/Logo/index.js.map +1 -1
- package/dist/components/MetricChart/MetricChart.d.ts +2 -0
- package/dist/components/MetricChart/MetricChart.js +15 -4
- package/dist/components/MetricChart/MetricChart.js.map +1 -1
- package/dist/components/MetricChart/index.d.ts +1 -1
- package/dist/components/Pane/Pane.js +2 -2
- package/dist/components/SankeyChart/AnimatedSankeyLink.d.ts +17 -0
- package/dist/components/SankeyChart/AnimatedSankeyLink.js +44 -0
- package/dist/components/SankeyChart/AnimatedSankeyLink.js.map +1 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.d.ts +16 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.js +42 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.js.map +1 -0
- package/dist/components/SankeyChart/SankeyChart.d.ts +42 -0
- package/dist/components/SankeyChart/SankeyChart.js +185 -0
- package/dist/components/SankeyChart/SankeyChart.js.map +1 -0
- package/dist/components/SankeyChart/SankeyChart.types.d.ts +90 -0
- package/dist/components/SankeyChart/SankeyChart.types.js +0 -0
- package/dist/components/SankeyChart/SankeyChart.utils.d.ts +21 -0
- package/dist/components/SankeyChart/SankeyChart.utils.js +299 -0
- package/dist/components/SankeyChart/SankeyChart.utils.js.map +1 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.d.ts +10 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.js +30 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.js.map +1 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.d.ts +9 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.js +29 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.js.map +1 -0
- package/dist/components/SankeyChart/constants.d.ts +22 -0
- package/dist/components/SankeyChart/constants.js +27 -0
- package/dist/components/SankeyChart/constants.js.map +1 -0
- package/dist/components/SankeyChart/index.d.ts +5 -0
- package/dist/components/SankeyChart/index.js +4 -0
- package/dist/components/Select/Select.js +5 -5
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectPrimitive.d.ts +6 -0
- package/dist/components/Select/SelectPrimitive.js +3 -2
- package/dist/components/Select/SelectPrimitive.js.map +1 -1
- package/dist/components/Slider/Slider.js +10 -3
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/SplitViewPane/SplitViewPane.d.ts +2 -1
- package/dist/components/SplitViewPane/SplitViewPane.js +113 -219
- package/dist/components/SplitViewPane/SplitViewPane.js.map +1 -1
- package/dist/components/SplitViewPane/SplitViewPaneHeader.js +2 -2
- package/dist/components/SplitViewPane/useShowChildren.d.ts +5 -0
- package/dist/components/SplitViewPane/useShowChildren.js +14 -0
- package/dist/components/SplitViewPane/useShowChildren.js.map +1 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.d.ts +21 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.js +145 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.js.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +4 -3
- package/dist/components/Textarea/Textarea.js +7 -21
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/ThinkingState/LoadingIndicator.d.ts +1 -0
- package/dist/components/ThinkingState/LoadingIndicator.js +1 -1
- package/dist/components/ThinkingState/ThinkingState.d.ts +1 -0
- package/dist/components/ThinkingState/ThinkingState.js +5 -4
- package/dist/components/ThinkingState/ThinkingState.js.map +1 -1
- package/dist/components/ThinkingState/index.js +1 -1
- package/dist/components/Toast/index.js +33 -28
- package/dist/components/Toast/index.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +2 -2
- package/dist/components/Typeahead/Typeahead.js +18 -8
- package/dist/components/Typeahead/Typeahead.js.map +1 -1
- package/dist/components/Typeahead/Typeahead.types.d.ts +2 -0
- package/dist/components/Typeahead/TypeaheadParts.d.ts +3 -1
- package/dist/components/Typeahead/TypeaheadParts.js +7 -6
- package/dist/components/Typeahead/TypeaheadParts.js.map +1 -1
- package/dist/components/Typeahead/TypeaheadView.js +19 -13
- package/dist/components/Typeahead/TypeaheadView.js.map +1 -1
- package/dist/components/Typeahead/useTypeaheadDomSync.d.ts +1 -1
- package/dist/components/Typeahead/useTypeaheadDomSync.js +14 -8
- package/dist/components/Typeahead/useTypeaheadDomSync.js.map +1 -1
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.d.ts +2 -1
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js +10 -9
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js.map +1 -1
- package/dist/components/UnsavedChangesDialog.js +2 -2
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +7 -5
- package/dist/components.css +22 -18
- package/dist/hooks/useDebouncedCommit.d.ts +14 -0
- package/dist/hooks/useDebouncedCommit.js +35 -0
- package/dist/hooks/useDebouncedCommit.js.map +1 -0
- package/dist/icons/LongTailPhosphorIcons.js +1 -1
- package/dist/icons/PaddedPhosphorIcons.d.ts +1 -0
- package/dist/icons/PaddedPhosphorIcons.js +4 -2
- package/dist/icons/PaddedPhosphorIcons.js.map +1 -1
- package/dist/icons/WeightedPhosphorIcons.d.ts +1 -0
- package/dist/icons/WeightedPhosphorIcons.js +3 -1
- package/dist/icons/WeightedPhosphorIcons.js.map +1 -1
- package/dist/icons/index.js +3 -3
- package/dist/index.js +7 -5
- package/dist/utilities.css +1 -1
- package/dist/utils/chartColors.d.ts +2 -1
- package/dist/utils/chartColors.js +2 -1
- package/dist/utils/chartColors.js.map +1 -1
- package/dist/utils/componentSizes.d.ts +0 -1
- package/dist/utils/componentSizes.js.map +1 -1
- package/dist/utils/lazy-codemirror-loader.d.ts +0 -2
- package/dist/utils/lazy-codemirror-loader.js +1 -3
- package/dist/utils/lazy-codemirror-loader.js.map +1 -1
- package/docs/DESIGN.md +17 -0
- package/docs/STYLES.md +4 -1
- package/package.json +21 -8
- package/dist/_chunks/LoadingIndicator-DgtU9aqD.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
|
+
import { type OptionRowSize } from '../../utils/componentSizes.js';
|
|
3
4
|
export declare const DropdownMenu: typeof DropdownMenuPrimitive.Root;
|
|
4
5
|
export declare const DropdownMenuTrigger: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
5
6
|
export declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPortalProps>;
|
|
@@ -15,10 +16,20 @@ export declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<D
|
|
|
15
16
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
16
17
|
export declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
17
18
|
inset?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Controls minimum row height and default label typography.
|
|
21
|
+
* Rich or multiline content grows intrinsically.
|
|
22
|
+
*/
|
|
23
|
+
size?: OptionRowSize;
|
|
18
24
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
19
25
|
export declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
20
26
|
export declare const DropdownMenuSub: React.FC<DropdownMenuPrimitive.DropdownMenuSubProps>;
|
|
21
27
|
export declare const DropdownMenuSubTrigger: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubTriggerProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
22
28
|
inset?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Controls minimum row height and default label typography.
|
|
31
|
+
* Rich or multiline content grows intrinsically.
|
|
32
|
+
*/
|
|
33
|
+
size?: OptionRowSize;
|
|
23
34
|
} & React.RefAttributes<HTMLDivElement>>;
|
|
24
35
|
export declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { OPTION_ROW_SIZES } from "../../utils/componentSizes.js";
|
|
2
3
|
import zIndices from "../../utils/zIndices.js";
|
|
3
4
|
import { useZIndex } from "../../utils/useZIndex.js";
|
|
4
5
|
import { ZIndexProvider } from "../../utils/ZIndexContext.js";
|
|
@@ -109,9 +110,9 @@ var DropdownMenuContent = React$1.forwardRef(({ className, sideOffset = 4, child
|
|
|
109
110
|
}) });
|
|
110
111
|
});
|
|
111
112
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
112
|
-
var DropdownMenuItem = React$1.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Item, {
|
|
113
|
+
var DropdownMenuItem = React$1.forwardRef(({ className, inset, size = "md", ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Item, {
|
|
113
114
|
ref,
|
|
114
|
-
className: cn("
|
|
115
|
+
className: cn("flex cursor-pointer select-none items-center rounded-sm px-space-2 py-space-1 outline-none transition-colors focus:bg-surface-level-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50", OPTION_ROW_SIZES[size].minHeightClassName, size === "sm" ? "text-xs leading-4" : "text-sm", inset && "pl-space-6", className),
|
|
115
116
|
...props
|
|
116
117
|
}));
|
|
117
118
|
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
@@ -122,9 +123,9 @@ var DropdownMenuSeparator = React$1.forwardRef(({ className, ...props }, ref) =>
|
|
|
122
123
|
}));
|
|
123
124
|
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
124
125
|
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
125
|
-
var DropdownMenuSubTrigger = React$1.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.SubTrigger, {
|
|
126
|
+
var DropdownMenuSubTrigger = React$1.forwardRef(({ className, inset, children, size = "md", ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.SubTrigger, {
|
|
126
127
|
ref,
|
|
127
|
-
className: cn("
|
|
128
|
+
className: cn("flex cursor-pointer select-none items-center rounded-sm px-space-2 py-space-1 outline-none transition-colors focus:bg-surface-level-2 data-[state=open]:bg-surface-level-2", OPTION_ROW_SIZES[size].minHeightClassName, size === "sm" ? "text-xs leading-4" : "text-sm", inset && "pl-space-6", className),
|
|
128
129
|
...props,
|
|
129
130
|
children
|
|
130
131
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\n\nimport { cn } from '../../utils/cn';\nimport { useZIndex } from '../../utils/useZIndex';\nimport { ZIndexProvider } from '../../utils/ZIndexContext';\nimport zIndices from '../../utils/zIndices';\n\nexport const DropdownMenu: typeof DropdownMenuPrimitive.Root = (props) => {\n const [open = false, onOpenChange] = useControllableState({\n prop: props.open,\n defaultProp: props.defaultOpen ?? false,\n onChange: props.onOpenChange,\n });\n\n return (\n <DropdownMenuPrimitive.Root\n {...props}\n open={open}\n onOpenChange={onOpenChange}\n defaultOpen={undefined}\n />\n );\n};\n\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\n\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\n\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuLabel = DropdownMenuPrimitive.Label;\n\n/**\n * Stops clicks outside an open dropdown from reaching elements underneath it\n * (e.g. a table row's `onClick`) without covering the viewport, so scrolling\n * the content behind the menu keeps working natively.\n *\n * React delegates `onClick` at the root in the bubble phase, so a capture-phase\n * `click` listener on `document` runs first and can stop the event before it\n * gets there. pointerdown is left alone so Radix's outside-dismiss keeps\n * working; the wheel listener is passive so native scroll is never interrupted,\n * it just also dismisses the menu (an open menu jitters as Radix repositions it\n * to follow its trigger while scrolling).\n *\n * \"Inside\" is any element with `role=\"menu\"` in its ancestry — Radix gives that\n * role to both the content and its submenus (which portal out separately), so\n * clicks and scrolls inside a submenu are correctly treated as inside.\n */\nconst OutsideClickGuard = () => {\n const cleanupTimerRef =\n React.useRef<ReturnType<typeof setTimeout>>(undefined);\n const pointerDownOutsideRef = React.useRef(false);\n\n React.useLayoutEffect(() => {\n clearTimeout(cleanupTimerRef.current);\n pointerDownOutsideRef.current = false;\n\n const isOutside = (target: EventTarget | null) =>\n !(target instanceof Element) || !target.closest('[role=\"menu\"]');\n\n // Observe (don't block) outside pointerdowns: Radix dismisses on them, so\n // this tells us the trailing click needs absorbing on cleanup.\n const onPointerDown = (event: PointerEvent) => {\n if (isOutside(event.target)) pointerDownOutsideRef.current = true;\n };\n document.addEventListener('pointerdown', onPointerDown, true);\n\n // Block clicks that land outside the menu from reaching the content beneath.\n const onClick = (event: MouseEvent) => {\n if (isOutside(event.target)) {\n event.preventDefault();\n event.stopPropagation();\n }\n };\n document.addEventListener('click', onClick, true);\n\n // Dismiss on scroll of the content behind the menu: an anchored menu that\n // stays open jitters as Radix repositions it to chase its trigger. Passive,\n // so the wheel still scrolls the content natively on the same gesture.\n // Scrolling inside the menu (e.g. a details panel) leaves it open.\n const onWheel = (event: WheelEvent) => {\n if (isOutside(event.target)) {\n document.dispatchEvent(\n new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })\n );\n }\n };\n document.addEventListener('wheel', onWheel, { passive: true });\n\n return () => {\n document.removeEventListener('pointerdown', onPointerDown, true);\n document.removeEventListener('click', onClick, true);\n document.removeEventListener('wheel', onWheel);\n\n // Radix dismisses on the outside pointerdown and unmounts this guard\n // before the trailing click fires, so absorb that one click here. Only\n // for pointer dismissals — Escape / item selection have no trailing\n // outside click, and absorbing an unrelated later click would be wrong.\n if (!pointerDownOutsideRef.current) return;\n\n const absorb = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n };\n const opts = { capture: true, once: true } as const;\n document.addEventListener('pointerup', absorb, opts);\n document.addEventListener('mouseup', absorb, opts);\n document.addEventListener('click', absorb, opts);\n\n cleanupTimerRef.current = setTimeout(() => {\n if (typeof document === 'undefined') return;\n document.removeEventListener('pointerup', absorb, { capture: true });\n document.removeEventListener('mouseup', absorb, { capture: true });\n document.removeEventListener('click', absorb, { capture: true });\n }, 300);\n };\n }, []);\n\n return null;\n};\n\nexport const DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {\n /**\n * When true, stops clicks outside the open menu from reaching elements\n * underneath (e.g. table rows), while leaving scrolling of that content\n * intact. Radix's outside-click dismiss still works normally.\n */\n preventClickThrough?: boolean;\n }\n>(\n (\n { className, sideOffset = 4, children, preventClickThrough, ...props },\n ref\n ) => {\n const zIndex = useZIndex(zIndices.popover);\n\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n 'min-w-[8rem] overflow-hidden rounded-md border border-subtle bg-popover p-space-1 shadow-lg',\n className\n )}\n {...props}\n style={{ zIndex, ...props.style }}\n >\n {preventClickThrough && <OutsideClickGuard />}\n <ZIndexProvider value={zIndex}>{children}</ZIndexProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n );\n }\n);\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nexport const DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n 'text-md flex cursor-pointer select-none items-center rounded-sm px-space-2 py-1.5 outline-none transition-colors focus:bg-surface-level-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n inset && 'pl-space-6',\n className\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nexport const DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn('my-space-1 h-px bg-surface-level-2', className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\n\nexport const DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n 'text-md flex cursor-pointer select-none items-center rounded-sm px-space-2 py-1.5 outline-none transition-colors focus:bg-surface-level-2 data-[state=open]:bg-surface-level-2',\n inset && 'pl-space-6',\n className\n )}\n {...props}\n >\n {children}\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuSubTrigger.displayName =\n DropdownMenuPrimitive.SubTrigger.displayName;\n\nexport const DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const zIndex = useZIndex(zIndices.popover);\n\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n 'min-w-[8rem] overflow-hidden rounded-md border border-subtle bg-popover p-space-1 shadow-lg',\n className\n )}\n style={{ zIndex }}\n {...props}\n >\n <ZIndexProvider value={zIndex}>{children}</ZIndexProvider>\n </DropdownMenuPrimitive.SubContent>\n </DropdownMenuPrimitive.Portal>\n );\n});\nDropdownMenuSubContent.displayName =\n DropdownMenuPrimitive.SubContent.displayName;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\n\nimport { cn } from '../../utils/cn';\nimport {\n OPTION_ROW_SIZES,\n type OptionRowSize,\n} from '../../utils/componentSizes';\nimport { useZIndex } from '../../utils/useZIndex';\nimport { ZIndexProvider } from '../../utils/ZIndexContext';\nimport zIndices from '../../utils/zIndices';\n\nexport const DropdownMenu: typeof DropdownMenuPrimitive.Root = (props) => {\n const [open = false, onOpenChange] = useControllableState({\n prop: props.open,\n defaultProp: props.defaultOpen ?? false,\n onChange: props.onOpenChange,\n });\n\n return (\n <DropdownMenuPrimitive.Root\n {...props}\n open={open}\n onOpenChange={onOpenChange}\n defaultOpen={undefined}\n />\n );\n};\n\nexport const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\n\nexport const DropdownMenuPortal = DropdownMenuPrimitive.Portal;\n\nexport const DropdownMenuGroup = DropdownMenuPrimitive.Group;\n\nexport const DropdownMenuLabel = DropdownMenuPrimitive.Label;\n\n/**\n * Stops clicks outside an open dropdown from reaching elements underneath it\n * (e.g. a table row's `onClick`) without covering the viewport, so scrolling\n * the content behind the menu keeps working natively.\n *\n * React delegates `onClick` at the root in the bubble phase, so a capture-phase\n * `click` listener on `document` runs first and can stop the event before it\n * gets there. pointerdown is left alone so Radix's outside-dismiss keeps\n * working; the wheel listener is passive so native scroll is never interrupted,\n * it just also dismisses the menu (an open menu jitters as Radix repositions it\n * to follow its trigger while scrolling).\n *\n * \"Inside\" is any element with `role=\"menu\"` in its ancestry — Radix gives that\n * role to both the content and its submenus (which portal out separately), so\n * clicks and scrolls inside a submenu are correctly treated as inside.\n */\nconst OutsideClickGuard = () => {\n const cleanupTimerRef =\n React.useRef<ReturnType<typeof setTimeout>>(undefined);\n const pointerDownOutsideRef = React.useRef(false);\n\n React.useLayoutEffect(() => {\n clearTimeout(cleanupTimerRef.current);\n pointerDownOutsideRef.current = false;\n\n const isOutside = (target: EventTarget | null) =>\n !(target instanceof Element) || !target.closest('[role=\"menu\"]');\n\n // Observe (don't block) outside pointerdowns: Radix dismisses on them, so\n // this tells us the trailing click needs absorbing on cleanup.\n const onPointerDown = (event: PointerEvent) => {\n if (isOutside(event.target)) pointerDownOutsideRef.current = true;\n };\n document.addEventListener('pointerdown', onPointerDown, true);\n\n // Block clicks that land outside the menu from reaching the content beneath.\n const onClick = (event: MouseEvent) => {\n if (isOutside(event.target)) {\n event.preventDefault();\n event.stopPropagation();\n }\n };\n document.addEventListener('click', onClick, true);\n\n // Dismiss on scroll of the content behind the menu: an anchored menu that\n // stays open jitters as Radix repositions it to chase its trigger. Passive,\n // so the wheel still scrolls the content natively on the same gesture.\n // Scrolling inside the menu (e.g. a details panel) leaves it open.\n const onWheel = (event: WheelEvent) => {\n if (isOutside(event.target)) {\n document.dispatchEvent(\n new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })\n );\n }\n };\n document.addEventListener('wheel', onWheel, { passive: true });\n\n return () => {\n document.removeEventListener('pointerdown', onPointerDown, true);\n document.removeEventListener('click', onClick, true);\n document.removeEventListener('wheel', onWheel);\n\n // Radix dismisses on the outside pointerdown and unmounts this guard\n // before the trailing click fires, so absorb that one click here. Only\n // for pointer dismissals — Escape / item selection have no trailing\n // outside click, and absorbing an unrelated later click would be wrong.\n if (!pointerDownOutsideRef.current) return;\n\n const absorb = (event: Event) => {\n event.preventDefault();\n event.stopPropagation();\n };\n const opts = { capture: true, once: true } as const;\n document.addEventListener('pointerup', absorb, opts);\n document.addEventListener('mouseup', absorb, opts);\n document.addEventListener('click', absorb, opts);\n\n cleanupTimerRef.current = setTimeout(() => {\n if (typeof document === 'undefined') return;\n document.removeEventListener('pointerup', absorb, { capture: true });\n document.removeEventListener('mouseup', absorb, { capture: true });\n document.removeEventListener('click', absorb, { capture: true });\n }, 300);\n };\n }, []);\n\n return null;\n};\n\nexport const DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> & {\n /**\n * When true, stops clicks outside the open menu from reaching elements\n * underneath (e.g. table rows), while leaving scrolling of that content\n * intact. Radix's outside-click dismiss still works normally.\n */\n preventClickThrough?: boolean;\n }\n>(\n (\n { className, sideOffset = 4, children, preventClickThrough, ...props },\n ref\n ) => {\n const zIndex = useZIndex(zIndices.popover);\n\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n 'min-w-[8rem] overflow-hidden rounded-md border border-subtle bg-popover p-space-1 shadow-lg',\n className\n )}\n {...props}\n style={{ zIndex, ...props.style }}\n >\n {preventClickThrough && <OutsideClickGuard />}\n <ZIndexProvider value={zIndex}>{children}</ZIndexProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n );\n }\n);\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nexport const DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n /**\n * Controls minimum row height and default label typography.\n * Rich or multiline content grows intrinsically.\n */\n size?: OptionRowSize;\n }\n>(({ className, inset, size = 'md', ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n 'flex cursor-pointer select-none items-center rounded-sm px-space-2 py-space-1 outline-none transition-colors focus:bg-surface-level-2 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n OPTION_ROW_SIZES[size].minHeightClassName,\n size === 'sm' ? 'text-xs leading-4' : 'text-sm',\n inset && 'pl-space-6',\n className\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nexport const DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn('my-space-1 h-px bg-surface-level-2', className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nexport const DropdownMenuSub = DropdownMenuPrimitive.Sub;\n\nexport const DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n /**\n * Controls minimum row height and default label typography.\n * Rich or multiline content grows intrinsically.\n */\n size?: OptionRowSize;\n }\n>(({ className, inset, children, size = 'md', ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n 'flex cursor-pointer select-none items-center rounded-sm px-space-2 py-space-1 outline-none transition-colors focus:bg-surface-level-2 data-[state=open]:bg-surface-level-2',\n OPTION_ROW_SIZES[size].minHeightClassName,\n size === 'sm' ? 'text-xs leading-4' : 'text-sm',\n inset && 'pl-space-6',\n className\n )}\n {...props}\n >\n {children}\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuSubTrigger.displayName =\n DropdownMenuPrimitive.SubTrigger.displayName;\n\nexport const DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const zIndex = useZIndex(zIndices.popover);\n\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n 'min-w-[8rem] overflow-hidden rounded-md border border-subtle bg-popover p-space-1 shadow-lg',\n className\n )}\n style={{ zIndex }}\n {...props}\n >\n <ZIndexProvider value={zIndex}>{children}</ZIndexProvider>\n </DropdownMenuPrimitive.SubContent>\n </DropdownMenuPrimitive.Portal>\n );\n});\nDropdownMenuSubContent.displayName =\n DropdownMenuPrimitive.SubContent.displayName;\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
2
|
import { Text } from "../Text/Text.js";
|
|
3
|
-
import { CaretRightIcon, XCloseIcon } from "../../icons/PaddedPhosphorIcons.js";
|
|
4
3
|
import { Icon } from "../Icon/Icon.js";
|
|
5
4
|
import { IconButton } from "../IconButton/IconButton.js";
|
|
5
|
+
import { CaretRightIcon, XCloseIcon } from "../../icons/PaddedPhosphorIcons.js";
|
|
6
6
|
import { CopyIconButton } from "../CopyButton/CopyButton.js";
|
|
7
7
|
import { prettifyErrorPayload } from "../../utils/prettify-error-payload.js";
|
|
8
8
|
import { useId, useLayoutEffect, useRef, useState } from "react";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { type ControlSize } from '../../utils/componentSizes.js';
|
|
2
3
|
import type { IconComponent, IconWeight } from '../../utils/icon-types.js';
|
|
3
4
|
import type { TooltipProps } from '../Tooltip/index.js';
|
|
5
|
+
type GroupedTabsSize = Exclude<ControlSize, 'lg'>;
|
|
4
6
|
export interface GroupedTabOption<T> {
|
|
5
7
|
display?: ReactNode | ((isActive: boolean) => ReactNode);
|
|
6
8
|
value: T;
|
|
@@ -12,7 +14,6 @@ export interface GroupedTabOption<T> {
|
|
|
12
14
|
leftDecorator?: ReactNode;
|
|
13
15
|
rightDecorator?: ReactNode;
|
|
14
16
|
className?: string;
|
|
15
|
-
size?: 'xs' | 'sm' | 'md';
|
|
16
17
|
textClassName?: string;
|
|
17
18
|
'aria-label'?: string;
|
|
18
19
|
}
|
|
@@ -21,7 +22,8 @@ export type GroupedTabsProps<T extends string | number> = {
|
|
|
21
22
|
onChange: (value: T) => void;
|
|
22
23
|
options: readonly GroupedTabOption<T>[];
|
|
23
24
|
className?: string;
|
|
24
|
-
|
|
25
|
+
/** Exact outer group height: xs=20px, sm=24px, md=32px. */
|
|
26
|
+
size?: GroupedTabsSize;
|
|
25
27
|
disabled?: boolean;
|
|
26
28
|
tooltip?: string | ReactNode;
|
|
27
29
|
selectionClassName?: string;
|
|
@@ -29,3 +31,4 @@ export type GroupedTabsProps<T extends string | number> = {
|
|
|
29
31
|
indicatorClassName?: string;
|
|
30
32
|
};
|
|
31
33
|
export declare const GroupedTabs: <T extends string | number>({ value, onChange, options, className, size, disabled, tooltip, selectionClassName, indicatorClassName, }: GroupedTabsProps<T>) => import("react").JSX.Element;
|
|
34
|
+
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
|
+
import { CONTROL_SIZES } from "../../utils/componentSizes.js";
|
|
2
3
|
import { Text } from "../Text/Text.js";
|
|
3
4
|
import { Tooltip } from "../Tooltip/Tooltip.js";
|
|
4
|
-
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
//#region packages/components/src/components/GroupedTabs/GroupedTabs.tsx
|
|
7
8
|
var GroupedTabs = ({ value, onChange, options, className, size = "sm", disabled, tooltip, selectionClassName, indicatorClassName }) => {
|
|
@@ -10,43 +11,49 @@ var GroupedTabs = ({ value, onChange, options, className, size = "sm", disabled,
|
|
|
10
11
|
const [clipState, setClipState] = useState({
|
|
11
12
|
path: "",
|
|
12
13
|
isFirst: true,
|
|
13
|
-
|
|
14
|
+
shouldAnimate: false
|
|
14
15
|
});
|
|
16
|
+
const activeIndex = useMemo(() => options.findIndex((option) => option.value === value), [options, value]);
|
|
17
|
+
const updateClipState = useCallback((container, activeButton, shouldAnimate) => {
|
|
18
|
+
const containerRect = container.getBoundingClientRect();
|
|
19
|
+
const buttonRect = activeButton.getBoundingClientRect();
|
|
20
|
+
const roundValue = size === "xs" ? "1px" : size === "sm" ? "2px" : "4px";
|
|
21
|
+
const top = buttonRect.top - containerRect.top - container.clientTop;
|
|
22
|
+
const bottom = containerRect.bottom - buttonRect.bottom - container.clientTop;
|
|
23
|
+
const left = buttonRect.left - containerRect.left - container.clientLeft;
|
|
24
|
+
const path = `inset(${top}px ${containerRect.right - buttonRect.right - container.clientLeft}px ${bottom}px ${left}px round ${roundValue})`;
|
|
25
|
+
setClipState((prev) => prev.path === path ? prev : {
|
|
26
|
+
path,
|
|
27
|
+
isFirst: false,
|
|
28
|
+
shouldAnimate: shouldAnimate && !prev.isFirst
|
|
29
|
+
});
|
|
30
|
+
}, [size]);
|
|
15
31
|
useEffect(() => {
|
|
16
32
|
const container = containerRef.current;
|
|
17
|
-
const activeIndex = options.findIndex((option) => option.value === value);
|
|
18
33
|
const activeButton = buttonRefs.current[activeIndex];
|
|
19
|
-
if (activeButton
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const left = buttonRect.left - containerRect.left - container.clientLeft;
|
|
26
|
-
const right = containerRect.right - buttonRect.right - container.clientLeft;
|
|
27
|
-
setClipState((prev) => ({
|
|
28
|
-
path: `inset(${top}px ${right}px ${bottom}px ${left}px round ${roundValue})`,
|
|
29
|
-
isFirst: false,
|
|
30
|
-
animate: !prev.isFirst
|
|
31
|
-
}));
|
|
32
|
-
}
|
|
34
|
+
if (!activeButton || !container) return;
|
|
35
|
+
updateClipState(container, activeButton, true);
|
|
36
|
+
const observer = new ResizeObserver(() => updateClipState(container, activeButton, false));
|
|
37
|
+
observer.observe(container);
|
|
38
|
+
observer.observe(activeButton);
|
|
39
|
+
return () => observer.disconnect();
|
|
33
40
|
}, [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
activeIndex,
|
|
42
|
+
updateClipState,
|
|
43
|
+
value
|
|
37
44
|
]);
|
|
38
45
|
const isActive = (option) => option.value === value;
|
|
39
46
|
return /* @__PURE__ */ jsx(Tooltip, {
|
|
40
47
|
title: tooltip,
|
|
41
48
|
children: /* @__PURE__ */ jsxs("div", {
|
|
42
49
|
ref: containerRef,
|
|
43
|
-
className: cn("relative flex w-fit border border-subtle bg-surface-level-1 p-0.5
|
|
50
|
+
className: cn("relative box-border flex w-fit border border-subtle bg-surface-level-1 dark:border-muted", CONTROL_SIZES[size].heightClassName, size === "md" ? "p-0.5" : "p-px", size === "xs" ? "gap-px" : "gap-0.5", size === "xs" ? "rounded-[2px]" : size === "sm" ? "rounded-[3px]" : "rounded-sm", className),
|
|
44
51
|
children: [clipState.path && /* @__PURE__ */ jsx("div", {
|
|
45
52
|
"aria-hidden": true,
|
|
46
53
|
className: cn("pointer-events-none absolute inset-0 bg-surface-level-4", selectionClassName, indicatorClassName),
|
|
47
54
|
style: {
|
|
48
55
|
clipPath: clipState.path,
|
|
49
|
-
transition: clipState.
|
|
56
|
+
transition: clipState.shouldAnimate ? "clip-path 0.25s ease-out, background-color 0.25s ease-out" : "none"
|
|
50
57
|
}
|
|
51
58
|
}), options.map((option, idx) => /* @__PURE__ */ jsx(Tooltip, {
|
|
52
59
|
title: option.tooltip,
|
|
@@ -59,10 +66,10 @@ var GroupedTabs = ({ value, onChange, options, className, size = "sm", disabled,
|
|
|
59
66
|
"aria-label": option["aria-label"],
|
|
60
67
|
onClick: () => onChange(option.value),
|
|
61
68
|
disabled: option.disabled || disabled,
|
|
62
|
-
className: cn("relative flex items-center gap-space-1", "transition-colors duration-fast", {
|
|
63
|
-
"rounded-xs px-space-1
|
|
64
|
-
"rounded-xs px-space-2
|
|
65
|
-
"rounded-sm px-space-2
|
|
69
|
+
className: cn("relative flex h-full items-center gap-space-1", "transition-colors duration-fast", {
|
|
70
|
+
"rounded-xs px-space-1": size === "xs",
|
|
71
|
+
"rounded-xs px-space-2": size === "sm",
|
|
72
|
+
"rounded-sm px-space-2": size === "md"
|
|
66
73
|
}, {
|
|
67
74
|
"text-primary": isActive(option),
|
|
68
75
|
"text-secondary hover:bg-surface-level-1-hover": !isActive(option)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedTabs.js","names":[],"sources":["../../../src/components/GroupedTabs/GroupedTabs.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useEffect, useRef, useState } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport type { IconComponent, IconWeight } from '../../utils/icon-types';\nimport { Text } from '../Text';\nimport type { TooltipProps } from '../Tooltip';\nimport { Tooltip } from '../Tooltip';\n\nexport interface GroupedTabOption<T> {\n display?: ReactNode | ((isActive: boolean) => ReactNode);\n value: T;\n disabled?: boolean;\n tooltip?: string | ReactNode;\n tooltipProps?: Omit<TooltipProps, 'children' | 'title'>;\n icon?: IconComponent;\n iconWeight?: IconWeight;\n leftDecorator?: ReactNode;\n rightDecorator?: ReactNode;\n className?: string;\n
|
|
1
|
+
{"version":3,"file":"GroupedTabs.js","names":[],"sources":["../../../src/components/GroupedTabs/GroupedTabs.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { CONTROL_SIZES, type ControlSize } from '../../utils/componentSizes';\nimport type { IconComponent, IconWeight } from '../../utils/icon-types';\nimport { Text } from '../Text';\nimport type { TooltipProps } from '../Tooltip';\nimport { Tooltip } from '../Tooltip';\n\ntype GroupedTabsSize = Exclude<ControlSize, 'lg'>;\n\nexport interface GroupedTabOption<T> {\n display?: ReactNode | ((isActive: boolean) => ReactNode);\n value: T;\n disabled?: boolean;\n tooltip?: string | ReactNode;\n tooltipProps?: Omit<TooltipProps, 'children' | 'title'>;\n icon?: IconComponent;\n iconWeight?: IconWeight;\n leftDecorator?: ReactNode;\n rightDecorator?: ReactNode;\n className?: string;\n textClassName?: string;\n 'aria-label'?: string;\n}\n\nexport type GroupedTabsProps<T extends string | number> = {\n value: T;\n onChange: (value: T) => void;\n options: readonly GroupedTabOption<T>[];\n className?: string;\n /** Exact outer group height: xs=20px, sm=24px, md=32px. */\n size?: GroupedTabsSize;\n disabled?: boolean;\n tooltip?: string | ReactNode;\n selectionClassName?: string;\n /** Optional class for the indicator (e.g. to override top/height for custom margins) */\n indicatorClassName?: string;\n};\n\nexport const GroupedTabs = <T extends string | number>({\n value,\n onChange,\n options,\n className,\n size = 'sm',\n disabled,\n tooltip,\n selectionClassName,\n indicatorClassName,\n}: GroupedTabsProps<T>) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);\n const [clipState, setClipState] = useState<{\n path: string;\n isFirst: boolean;\n shouldAnimate: boolean;\n }>({ path: '', isFirst: true, shouldAnimate: false });\n const activeIndex = useMemo(\n () => options.findIndex((option) => option.value === value),\n [options, value]\n );\n\n const updateClipState = useCallback(\n (\n container: HTMLDivElement,\n activeButton: HTMLButtonElement,\n shouldAnimate: boolean\n ) => {\n const containerRect = container.getBoundingClientRect();\n const buttonRect = activeButton.getBoundingClientRect();\n const roundValue = size === 'xs' ? '1px' : size === 'sm' ? '2px' : '4px';\n\n const top = buttonRect.top - containerRect.top - container.clientTop;\n const bottom =\n containerRect.bottom - buttonRect.bottom - container.clientTop;\n const left = buttonRect.left - containerRect.left - container.clientLeft;\n const right =\n containerRect.right - buttonRect.right - container.clientLeft;\n\n const path = `inset(${top}px ${right}px ${bottom}px ${left}px round ${roundValue})`;\n setClipState((prev) =>\n prev.path === path\n ? prev\n : {\n path,\n isFirst: false,\n shouldAnimate: shouldAnimate && !prev.isFirst,\n }\n );\n },\n [size]\n );\n\n useEffect(() => {\n const container = containerRef.current;\n const activeButton = buttonRefs.current[activeIndex];\n if (!activeButton || !container) return;\n\n updateClipState(container, activeButton, true);\n\n const observer = new ResizeObserver(() =>\n updateClipState(container, activeButton, false)\n );\n observer.observe(container);\n observer.observe(activeButton);\n\n return () => observer.disconnect();\n }, [activeIndex, updateClipState, value]);\n\n const isActive = (option: GroupedTabOption<T>) => option.value === value;\n return (\n <Tooltip title={tooltip}>\n <div\n ref={containerRef}\n className={cn(\n 'relative box-border flex w-fit border border-subtle bg-surface-level-1 dark:border-muted',\n CONTROL_SIZES[size].heightClassName,\n size === 'md' ? 'p-0.5' : 'p-px',\n size === 'xs' ? 'gap-px' : 'gap-0.5',\n size === 'xs'\n ? 'rounded-[2px]'\n : size === 'sm'\n ? 'rounded-[3px]'\n : 'rounded-sm',\n className\n )}\n >\n {/* Clip-path animated background indicator */}\n {clipState.path && (\n <div\n aria-hidden\n className={cn(\n 'pointer-events-none absolute inset-0 bg-surface-level-4',\n selectionClassName,\n indicatorClassName\n )}\n style={{\n clipPath: clipState.path,\n transition: clipState.shouldAnimate\n ? 'clip-path 0.25s ease-out, background-color 0.25s ease-out'\n : 'none',\n }}\n />\n )}\n\n {options.map((option, idx) => (\n <Tooltip\n key={option.value}\n title={option.tooltip}\n {...option.tooltipProps}\n >\n {/* eslint-disable-next-line react/forbid-elements */}\n <button\n ref={(el) => {\n buttonRefs.current[idx] = el;\n }}\n type=\"button\"\n aria-label={option['aria-label']}\n onClick={() => onChange(option.value)}\n disabled={option.disabled || disabled}\n className={cn(\n 'relative flex h-full items-center gap-space-1',\n 'transition-colors duration-fast',\n {\n 'rounded-xs px-space-1': size === 'xs',\n 'rounded-xs px-space-2': size === 'sm',\n 'rounded-sm px-space-2': size === 'md',\n },\n {\n 'text-primary': isActive(option),\n 'text-secondary hover:bg-surface-level-1-hover':\n !isActive(option),\n },\n (option.disabled || disabled) &&\n 'text-disabled hover:bg-transparent',\n option.className\n )}\n >\n {option.icon && (\n <option.icon\n aria-hidden\n className={size === 'xs' ? 'size-3' : 'size-4'}\n weight={option.iconWeight}\n />\n )}\n {option.leftDecorator}\n {option.display &&\n (typeof option.display === 'function' ? (\n option.display(isActive(option))\n ) : (\n <Text\n variant={size === 'xs' ? 'xs' : 'sm'}\n className={cn('font-medium', option.textClassName)}\n >\n {option.display}\n </Text>\n ))}\n {option.rightDecorator}\n </button>\n </Tooltip>\n ))}\n </div>\n </Tooltip>\n );\n};\n"],"mappings":";;;;;;;AAyCA,IAAa,eAA0C,EACrD,OACA,UACA,SACA,WACA,OAAO,MACP,UACA,SACA,oBACA,yBACyB;CACzB,MAAM,eAAe,OAAuB,IAAI;CAChD,MAAM,aAAa,OAAqC,CAAC,CAAC;CAC1D,MAAM,CAAC,WAAW,gBAAgB,SAI/B;EAAE,MAAM;EAAI,SAAS;EAAM,eAAe;CAAM,CAAC;CACpD,MAAM,cAAc,cACZ,QAAQ,WAAW,WAAW,OAAO,UAAU,KAAK,GAC1D,CAAC,SAAS,KAAK,CACjB;CAEA,MAAM,kBAAkB,aAEpB,WACA,cACA,kBACG;EACH,MAAM,gBAAgB,UAAU,sBAAsB;EACtD,MAAM,aAAa,aAAa,sBAAsB;EACtD,MAAM,aAAa,SAAS,OAAO,QAAQ,SAAS,OAAO,QAAQ;EAEnE,MAAM,MAAM,WAAW,MAAM,cAAc,MAAM,UAAU;EAC3D,MAAM,SACJ,cAAc,SAAS,WAAW,SAAS,UAAU;EACvD,MAAM,OAAO,WAAW,OAAO,cAAc,OAAO,UAAU;EAI9D,MAAM,OAAO,SAAS,IAAI,KAFxB,cAAc,QAAQ,WAAW,QAAQ,UAAU,WAEhB,KAAK,OAAO,KAAK,KAAK,WAAW,WAAW;EACjF,cAAc,SACZ,KAAK,SAAS,OACV,OACA;GACE;GACA,SAAS;GACT,eAAe,iBAAiB,CAAC,KAAK;EACxC,CACN;CACF,GACA,CAAC,IAAI,CACP;CAEA,gBAAgB;EACd,MAAM,YAAY,aAAa;EAC/B,MAAM,eAAe,WAAW,QAAQ;EACxC,IAAI,CAAC,gBAAgB,CAAC,WAAW;EAEjC,gBAAgB,WAAW,cAAc,IAAI;EAE7C,MAAM,WAAW,IAAI,qBACnB,gBAAgB,WAAW,cAAc,KAAK,CAChD;EACA,SAAS,QAAQ,SAAS;EAC1B,SAAS,QAAQ,YAAY;EAE7B,aAAa,SAAS,WAAW;CACnC,GAAG;EAAC;EAAa;EAAiB;CAAK,CAAC;CAExC,MAAM,YAAY,WAAgC,OAAO,UAAU;CACnE,OACE,oBAAC,SAAD;EAAS,OAAO;EACd,UAAA,qBAAC,OAAD;GACE,KAAK;GACL,WAAW,GACT,4FACA,cAAc,KAAK,CAAC,iBACpB,SAAS,OAAO,UAAU,QAC1B,SAAS,OAAO,WAAW,WAC3B,SAAS,OACL,kBACA,SAAS,OACP,kBACA,cACN,SACF;GAbF,UAAA,CAgBG,UAAU,QACT,oBAAC,OAAD;IACE,eAAA;IACA,WAAW,GACT,2DACA,oBACA,kBACF;IACA,OAAO;KACL,UAAU,UAAU;KACpB,YAAY,UAAU,gBAClB,8DACA;IACN;GACD,CAAA,GAGF,QAAQ,KAAK,QAAQ,QACpB,oBAAC,SAAD;IAEE,OAAO,OAAO;IACd,GAAI,OAAO;IAGX,UAAA,qBAAC,UAAD;KACE,MAAM,OAAO;MACX,WAAW,QAAQ,OAAO;KAC5B;KACA,MAAK;KACL,cAAY,OAAO;KACnB,eAAe,SAAS,OAAO,KAAK;KACpC,UAAU,OAAO,YAAY;KAC7B,WAAW,GACT,iDACA,mCACA;MACE,yBAAyB,SAAS;MAClC,yBAAyB,SAAS;MAClC,yBAAyB,SAAS;KACpC,GACA;MACE,gBAAgB,SAAS,MAAM;MAC/B,iDACE,CAAC,SAAS,MAAM;KACpB,IACC,OAAO,YAAY,aAClB,sCACF,OAAO,SACT;KAxBF,UAAA;MA0BG,OAAO,QACN,oBAAC,OAAO,MAAR;OACE,eAAA;OACA,WAAW,SAAS,OAAO,WAAW;OACtC,QAAQ,OAAO;MAChB,CAAA;MAEF,OAAO;MACP,OAAO,YACL,OAAO,OAAO,YAAY,aACzB,OAAO,QAAQ,SAAS,MAAM,CAAC,IAE/B,oBAAC,MAAD;OACE,SAAS,SAAS,OAAO,OAAO;OAChC,WAAW,GAAG,eAAe,OAAO,aAAa;OAEhD,UAAA,OAAO;MACJ,CAAA;MAET,OAAO;KACF;;GACD,GApDF,OAAO,KAoDL,CACV,CACE;;CACE,CAAA;AAEb"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
2
|
import { CONTROL_SIZES } from "../../utils/componentSizes.js";
|
|
3
|
-
import { Tooltip } from "../Tooltip/Tooltip.js";
|
|
4
|
-
import { buttonStyleMap } from "../Button/constants.js";
|
|
5
3
|
import { Spinner } from "../Spinner/Spinner.js";
|
|
4
|
+
import { buttonStyleMap } from "../Button/constants.js";
|
|
5
|
+
import { Tooltip } from "../Tooltip/Tooltip.js";
|
|
6
6
|
import { cloneElement, forwardRef, isValidElement } from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
8
8
|
//#region packages/components/src/components/IconButton/IconButton.tsx
|
|
@@ -3,8 +3,8 @@ import { Text } from "../Text/Text.js";
|
|
|
3
3
|
import { Icon } from "../Icon/Icon.js";
|
|
4
4
|
import { IconButton } from "../IconButton/IconButton.js";
|
|
5
5
|
import { mergeRefs } from "../../utils/merge-refs.js";
|
|
6
|
-
import { INPUT_TEXT_CLASSES, getInputContainerClasses, getInputElementClasses } from "./inputStyles.js";
|
|
7
6
|
import { CONTROL_ICON_SIZES } from "../../utils/controlIconSizes.js";
|
|
7
|
+
import { INPUT_TEXT_CLASSES, getInputContainerClasses, getInputElementClasses } from "./inputStyles.js";
|
|
8
8
|
import { forwardRef, useEffect, useId, useRef, useState } from "react";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { useDebouncedCallback } from "use-debounce";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { LineChartProps } from './LineChart.types.js';
|
|
2
|
-
export type { LineChartDisplayAxis, LineChartDisplaySeries, LineChartGrid, LineChartInteractionDatum, LineChartInteractionPoint, LineChartPoint, LineChartProps, LineChartSelectionRange, LineChartSeries, LineChartXScale, LineChartYBand, LineChartYAxis, } from './LineChart.types.js';
|
|
2
|
+
export type { LineChartDisplayAxis, LineChartDisplaySeries, LineChartGrid, LineChartInteractionDatum, LineChartInteractionPoint, LineChartPoint, LineChartProps, LineChartSelectionRange, LineChartSeries, LineChartVerticalMarker, LineChartXScale, LineChartYBand, LineChartYAxis, } from './LineChart.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Responsive numeric line chart with nullable observations, multiple axes,
|
|
5
5
|
* legend filtering, configurable grids and x scales, brushing, and
|
|
6
6
|
* consumer-owned tooltip interactions.
|
|
7
7
|
*/
|
|
8
|
-
export declare const LineChart: ({ series, yAxes, xDomain, formatXValue, xScale: xScaleType, xTickCount, xTickValues, selectedIds, showPoints, connectNulls, grid, activeX, activeLegendItemId, selectionRange, yBands, renderActiveOverlay, shouldAnimate, isRendering, showLegend, legendProps, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, className, ...rest }: LineChartProps) => import("react").JSX.Element;
|
|
8
|
+
export declare const LineChart: ({ series, yAxes, xDomain, formatXValue, xScale: xScaleType, xTickCount, xTickValues, selectedIds, showPoints, connectNulls, grid, activeX, activeLegendItemId, selectionRange, yBands, verticalMarkers, renderActiveOverlay, shouldAnimate, isRendering, showLegend, legendProps, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, className, ...rest }: LineChartProps) => import("react").JSX.Element;
|
|
@@ -59,7 +59,7 @@ var resolveAxes = (layouts, innerHeight) => layouts.map(({ config, domain, nice,
|
|
|
59
59
|
* legend filtering, configurable grids and x scales, brushing, and
|
|
60
60
|
* consumer-owned tooltip interactions.
|
|
61
61
|
*/
|
|
62
|
-
var LineChart = ({ series, yAxes, xDomain, formatXValue = defaultFormatValue, xScale: xScaleType = "linear", xTickCount, xTickValues, selectedIds, showPoints = false, connectNulls = true, grid, activeX, activeLegendItemId, selectionRange, yBands = [], renderActiveOverlay, shouldAnimate = true, isRendering = true, showLegend, legendProps, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, className, ...rest }) => {
|
|
62
|
+
var LineChart = ({ series, yAxes, xDomain, formatXValue = defaultFormatValue, xScale: xScaleType = "linear", xTickCount, xTickValues, selectedIds, showPoints = false, connectNulls = true, grid, activeX, activeLegendItemId, selectionRange, yBands = [], verticalMarkers = [], renderActiveOverlay, shouldAnimate = true, isRendering = true, showLegend, legendProps, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, className, ...rest }) => {
|
|
63
63
|
const [activeBuiltInLegendItemId, setActiveBuiltInLegendItemId] = useState(null);
|
|
64
64
|
const ariaProps = {
|
|
65
65
|
"aria-describedby": rest["aria-describedby"],
|
|
@@ -88,6 +88,7 @@ var LineChart = ({ series, yAxes, xDomain, formatXValue = defaultFormatValue, xS
|
|
|
88
88
|
points: item.points.filter((point) => point.x >= resolvedXDomain[0] && point.x <= resolvedXDomain[1])
|
|
89
89
|
})) : visibleSeries;
|
|
90
90
|
const renderedActiveX = xScaleType !== "band" || activeX != null && activeX >= resolvedXDomain[0] && activeX <= resolvedXDomain[1] ? activeX : void 0;
|
|
91
|
+
const renderedVerticalMarkers = xScaleType === "band" ? verticalMarkers.filter((marker) => marker.x >= resolvedXDomain[0] && marker.x <= resolvedXDomain[1]) : verticalMarkers;
|
|
91
92
|
const axisLayouts = resolveAxisLayouts(renderedSeries, yAxes, resolvedXDomain, connectNulls);
|
|
92
93
|
const chartMargin = {
|
|
93
94
|
top: CHART_MARGIN_TOP,
|
|
@@ -154,6 +155,7 @@ var LineChart = ({ series, yAxes, xDomain, formatXValue = defaultFormatValue, xS
|
|
|
154
155
|
activeLegendItemId: renderedActiveLegendItemId,
|
|
155
156
|
selectionRange,
|
|
156
157
|
yBands,
|
|
158
|
+
verticalMarkers: renderedVerticalMarkers,
|
|
157
159
|
shouldAnimate,
|
|
158
160
|
isRendering,
|
|
159
161
|
...ariaProps,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LineChart.js","names":[],"sources":["../../../src/components/LineChart/LineChart.tsx"],"sourcesContent":["import { useState, type HTMLAttributes } from 'react';\n\nimport { useResizeObserver } from '@mantine/hooks';\nimport { scaleLinear } from '@visx/scale';\n\nimport { getCategoricalLineChartColor } from '../../utils/chartColors';\nimport {\n CHART_AXIS_LABEL_THICKNESS,\n CHART_TICK_FONT_SIZE,\n CHART_VALUE_AXIS_TICK_LENGTH,\n} from '../../utils/chartConstants';\nimport { cn } from '../../utils/cn';\nimport { getYAxisLeftMargin } from '../../utils/getYAxisLeftMargin';\nimport { ChartLegend, type ChartLegendItem } from '../ChartLegend';\nimport {\n LINE_CHART_DEFAULT_TICK_COUNT,\n LINE_CHART_VALUE_AXIS_THICKNESS,\n} from './constants';\nimport type {\n LineChartDisplayAxis,\n LineChartDisplaySeries,\n LineChartProps,\n LineChartYAxis,\n} from './LineChart.types';\nimport {\n createLineChartXScaleAdapter,\n getLineChartYDomainValues,\n getLineSegments,\n getNumericDomain,\n normalizeLineChartSeries,\n} from './LineChart.utils';\nimport { LineChartPlot } from './LineChartPlot';\n\nexport type {\n LineChartDisplayAxis,\n LineChartDisplaySeries,\n LineChartGrid,\n LineChartInteractionDatum,\n LineChartInteractionPoint,\n LineChartPoint,\n LineChartProps,\n LineChartSelectionRange,\n LineChartSeries,\n LineChartXScale,\n LineChartYBand,\n LineChartYAxis,\n} from './LineChart.types';\n\ntype LineChartAxisLayout = {\n config: LineChartYAxis;\n domain: [number, number];\n nice: boolean;\n thickness: number;\n};\n\nconst DEFAULT_AXIS_ID = 'line-chart-default-axis';\nconst CHART_MARGIN_TOP = 8;\nconst CHART_MARGIN_BOTTOM = 28;\nconst CHART_MARGIN_RIGHT = 8;\nconst DEFAULT_STROKE_WIDTH = 2;\nconst NUMBER_FORMATTER = new Intl.NumberFormat('en-US', {\n maximumFractionDigits: 2,\n});\n\nconst defaultFormatValue = (value: number) => NUMBER_FORMATTER.format(value);\n\nconst resolveAxisLayouts = (\n series: readonly LineChartDisplaySeries[],\n configuredAxes: readonly LineChartYAxis[] | undefined,\n xDomain: readonly [number, number],\n connectNulls: boolean\n): readonly LineChartAxisLayout[] => {\n const axisConfigs = new Map(\n configuredAxes?.map((axis) => [axis.id, axis]) ?? []\n );\n const axisIds = Array.from(\n new Set(configuredAxes?.map((axis) => axis.id) ?? [])\n );\n\n for (const item of series) {\n if (!axisIds.includes(item.yAxisId)) axisIds.push(item.yAxisId);\n }\n if (axisIds.length === 0) axisIds.push(DEFAULT_AXIS_ID);\n\n return axisIds.map((axisId): LineChartAxisLayout => {\n const config = axisConfigs.get(axisId) ?? { id: axisId };\n const values = series\n .filter((item) => item.yAxisId === axisId)\n .flatMap((item) =>\n getLineChartYDomainValues(item.points, xDomain, connectNulls)\n );\n const domain = getNumericDomain(values, config.domain);\n const nice = config.domain == null;\n const tickLabelThickness = getYAxisLeftMargin({\n yDomain: domain,\n formatTick: config.formatValue ?? defaultFormatValue,\n fontSize: CHART_TICK_FONT_SIZE,\n numTicks: config.tickCount ?? LINE_CHART_DEFAULT_TICK_COUNT,\n tickValues: config.tickValues,\n tickLength: CHART_VALUE_AXIS_TICK_LENGTH,\n nice,\n });\n return {\n config,\n domain,\n nice,\n thickness:\n Math.max(LINE_CHART_VALUE_AXIS_THICKNESS, tickLabelThickness) +\n (config.label == null ? 0 : CHART_AXIS_LABEL_THICKNESS),\n };\n });\n};\n\nconst resolveAxes = (\n layouts: readonly LineChartAxisLayout[],\n innerHeight: number\n): readonly LineChartDisplayAxis[] =>\n layouts.map(({ config, domain, nice, thickness }) => ({\n ...config,\n thickness,\n scale: scaleLinear<number>({\n domain,\n range: [innerHeight, 0],\n nice,\n }),\n }));\n\n/**\n * Responsive numeric line chart with nullable observations, multiple axes,\n * legend filtering, configurable grids and x scales, brushing, and\n * consumer-owned tooltip interactions.\n */\nexport const LineChart = ({\n series,\n yAxes,\n xDomain,\n formatXValue = defaultFormatValue,\n xScale: xScaleType = 'linear',\n xTickCount,\n xTickValues,\n selectedIds,\n showPoints = false,\n connectNulls = true,\n grid,\n activeX,\n activeLegendItemId,\n selectionRange,\n yBands = [],\n renderActiveOverlay,\n shouldAnimate = true,\n isRendering = true,\n showLegend,\n legendProps,\n onDatumPointerDown,\n onDatumPointerMove,\n onDatumPointerOut,\n onDatumPointerUp,\n className,\n ...rest\n}: LineChartProps) => {\n const [activeBuiltInLegendItemId, setActiveBuiltInLegendItemId] = useState<\n string | null\n >(null);\n const ariaProps = {\n 'aria-describedby': rest['aria-describedby'],\n 'aria-label': rest['aria-label'],\n };\n const rootProps: HTMLAttributes<HTMLDivElement> = { ...rest };\n delete rootProps['aria-describedby'];\n delete rootProps['aria-label'];\n\n const [containerRef, dimensions] = useResizeObserver();\n const normalizedSeries = normalizeLineChartSeries(series);\n const defaultAxisId = yAxes?.[0]?.id ?? DEFAULT_AXIS_ID;\n const displaySeries: readonly LineChartDisplaySeries[] = normalizedSeries.map(\n (item, index) => ({\n ...item,\n color: item.color ?? getCategoricalLineChartColor(index),\n yAxisId: item.yAxisId ?? defaultAxisId,\n strokeWidth:\n item.strokeWidth == null || !Number.isFinite(item.strokeWidth)\n ? DEFAULT_STROKE_WIDTH\n : Math.max(0, item.strokeWidth),\n opacity:\n item.opacity == null || !Number.isFinite(item.opacity)\n ? 1\n : Math.min(1, Math.max(0, item.opacity)),\n showActiveMarker: item.showActiveMarker ?? true,\n })\n );\n const visibleSeries =\n selectedIds == null || selectedIds.size === 0\n ? displaySeries\n : displaySeries.filter((item) => selectedIds.has(item.id));\n const xValues = visibleSeries.flatMap((item) =>\n item.points.map((point) => point.x)\n );\n const resolvedXDomain = getNumericDomain(xValues, xDomain);\n const uniqueXValues = Array.from(\n new Set(\n xValues.filter(\n (value) => value >= resolvedXDomain[0] && value <= resolvedXDomain[1]\n )\n )\n ).sort((left, right) => left - right);\n const renderedSeries =\n xScaleType === 'band'\n ? visibleSeries.map((item) => ({\n ...item,\n points: item.points.filter(\n (point) =>\n point.x >= resolvedXDomain[0] && point.x <= resolvedXDomain[1]\n ),\n }))\n : visibleSeries;\n const renderedActiveX =\n xScaleType !== 'band' ||\n (activeX != null &&\n activeX >= resolvedXDomain[0] &&\n activeX <= resolvedXDomain[1])\n ? activeX\n : undefined;\n const axisLayouts = resolveAxisLayouts(\n renderedSeries,\n yAxes,\n resolvedXDomain,\n connectNulls\n );\n const chartMargin = {\n top: CHART_MARGIN_TOP,\n right:\n CHART_MARGIN_RIGHT +\n axisLayouts.slice(1).reduce((total, axis) => total + axis.thickness, 0),\n bottom: CHART_MARGIN_BOTTOM,\n left: axisLayouts[0]?.thickness ?? LINE_CHART_VALUE_AXIS_THICKNESS,\n };\n const innerWidth = Math.max(\n 0,\n dimensions.width - chartMargin.left - chartMargin.right\n );\n const innerHeight = Math.max(\n 0,\n dimensions.height - chartMargin.top - chartMargin.bottom\n );\n const xScaleAdapter = createLineChartXScaleAdapter({\n type: xScaleType,\n domain: resolvedXDomain,\n values: uniqueXValues,\n width: innerWidth,\n nice: xDomain == null,\n });\n const axes = resolveAxes(axisLayouts, innerHeight);\n\n const activeXPos =\n renderedActiveX == null ? null : xScaleAdapter.getPosition(renderedActiveX);\n const overlayX =\n activeXPos != null && activeXPos >= 0 && activeXPos <= innerWidth\n ? chartMargin.left + activeXPos\n : null;\n\n const legendItems: readonly ChartLegendItem[] = displaySeries.map((item) => ({\n id: item.id,\n label: item.label,\n markerColor: item.color,\n selected: selectedIds?.has(item.id) ?? false,\n }));\n const hasLegend =\n (showLegend ?? legendItems.length > 1) && legendItems.length > 0;\n const legendLayout = legendProps?.layout ?? 'inline';\n const requestedActiveLegendItemId =\n (hasLegend ? activeBuiltInLegendItemId : null) ?? activeLegendItemId;\n const renderedActiveLegendItemId = renderedSeries.some((item) => {\n if (item.id !== requestedActiveLegendItemId) return false;\n\n const [domainMinimum, domainMaximum] = resolvedXDomain;\n const hasVisiblePoint =\n showPoints &&\n item.points.some(\n (point) =>\n point.y != null &&\n point.x >= domainMinimum &&\n point.x <= domainMaximum\n );\n if (hasVisiblePoint) return true;\n\n return getLineSegments(item.points, connectNulls).some((segment) =>\n segment.some((point, index) => {\n const nextPoint = segment[index + 1];\n return (\n nextPoint != null &&\n point.x <= domainMaximum &&\n nextPoint.x >= domainMinimum\n );\n })\n );\n })\n ? requestedActiveLegendItemId\n : null;\n const showGridRows = grid === false ? false : (grid?.rows ?? true);\n const showGridColumns = grid === false ? false : (grid?.columns ?? true);\n\n const plot = (\n <div ref={containerRef} className=\"relative size-full min-h-0 min-w-0\">\n <LineChartPlot\n width={dimensions.width}\n height={dimensions.height}\n innerWidth={innerWidth}\n innerHeight={innerHeight}\n chartMargin={chartMargin}\n xScale={xScaleAdapter.scale}\n getXPosition={xScaleAdapter.getPosition}\n getXValue={xScaleAdapter.getValue}\n getXRangePosition={xScaleAdapter.getRangePosition}\n uniqueXValues={uniqueXValues}\n axes={axes}\n series={renderedSeries}\n formatXValue={formatXValue}\n xTickCount={xTickCount}\n xTickValues={xTickValues}\n showPoints={showPoints}\n connectNulls={connectNulls}\n showGridRows={showGridRows}\n showGridColumns={showGridColumns}\n activeX={renderedActiveX}\n activeLegendItemId={renderedActiveLegendItemId}\n selectionRange={selectionRange}\n yBands={yBands}\n shouldAnimate={shouldAnimate}\n isRendering={isRendering}\n {...ariaProps}\n onDatumPointerDown={onDatumPointerDown}\n onDatumPointerMove={onDatumPointerMove}\n onDatumPointerOut={onDatumPointerOut}\n onDatumPointerUp={onDatumPointerUp}\n />\n {renderActiveOverlay != null &&\n overlayX != null &&\n renderedActiveX != null &&\n renderActiveOverlay({ xPosition: overlayX, activeX: renderedActiveX })}\n </div>\n );\n\n const legend = (\n <ChartLegend\n aria-label={`${ariaProps['aria-label']} legend`}\n {...legendProps}\n layout={legendLayout}\n items={legendItems}\n onItemActiveChange={(item) => {\n setActiveBuiltInLegendItemId(item?.id ?? null);\n legendProps?.onItemActiveChange?.(item);\n }}\n />\n );\n\n return (\n <div\n className={cn('size-full min-h-0 min-w-0 @container', className)}\n {...rootProps}\n >\n {hasLegend ? (\n <div\n className={cn(\n 'grid size-full min-h-0 min-w-0 grid-cols-1',\n legendLayout === 'list'\n ? 'grid-rows-[minmax(0,1fr)_minmax(0,35%)] gap-space-3 @[560px]:grid-cols-[minmax(0,1fr)_minmax(10rem,30%)] @[560px]:grid-rows-1 @[560px]:gap-space-5'\n : 'grid-rows-[minmax(0,1fr)_auto] gap-space-2'\n )}\n >\n {plot}\n {legendLayout === 'list' ? (\n <div className=\"scroll-mask-t scroll-mask-b min-h-0 min-w-0 overflow-y-auto overflow-x-hidden\">\n {legend}\n </div>\n ) : (\n legend\n )}\n </div>\n ) : (\n plot\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LineChart.js","names":[],"sources":["../../../src/components/LineChart/LineChart.tsx"],"sourcesContent":["import { useState, type HTMLAttributes } from 'react';\n\nimport { useResizeObserver } from '@mantine/hooks';\nimport { scaleLinear } from '@visx/scale';\n\nimport { getCategoricalLineChartColor } from '../../utils/chartColors';\nimport {\n CHART_AXIS_LABEL_THICKNESS,\n CHART_TICK_FONT_SIZE,\n CHART_VALUE_AXIS_TICK_LENGTH,\n} from '../../utils/chartConstants';\nimport { cn } from '../../utils/cn';\nimport { getYAxisLeftMargin } from '../../utils/getYAxisLeftMargin';\nimport { ChartLegend, type ChartLegendItem } from '../ChartLegend';\nimport {\n LINE_CHART_DEFAULT_TICK_COUNT,\n LINE_CHART_VALUE_AXIS_THICKNESS,\n} from './constants';\nimport type {\n LineChartDisplayAxis,\n LineChartDisplaySeries,\n LineChartProps,\n LineChartYAxis,\n} from './LineChart.types';\nimport {\n createLineChartXScaleAdapter,\n getLineChartYDomainValues,\n getLineSegments,\n getNumericDomain,\n normalizeLineChartSeries,\n} from './LineChart.utils';\nimport { LineChartPlot } from './LineChartPlot';\n\nexport type {\n LineChartDisplayAxis,\n LineChartDisplaySeries,\n LineChartGrid,\n LineChartInteractionDatum,\n LineChartInteractionPoint,\n LineChartPoint,\n LineChartProps,\n LineChartSelectionRange,\n LineChartSeries,\n LineChartVerticalMarker,\n LineChartXScale,\n LineChartYBand,\n LineChartYAxis,\n} from './LineChart.types';\n\ntype LineChartAxisLayout = {\n config: LineChartYAxis;\n domain: [number, number];\n nice: boolean;\n thickness: number;\n};\n\nconst DEFAULT_AXIS_ID = 'line-chart-default-axis';\nconst CHART_MARGIN_TOP = 8;\nconst CHART_MARGIN_BOTTOM = 28;\nconst CHART_MARGIN_RIGHT = 8;\nconst DEFAULT_STROKE_WIDTH = 2;\nconst NUMBER_FORMATTER = new Intl.NumberFormat('en-US', {\n maximumFractionDigits: 2,\n});\n\nconst defaultFormatValue = (value: number) => NUMBER_FORMATTER.format(value);\n\nconst resolveAxisLayouts = (\n series: readonly LineChartDisplaySeries[],\n configuredAxes: readonly LineChartYAxis[] | undefined,\n xDomain: readonly [number, number],\n connectNulls: boolean\n): readonly LineChartAxisLayout[] => {\n const axisConfigs = new Map(\n configuredAxes?.map((axis) => [axis.id, axis]) ?? []\n );\n const axisIds = Array.from(\n new Set(configuredAxes?.map((axis) => axis.id) ?? [])\n );\n\n for (const item of series) {\n if (!axisIds.includes(item.yAxisId)) axisIds.push(item.yAxisId);\n }\n if (axisIds.length === 0) axisIds.push(DEFAULT_AXIS_ID);\n\n return axisIds.map((axisId): LineChartAxisLayout => {\n const config = axisConfigs.get(axisId) ?? { id: axisId };\n const values = series\n .filter((item) => item.yAxisId === axisId)\n .flatMap((item) =>\n getLineChartYDomainValues(item.points, xDomain, connectNulls)\n );\n const domain = getNumericDomain(values, config.domain);\n const nice = config.domain == null;\n const tickLabelThickness = getYAxisLeftMargin({\n yDomain: domain,\n formatTick: config.formatValue ?? defaultFormatValue,\n fontSize: CHART_TICK_FONT_SIZE,\n numTicks: config.tickCount ?? LINE_CHART_DEFAULT_TICK_COUNT,\n tickValues: config.tickValues,\n tickLength: CHART_VALUE_AXIS_TICK_LENGTH,\n nice,\n });\n return {\n config,\n domain,\n nice,\n thickness:\n Math.max(LINE_CHART_VALUE_AXIS_THICKNESS, tickLabelThickness) +\n (config.label == null ? 0 : CHART_AXIS_LABEL_THICKNESS),\n };\n });\n};\n\nconst resolveAxes = (\n layouts: readonly LineChartAxisLayout[],\n innerHeight: number\n): readonly LineChartDisplayAxis[] =>\n layouts.map(({ config, domain, nice, thickness }) => ({\n ...config,\n thickness,\n scale: scaleLinear<number>({\n domain,\n range: [innerHeight, 0],\n nice,\n }),\n }));\n\n/**\n * Responsive numeric line chart with nullable observations, multiple axes,\n * legend filtering, configurable grids and x scales, brushing, and\n * consumer-owned tooltip interactions.\n */\nexport const LineChart = ({\n series,\n yAxes,\n xDomain,\n formatXValue = defaultFormatValue,\n xScale: xScaleType = 'linear',\n xTickCount,\n xTickValues,\n selectedIds,\n showPoints = false,\n connectNulls = true,\n grid,\n activeX,\n activeLegendItemId,\n selectionRange,\n yBands = [],\n verticalMarkers = [],\n renderActiveOverlay,\n shouldAnimate = true,\n isRendering = true,\n showLegend,\n legendProps,\n onDatumPointerDown,\n onDatumPointerMove,\n onDatumPointerOut,\n onDatumPointerUp,\n className,\n ...rest\n}: LineChartProps) => {\n const [activeBuiltInLegendItemId, setActiveBuiltInLegendItemId] = useState<\n string | null\n >(null);\n const ariaProps = {\n 'aria-describedby': rest['aria-describedby'],\n 'aria-label': rest['aria-label'],\n };\n const rootProps: HTMLAttributes<HTMLDivElement> = { ...rest };\n delete rootProps['aria-describedby'];\n delete rootProps['aria-label'];\n\n const [containerRef, dimensions] = useResizeObserver();\n const normalizedSeries = normalizeLineChartSeries(series);\n const defaultAxisId = yAxes?.[0]?.id ?? DEFAULT_AXIS_ID;\n const displaySeries: readonly LineChartDisplaySeries[] = normalizedSeries.map(\n (item, index) => ({\n ...item,\n color: item.color ?? getCategoricalLineChartColor(index),\n yAxisId: item.yAxisId ?? defaultAxisId,\n strokeWidth:\n item.strokeWidth == null || !Number.isFinite(item.strokeWidth)\n ? DEFAULT_STROKE_WIDTH\n : Math.max(0, item.strokeWidth),\n opacity:\n item.opacity == null || !Number.isFinite(item.opacity)\n ? 1\n : Math.min(1, Math.max(0, item.opacity)),\n showActiveMarker: item.showActiveMarker ?? true,\n })\n );\n const visibleSeries =\n selectedIds == null || selectedIds.size === 0\n ? displaySeries\n : displaySeries.filter((item) => selectedIds.has(item.id));\n const xValues = visibleSeries.flatMap((item) =>\n item.points.map((point) => point.x)\n );\n const resolvedXDomain = getNumericDomain(xValues, xDomain);\n const uniqueXValues = Array.from(\n new Set(\n xValues.filter(\n (value) => value >= resolvedXDomain[0] && value <= resolvedXDomain[1]\n )\n )\n ).sort((left, right) => left - right);\n const renderedSeries =\n xScaleType === 'band'\n ? visibleSeries.map((item) => ({\n ...item,\n points: item.points.filter(\n (point) =>\n point.x >= resolvedXDomain[0] && point.x <= resolvedXDomain[1]\n ),\n }))\n : visibleSeries;\n const renderedActiveX =\n xScaleType !== 'band' ||\n (activeX != null &&\n activeX >= resolvedXDomain[0] &&\n activeX <= resolvedXDomain[1])\n ? activeX\n : undefined;\n // A band scale extrapolates past its outer buckets, so a marker just outside\n // the domain can still project inside the plot.\n const renderedVerticalMarkers =\n xScaleType === 'band'\n ? verticalMarkers.filter(\n (marker) =>\n marker.x >= resolvedXDomain[0] && marker.x <= resolvedXDomain[1]\n )\n : verticalMarkers;\n const axisLayouts = resolveAxisLayouts(\n renderedSeries,\n yAxes,\n resolvedXDomain,\n connectNulls\n );\n const chartMargin = {\n top: CHART_MARGIN_TOP,\n right:\n CHART_MARGIN_RIGHT +\n axisLayouts.slice(1).reduce((total, axis) => total + axis.thickness, 0),\n bottom: CHART_MARGIN_BOTTOM,\n left: axisLayouts[0]?.thickness ?? LINE_CHART_VALUE_AXIS_THICKNESS,\n };\n const innerWidth = Math.max(\n 0,\n dimensions.width - chartMargin.left - chartMargin.right\n );\n const innerHeight = Math.max(\n 0,\n dimensions.height - chartMargin.top - chartMargin.bottom\n );\n const xScaleAdapter = createLineChartXScaleAdapter({\n type: xScaleType,\n domain: resolvedXDomain,\n values: uniqueXValues,\n width: innerWidth,\n nice: xDomain == null,\n });\n const axes = resolveAxes(axisLayouts, innerHeight);\n\n const activeXPos =\n renderedActiveX == null ? null : xScaleAdapter.getPosition(renderedActiveX);\n const overlayX =\n activeXPos != null && activeXPos >= 0 && activeXPos <= innerWidth\n ? chartMargin.left + activeXPos\n : null;\n\n const legendItems: readonly ChartLegendItem[] = displaySeries.map((item) => ({\n id: item.id,\n label: item.label,\n markerColor: item.color,\n selected: selectedIds?.has(item.id) ?? false,\n }));\n const hasLegend =\n (showLegend ?? legendItems.length > 1) && legendItems.length > 0;\n const legendLayout = legendProps?.layout ?? 'inline';\n const requestedActiveLegendItemId =\n (hasLegend ? activeBuiltInLegendItemId : null) ?? activeLegendItemId;\n const renderedActiveLegendItemId = renderedSeries.some((item) => {\n if (item.id !== requestedActiveLegendItemId) return false;\n\n const [domainMinimum, domainMaximum] = resolvedXDomain;\n const hasVisiblePoint =\n showPoints &&\n item.points.some(\n (point) =>\n point.y != null &&\n point.x >= domainMinimum &&\n point.x <= domainMaximum\n );\n if (hasVisiblePoint) return true;\n\n return getLineSegments(item.points, connectNulls).some((segment) =>\n segment.some((point, index) => {\n const nextPoint = segment[index + 1];\n return (\n nextPoint != null &&\n point.x <= domainMaximum &&\n nextPoint.x >= domainMinimum\n );\n })\n );\n })\n ? requestedActiveLegendItemId\n : null;\n const showGridRows = grid === false ? false : (grid?.rows ?? true);\n const showGridColumns = grid === false ? false : (grid?.columns ?? true);\n\n const plot = (\n <div ref={containerRef} className=\"relative size-full min-h-0 min-w-0\">\n <LineChartPlot\n width={dimensions.width}\n height={dimensions.height}\n innerWidth={innerWidth}\n innerHeight={innerHeight}\n chartMargin={chartMargin}\n xScale={xScaleAdapter.scale}\n getXPosition={xScaleAdapter.getPosition}\n getXValue={xScaleAdapter.getValue}\n getXRangePosition={xScaleAdapter.getRangePosition}\n uniqueXValues={uniqueXValues}\n axes={axes}\n series={renderedSeries}\n formatXValue={formatXValue}\n xTickCount={xTickCount}\n xTickValues={xTickValues}\n showPoints={showPoints}\n connectNulls={connectNulls}\n showGridRows={showGridRows}\n showGridColumns={showGridColumns}\n activeX={renderedActiveX}\n activeLegendItemId={renderedActiveLegendItemId}\n selectionRange={selectionRange}\n yBands={yBands}\n verticalMarkers={renderedVerticalMarkers}\n shouldAnimate={shouldAnimate}\n isRendering={isRendering}\n {...ariaProps}\n onDatumPointerDown={onDatumPointerDown}\n onDatumPointerMove={onDatumPointerMove}\n onDatumPointerOut={onDatumPointerOut}\n onDatumPointerUp={onDatumPointerUp}\n />\n {renderActiveOverlay != null &&\n overlayX != null &&\n renderedActiveX != null &&\n renderActiveOverlay({ xPosition: overlayX, activeX: renderedActiveX })}\n </div>\n );\n\n const legend = (\n <ChartLegend\n aria-label={`${ariaProps['aria-label']} legend`}\n {...legendProps}\n layout={legendLayout}\n items={legendItems}\n onItemActiveChange={(item) => {\n setActiveBuiltInLegendItemId(item?.id ?? null);\n legendProps?.onItemActiveChange?.(item);\n }}\n />\n );\n\n return (\n <div\n className={cn('size-full min-h-0 min-w-0 @container', className)}\n {...rootProps}\n >\n {hasLegend ? (\n <div\n className={cn(\n 'grid size-full min-h-0 min-w-0 grid-cols-1',\n legendLayout === 'list'\n ? 'grid-rows-[minmax(0,1fr)_minmax(0,35%)] gap-space-3 @[560px]:grid-cols-[minmax(0,1fr)_minmax(10rem,30%)] @[560px]:grid-rows-1 @[560px]:gap-space-5'\n : 'grid-rows-[minmax(0,1fr)_auto] gap-space-2'\n )}\n >\n {plot}\n {legendLayout === 'list' ? (\n <div className=\"scroll-mask-t scroll-mask-b min-h-0 min-w-0 overflow-y-auto overflow-x-hidden\">\n {legend}\n </div>\n ) : (\n legend\n )}\n </div>\n ) : (\n plot\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -57,6 +57,20 @@ export type LineChartGrid = {
|
|
|
57
57
|
/** Defaults to true. Grid guides are dashed. */
|
|
58
58
|
columns?: boolean;
|
|
59
59
|
};
|
|
60
|
+
export type LineChartVerticalMarkerVariant = 'default' | 'error';
|
|
61
|
+
/**
|
|
62
|
+
* A dashed vertical marker for a moment on the x axis, such as a deploy or an
|
|
63
|
+
* incident. Drawn under the series so it annotates the data rather than
|
|
64
|
+
* competing with it.
|
|
65
|
+
*/
|
|
66
|
+
export type LineChartVerticalMarker = {
|
|
67
|
+
id: string;
|
|
68
|
+
x: number;
|
|
69
|
+
/** Defaults to `default`. Use `error` for an incident or failure. */
|
|
70
|
+
variant?: LineChartVerticalMarkerVariant;
|
|
71
|
+
/** Rendered at the top of the marker. */
|
|
72
|
+
label?: string;
|
|
73
|
+
};
|
|
60
74
|
export type LineChartYBand = {
|
|
61
75
|
id: string;
|
|
62
76
|
/** Defaults to the primary y axis. */
|
|
@@ -116,6 +130,8 @@ export type LineChartProps = Omit<HTMLAttributes<HTMLDivElement>, 'children' | '
|
|
|
116
130
|
selectionRange?: LineChartSelectionRange;
|
|
117
131
|
/** Background ranges such as warning or threshold regions. */
|
|
118
132
|
yBands?: readonly LineChartYBand[];
|
|
133
|
+
/** Vertical markers for moments on the x axis, such as a deploy. */
|
|
134
|
+
verticalMarkers?: readonly LineChartVerticalMarker[];
|
|
119
135
|
/**
|
|
120
136
|
* Rendered inside the plot's relative container at the resolved activeX
|
|
121
137
|
* position (SVG-container-relative pixels). Only invoked when activeX is
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AriaAttributes, type PointerEvent } from 'react';
|
|
2
2
|
import type { scaleBand, scaleLinear } from '@visx/scale';
|
|
3
|
-
import type { LineChartDisplayAxis, LineChartDisplaySeries, LineChartInteractionDatum, LineChartSelectionRange, LineChartYBand } from './LineChart.types.js';
|
|
3
|
+
import type { LineChartDisplayAxis, LineChartDisplaySeries, LineChartInteractionDatum, LineChartSelectionRange, LineChartVerticalMarker, LineChartYBand } from './LineChart.types.js';
|
|
4
4
|
type LineChartPlotProps = {
|
|
5
5
|
width: number;
|
|
6
6
|
height: number;
|
|
@@ -30,6 +30,7 @@ type LineChartPlotProps = {
|
|
|
30
30
|
activeLegendItemId?: string | null;
|
|
31
31
|
selectionRange?: LineChartSelectionRange;
|
|
32
32
|
yBands: readonly LineChartYBand[];
|
|
33
|
+
verticalMarkers: readonly LineChartVerticalMarker[];
|
|
33
34
|
shouldAnimate: boolean;
|
|
34
35
|
isRendering: boolean;
|
|
35
36
|
'aria-describedby'?: AriaAttributes['aria-describedby'];
|
|
@@ -39,5 +40,5 @@ type LineChartPlotProps = {
|
|
|
39
40
|
onDatumPointerOut?: () => void;
|
|
40
41
|
onDatumPointerUp?: (datum: LineChartInteractionDatum, event: PointerEvent<SVGSVGElement>) => void;
|
|
41
42
|
};
|
|
42
|
-
export declare const LineChartPlot: ({ width, height, innerWidth, innerHeight, chartMargin, xScale, getXPosition, getXValue, getXRangePosition, uniqueXValues, axes, series, formatXValue, xTickCount, xTickValues, showPoints, connectNulls, showGridRows, showGridColumns, activeX, activeLegendItemId, selectionRange, yBands, shouldAnimate, isRendering, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, ...ariaProps }: LineChartPlotProps) => import("react").JSX.Element;
|
|
43
|
+
export declare const LineChartPlot: ({ width, height, innerWidth, innerHeight, chartMargin, xScale, getXPosition, getXValue, getXRangePosition, uniqueXValues, axes, series, formatXValue, xTickCount, xTickValues, showPoints, connectNulls, showGridRows, showGridColumns, activeX, activeLegendItemId, selectionRange, yBands, verticalMarkers, shouldAnimate, isRendering, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, ...ariaProps }: LineChartPlotProps) => import("react").JSX.Element;
|
|
43
44
|
export {};
|
|
@@ -5,6 +5,7 @@ import { getChartTickValues, getNearestChartValue } from "../../utils/chartValue
|
|
|
5
5
|
import { getChartAxisLabelOffset, getSecondaryChartAxisOffset } from "../../utils/chartAxisLayout.js";
|
|
6
6
|
import "./constants.js";
|
|
7
7
|
import { getLineSegments, getSelectionBounds } from "./LineChart.utils.js";
|
|
8
|
+
import { LineChartVerticalMarkers } from "./LineChartVerticalMarkers.js";
|
|
8
9
|
import { useId, useRef } from "react";
|
|
9
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import { animated, useReducedMotion, useSpring } from "@react-spring/web";
|
|
@@ -26,7 +27,7 @@ var getInteractionDatum = (nearestX, series, xPosition, pointerX) => ({
|
|
|
26
27
|
value: item.points.find((point) => point.x === nearestX)?.y ?? null
|
|
27
28
|
}))
|
|
28
29
|
});
|
|
29
|
-
var LineChartPlot = ({ width, height, innerWidth, innerHeight, chartMargin, xScale, getXPosition, getXValue, getXRangePosition, uniqueXValues, axes, series, formatXValue, xTickCount, xTickValues, showPoints, connectNulls, showGridRows, showGridColumns, activeX, activeLegendItemId, selectionRange, yBands, shouldAnimate, isRendering, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, ...ariaProps }) => {
|
|
30
|
+
var LineChartPlot = ({ width, height, innerWidth, innerHeight, chartMargin, xScale, getXPosition, getXValue, getXRangePosition, uniqueXValues, axes, series, formatXValue, xTickCount, xTickValues, showPoints, connectNulls, showGridRows, showGridColumns, activeX, activeLegendItemId, selectionRange, yBands, verticalMarkers, shouldAnimate, isRendering, onDatumPointerDown, onDatumPointerMove, onDatumPointerOut, onDatumPointerUp, ...ariaProps }) => {
|
|
30
31
|
const activePointerId = useRef(null);
|
|
31
32
|
const clipPathId = `line-chart-${useId().replace(/:/g, "")}`;
|
|
32
33
|
const revealClipPathId = `${clipPathId}-reveal`;
|
|
@@ -153,6 +154,12 @@ var LineChartPlot = ({ width, height, innerWidth, innerHeight, chartMargin, xSca
|
|
|
153
154
|
stroke: "var(--border-subtle)",
|
|
154
155
|
strokeDasharray: "4 2"
|
|
155
156
|
}),
|
|
157
|
+
/* @__PURE__ */ jsx(LineChartVerticalMarkers, {
|
|
158
|
+
verticalMarkers,
|
|
159
|
+
innerWidth,
|
|
160
|
+
innerHeight,
|
|
161
|
+
getXPosition
|
|
162
|
+
}),
|
|
156
163
|
/* @__PURE__ */ jsx(AxisBottom, {
|
|
157
164
|
scale: xScale,
|
|
158
165
|
top: innerHeight,
|