@grafana/components 0.0.30 → 0.0.32

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.
@@ -4,7 +4,7 @@ import { EventBus, GrafanaTheme2 } from '@grafana/data';
4
4
  import { ThemeColorMode } from '@grafana/design-tokens';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  import { IconName } from '@grafana/ui';
7
- import { Placement } from '@floating-ui/react';
7
+ import { Placement, FloatingPortalProps } from '@floating-ui/react';
8
8
 
9
9
  interface ColorModeChangeProps {
10
10
  getAppEvents: () => EventBus;
@@ -84,6 +84,10 @@ interface PopoverProps {
84
84
  * Virtual element to anchor the popover to instead of the trigger
85
85
  */
86
86
  virtualElement?: React.RefObject<Element>;
87
+ /**
88
+ * DOM element or ID to render the portal into, falls back to document.body
89
+ */
90
+ portalContainer?: FloatingPortalProps['root'];
87
91
  }
88
92
  declare const Popover: React$1.ForwardRefExoticComponent<PopoverProps & React$1.RefAttributes<HTMLElement>>;
89
93
 
@@ -117,6 +121,39 @@ interface GenericSkeletonProps {
117
121
  }
118
122
  declare const GenericSkeleton: ({ animationDuration }: GenericSkeletonProps) => react_jsx_runtime.JSX.Element;
119
123
 
124
+ type PortalRoot = FloatingPortalProps['root'];
125
+ interface PortalContextType {
126
+ root: PortalRoot;
127
+ setRoot: (root: PortalRoot) => void;
128
+ }
129
+ interface PortalProviderProps {
130
+ children: ReactNode;
131
+ defaultRoot?: PortalRoot;
132
+ }
133
+ interface UsePortalOptions {
134
+ /** If true, returns document.body instead of throwing when outside provider */
135
+ allowOutsideProvider?: boolean;
136
+ }
137
+ /**
138
+ * Provides a shared context for a portal root, which can be a selector string,
139
+ * an HTMLElement, or null.
140
+ *
141
+ * Wrap your application (or a subtree) in this provider to enable
142
+ * Popover, Tooltip, and other floating components to render correctly.
143
+ *
144
+ * @example
145
+ * ```tsx
146
+ * <PortalProvider defaultRoot={document.getElementById('grafana-portal-container')}>
147
+ * <App />
148
+ * </PortalProvider>
149
+ * ```
150
+ */
151
+ declare const PortalProvider: React__default.FC<PortalProviderProps>;
152
+ /**
153
+ * Use this hook to access the portal root context.
154
+ */
155
+ declare const usePortal: ({ allowOutsideProvider }?: UsePortalOptions) => PortalContextType;
156
+
120
157
  type StackedChartNoDataMessageFormatter = (args: {
121
158
  period: string;
122
159
  className: string;
@@ -143,6 +180,30 @@ interface StackedChartCategory {
143
180
  }
144
181
  type StackedChartCategories<T extends string> = Partial<Record<T, StackedChartCategory>>;
145
182
  type StackedChartSortOrder = 'largest-first' | 'smallest-first' | 'natural';
183
+ /**
184
+ * Callback function invoked when a mouse event occurs on a chart segment.
185
+ *
186
+ * This is a simplified callback (not a React event handler) that receives
187
+ * segment metadata. It's called after the internal event handling is complete.
188
+ *
189
+ * @param args - Object containing segment metadata
190
+ * @param args.categoryId - The unique identifier of the segment that triggered the event
191
+ * @param args.ref - React ref to the segment's DOM element, useful for positioning tooltips or popovers
192
+ *
193
+ * @example
194
+ * ```tsx
195
+ * <StackedChart
196
+ * categories={categories}
197
+ * onSegmentMouseOver={({ categoryId, ref }) => {
198
+ * console.log(`Hovered segment: ${categoryId}`);
199
+ * // ref.current gives you the DOM element if needed
200
+ * }}
201
+ * onSegmentMouseOut={({ categoryId }) => {
202
+ * console.log(`Left segment: ${categoryId}`);
203
+ * }}
204
+ * />
205
+ * ```
206
+ */
146
207
  type MouseEventHandler = ({ categoryId, ref, }: {
147
208
  categoryId: string;
148
209
  ref: React.ForwardedRef<HTMLDivElement>;
@@ -167,11 +228,33 @@ interface StackedChartProps<T extends string> {
167
228
  */
168
229
  isDimmed?: boolean;
169
230
  /**
170
- * Event handler for whenever a segment gets a mouseover event
231
+ * Callback invoked when the mouse enters a chart segment.
232
+ *
233
+ * Receives the segment's `categoryId` and a `ref` to its DOM element.
234
+ * Use this to implement custom hover behavior, analytics tracking,
235
+ * or coordination with external UI elements.
236
+ *
237
+ * @example
238
+ * ```tsx
239
+ * onSegmentMouseOver={({ categoryId, ref }) => {
240
+ * setActiveCategory(categoryId);
241
+ * // Optional: use ref.current for DOM measurements
242
+ * }}
243
+ * ```
171
244
  */
172
245
  onSegmentMouseOver?: MouseEventHandler;
173
246
  /**
174
- * Event handler for whenever a segment gets a mouseout event
247
+ * Callback invoked when the mouse leaves a chart segment.
248
+ *
249
+ * Receives the segment's `categoryId` and a `ref` to its DOM element.
250
+ * Use this to clean up hover state or hide related UI elements.
251
+ *
252
+ * @example
253
+ * ```tsx
254
+ * onSegmentMouseOut={({ categoryId }) => {
255
+ * setActiveCategory(null);
256
+ * }}
257
+ * ```
175
258
  */
176
259
  onSegmentMouseOut?: MouseEventHandler;
177
260
  /**
@@ -310,5 +393,5 @@ interface FormatNumberOptions {
310
393
  */
311
394
  declare const formatNumber: (value: number | string | "loading", options?: FormatNumberOptions) => string;
312
395
 
313
- export { ColorMode, ColorModeChangeHandler, ColorModeProvider, ComparisonBadge, ComparisonTooltip, GenericSkeleton, Popover, StackedChart, StackedChartNoData, StackedChartSegment, StackedChartSegmentTooltip, StackedChartSkeleton, calculateComparison, formatNumber, useColorMode, useColorModeChange };
314
- export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, StackedChartCategories, StackedChartCategory, StackedChartNoDataMessageFormatter, StackedChartNoDataProps, StackedChartProps, StackedChartSegmentTooltipProps, StackedChartSkeletonProps, StackedChartSortOrder, StackedChartTooltipContentFormatter };
396
+ export { ColorMode, ColorModeChangeHandler, ColorModeProvider, ComparisonBadge, ComparisonTooltip, GenericSkeleton, Popover, PortalProvider, StackedChart, StackedChartNoData, StackedChartSegment, StackedChartSegmentTooltip, StackedChartSkeleton, calculateComparison, formatNumber, useColorMode, useColorModeChange, usePortal };
397
+ export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, PortalContextType, PortalProviderProps, PortalRoot, SegmentMouseEventHandler, StackedChartCategories, StackedChartCategory, StackedChartNoDataMessageFormatter, StackedChartNoDataProps, StackedChartProps, StackedChartSegmentProps, StackedChartSegmentState, StackedChartSegmentTooltipProps, StackedChartSkeletonProps, StackedChartSortOrder, StackedChartTooltipContentFormatter, UsePortalOptions };
@@ -11,7 +11,6 @@ const ColorModeProvider = ({
11
11
  }) => {
12
12
  const [colorMode, setColorMode] = useState(defaultColorMode);
13
13
  useEffect(() => {
14
- console.log("ColorModeProvider", { colorMode });
15
14
  document.documentElement.setAttribute("data-color-mode", colorMode);
16
15
  }, [colorMode]);
17
16
  return /* @__PURE__ */ jsx(ColorModeContext.Provider, { value: { colorMode, setColorMode }, children });
@@ -1 +1 @@
1
- {"version":3,"file":"ColorModeProvider.js","sources":["../../../../src/components/ColorModeProvider/ColorModeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { ThemeColorMode } from '@grafana/design-tokens';\n\nimport '@grafana/design-tokens/dist/css/legacy/primitives.css';\nimport '@grafana/design-tokens/dist/css/legacy/colors.light.css';\nimport '@grafana/design-tokens/dist/css/legacy/colors.dark.css';\n\nexport interface ColorModeContextType {\n colorMode: ThemeColorMode;\n setColorMode: (colorMode: ThemeColorMode) => void;\n}\n\nexport interface ColorModeProviderProps {\n children: ReactNode;\n defaultColorMode?: ThemeColorMode;\n}\n\nconst ColorModeContext = createContext<ColorModeContextType | undefined>(undefined);\n\n/**\n * Provides a shared context for the currently-active theme color mode, and sets\n * the data-color-mode attribute on the document element whenever it changes.\n */\nexport const ColorModeProvider: React.FC<ColorModeProviderProps> = ({\n children,\n defaultColorMode = 'light',\n}) => {\n const [colorMode, setColorMode] = useState<ThemeColorMode>(defaultColorMode);\n\n useEffect(() => {\n console.log('ColorModeProvider', { colorMode });\n document.documentElement.setAttribute('data-color-mode', colorMode);\n }, [colorMode]);\n\n return (\n <ColorModeContext.Provider value={{ colorMode, setColorMode }}>\n {children}\n </ColorModeContext.Provider>\n );\n};\n\n/**\n * Use this to query the active color mode, or to set it, e.g. with an effect\n * hook within a component which explicitly changes the active color mode:\n *\n * useEffect(() => {\n * setColorMode(colorMode);\n * }, [colorMode]);\n *\n */\nexport const useColorMode = () => {\n const context = useContext(ColorModeContext);\n\n if (context === undefined) {\n throw new Error('useColorMode must be used within a ColorModeProvider');\n }\n\n return context;\n};\n"],"names":[],"mappings":";;;;;;AAiBA,MAAM,gBAAA,GAAmB,cAAgD,MAAS,CAAA;AAM3E,MAAM,oBAAsD,CAAC;AAAA,EAClE,QAAA;AAAA,EACA,gBAAA,GAAmB;AACrB,CAAA,KAAM;AACJ,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAyB,gBAAgB,CAAA;AAE3E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAA,CAAQ,GAAA,CAAI,mBAAA,EAAqB,EAAE,SAAA,EAAW,CAAA;AAC9C,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,iBAAA,EAAmB,SAAS,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,uBACE,GAAA,CAAC,iBAAiB,QAAA,EAAjB,EAA0B,OAAO,EAAE,SAAA,EAAW,YAAA,EAAa,EACzD,QAAA,EACH,CAAA;AAEJ;AAWO,MAAM,eAAe,MAAM;AAChC,EAAA,MAAM,OAAA,GAAU,WAAW,gBAAgB,CAAA;AAE3C,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,EACxE;AAEA,EAAA,OAAO,OAAA;AACT;;;;"}
1
+ {"version":3,"file":"ColorModeProvider.js","sources":["../../../../src/components/ColorModeProvider/ColorModeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';\nimport { ThemeColorMode } from '@grafana/design-tokens';\n\nimport '@grafana/design-tokens/dist/css/legacy/primitives.css';\nimport '@grafana/design-tokens/dist/css/legacy/colors.light.css';\nimport '@grafana/design-tokens/dist/css/legacy/colors.dark.css';\n\nexport interface ColorModeContextType {\n colorMode: ThemeColorMode;\n setColorMode: (colorMode: ThemeColorMode) => void;\n}\n\nexport interface ColorModeProviderProps {\n children: ReactNode;\n defaultColorMode?: ThemeColorMode;\n}\n\nconst ColorModeContext = createContext<ColorModeContextType | undefined>(undefined);\n\n/**\n * Provides a shared context for the currently-active theme color mode, and sets\n * the data-color-mode attribute on the document element whenever it changes.\n */\nexport const ColorModeProvider: React.FC<ColorModeProviderProps> = ({\n children,\n defaultColorMode = 'light',\n}) => {\n const [colorMode, setColorMode] = useState<ThemeColorMode>(defaultColorMode);\n\n useEffect(() => {\n document.documentElement.setAttribute('data-color-mode', colorMode);\n }, [colorMode]);\n\n return (\n <ColorModeContext.Provider value={{ colorMode, setColorMode }}>\n {children}\n </ColorModeContext.Provider>\n );\n};\n\n/**\n * Use this to query the active color mode, or to set it, e.g. with an effect\n * hook within a component which explicitly changes the active color mode:\n *\n * useEffect(() => {\n * setColorMode(colorMode);\n * }, [colorMode]);\n *\n */\nexport const useColorMode = () => {\n const context = useContext(ColorModeContext);\n\n if (context === undefined) {\n throw new Error('useColorMode must be used within a ColorModeProvider');\n }\n\n return context;\n};\n"],"names":[],"mappings":";;;;;;AAiBA,MAAM,gBAAA,GAAmB,cAAgD,MAAS,CAAA;AAM3E,MAAM,oBAAsD,CAAC;AAAA,EAClE,QAAA;AAAA,EACA,gBAAA,GAAmB;AACrB,CAAA,KAAM;AACJ,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAyB,gBAAgB,CAAA;AAE3E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,YAAA,CAAa,iBAAA,EAAmB,SAAS,CAAA;AAAA,EACpE,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,uBACE,GAAA,CAAC,iBAAiB,QAAA,EAAjB,EAA0B,OAAO,EAAE,SAAA,EAAW,YAAA,EAAa,EACzD,QAAA,EACH,CAAA;AAEJ;AAWO,MAAM,eAAe,MAAM;AAChC,EAAA,MAAM,OAAA,GAAU,WAAW,gBAAgB,CAAA;AAE3C,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,EACxE;AAEA,EAAA,OAAO,OAAA;AACT;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef, useRef, useId, useState, useLayoutEffect, useCallback, cloneElement } from 'react';
3
3
  import { useFloating, autoUpdate, useInteractions, useDismiss, useHover, useFocus, useRole, safePolygon, useTransitionStyles, FloatingPortal, FloatingArrow, flip, shift, offset, arrow } from '@floating-ui/react';
4
+ import { usePortal } from '../PortalProvider/PortalProvider.js';
4
5
  import { getStyles } from './Popover.styles.js';
5
6
 
6
7
  const POPOVER_OFFSET = 8;
@@ -35,7 +36,8 @@ const Popover = forwardRef(
35
36
  placement = "bottom",
36
37
  transitionDuration = 200,
37
38
  hideDelay = 500,
38
- virtualElement
39
+ virtualElement,
40
+ portalContainer
39
41
  }, forwardedRef) => {
40
42
  const arrowRef = useRef(null);
41
43
  const closeTimer = useRef(void 0);
@@ -45,6 +47,17 @@ const Popover = forwardRef(
45
47
  const isOpen = isOpenControlled != null ? isOpenControlled : isOpenState;
46
48
  const middleware = getMiddleware({ placement, arrowRef });
47
49
  const styles = getStyles();
50
+ const { root } = usePortal();
51
+ const portalRoot = (() => {
52
+ switch (true) {
53
+ case portalContainer instanceof HTMLElement:
54
+ return portalContainer;
55
+ case typeof portalContainer === "string":
56
+ return document.getElementById(portalContainer);
57
+ default:
58
+ return root;
59
+ }
60
+ })();
48
61
  const { context, refs, floatingStyles } = useFloating({
49
62
  open: isOpen,
50
63
  placement,
@@ -121,7 +134,7 @@ const Popover = forwardRef(
121
134
  "aria-describedby": isOpen ? popoverId : void 0,
122
135
  ...getReferenceProps()
123
136
  }),
124
- (isDelayedOpen || isOpen) && /* @__PURE__ */ jsx(FloatingPortal, { root: document.getElementById("grafana-portal-container"), children: /* @__PURE__ */ jsx("div", { ref: refs.setFloating, style: floatingStyles, ...getFloatingProps(), children: /* @__PURE__ */ jsxs("div", { style: transitionStyles, className: styles.shadow, children: [
137
+ (isDelayedOpen || isOpen) && /* @__PURE__ */ jsx(FloatingPortal, { root: portalRoot, children: /* @__PURE__ */ jsx("div", { ref: refs.setFloating, style: floatingStyles, ...getFloatingProps(), children: /* @__PURE__ */ jsxs("div", { style: transitionStyles, className: styles.shadow, children: [
125
138
  /* @__PURE__ */ jsx(FloatingArrow, { className: styles.arrow, ref: arrowRef, context }),
126
139
  /* @__PURE__ */ jsx("div", { id: popoverId, role: "tooltip", className: styles.container, children })
127
140
  ] }) }) })
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.js","sources":["../../../../src/components/Popover/Popover.tsx"],"sourcesContent":["import {\n forwardRef,\n cloneElement,\n useCallback,\n useId,\n useRef,\n useState,\n useLayoutEffect,\n JSX,\n} from 'react';\nimport {\n arrow,\n autoUpdate,\n flip,\n FloatingArrow,\n offset,\n Placement,\n safePolygon,\n shift,\n useDismiss,\n useFloating,\n useFocus,\n useHover,\n useRole,\n useInteractions,\n useTransitionStyles,\n FloatingPortal,\n} from '@floating-ui/react';\nimport { getStyles } from './Popover.styles';\n\nexport interface PopoverProps {\n /**\n * Content used to trigger the Popover being displayed\n */\n trigger: JSX.Element;\n\n /**\n * Content to render within the Popover\n */\n children: JSX.Element;\n /**\n * Should the popover be open? Implicitly means the popover visibility is\n * controlled; if omitted, the popover target will control visibility\n */\n isOpen?: boolean;\n\n /**\n * Set to true if you want the tooltip to stay long enough so the user can\n * move mouse over content to select text or click a link\n */\n isInteractive?: boolean;\n\n /**\n * Placement of the Popover relative to the trigger content\n */\n placement?: Placement;\n\n /**\n * Transition duration for hide/show effects, in milliseconds\n */\n transitionDuration?: number;\n\n /**\n * Additional delay before hiding the popover after mouseout, in milliseconds\n */\n hideDelay?: number;\n\n /**\n * Virtual element to anchor the popover to instead of the trigger\n */\n virtualElement?: React.RefObject<Element>;\n}\n\nconst POPOVER_OFFSET = 8;\n\nconst getMiddleware = ({\n placement,\n arrowRef,\n}: {\n placement?: Placement;\n arrowRef: React.RefObject<null>;\n}) => {\n const BOUNDARY_ELEMENT_ID = 'floating-boundary';\n const _flip = flip({\n // Ensure we flip to the perpendicular axis if it doesn't fit\n // on narrow viewports.\n crossAxis: 'alignment',\n fallbackAxisSideDirection: 'end',\n boundary: document.getElementById(BOUNDARY_ELEMENT_ID) ?? undefined,\n });\n\n const middleware = placement?.includes('-') ? [_flip, shift()] : [shift(), _flip];\n\n // the order of middleware is important!\n // `arrow` should almost always be at the end\n // see https://floating-ui.com/docs/arrow#order\n return [\n offset(POPOVER_OFFSET),\n ...middleware,\n arrow({\n element: arrowRef,\n }),\n ];\n};\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(\n (\n {\n trigger,\n children,\n isOpen: isOpenControlled,\n isInteractive = false,\n placement = 'bottom',\n transitionDuration = 200,\n hideDelay = 500,\n virtualElement,\n },\n forwardedRef,\n ) => {\n const arrowRef = useRef(null);\n const closeTimer = useRef<number | undefined>(undefined);\n const popoverId = useId();\n const [isOpenState, setOpen] = useState(isOpenControlled);\n const [isDelayedOpen, setDelayedOpen] = useState(isOpenControlled);\n const isOpen = isOpenControlled ?? isOpenState;\n const middleware = getMiddleware({ placement, arrowRef });\n const styles = getStyles();\n\n const { context, refs, floatingStyles } = useFloating({\n open: isOpen,\n placement,\n onOpenChange: (open) => {\n setOpen(open);\n clearTimeout(closeTimer.current);\n\n if (!open) {\n closeTimer.current = window.setTimeout(() => {\n setDelayedOpen(open);\n }, transitionDuration + hideDelay);\n } else {\n setDelayedOpen(open);\n }\n },\n middleware,\n whileElementsMounted: autoUpdate,\n });\n\n useLayoutEffect(() => {\n if (virtualElement && virtualElement.current !== null) {\n const domRect = virtualElement.current.getBoundingClientRect();\n\n refs.setPositionReference({\n getBoundingClientRect: () => {\n if (virtualElement.current !== null)\n return virtualElement.current.getBoundingClientRect();\n return domRect;\n },\n contextElement: virtualElement.current,\n });\n }\n }, [refs, virtualElement]);\n\n const { getReferenceProps, getFloatingProps } = useInteractions([\n useDismiss(context),\n useHover(context, {\n handleClose: isInteractive ? safePolygon() : undefined,\n move: false,\n delay: {\n open: 0,\n close: hideDelay,\n },\n }),\n useFocus(context),\n useRole(context),\n ]);\n\n const { styles: transitionStyles } = useTransitionStyles(context, {\n duration: transitionDuration,\n initial: ({ side }) => ({\n opacity: 0,\n transform:\n side === 'top' || side === 'bottom'\n ? `translateY(${POPOVER_OFFSET}px)`\n : `translateX(${POPOVER_OFFSET}px)`,\n }),\n open: ({ side }) => ({\n opacity: 1,\n transform: side === 'top' || side === 'bottom' ? `translateY(0)` : `translateX(0)`,\n }),\n close: ({ side }) => ({\n opacity: 0,\n transform:\n side === 'top' || side === 'bottom'\n ? `translateY(${POPOVER_OFFSET}px)`\n : `translateX(${POPOVER_OFFSET}px)`,\n }),\n });\n\n const handleRef = useCallback(\n (ref: HTMLElement | null) => {\n refs.setReference(ref);\n\n if (typeof forwardedRef === 'function') {\n forwardedRef(ref);\n } else if (forwardedRef) {\n forwardedRef.current = ref;\n }\n },\n [forwardedRef, refs],\n );\n\n return (\n <>\n {/* element to trigger displaying the popover */}\n {cloneElement(trigger, {\n ref: handleRef,\n tabIndex: 0,\n 'aria-describedby': isOpen ? popoverId : undefined,\n ...getReferenceProps(),\n })}\n {/* content to render inside the popover when open */}\n {(isDelayedOpen || isOpen) && (\n <FloatingPortal root={document.getElementById('grafana-portal-container')}>\n <div ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>\n <div style={transitionStyles} className={styles.shadow}>\n <FloatingArrow className={styles.arrow} ref={arrowRef} context={context} />\n <div id={popoverId} role=\"tooltip\" className={styles.container}>\n {children}\n </div>\n </div>\n </div>\n </FloatingPortal>\n )}\n </>\n );\n },\n);\n\nPopover.displayName = 'Popover';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Popover.js","sources":["../../../../src/components/Popover/Popover.tsx"],"sourcesContent":["import {\n forwardRef,\n cloneElement,\n useCallback,\n useId,\n useRef,\n useState,\n useLayoutEffect,\n JSX,\n} from 'react';\nimport {\n arrow,\n autoUpdate,\n flip,\n FloatingArrow,\n offset,\n Placement,\n safePolygon,\n shift,\n useDismiss,\n useFloating,\n useFocus,\n useHover,\n useRole,\n useInteractions,\n useTransitionStyles,\n FloatingPortal,\n FloatingPortalProps,\n} from '@floating-ui/react';\nimport { usePortal } from '../PortalProvider';\nimport { getStyles } from './Popover.styles';\n\nexport interface PopoverProps {\n /**\n * Content used to trigger the Popover being displayed\n */\n trigger: JSX.Element;\n\n /**\n * Content to render within the Popover\n */\n children: JSX.Element;\n /**\n * Should the popover be open? Implicitly means the popover visibility is\n * controlled; if omitted, the popover target will control visibility\n */\n isOpen?: boolean;\n\n /**\n * Set to true if you want the tooltip to stay long enough so the user can\n * move mouse over content to select text or click a link\n */\n isInteractive?: boolean;\n\n /**\n * Placement of the Popover relative to the trigger content\n */\n placement?: Placement;\n\n /**\n * Transition duration for hide/show effects, in milliseconds\n */\n transitionDuration?: number;\n\n /**\n * Additional delay before hiding the popover after mouseout, in milliseconds\n */\n hideDelay?: number;\n\n /**\n * Virtual element to anchor the popover to instead of the trigger\n */\n virtualElement?: React.RefObject<Element>;\n\n /**\n * DOM element or ID to render the portal into, falls back to document.body\n */\n portalContainer?: FloatingPortalProps['root'];\n}\n\nconst POPOVER_OFFSET = 8;\n\nconst getMiddleware = ({\n placement,\n arrowRef,\n}: {\n placement?: Placement;\n arrowRef: React.RefObject<null>;\n}) => {\n const BOUNDARY_ELEMENT_ID = 'floating-boundary';\n const _flip = flip({\n // Ensure we flip to the perpendicular axis if it doesn't fit\n // on narrow viewports.\n crossAxis: 'alignment',\n fallbackAxisSideDirection: 'end',\n boundary: document.getElementById(BOUNDARY_ELEMENT_ID) ?? undefined,\n });\n\n const middleware = placement?.includes('-') ? [_flip, shift()] : [shift(), _flip];\n\n // the order of middleware is important!\n // `arrow` should almost always be at the end\n // see https://floating-ui.com/docs/arrow#order\n return [\n offset(POPOVER_OFFSET),\n ...middleware,\n arrow({\n element: arrowRef,\n }),\n ];\n};\n\nexport const Popover = forwardRef<HTMLElement, PopoverProps>(\n (\n {\n trigger,\n children,\n isOpen: isOpenControlled,\n isInteractive = false,\n placement = 'bottom',\n transitionDuration = 200,\n hideDelay = 500,\n virtualElement,\n portalContainer,\n },\n forwardedRef,\n ) => {\n const arrowRef = useRef(null);\n const closeTimer = useRef<number | undefined>(undefined);\n const popoverId = useId();\n const [isOpenState, setOpen] = useState(isOpenControlled);\n const [isDelayedOpen, setDelayedOpen] = useState(isOpenControlled);\n const isOpen = isOpenControlled ?? isOpenState;\n const middleware = getMiddleware({ placement, arrowRef });\n const styles = getStyles();\n const { root } = usePortal();\n\n const portalRoot = (() => {\n switch (true) {\n case portalContainer instanceof HTMLElement:\n return portalContainer;\n case typeof portalContainer === 'string':\n return document.getElementById(portalContainer);\n default:\n return root;\n }\n })();\n\n const { context, refs, floatingStyles } = useFloating({\n open: isOpen,\n placement,\n onOpenChange: (open) => {\n setOpen(open);\n clearTimeout(closeTimer.current);\n\n if (!open) {\n closeTimer.current = window.setTimeout(() => {\n setDelayedOpen(open);\n }, transitionDuration + hideDelay);\n } else {\n setDelayedOpen(open);\n }\n },\n middleware,\n whileElementsMounted: autoUpdate,\n });\n\n useLayoutEffect(() => {\n if (virtualElement && virtualElement.current !== null) {\n const domRect = virtualElement.current.getBoundingClientRect();\n\n refs.setPositionReference({\n getBoundingClientRect: () => {\n if (virtualElement.current !== null)\n return virtualElement.current.getBoundingClientRect();\n return domRect;\n },\n contextElement: virtualElement.current,\n });\n }\n }, [refs, virtualElement]);\n\n const { getReferenceProps, getFloatingProps } = useInteractions([\n useDismiss(context),\n useHover(context, {\n handleClose: isInteractive ? safePolygon() : undefined,\n move: false,\n delay: {\n open: 0,\n close: hideDelay,\n },\n }),\n useFocus(context),\n useRole(context),\n ]);\n\n const { styles: transitionStyles } = useTransitionStyles(context, {\n duration: transitionDuration,\n initial: ({ side }) => ({\n opacity: 0,\n transform:\n side === 'top' || side === 'bottom'\n ? `translateY(${POPOVER_OFFSET}px)`\n : `translateX(${POPOVER_OFFSET}px)`,\n }),\n open: ({ side }) => ({\n opacity: 1,\n transform: side === 'top' || side === 'bottom' ? `translateY(0)` : `translateX(0)`,\n }),\n close: ({ side }) => ({\n opacity: 0,\n transform:\n side === 'top' || side === 'bottom'\n ? `translateY(${POPOVER_OFFSET}px)`\n : `translateX(${POPOVER_OFFSET}px)`,\n }),\n });\n\n const handleRef = useCallback(\n (ref: HTMLElement | null) => {\n refs.setReference(ref);\n\n if (typeof forwardedRef === 'function') {\n forwardedRef(ref);\n } else if (forwardedRef) {\n forwardedRef.current = ref;\n }\n },\n [forwardedRef, refs],\n );\n\n return (\n <>\n {/* element to trigger displaying the popover */}\n {cloneElement(trigger, {\n ref: handleRef,\n tabIndex: 0,\n 'aria-describedby': isOpen ? popoverId : undefined,\n ...getReferenceProps(),\n })}\n {/* content to render inside the popover when open */}\n {(isDelayedOpen || isOpen) && (\n <FloatingPortal root={portalRoot}>\n <div ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>\n <div style={transitionStyles} className={styles.shadow}>\n <FloatingArrow className={styles.arrow} ref={arrowRef} context={context} />\n <div id={popoverId} role=\"tooltip\" className={styles.container}>\n {children}\n </div>\n </div>\n </div>\n </FloatingPortal>\n )}\n </>\n );\n },\n);\n\nPopover.displayName = 'Popover';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useState } from 'react';
3
+
4
+ const PortalContext = createContext(void 0);
5
+ const PortalProvider = ({ children, defaultRoot = null }) => {
6
+ const [root, setRoot] = useState(defaultRoot != null ? defaultRoot : document.body);
7
+ return /* @__PURE__ */ jsx(PortalContext.Provider, { value: { root, setRoot }, children });
8
+ };
9
+ const usePortal = ({ allowOutsideProvider = false } = {}) => {
10
+ const context = useContext(PortalContext);
11
+ if (context === void 0) {
12
+ if (allowOutsideProvider) {
13
+ return { root: document.body, setRoot: () => {
14
+ } };
15
+ }
16
+ throw new Error("usePortal must be used within a PortalProvider");
17
+ }
18
+ return context;
19
+ };
20
+
21
+ export { PortalProvider, usePortal };
22
+ //# sourceMappingURL=PortalProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PortalProvider.js","sources":["../../../../src/components/PortalProvider/PortalProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useState, ReactNode } from 'react';\nimport type { FloatingPortalProps } from '@floating-ui/react';\n\nexport type PortalRoot = FloatingPortalProps['root'];\n\nexport interface PortalContextType {\n root: PortalRoot;\n setRoot: (root: PortalRoot) => void;\n}\n\nexport interface PortalProviderProps {\n children: ReactNode;\n defaultRoot?: PortalRoot;\n}\n\nexport interface UsePortalOptions {\n /** If true, returns document.body instead of throwing when outside provider */\n allowOutsideProvider?: boolean;\n}\n\nconst PortalContext = createContext<PortalContextType | undefined>(undefined);\n\n/**\n * Provides a shared context for a portal root, which can be a selector string,\n * an HTMLElement, or null.\n *\n * Wrap your application (or a subtree) in this provider to enable\n * Popover, Tooltip, and other floating components to render correctly.\n *\n * @example\n * ```tsx\n * <PortalProvider defaultRoot={document.getElementById('grafana-portal-container')}>\n * <App />\n * </PortalProvider>\n * ```\n */\nexport const PortalProvider: React.FC<PortalProviderProps> = ({ children, defaultRoot = null }) => {\n const [root, setRoot] = useState<PortalRoot>(defaultRoot ?? document.body);\n\n return <PortalContext.Provider value={{ root, setRoot }}>{children}</PortalContext.Provider>;\n};\n\n/**\n * Use this hook to access the portal root context.\n */\nexport const usePortal = ({ allowOutsideProvider = false }: UsePortalOptions = {}) => {\n const context = useContext(PortalContext);\n\n if (context === undefined) {\n if (allowOutsideProvider) {\n return { root: document.body, setRoot: () => {} };\n }\n throw new Error('usePortal must be used within a PortalProvider');\n }\n\n return context;\n};\n"],"names":[],"mappings":";;;AAoBA,MAAM,aAAA,GAAgB,cAA6C,MAAS,CAAA;AAgBrE,MAAM,iBAAgD,CAAC,EAAE,QAAA,EAAU,WAAA,GAAc,MAAK,KAAM;AACjG,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,IAAI,QAAA,CAAqB,WAAA,IAAA,IAAA,GAAA,WAAA,GAAe,SAAS,IAAI,CAAA;AAEzE,EAAA,uBAAO,GAAA,CAAC,cAAc,QAAA,EAAd,EAAuB,OAAO,EAAE,IAAA,EAAM,OAAA,EAAQ,EAAI,QAAA,EAAS,CAAA;AACrE;AAKO,MAAM,YAAY,CAAC,EAAE,uBAAuB,KAAA,EAAM,GAAsB,EAAC,KAAM;AACpF,EAAA,MAAM,OAAA,GAAU,WAAW,aAAa,CAAA;AAExC,EAAA,IAAI,YAAY,MAAA,EAAW;AACzB,IAAA,IAAI,oBAAA,EAAsB;AACxB,MAAA,OAAO,EAAE,IAAA,EAAM,QAAA,CAAS,IAAA,EAAM,SAAS,MAAM;AAAA,MAAC,CAAA,EAAE;AAAA,IAClD;AACA,IAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO,OAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"StackedChart.js","sources":["../../../../src/components/StackedChart/StackedChart.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { StackedChartSegment } from '../StackedChartSegment';\nimport { getStyles } from './StackedChart.styles';\nimport { StackedChartSegmentState, SegmentMouseEventHandler } from '../StackedChartSegment/types';\nimport { StackedChartSegmentTooltip } from '../StackedChartSegmentTooltip';\nimport { StackedChartSkeleton } from '../StackedChartSkeleton';\nimport { StackedChartNoData, StackedChartNoDataProps } from '../StackedChartNoData';\nimport { STACKED_CHART_DEFAULT_HEIGHT } from './common';\n\nexport interface StackedChartCategory {\n title: string;\n value: number;\n}\n\nconst DUMMY_CATEGORY: StackedChartCategory = {\n title: 'Category',\n value: 0.01,\n};\n\nexport type StackedChartCategories<T extends string> = Partial<Record<T, StackedChartCategory>>;\n\nexport type StackedChartSortOrder = 'largest-first' | 'smallest-first' | 'natural';\n\ntype MouseEventHandler = ({\n categoryId,\n ref,\n}: {\n categoryId: string;\n ref: React.ForwardedRef<HTMLDivElement>;\n}) => void;\n\nexport interface StackedChartProps<T extends string> {\n /**\n * How should category segments be sorted?\n */\n sortOrder?: StackedChartSortOrder;\n\n /**\n * Height to render the stacked chart, in pixels\n */\n height?: number;\n\n /**\n * initial category ID to highlight; if omitted then all segments will render\n * in colour and highlight based on mouseover events.\n */\n highlightedCategoryId?: T;\n\n /**\n * Should the whole StackedChart be dimmed, i.e. with a highlighted category\n * not in color, all other categories faded out?\n */\n isDimmed?: boolean;\n\n /**\n * Event handler for whenever a segment gets a mouseover event\n */\n onSegmentMouseOver?: MouseEventHandler;\n\n /**\n * Event handler for whenever a segment gets a mouseout event\n */\n onSegmentMouseOut?: MouseEventHandler;\n\n /**\n * String representing the period for which the chart is displaying data\n */\n period?: string;\n\n formatNoDataMessage?: StackedChartNoDataProps['formatMessage'];\n\n /**\n * Is the StackedChart explicitly in a loading state?\n */\n isSkeleton?: boolean;\n\n /**\n * Array of StackedChartCategory to build the chart from\n */\n categories?: StackedChartCategories<T>;\n}\n\n/**\n * Custom hook to assign a ref per categoryId\n */\nconst useCategoryRefs = <T extends string>(\n categoryIds: T[],\n): Record<T, React.RefObject<HTMLDivElement>> => {\n const refsMap = useRef<Map<T, React.RefObject<HTMLDivElement>>>(new Map());\n\n for (const categoryId of categoryIds) {\n if (!refsMap.current.has(categoryId)) {\n refsMap.current.set(categoryId, { current: null });\n }\n }\n\n const result = {} as Record<T, React.RefObject<HTMLDivElement>>;\n for (const categoryId of categoryIds) {\n result[categoryId] = refsMap.current.get(categoryId)!;\n }\n\n return result;\n};\n\nexport const StackedChart = <T extends string>({\n categories,\n highlightedCategoryId: initialHighlightedCategoryId,\n isSkeleton = false,\n isDimmed = false,\n sortOrder = 'largest-first',\n height = STACKED_CHART_DEFAULT_HEIGHT,\n onSegmentMouseOver,\n onSegmentMouseOut,\n period = 'current',\n formatNoDataMessage,\n}: StackedChartProps<T>) => {\n const shouldRenderSkeleton = isSkeleton || !categories;\n let highlightedSegmentRef = useRef<HTMLDivElement>(null);\n const [highlightedCategoryId, setHighlightedCategoryId] = useState<string | undefined>(\n /**\n * Using a type assertion here ultimately because you can’t pass a generic\n * type argument to the type of props in React.forwardRef (it is inferred as\n * of type string).\n *\n * See: https://stackoverflow.com/questions/51884498/using-react-forwardref-with-typescript-generic-jsx-arguments\n */\n initialHighlightedCategoryId as string,\n );\n const [isHovered, setHovered] = useState<boolean>(false);\n const timer = useRef<number | undefined>(undefined);\n\n const sortedCategoryIds = (() => {\n let categoryIds: T[] = [];\n\n for (const categoryId in categories) {\n categoryIds.push(categoryId);\n }\n\n // return early since we may not even have any categories at this point\n if (shouldRenderSkeleton) return categoryIds;\n\n switch (sortOrder) {\n case 'largest-first':\n return categoryIds.sort(\n (a, b) => (categories[b]?.value ?? 0) - (categories[a]?.value ?? 0),\n );\n case 'smallest-first':\n return categoryIds.sort(\n (a, b) => (categories[a]?.value ?? 0) - (categories[b]?.value ?? 0),\n );\n default:\n return categoryIds;\n }\n })();\n\n const categoryRefs = useCategoryRefs(sortedCategoryIds);\n\n /**\n * Determine what the total value is for all category values combined\n */\n const total = shouldRenderSkeleton\n ? 0\n : Math.round(\n sortedCategoryIds.reduce((sum, categoryId) => {\n return sum + (categories[categoryId]?.value ?? 0);\n }, 0),\n );\n\n const styles = getStyles({ height });\n\n const onMouseOver: SegmentMouseEventHandler =\n ({ ref, categoryId }) =>\n () => {\n clearTimeout(timer.current);\n setHovered(true);\n\n /**\n * Only update the highlighted category ID if an initial category wasn’t\n * supplied\n */\n if (!initialHighlightedCategoryId) {\n setHighlightedCategoryId(categoryId);\n }\n\n if (onSegmentMouseOver) onSegmentMouseOver({ ref, categoryId });\n };\n\n const onMouseOut: SegmentMouseEventHandler =\n ({ ref, categoryId }) =>\n () => {\n /**\n * TODO: rather than use a timeout, maybe use the parent element being\n * moused over (event bubbling?) to validate whether the mouse has left\n * the entire chart vs an individual segment?\n */\n timer.current = window.setTimeout(() => {\n setHovered(false);\n /**\n * Only clear the highlighted category if an initial category wasn’t\n * supplied\n */\n if (!initialHighlightedCategoryId) {\n setHighlightedCategoryId(undefined);\n }\n }, 50);\n\n if (onSegmentMouseOut) onSegmentMouseOut({ ref, categoryId });\n };\n\n const highlightedCategory: (StackedChartCategory & { index: number }) | undefined =\n !shouldRenderSkeleton &&\n initialHighlightedCategoryId &&\n categories[initialHighlightedCategoryId]\n ? {\n ...categories[initialHighlightedCategoryId],\n index: sortedCategoryIds.indexOf(initialHighlightedCategoryId),\n }\n : undefined;\n\n const content = (\n <div className={styles.container}>\n {sortedCategoryIds.map((categoryId, index) => {\n /**\n * Some dummy category data is returned here for the scenario when\n * rendering a skeleton\n */\n const category =\n !shouldRenderSkeleton && categories?.[categoryId] !== undefined\n ? categories?.[categoryId]\n : DUMMY_CATEGORY;\n const ref = categoryRefs[categoryId];\n\n if (categoryId === initialHighlightedCategoryId && ref) highlightedSegmentRef = ref;\n\n const segmentState = ((): StackedChartSegmentState => {\n switch (true) {\n /**\n * If no initial highlighted category was set, and a category is\n * moused over, use the _active_ state for that category rather\n * than the color state; this adds a drop shadow to the segment.\n * Also, if an initial highlighted category _was_ set, and it\n * receives a hover event, also set it to active.\n */\n case !initialHighlightedCategoryId && highlightedCategoryId === categoryId && !isDimmed:\n case initialHighlightedCategoryId === categoryId && isHovered && !isDimmed:\n return 'active';\n\n /**\n * - no initial highlighted category ID was passed _and_ there is\n * no highlightedCategoryId (i.e. all segments in colour)\n * - the highlighted category is this category (but the chart\n * isn’t dimmed)\n * - the initial highlighted category is this category (but the\n * chart isn’t dimmed)\n */\n case !initialHighlightedCategoryId && !highlightedCategoryId && !isDimmed:\n case initialHighlightedCategoryId === categoryId && !isDimmed:\n return 'color';\n\n /**\n * Has an initial highlighted category, but is dimmed (i.e. all\n * segments excepted the highlighted category)\n */\n case Boolean(initialHighlightedCategoryId) &&\n initialHighlightedCategoryId !== categoryId &&\n isDimmed:\n case !initialHighlightedCategoryId && !highlightedCategoryId && isDimmed:\n case highlightedCategoryId !== categoryId && isDimmed:\n return 'dimmed';\n\n /**\n * In any other case, the segment should be the default grey\n */\n default:\n return 'default';\n }\n })();\n\n const segmentProps = {\n key: categoryId,\n index,\n ref,\n categoryId,\n title: category.title,\n value: category.value,\n total,\n state: segmentState,\n onMouseOver,\n onMouseOut,\n };\n\n /**\n * Only wrap the segment in a tooltip if no initially highlighted\n * category ID was supplied.\n */\n return initialHighlightedCategoryId ? (\n <StackedChartSegment {...segmentProps} />\n ) : (\n <StackedChartSegmentTooltip\n key={categoryId}\n title={category.title}\n value={category.value}\n index={index}\n hideDelay={0}\n trigger={<StackedChartSegment {...segmentProps} />}\n />\n );\n })}\n </div>\n );\n\n /**\n * If we should only render a skeleton, return early with that component\n */\n if (shouldRenderSkeleton) return <StackedChartSkeleton height={height} />;\n\n /**\n * If the initial highlighted category isn’t within the provided set of\n * categories, fall back to displaying the No Data component\n */\n if (\n initialHighlightedCategoryId &&\n sortedCategoryIds.indexOf(initialHighlightedCategoryId) === -1\n )\n return (\n <StackedChartNoData height={height} period={period} formatMessage={formatNoDataMessage} />\n );\n\n /**\n * If an initial highlight category was provided, wrap the entire chart inside\n * a tooltip using the props for the highlighted segment to drive the tooltip\n * content.\n */\n return initialHighlightedCategoryId && highlightedCategory ? (\n <StackedChartSegmentTooltip\n hideDelay={0}\n trigger={content}\n virtualElement={highlightedSegmentRef}\n {...highlightedCategory}\n />\n ) : (\n content\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StackedChart.js","sources":["../../../../src/components/StackedChart/StackedChart.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport { StackedChartSegment } from '../StackedChartSegment';\nimport { getStyles } from './StackedChart.styles';\nimport { StackedChartSegmentState, SegmentMouseEventHandler } from '../StackedChartSegment/types';\nimport { StackedChartSegmentTooltip } from '../StackedChartSegmentTooltip';\nimport { StackedChartSkeleton } from '../StackedChartSkeleton';\nimport { StackedChartNoData, StackedChartNoDataProps } from '../StackedChartNoData';\nimport { STACKED_CHART_DEFAULT_HEIGHT } from './common';\n\nexport interface StackedChartCategory {\n title: string;\n value: number;\n}\n\nconst DUMMY_CATEGORY: StackedChartCategory = {\n title: 'Category',\n value: 0.01,\n};\n\nexport type StackedChartCategories<T extends string> = Partial<Record<T, StackedChartCategory>>;\n\nexport type StackedChartSortOrder = 'largest-first' | 'smallest-first' | 'natural';\n\n/**\n * Callback function invoked when a mouse event occurs on a chart segment.\n *\n * This is a simplified callback (not a React event handler) that receives\n * segment metadata. It's called after the internal event handling is complete.\n *\n * @param args - Object containing segment metadata\n * @param args.categoryId - The unique identifier of the segment that triggered the event\n * @param args.ref - React ref to the segment's DOM element, useful for positioning tooltips or popovers\n *\n * @example\n * ```tsx\n * <StackedChart\n * categories={categories}\n * onSegmentMouseOver={({ categoryId, ref }) => {\n * console.log(`Hovered segment: ${categoryId}`);\n * // ref.current gives you the DOM element if needed\n * }}\n * onSegmentMouseOut={({ categoryId }) => {\n * console.log(`Left segment: ${categoryId}`);\n * }}\n * />\n * ```\n */\ntype MouseEventHandler = ({\n categoryId,\n ref,\n}: {\n categoryId: string;\n ref: React.ForwardedRef<HTMLDivElement>;\n}) => void;\n\nexport interface StackedChartProps<T extends string> {\n /**\n * How should category segments be sorted?\n */\n sortOrder?: StackedChartSortOrder;\n\n /**\n * Height to render the stacked chart, in pixels\n */\n height?: number;\n\n /**\n * initial category ID to highlight; if omitted then all segments will render\n * in colour and highlight based on mouseover events.\n */\n highlightedCategoryId?: T;\n\n /**\n * Should the whole StackedChart be dimmed, i.e. with a highlighted category\n * not in color, all other categories faded out?\n */\n isDimmed?: boolean;\n\n /**\n * Callback invoked when the mouse enters a chart segment.\n *\n * Receives the segment's `categoryId` and a `ref` to its DOM element.\n * Use this to implement custom hover behavior, analytics tracking,\n * or coordination with external UI elements.\n *\n * @example\n * ```tsx\n * onSegmentMouseOver={({ categoryId, ref }) => {\n * setActiveCategory(categoryId);\n * // Optional: use ref.current for DOM measurements\n * }}\n * ```\n */\n onSegmentMouseOver?: MouseEventHandler;\n\n /**\n * Callback invoked when the mouse leaves a chart segment.\n *\n * Receives the segment's `categoryId` and a `ref` to its DOM element.\n * Use this to clean up hover state or hide related UI elements.\n *\n * @example\n * ```tsx\n * onSegmentMouseOut={({ categoryId }) => {\n * setActiveCategory(null);\n * }}\n * ```\n */\n onSegmentMouseOut?: MouseEventHandler;\n\n /**\n * String representing the period for which the chart is displaying data\n */\n period?: string;\n\n formatNoDataMessage?: StackedChartNoDataProps['formatMessage'];\n\n /**\n * Is the StackedChart explicitly in a loading state?\n */\n isSkeleton?: boolean;\n\n /**\n * Array of StackedChartCategory to build the chart from\n */\n categories?: StackedChartCategories<T>;\n}\n\n/**\n * Custom hook to assign a ref per categoryId\n */\nconst useCategoryRefs = <T extends string>(\n categoryIds: T[],\n): Record<T, React.RefObject<HTMLDivElement>> => {\n const refsMap = useRef<Map<T, React.RefObject<HTMLDivElement>>>(new Map());\n\n for (const categoryId of categoryIds) {\n if (!refsMap.current.has(categoryId)) {\n refsMap.current.set(categoryId, { current: null });\n }\n }\n\n const result = {} as Record<T, React.RefObject<HTMLDivElement>>;\n for (const categoryId of categoryIds) {\n result[categoryId] = refsMap.current.get(categoryId)!;\n }\n\n return result;\n};\n\nexport const StackedChart = <T extends string>({\n categories,\n highlightedCategoryId: initialHighlightedCategoryId,\n isSkeleton = false,\n isDimmed = false,\n sortOrder = 'largest-first',\n height = STACKED_CHART_DEFAULT_HEIGHT,\n onSegmentMouseOver,\n onSegmentMouseOut,\n period = 'current',\n formatNoDataMessage,\n}: StackedChartProps<T>) => {\n const shouldRenderSkeleton = isSkeleton || !categories;\n let highlightedSegmentRef = useRef<HTMLDivElement>(null);\n const [highlightedCategoryId, setHighlightedCategoryId] = useState<string | undefined>(\n /**\n * Using a type assertion here ultimately because you can’t pass a generic\n * type argument to the type of props in React.forwardRef (it is inferred as\n * of type string).\n *\n * See: https://stackoverflow.com/questions/51884498/using-react-forwardref-with-typescript-generic-jsx-arguments\n */\n initialHighlightedCategoryId as string,\n );\n const [isHovered, setHovered] = useState<boolean>(false);\n const timer = useRef<number | undefined>(undefined);\n\n const sortedCategoryIds = (() => {\n let categoryIds: T[] = [];\n\n for (const categoryId in categories) {\n categoryIds.push(categoryId);\n }\n\n // return early since we may not even have any categories at this point\n if (shouldRenderSkeleton) return categoryIds;\n\n switch (sortOrder) {\n case 'largest-first':\n return categoryIds.sort(\n (a, b) => (categories[b]?.value ?? 0) - (categories[a]?.value ?? 0),\n );\n case 'smallest-first':\n return categoryIds.sort(\n (a, b) => (categories[a]?.value ?? 0) - (categories[b]?.value ?? 0),\n );\n default:\n return categoryIds;\n }\n })();\n\n const categoryRefs = useCategoryRefs(sortedCategoryIds);\n\n /**\n * Determine what the total value is for all category values combined\n */\n const total = shouldRenderSkeleton\n ? 0\n : Math.round(\n sortedCategoryIds.reduce((sum, categoryId) => {\n return sum + (categories[categoryId]?.value ?? 0);\n }, 0),\n );\n\n const styles = getStyles({ height });\n\n const onMouseOver: SegmentMouseEventHandler =\n ({ ref, categoryId }) =>\n () => {\n clearTimeout(timer.current);\n setHovered(true);\n\n /**\n * Only update the highlighted category ID if an initial category wasn’t\n * supplied\n */\n if (!initialHighlightedCategoryId) {\n setHighlightedCategoryId(categoryId);\n }\n\n if (onSegmentMouseOver) onSegmentMouseOver({ ref, categoryId });\n };\n\n const onMouseOut: SegmentMouseEventHandler =\n ({ ref, categoryId }) =>\n () => {\n /**\n * TODO: rather than use a timeout, maybe use the parent element being\n * moused over (event bubbling?) to validate whether the mouse has left\n * the entire chart vs an individual segment?\n */\n timer.current = window.setTimeout(() => {\n setHovered(false);\n /**\n * Only clear the highlighted category if an initial category wasn’t\n * supplied\n */\n if (!initialHighlightedCategoryId) {\n setHighlightedCategoryId(undefined);\n }\n }, 50);\n\n if (onSegmentMouseOut) onSegmentMouseOut({ ref, categoryId });\n };\n\n const highlightedCategory: (StackedChartCategory & { index: number }) | undefined =\n !shouldRenderSkeleton &&\n initialHighlightedCategoryId &&\n categories[initialHighlightedCategoryId]\n ? {\n ...categories[initialHighlightedCategoryId],\n index: sortedCategoryIds.indexOf(initialHighlightedCategoryId),\n }\n : undefined;\n\n const content = (\n <div className={styles.container}>\n {sortedCategoryIds.map((categoryId, index) => {\n /**\n * Some dummy category data is returned here for the scenario when\n * rendering a skeleton\n */\n const category =\n !shouldRenderSkeleton && categories?.[categoryId] !== undefined\n ? categories?.[categoryId]\n : DUMMY_CATEGORY;\n const ref = categoryRefs[categoryId];\n\n if (categoryId === initialHighlightedCategoryId && ref) highlightedSegmentRef = ref;\n\n const segmentState = ((): StackedChartSegmentState => {\n switch (true) {\n /**\n * If no initial highlighted category was set, and a category is\n * moused over, use the _active_ state for that category rather\n * than the color state; this adds a drop shadow to the segment.\n * Also, if an initial highlighted category _was_ set, and it\n * receives a hover event, also set it to active.\n */\n case !initialHighlightedCategoryId && highlightedCategoryId === categoryId && !isDimmed:\n case initialHighlightedCategoryId === categoryId && isHovered && !isDimmed:\n return 'active';\n\n /**\n * - no initial highlighted category ID was passed _and_ there is\n * no highlightedCategoryId (i.e. all segments in colour)\n * - the highlighted category is this category (but the chart\n * isn’t dimmed)\n * - the initial highlighted category is this category (but the\n * chart isn’t dimmed)\n */\n case !initialHighlightedCategoryId && !highlightedCategoryId && !isDimmed:\n case initialHighlightedCategoryId === categoryId && !isDimmed:\n return 'color';\n\n /**\n * Has an initial highlighted category, but is dimmed (i.e. all\n * segments excepted the highlighted category)\n */\n case Boolean(initialHighlightedCategoryId) &&\n initialHighlightedCategoryId !== categoryId &&\n isDimmed:\n case !initialHighlightedCategoryId && !highlightedCategoryId && isDimmed:\n case highlightedCategoryId !== categoryId && isDimmed:\n return 'dimmed';\n\n /**\n * In any other case, the segment should be the default grey\n */\n default:\n return 'default';\n }\n })();\n\n const segmentProps = {\n key: categoryId,\n index,\n ref,\n categoryId,\n title: category.title,\n value: category.value,\n total,\n state: segmentState,\n onMouseOver,\n onMouseOut,\n };\n\n /**\n * Only wrap the segment in a tooltip if no initially highlighted\n * category ID was supplied.\n */\n return initialHighlightedCategoryId ? (\n <StackedChartSegment {...segmentProps} />\n ) : (\n <StackedChartSegmentTooltip\n key={categoryId}\n title={category.title}\n value={category.value}\n index={index}\n hideDelay={0}\n trigger={<StackedChartSegment {...segmentProps} />}\n />\n );\n })}\n </div>\n );\n\n /**\n * If we should only render a skeleton, return early with that component\n */\n if (shouldRenderSkeleton) return <StackedChartSkeleton height={height} />;\n\n /**\n * If the initial highlighted category isn’t within the provided set of\n * categories, fall back to displaying the No Data component\n */\n if (\n initialHighlightedCategoryId &&\n sortedCategoryIds.indexOf(initialHighlightedCategoryId) === -1\n )\n return (\n <StackedChartNoData height={height} period={period} formatMessage={formatNoDataMessage} />\n );\n\n /**\n * If an initial highlight category was provided, wrap the entire chart inside\n * a tooltip using the props for the highlighted segment to drive the tooltip\n * content.\n */\n return initialHighlightedCategoryId && highlightedCategory ? (\n <StackedChartSegmentTooltip\n hideDelay={0}\n trigger={content}\n virtualElement={highlightedSegmentRef}\n {...highlightedCategory}\n />\n ) : (\n content\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
+ import { useEffect } from 'react';
2
+ import { ThemeChangedEvent } from '@grafana/runtime';
1
3
  import { useColorMode } from '../components/ColorModeProvider/ColorModeProvider.js';
2
- import { ThemeChangedEvent } from '../node_modules/@grafana/runtime/dist/esm/services/appEvents.js';
3
4
 
4
5
  const getThemeColorMode = (isLight) => {
5
6
  return isLight ? "light" : "dark";
@@ -8,18 +9,26 @@ const useColorModeChange = ({
8
9
  getAppEvents,
9
10
  useTheme2
10
11
  }) => {
11
- const { setColorMode } = useColorMode();
12
+ const { colorMode, setColorMode } = useColorMode();
12
13
  const appEvents = getAppEvents();
13
- appEvents.subscribe(ThemeChangedEvent, ({ payload }) => {
14
- const { isLight: isLight2 } = payload;
15
- const newColorMode2 = getThemeColorMode(isLight2);
16
- console.log("ThemeChangedEvent", { colorMode: newColorMode2 });
17
- setColorMode(newColorMode2);
18
- });
19
14
  const { isLight } = useTheme2();
20
- const newColorMode = getThemeColorMode(isLight);
21
- setColorMode(newColorMode);
22
- return newColorMode;
15
+ useEffect(() => {
16
+ const themeChangedEvent = appEvents.subscribe(
17
+ ThemeChangedEvent,
18
+ ({ payload }) => {
19
+ const newColorMode = getThemeColorMode(payload.isLight);
20
+ setColorMode(newColorMode);
21
+ }
22
+ );
23
+ return () => {
24
+ themeChangedEvent.unsubscribe();
25
+ };
26
+ }, [appEvents, setColorMode]);
27
+ useEffect(() => {
28
+ const newColorMode = getThemeColorMode(isLight);
29
+ setColorMode(newColorMode);
30
+ }, [isLight, setColorMode]);
31
+ return colorMode;
23
32
  };
24
33
 
25
34
  export { useColorModeChange };
@@ -1 +1 @@
1
- {"version":3,"file":"useColorModeChange.js","sources":["../../../src/hooks/useColorModeChange.ts"],"sourcesContent":["import type { EventBus, GrafanaTheme2 } from '@grafana/data';\nimport { ThemeChangedEvent } from '@grafana/runtime';\nimport type { ThemeColorMode } from '@grafana/design-tokens';\nimport { useColorMode } from '../components/ColorModeProvider/ColorModeProvider';\n\nconst getThemeColorMode = (isLight: boolean): ThemeColorMode => {\n return isLight ? 'light' : 'dark';\n};\n\nexport interface ColorModeChangeProps {\n getAppEvents: () => EventBus;\n useTheme2: () => GrafanaTheme2;\n}\n\n/**\n * This allows us to register an event listener for when the ThemeChangedEvent\n * is fired, and update the currently active color mode accordingly. It will\n * also determine the color mode on initial registration, and update the context\n * provider accordingly.\n */\nexport const useColorModeChange = ({\n getAppEvents,\n useTheme2,\n}: ColorModeChangeProps): ThemeColorMode => {\n const { setColorMode } = useColorMode();\n const appEvents = getAppEvents();\n\n appEvents.subscribe<ThemeChangedEvent>(ThemeChangedEvent, ({ payload }) => {\n const { isLight } = payload;\n const newColorMode = getThemeColorMode(isLight);\n\n console.log('ThemeChangedEvent', { colorMode: newColorMode });\n setColorMode(newColorMode);\n });\n\n /**\n * Longterm the current colorMode should ideally exist outside of useTheme2\n */\n const { isLight } = useTheme2();\n const newColorMode = getThemeColorMode(isLight);\n setColorMode(newColorMode);\n\n return newColorMode;\n};\n"],"names":["isLight","newColorMode"],"mappings":";;;AAKA,MAAM,iBAAA,GAAoB,CAAC,OAAA,KAAqC;AAC9D,EAAA,OAAO,UAAU,OAAA,GAAU,MAAA;AAC7B,CAAA;AAaO,MAAM,qBAAqB,CAAC;AAAA,EACjC,YAAA;AAAA,EACA;AACF,CAAA,KAA4C;AAC1C,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,YAAA,EAAa;AACtC,EAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,EAAA,SAAA,CAAU,SAAA,CAA6B,iBAAA,EAAmB,CAAC,EAAE,SAAQ,KAAM;AACzE,IAAA,MAAM,EAAE,OAAA,EAAAA,QAAAA,EAAQ,GAAI,OAAA;AACpB,IAAA,MAAMC,aAAAA,GAAe,kBAAkBD,QAAO,CAAA;AAE9C,IAAA,OAAA,CAAQ,GAAA,CAAI,mBAAA,EAAqB,EAAE,SAAA,EAAWC,eAAc,CAAA;AAC5D,IAAA,YAAA,CAAaA,aAAY,CAAA;AAAA,EAC3B,CAAC,CAAA;AAKD,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,SAAA,EAAU;AAC9B,EAAA,MAAM,YAAA,GAAe,kBAAkB,OAAO,CAAA;AAC9C,EAAA,YAAA,CAAa,YAAY,CAAA;AAEzB,EAAA,OAAO,YAAA;AACT;;;;"}
1
+ {"version":3,"file":"useColorModeChange.js","sources":["../../../src/hooks/useColorModeChange.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { EventBus, GrafanaTheme2 } from '@grafana/data';\nimport { ThemeChangedEvent } from '@grafana/runtime';\nimport type { ThemeColorMode } from '@grafana/design-tokens';\nimport { useColorMode } from '../components/ColorModeProvider/ColorModeProvider';\n\nconst getThemeColorMode = (isLight: boolean): ThemeColorMode => {\n return isLight ? 'light' : 'dark';\n};\n\nexport interface ColorModeChangeProps {\n getAppEvents: () => EventBus;\n useTheme2: () => GrafanaTheme2;\n}\n\n/**\n * This allows us to register an event listener for when the ThemeChangedEvent\n * is fired, and update the currently active color mode accordingly. It will\n * also determine the color mode on initial registration, and update the context\n * provider accordingly.\n */\nexport const useColorModeChange = ({\n getAppEvents,\n useTheme2,\n}: ColorModeChangeProps): ThemeColorMode => {\n const { colorMode, setColorMode } = useColorMode();\n const appEvents = getAppEvents();\n\n /**\n * Longterm the current colorMode should ideally exist outside of useTheme2\n */\n const { isLight } = useTheme2();\n\n useEffect(() => {\n const themeChangedEvent = appEvents.subscribe<ThemeChangedEvent>(\n ThemeChangedEvent,\n ({ payload }) => {\n const newColorMode = getThemeColorMode(payload.isLight);\n setColorMode(newColorMode);\n },\n );\n\n return () => {\n themeChangedEvent.unsubscribe();\n };\n }, [appEvents, setColorMode]);\n\n // Sync color mode when theme changes (runs on mount and when isLight changes)\n useEffect(() => {\n const newColorMode = getThemeColorMode(isLight);\n setColorMode(newColorMode);\n }, [isLight, setColorMode]);\n\n return colorMode;\n};\n"],"names":[],"mappings":";;;;AAMA,MAAM,iBAAA,GAAoB,CAAC,OAAA,KAAqC;AAC9D,EAAA,OAAO,UAAU,OAAA,GAAU,MAAA;AAC7B,CAAA;AAaO,MAAM,qBAAqB,CAAC;AAAA,EACjC,YAAA;AAAA,EACA;AACF,CAAA,KAA4C;AAC1C,EAAA,MAAM,EAAE,SAAA,EAAW,YAAA,EAAa,GAAI,YAAA,EAAa;AACjD,EAAA,MAAM,YAAY,YAAA,EAAa;AAK/B,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,SAAA,EAAU;AAE9B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,oBAAoB,SAAA,CAAU,SAAA;AAAA,MAClC,iBAAA;AAAA,MACA,CAAC,EAAE,OAAA,EAAQ,KAAM;AACf,QAAA,MAAM,YAAA,GAAe,iBAAA,CAAkB,OAAA,CAAQ,OAAO,CAAA;AACtD,QAAA,YAAA,CAAa,YAAY,CAAA;AAAA,MAC3B;AAAA,KACF;AAEA,IAAA,OAAO,MAAM;AACX,MAAA,iBAAA,CAAkB,WAAA,EAAY;AAAA,IAChC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,SAAA,EAAW,YAAY,CAAC,CAAA;AAG5B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,YAAA,GAAe,kBAAkB,OAAO,CAAA;AAC9C,IAAA,YAAA,CAAa,YAAY,CAAA;AAAA,EAC3B,CAAA,EAAG,CAAC,OAAA,EAAS,YAAY,CAAC,CAAA;AAE1B,EAAA,OAAO,SAAA;AACT;;;;"}
@@ -4,7 +4,7 @@ import { EventBus, GrafanaTheme2 } from '@grafana/data';
4
4
  import { ThemeColorMode } from '@grafana/design-tokens';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  import { IconName } from '@grafana/ui';
7
- import { Placement } from '@floating-ui/react';
7
+ import { Placement, FloatingPortalProps } from '@floating-ui/react';
8
8
 
9
9
  interface ColorModeChangeProps {
10
10
  getAppEvents: () => EventBus;
@@ -84,6 +84,10 @@ interface PopoverProps {
84
84
  * Virtual element to anchor the popover to instead of the trigger
85
85
  */
86
86
  virtualElement?: React.RefObject<Element>;
87
+ /**
88
+ * DOM element or ID to render the portal into, falls back to document.body
89
+ */
90
+ portalContainer?: FloatingPortalProps['root'];
87
91
  }
88
92
  declare const Popover: React$1.ForwardRefExoticComponent<PopoverProps & React$1.RefAttributes<HTMLElement>>;
89
93
 
@@ -117,6 +121,39 @@ interface GenericSkeletonProps {
117
121
  }
118
122
  declare const GenericSkeleton: ({ animationDuration }: GenericSkeletonProps) => react_jsx_runtime.JSX.Element;
119
123
 
124
+ type PortalRoot = FloatingPortalProps['root'];
125
+ interface PortalContextType {
126
+ root: PortalRoot;
127
+ setRoot: (root: PortalRoot) => void;
128
+ }
129
+ interface PortalProviderProps {
130
+ children: ReactNode;
131
+ defaultRoot?: PortalRoot;
132
+ }
133
+ interface UsePortalOptions {
134
+ /** If true, returns document.body instead of throwing when outside provider */
135
+ allowOutsideProvider?: boolean;
136
+ }
137
+ /**
138
+ * Provides a shared context for a portal root, which can be a selector string,
139
+ * an HTMLElement, or null.
140
+ *
141
+ * Wrap your application (or a subtree) in this provider to enable
142
+ * Popover, Tooltip, and other floating components to render correctly.
143
+ *
144
+ * @example
145
+ * ```tsx
146
+ * <PortalProvider defaultRoot={document.getElementById('grafana-portal-container')}>
147
+ * <App />
148
+ * </PortalProvider>
149
+ * ```
150
+ */
151
+ declare const PortalProvider: React__default.FC<PortalProviderProps>;
152
+ /**
153
+ * Use this hook to access the portal root context.
154
+ */
155
+ declare const usePortal: ({ allowOutsideProvider }?: UsePortalOptions) => PortalContextType;
156
+
120
157
  type StackedChartNoDataMessageFormatter = (args: {
121
158
  period: string;
122
159
  className: string;
@@ -143,6 +180,30 @@ interface StackedChartCategory {
143
180
  }
144
181
  type StackedChartCategories<T extends string> = Partial<Record<T, StackedChartCategory>>;
145
182
  type StackedChartSortOrder = 'largest-first' | 'smallest-first' | 'natural';
183
+ /**
184
+ * Callback function invoked when a mouse event occurs on a chart segment.
185
+ *
186
+ * This is a simplified callback (not a React event handler) that receives
187
+ * segment metadata. It's called after the internal event handling is complete.
188
+ *
189
+ * @param args - Object containing segment metadata
190
+ * @param args.categoryId - The unique identifier of the segment that triggered the event
191
+ * @param args.ref - React ref to the segment's DOM element, useful for positioning tooltips or popovers
192
+ *
193
+ * @example
194
+ * ```tsx
195
+ * <StackedChart
196
+ * categories={categories}
197
+ * onSegmentMouseOver={({ categoryId, ref }) => {
198
+ * console.log(`Hovered segment: ${categoryId}`);
199
+ * // ref.current gives you the DOM element if needed
200
+ * }}
201
+ * onSegmentMouseOut={({ categoryId }) => {
202
+ * console.log(`Left segment: ${categoryId}`);
203
+ * }}
204
+ * />
205
+ * ```
206
+ */
146
207
  type MouseEventHandler = ({ categoryId, ref, }: {
147
208
  categoryId: string;
148
209
  ref: React.ForwardedRef<HTMLDivElement>;
@@ -167,11 +228,33 @@ interface StackedChartProps<T extends string> {
167
228
  */
168
229
  isDimmed?: boolean;
169
230
  /**
170
- * Event handler for whenever a segment gets a mouseover event
231
+ * Callback invoked when the mouse enters a chart segment.
232
+ *
233
+ * Receives the segment's `categoryId` and a `ref` to its DOM element.
234
+ * Use this to implement custom hover behavior, analytics tracking,
235
+ * or coordination with external UI elements.
236
+ *
237
+ * @example
238
+ * ```tsx
239
+ * onSegmentMouseOver={({ categoryId, ref }) => {
240
+ * setActiveCategory(categoryId);
241
+ * // Optional: use ref.current for DOM measurements
242
+ * }}
243
+ * ```
171
244
  */
172
245
  onSegmentMouseOver?: MouseEventHandler;
173
246
  /**
174
- * Event handler for whenever a segment gets a mouseout event
247
+ * Callback invoked when the mouse leaves a chart segment.
248
+ *
249
+ * Receives the segment's `categoryId` and a `ref` to its DOM element.
250
+ * Use this to clean up hover state or hide related UI elements.
251
+ *
252
+ * @example
253
+ * ```tsx
254
+ * onSegmentMouseOut={({ categoryId }) => {
255
+ * setActiveCategory(null);
256
+ * }}
257
+ * ```
175
258
  */
176
259
  onSegmentMouseOut?: MouseEventHandler;
177
260
  /**
@@ -310,5 +393,5 @@ interface FormatNumberOptions {
310
393
  */
311
394
  declare const formatNumber: (value: number | string | "loading", options?: FormatNumberOptions) => string;
312
395
 
313
- export { ColorMode, ColorModeChangeHandler, ColorModeProvider, ComparisonBadge, ComparisonTooltip, GenericSkeleton, Popover, StackedChart, StackedChartNoData, StackedChartSegment, StackedChartSegmentTooltip, StackedChartSkeleton, calculateComparison, formatNumber, useColorMode, useColorModeChange };
314
- export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, StackedChartCategories, StackedChartCategory, StackedChartNoDataMessageFormatter, StackedChartNoDataProps, StackedChartProps, StackedChartSegmentTooltipProps, StackedChartSkeletonProps, StackedChartSortOrder, StackedChartTooltipContentFormatter };
396
+ export { ColorMode, ColorModeChangeHandler, ColorModeProvider, ComparisonBadge, ComparisonTooltip, GenericSkeleton, Popover, PortalProvider, StackedChart, StackedChartNoData, StackedChartSegment, StackedChartSegmentTooltip, StackedChartSkeleton, calculateComparison, formatNumber, useColorMode, useColorModeChange, usePortal };
397
+ export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, PortalContextType, PortalProviderProps, PortalRoot, SegmentMouseEventHandler, StackedChartCategories, StackedChartCategory, StackedChartNoDataMessageFormatter, StackedChartNoDataProps, StackedChartProps, StackedChartSegmentProps, StackedChartSegmentState, StackedChartSegmentTooltipProps, StackedChartSkeletonProps, StackedChartSortOrder, StackedChartTooltipContentFormatter, UsePortalOptions };
package/dist/esm/index.js CHANGED
@@ -5,6 +5,7 @@ export { ComparisonBadge } from './components/ComparisonBadge/ComparisonBadge.js
5
5
  export { ComparisonTooltip } from './components/ComparisonTooltip/ComparisonTooltip.js';
6
6
  export { GenericSkeleton } from './components/GenericSkeleton/GenericSkeleton.js';
7
7
  export { Popover } from './components/Popover/Popover.js';
8
+ export { PortalProvider, usePortal } from './components/PortalProvider/PortalProvider.js';
8
9
  export { StackedChart } from './components/StackedChart/StackedChart.js';
9
10
  export { StackedChartNoData } from './components/StackedChartNoData/StackedChartNoData.js';
10
11
  export { StackedChartSegment } from './components/StackedChartSegment/StackedChartSegment.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Grafana Labs",
3
3
  "name": "@grafana/components",
4
4
  "license": "Apache-2.0",
5
- "version": "0.0.30",
5
+ "version": "0.0.32",
6
6
  "description": "Product Design Engineering Components for Grafana",
7
7
  "repository": {
8
8
  "type": "git",
@@ -68,7 +68,6 @@
68
68
  "@eslint/eslintrc": "^3.3.1",
69
69
  "@eslint/js": "^9.37.0",
70
70
  "@grafana/eslint-config": "^9.0.0",
71
- "@grafana/runtime": "^11.5.3",
72
71
  "@grafana/tsconfig": "^2.0.1",
73
72
  "@rollup/plugin-commonjs": "^29",
74
73
  "@rollup/plugin-eslint": "^9.2.0",
@@ -120,6 +119,7 @@
120
119
  "@floating-ui/react": "^0.27.16",
121
120
  "@grafana/data": "^11.5.3",
122
121
  "@grafana/design-tokens": "^0.0.26",
122
+ "@grafana/runtime": "^11.5.3",
123
123
  "@grafana/ui": "^11.5.3",
124
124
  "clsx": "^2.1.1",
125
125
  "react-useportal": "^1.0.19"