@seekalfred/primitives 0.6.0 → 0.7.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.
- package/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
4
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
5
|
+
import { SMOOTH, live, retarget } from "../lib/spring-tween.js";
|
|
6
|
+
import { RollingNumber } from "../lib/field-message.js";
|
|
7
|
+
import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/split-bar/split-bar.tsx
|
|
10
|
+
/** The gap between segments, the padding of an inside label, the gap between outside labels, and the lane under the bar, in pixels. */
|
|
11
|
+
const GAP = 2;
|
|
12
|
+
const OUT_GAP = 10;
|
|
13
|
+
const LANE = 34;
|
|
14
|
+
/** Where an outside label starts in the lane. */
|
|
15
|
+
const LABEL_TOP = 12;
|
|
16
|
+
/** How much a highlighted segment widens, as a share of the bar. */
|
|
17
|
+
const WIDEN = .045;
|
|
18
|
+
const NEUTRALS = [38, 24];
|
|
19
|
+
const colorOf = (segment, index) => segment.color ?? (index < 4 ? `var(--color-chart-${index + 1})` : `color-mix(in oklab, var(--color-foreground) ${NEUTRALS[(index - 4) % NEUTRALS.length]}%, var(--color-surface))`);
|
|
20
|
+
const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
|
|
21
|
+
/** Text widths from canvas metrics, cached per font, so fitting never reads layout while animating. */
|
|
22
|
+
const widths = /* @__PURE__ */ new Map();
|
|
23
|
+
function measure(text, font) {
|
|
24
|
+
const key = `${font}|${text}`;
|
|
25
|
+
const known = widths.get(key);
|
|
26
|
+
if (known !== void 0) return known;
|
|
27
|
+
let width = text.length * 7;
|
|
28
|
+
if (typeof document !== "undefined") {
|
|
29
|
+
const context = document.createElement("canvas").getContext("2d");
|
|
30
|
+
if (context) {
|
|
31
|
+
context.font = font;
|
|
32
|
+
width = context.measureText(text).width;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
widths.set(key, width);
|
|
36
|
+
return width;
|
|
37
|
+
}
|
|
38
|
+
/** Where every segment's edges go: shares of the visible total, the highlighted one widened and the rest giving way in proportion; each label's fit; and the outside labels pushed apart. */
|
|
39
|
+
function layoutBar(items, shares, off, width, font, activeId) {
|
|
40
|
+
if (!width) return [];
|
|
41
|
+
const percentOf = (index) => `${(Math.round(shares[index] * 1e3) / 10).toFixed(1)}%`;
|
|
42
|
+
const need = (index) => 20 + measure(items[index].label, font) + 10 + measure(percentOf(index), font);
|
|
43
|
+
const base = shares.map((share) => share * width);
|
|
44
|
+
const at = items.findIndex((item) => item.id === activeId);
|
|
45
|
+
let sized = base;
|
|
46
|
+
if (at >= 0) {
|
|
47
|
+
const room = width - base[at];
|
|
48
|
+
const extra = clamp(Math.max(WIDEN * width, need(at) - base[at] + GAP), 0, room * .8);
|
|
49
|
+
const scale = room > 0 ? (room - extra) / room : 1;
|
|
50
|
+
sized = base.map((w, index) => index === at ? w + extra : w * scale);
|
|
51
|
+
}
|
|
52
|
+
const rows = items.map((item, index) => {
|
|
53
|
+
const start = sized.slice(0, index).reduce((sum, w) => sum + w, 0);
|
|
54
|
+
const end = start + sized[index];
|
|
55
|
+
const inner = sized[index] - GAP;
|
|
56
|
+
const hiddenItem = off.has(item.id);
|
|
57
|
+
const fit = hiddenItem || inner <= 0 ? "out" : inner >= need(index) ? "full" : inner >= measure(percentOf(index), font) + 20 - 4 ? "pct" : "out";
|
|
58
|
+
return {
|
|
59
|
+
id: item.id,
|
|
60
|
+
start,
|
|
61
|
+
end,
|
|
62
|
+
fit,
|
|
63
|
+
lx: (start + end) / 2,
|
|
64
|
+
outW: measure(item.label, font) + measure(percentOf(index), font) + 8,
|
|
65
|
+
hiddenItem
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
const outs = rows.filter((row) => row.fit === "out" && !row.hiddenItem);
|
|
69
|
+
const lefts = outs.map((row) => clamp(row.lx - row.outW / 2, 0, Math.max(0, width - row.outW)));
|
|
70
|
+
for (let i = 1; i < outs.length; i++) lefts[i] = Math.max(lefts[i], lefts[i - 1] + outs[i - 1].outW + OUT_GAP);
|
|
71
|
+
if (outs.length && lefts[outs.length - 1] + outs[outs.length - 1].outW > width) {
|
|
72
|
+
lefts[outs.length - 1] = Math.max(0, width - outs[outs.length - 1].outW);
|
|
73
|
+
for (let i = outs.length - 2; i >= 0; i--) lefts[i] = Math.min(lefts[i], lefts[i + 1] - outs[i].outW - OUT_GAP);
|
|
74
|
+
}
|
|
75
|
+
return rows.map(({ id, start, end, fit, lx, outW }) => {
|
|
76
|
+
const out = outs.findIndex((row) => row.id === id);
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
start,
|
|
80
|
+
end,
|
|
81
|
+
fit,
|
|
82
|
+
lx: out >= 0 ? lefts[out] + outW / 2 : lx,
|
|
83
|
+
outW
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** A number whose digits roll to the new figure, turning the way the value moved. Hidden from assistive technology: the readout and table carry the same numbers as text. */
|
|
88
|
+
function Roll({ text, value }) {
|
|
89
|
+
const [trail, setTrail] = useState({
|
|
90
|
+
value,
|
|
91
|
+
direction: 1
|
|
92
|
+
});
|
|
93
|
+
if (trail.value !== value) setTrail({
|
|
94
|
+
value,
|
|
95
|
+
direction: value < trail.value ? -1 : 1
|
|
96
|
+
});
|
|
97
|
+
return /* @__PURE__ */ jsx(RollingNumber, {
|
|
98
|
+
word: text,
|
|
99
|
+
direction: trail.direction
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const SplitBar = forwardRef(function SplitBar({ segments, title, description, format, unit, totalLabel = "Total", hidden, defaultHidden, onHiddenChange, legend = true, locale = "en-US", label, className, style }, ref) {
|
|
103
|
+
const bar = useRef(null);
|
|
104
|
+
const root = useRef(null);
|
|
105
|
+
const titleId = useId();
|
|
106
|
+
const items = useMemo(() => segments.filter((segment) => segment.value > 0), [segments]);
|
|
107
|
+
const [ownHidden, setOwnHidden] = useState([...defaultHidden ?? []]);
|
|
108
|
+
const off = new Set((hidden ?? ownHidden).filter((id) => items.some((item) => item.id === id)));
|
|
109
|
+
const visible = items.filter((item) => !off.has(item.id));
|
|
110
|
+
const total = visible.reduce((sum, item) => sum + item.value, 0);
|
|
111
|
+
const share = (item) => off.has(item.id) || !total ? 0 : item.value / total;
|
|
112
|
+
const pct = (item) => Math.round(share(item) * 1e3) / 10;
|
|
113
|
+
const pctText = (item) => `${pct(item).toFixed(1)}%`;
|
|
114
|
+
const numbers = useMemo(() => new Intl.NumberFormat(locale, format), [locale, format]);
|
|
115
|
+
const unitText = unit ? ` ${unit}` : "";
|
|
116
|
+
const valueText = (value) => `${numbers.format(value)}${unitText}`;
|
|
117
|
+
const [hot, setHot] = useState(null);
|
|
118
|
+
const activeId = hot && visible.some((item) => item.id === hot) ? hot : null;
|
|
119
|
+
const activeItem = items.find((item) => item.id === activeId) ?? null;
|
|
120
|
+
const [announcement, setAnnouncement] = useState("");
|
|
121
|
+
const [shaking, setShaking] = useState(null);
|
|
122
|
+
const toggle = (item) => {
|
|
123
|
+
const isOff = off.has(item.id);
|
|
124
|
+
if (!isOff && visible.length <= 1) {
|
|
125
|
+
setAnnouncement(`${item.label} has to stay on: at least one segment must show.`);
|
|
126
|
+
setShaking(item.id);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const next = isOff ? [...off].filter((id) => id !== item.id) : [...off, item.id];
|
|
130
|
+
if (hidden === void 0) setOwnHidden(next);
|
|
131
|
+
onHiddenChange?.(next);
|
|
132
|
+
const remaining = items.filter((other) => !next.includes(other.id));
|
|
133
|
+
setAnnouncement(`${item.label} switched ${isOff ? "on" : "off"}. ${remaining.length} of ${items.length} segments showing.`);
|
|
134
|
+
if (!isOff && hot === item.id) setHot(null);
|
|
135
|
+
};
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (!shaking) return;
|
|
138
|
+
const timer = window.setTimeout(() => setShaking(null), 800);
|
|
139
|
+
return () => window.clearTimeout(timer);
|
|
140
|
+
}, [shaking]);
|
|
141
|
+
const [width, setWidth] = useState(0);
|
|
142
|
+
useIsomorphicLayoutEffect(() => {
|
|
143
|
+
const node = bar.current;
|
|
144
|
+
if (!node || typeof ResizeObserver === "undefined") return;
|
|
145
|
+
const observer = new ResizeObserver(() => setWidth(node.clientWidth));
|
|
146
|
+
observer.observe(node);
|
|
147
|
+
setWidth(node.clientWidth);
|
|
148
|
+
return () => observer.disconnect();
|
|
149
|
+
}, []);
|
|
150
|
+
const ready = width > 0;
|
|
151
|
+
const [font, setFont] = useState("500 12px sans-serif");
|
|
152
|
+
const [, setFontEpoch] = useState(0);
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
let current = true;
|
|
155
|
+
document.fonts?.ready.then(() => {
|
|
156
|
+
if (!current) return;
|
|
157
|
+
widths.clear();
|
|
158
|
+
setFontEpoch((epoch) => epoch + 1);
|
|
159
|
+
});
|
|
160
|
+
return () => {
|
|
161
|
+
current = false;
|
|
162
|
+
};
|
|
163
|
+
}, []);
|
|
164
|
+
useIsomorphicLayoutEffect(() => {
|
|
165
|
+
if (bar.current) setFont(getComputedStyle(bar.current).font);
|
|
166
|
+
}, [ready]);
|
|
167
|
+
const plan = layoutBar(items, items.map(share), off, width, font, activeId);
|
|
168
|
+
const planOf = (id) => plan.find((row) => row.id === id);
|
|
169
|
+
const lane = plan.some((row) => row.fit === "out" && !off.has(row.id));
|
|
170
|
+
const edges = useRef(/* @__PURE__ */ new Map());
|
|
171
|
+
const paint = useCallback(() => {
|
|
172
|
+
const node = root.current;
|
|
173
|
+
if (!node) return;
|
|
174
|
+
for (const el of node.querySelectorAll("[data-seg]")) {
|
|
175
|
+
const at = edges.current.get(el.dataset.seg);
|
|
176
|
+
if (!at) continue;
|
|
177
|
+
el.style.left = `${at.s.value + GAP / 2}px`;
|
|
178
|
+
el.style.width = `${Math.max(0, at.e.value - at.s.value - GAP)}px`;
|
|
179
|
+
}
|
|
180
|
+
for (const el of node.querySelectorAll("[data-out]")) {
|
|
181
|
+
const at = edges.current.get(el.dataset.out);
|
|
182
|
+
if (at) el.style.translate = `${at.lx.value - Number(el.dataset.w) / 2}px 0`;
|
|
183
|
+
}
|
|
184
|
+
for (const el of node.querySelectorAll("[data-lead]")) {
|
|
185
|
+
const at = edges.current.get(el.dataset.lead);
|
|
186
|
+
if (!at) continue;
|
|
187
|
+
const cx = (at.s.value + at.e.value) / 2;
|
|
188
|
+
el.setAttribute("d", `M${cx.toFixed(1)},0C${cx.toFixed(1)},12 ${at.lx.value.toFixed(1)},7 ${at.lx.value.toFixed(1)},10`);
|
|
189
|
+
}
|
|
190
|
+
}, []);
|
|
191
|
+
useIsomorphicLayoutEffect(paint);
|
|
192
|
+
const planKey = plan.map((row) => `${row.id}:${Math.round(row.start)}:${Math.round(row.end)}:${Math.round(row.lx)}`).join("|");
|
|
193
|
+
useIsomorphicLayoutEffect(() => {
|
|
194
|
+
for (const key of [...edges.current.keys()]) {
|
|
195
|
+
if (items.some((item) => item.id === key)) continue;
|
|
196
|
+
const gone = edges.current.get(key);
|
|
197
|
+
gone.s.stop();
|
|
198
|
+
gone.e.stop();
|
|
199
|
+
gone.lx.stop();
|
|
200
|
+
edges.current.delete(key);
|
|
201
|
+
}
|
|
202
|
+
for (const row of plan) {
|
|
203
|
+
const at = edges.current.get(row.id);
|
|
204
|
+
if (!at) {
|
|
205
|
+
edges.current.set(row.id, {
|
|
206
|
+
s: live(row.start),
|
|
207
|
+
e: live(row.end),
|
|
208
|
+
lx: live(row.lx)
|
|
209
|
+
});
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
retarget(at.s, row.start, SMOOTH, paint);
|
|
213
|
+
retarget(at.e, row.end, SMOOTH, paint);
|
|
214
|
+
retarget(at.lx, row.lx, SMOOTH, paint);
|
|
215
|
+
}
|
|
216
|
+
paint();
|
|
217
|
+
}, [planKey, paint]);
|
|
218
|
+
useEffect(() => () => {
|
|
219
|
+
for (const at of edges.current.values()) {
|
|
220
|
+
at.s.stop();
|
|
221
|
+
at.e.stop();
|
|
222
|
+
at.lx.stop();
|
|
223
|
+
}
|
|
224
|
+
}, []);
|
|
225
|
+
const idAt = (clientX) => {
|
|
226
|
+
const rect = bar.current?.getBoundingClientRect();
|
|
227
|
+
if (!rect?.width || !total) return null;
|
|
228
|
+
let cursor = 0;
|
|
229
|
+
const x = clientX - rect.left;
|
|
230
|
+
for (const item of visible) {
|
|
231
|
+
cursor += item.value / total * rect.width;
|
|
232
|
+
if (x < cursor) return item.id;
|
|
233
|
+
}
|
|
234
|
+
return visible[visible.length - 1]?.id ?? null;
|
|
235
|
+
};
|
|
236
|
+
useEffect(() => {
|
|
237
|
+
if (!hot) return;
|
|
238
|
+
const away = (event) => {
|
|
239
|
+
if (event.pointerType !== "mouse" && !bar.current?.contains(event.target)) setHot(null);
|
|
240
|
+
};
|
|
241
|
+
document.addEventListener("pointerdown", away);
|
|
242
|
+
return () => document.removeEventListener("pointerdown", away);
|
|
243
|
+
}, [hot]);
|
|
244
|
+
const onKeyDown = (event) => {
|
|
245
|
+
const at = visible.findIndex((item) => item.id === activeId);
|
|
246
|
+
const go = (index) => {
|
|
247
|
+
event.preventDefault();
|
|
248
|
+
const next = visible[(index + visible.length) % visible.length];
|
|
249
|
+
if (!next) return;
|
|
250
|
+
setHot(next.id);
|
|
251
|
+
setAnnouncement(`${next.label}, ${pctText(next)}, ${valueText(next.value)}`);
|
|
252
|
+
};
|
|
253
|
+
if (event.key === "ArrowRight" || event.key === "ArrowDown") go(at + 1);
|
|
254
|
+
else if (event.key === "ArrowLeft" || event.key === "ArrowUp") go(at < 0 ? visible.length - 1 : at - 1);
|
|
255
|
+
else if (event.key === "Home") go(0);
|
|
256
|
+
else if (event.key === "End") go(visible.length - 1);
|
|
257
|
+
else if ((event.key === "Enter" || event.key === " ") && activeItem) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
toggle(activeItem);
|
|
260
|
+
} else if (event.key === "Escape" && hot) {
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
setHot(null);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const name = label ?? title ?? "Split bar";
|
|
266
|
+
const summary = total ? `${name}. ${totalLabel} ${valueText(total)}. ${visible.map((item) => `${item.label} ${pctText(item)}`).join(", ")}.` : `${name}. No data.`;
|
|
267
|
+
const readout = activeItem ? {
|
|
268
|
+
key: activeItem.id,
|
|
269
|
+
label: activeItem.label,
|
|
270
|
+
value: activeItem.value,
|
|
271
|
+
text: numbers.format(activeItem.value),
|
|
272
|
+
share: pctText(activeItem)
|
|
273
|
+
} : {
|
|
274
|
+
key: "__total",
|
|
275
|
+
label: totalLabel,
|
|
276
|
+
value: total,
|
|
277
|
+
text: numbers.format(total),
|
|
278
|
+
share: null
|
|
279
|
+
};
|
|
280
|
+
return /* @__PURE__ */ jsxs("figure", {
|
|
281
|
+
ref,
|
|
282
|
+
"data-slot": "split-bar",
|
|
283
|
+
"aria-label": name,
|
|
284
|
+
className: cn("split-bar box-border font-sans text-foreground", className),
|
|
285
|
+
style,
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ jsxs("div", {
|
|
288
|
+
className: "split-bar-head",
|
|
289
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
290
|
+
className: "min-w-0",
|
|
291
|
+
children: [title ? /* @__PURE__ */ jsx("p", {
|
|
292
|
+
id: titleId,
|
|
293
|
+
className: "m-0 text-base font-medium",
|
|
294
|
+
children: title
|
|
295
|
+
}) : null, description ? /* @__PURE__ */ jsx("p", {
|
|
296
|
+
className: "m-0 text-sm text-text-secondary",
|
|
297
|
+
children: description
|
|
298
|
+
}) : null]
|
|
299
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
300
|
+
"aria-hidden": "true",
|
|
301
|
+
"data-slot": "split-bar-readout",
|
|
302
|
+
className: "split-bar-readout",
|
|
303
|
+
children: /* @__PURE__ */ jsxs(SwapText, {
|
|
304
|
+
swapKey: readout.key,
|
|
305
|
+
motion: "rise",
|
|
306
|
+
children: [
|
|
307
|
+
/* @__PURE__ */ jsx("span", {
|
|
308
|
+
className: "text-text-secondary",
|
|
309
|
+
children: readout.label
|
|
310
|
+
}),
|
|
311
|
+
" ",
|
|
312
|
+
/* @__PURE__ */ jsx("span", {
|
|
313
|
+
className: "font-medium tabular-nums",
|
|
314
|
+
children: /* @__PURE__ */ jsx(Roll, {
|
|
315
|
+
text: readout.text,
|
|
316
|
+
value: readout.value
|
|
317
|
+
})
|
|
318
|
+
}),
|
|
319
|
+
unit ? /* @__PURE__ */ jsx("span", {
|
|
320
|
+
className: "text-text-secondary",
|
|
321
|
+
children: unitText
|
|
322
|
+
}) : null,
|
|
323
|
+
readout.share ? /* @__PURE__ */ jsx("span", {
|
|
324
|
+
className: "ms-2 tabular-nums text-text-secondary",
|
|
325
|
+
children: readout.share
|
|
326
|
+
}) : null
|
|
327
|
+
]
|
|
328
|
+
})
|
|
329
|
+
})]
|
|
330
|
+
}),
|
|
331
|
+
/* @__PURE__ */ jsxs("div", {
|
|
332
|
+
ref: root,
|
|
333
|
+
className: "relative min-w-0",
|
|
334
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
335
|
+
ref: bar,
|
|
336
|
+
"data-slot": "split-bar-bar",
|
|
337
|
+
"data-active": activeItem ? "" : void 0,
|
|
338
|
+
role: total ? "application" : void 0,
|
|
339
|
+
tabIndex: total ? 0 : void 0,
|
|
340
|
+
"aria-roledescription": total ? "split bar" : void 0,
|
|
341
|
+
"aria-label": total ? `${summary} Arrow keys highlight a segment, Enter switches it off.` : void 0,
|
|
342
|
+
className: "split-bar-bar",
|
|
343
|
+
onPointerMove: (event) => {
|
|
344
|
+
if (event.pointerType === "mouse") setHot(idAt(event.clientX));
|
|
345
|
+
},
|
|
346
|
+
onPointerDown: (event) => {
|
|
347
|
+
if (event.pointerType !== "mouse") setHot((now) => now === idAt(event.clientX) ? null : idAt(event.clientX));
|
|
348
|
+
},
|
|
349
|
+
onPointerLeave: (event) => {
|
|
350
|
+
if (event.pointerType === "mouse") setHot(null);
|
|
351
|
+
},
|
|
352
|
+
onKeyDown,
|
|
353
|
+
onBlur: () => setHot(null),
|
|
354
|
+
children: [items.map((item, index) => {
|
|
355
|
+
const row = planOf(item.id);
|
|
356
|
+
return /* @__PURE__ */ jsx("div", {
|
|
357
|
+
"data-seg": item.id,
|
|
358
|
+
"data-fit": row?.fit,
|
|
359
|
+
"data-active": activeId === item.id || void 0,
|
|
360
|
+
"data-dim": activeId !== null && activeId !== item.id || void 0,
|
|
361
|
+
"data-off": off.has(item.id) || void 0,
|
|
362
|
+
"aria-hidden": "true",
|
|
363
|
+
className: "split-bar-seg",
|
|
364
|
+
style: { "--series": colorOf(item, index) },
|
|
365
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
366
|
+
className: "split-bar-seg-label",
|
|
367
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
368
|
+
className: "split-bar-name",
|
|
369
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
370
|
+
className: "truncate",
|
|
371
|
+
children: item.label
|
|
372
|
+
})
|
|
373
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
374
|
+
className: "tabular-nums",
|
|
375
|
+
children: /* @__PURE__ */ jsx(Roll, {
|
|
376
|
+
text: pctText(item),
|
|
377
|
+
value: pct(item)
|
|
378
|
+
})
|
|
379
|
+
})]
|
|
380
|
+
})
|
|
381
|
+
}, item.id);
|
|
382
|
+
}), total ? null : /* @__PURE__ */ jsx("p", {
|
|
383
|
+
className: "absolute inset-0 m-0 grid place-items-center text-sm text-muted-foreground",
|
|
384
|
+
children: "No data"
|
|
385
|
+
})]
|
|
386
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
387
|
+
"data-open": lane || void 0,
|
|
388
|
+
"aria-hidden": "true",
|
|
389
|
+
className: "split-bar-lane",
|
|
390
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
391
|
+
className: "relative min-h-0 overflow-hidden",
|
|
392
|
+
style: { height: LANE },
|
|
393
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
394
|
+
width: width || void 0,
|
|
395
|
+
height: LANE,
|
|
396
|
+
className: "absolute inset-x-0 top-0 block overflow-visible",
|
|
397
|
+
focusable: "false",
|
|
398
|
+
children: items.map((item) => /* @__PURE__ */ jsx("path", {
|
|
399
|
+
"data-lead": item.id,
|
|
400
|
+
"data-on": planOf(item.id)?.fit === "out" && !off.has(item.id) || void 0,
|
|
401
|
+
className: "split-bar-leader"
|
|
402
|
+
}, item.id))
|
|
403
|
+
}), items.map((item) => {
|
|
404
|
+
const row = planOf(item.id);
|
|
405
|
+
const on = row?.fit === "out" && !off.has(item.id);
|
|
406
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
407
|
+
"data-out": item.id,
|
|
408
|
+
"data-w": row?.outW ?? 0,
|
|
409
|
+
"data-on": on || void 0,
|
|
410
|
+
className: "split-bar-out",
|
|
411
|
+
style: {
|
|
412
|
+
width: row?.outW,
|
|
413
|
+
top: LABEL_TOP
|
|
414
|
+
},
|
|
415
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
416
|
+
className: "truncate",
|
|
417
|
+
children: item.label
|
|
418
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
419
|
+
className: "tabular-nums text-foreground",
|
|
420
|
+
children: pctText(item)
|
|
421
|
+
})]
|
|
422
|
+
}, item.id);
|
|
423
|
+
})]
|
|
424
|
+
})
|
|
425
|
+
})]
|
|
426
|
+
}),
|
|
427
|
+
legend && items.length ? /* @__PURE__ */ jsx("div", {
|
|
428
|
+
className: "-mx-2 flex min-w-0 flex-wrap gap-x-1 gap-y-0.5",
|
|
429
|
+
onPointerLeave: () => setHot(null),
|
|
430
|
+
children: items.map((item, index) => {
|
|
431
|
+
const isOff = off.has(item.id);
|
|
432
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
433
|
+
type: "button",
|
|
434
|
+
"data-slot": "split-bar-toggle",
|
|
435
|
+
"data-strain": shaking === item.id ? "" : void 0,
|
|
436
|
+
"aria-pressed": !isOff,
|
|
437
|
+
"aria-label": isOff ? `${item.label}, hidden` : `${item.label}, ${pctText(item)}`,
|
|
438
|
+
onClick: () => toggle(item),
|
|
439
|
+
onPointerEnter: (event) => {
|
|
440
|
+
if (event.pointerType === "mouse" && !isOff) setHot(item.id);
|
|
441
|
+
},
|
|
442
|
+
onFocus: (event) => {
|
|
443
|
+
if (event.currentTarget.matches(":focus-visible") && !isOff) setHot(item.id);
|
|
444
|
+
},
|
|
445
|
+
onBlur: () => setHot(null),
|
|
446
|
+
style: {
|
|
447
|
+
"--series": colorOf(item, index),
|
|
448
|
+
"--strain-x": "6px"
|
|
449
|
+
},
|
|
450
|
+
className: "chart-legend-toggle data-[strain]:motion-safe:animate-field-strain",
|
|
451
|
+
children: [
|
|
452
|
+
/* @__PURE__ */ jsx("span", {
|
|
453
|
+
"aria-hidden": "true",
|
|
454
|
+
className: "split-bar-swatch"
|
|
455
|
+
}),
|
|
456
|
+
/* @__PURE__ */ jsx("span", {
|
|
457
|
+
className: "chart-toggle-label",
|
|
458
|
+
children: item.label
|
|
459
|
+
}),
|
|
460
|
+
/* @__PURE__ */ jsx("span", {
|
|
461
|
+
"aria-hidden": "true",
|
|
462
|
+
className: "text-xs tabular-nums text-muted-foreground",
|
|
463
|
+
children: isOff ? "Off" : pctText(item)
|
|
464
|
+
})
|
|
465
|
+
]
|
|
466
|
+
}, item.id);
|
|
467
|
+
})
|
|
468
|
+
}) : null,
|
|
469
|
+
/* @__PURE__ */ jsx("p", {
|
|
470
|
+
className: "sr-only",
|
|
471
|
+
"aria-live": "polite",
|
|
472
|
+
"aria-atomic": "true",
|
|
473
|
+
children: announcement
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ jsx("p", {
|
|
476
|
+
className: "sr-only",
|
|
477
|
+
children: summary
|
|
478
|
+
}),
|
|
479
|
+
total ? /* @__PURE__ */ jsx("div", {
|
|
480
|
+
className: "sr-only",
|
|
481
|
+
children: /* @__PURE__ */ jsxs("table", { children: [
|
|
482
|
+
/* @__PURE__ */ jsx("caption", { children: name }),
|
|
483
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
484
|
+
/* @__PURE__ */ jsx("th", {
|
|
485
|
+
scope: "col",
|
|
486
|
+
children: "Segment"
|
|
487
|
+
}),
|
|
488
|
+
/* @__PURE__ */ jsx("th", {
|
|
489
|
+
scope: "col",
|
|
490
|
+
children: "Value"
|
|
491
|
+
}),
|
|
492
|
+
/* @__PURE__ */ jsx("th", {
|
|
493
|
+
scope: "col",
|
|
494
|
+
children: "Share"
|
|
495
|
+
})
|
|
496
|
+
] }) }),
|
|
497
|
+
/* @__PURE__ */ jsx("tbody", { children: items.map((item) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
498
|
+
/* @__PURE__ */ jsx("th", {
|
|
499
|
+
scope: "row",
|
|
500
|
+
children: item.label
|
|
501
|
+
}),
|
|
502
|
+
/* @__PURE__ */ jsx("td", { children: valueText(item.value) }),
|
|
503
|
+
/* @__PURE__ */ jsx("td", { children: off.has(item.id) ? "Off" : pctText(item) })
|
|
504
|
+
] }, item.id)) })
|
|
505
|
+
] })
|
|
506
|
+
}) : null
|
|
507
|
+
]
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
//#endregion
|
|
511
|
+
export { SplitBar };
|
|
512
|
+
|
|
513
|
+
//# sourceMappingURL=split-bar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-bar.js","names":[],"sources":["../../src/split-bar/split-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { RollingNumber } from \"../lib/field-message.js\";\nimport { useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { live, retarget, SMOOTH, type Live } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * SplitBar: one 100% bar split into shares, for traffic sources, budgets,\n * storage or votes. Resting on a segment (or arrowing onto it) widens it a little\n * (a small one widens enough to show its own label) while the others give way\n * proportionally and lose their colour, and the readout above names it: share,\n * value and unit.\n *\n * A label sits inside its segment when it fits, as name and percent or percent\n * alone, and otherwise moves to a lane under the bar on a soft leader line,\n * pushed apart so labels never overlap. The legend switches segments off and on:\n * an off segment collapses in place and the rest reflow to fill the bar, their\n * percentages rolling digit by digit to the new shares. New data does the same,\n * so switching a period is one continuous reflow. The last visible segment\n * cannot be switched off: its legend item shakes and the reason is announced.\n *\n * Segment edges, leader lines and outside labels are written straight to the DOM\n * by one small spring tween (a new target mid flight retargets from where the\n * edges are); name, fit and colour changes are CSS transitions.\n */\nexport interface SplitSegment {\n /** Stable identity. Segments are matched by id when the data changes. */\n id: string;\n label: string;\n /** Shares are each value over the total of the visible segments; zero and negative values are left out. */\n value: number;\n /** Any CSS colour. Defaults to chart-1 to chart-4 in order, then two neutrals meant for catch-all shares. */\n color?: string;\n}\n\nexport interface SplitBarProps {\n /** The shares in display order. */\n segments: readonly SplitSegment[];\n /** Heading above the bar and the accessible name unless `label` is set. */\n title?: string;\n /** One line under the title. */\n description?: string;\n /** Number format for values in the readout, such as currency for a budget. Shares always read as percentages with one decimal. */\n format?: Intl.NumberFormatOptions;\n /** Word after values in the readout, such as \"sessions\". */\n unit?: string;\n /** Label of the readout when nothing is highlighted. @default \"Total\" */\n totalLabel?: string;\n /** Ids of segments that are switched off (controlled). */\n hidden?: readonly string[];\n /** Ids switched off at first (uncontrolled). */\n defaultHidden?: readonly string[];\n /** Called with the new list when the legend or the keyboard switches a segment. */\n onHiddenChange?: (hidden: string[]) => void;\n /** The legend. Each item toggles its segment and highlights it on hover. @default true */\n legend?: boolean;\n /** Formatting locale. Fixed by default so server and client render the same digits. @default \"en-US\" */\n locale?: string;\n /** Accessible name. Defaults to the title. */\n label?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n style?: CSSProperties;\n}\n\ntype Fit = \"full\" | \"pct\" | \"out\";\ntype Edges = { s: Live; e: Live; lx: Live };\ntype Plan = { id: string; start: number; end: number; fit: Fit; lx: number; outW: number };\n\n/** The gap between segments, the padding of an inside label, the gap between outside labels, and the lane under the bar, in pixels. */\nconst GAP = 2;\nconst PAD = 10;\nconst OUT_GAP = 10;\nconst LANE = 34;\n/** Where an outside label starts in the lane. */\nconst LABEL_TOP = 12;\n/** How much a highlighted segment widens, as a share of the bar. */\nconst WIDEN = 0.045;\nconst NEUTRALS = [38, 24];\nconst colorOf = (segment: SplitSegment, index: number) =>\n segment.color ??\n (index < 4\n ? `var(--color-chart-${index + 1})`\n : `color-mix(in oklab, var(--color-foreground) ${NEUTRALS[(index - 4) % NEUTRALS.length]}%, var(--color-surface))`);\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\n\n/** Text widths from canvas metrics, cached per font, so fitting never reads layout while animating. */\nconst widths = new Map<string, number>();\nfunction measure(text: string, font: string) {\n const key = `${font}|${text}`;\n const known = widths.get(key);\n if (known !== undefined) return known;\n let width = text.length * 7;\n if (typeof document !== \"undefined\") {\n const context = document.createElement(\"canvas\").getContext(\"2d\");\n if (context) {\n context.font = font;\n width = context.measureText(text).width;\n }\n }\n widths.set(key, width);\n return width;\n}\n\n/** Where every segment's edges go: shares of the visible total, the highlighted one widened and the rest giving way in proportion; each label's fit; and the outside labels pushed apart. */\nfunction layoutBar(items: SplitSegment[], shares: number[], off: Set<string>, width: number, font: string, activeId: string | null): Plan[] {\n if (!width) return [];\n const percentOf = (index: number) => `${(Math.round(shares[index]! * 1000) / 10).toFixed(1)}%`;\n const need = (index: number) => 2 * PAD + measure(items[index]!.label, font) + 10 + measure(percentOf(index), font);\n const base = shares.map((share) => share * width);\n const at = items.findIndex((item) => item.id === activeId);\n let sized = base;\n if (at >= 0) {\n const room = width - base[at]!;\n const extra = clamp(Math.max(WIDEN * width, need(at) - base[at]! + GAP), 0, room * 0.8);\n const scale = room > 0 ? (room - extra) / room : 1;\n sized = base.map((w, index) => (index === at ? w + extra : w * scale));\n }\n const rows = items.map((item, index) => {\n const start = sized.slice(0, index).reduce((sum, w) => sum + w, 0);\n const end = start + sized[index]!;\n const inner = sized[index]! - GAP;\n const hiddenItem = off.has(item.id);\n const fit: Fit = hiddenItem || inner <= 0 ? \"out\" : inner >= need(index) ? \"full\" : inner >= measure(percentOf(index), font) + 2 * PAD - 4 ? \"pct\" : \"out\";\n return { id: item.id, start, end, fit, lx: (start + end) / 2, outW: measure(item.label, font) + measure(percentOf(index), font) + 8, hiddenItem };\n });\n // Outside labels sit under their segment and are pushed apart, never past the bar's edges.\n const outs = rows.filter((row) => row.fit === \"out\" && !row.hiddenItem);\n const lefts = outs.map((row) => clamp(row.lx - row.outW / 2, 0, Math.max(0, width - row.outW)));\n for (let i = 1; i < outs.length; i++) lefts[i] = Math.max(lefts[i]!, lefts[i - 1]! + outs[i - 1]!.outW + OUT_GAP);\n if (outs.length && lefts[outs.length - 1]! + outs[outs.length - 1]!.outW > width) {\n lefts[outs.length - 1] = Math.max(0, width - outs[outs.length - 1]!.outW);\n for (let i = outs.length - 2; i >= 0; i--) lefts[i] = Math.min(lefts[i]!, lefts[i + 1]! - outs[i]!.outW - OUT_GAP);\n }\n return rows.map(({ id, start, end, fit, lx, outW }) => {\n const out = outs.findIndex((row) => row.id === id);\n return { id, start, end, fit, lx: out >= 0 ? lefts[out]! + outW / 2 : lx, outW };\n });\n}\n\n/** A number whose digits roll to the new figure, turning the way the value moved. Hidden from assistive technology: the readout and table carry the same numbers as text. */\nfunction Roll({ text, value }: { text: string; value: number }) {\n const [trail, setTrail] = useState({ value, direction: 1 as 1 | -1 });\n if (trail.value !== value) setTrail({ value, direction: value < trail.value ? -1 : 1 });\n return <RollingNumber word={text} direction={trail.direction} />;\n}\n\nexport const SplitBar = forwardRef<HTMLElement, SplitBarProps>(function SplitBar(\n {\n segments,\n title,\n description,\n format,\n unit,\n totalLabel = \"Total\",\n hidden,\n defaultHidden,\n onHiddenChange,\n legend = true,\n locale = \"en-US\",\n label,\n className,\n style,\n },\n ref,\n) {\n const bar = useRef<HTMLDivElement>(null);\n const root = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const items = useMemo(() => segments.filter((segment) => segment.value > 0), [segments]);\n\n const [ownHidden, setOwnHidden] = useState<string[]>([...(defaultHidden ?? [])]);\n const off = new Set((hidden ?? ownHidden).filter((id) => items.some((item) => item.id === id)));\n const visible = items.filter((item) => !off.has(item.id));\n const total = visible.reduce((sum, item) => sum + item.value, 0);\n const share = (item: SplitSegment) => (off.has(item.id) || !total ? 0 : item.value / total);\n const pct = (item: SplitSegment) => Math.round(share(item) * 1000) / 10;\n const pctText = (item: SplitSegment) => `${pct(item).toFixed(1)}%`;\n const numbers = useMemo(() => new Intl.NumberFormat(locale, format), [locale, format]);\n const unitText = unit ? ` ${unit}` : \"\";\n const valueText = (value: number) => `${numbers.format(value)}${unitText}`;\n\n const [hot, setHot] = useState<string | null>(null);\n const activeId = hot && visible.some((item) => item.id === hot) ? hot : null;\n const activeItem = items.find((item) => item.id === activeId) ?? null;\n const [announcement, setAnnouncement] = useState(\"\");\n const [shaking, setShaking] = useState<string | null>(null);\n\n const toggle = (item: SplitSegment) => {\n const isOff = off.has(item.id);\n if (!isOff && visible.length <= 1) {\n setAnnouncement(`${item.label} has to stay on: at least one segment must show.`);\n setShaking(item.id);\n return;\n }\n const next = isOff ? [...off].filter((id) => id !== item.id) : [...off, item.id];\n if (hidden === undefined) setOwnHidden(next);\n onHiddenChange?.(next);\n const remaining = items.filter((other) => !next.includes(other.id));\n setAnnouncement(`${item.label} switched ${isOff ? \"on\" : \"off\"}. ${remaining.length} of ${items.length} segments showing.`);\n if (!isOff && hot === item.id) setHot(null);\n };\n useEffect(() => {\n if (!shaking) return;\n const timer = window.setTimeout(() => setShaking(null), 800);\n return () => window.clearTimeout(timer);\n }, [shaking]);\n\n // The bar draws in its own pixels: measure its width and keep it.\n const [width, setWidth] = useState(0);\n useIsomorphicLayoutEffect(() => {\n const node = bar.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => setWidth(node.clientWidth));\n observer.observe(node);\n setWidth(node.clientWidth);\n return () => observer.disconnect();\n }, []);\n const ready = width > 0;\n const [font, setFont] = useState(\"500 12px sans-serif\");\n // Text measured before the web font arrives is narrower than it will be: measure again once fonts are ready.\n const [, setFontEpoch] = useState(0);\n useEffect(() => {\n let current = true;\n void document.fonts?.ready.then(() => {\n if (!current) return;\n widths.clear();\n setFontEpoch((epoch) => epoch + 1);\n });\n return () => {\n current = false;\n };\n }, []);\n useIsomorphicLayoutEffect(() => {\n if (bar.current) setFont(getComputedStyle(bar.current).font);\n }, [ready]);\n\n const plan = layoutBar(items, items.map(share), off, width, font, activeId);\n const planOf = (id: string) => plan.find((row) => row.id === id);\n const lane = plan.some((row) => row.fit === \"out\" && !off.has(row.id));\n\n // The edges live outside React; one writer puts them on the elements.\n const edges = useRef(new Map<string, Edges>());\n const paint = useCallback(() => {\n const node = root.current;\n if (!node) return;\n for (const el of node.querySelectorAll<HTMLElement>(\"[data-seg]\")) {\n const at = edges.current.get(el.dataset.seg!);\n if (!at) continue;\n el.style.left = `${at.s.value + GAP / 2}px`;\n el.style.width = `${Math.max(0, at.e.value - at.s.value - GAP)}px`;\n }\n for (const el of node.querySelectorAll<HTMLElement>(\"[data-out]\")) {\n const at = edges.current.get(el.dataset.out!);\n if (at) el.style.translate = `${at.lx.value - Number(el.dataset.w) / 2}px 0`;\n }\n for (const el of node.querySelectorAll<SVGPathElement>(\"[data-lead]\")) {\n const at = edges.current.get(el.dataset.lead!);\n if (!at) continue;\n const cx = (at.s.value + at.e.value) / 2;\n el.setAttribute(\"d\", `M${cx.toFixed(1)},0C${cx.toFixed(1)},12 ${at.lx.value.toFixed(1)},7 ${at.lx.value.toFixed(1)},${LABEL_TOP - 2}`);\n }\n }, []);\n useIsomorphicLayoutEffect(paint);\n const planKey = plan.map((row) => `${row.id}:${Math.round(row.start)}:${Math.round(row.end)}:${Math.round(row.lx)}`).join(\"|\");\n useIsomorphicLayoutEffect(() => {\n for (const key of [...edges.current.keys()]) {\n if (items.some((item) => item.id === key)) continue;\n const gone = edges.current.get(key)!;\n gone.s.stop();\n gone.e.stop();\n gone.lx.stop();\n edges.current.delete(key);\n }\n for (const row of plan) {\n const at = edges.current.get(row.id);\n if (!at) {\n edges.current.set(row.id, { s: live(row.start), e: live(row.end), lx: live(row.lx) });\n continue;\n }\n retarget(at.s, row.start, SMOOTH, paint);\n retarget(at.e, row.end, SMOOTH, paint);\n retarget(at.lx, row.lx, SMOOTH, paint);\n }\n paint();\n // The key stands for the plan.\n \n }, [planKey, paint]);\n useEffect(\n () => () => {\n for (const at of edges.current.values()) {\n at.s.stop();\n at.e.stop();\n at.lx.stop();\n }\n },\n [],\n );\n\n // The segment under the pointer, found on the unwidened edges so the highlight never flickers at a boundary.\n const idAt = (clientX: number) => {\n const rect = bar.current?.getBoundingClientRect();\n if (!rect?.width || !total) return null;\n let cursor = 0;\n const x = clientX - rect.left;\n for (const item of visible) {\n cursor += (item.value / total) * rect.width;\n if (x < cursor) return item.id;\n }\n return visible[visible.length - 1]?.id ?? null;\n };\n useEffect(() => {\n if (!hot) return;\n const away = (event: globalThis.PointerEvent) => {\n if (event.pointerType !== \"mouse\" && !bar.current?.contains(event.target as Node)) setHot(null);\n };\n document.addEventListener(\"pointerdown\", away);\n return () => document.removeEventListener(\"pointerdown\", away);\n }, [hot]);\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const at = visible.findIndex((item) => item.id === activeId);\n const go = (index: number) => {\n event.preventDefault();\n const next = visible[(index + visible.length) % visible.length];\n if (!next) return;\n setHot(next.id);\n setAnnouncement(`${next.label}, ${pctText(next)}, ${valueText(next.value)}`);\n };\n if (event.key === \"ArrowRight\" || event.key === \"ArrowDown\") go(at + 1);\n else if (event.key === \"ArrowLeft\" || event.key === \"ArrowUp\") go(at < 0 ? visible.length - 1 : at - 1);\n else if (event.key === \"Home\") go(0);\n else if (event.key === \"End\") go(visible.length - 1);\n else if ((event.key === \"Enter\" || event.key === \" \") && activeItem) {\n event.preventDefault();\n toggle(activeItem);\n } else if (event.key === \"Escape\" && hot) {\n event.preventDefault();\n setHot(null);\n }\n };\n\n const name = label ?? title ?? \"Split bar\";\n const summary = total\n ? `${name}. ${totalLabel} ${valueText(total)}. ${visible.map((item) => `${item.label} ${pctText(item)}`).join(\", \")}.`\n : `${name}. No data.`;\n const readout = activeItem\n ? { key: activeItem.id, label: activeItem.label, value: activeItem.value, text: numbers.format(activeItem.value), share: pctText(activeItem) }\n : { key: \"__total\", label: totalLabel, value: total, text: numbers.format(total), share: null };\n\n return (\n <figure\n ref={ref}\n data-slot=\"split-bar\"\n aria-label={name}\n className={cn(\"split-bar box-border font-sans text-foreground\", className)}\n style={style}\n >\n <div className=\"split-bar-head\">\n <div className=\"min-w-0\">\n {title ? (\n <p id={titleId} className=\"m-0 text-base font-medium\">\n {title}\n </p>\n ) : null}\n {description ? <p className=\"m-0 text-sm text-text-secondary\">{description}</p> : null}\n </div>\n <p aria-hidden=\"true\" data-slot=\"split-bar-readout\" className=\"split-bar-readout\">\n <SwapText swapKey={readout.key} motion=\"rise\">\n <span className=\"text-text-secondary\">{readout.label}</span>{\" \"}\n <span className=\"font-medium tabular-nums\">\n <Roll text={readout.text} value={readout.value} />\n </span>\n {unit ? <span className=\"text-text-secondary\">{unitText}</span> : null}\n {readout.share ? <span className=\"ms-2 tabular-nums text-text-secondary\">{readout.share}</span> : null}\n </SwapText>\n </p>\n </div>\n <div ref={root} className=\"relative min-w-0\">\n <div\n ref={bar}\n data-slot=\"split-bar-bar\"\n data-active={activeItem ? \"\" : undefined}\n role={total ? \"application\" : undefined}\n tabIndex={total ? 0 : undefined}\n aria-roledescription={total ? \"split bar\" : undefined}\n aria-label={total ? `${summary} Arrow keys highlight a segment, Enter switches it off.` : undefined}\n className=\"split-bar-bar\"\n onPointerMove={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType === \"mouse\") setHot(idAt(event.clientX));\n }}\n onPointerDown={(event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"mouse\") setHot((now) => (now === idAt(event.clientX) ? null : idAt(event.clientX)));\n }}\n onPointerLeave={(event) => {\n if (event.pointerType === \"mouse\") setHot(null);\n }}\n onKeyDown={onKeyDown}\n onBlur={() => setHot(null)}\n >\n {items.map((item, index) => {\n const row = planOf(item.id);\n return (\n <div\n key={item.id}\n data-seg={item.id}\n data-fit={row?.fit}\n data-active={activeId === item.id || undefined}\n data-dim={(activeId !== null && activeId !== item.id) || undefined}\n data-off={off.has(item.id) || undefined}\n aria-hidden=\"true\"\n className=\"split-bar-seg\"\n style={{ \"--series\": colorOf(item, index) } as CSSProperties}\n >\n <span className=\"split-bar-seg-label\">\n <span className=\"split-bar-name\">\n <span className=\"truncate\">{item.label}</span>\n </span>\n <span className=\"tabular-nums\">\n <Roll text={pctText(item)} value={pct(item)} />\n </span>\n </span>\n </div>\n );\n })}\n {total ? null : <p className=\"absolute inset-0 m-0 grid place-items-center text-sm text-muted-foreground\">No data</p>}\n </div>\n <div data-open={lane || undefined} aria-hidden=\"true\" className=\"split-bar-lane\">\n <div className=\"relative min-h-0 overflow-hidden\" style={{ height: LANE }}>\n <svg width={width || undefined} height={LANE} className=\"absolute inset-x-0 top-0 block overflow-visible\" focusable=\"false\">\n {items.map((item) => (\n <path key={item.id} data-lead={item.id} data-on={(planOf(item.id)?.fit === \"out\" && !off.has(item.id)) || undefined} className=\"split-bar-leader\" />\n ))}\n </svg>\n {items.map((item) => {\n const row = planOf(item.id);\n const on = row?.fit === \"out\" && !off.has(item.id);\n return (\n <span\n key={item.id}\n data-out={item.id}\n data-w={row?.outW ?? 0}\n data-on={on || undefined}\n className=\"split-bar-out\"\n style={{ width: row?.outW, top: LABEL_TOP }}\n >\n <span className=\"truncate\">{item.label}</span>\n <span className=\"tabular-nums text-foreground\">{pctText(item)}</span>\n </span>\n );\n })}\n </div>\n </div>\n </div>\n {legend && items.length ? (\n <div className=\"-mx-2 flex min-w-0 flex-wrap gap-x-1 gap-y-0.5\" onPointerLeave={() => setHot(null)}>\n {items.map((item, index) => {\n const isOff = off.has(item.id);\n return (\n <button\n key={item.id}\n type=\"button\"\n data-slot=\"split-bar-toggle\"\n data-strain={shaking === item.id ? \"\" : undefined}\n aria-pressed={!isOff}\n aria-label={isOff ? `${item.label}, hidden` : `${item.label}, ${pctText(item)}`}\n onClick={() => toggle(item)}\n onPointerEnter={(event) => {\n if (event.pointerType === \"mouse\" && !isOff) setHot(item.id);\n }}\n onFocus={(event) => {\n if (event.currentTarget.matches(\":focus-visible\") && !isOff) setHot(item.id);\n }}\n onBlur={() => setHot(null)}\n style={{ \"--series\": colorOf(item, index), \"--strain-x\": \"6px\" } as CSSProperties}\n className=\"chart-legend-toggle data-[strain]:motion-safe:animate-field-strain\"\n >\n <span aria-hidden=\"true\" className=\"split-bar-swatch\" />\n <span className=\"chart-toggle-label\">{item.label}</span>\n <span aria-hidden=\"true\" className=\"text-xs tabular-nums text-muted-foreground\">\n {isOff ? \"Off\" : pctText(item)}\n </span>\n </button>\n );\n })}\n </div>\n ) : null}\n <p className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcement}\n </p>\n <p className=\"sr-only\">{summary}</p>\n {total ? (\n <div className=\"sr-only\">\n <table>\n <caption>{name}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Segment</th>\n <th scope=\"col\">Value</th>\n <th scope=\"col\">Share</th>\n </tr>\n </thead>\n <tbody>\n {items.map((item) => (\n <tr key={item.id}>\n <th scope=\"row\">{item.label}</th>\n <td>{valueText(item.value)}</td>\n <td>{off.has(item.id) ? \"Off\" : pctText(item)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n </figure>\n );\n});\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ export interface SplitButtonAction {
|
|
|
7
7
|
/** Runs when the item is chosen. */
|
|
8
8
|
onSelect?: () => void;
|
|
9
9
|
isDisabled?: boolean;
|
|
10
|
-
/** Tints the item
|
|
10
|
+
/** Tints the item red, and the menu's highlight with it. Pair it with a trash icon. */
|
|
11
11
|
destructive?: boolean;
|
|
12
12
|
/** An icon before the label. Mark it `aria-hidden`. */
|
|
13
13
|
icon?: ReactNode;
|
|
@@ -16,6 +16,7 @@ function iconKey(node) {
|
|
|
16
16
|
}
|
|
17
17
|
const half = [
|
|
18
18
|
"relative inline-flex items-center justify-center cursor-pointer select-none font-sans font-medium",
|
|
19
|
+
"border-0 bg-transparent text-inherit",
|
|
19
20
|
"[-webkit-tap-highlight-color:transparent]",
|
|
20
21
|
"outline-none",
|
|
21
22
|
"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width)",
|
|
@@ -25,14 +26,14 @@ const half = [
|
|
|
25
26
|
].join(" ");
|
|
26
27
|
const looks = {
|
|
27
28
|
primary: {
|
|
28
|
-
pill: "bg-primary text-primary-foreground shadow-surface rounded-
|
|
29
|
-
main: "h-(--density-control-h-md)
|
|
30
|
-
trigger: "h-(--density-control-h-md) w-
|
|
29
|
+
pill: "bg-primary text-primary-foreground shadow-surface rounded-full",
|
|
30
|
+
main: "h-(--density-control-h-md) ps-(--density-control-pad-md) pe-4 text-base rounded-s-full data-[hovered]:bg-primary-hover data-[pressed]:bg-primary-pressed",
|
|
31
|
+
trigger: "h-(--density-control-h-md) w-11 rounded-e-full border-s-(length:--border-width) border-primary-foreground/20 aria-[expanded=false]:data-[hovered]:bg-primary-hover aria-[expanded=false]:data-[pressed]:bg-primary-pressed"
|
|
31
32
|
},
|
|
32
33
|
secondary: {
|
|
33
|
-
pill: "bg-
|
|
34
|
-
main: "h-(--density-control-h-sm)
|
|
35
|
-
trigger: "h-(--density-control-h-sm) w-
|
|
34
|
+
pill: "bg-surface text-foreground border-(length:--border-width) border-border shadow-surface rounded-full",
|
|
35
|
+
main: "h-(--density-control-h-sm) ps-(--density-control-pad-sm) pe-3 text-sm rounded-s-full data-[hovered]:bg-muted data-[pressed]:bg-muted",
|
|
36
|
+
trigger: "h-(--density-control-h-sm) w-9 rounded-e-full border-s-(length:--border-width) border-border data-[hovered]:bg-muted data-[pressed]:bg-muted"
|
|
36
37
|
}
|
|
37
38
|
};
|
|
38
39
|
/**
|
|
@@ -67,7 +68,7 @@ function SplitButton({ children: label, actions, onPress, isDisabled, icon, vari
|
|
|
67
68
|
"aria-hidden": "true",
|
|
68
69
|
children: /* @__PURE__ */ jsxs("span", {
|
|
69
70
|
ref: contentRef,
|
|
70
|
-
className: "inline-flex flex-none items-center gap-2 whitespace-nowrap",
|
|
71
|
+
className: "inline-flex flex-none items-center gap-2 whitespace-nowrap [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-(--icon-sm)",
|
|
71
72
|
children: [icon ? /* @__PURE__ */ jsx(IconSwap, {
|
|
72
73
|
iconKey: glyph,
|
|
73
74
|
children: icon
|
|
@@ -99,17 +100,17 @@ function SplitButton({ children: label, actions, onPress, isDisabled, icon, vari
|
|
|
99
100
|
})
|
|
100
101
|
}), /* @__PURE__ */ jsx(Menu$1, {
|
|
101
102
|
placement: "bottom end",
|
|
103
|
+
offset: 6,
|
|
102
104
|
shouldFocusWrap: true,
|
|
103
|
-
|
|
104
|
-
children: actions.map((action, index) => /* @__PURE__ */ jsxs(MenuItem$1, {
|
|
105
|
+
children: actions.map((action) => /* @__PURE__ */ jsxs(MenuItem$1, {
|
|
105
106
|
id: action.label,
|
|
106
107
|
textValue: action.label,
|
|
107
108
|
isDisabled: action.isDisabled,
|
|
108
109
|
onAction: action.onSelect,
|
|
109
|
-
|
|
110
|
-
className: cn("menu-item-motion gap-2.5" + (action.destructive ? " text-destructive-strong data-[focused]:bg-[color-mix(in_oklab,var(--color-destructive-strong)_8%,var(--color-card))]" : "")),
|
|
110
|
+
destructive: action.destructive,
|
|
111
111
|
children: [action.icon ? /* @__PURE__ */ jsx("span", {
|
|
112
112
|
"aria-hidden": "true",
|
|
113
|
+
className: action.destructive ? "inline-flex" : "inline-flex text-text-secondary",
|
|
113
114
|
children: action.icon
|
|
114
115
|
}) : null, action.label]
|
|
115
116
|
}, action.label))
|