@galaxy-io/dls 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/charts/BarChart.js +29 -13
  2. package/dist/charts/ChartCategoryTargets.d.ts +2 -2
  3. package/dist/charts/ChartCategoryTargets.js +2 -1
  4. package/dist/charts/ChartFrame.d.ts +7 -5
  5. package/dist/charts/ChartFrame.js +151 -76
  6. package/dist/charts/ChartLegend.d.ts +14 -4
  7. package/dist/charts/ChartLegend.js +90 -53
  8. package/dist/charts/ChartMarkTargets.d.ts +2 -2
  9. package/dist/charts/ChartMarkTargets.js +2 -1
  10. package/dist/charts/Heatmap.js +19 -7
  11. package/dist/charts/LineChart.js +19 -8
  12. package/dist/charts/PieChart.js +16 -7
  13. package/dist/charts/chartSelection.d.ts +15 -0
  14. package/dist/charts/chartSelection.js +22 -0
  15. package/dist/charts/constants.d.ts +4 -0
  16. package/dist/charts/constants.js +6 -2
  17. package/dist/charts/types.d.ts +10 -1
  18. package/dist/charts/useChartInteraction.d.ts +25 -3
  19. package/dist/charts/useChartInteraction.js +56 -10
  20. package/dist/drawer/Drawer.d.ts +3 -1
  21. package/dist/drawer/Drawer.js +43 -14
  22. package/dist/feedback/Alert.d.ts +1 -1
  23. package/dist/inputs/CheckboxGroup.js +1 -1
  24. package/dist/inputs/CheckboxInput.js +1 -1
  25. package/dist/inputs/ColorInput.js +1 -1
  26. package/dist/inputs/CronInput.js +1 -1
  27. package/dist/inputs/DateInput.js +1 -1
  28. package/dist/inputs/KeyValueInput.js +1 -1
  29. package/dist/inputs/SelectInput.js +1 -1
  30. package/dist/inputs/SwitchInput.js +1 -1
  31. package/dist/inputs/TextAreaInput.js +6 -0
  32. package/dist/inputs/internal/ownField.js +1 -1
  33. package/dist/styles.css +1 -1
  34. package/dist/table/TableCore.js +6 -1
  35. package/dist/timeline/Timeline.d.ts +22 -1
  36. package/dist/timeline/Timeline.js +75 -43
  37. package/package.json +5 -1
@@ -49,7 +49,7 @@ function visualOrder(components) {
49
49
  return [...indexed].sort((a, b) => rank(a) - rank(b) || a.index - b.index).map(({ component }) => component);
50
50
  }
51
51
  function BarChartInner(props, ref) {
52
- const { series, groups, normalization = "NONE", minSegmentLength = 0, valueFormatter, labelFormatter, valueDomain, axisLabels, hasLegend = true, hasTooltip = true, renderTooltip, isLoading = false, placeholder = DEFAULT_PLACEHOLDER, onSelect, selection, ariaLabel, id, className, style, ...rest } = props;
52
+ const { series, groups, normalization = "NONE", minSegmentLength = 0, valueFormatter, labelFormatter, valueDomain, axisLabels, hasLegend = true, hasTooltip = true, renderTooltip, isLoading = false, placeholder = DEFAULT_PLACEHOLDER, onSelect, selection, isFilterable = false, defaultSelection, onSelectionChange, ariaLabel, id, className, style, ...rest } = props;
53
53
  const plotRef = useRef(null);
54
54
  const colors = useSeriesColorResolver(series);
55
55
  const axisValueFormatter = normalization === "PERCENT" && valueFormatter === void 0 ? PERCENT_AXIS_FORMATTER : valueFormatter;
@@ -107,7 +107,10 @@ function BarChartInner(props, ref) {
107
107
  categoryKeys,
108
108
  categoryMuteState: ChartMarkState.DIMMED,
109
109
  selection,
110
- isDisabled: !(hasTooltip || onSelect) || isLoading || isEmpty,
110
+ isFilterable,
111
+ defaultSelection,
112
+ onSelectionChange,
113
+ isDisabled: !(hasTooltip || onSelect || isFilterable) || isLoading || isEmpty,
111
114
  isLoading
112
115
  });
113
116
  const seriesKeyOf = useCallback((rect) => isStacked ? rect.componentKey : rect.metric, [isStacked]);
@@ -174,29 +177,41 @@ function BarChartInner(props, ref) {
174
177
  width: rect.width,
175
178
  height: rect.height
176
179
  })), [geometry.rects]);
177
- const handleCategorySelect = useCallback((index) => {
180
+ const { toggleSelection } = interaction;
181
+ const handleCategorySelect = useCallback((index, isAdditive = false) => {
178
182
  const group = groups[index];
179
- if (!group || !onSelect) return;
180
- onSelect({
183
+ if (!group) return;
184
+ onSelect?.({
181
185
  categoryIndex: index,
182
186
  categoryKey: group.label
183
187
  });
184
- }, [groups, onSelect]);
185
- const handleMarkSelect = useCallback((index) => {
188
+ toggleSelection({ categoryKey: group.label }, isAdditive);
189
+ }, [
190
+ groups,
191
+ onSelect,
192
+ toggleSelection
193
+ ]);
194
+ const handleMarkSelect = useCallback((index, isAdditive = false) => {
186
195
  const rect = geometry.rects[index];
187
196
  const group = groups[rect?.groupIndex ?? -1];
188
- if (!rect || !group || !onSelect) return;
189
- onSelect({
197
+ if (!rect || !group) return;
198
+ const seriesKey = seriesKeyOf(rect);
199
+ onSelect?.({
190
200
  categoryIndex: rect.groupIndex,
191
201
  categoryKey: group.label,
192
- seriesKey: seriesKeyOf(rect),
202
+ seriesKey,
193
203
  value: rect.value
194
204
  });
205
+ toggleSelection({
206
+ categoryKey: group.label,
207
+ seriesKey
208
+ }, isAdditive);
195
209
  }, [
196
210
  geometry.rects,
197
211
  groups,
198
212
  onSelect,
199
- seriesKeyOf
213
+ seriesKeyOf,
214
+ toggleSelection
200
215
  ]);
201
216
  const activeBand = interaction.activeIndex === null ? void 0 : geometry.bands[interaction.activeIndex];
202
217
  return /* @__PURE__ */ jsxs(ChartFrame, {
@@ -236,13 +251,14 @@ function BarChartInner(props, ref) {
236
251
  renderTooltip,
237
252
  categoryTargets,
238
253
  markTargets,
239
- onCategorySelect: onSelect && handleCategorySelect,
240
- onMarkSelect: onSelect && handleMarkSelect,
254
+ onCategorySelect: onSelect || isFilterable ? handleCategorySelect : void 0,
255
+ onMarkSelect: onSelect || isFilterable ? handleMarkSelect : void 0,
241
256
  anchorHalfWidth: categoryStep / 2,
242
257
  isEmpty,
243
258
  isLoading,
244
259
  placeholder,
245
260
  hasLegend,
261
+ formatCategoryKey: formatLabel,
246
262
  legendItems,
247
263
  legendPlacement: ChartLegendPlacement.BOTTOM,
248
264
  legendSwatch: ChartLegendSwatch.SQUARE,
@@ -7,8 +7,8 @@ export interface ChartCategoryTargetsProps {
7
7
  onEnter: (categoryIndex: number) => void;
8
8
  /** Pointer left the target layers (moving between targets is not leaving). */
9
9
  onLeave: () => void;
10
- /** Click on a category; supplying it makes the targets clickable. */
11
- onSelect?: ((categoryIndex: number) => void) | undefined;
10
+ /** Click on a category (additive with ⌘ / Ctrl / Shift); supplying it makes the targets clickable. */
11
+ onSelect?: ((categoryIndex: number, isAdditive: boolean) => void) | undefined;
12
12
  }
13
13
  /** @internal The category hit layer. */
14
14
  export declare function ChartCategoryTargets({ targets, onEnter, onLeave, onSelect, }: ChartCategoryTargetsProps): import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { ChartTargetShape } from "./types.js";
2
2
  import { CHART_HIT_TARGET_CSS, leaveUnlessTarget } from "./ChartPrimitives.js";
3
+ import { isAdditiveEvent } from "./chartSelection.js";
3
4
  import { Fragment, jsx } from "react/jsx-runtime";
4
5
  import { cx } from "@linaria/core";
5
6
  //#region src/charts/ChartCategoryTargets.tsx
@@ -16,7 +17,7 @@ function ChartCategoryTargets({ targets, onEnter, onLeave, onSelect }) {
16
17
  const handlers = {
17
18
  onMouseEnter: () => onEnter(index),
18
19
  onMouseLeave: (event) => leaveUnlessTarget(event, onLeave),
19
- ...onSelect && { onClick: () => onSelect(index) }
20
+ ...onSelect && { onClick: (event) => onSelect(index, isAdditiveEvent(event)) }
20
21
  };
21
22
  return target.shape === ChartTargetShape.RECT ? /* @__PURE__ */ jsx("rect", {
22
23
  x: target.x,
@@ -48,10 +48,10 @@ export interface ChartFrameProps {
48
48
  markTargets?: ChartMarkTarget[];
49
49
  /** Transform applied to both target layers (radial charts: their center). */
50
50
  targetsTransform?: string;
51
- /** Category chosen by click, Enter or Space. Supplying it makes the plot selectable. */
52
- onCategorySelect?: ((categoryIndex: number) => void) | undefined;
53
- /** Mark clicked, by index in `markTargets`. */
54
- onMarkSelect?: ((markIndex: number) => void) | undefined;
51
+ /** Category chosen by click, Enter or Space (additive with ⌘ / Ctrl / Shift). Supplying it makes the plot selectable. */
52
+ onCategorySelect?: ((categoryIndex: number, isAdditive: boolean) => void) | undefined;
53
+ /** Mark clicked, by index in `markTargets` (additive with ⌘ / Ctrl / Shift). */
54
+ onMarkSelect?: ((markIndex: number, isAdditive: boolean) => void) | undefined;
55
55
  /** Half a category's width (tooltip sits beside it). */
56
56
  anchorHalfWidth?: number;
57
57
  /** Radial charts: their center x, in plot coordinates. */
@@ -76,10 +76,12 @@ export interface ChartFrameProps {
76
76
  tooltipSwatch?: ChartLegendSwatch | undefined;
77
77
  /** A chart's own legend in place of the series legend (Heatmap's step legend). */
78
78
  legend?: ReactNode;
79
+ /** Names a raw category key in the filter chip (the chart's `labelFormatter`). @default the key */
80
+ formatCategoryKey?: ((key: string) => string) | undefined;
79
81
  /** Passthrough: `className` on the root. */
80
82
  className?: string | undefined;
81
83
  /** Passthrough: `style` on the root. */
82
84
  style?: CSSProperties | undefined;
83
85
  }
84
86
  /** @internal The chart shell. */
85
- export declare function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions, interaction, kind, categoryCount, ariaLabel, aspectRatio, axes, children, plotOverlay, plotClassName, describeCategory, hasTooltip, buildCategoryTooltip, renderTooltip, categoryTargets, markTargets, targetsTransform, onCategorySelect, onMarkSelect, anchorHalfWidth, placeAwayFromX, verticalAlign, isEmpty, isLoading, placeholder, hasLegend, legendItems, legendPlacement, legendSwatch, tooltipSwatch, legend, }: ChartFrameProps): import("react").JSX.Element;
87
+ export declare function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions, interaction, kind, categoryCount, ariaLabel, aspectRatio, axes, children, plotOverlay, plotClassName, describeCategory, hasTooltip, buildCategoryTooltip, renderTooltip, categoryTargets, markTargets, targetsTransform, onCategorySelect, onMarkSelect, anchorHalfWidth, placeAwayFromX, verticalAlign, isEmpty, isLoading, placeholder, hasLegend, legendItems, legendPlacement, legendSwatch, tooltipSwatch, legend, formatCategoryKey, }: ChartFrameProps): import("react").JSX.Element;
@@ -1,7 +1,9 @@
1
1
  import Skeleton, { SkeletonVariant } from "../feedback/Skeleton.js";
2
2
  import { ChartKind, ChartLegendPlacement } from "./types.js";
3
- import { CHART_LEGEND_SIDE_GAP } from "./constants.js";
3
+ import { CHART_LEGEND_FILTER_EXTENT, CHART_LEGEND_SIDE_GAP } from "./constants.js";
4
+ import Chip, { ChipSize } from "../chips/Chip.js";
4
5
  import EmptyState, { EmptyStateSize } from "../feedback/EmptyState.js";
6
+ import { isAdditiveEvent } from "./chartSelection.js";
5
7
  import { ChartCategoryTargets } from "./ChartCategoryTargets.js";
6
8
  import { ChartLegend } from "./ChartLegend.js";
7
9
  import { ChartMarkTargets } from "./ChartMarkTargets.js";
@@ -11,12 +13,16 @@ import { styled } from "@linaria/react";
11
13
  import { useCallback, useMemo, useState } from "react";
12
14
  import { jsx, jsxs } from "react/jsx-runtime";
13
15
  import { cx } from "@linaria/core";
16
+ import { FunnelSimpleIcon } from "@phosphor-icons/react";
14
17
  //#region src/charts/ChartFrame.tsx
15
18
  /**
16
19
  * @internal The shell every chart renders into: the root (sizing, legend placement, passthrough),
17
20
  * the measured plot box (one tab stop when interactive), the SVG and its inset translate, axes,
18
21
  * marks, hit targets, the in-chart tooltip, the empty and loading overlays, the live region and
19
- * the legend. It knows nothing about scales or geometry: axes and marks arrive as nodes, and every
22
+ * the legend. On a filterable chart dismissible filter chips follow the legend entries while
23
+ * something is pinned. One pin shows a chip per part ("06:00" and "Failed" for one segment): a
24
+ * chip's × drops that part and keeps the other. Several pins collapse into one "3 filters" chip
25
+ * (the list in its tooltip) whose × clears them all. It knows nothing about scales or geometry: axes and marks arrive as nodes, and every
20
26
  * coordinate handed to it is in plot space (the frame adds the insets once).
21
27
  *
22
28
  * Sizing: the root fills its parent (`width` / `height: 100%`); against a parent of indefinite
@@ -54,9 +60,27 @@ var Overlay = /*#__PURE__*/ styled("div")({
54
60
  });
55
61
  /** A standalone `Skeleton RECT` that takes the whole plot box. */
56
62
  var LOADING_SKELETON_CSS = "gx-ChartFrame-LOADING_SKELETON_CSS-l1oi7u1q";
63
+ /**
64
+ * A chart's own legend (Heatmap's steps) with the filter chip after it. Both carry the legend's 8px
65
+ * top padding and center on each other, so the chip centers on the legend's text line.
66
+ */
67
+ var CustomLegendBar = /*#__PURE__*/ styled("div")({
68
+ name: "CustomLegendBar",
69
+ class: "gx-ChartFrame-CustomLegendBar-c1jrxj0u",
70
+ propsAsIs: false
71
+ });
72
+ /**
73
+ * The chips after a chart's own legend. Its height (the 8px top padding and a 20px chip) is kept
74
+ * while nothing is pinned, so the plot above never moves when a chip appears.
75
+ */
76
+ var CustomFilterSlot = /*#__PURE__*/ styled("div")({
77
+ name: "CustomFilterSlot",
78
+ class: "gx-ChartFrame-CustomFilterSlot-celuo85",
79
+ propsAsIs: false
80
+ });
57
81
  var LiveRegion = /*#__PURE__*/ styled("div")({
58
82
  name: "LiveRegion",
59
- class: "gx-ChartFrame-LiveRegion-l1jrxj0u",
83
+ class: "gx-ChartFrame-LiveRegion-lvgwxyl",
60
84
  propsAsIs: false
61
85
  });
62
86
  /** The default accessible name, phrased once so the charts cannot drift. */
@@ -85,7 +109,7 @@ var NAVIGATION_KEYS = new Set([
85
109
  "End"
86
110
  ]);
87
111
  /** @internal The chart shell. */
88
- function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions, interaction, kind, categoryCount, ariaLabel, aspectRatio, axes, children, plotOverlay, plotClassName, describeCategory, hasTooltip, buildCategoryTooltip, renderTooltip, categoryTargets, markTargets, targetsTransform, onCategorySelect, onMarkSelect, anchorHalfWidth, placeAwayFromX, verticalAlign, isEmpty, isLoading, placeholder, hasLegend, legendItems, legendPlacement, legendSwatch, tooltipSwatch, legend }) {
112
+ function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions, interaction, kind, categoryCount, ariaLabel, aspectRatio, axes, children, plotOverlay, plotClassName, describeCategory, hasTooltip, buildCategoryTooltip, renderTooltip, categoryTargets, markTargets, targetsTransform, onCategorySelect, onMarkSelect, anchorHalfWidth, placeAwayFromX, verticalAlign, isEmpty, isLoading, placeholder, hasLegend, legendItems, legendPlacement, legendSwatch, tooltipSwatch, legend, formatCategoryKey }) {
89
113
  const { width, height, insets, isMeasured } = dimensions;
90
114
  const [announcement, setAnnouncement] = useState("");
91
115
  const { rootPassthrough, plotAria } = useMemo(() => {
@@ -138,17 +162,29 @@ function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions,
138
162
  if (event.key === " ") event.preventDefault();
139
163
  if (onCategorySelect && interaction.activeIndex !== null) {
140
164
  event.preventDefault();
141
- onCategorySelect(interaction.activeIndex);
165
+ onCategorySelect(interaction.activeIndex, isAdditiveEvent(event));
142
166
  }
143
167
  return;
144
168
  }
145
- if (event.key === "Escape" && interaction.activeIndex !== null) {
169
+ if (event.key !== "Escape") return;
170
+ if (interaction.activeIndex !== null) {
146
171
  event.preventDefault();
147
172
  event.stopPropagation();
148
173
  interaction.onKeyboardClear();
149
174
  setAnnouncement("");
175
+ } else if (interaction.isFilterable && interaction.hasSelection) {
176
+ event.preventDefault();
177
+ event.stopPropagation();
178
+ interaction.clearSelection();
179
+ setAnnouncement("Filter cleared.");
150
180
  }
151
181
  };
182
+ const handleLegendClick = (seriesKey, isAdditive) => interaction.toggleSelection({ seriesKey }, isAdditive);
183
+ const handleFilterDismiss = (rest) => {
184
+ if (rest === null) interaction.clearSelection();
185
+ else interaction.setSelection([rest]);
186
+ if (isInteractive) plotRef.current?.focus();
187
+ };
152
188
  const handleFocus = (event) => {
153
189
  if (event.target !== event.currentTarget) return;
154
190
  let isKeyboardFocus = true;
@@ -174,6 +210,55 @@ function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions,
174
210
  "aria-label": resolvedAriaLabel
175
211
  };
176
212
  const isSkeleton = isLoading && isEmpty;
213
+ const isSideLegend = legendPlacement === ChartLegendPlacement.RIGHT;
214
+ const categoryLabel = (key) => formatCategoryKey?.(key) ?? key;
215
+ const seriesLabel = (key) => legendItems.find((item) => item.key === key)?.label ?? key;
216
+ const describePin = (pin) => [pin.categoryKey === void 0 ? void 0 : categoryLabel(pin.categoryKey), pin.seriesKey === void 0 ? void 0 : seriesLabel(pin.seriesKey)].filter((part) => part !== void 0).join(" · ");
217
+ /** One chip per part of a single pin; one "N filters" chip for several. */
218
+ const filterChipParts = (() => {
219
+ const pins = interaction.selection;
220
+ const [pin] = pins;
221
+ if (!interaction.isFilterable || pin === void 0) return [];
222
+ if (pins.length > 1) return [{
223
+ key: "all",
224
+ label: `${pins.length} filters`,
225
+ rest: null
226
+ }];
227
+ const parts = [];
228
+ if (pin.categoryKey !== void 0) parts.push({
229
+ key: "category",
230
+ label: categoryLabel(pin.categoryKey),
231
+ rest: pin.seriesKey === void 0 ? null : { seriesKey: pin.seriesKey }
232
+ });
233
+ if (pin.seriesKey !== void 0) parts.push({
234
+ key: "series",
235
+ label: seriesLabel(pin.seriesKey),
236
+ rest: pin.categoryKey === void 0 ? null : { categoryKey: pin.categoryKey }
237
+ });
238
+ return parts;
239
+ })();
240
+ const filterChip = filterChipParts.length === 0 ? null : filterChipParts.map((part, index) => /* @__PURE__ */ jsx(Chip, {
241
+ size: ChipSize.SMALL,
242
+ ...index === 0 && { icon: FunnelSimpleIcon },
243
+ label: part.label,
244
+ tooltip: part.key === "all" ? interaction.selection.map(describePin).join(", ") : "",
245
+ onDismiss: () => handleFilterDismiss(part.rest)
246
+ }, part.key));
247
+ const legendNode = !hasLegend || isSkeleton ? null : legend ?? /* @__PURE__ */ jsx(ChartLegend, {
248
+ items: legendItems,
249
+ placement: legendPlacement,
250
+ swatch: legendSwatch,
251
+ inset: isSideLegend ? CHART_LEGEND_SIDE_GAP : insets.left,
252
+ extent: isSideLegend ? Math.max(0, height - (interaction.isFilterable ? CHART_LEGEND_FILTER_EXTENT : 0)) : 0,
253
+ isFixedExtent: !interaction.isFilterable,
254
+ hasFilterSlot: interaction.isFilterable,
255
+ filter: filterChip,
256
+ getEntryState: interaction.getSeriesState,
257
+ isEntryPinned: interaction.isSeriesPinned,
258
+ onItemClick: interaction.isFilterable ? handleLegendClick : void 0,
259
+ onItemEnter: interaction.onSeriesEnter,
260
+ onItemLeave: interaction.onSeriesLeave
261
+ });
177
262
  const hasTargets = isMeasured && isInteractive && categoryTargets.length > 0;
178
263
  const rootStyle = {
179
264
  "--gx-chart-aspect-ratio": aspectRatio,
@@ -186,77 +271,67 @@ function ChartFrame({ rootRef, plotRef, id, className, style, rest, dimensions,
186
271
  "aria-busy": isLoading || void 0,
187
272
  className: cx(CHART_LEGEND_PLACEMENT_TO_CSS_MAP[legendPlacement], className),
188
273
  style: rootStyle,
189
- children: [
190
- /* @__PURE__ */ jsxs(PlotBox, {
191
- ...plotAriaRest,
192
- ...plotA11yProps,
193
- ref: plotRef,
194
- className: cx(isInteractive && PLOT_INTERACTIVE_CSS, plotClassName),
195
- children: [
196
- isMeasured && !isSkeleton && /* @__PURE__ */ jsx(ChartSvg, {
197
- "aria-hidden": "true",
198
- width,
199
- height,
200
- children: /* @__PURE__ */ jsxs("g", {
201
- transform: `translate(${insets.left}, ${insets.top})`,
202
- children: [
203
- axes,
204
- /* @__PURE__ */ jsx("g", { children }),
205
- hasTargets && /* @__PURE__ */ jsxs("g", {
206
- transform: targetsTransform,
207
- children: [/* @__PURE__ */ jsx(ChartCategoryTargets, {
208
- targets: categoryTargets,
209
- onEnter: interaction.onCategoryEnter,
210
- onLeave: interaction.onCategoryLeave,
211
- onSelect: onCategorySelect
212
- }), onMarkSelect && markTargets && markTargets.length > 0 && /* @__PURE__ */ jsx(ChartMarkTargets, {
213
- targets: markTargets,
214
- onEnter: interaction.onCategoryEnter,
215
- onLeave: interaction.onCategoryLeave,
216
- onSelect: onMarkSelect
217
- })]
218
- })
219
- ]
220
- })
221
- }),
222
- isMeasured && !isSkeleton && plotOverlay,
223
- isMeasured && isEmpty && !isLoading && /* @__PURE__ */ jsx(Overlay, { children: /* @__PURE__ */ jsx(EmptyState, {
224
- size: EmptyStateSize.SMALL,
225
- header: placeholder,
226
- role: "status"
227
- }) }),
228
- isSkeleton && /* @__PURE__ */ jsx(Skeleton, {
229
- variant: SkeletonVariant.RECT,
230
- className: LOADING_SKELETON_CSS
231
- }),
232
- isMeasured && hasTooltip && !isSkeleton && /* @__PURE__ */ jsx(ChartTooltip, {
233
- model: tooltipModel,
234
- containerWidth: width,
235
- containerHeight: height,
236
- anchorHalfWidth,
237
- placeAwayFromX: placeAwayFromX === void 0 ? void 0 : placeAwayFromX + insets.left,
238
- verticalAlign,
239
- renderTooltip,
240
- swatch: tooltipSwatch
241
- }),
242
- /* @__PURE__ */ jsx(LiveRegion, {
243
- "aria-live": "polite",
244
- children: announcement
274
+ children: [/* @__PURE__ */ jsxs(PlotBox, {
275
+ ...plotAriaRest,
276
+ ...plotA11yProps,
277
+ ref: plotRef,
278
+ className: cx(isInteractive && PLOT_INTERACTIVE_CSS, plotClassName),
279
+ children: [
280
+ isMeasured && !isSkeleton && /* @__PURE__ */ jsx(ChartSvg, {
281
+ "aria-hidden": "true",
282
+ width,
283
+ height,
284
+ children: /* @__PURE__ */ jsxs("g", {
285
+ transform: `translate(${insets.left}, ${insets.top})`,
286
+ children: [
287
+ axes,
288
+ /* @__PURE__ */ jsx("g", { children }),
289
+ hasTargets && /* @__PURE__ */ jsxs("g", {
290
+ transform: targetsTransform,
291
+ children: [/* @__PURE__ */ jsx(ChartCategoryTargets, {
292
+ targets: categoryTargets,
293
+ onEnter: interaction.onCategoryEnter,
294
+ onLeave: interaction.onCategoryLeave,
295
+ onSelect: onCategorySelect
296
+ }), onMarkSelect && markTargets && markTargets.length > 0 && /* @__PURE__ */ jsx(ChartMarkTargets, {
297
+ targets: markTargets,
298
+ onEnter: interaction.onCategoryEnter,
299
+ onLeave: interaction.onCategoryLeave,
300
+ onSelect: onMarkSelect
301
+ })]
302
+ })
303
+ ]
245
304
  })
246
- ]
247
- }),
248
- hasLegend && !isSkeleton && legend,
249
- hasLegend && !isSkeleton && legend === void 0 && /* @__PURE__ */ jsx(ChartLegend, {
250
- items: legendItems,
251
- placement: legendPlacement,
252
- swatch: legendSwatch,
253
- inset: legendPlacement === ChartLegendPlacement.RIGHT ? CHART_LEGEND_SIDE_GAP : insets.left,
254
- extent: legendPlacement === ChartLegendPlacement.RIGHT ? height : 0,
255
- hoveredKey: interaction.hoveredSeriesKey,
256
- onItemEnter: interaction.onSeriesEnter,
257
- onItemLeave: interaction.onSeriesLeave
258
- })
259
- ]
305
+ }),
306
+ isMeasured && !isSkeleton && plotOverlay,
307
+ isMeasured && isEmpty && !isLoading && /* @__PURE__ */ jsx(Overlay, { children: /* @__PURE__ */ jsx(EmptyState, {
308
+ size: EmptyStateSize.SMALL,
309
+ header: placeholder,
310
+ role: "status"
311
+ }) }),
312
+ isSkeleton && /* @__PURE__ */ jsx(Skeleton, {
313
+ variant: SkeletonVariant.RECT,
314
+ className: LOADING_SKELETON_CSS
315
+ }),
316
+ isMeasured && hasTooltip && !isSkeleton && /* @__PURE__ */ jsx(ChartTooltip, {
317
+ model: tooltipModel,
318
+ containerWidth: width,
319
+ containerHeight: height,
320
+ anchorHalfWidth,
321
+ placeAwayFromX: placeAwayFromX === void 0 ? void 0 : placeAwayFromX + insets.left,
322
+ verticalAlign,
323
+ renderTooltip,
324
+ swatch: tooltipSwatch
325
+ }),
326
+ /* @__PURE__ */ jsx(LiveRegion, {
327
+ "aria-live": "polite",
328
+ children: announcement
329
+ })
330
+ ]
331
+ }), legendNode !== null && (interaction.isFilterable && legend !== void 0 ? /* @__PURE__ */ jsxs(CustomLegendBar, { children: [legendNode, /* @__PURE__ */ jsx(CustomFilterSlot, {
332
+ "aria-hidden": filterChip === null || void 0,
333
+ children: filterChip
334
+ })] }) : legendNode)]
260
335
  });
261
336
  }
262
337
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { type ChartLegendItem, ChartLegendPlacement } from "./types.js";
2
+ import { type ChartLegendItem, ChartLegendPlacement, ChartMarkState } from "./types.js";
3
3
  /** @internal Swatch shape: a dot for line series, a square for filled series. */
4
4
  export declare enum ChartLegendSwatch {
5
5
  /** `Circle SMALL`: line series. */
@@ -19,12 +19,22 @@ export interface ChartLegendProps {
19
19
  inset: number;
20
20
  /** RIGHT: the plot height the column fits into (0 = unconstrained). */
21
21
  extent?: number;
22
- /** The series key being isolated. */
23
- hoveredKey: string | null;
22
+ /** RIGHT: the column is `extent` tall with its entries centered; off, it hugs the entries (a Reset follows them). @default true */
23
+ isFixedExtent?: boolean;
24
+ /** An entry's emphasis: the hovered entry, else whether the selection covers its series. */
25
+ getEntryState: (key: string) => ChartMarkState;
26
+ /** The entry's series is pinned on its own: its toggle reads pressed. */
27
+ isEntryPinned?: (key: string) => boolean;
28
+ /** Filterable charts: keep a slot after the entries for the filter chip. @default false */
29
+ hasFilterSlot?: boolean;
30
+ /** The filter chip, or `null` while nothing is pinned (the slot keeps its space). */
31
+ filter?: ReactNode;
32
+ /** Click on an entry (filterable charts); supplying it makes each inline entry a toggle button. */
33
+ onItemClick?: ((key: string, isAdditive: boolean) => void) | undefined;
24
34
  /** Pointer entered an entry. */
25
35
  onItemEnter: (key: string) => void;
26
36
  /** Pointer left an entry. */
27
37
  onItemLeave: () => void;
28
38
  }
29
39
  /** @internal The chart legend. */
30
- export declare function ChartLegend({ items, placement, swatch, inset, extent, hoveredKey, onItemEnter, onItemLeave, }: ChartLegendProps): ReactNode;
40
+ export declare function ChartLegend({ items, placement, swatch, inset, extent, isFixedExtent, hasFilterSlot, filter, getEntryState, isEntryPinned, onItemClick, onItemEnter, onItemLeave, }: ChartLegendProps): ReactNode;