@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.
Files changed (185) hide show
  1. package/dist/_chunks/{LoadingIndicator-DgtU9aqD.js → LoadingIndicator-CDcArsiM.js} +13 -12
  2. package/dist/_chunks/LoadingIndicator-CDcArsiM.js.map +1 -0
  3. package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js +270 -0
  4. package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js.map +1 -0
  5. package/dist/catalog.json +527 -408
  6. package/dist/components/Attachment/Attachment.d.ts +21 -0
  7. package/dist/components/Attachment/Attachment.js +165 -0
  8. package/dist/components/Attachment/Attachment.js.map +1 -0
  9. package/dist/components/Attachment/AttachmentList.d.ts +5 -0
  10. package/dist/components/Attachment/AttachmentList.js +32 -0
  11. package/dist/components/Attachment/AttachmentList.js.map +1 -0
  12. package/dist/components/Attachment/index.d.ts +4 -0
  13. package/dist/components/Attachment/index.js +3 -0
  14. package/dist/components/Attachment/render-attachment-icon.d.ts +3 -0
  15. package/dist/components/Attachment/render-attachment-icon.js +2 -0
  16. package/dist/components/Avatar/Avatar.js +2 -2
  17. package/dist/components/Badge/Badge.d.ts +2 -0
  18. package/dist/components/Badge/Badge.js +12 -1
  19. package/dist/components/Badge/Badge.js.map +1 -1
  20. package/dist/components/Banner/Banner.js +1 -1
  21. package/dist/components/Button/Button.js +1 -1
  22. package/dist/components/Button/constants.js +2 -2
  23. package/dist/components/Button/constants.js.map +1 -1
  24. package/dist/components/ChartCard/ChartCard.d.ts +1 -4
  25. package/dist/components/ChartCard/ChartCard.js +2 -2
  26. package/dist/components/ChartCard/ChartCard.js.map +1 -1
  27. package/dist/components/ChartCard/ChartCardSkeleton.d.ts +1 -1
  28. package/dist/components/ChartCard/ChartCardSkeleton.js +37 -0
  29. package/dist/components/ChartCard/ChartCardSkeleton.js.map +1 -1
  30. package/dist/components/ChartLegend/ChartLegend.js +2 -2
  31. package/dist/components/ChartLegend/ChartLegendItemRenderer.js +2 -2
  32. package/dist/components/ChartLegend/ChartLegendItemRenderer.js.map +1 -1
  33. package/dist/components/Code/Code.utils.d.ts +3 -2
  34. package/dist/components/Code/Code.utils.js +7 -3
  35. package/dist/components/Code/Code.utils.js.map +1 -1
  36. package/dist/components/Code/CodeLanguageSelect.d.ts +7 -0
  37. package/dist/components/Code/CodeLanguageSelect.js +29 -0
  38. package/dist/components/Code/CodeLanguageSelect.js.map +1 -0
  39. package/dist/components/Code/CodeLite.js +3 -0
  40. package/dist/components/Code/CodeLite.js.map +1 -1
  41. package/dist/components/Code/CodeToolbar.d.ts +7 -0
  42. package/dist/components/Code/CodeToolbar.js +19 -0
  43. package/dist/components/Code/CodeToolbar.js.map +1 -0
  44. package/dist/components/Code/codeLanguages.d.ts +7 -0
  45. package/dist/components/Code/codeLanguages.js +59 -0
  46. package/dist/components/Code/codeLanguages.js.map +1 -0
  47. package/dist/components/Code/index.d.ts +15 -2
  48. package/dist/components/Code/index.js +70 -50
  49. package/dist/components/Code/index.js.map +1 -1
  50. package/dist/components/Code/types.d.ts +1 -1
  51. package/dist/components/Command/Command.d.ts +10 -13
  52. package/dist/components/Command/Command.js +7 -2
  53. package/dist/components/Command/Command.js.map +1 -1
  54. package/dist/components/Command/CommandMenu.d.ts +7 -1
  55. package/dist/components/Command/CommandMenu.js +17 -9
  56. package/dist/components/Command/CommandMenu.js.map +1 -1
  57. package/dist/components/ContextMenu/ContextMenu.d.ts +6 -0
  58. package/dist/components/ContextMenu/ContextMenu.js +3 -2
  59. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  60. package/dist/components/CopyButton/CopyButton.js +2 -2
  61. package/dist/components/Dialog/index.js +2 -2
  62. package/dist/components/DropdownMenu/DropdownMenu.d.ts +11 -0
  63. package/dist/components/DropdownMenu/DropdownMenu.js +5 -4
  64. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  65. package/dist/components/ErrorMessage/ErrorMessage.js +1 -1
  66. package/dist/components/GroupedTabs/GroupedTabs.d.ts +5 -2
  67. package/dist/components/GroupedTabs/GroupedTabs.js +33 -26
  68. package/dist/components/GroupedTabs/GroupedTabs.js.map +1 -1
  69. package/dist/components/IconButton/IconButton.js +2 -2
  70. package/dist/components/Input/Input.js +1 -1
  71. package/dist/components/LineChart/LineChart.d.ts +2 -2
  72. package/dist/components/LineChart/LineChart.js +3 -1
  73. package/dist/components/LineChart/LineChart.js.map +1 -1
  74. package/dist/components/LineChart/LineChart.types.d.ts +16 -0
  75. package/dist/components/LineChart/LineChartPlot.d.ts +3 -2
  76. package/dist/components/LineChart/LineChartPlot.js +8 -1
  77. package/dist/components/LineChart/LineChartPlot.js.map +1 -1
  78. package/dist/components/LineChart/LineChartVerticalMarkers.d.ts +8 -0
  79. package/dist/components/LineChart/LineChartVerticalMarkers.js +47 -0
  80. package/dist/components/LineChart/LineChartVerticalMarkers.js.map +1 -0
  81. package/dist/components/LineChart/index.d.ts +1 -1
  82. package/dist/components/Logo/index.js +11 -6
  83. package/dist/components/Logo/index.js.map +1 -1
  84. package/dist/components/MetricChart/MetricChart.d.ts +2 -0
  85. package/dist/components/MetricChart/MetricChart.js +15 -4
  86. package/dist/components/MetricChart/MetricChart.js.map +1 -1
  87. package/dist/components/MetricChart/index.d.ts +1 -1
  88. package/dist/components/Pane/Pane.js +2 -2
  89. package/dist/components/SankeyChart/AnimatedSankeyLink.d.ts +17 -0
  90. package/dist/components/SankeyChart/AnimatedSankeyLink.js +44 -0
  91. package/dist/components/SankeyChart/AnimatedSankeyLink.js.map +1 -0
  92. package/dist/components/SankeyChart/AnimatedSankeyNode.d.ts +16 -0
  93. package/dist/components/SankeyChart/AnimatedSankeyNode.js +42 -0
  94. package/dist/components/SankeyChart/AnimatedSankeyNode.js.map +1 -0
  95. package/dist/components/SankeyChart/SankeyChart.d.ts +42 -0
  96. package/dist/components/SankeyChart/SankeyChart.js +185 -0
  97. package/dist/components/SankeyChart/SankeyChart.js.map +1 -0
  98. package/dist/components/SankeyChart/SankeyChart.types.d.ts +90 -0
  99. package/dist/components/SankeyChart/SankeyChart.types.js +0 -0
  100. package/dist/components/SankeyChart/SankeyChart.utils.d.ts +21 -0
  101. package/dist/components/SankeyChart/SankeyChart.utils.js +299 -0
  102. package/dist/components/SankeyChart/SankeyChart.utils.js.map +1 -0
  103. package/dist/components/SankeyChart/SankeyNodeLabel.d.ts +10 -0
  104. package/dist/components/SankeyChart/SankeyNodeLabel.js +30 -0
  105. package/dist/components/SankeyChart/SankeyNodeLabel.js.map +1 -0
  106. package/dist/components/SankeyChart/SankeyNodeLabel.utils.d.ts +9 -0
  107. package/dist/components/SankeyChart/SankeyNodeLabel.utils.js +29 -0
  108. package/dist/components/SankeyChart/SankeyNodeLabel.utils.js.map +1 -0
  109. package/dist/components/SankeyChart/constants.d.ts +22 -0
  110. package/dist/components/SankeyChart/constants.js +27 -0
  111. package/dist/components/SankeyChart/constants.js.map +1 -0
  112. package/dist/components/SankeyChart/index.d.ts +5 -0
  113. package/dist/components/SankeyChart/index.js +4 -0
  114. package/dist/components/Select/Select.js +5 -5
  115. package/dist/components/Select/Select.js.map +1 -1
  116. package/dist/components/Select/SelectPrimitive.d.ts +6 -0
  117. package/dist/components/Select/SelectPrimitive.js +3 -2
  118. package/dist/components/Select/SelectPrimitive.js.map +1 -1
  119. package/dist/components/Slider/Slider.js +10 -3
  120. package/dist/components/Slider/Slider.js.map +1 -1
  121. package/dist/components/SplitViewPane/SplitViewPane.d.ts +2 -1
  122. package/dist/components/SplitViewPane/SplitViewPane.js +113 -219
  123. package/dist/components/SplitViewPane/SplitViewPane.js.map +1 -1
  124. package/dist/components/SplitViewPane/SplitViewPaneHeader.js +2 -2
  125. package/dist/components/SplitViewPane/useShowChildren.d.ts +5 -0
  126. package/dist/components/SplitViewPane/useShowChildren.js +14 -0
  127. package/dist/components/SplitViewPane/useShowChildren.js.map +1 -0
  128. package/dist/components/SplitViewPane/useSplitViewPaneResize.d.ts +21 -0
  129. package/dist/components/SplitViewPane/useSplitViewPaneResize.js +145 -0
  130. package/dist/components/SplitViewPane/useSplitViewPaneResize.js.map +1 -0
  131. package/dist/components/Textarea/Textarea.d.ts +4 -3
  132. package/dist/components/Textarea/Textarea.js +7 -21
  133. package/dist/components/Textarea/Textarea.js.map +1 -1
  134. package/dist/components/ThinkingState/LoadingIndicator.d.ts +1 -0
  135. package/dist/components/ThinkingState/LoadingIndicator.js +1 -1
  136. package/dist/components/ThinkingState/ThinkingState.d.ts +1 -0
  137. package/dist/components/ThinkingState/ThinkingState.js +5 -4
  138. package/dist/components/ThinkingState/ThinkingState.js.map +1 -1
  139. package/dist/components/ThinkingState/index.js +1 -1
  140. package/dist/components/Toast/index.js +33 -28
  141. package/dist/components/Toast/index.js.map +1 -1
  142. package/dist/components/Tooltip/Tooltip.js +2 -2
  143. package/dist/components/Typeahead/Typeahead.js +18 -8
  144. package/dist/components/Typeahead/Typeahead.js.map +1 -1
  145. package/dist/components/Typeahead/Typeahead.types.d.ts +2 -0
  146. package/dist/components/Typeahead/TypeaheadParts.d.ts +3 -1
  147. package/dist/components/Typeahead/TypeaheadParts.js +7 -6
  148. package/dist/components/Typeahead/TypeaheadParts.js.map +1 -1
  149. package/dist/components/Typeahead/TypeaheadView.js +19 -13
  150. package/dist/components/Typeahead/TypeaheadView.js.map +1 -1
  151. package/dist/components/Typeahead/useTypeaheadDomSync.d.ts +1 -1
  152. package/dist/components/Typeahead/useTypeaheadDomSync.js +14 -8
  153. package/dist/components/Typeahead/useTypeaheadDomSync.js.map +1 -1
  154. package/dist/components/Typeahead/useTypeaheadUncontrolledInput.d.ts +2 -1
  155. package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js +10 -9
  156. package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js.map +1 -1
  157. package/dist/components/UnsavedChangesDialog.js +2 -2
  158. package/dist/components/index.d.ts +3 -1
  159. package/dist/components/index.js +7 -5
  160. package/dist/components.css +22 -18
  161. package/dist/hooks/useDebouncedCommit.d.ts +14 -0
  162. package/dist/hooks/useDebouncedCommit.js +35 -0
  163. package/dist/hooks/useDebouncedCommit.js.map +1 -0
  164. package/dist/icons/LongTailPhosphorIcons.js +1 -1
  165. package/dist/icons/PaddedPhosphorIcons.d.ts +1 -0
  166. package/dist/icons/PaddedPhosphorIcons.js +4 -2
  167. package/dist/icons/PaddedPhosphorIcons.js.map +1 -1
  168. package/dist/icons/WeightedPhosphorIcons.d.ts +1 -0
  169. package/dist/icons/WeightedPhosphorIcons.js +3 -1
  170. package/dist/icons/WeightedPhosphorIcons.js.map +1 -1
  171. package/dist/icons/index.js +3 -3
  172. package/dist/index.js +7 -5
  173. package/dist/utilities.css +1 -1
  174. package/dist/utils/chartColors.d.ts +2 -1
  175. package/dist/utils/chartColors.js +2 -1
  176. package/dist/utils/chartColors.js.map +1 -1
  177. package/dist/utils/componentSizes.d.ts +0 -1
  178. package/dist/utils/componentSizes.js.map +1 -1
  179. package/dist/utils/lazy-codemirror-loader.d.ts +0 -2
  180. package/dist/utils/lazy-codemirror-loader.js +1 -3
  181. package/dist/utils/lazy-codemirror-loader.js.map +1 -1
  182. package/docs/DESIGN.md +17 -0
  183. package/docs/STYLES.md +4 -1
  184. package/package.json +21 -8
  185. 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("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", inset && "pl-space-6", className),
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("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", inset && "pl-space-6", className),
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
- size?: 'xs' | 'sm' | 'md';
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
- animate: false
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 && container) {
20
- const containerRect = container.getBoundingClientRect();
21
- const buttonRect = activeButton.getBoundingClientRect();
22
- const roundValue = size === "xs" ? "1px" : size === "sm" ? "2px" : "4px";
23
- const top = buttonRect.top - containerRect.top - container.clientTop;
24
- const bottom = containerRect.bottom - buttonRect.bottom - container.clientTop;
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
- size,
35
- value,
36
- options
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 dark:border-muted", size === "xs" ? "gap-px" : "gap-0.5", size === "xs" ? "rounded-[2px]" : size === "sm" ? "rounded-[3px]" : "rounded-sm", className),
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.animate ? "clip-path 0.25s ease-out, background-color 0.25s ease-out" : "none"
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 py-px": size === "xs",
64
- "rounded-xs px-space-2 py-0.5": size === "sm",
65
- "rounded-sm px-space-2 py-space-1": size === "md"
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 size?: 'xs' | 'sm' | 'md';\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 size?: 'xs' | 'sm' | 'md';\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 animate: boolean;\n }>({ path: '', isFirst: true, animate: false });\n\n useEffect(() => {\n const container = containerRef.current;\n const activeIndex = options.findIndex((option) => option.value === value);\n const activeButton = buttonRefs.current[activeIndex];\n\n if (activeButton && container) {\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 setClipState((prev) => ({\n path: `inset(${top}px ${right}px ${bottom}px ${left}px round ${roundValue})`,\n isFirst: false,\n animate: !prev.isFirst,\n }));\n }\n }, [size, value, options]);\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 flex w-fit border border-subtle bg-surface-level-1 p-0.5 dark:border-muted',\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.animate\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 items-center gap-space-1',\n 'transition-colors duration-fast',\n {\n 'rounded-xs px-space-1 py-px': size === 'xs',\n 'rounded-xs px-space-2 py-0.5': size === 'sm',\n 'rounded-sm px-space-2 py-space-1': 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":";;;;;;AAsCA,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,SAAS;CAAM,CAAC;CAE9C,gBAAgB;EACd,MAAM,YAAY,aAAa;EAC/B,MAAM,cAAc,QAAQ,WAAW,WAAW,OAAO,UAAU,KAAK;EACxE,MAAM,eAAe,WAAW,QAAQ;EAExC,IAAI,gBAAgB,WAAW;GAC7B,MAAM,gBAAgB,UAAU,sBAAsB;GACtD,MAAM,aAAa,aAAa,sBAAsB;GACtD,MAAM,aAAa,SAAS,OAAO,QAAQ,SAAS,OAAO,QAAQ;GAEnE,MAAM,MAAM,WAAW,MAAM,cAAc,MAAM,UAAU;GAC3D,MAAM,SACJ,cAAc,SAAS,WAAW,SAAS,UAAU;GACvD,MAAM,OAAO,WAAW,OAAO,cAAc,OAAO,UAAU;GAC9D,MAAM,QACJ,cAAc,QAAQ,WAAW,QAAQ,UAAU;GAErD,cAAc,UAAU;IACtB,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,OAAO,KAAK,KAAK,WAAW,WAAW;IAC1E,SAAS;IACT,SAAS,CAAC,KAAK;GACjB,EAAE;EACJ;CACF,GAAG;EAAC;EAAM;EAAO;CAAO,CAAC;CAEzB,MAAM,YAAY,WAAgC,OAAO,UAAU;CACnE,OACE,oBAAC,SAAD;EAAS,OAAO;EACd,UAAA,qBAAC,OAAD;GACE,KAAK;GACL,WAAW,GACT,uFACA,SAAS,OAAO,WAAW,WAC3B,SAAS,OACL,kBACA,SAAS,OACP,kBACA,cACN,SACF;GAXF,UAAA,CAcG,UAAU,QACT,oBAAC,OAAD;IACE,eAAA;IACA,WAAW,GACT,2DACA,oBACA,kBACF;IACA,OAAO;KACL,UAAU,UAAU;KACpB,YAAY,UAAU,UAClB,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,0CACA,mCACA;MACE,+BAA+B,SAAS;MACxC,gCAAgC,SAAS;MACzC,oCAAoC,SAAS;KAC/C,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
+ {"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":"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"}
@@ -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":";;;;;;;;;;;;;AAuDA,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AACzB,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB,IAAI,KAAK,aAAa,SAAS,EACtD,uBAAuB,EACzB,CAAC;AAED,IAAM,sBAAsB,UAAkB,iBAAiB,OAAO,KAAK;AAE3E,IAAM,sBACJ,QACA,gBACA,SACA,iBACmC;CACnC,MAAM,cAAc,IAAI,IACtB,gBAAgB,KAAK,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CACrD;CACA,MAAM,UAAU,MAAM,KACpB,IAAI,IAAI,gBAAgB,KAAK,SAAS,KAAK,EAAE,KAAK,CAAC,CAAC,CACtD;CAEA,KAAK,MAAM,QAAQ,QACjB,IAAI,CAAC,QAAQ,SAAS,KAAK,OAAO,GAAG,QAAQ,KAAK,KAAK,OAAO;CAEhE,IAAI,QAAQ,WAAW,GAAG,QAAQ,KAAK,eAAe;CAEtD,OAAO,QAAQ,KAAK,WAAgC;EAClD,MAAM,SAAS,YAAY,IAAI,MAAM,KAAK,EAAE,IAAI,OAAO;EACvD,MAAM,SAAS,OACZ,QAAQ,SAAS,KAAK,YAAY,MAAM,CAAC,CACzC,SAAS,SACR,0BAA0B,KAAK,QAAQ,SAAS,YAAY,CAC9D;EACF,MAAM,SAAS,iBAAiB,QAAQ,OAAO,MAAM;EACrD,MAAM,OAAO,OAAO,UAAU;EAC9B,MAAM,qBAAqB,mBAAmB;GAC5C,SAAS;GACT,YAAY,OAAO,eAAe;GAClC,UAAA;GACA,UAAU,OAAO,aAAA;GACjB,YAAY,OAAO;GACnB,YAAA;GACA;EACF,CAAC;EACD,OAAO;GACL;GACA;GACA;GACA,WACE,KAAK,IAAA,IAAqC,kBAAkB,KAC3D,OAAO,SAAS,OAAO,IAAA;EAC5B;CACF,CAAC;AACH;AAEA,IAAM,eACJ,SACA,gBAEA,QAAQ,KAAK,EAAE,QAAQ,QAAQ,MAAM,iBAAiB;CACpD,GAAG;CACH;CACA,OAAO,YAAoB;EACzB;EACA,OAAO,CAAC,aAAa,CAAC;EACtB;CACF,CAAC;AACH,EAAE;;;;;;AAOJ,IAAa,aAAa,EACxB,QACA,OACA,SACA,eAAe,oBACf,QAAQ,aAAa,UACrB,YACA,aACA,aACA,aAAa,OACb,eAAe,MACf,MACA,SACA,oBACA,gBACA,SAAS,CAAC,GACV,qBACA,gBAAgB,MAChB,cAAc,MACd,YACA,aACA,oBACA,oBACA,mBACA,kBACA,WACA,GAAG,WACiB;CACpB,MAAM,CAAC,2BAA2B,gCAAgC,SAEhE,IAAI;CACN,MAAM,YAAY;EAChB,oBAAoB,KAAK;EACzB,cAAc,KAAK;CACrB;CACA,MAAM,YAA4C,EAAE,GAAG,KAAK;CAC5D,OAAO,UAAU;CACjB,OAAO,UAAU;CAEjB,MAAM,CAAC,cAAc,cAAc,kBAAkB;CACrD,MAAM,mBAAmB,yBAAyB,MAAM;CACxD,MAAM,gBAAgB,QAAQ,EAAE,EAAE,MAAM;CACxC,MAAM,gBAAmD,iBAAiB,KACvE,MAAM,WAAW;EAChB,GAAG;EACH,OAAO,KAAK,SAAS,6BAA6B,KAAK;EACvD,SAAS,KAAK,WAAW;EACzB,aACE,KAAK,eAAe,QAAQ,CAAC,OAAO,SAAS,KAAK,WAAW,IACzD,uBACA,KAAK,IAAI,GAAG,KAAK,WAAW;EAClC,SACE,KAAK,WAAW,QAAQ,CAAC,OAAO,SAAS,KAAK,OAAO,IACjD,IACA,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC;EAC3C,kBAAkB,KAAK,oBAAoB;CAC7C,EACF;CACA,MAAM,gBACJ,eAAe,QAAQ,YAAY,SAAS,IACxC,gBACA,cAAc,QAAQ,SAAS,YAAY,IAAI,KAAK,EAAE,CAAC;CAC7D,MAAM,UAAU,cAAc,SAAS,SACrC,KAAK,OAAO,KAAK,UAAU,MAAM,CAAC,CACpC;CACA,MAAM,kBAAkB,iBAAiB,SAAS,OAAO;CACzD,MAAM,gBAAgB,MAAM,KAC1B,IAAI,IACF,QAAQ,QACL,UAAU,SAAS,gBAAgB,MAAM,SAAS,gBAAgB,EACrE,CACF,CACF,CAAC,CAAC,MAAM,MAAM,UAAU,OAAO,KAAK;CACpC,MAAM,iBACJ,eAAe,SACX,cAAc,KAAK,UAAU;EAC3B,GAAG;EACH,QAAQ,KAAK,OAAO,QACjB,UACC,MAAM,KAAK,gBAAgB,MAAM,MAAM,KAAK,gBAAgB,EAChE;CACF,EAAE,IACF;CACN,MAAM,kBACJ,eAAe,UACd,WAAW,QACV,WAAW,gBAAgB,MAC3B,WAAW,gBAAgB,KACzB,UACA,KAAA;CACN,MAAM,cAAc,mBAClB,gBACA,OACA,iBACA,YACF;CACA,MAAM,cAAc;EAClB,KAAK;EACL,OACE,qBACA,YAAY,MAAM,CAAC,CAAC,CAAC,QAAQ,OAAO,SAAS,QAAQ,KAAK,WAAW,CAAC;EACxE,QAAQ;EACR,MAAM,YAAY,EAAE,EAAE,aAAA;CACxB;CACA,MAAM,aAAa,KAAK,IACtB,GACA,WAAW,QAAQ,YAAY,OAAO,YAAY,KACpD;CACA,MAAM,cAAc,KAAK,IACvB,GACA,WAAW,SAAS,YAAY,MAAM,YAAY,MACpD;CACA,MAAM,gBAAgB,6BAA6B;EACjD,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,MAAM,WAAW;CACnB,CAAC;CACD,MAAM,OAAO,YAAY,aAAa,WAAW;CAEjD,MAAM,aACJ,mBAAmB,OAAO,OAAO,cAAc,YAAY,eAAe;CAC5E,MAAM,WACJ,cAAc,QAAQ,cAAc,KAAK,cAAc,aACnD,YAAY,OAAO,aACnB;CAEN,MAAM,cAA0C,cAAc,KAAK,UAAU;EAC3E,IAAI,KAAK;EACT,OAAO,KAAK;EACZ,aAAa,KAAK;EAClB,UAAU,aAAa,IAAI,KAAK,EAAE,KAAK;CACzC,EAAE;CACF,MAAM,aACH,cAAc,YAAY,SAAS,MAAM,YAAY,SAAS;CACjE,MAAM,eAAe,aAAa,UAAU;CAC5C,MAAM,+BACH,YAAY,4BAA4B,SAAS;CACpD,MAAM,6BAA6B,eAAe,MAAM,SAAS;EAC/D,IAAI,KAAK,OAAO,6BAA6B,OAAO;EAEpD,MAAM,CAAC,eAAe,iBAAiB;EASvC,IAPE,cACA,KAAK,OAAO,MACT,UACC,MAAM,KAAK,QACX,MAAM,KAAK,iBACX,MAAM,KAAK,aACf,GACmB,OAAO;EAE5B,OAAO,gBAAgB,KAAK,QAAQ,YAAY,CAAC,CAAC,MAAM,YACtD,QAAQ,MAAM,OAAO,UAAU;GAC7B,MAAM,YAAY,QAAQ,QAAQ;GAClC,OACE,aAAa,QACb,MAAM,KAAK,iBACX,UAAU,KAAK;EAEnB,CAAC,CACH;CACF,CAAC,IACG,8BACA;CACJ,MAAM,eAAe,SAAS,QAAQ,QAAS,MAAM,QAAQ;CAC7D,MAAM,kBAAkB,SAAS,QAAQ,QAAS,MAAM,WAAW;CAEnE,MAAM,OACJ,qBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;EAAlC,UAAA,CACE,oBAAC,eAAD;GACE,OAAO,WAAW;GAClB,QAAQ,WAAW;GACP;GACC;GACA;GACb,QAAQ,cAAc;GACtB,cAAc,cAAc;GAC5B,WAAW,cAAc;GACzB,mBAAmB,cAAc;GAClB;GACT;GACN,QAAQ;GACM;GACF;GACC;GACD;GACE;GACA;GACG;GACjB,SAAS;GACT,oBAAoB;GACJ;GACR;GACO;GACF;GACb,GAAI;GACgB;GACA;GACD;GACD;EACnB,CAAA,GACA,uBAAuB,QACtB,YAAY,QACZ,mBAAmB,QACnB,oBAAoB;GAAE,WAAW;GAAU,SAAS;EAAgB,CAAC,CACpE;;CAGP,MAAM,SACJ,oBAAC,aAAD;EACE,cAAY,GAAG,UAAU,cAAc;EACvC,GAAI;EACJ,QAAQ;EACR,OAAO;EACP,qBAAqB,SAAS;GAC5B,6BAA6B,MAAM,MAAM,IAAI;GAC7C,aAAa,qBAAqB,IAAI;EACxC;CACD,CAAA;CAGH,OACE,oBAAC,OAAD;EACE,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;EAEH,UAAA,YACC,qBAAC,OAAD;GACE,WAAW,GACT,8CACA,iBAAiB,SACb,uJACA,4CACN;GANF,UAAA,CAQG,MACA,iBAAiB,SAChB,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA;GACE,CAAA,IAEL,MAEC;EAEL,CAAA,IAAA;CAEC,CAAA;AAET"}
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,