@grafana/components 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/cjs/index.cjs +31 -29
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +241 -33
- package/dist/esm/components/ColorModeProvider/ColorModeProvider.js +0 -1
- package/dist/esm/components/ColorModeProvider/ColorModeProvider.js.map +1 -1
- package/dist/esm/components/ComparisonTooltip/ComparisonTooltip.js.map +1 -1
- package/dist/esm/components/Popover/Popover.js +5 -3
- package/dist/esm/components/Popover/Popover.js.map +1 -1
- package/dist/esm/components/PortalProvider/PortalProvider.js +7 -14
- package/dist/esm/components/PortalProvider/PortalProvider.js.map +1 -1
- package/dist/esm/components/StackedChart/StackedChart.js.map +1 -1
- package/dist/esm/components/StackedChartSegmentTooltip/StackedChartSegmentTooltip.js.map +1 -1
- package/dist/esm/hooks/useColorModeChange.js +19 -10
- package/dist/esm/hooks/useColorModeChange.js.map +1 -1
- package/dist/esm/index.d.ts +241 -33
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/formatters.js.map +1 -1
- package/package.json +2 -2
package/dist/cjs/index.d.cts
CHANGED
|
@@ -49,15 +49,7 @@ declare const ColorModeProvider: React__default.FC<ColorModeProviderProps>;
|
|
|
49
49
|
*/
|
|
50
50
|
declare const useColorMode: () => ColorModeContextType;
|
|
51
51
|
|
|
52
|
-
interface
|
|
53
|
-
/**
|
|
54
|
-
* Content used to trigger the Popover being displayed
|
|
55
|
-
*/
|
|
56
|
-
trigger: JSX.Element;
|
|
57
|
-
/**
|
|
58
|
-
* Content to render within the Popover
|
|
59
|
-
*/
|
|
60
|
-
children: JSX.Element;
|
|
52
|
+
interface SharedPopoverProps {
|
|
61
53
|
/**
|
|
62
54
|
* Should the popover be open? Implicitly means the popover visibility is
|
|
63
55
|
* controlled; if omitted, the popover target will control visibility
|
|
@@ -89,9 +81,38 @@ interface PopoverProps {
|
|
|
89
81
|
*/
|
|
90
82
|
portalContainer?: FloatingPortalProps['root'];
|
|
91
83
|
}
|
|
84
|
+
interface PopoverWithTriggerProps extends SharedPopoverProps {
|
|
85
|
+
/**
|
|
86
|
+
* Content used to trigger the Popover being displayed
|
|
87
|
+
*/
|
|
88
|
+
trigger: JSX.Element;
|
|
89
|
+
/**
|
|
90
|
+
* Content to render within the Popover
|
|
91
|
+
*/
|
|
92
|
+
children: JSX.Element;
|
|
93
|
+
/**
|
|
94
|
+
* When using a Trigger prop, there shouldn’t be a content prop
|
|
95
|
+
*/
|
|
96
|
+
content?: never;
|
|
97
|
+
}
|
|
98
|
+
interface PopoverWithContentProps extends SharedPopoverProps {
|
|
99
|
+
/**
|
|
100
|
+
* Content to render within the Popover
|
|
101
|
+
*/
|
|
102
|
+
content: JSX.Element | string;
|
|
103
|
+
/**
|
|
104
|
+
* Children to use as the trigger for the Popover
|
|
105
|
+
*/
|
|
106
|
+
children: JSX.Element;
|
|
107
|
+
/**
|
|
108
|
+
* When using a Content prop, there shouldn’t be a trigger prop
|
|
109
|
+
*/
|
|
110
|
+
trigger?: never;
|
|
111
|
+
}
|
|
112
|
+
type PopoverProps = PopoverWithTriggerProps | PopoverWithContentProps;
|
|
92
113
|
declare const Popover: React$1.ForwardRefExoticComponent<PopoverProps & React$1.RefAttributes<HTMLElement>>;
|
|
93
114
|
|
|
94
|
-
interface ComparisonTooltipProps extends Omit<
|
|
115
|
+
interface ComparisonTooltipProps extends Omit<PopoverWithTriggerProps, 'children'> {
|
|
95
116
|
current?: string;
|
|
96
117
|
previous?: string;
|
|
97
118
|
previousLabel?: string;
|
|
@@ -130,15 +151,29 @@ interface PortalProviderProps {
|
|
|
130
151
|
children: ReactNode;
|
|
131
152
|
defaultRoot?: PortalRoot;
|
|
132
153
|
}
|
|
154
|
+
interface UsePortalOptions {
|
|
155
|
+
/** If true, returns document.body instead of throwing when outside provider */
|
|
156
|
+
allowOutsideProvider?: boolean;
|
|
157
|
+
}
|
|
133
158
|
/**
|
|
134
159
|
* Provides a shared context for a portal root, which can be a selector string,
|
|
135
160
|
* an HTMLElement, or null.
|
|
161
|
+
*
|
|
162
|
+
* Wrap your application (or a subtree) in this provider to enable
|
|
163
|
+
* Popover, Tooltip, and other floating components to render correctly.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```tsx
|
|
167
|
+
* <PortalProvider defaultRoot={document.getElementById('grafana-portal-container')}>
|
|
168
|
+
* <App />
|
|
169
|
+
* </PortalProvider>
|
|
170
|
+
* ```
|
|
136
171
|
*/
|
|
137
172
|
declare const PortalProvider: React__default.FC<PortalProviderProps>;
|
|
138
173
|
/**
|
|
139
174
|
* Use this hook to access the portal root context.
|
|
140
175
|
*/
|
|
141
|
-
declare const usePortal: () => PortalContextType;
|
|
176
|
+
declare const usePortal: ({ allowOutsideProvider }?: UsePortalOptions) => PortalContextType;
|
|
142
177
|
|
|
143
178
|
type StackedChartNoDataMessageFormatter = (args: {
|
|
144
179
|
period: string;
|
|
@@ -166,6 +201,30 @@ interface StackedChartCategory {
|
|
|
166
201
|
}
|
|
167
202
|
type StackedChartCategories<T extends string> = Partial<Record<T, StackedChartCategory>>;
|
|
168
203
|
type StackedChartSortOrder = 'largest-first' | 'smallest-first' | 'natural';
|
|
204
|
+
/**
|
|
205
|
+
* Callback function invoked when a mouse event occurs on a chart segment.
|
|
206
|
+
*
|
|
207
|
+
* This is a simplified callback (not a React event handler) that receives
|
|
208
|
+
* segment metadata. It's called after the internal event handling is complete.
|
|
209
|
+
*
|
|
210
|
+
* @param args - Object containing segment metadata
|
|
211
|
+
* @param args.categoryId - The unique identifier of the segment that triggered the event
|
|
212
|
+
* @param args.ref - React ref to the segment's DOM element, useful for positioning tooltips or popovers
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```tsx
|
|
216
|
+
* <StackedChart
|
|
217
|
+
* categories={categories}
|
|
218
|
+
* onSegmentMouseOver={({ categoryId, ref }) => {
|
|
219
|
+
* console.log(`Hovered segment: ${categoryId}`);
|
|
220
|
+
* // ref.current gives you the DOM element if needed
|
|
221
|
+
* }}
|
|
222
|
+
* onSegmentMouseOut={({ categoryId }) => {
|
|
223
|
+
* console.log(`Left segment: ${categoryId}`);
|
|
224
|
+
* }}
|
|
225
|
+
* />
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
169
228
|
type MouseEventHandler = ({ categoryId, ref, }: {
|
|
170
229
|
categoryId: string;
|
|
171
230
|
ref: React.ForwardedRef<HTMLDivElement>;
|
|
@@ -190,11 +249,33 @@ interface StackedChartProps<T extends string> {
|
|
|
190
249
|
*/
|
|
191
250
|
isDimmed?: boolean;
|
|
192
251
|
/**
|
|
193
|
-
*
|
|
252
|
+
* Callback invoked when the mouse enters a chart segment.
|
|
253
|
+
*
|
|
254
|
+
* Receives the segment's `categoryId` and a `ref` to its DOM element.
|
|
255
|
+
* Use this to implement custom hover behavior, analytics tracking,
|
|
256
|
+
* or coordination with external UI elements.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```tsx
|
|
260
|
+
* onSegmentMouseOver={({ categoryId, ref }) => {
|
|
261
|
+
* setActiveCategory(categoryId);
|
|
262
|
+
* // Optional: use ref.current for DOM measurements
|
|
263
|
+
* }}
|
|
264
|
+
* ```
|
|
194
265
|
*/
|
|
195
266
|
onSegmentMouseOver?: MouseEventHandler;
|
|
196
267
|
/**
|
|
197
|
-
*
|
|
268
|
+
* Callback invoked when the mouse leaves a chart segment.
|
|
269
|
+
*
|
|
270
|
+
* Receives the segment's `categoryId` and a `ref` to its DOM element.
|
|
271
|
+
* Use this to clean up hover state or hide related UI elements.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```tsx
|
|
275
|
+
* onSegmentMouseOut={({ categoryId }) => {
|
|
276
|
+
* setActiveCategory(null);
|
|
277
|
+
* }}
|
|
278
|
+
* ```
|
|
198
279
|
*/
|
|
199
280
|
onSegmentMouseOut?: MouseEventHandler;
|
|
200
281
|
/**
|
|
@@ -276,7 +357,7 @@ declare const StackedChartSegment: React__default.ForwardRefExoticComponent<Stac
|
|
|
276
357
|
type StackedChartTooltipContentFormatter = (args: {
|
|
277
358
|
colorClassName: string;
|
|
278
359
|
} & Required<Pick<StackedChartSegmentProps<string>, 'value' | 'total' | 'index'>>) => React.ReactElement;
|
|
279
|
-
interface StackedChartSegmentTooltipProps<T extends string> extends Pick<StackedChartSegmentProps<T>, 'title' | 'value' | 'total' | 'index'>, Omit<
|
|
360
|
+
interface StackedChartSegmentTooltipProps<T extends string> extends Pick<StackedChartSegmentProps<T>, 'title' | 'value' | 'total' | 'index'>, Omit<PopoverWithTriggerProps, 'children'> {
|
|
280
361
|
formatContent?: StackedChartTooltipContentFormatter;
|
|
281
362
|
}
|
|
282
363
|
declare const StackedChartSegmentTooltip: <T extends string>({ trigger, placement, hideDelay, title, value, total, index, formatContent, virtualElement, }: StackedChartSegmentTooltipProps<T>) => react_jsx_runtime.JSX.Element;
|
|
@@ -290,48 +371,175 @@ interface StackedChartSkeletonProps {
|
|
|
290
371
|
declare const StackedChartSkeleton: ({ height, }: StackedChartSkeletonProps) => react_jsx_runtime.JSX.Element;
|
|
291
372
|
|
|
292
373
|
/**
|
|
293
|
-
*
|
|
374
|
+
* Utilities for calculating and displaying value comparisons.
|
|
375
|
+
* @module comparison
|
|
376
|
+
*/
|
|
377
|
+
/**
|
|
378
|
+
* Result of a comparison calculation between two values.
|
|
379
|
+
*
|
|
380
|
+
* @public
|
|
381
|
+
* @category Utilities
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* const result: ComparisonResult = calculateComparison(150, 100);
|
|
386
|
+
* // result = {
|
|
387
|
+
* // hasComparison: true,
|
|
388
|
+
* // direction: 'up',
|
|
389
|
+
* // percentageChange: 50,
|
|
390
|
+
* // percentageLabel: '50.00%'
|
|
391
|
+
* // }
|
|
392
|
+
* ```
|
|
294
393
|
*/
|
|
295
394
|
interface ComparisonResult {
|
|
395
|
+
/**
|
|
396
|
+
* Whether a valid comparison could be made.
|
|
397
|
+
* False when either value is null, undefined, or baseline is 0.
|
|
398
|
+
*/
|
|
296
399
|
hasComparison: boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Direction of change: 'up' (increase), 'down' (decrease), or 'neutral' (no change).
|
|
402
|
+
*/
|
|
297
403
|
direction: 'up' | 'down' | 'neutral';
|
|
404
|
+
/**
|
|
405
|
+
* Absolute percentage change between values (always positive).
|
|
406
|
+
* Use `direction` to determine if it's an increase or decrease.
|
|
407
|
+
*/
|
|
298
408
|
percentageChange: number;
|
|
409
|
+
/**
|
|
410
|
+
* Human-readable percentage label (e.g., "25.50%", "1.2k%").
|
|
411
|
+
* Empty string when `hasComparison` is false.
|
|
412
|
+
*/
|
|
299
413
|
percentageLabel: string;
|
|
300
414
|
}
|
|
301
415
|
/**
|
|
302
|
-
* Calculate comparison metrics between a current value and a baseline value
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
416
|
+
* Calculate comparison metrics between a current value and a baseline value.
|
|
417
|
+
*
|
|
418
|
+
* Use this utility to compute percentage changes for period-over-period comparisons,
|
|
419
|
+
* such as "this month vs last month" or "current vs previous".
|
|
420
|
+
*
|
|
421
|
+
* @public
|
|
422
|
+
* @category Utilities
|
|
423
|
+
*
|
|
424
|
+
* @param current - The current/new value to compare
|
|
425
|
+
* @param baseline - The baseline/previous value to compare against
|
|
426
|
+
* @returns A {@link ComparisonResult} object with direction, percentage, and formatted label
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* // Basic increase
|
|
430
|
+
* calculateComparison(150, 100)
|
|
431
|
+
* // Returns: { hasComparison: true, direction: 'up', percentageChange: 50, percentageLabel: '50.00%' }
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* // Decrease
|
|
435
|
+
* calculateComparison(75, 100)
|
|
436
|
+
* // Returns: { hasComparison: true, direction: 'down', percentageChange: 25, percentageLabel: '25.00%' }
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* // No change
|
|
440
|
+
* calculateComparison(100, 100)
|
|
441
|
+
* // Returns: { hasComparison: true, direction: 'neutral', percentageChange: 0, percentageLabel: '0%' }
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* // Handle missing values gracefully
|
|
445
|
+
* calculateComparison(100, null)
|
|
446
|
+
* // Returns: { hasComparison: false, direction: 'neutral', percentageChange: 0, percentageLabel: '' }
|
|
447
|
+
*
|
|
448
|
+
* calculateComparison(undefined, 100)
|
|
449
|
+
* // Returns: { hasComparison: false, direction: 'neutral', percentageChange: 0, percentageLabel: '' }
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* // Division by zero protection
|
|
453
|
+
* calculateComparison(100, 0)
|
|
454
|
+
* // Returns: { hasComparison: false, direction: 'neutral', percentageChange: 0, percentageLabel: '' }
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* // Use with ComparisonBadge component
|
|
458
|
+
* ```tsx
|
|
459
|
+
* import { calculateComparison, ComparisonBadge } from '@grafana/components';
|
|
460
|
+
*
|
|
461
|
+
* const { direction, percentageLabel } = calculateComparison(currentSpend, previousSpend);
|
|
462
|
+
*
|
|
463
|
+
* <ComparisonBadge
|
|
464
|
+
* current={currentSpend}
|
|
465
|
+
* previous={previousSpend}
|
|
466
|
+
* timeframeLabel="last month"
|
|
467
|
+
* />
|
|
468
|
+
* ```
|
|
306
469
|
*/
|
|
307
470
|
declare const calculateComparison: (current: number | undefined | null, baseline: number | undefined | null) => ComparisonResult;
|
|
308
471
|
|
|
309
472
|
/**
|
|
310
|
-
*
|
|
473
|
+
* Options for formatting numbers with `formatNumber`.
|
|
474
|
+
*
|
|
475
|
+
* @public
|
|
476
|
+
* @category Utilities
|
|
311
477
|
*/
|
|
312
478
|
interface FormatNumberOptions {
|
|
313
|
-
/**
|
|
479
|
+
/**
|
|
480
|
+
* Number of decimal places to display.
|
|
481
|
+
* @default 2
|
|
482
|
+
*/
|
|
314
483
|
decimals?: number;
|
|
315
|
-
/**
|
|
484
|
+
/**
|
|
485
|
+
* Values at or above this threshold will use compact notation (k, m, b).
|
|
486
|
+
* @default 1000
|
|
487
|
+
*/
|
|
316
488
|
compactThreshold?: number;
|
|
317
|
-
/**
|
|
489
|
+
/**
|
|
490
|
+
* When true, always use full precision with locale formatting.
|
|
491
|
+
* Disables compact notation regardless of value.
|
|
492
|
+
* @default false
|
|
493
|
+
*/
|
|
318
494
|
precise?: boolean;
|
|
319
495
|
}
|
|
320
496
|
/**
|
|
321
|
-
* Format numbers with optional compact notation (k, m, b)
|
|
322
|
-
*
|
|
497
|
+
* Format numbers with optional compact notation (k, m, b).
|
|
498
|
+
*
|
|
499
|
+
* Use this utility to display numbers consistently across your application.
|
|
500
|
+
* The function handles large numbers with compact notation and provides
|
|
501
|
+
* locale-aware formatting for smaller values.
|
|
323
502
|
*
|
|
324
|
-
* @
|
|
325
|
-
* @
|
|
326
|
-
*
|
|
503
|
+
* @public
|
|
504
|
+
* @category Utilities
|
|
505
|
+
*
|
|
506
|
+
* @param value - The number to format. Accepts number, numeric string, or 'loading'
|
|
507
|
+
* @param options - Formatting options (decimals, compactThreshold, precise)
|
|
508
|
+
* @returns Formatted number string without currency or percent symbols
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* // Basic usage - compact notation for large numbers
|
|
512
|
+
* formatNumber(1234) // "1.2k"
|
|
513
|
+
* formatNumber(1500000) // "1.5m"
|
|
514
|
+
* formatNumber(2000000000) // "2.0b"
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* // Small numbers use locale formatting
|
|
518
|
+
* formatNumber(999) // "999.00"
|
|
519
|
+
* formatNumber(42.5) // "42.50"
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* // Precise mode - disable compact notation
|
|
523
|
+
* formatNumber(1234, { precise: true }) // "1,234.00"
|
|
524
|
+
* formatNumber(1500000, { precise: true }) // "1,500,000.00"
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* // Custom decimal places
|
|
528
|
+
* formatNumber(1234.5678, { decimals: 0 }) // "1k"
|
|
529
|
+
* formatNumber(42.5678, { decimals: 3 }) // "42.568"
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* // With currency (add prefix yourself)
|
|
533
|
+
* `$${formatNumber(15000)}` // "$15.0k"
|
|
534
|
+
* `$${formatNumber(15000, { precise: true })}` // "$15,000.00"
|
|
327
535
|
*
|
|
328
536
|
* @example
|
|
329
|
-
*
|
|
330
|
-
* formatNumber(
|
|
331
|
-
* formatNumber(
|
|
332
|
-
* formatNumber(
|
|
537
|
+
* // Handle edge cases
|
|
538
|
+
* formatNumber('loading') // ""
|
|
539
|
+
* formatNumber('invalid') // "N/A"
|
|
540
|
+
* formatNumber(-5000) // "-5.0k"
|
|
333
541
|
*/
|
|
334
542
|
declare const formatNumber: (value: number | string | "loading", options?: FormatNumberOptions) => string;
|
|
335
543
|
|
|
336
544
|
export { ColorMode, ColorModeChangeHandler, ColorModeProvider, ComparisonBadge, ComparisonTooltip, GenericSkeleton, Popover, PortalProvider, StackedChart, StackedChartNoData, StackedChartSegment, StackedChartSegmentTooltip, StackedChartSkeleton, calculateComparison, formatNumber, useColorMode, useColorModeChange, usePortal };
|
|
337
|
-
export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, PortalContextType, PortalProviderProps, PortalRoot, StackedChartCategories, StackedChartCategory, StackedChartNoDataMessageFormatter, StackedChartNoDataProps, StackedChartProps, StackedChartSegmentTooltipProps, StackedChartSkeletonProps, StackedChartSortOrder, StackedChartTooltipContentFormatter };
|
|
545
|
+
export type { ColorModeChangeProps, ColorModeContextType, ColorModeProviderProps, ColorModeWrapper, ComparisonResult, ComparisonTooltipProps, FormatNumberOptions, GenericSkeletonProps, PopoverProps, PopoverWithContentProps, PopoverWithTriggerProps, PortalContextType, PortalProviderProps, PortalRoot, SegmentMouseEventHandler, SharedPopoverProps, 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
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComparisonTooltip.js","sources":["../../../../src/components/ComparisonTooltip/ComparisonTooltip.tsx"],"sourcesContent":["import { Icon, IconName } from '@grafana/ui';\nimport { Popover,
|
|
1
|
+
{"version":3,"file":"ComparisonTooltip.js","sources":["../../../../src/components/ComparisonTooltip/ComparisonTooltip.tsx"],"sourcesContent":["import { Icon, IconName } from '@grafana/ui';\nimport { Popover, PopoverWithTriggerProps } from '../Popover';\nimport { getStyles } from './ComparisonTooltip.styles';\n\nexport interface ComparisonTooltipProps extends Omit<PopoverWithTriggerProps, 'children'> {\n current?: string;\n previous?: string;\n previousLabel?: string;\n currentLabel?: string;\n title?: string;\n currentIcon?: IconName;\n previousIcon?: IconName;\n hideDelay?: number;\n}\n\nexport const ComparisonTooltip = ({\n trigger,\n placement = 'top',\n current,\n previous,\n previousLabel,\n currentLabel = 'Current',\n title,\n currentIcon = 'eye',\n previousIcon = 'clock-nine',\n hideDelay,\n}: ComparisonTooltipProps) => {\n const styles = getStyles();\n\n return (\n <Popover trigger={trigger} placement={placement} hideDelay={hideDelay}>\n <div className={styles.wrapper}>\n {title && <div className={styles.heading}>{title}</div>}\n <div className={styles.content}>\n <div className={styles.section}>\n <div className={styles.sectionTitle}>{currentLabel}</div>\n <div className={styles.value}>\n <Icon name={currentIcon} size=\"sm\" className={styles.icon} />\n <span>{current || 'N/A'}</span>\n </div>\n </div>\n <div className={styles.section}>\n <div className={styles.sectionTitle}>{previousLabel}</div>\n <div className={styles.value}>\n <Icon name={previousIcon} size=\"sm\" className={styles.icon} />\n <span>{previous || 'N/A'}</span>\n </div>\n </div>\n </div>\n </div>\n </Popover>\n );\n};\n"],"names":[],"mappings":";;;;;AAeO,MAAM,oBAAoB,CAAC;AAAA,EAChC,OAAA;AAAA,EACA,SAAA,GAAY,KAAA;AAAA,EACZ,OAAA;AAAA,EACA,QAAA;AAAA,EACA,aAAA;AAAA,EACA,YAAA,GAAe,SAAA;AAAA,EACf,KAAA;AAAA,EACA,WAAA,GAAc,KAAA;AAAA,EACd,YAAA,GAAe,YAAA;AAAA,EACf;AACF,CAAA,KAA8B;AAC5B,EAAA,MAAM,SAAS,SAAA,EAAU;AAEzB,EAAA,uBACE,GAAA,CAAC,WAAQ,OAAA,EAAkB,SAAA,EAAsB,WAC/C,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,OAAA,EACpB,QAAA,EAAA;AAAA,IAAA,KAAA,oBAAS,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,SAAU,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,oBACjD,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,OAAA,EACrB,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,OAAA,EACrB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,YAAA,EAAe,QAAA,EAAA,YAAA,EAAa,CAAA;AAAA,wBACnD,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,KAAA,EACrB,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,QAAK,IAAA,EAAM,WAAA,EAAa,MAAK,IAAA,EAAK,SAAA,EAAW,OAAO,IAAA,EAAM,CAAA;AAAA,0BAC3D,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,OAAA,IAAW,KAAA,EAAM;AAAA,SAAA,EAC1B;AAAA,OAAA,EACF,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,OAAA,EACrB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,YAAA,EAAe,QAAA,EAAA,aAAA,EAAc,CAAA;AAAA,wBACpD,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,MAAA,CAAO,KAAA,EACrB,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,QAAK,IAAA,EAAM,YAAA,EAAc,MAAK,IAAA,EAAK,SAAA,EAAW,OAAO,IAAA,EAAM,CAAA;AAAA,0BAC5D,GAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA,QAAA,IAAY,KAAA,EAAM;AAAA,SAAA,EAC3B;AAAA,OAAA,EACF;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -29,7 +29,8 @@ const getMiddleware = ({
|
|
|
29
29
|
};
|
|
30
30
|
const Popover = forwardRef(
|
|
31
31
|
({
|
|
32
|
-
trigger,
|
|
32
|
+
trigger: _trigger,
|
|
33
|
+
content: _content,
|
|
33
34
|
children,
|
|
34
35
|
isOpen: isOpenControlled,
|
|
35
36
|
isInteractive = false,
|
|
@@ -47,8 +48,8 @@ const Popover = forwardRef(
|
|
|
47
48
|
const isOpen = isOpenControlled != null ? isOpenControlled : isOpenState;
|
|
48
49
|
const middleware = getMiddleware({ placement, arrowRef });
|
|
49
50
|
const styles = getStyles();
|
|
51
|
+
const { root } = usePortal();
|
|
50
52
|
const portalRoot = (() => {
|
|
51
|
-
const { root } = usePortal();
|
|
52
53
|
switch (true) {
|
|
53
54
|
case portalContainer instanceof HTMLElement:
|
|
54
55
|
return portalContainer;
|
|
@@ -127,6 +128,7 @@ const Popover = forwardRef(
|
|
|
127
128
|
},
|
|
128
129
|
[forwardedRef, refs]
|
|
129
130
|
);
|
|
131
|
+
const [trigger, content] = typeof _trigger !== "undefined" ? [_trigger, children] : [children, _content];
|
|
130
132
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
133
|
cloneElement(trigger, {
|
|
132
134
|
ref: handleRef,
|
|
@@ -136,7 +138,7 @@ const Popover = forwardRef(
|
|
|
136
138
|
}),
|
|
137
139
|
(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: [
|
|
138
140
|
/* @__PURE__ */ jsx(FloatingArrow, { className: styles.arrow, ref: arrowRef, context }),
|
|
139
|
-
/* @__PURE__ */ jsx("div", { id: popoverId, role: "tooltip", className: styles.container, children })
|
|
141
|
+
/* @__PURE__ */ jsx("div", { id: popoverId, role: "tooltip", className: styles.container, children: content })
|
|
140
142
|
] }) }) })
|
|
141
143
|
] });
|
|
142
144
|
}
|
|
@@ -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 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 portalRoot = (() => {\n const { root } = usePortal();\n\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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"}
|
|
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 SharedPopoverProps {\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\nexport interface PopoverWithTriggerProps extends SharedPopoverProps {\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 /**\n * When using a Trigger prop, there shouldn’t be a content prop\n */\n content?: never;\n}\n\nexport interface PopoverWithContentProps extends SharedPopoverProps {\n /**\n * Content to render within the Popover\n */\n content: JSX.Element | string;\n\n /**\n * Children to use as the trigger for the Popover\n */\n children: JSX.Element;\n\n /**\n * When using a Content prop, there shouldn’t be a trigger prop\n */\n trigger?: never;\n}\n\nexport type PopoverProps = PopoverWithTriggerProps | PopoverWithContentProps;\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: _trigger,\n content: _content,\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 /**\n * The interface of Popover supports two methods of usage where the children can\n * either represent the content of the popover, or the element to trigger\n * rendering the popover. Dependent on whether the trigger or content prop is\n * provided, the children prop will handle the other scenario.\n */\n const [trigger, content] =\n typeof _trigger !== 'undefined' ? [_trigger, children] : [children, _content];\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 {content}\n </div>\n </div>\n </div>\n </FloatingPortal>\n )}\n </>\n );\n },\n);\n\nPopover.displayName = 'Popover';\n"],"names":[],"mappings":"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"}
|
|
@@ -2,24 +2,17 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { createContext, useContext, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
const PortalContext = createContext(void 0);
|
|
5
|
-
const PortalProvider = ({
|
|
6
|
-
|
|
7
|
-
defaultRoot: _root = null
|
|
8
|
-
}) => {
|
|
9
|
-
const defaultRoot = (() => {
|
|
10
|
-
switch (true) {
|
|
11
|
-
case Boolean(_root):
|
|
12
|
-
return _root;
|
|
13
|
-
default:
|
|
14
|
-
return document.body;
|
|
15
|
-
}
|
|
16
|
-
})();
|
|
17
|
-
const [root, setRoot] = useState(defaultRoot);
|
|
5
|
+
const PortalProvider = ({ children, defaultRoot = null }) => {
|
|
6
|
+
const [root, setRoot] = useState(defaultRoot != null ? defaultRoot : document.body);
|
|
18
7
|
return /* @__PURE__ */ jsx(PortalContext.Provider, { value: { root, setRoot }, children });
|
|
19
8
|
};
|
|
20
|
-
const usePortal = () => {
|
|
9
|
+
const usePortal = ({ allowOutsideProvider = false } = {}) => {
|
|
21
10
|
const context = useContext(PortalContext);
|
|
22
11
|
if (context === void 0) {
|
|
12
|
+
if (allowOutsideProvider) {
|
|
13
|
+
return { root: document.body, setRoot: () => {
|
|
14
|
+
} };
|
|
15
|
+
}
|
|
23
16
|
throw new Error("usePortal must be used within a PortalProvider");
|
|
24
17
|
}
|
|
25
18
|
return context;
|
|
@@ -1 +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\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
|
|
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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"}
|