@seekalfred/primitives 0.5.0 → 0.6.0
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 +664 -9
- package/dist/accordion/accordion.d.ts +5 -5
- package/dist/accordion/accordion.js +22 -11
- package/dist/accordion/accordion.js.map +1 -1
- package/dist/action-button/action-button.d.ts +50 -0
- package/dist/action-button/action-button.js +159 -0
- package/dist/action-button/action-button.js.map +1 -0
- package/dist/action-button/index.d.ts +2 -0
- package/dist/action-button/index.js +2 -0
- package/dist/alert/alert.d.ts +16 -2
- package/dist/alert/alert.js +118 -20
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +1 -1
- package/dist/alert-dialog/alert-dialog.js +4 -2
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/avatar/avatar.d.ts +7 -1
- package/dist/avatar/avatar.js +51 -18
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +10 -3
- package/dist/badge/badge.js +37 -5
- package/dist/badge/badge.js.map +1 -1
- package/dist/breadcrumb/breadcrumb.d.ts +3 -30
- package/dist/breadcrumb/breadcrumb.js +25 -8
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/button/button.d.ts +36 -23
- package/dist/button/button.js +178 -33
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.d.ts +76 -0
- package/dist/button-group/button-group.js +403 -0
- package/dist/button-group/button-group.js.map +1 -0
- package/dist/button-group/index.d.ts +2 -0
- package/dist/button-group/index.js +2 -0
- package/dist/calendar/calendar.d.ts +3 -3
- package/dist/calendar/calendar.js +263 -34
- package/dist/calendar/calendar.js.map +1 -1
- package/dist/card/card.d.ts +14 -7
- package/dist/card/card.js +32 -14
- package/dist/card/card.js.map +1 -1
- package/dist/checkbox/checkbox.d.ts +2 -9
- package/dist/checkbox/checkbox.js +28 -29
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/confirm-morph/confirm-morph.d.ts +85 -0
- package/dist/confirm-morph/confirm-morph.js +652 -0
- package/dist/confirm-morph/confirm-morph.js.map +1 -0
- package/dist/confirm-morph/index.d.ts +2 -0
- package/dist/confirm-morph/index.js +2 -0
- package/dist/copy-button/copy-button.d.ts +32 -0
- package/dist/copy-button/copy-button.js +134 -0
- package/dist/copy-button/copy-button.js.map +1 -0
- package/dist/copy-button/index.d.ts +2 -0
- package/dist/copy-button/index.js +2 -0
- package/dist/date-picker/date-picker.d.ts +51 -0
- package/dist/date-picker/date-picker.js +241 -0
- package/dist/date-picker/date-picker.js.map +1 -0
- package/dist/date-picker/date-range-picker.d.ts +78 -0
- package/dist/date-picker/date-range-picker.js +1015 -0
- package/dist/date-picker/date-range-picker.js.map +1 -0
- package/dist/date-picker/index.d.ts +4 -0
- package/dist/date-picker/index.js +4 -0
- package/dist/date-picker/picker-parts.d.ts +46 -0
- package/dist/date-picker/picker-parts.js +135 -0
- package/dist/date-picker/picker-parts.js.map +1 -0
- package/dist/dialog/dialog.d.ts +12 -16
- package/dist/dialog/dialog.js +45 -14
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/empty-state/empty-state.d.ts +44 -0
- package/dist/empty-state/empty-state.js +83 -0
- package/dist/empty-state/empty-state.js.map +1 -0
- package/dist/empty-state/index.d.ts +2 -0
- package/dist/empty-state/index.js +2 -0
- package/dist/expanding-button-group/expanding-button-group.d.ts +54 -0
- package/dist/expanding-button-group/expanding-button-group.js +350 -0
- package/dist/expanding-button-group/expanding-button-group.js.map +1 -0
- package/dist/expanding-button-group/index.d.ts +2 -0
- package/dist/expanding-button-group/index.js +2 -0
- package/dist/file-drop-zone/file-drop-zone.d.ts +71 -0
- package/dist/file-drop-zone/file-drop-zone.js +116 -0
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -0
- package/dist/file-drop-zone/index.d.ts +2 -0
- package/dist/file-drop-zone/index.js +2 -0
- package/dist/filter-bar/filter-bar.d.ts +121 -0
- package/dist/filter-bar/filter-bar.js +300 -0
- package/dist/filter-bar/filter-bar.js.map +1 -0
- package/dist/filter-bar/index.d.ts +2 -0
- package/dist/filter-bar/index.js +2 -0
- package/dist/floating-button-group/floating-button-group.d.ts +61 -0
- package/dist/floating-button-group/floating-button-group.js +340 -0
- package/dist/floating-button-group/floating-button-group.js.map +1 -0
- package/dist/floating-button-group/index.d.ts +2 -0
- package/dist/floating-button-group/index.js +2 -0
- package/dist/icon-morph/icon-morph.d.ts +55 -0
- package/dist/icon-morph/icon-morph.js +331 -0
- package/dist/icon-morph/icon-morph.js.map +1 -0
- package/dist/icon-morph/index.d.ts +2 -0
- package/dist/icon-morph/index.js +2 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.js +29 -7
- package/dist/input/input.d.ts +23 -1
- package/dist/input/input.js +37 -13
- package/dist/input/input.js.map +1 -1
- package/dist/label/label.d.ts +1 -1
- package/dist/label/label.js +4 -2
- package/dist/label/label.js.map +1 -1
- package/dist/lib/count-to.js +113 -0
- package/dist/lib/count-to.js.map +1 -0
- package/dist/lib/eye-morph.js +62 -0
- package/dist/lib/eye-morph.js.map +1 -0
- package/dist/lib/field-message.js +336 -0
- package/dist/lib/field-message.js.map +1 -0
- package/dist/lib/field-shell.js +81 -0
- package/dist/lib/field-shell.js.map +1 -0
- package/dist/lib/field-surface.js +4 -2
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flip.js +80 -0
- package/dist/lib/flip.js.map +1 -0
- package/dist/lib/height-follow.js +59 -0
- package/dist/lib/height-follow.js.map +1 -0
- package/dist/lib/icon-swap.js +47 -0
- package/dist/lib/icon-swap.js.map +1 -0
- package/dist/lib/leaving-list.js +57 -0
- package/dist/lib/leaving-list.js.map +1 -0
- package/dist/lib/merge-refs.js +22 -0
- package/dist/lib/merge-refs.js.map +1 -0
- package/dist/lib/morph-text.js +128 -0
- package/dist/lib/morph-text.js.map +1 -0
- package/dist/lib/motion.js +162 -0
- package/dist/lib/motion.js.map +1 -0
- package/dist/lib/pausable-timeout.js +67 -0
- package/dist/lib/pausable-timeout.js.map +1 -0
- package/dist/lib/rolling-value.js +31 -0
- package/dist/lib/rolling-value.js.map +1 -0
- package/dist/lib/spring-progress.js +114 -0
- package/dist/lib/spring-progress.js.map +1 -0
- package/dist/lib/svg-id.js +10 -0
- package/dist/lib/svg-id.js.map +1 -0
- package/dist/lib/swap-text.js +77 -0
- package/dist/lib/swap-text.js.map +1 -0
- package/dist/lib/use-copy-feedback.js +36 -0
- package/dist/lib/use-copy-feedback.js.map +1 -0
- package/dist/menu/menu.d.ts +5 -5
- package/dist/menu/menu.js +10 -6
- package/dist/menu/menu.js.map +1 -1
- package/dist/page-header/index.d.ts +2 -0
- package/dist/page-header/index.js +2 -0
- package/dist/page-header/page-header.d.ts +75 -0
- package/dist/page-header/page-header.js +83 -0
- package/dist/page-header/page-header.js.map +1 -0
- package/dist/password-field/index.d.ts +2 -0
- package/dist/password-field/index.js +2 -0
- package/dist/password-field/password-field.d.ts +37 -0
- package/dist/password-field/password-field.js +101 -0
- package/dist/password-field/password-field.js.map +1 -0
- package/dist/password-strength/index.d.ts +2 -0
- package/dist/password-strength/index.js +2 -0
- package/dist/password-strength/password-strength.d.ts +127 -0
- package/dist/password-strength/password-strength.js +455 -0
- package/dist/password-strength/password-strength.js.map +1 -0
- package/dist/popover/popover.d.ts +10 -2
- package/dist/popover/popover.js +6 -4
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.d.ts +2 -2
- package/dist/progress/progress.js +50 -7
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/radio-group.d.ts +3 -3
- package/dist/radio-group/radio-group.js +14 -10
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/scroll-area/scroll-area.d.ts +17 -6
- package/dist/scroll-area/scroll-area.js +30 -6
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/search-field/index.d.ts +2 -0
- package/dist/search-field/index.js +2 -0
- package/dist/search-field/search-field.d.ts +26 -0
- package/dist/search-field/search-field.js +155 -0
- package/dist/search-field/search-field.js.map +1 -0
- package/dist/select/select.d.ts +3 -3
- package/dist/select/select.js +85 -24
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/separator/separator.js +4 -2
- package/dist/separator/separator.js.map +1 -1
- package/dist/sheet/sheet.d.ts +1 -1
- package/dist/sheet/sheet.js +6 -3
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/side-nav/index.d.ts +2 -0
- package/dist/side-nav/index.js +2 -0
- package/dist/side-nav/side-nav.d.ts +72 -0
- package/dist/side-nav/side-nav.js +142 -0
- package/dist/side-nav/side-nav.js.map +1 -0
- package/dist/skeleton/skeleton.d.ts +8 -2
- package/dist/skeleton/skeleton.js +9 -3
- package/dist/skeleton/skeleton.js.map +1 -1
- package/dist/slider/slider.d.ts +2 -2
- package/dist/slider/slider.js +64 -8
- package/dist/slider/slider.js.map +1 -1
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +4 -2
- package/dist/spinner/spinner.js.map +1 -1
- package/dist/split-button/index.d.ts +2 -0
- package/dist/split-button/index.js +2 -0
- package/dist/split-button/split-button.d.ts +46 -0
- package/dist/split-button/split-button.js +122 -0
- package/dist/split-button/split-button.js.map +1 -0
- package/dist/stat-card/index.d.ts +2 -0
- package/dist/stat-card/index.js +2 -0
- package/dist/stat-card/stat-card.d.ts +81 -0
- package/dist/stat-card/stat-card.js +152 -0
- package/dist/stat-card/stat-card.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/switch/switch.d.ts +2 -2
- package/dist/switch/switch.js +11 -8
- 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 +38 -7
- package/dist/table/table.js +114 -12
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.d.ts +4 -4
- package/dist/tabs/tabs.js +153 -12
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/textarea/textarea.d.ts +24 -1
- package/dist/textarea/textarea.js +27 -3
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/theme-switch/index.d.ts +2 -0
- package/dist/theme-switch/index.js +2 -0
- package/dist/theme-switch/theme-switch.d.ts +37 -0
- package/dist/theme-switch/theme-switch.js +123 -0
- package/dist/theme-switch/theme-switch.js.map +1 -0
- package/dist/time-picker/index.d.ts +2 -0
- package/dist/time-picker/index.js +2 -0
- package/dist/time-picker/time-picker.d.ts +54 -0
- package/dist/time-picker/time-picker.js +201 -0
- package/dist/time-picker/time-picker.js.map +1 -0
- package/dist/toast/toast.d.ts +2 -2
- package/dist/toast/toast.js +61 -39
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +3 -3
- package/dist/tooltip/tooltip.js +66 -15
- package/dist/tooltip/tooltip.js.map +1 -1
- package/package.json +83 -3
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect, useLeavingItems, useMorphWidth } from "../lib/motion.js";
|
|
4
|
+
import { Menu as Menu$1, MenuItem as MenuItem$1, MenuTrigger } from "../menu/menu.js";
|
|
5
|
+
import { isValidElement, useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { Button } from "react-aria-components";
|
|
7
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/button-group/button-group.tsx
|
|
9
|
+
/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */
|
|
10
|
+
const MENU = "button-group-menu";
|
|
11
|
+
/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */
|
|
12
|
+
function iconKey(node) {
|
|
13
|
+
if (!isValidElement(node)) return typeof node === "string" || typeof node === "number" ? String(node) : "";
|
|
14
|
+
const type = node.type;
|
|
15
|
+
return typeof type === "string" ? type : type?.displayName ?? type?.name ?? "icon";
|
|
16
|
+
}
|
|
17
|
+
const PHASE = "col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5";
|
|
18
|
+
function LeavingPhase({ id, onDone, children }) {
|
|
19
|
+
const remove = useCallback(() => onDone(id), [onDone, id]);
|
|
20
|
+
useExitBackstop(true, remove);
|
|
21
|
+
return /* @__PURE__ */ jsx("span", {
|
|
22
|
+
"aria-hidden": "true",
|
|
23
|
+
onAnimationEnd: (event) => {
|
|
24
|
+
if (event.target === event.currentTarget) remove();
|
|
25
|
+
},
|
|
26
|
+
className: cn(PHASE + " motion-safe:animate-label-out motion-reduce:animate-fade-out"),
|
|
27
|
+
children
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Icon and label of one segment. Every reserved label sits invisibly in the same
|
|
32
|
+
* grid cell, so the segment is as wide as its widest state and a new label
|
|
33
|
+
* crossfades inside a fixed box: it rises in from a soft blur while the old one
|
|
34
|
+
* lifts away. A label nobody reserved still never snaps: the slot springs to its
|
|
35
|
+
* width.
|
|
36
|
+
*/
|
|
37
|
+
function SegmentContent({ item }) {
|
|
38
|
+
const contentRef = useRef(null);
|
|
39
|
+
const phaseRef = useRef(null);
|
|
40
|
+
const key = iconKey(item.icon) + "|" + (item.content !== void 0 ? "\0content" : item.iconOnly ? "" : item.label);
|
|
41
|
+
useMorphWidth(contentRef, key);
|
|
42
|
+
const collapsible = item.icon ? "group-data-[compact]/bg:hidden" : "";
|
|
43
|
+
const node = /* @__PURE__ */ jsxs(Fragment$1, { children: [item.icon ? /* @__PURE__ */ jsx("span", {
|
|
44
|
+
className: "inline-flex flex-none [&_svg]:size-(--icon-sm)",
|
|
45
|
+
children: item.icon
|
|
46
|
+
}) : null, item.content !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
47
|
+
className: "inline-flex items-center tabular-nums",
|
|
48
|
+
children: item.content
|
|
49
|
+
}) : item.iconOnly ? null : /* @__PURE__ */ jsx("span", {
|
|
50
|
+
className: cn("inline-block " + collapsible),
|
|
51
|
+
children: item.label
|
|
52
|
+
})] });
|
|
53
|
+
const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);
|
|
54
|
+
const sizers = item.iconOnly || item.content !== void 0 ? [] : [.../* @__PURE__ */ new Set([item.label, ...item.reserve ?? []])];
|
|
55
|
+
return /* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: "button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]",
|
|
57
|
+
"aria-hidden": "true",
|
|
58
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
59
|
+
ref: contentRef,
|
|
60
|
+
className: "inline-grid flex-none items-center justify-items-center",
|
|
61
|
+
children: [
|
|
62
|
+
sizers.map((text) => /* @__PURE__ */ jsxs("span", {
|
|
63
|
+
className: cn(PHASE + " pointer-events-none invisible"),
|
|
64
|
+
children: [item.icon ? /* @__PURE__ */ jsx("span", { className: "inline-block size-4 flex-none" }) : null, /* @__PURE__ */ jsx("span", {
|
|
65
|
+
className: cn("inline-block " + collapsible),
|
|
66
|
+
children: text
|
|
67
|
+
})]
|
|
68
|
+
}, text)),
|
|
69
|
+
leaving.map((entry) => /* @__PURE__ */ jsx(LeavingPhase, {
|
|
70
|
+
id: entry.id,
|
|
71
|
+
onDone: remove,
|
|
72
|
+
children: entry.value
|
|
73
|
+
}, entry.id)),
|
|
74
|
+
/* @__PURE__ */ jsx("span", {
|
|
75
|
+
ref: phaseRef,
|
|
76
|
+
className: cn(PHASE + (changed ? " motion-safe:animate-label-in motion-reduce:animate-fade-in" : "")),
|
|
77
|
+
children: node
|
|
78
|
+
}, key)
|
|
79
|
+
]
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const inside = (node, x, y) => {
|
|
84
|
+
const rect = node.getBoundingClientRect();
|
|
85
|
+
return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;
|
|
86
|
+
};
|
|
87
|
+
/** Segments are found by their data-key, so the group needs no ref per segment. */
|
|
88
|
+
const segmentsIn = (root) => Array.from(root?.querySelectorAll(":scope > [data-key]") ?? []);
|
|
89
|
+
const segmentIn = (root, key) => key === null ? void 0 : segmentsIn(root).find((node) => node.dataset.key === key);
|
|
90
|
+
/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */
|
|
91
|
+
function boxOf(group, node) {
|
|
92
|
+
const outer = group.getBoundingClientRect();
|
|
93
|
+
const inner = node.getBoundingClientRect();
|
|
94
|
+
const scale = group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1 ? outer.width / group.offsetWidth : 1;
|
|
95
|
+
return [
|
|
96
|
+
(inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,
|
|
97
|
+
(inner.top - outer.top) / scale - group.clientTop + group.scrollTop,
|
|
98
|
+
inner.width / scale,
|
|
99
|
+
inner.height / scale
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
const inert = (node) => node.disabled || node.getAttribute("aria-disabled") === "true";
|
|
103
|
+
const TRAVEL = "translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)";
|
|
104
|
+
const FADE = "opacity var(--duration-quick) var(--ease-settle)";
|
|
105
|
+
const FADE_INSTANT = "opacity var(--duration-instant) var(--ease-settle)";
|
|
106
|
+
const focusRing = "data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)";
|
|
107
|
+
/**
|
|
108
|
+
* Related actions joined into one surface: a shared border and radius, hairline
|
|
109
|
+
* dividers, no gaps. One soft highlight glides between segments under the
|
|
110
|
+
* pointer or keyboard focus, the pressed segment answers in place, and an
|
|
111
|
+
* optional chevron segment opens more actions in a menu aligned to the group's
|
|
112
|
+
* edge.
|
|
113
|
+
*
|
|
114
|
+
* Tab visits every segment in order; the arrow keys along the orientation, Home
|
|
115
|
+
* and End also move between them. A segment's label can change in place ("Share"
|
|
116
|
+
* answers "Copied") without resizing it, and a label that changes right after its
|
|
117
|
+
* segment is pressed is announced once.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* <ButtonGroup label="Document actions" items={[{ id: "edit", label: "Edit" }, { id: "share", label: "Share", reserve: ["Copied"] }]} />
|
|
121
|
+
*/
|
|
122
|
+
function ButtonGroup({ items, menu, label, variant = "outline", size = "md", orientation = "horizontal", collapseLabels = true, isDisabled = false, className }) {
|
|
123
|
+
const root = useRef(null);
|
|
124
|
+
const highlight = useRef(null);
|
|
125
|
+
const vertical = orientation === "vertical";
|
|
126
|
+
const [hover, setHover] = useState(null);
|
|
127
|
+
const [pressed, setPressed] = useState(null);
|
|
128
|
+
const [focused, setFocused] = useState(null);
|
|
129
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
130
|
+
const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;
|
|
131
|
+
const keys = [...items.map((item) => item.id), ...menu ? [MENU] : []];
|
|
132
|
+
const activeIndex = active ? keys.indexOf(active) : -1;
|
|
133
|
+
const inertKey = String(isDisabled) + items.map((item) => item.isDisabled ? 1 : 0).join("");
|
|
134
|
+
const shown = useRef(null);
|
|
135
|
+
useIsomorphicLayoutEffect(() => {
|
|
136
|
+
const el = highlight.current;
|
|
137
|
+
const group = root.current;
|
|
138
|
+
if (!el || !group) return;
|
|
139
|
+
const reduced = prefersReducedMotion();
|
|
140
|
+
const node = segmentIn(group, active);
|
|
141
|
+
if (!node || inert(node) && active !== focused) {
|
|
142
|
+
if (shown.current) {
|
|
143
|
+
el.style.transition = reduced ? FADE_INSTANT : FADE;
|
|
144
|
+
el.style.opacity = "0";
|
|
145
|
+
}
|
|
146
|
+
shown.current = null;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const [x, y, width, height] = boxOf(group, node);
|
|
150
|
+
const arriving = !shown.current || reduced;
|
|
151
|
+
el.style.transition = arriving ? reduced ? FADE_INSTANT : FADE : TRAVEL;
|
|
152
|
+
el.style.translate = x + "px " + y + "px";
|
|
153
|
+
el.style.width = width + "px";
|
|
154
|
+
el.style.height = height + "px";
|
|
155
|
+
el.style.opacity = "1";
|
|
156
|
+
shown.current = active;
|
|
157
|
+
}, [
|
|
158
|
+
active,
|
|
159
|
+
inertKey,
|
|
160
|
+
focused
|
|
161
|
+
]);
|
|
162
|
+
const keyList = keys.join("\0");
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const group = root.current;
|
|
165
|
+
const el = highlight.current;
|
|
166
|
+
if (!group || !el || typeof ResizeObserver === "undefined") return;
|
|
167
|
+
let frame = 0;
|
|
168
|
+
const observer = new ResizeObserver(() => {
|
|
169
|
+
cancelAnimationFrame(frame);
|
|
170
|
+
frame = requestAnimationFrame(() => {
|
|
171
|
+
const node = segmentIn(group, shown.current);
|
|
172
|
+
if (!node) return;
|
|
173
|
+
const [x, y, width, height] = boxOf(group, node);
|
|
174
|
+
el.style.transition = FADE;
|
|
175
|
+
el.style.translate = x + "px " + y + "px";
|
|
176
|
+
el.style.width = width + "px";
|
|
177
|
+
el.style.height = height + "px";
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
observer.observe(group);
|
|
181
|
+
segmentsIn(group).forEach((node) => observer.observe(node));
|
|
182
|
+
return () => {
|
|
183
|
+
cancelAnimationFrame(frame);
|
|
184
|
+
observer.disconnect();
|
|
185
|
+
};
|
|
186
|
+
}, [keyList]);
|
|
187
|
+
const [overflowing, setCompact] = useState(false);
|
|
188
|
+
const fullWidth = useRef(0);
|
|
189
|
+
const blockedAt = useRef(0);
|
|
190
|
+
const collapsible = collapseLabels && !vertical && items.some((item) => item.icon && !item.iconOnly && item.content === void 0);
|
|
191
|
+
const compact = collapsible && overflowing;
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
const group = root.current;
|
|
194
|
+
const parent = group?.parentElement;
|
|
195
|
+
if (!group || !parent || !collapsible || typeof ResizeObserver === "undefined") return;
|
|
196
|
+
const check = () => {
|
|
197
|
+
const box = getComputedStyle(parent);
|
|
198
|
+
const available = parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);
|
|
199
|
+
if (group.dataset.compact === void 0) {
|
|
200
|
+
if (group.scrollWidth > group.clientWidth + 1) {
|
|
201
|
+
fullWidth.current = group.scrollWidth + 2;
|
|
202
|
+
blockedAt.current = available;
|
|
203
|
+
setCompact(true);
|
|
204
|
+
}
|
|
205
|
+
} else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);
|
|
206
|
+
};
|
|
207
|
+
let frame = 0;
|
|
208
|
+
const observer = new ResizeObserver(() => {
|
|
209
|
+
cancelAnimationFrame(frame);
|
|
210
|
+
frame = requestAnimationFrame(check);
|
|
211
|
+
});
|
|
212
|
+
observer.observe(group);
|
|
213
|
+
observer.observe(parent);
|
|
214
|
+
return () => {
|
|
215
|
+
cancelAnimationFrame(frame);
|
|
216
|
+
observer.disconnect();
|
|
217
|
+
};
|
|
218
|
+
}, [collapsible, keyList]);
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (!pressed) return;
|
|
221
|
+
const release = () => setPressed(null);
|
|
222
|
+
window.addEventListener("pointerup", release);
|
|
223
|
+
window.addEventListener("pointercancel", release);
|
|
224
|
+
return () => {
|
|
225
|
+
window.removeEventListener("pointerup", release);
|
|
226
|
+
window.removeEventListener("pointercancel", release);
|
|
227
|
+
};
|
|
228
|
+
}, [pressed]);
|
|
229
|
+
const hit = (event) => {
|
|
230
|
+
for (const node of segmentsIn(root.current)) if (inside(node, event.clientX, event.clientY)) return inert(node) ? null : node.dataset.key ?? null;
|
|
231
|
+
return null;
|
|
232
|
+
};
|
|
233
|
+
const onPointerMove = (event) => {
|
|
234
|
+
if (event.pointerType !== "touch") setHover(hit(event));
|
|
235
|
+
};
|
|
236
|
+
const onPointerLeave = () => setHover(null);
|
|
237
|
+
const onPointerDown = (event) => {
|
|
238
|
+
if (event.button === 0) setPressed(hit(event));
|
|
239
|
+
};
|
|
240
|
+
const onFocus = (event) => {
|
|
241
|
+
const target = event.target;
|
|
242
|
+
const key = target.dataset.key;
|
|
243
|
+
if (key === void 0) return;
|
|
244
|
+
let visible = true;
|
|
245
|
+
try {
|
|
246
|
+
visible = target.matches(":focus-visible");
|
|
247
|
+
} catch {}
|
|
248
|
+
setFocused(visible ? key : null);
|
|
249
|
+
};
|
|
250
|
+
const onBlur = (event) => {
|
|
251
|
+
if (!root.current?.contains(event.relatedTarget)) setFocused(null);
|
|
252
|
+
};
|
|
253
|
+
const onKeyDown = (event) => {
|
|
254
|
+
if (event.defaultPrevented) return;
|
|
255
|
+
const list = segmentsIn(root.current).filter((node) => !node.disabled);
|
|
256
|
+
const index = list.findIndex((node) => node === document.activeElement);
|
|
257
|
+
if (index < 0) return;
|
|
258
|
+
const rtl = !vertical && getComputedStyle(root.current).direction === "rtl";
|
|
259
|
+
const forward = vertical ? "ArrowDown" : rtl ? "ArrowLeft" : "ArrowRight";
|
|
260
|
+
const backward = vertical ? "ArrowUp" : rtl ? "ArrowRight" : "ArrowLeft";
|
|
261
|
+
const last = list.length - 1;
|
|
262
|
+
const target = event.key === forward ? index === last ? 0 : index + 1 : event.key === backward ? index === 0 ? last : index - 1 : event.key === "Home" ? 0 : event.key === "End" ? last : -1;
|
|
263
|
+
if (target < 0) return;
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
list[target].focus();
|
|
266
|
+
};
|
|
267
|
+
const [announcement, setAnnouncement] = useState("");
|
|
268
|
+
const labels = useRef(null);
|
|
269
|
+
const activated = useRef({
|
|
270
|
+
key: "",
|
|
271
|
+
at: 0
|
|
272
|
+
});
|
|
273
|
+
const labelList = items.map((item) => item.id + "\0" + item.label).join("");
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
const previous = labels.current;
|
|
276
|
+
labels.current = new Map(items.map((item) => [item.id, item.label]));
|
|
277
|
+
if (!previous) return;
|
|
278
|
+
const recent = performance.now() - activated.current.at < 1e3;
|
|
279
|
+
const changed = items.filter((item) => recent && item.id === activated.current.key && previous.has(item.id) && previous.get(item.id) !== item.label && item.content === void 0);
|
|
280
|
+
if (changed.length) setAnnouncement(changed.map((item) => item.label).join(", "));
|
|
281
|
+
}, [labelList]);
|
|
282
|
+
const select = (item) => {
|
|
283
|
+
activated.current = {
|
|
284
|
+
key: item.id,
|
|
285
|
+
at: performance.now()
|
|
286
|
+
};
|
|
287
|
+
item.onSelect?.();
|
|
288
|
+
};
|
|
289
|
+
const quiet = (index) => activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? "" : void 0;
|
|
290
|
+
const segments = items.map((item, index) => {
|
|
291
|
+
const off = item.isDisabled && !isDisabled;
|
|
292
|
+
const named = item.iconOnly || item.content !== void 0 || compact && !!item.icon;
|
|
293
|
+
const common = {
|
|
294
|
+
className: cn("button-group-segment " + focusRing),
|
|
295
|
+
"data-key": item.id,
|
|
296
|
+
"data-quiet": quiet(index),
|
|
297
|
+
"data-icon-only": item.iconOnly ? "" : void 0,
|
|
298
|
+
"data-collapsible": item.icon && !item.iconOnly && item.content === void 0 ? "" : void 0,
|
|
299
|
+
"aria-label": named ? item.label : void 0,
|
|
300
|
+
title: item.iconOnly || compact && item.icon ? item.label : void 0
|
|
301
|
+
};
|
|
302
|
+
const body = /* @__PURE__ */ jsx("span", {
|
|
303
|
+
className: "button-group-content",
|
|
304
|
+
children: /* @__PURE__ */ jsx(SegmentContent, { item })
|
|
305
|
+
});
|
|
306
|
+
const name = named ? null : /* @__PURE__ */ jsx("span", {
|
|
307
|
+
className: "sr-only",
|
|
308
|
+
children: item.label
|
|
309
|
+
});
|
|
310
|
+
if (item.href && !off && !isDisabled) return /* @__PURE__ */ jsxs("a", {
|
|
311
|
+
...common,
|
|
312
|
+
href: item.href,
|
|
313
|
+
onClick: () => select(item),
|
|
314
|
+
children: [body, name]
|
|
315
|
+
}, item.id);
|
|
316
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
317
|
+
...common,
|
|
318
|
+
type: "button",
|
|
319
|
+
disabled: isDisabled,
|
|
320
|
+
"aria-disabled": off || void 0,
|
|
321
|
+
onClick: off ? void 0 : () => select(item),
|
|
322
|
+
children: [body, name]
|
|
323
|
+
}, item.id);
|
|
324
|
+
});
|
|
325
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
326
|
+
ref: root,
|
|
327
|
+
role: "group",
|
|
328
|
+
"aria-label": label,
|
|
329
|
+
"aria-disabled": isDisabled || void 0,
|
|
330
|
+
"data-orientation": orientation,
|
|
331
|
+
"data-variant": variant,
|
|
332
|
+
"data-size": size,
|
|
333
|
+
"data-compact": compact ? "" : void 0,
|
|
334
|
+
onPointerMove,
|
|
335
|
+
onPointerLeave,
|
|
336
|
+
onPointerDown,
|
|
337
|
+
onFocus,
|
|
338
|
+
onBlur,
|
|
339
|
+
onKeyDown,
|
|
340
|
+
className: cn("button-group group/bg", className),
|
|
341
|
+
children: [
|
|
342
|
+
/* @__PURE__ */ jsx("span", {
|
|
343
|
+
ref: highlight,
|
|
344
|
+
"aria-hidden": "true",
|
|
345
|
+
"data-pressed": pressed && pressed === active ? "" : void 0,
|
|
346
|
+
className: "button-group-highlight"
|
|
347
|
+
}),
|
|
348
|
+
segments,
|
|
349
|
+
menu ? /* @__PURE__ */ jsxs(MenuTrigger, {
|
|
350
|
+
onOpenChange: setMenuOpen,
|
|
351
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
352
|
+
"data-key": MENU,
|
|
353
|
+
"data-trigger": "",
|
|
354
|
+
"data-quiet": quiet(items.length),
|
|
355
|
+
"aria-label": menu.label,
|
|
356
|
+
isDisabled,
|
|
357
|
+
className: cn("button-group-segment group/trigger " + focusRing),
|
|
358
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
359
|
+
className: "button-group-content",
|
|
360
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
361
|
+
viewBox: "0 0 24 24",
|
|
362
|
+
fill: "none",
|
|
363
|
+
stroke: "currentColor",
|
|
364
|
+
strokeWidth: 1.75,
|
|
365
|
+
strokeLinecap: "round",
|
|
366
|
+
strokeLinejoin: "round",
|
|
367
|
+
"aria-hidden": "true",
|
|
368
|
+
className: "size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180",
|
|
369
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
}), /* @__PURE__ */ jsx(Menu$1, {
|
|
373
|
+
placement: vertical ? "bottom start" : "bottom end",
|
|
374
|
+
offset: 6,
|
|
375
|
+
shouldFocusWrap: true,
|
|
376
|
+
className: cn("menu-motion " + (vertical ? "[--menu-ox:left]" : "[--menu-ox:right]")),
|
|
377
|
+
children: menu.items.map((action, index) => /* @__PURE__ */ jsxs(MenuItem$1, {
|
|
378
|
+
id: action.id,
|
|
379
|
+
textValue: action.label,
|
|
380
|
+
href: action.href,
|
|
381
|
+
isDisabled: action.isDisabled,
|
|
382
|
+
onAction: action.onSelect,
|
|
383
|
+
"data-i": Math.min(index, 4),
|
|
384
|
+
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))]" : "")),
|
|
385
|
+
children: [action.icon ? /* @__PURE__ */ jsx("span", {
|
|
386
|
+
"aria-hidden": "true",
|
|
387
|
+
children: action.icon
|
|
388
|
+
}) : null, action.label]
|
|
389
|
+
}, action.id))
|
|
390
|
+
})]
|
|
391
|
+
}) : null,
|
|
392
|
+
/* @__PURE__ */ jsx("span", {
|
|
393
|
+
className: "sr-only",
|
|
394
|
+
"aria-live": "polite",
|
|
395
|
+
children: announcement
|
|
396
|
+
})
|
|
397
|
+
]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
export { ButtonGroup };
|
|
402
|
+
|
|
403
|
+
//# sourceMappingURL=button-group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button-group.js","names":["AriaButton","Menu","MenuItem"],"sources":["../../src/button-group/button-group.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport {\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n useMorphWidth,\n prefersReducedMotion,\n} from \"../lib/motion.js\";\nimport { Menu, MenuItem, MenuTrigger } from \"../menu/menu.js\";\n\nexport type ButtonGroupVariant = \"outline\" | \"solid\";\nexport type ButtonGroupSize = \"sm\" | \"md\";\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ButtonGroupItem {\n /** Stable key for the segment. */\n id: string;\n /** Visible text and the accessible name. Changing it crossfades in place, so \"Share\" can answer \"Copied\". */\n label: string;\n /** Every other label this segment can show, such as [\"Copied\"]. The segment sizes to the widest, so a label change never resizes it. */\n reserve?: string[];\n /** An icon before the label. Swapping it for another morphs; re-rendering the same icon does not. Mark it `aria-hidden`. */\n icon?: ReactNode;\n /** Shows only the icon; the label becomes the accessible name and the tooltip. */\n iconOnly?: boolean;\n /** Visible content in place of the label, such as a live value. The label stays the accessible name. */\n content?: ReactNode;\n onSelect?: () => void;\n /** Renders the segment as a link. */\n href?: string;\n isDisabled?: boolean;\n}\n\nexport interface ButtonGroupMenuItem {\n id: string;\n label: string;\n /** An icon before the label. Mark it `aria-hidden`. */\n icon?: ReactNode;\n onSelect?: () => void;\n href?: string;\n isDisabled?: boolean;\n /** Colours the item as a destructive action. */\n destructive?: boolean;\n}\n\nexport interface ButtonGroupMenu {\n /** Accessible name and tooltip of the chevron segment, such as \"More actions\". */\n label: string;\n items: ButtonGroupMenuItem[];\n}\n\nexport interface ButtonGroupProps {\n items: ButtonGroupItem[];\n /** Adds a trailing chevron segment that opens these actions in a menu aligned to the group's edge. */\n menu?: ButtonGroupMenu;\n /** Accessible name of the group, such as \"Document actions\". */\n label: string;\n /** @default \"outline\" */\n variant?: ButtonGroupVariant;\n /** @default \"md\" */\n size?: ButtonGroupSize;\n /** @default \"horizontal\" */\n orientation?: ButtonGroupOrientation;\n /** When the row does not fit its container, segments with an icon drop their label and keep the icon. Horizontal only. @default true */\n collapseLabels?: boolean;\n /** Disables every segment and the menu. */\n isDisabled?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */\nconst MENU = \"button-group-menu\";\n\n/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */\nfunction iconKey(node: ReactNode): string {\n if (!isValidElement(node))\n return typeof node === \"string\" || typeof node === \"number\" ? String(node) : \"\";\n const type = node.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type?.displayName ?? type?.name ?? \"icon\");\n}\n\nconst PHASE =\n \"col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5\";\n\nfunction LeavingPhase({\n id,\n onDone,\n children,\n}: {\n id: number;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n className={cn(PHASE + \" motion-safe:animate-label-out motion-reduce:animate-fade-out\")}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Icon and label of one segment. Every reserved label sits invisibly in the same\n * grid cell, so the segment is as wide as its widest state and a new label\n * crossfades inside a fixed box: it rises in from a soft blur while the old one\n * lifts away. A label nobody reserved still never snaps: the slot springs to its\n * width.\n */\nfunction SegmentContent({ item }: { item: ButtonGroupItem }) {\n const contentRef = useRef<HTMLSpanElement>(null);\n const phaseRef = useRef<HTMLSpanElement>(null);\n const glyph = iconKey(item.icon);\n const key =\n glyph + \"|\" + (item.content !== undefined ? \"\\u0000content\" : item.iconOnly ? \"\" : item.label);\n useMorphWidth(contentRef, key);\n\n const collapsible = item.icon ? \"group-data-[compact]/bg:hidden\" : \"\";\n const node = (\n <>\n {item.icon ? (\n <span className=\"inline-flex flex-none [&_svg]:size-(--icon-sm)\">{item.icon}</span>\n ) : null}\n {item.content !== undefined ? (\n <span className=\"inline-flex items-center tabular-nums\">{item.content}</span>\n ) : item.iconOnly ? null : (\n <span className={cn(\"inline-block \" + collapsible)}>{item.label}</span>\n )}\n </>\n );\n const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);\n const sizers =\n item.iconOnly || item.content !== undefined\n ? []\n : [...new Set([item.label, ...(item.reserve ?? [])])];\n\n return (\n <span\n className=\"button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]\"\n aria-hidden=\"true\"\n >\n <span ref={contentRef} className=\"inline-grid flex-none items-center justify-items-center\">\n {sizers.map((text) => (\n <span key={text} className={cn(PHASE + \" pointer-events-none invisible\")}>\n {item.icon ? <span className=\"inline-block size-4 flex-none\" /> : null}\n <span className={cn(\"inline-block \" + collapsible)}>{text}</span>\n </span>\n ))}\n {leaving.map((entry) => (\n <LeavingPhase key={entry.id} id={entry.id} onDone={remove}>\n {entry.value}\n </LeavingPhase>\n ))}\n <span\n key={key}\n ref={phaseRef}\n className={cn(\n PHASE + (changed ? \" motion-safe:animate-label-in motion-reduce:animate-fade-in\" : \"\"),\n )}\n >\n {node}\n </span>\n </span>\n </span>\n );\n}\n\nconst inside = (node: HTMLElement, x: number, y: number) => {\n const rect = node.getBoundingClientRect();\n return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;\n};\n/** Segments are found by their data-key, so the group needs no ref per segment. */\nconst segmentsIn = (root: HTMLElement | null) =>\n Array.from(root?.querySelectorAll<HTMLElement>(\":scope > [data-key]\") ?? []);\nconst segmentIn = (root: HTMLElement | null, key: string | null) =>\n key === null ? undefined : segmentsIn(root).find((node) => node.dataset.key === key);\n/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */\nfunction boxOf(group: HTMLElement, node: HTMLElement) {\n const outer = group.getBoundingClientRect();\n const inner = node.getBoundingClientRect();\n // offsetWidth is rounded to whole pixels, so only a real transform (more than a pixel apart) counts as scale.\n const scale =\n group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1\n ? outer.width / group.offsetWidth\n : 1;\n return [\n (inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,\n (inner.top - outer.top) / scale - group.clientTop + group.scrollTop,\n inner.width / scale,\n inner.height / scale,\n ] as const;\n}\nconst inert = (node: HTMLElement) =>\n (node as HTMLButtonElement).disabled || node.getAttribute(\"aria-disabled\") === \"true\";\n\nconst TRAVEL =\n \"translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE = \"opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE_INSTANT = \"opacity var(--duration-instant) var(--ease-settle)\";\n\nconst focusRing =\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)\";\n\n/**\n * Related actions joined into one surface: a shared border and radius, hairline\n * dividers, no gaps. One soft highlight glides between segments under the\n * pointer or keyboard focus, the pressed segment answers in place, and an\n * optional chevron segment opens more actions in a menu aligned to the group's\n * edge.\n *\n * Tab visits every segment in order; the arrow keys along the orientation, Home\n * and End also move between them. A segment's label can change in place (\"Share\"\n * answers \"Copied\") without resizing it, and a label that changes right after its\n * segment is pressed is announced once.\n *\n * @example\n * <ButtonGroup label=\"Document actions\" items={[{ id: \"edit\", label: \"Edit\" }, { id: \"share\", label: \"Share\", reserve: [\"Copied\"] }]} />\n */\nexport function ButtonGroup({\n items,\n menu,\n label,\n variant = \"outline\",\n size = \"md\",\n orientation = \"horizontal\",\n collapseLabels = true,\n isDisabled = false,\n className,\n}: ButtonGroupProps) {\n const root = useRef<HTMLDivElement>(null);\n const highlight = useRef<HTMLSpanElement>(null);\n const vertical = orientation === \"vertical\";\n\n // The highlight sits on the pointer's segment, else the pressed one (touch), else the open menu's chevron, else keyboard focus.\n const [hover, setHover] = useState<string | null>(null);\n const [pressed, setPressed] = useState<string | null>(null);\n const [focused, setFocused] = useState<string | null>(null);\n const [menuOpen, setMenuOpen] = useState(false);\n const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;\n const keys = [...items.map((item) => item.id), ...(menu ? [MENU] : [])];\n const activeIndex = active ? keys.indexOf(active) : -1;\n const inertKey = String(isDisabled) + items.map((item) => (item.isDisabled ? 1 : 0)).join(\"\");\n\n const shown = useRef<string | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const el = highlight.current;\n const group = root.current;\n if (!el || !group) return;\n const reduced = prefersReducedMotion();\n const node = segmentIn(group, active);\n // Hovering a disabled segment shows nothing; focus on one still shows where the keyboard is.\n if (!node || (inert(node) && active !== focused)) {\n if (shown.current) {\n el.style.transition = reduced ? FADE_INSTANT : FADE;\n el.style.opacity = \"0\";\n }\n shown.current = null;\n return;\n }\n const [x, y, width, height] = boxOf(group, node);\n // Arriving from nowhere, it appears in place; only moves between segments travel.\n const arriving = !shown.current || reduced;\n el.style.transition = arriving ? (reduced ? FADE_INSTANT : FADE) : TRAVEL;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n el.style.opacity = \"1\";\n shown.current = active;\n // inertKey re-checks a segment that turns disabled under a still pointer.\n }, [active, inertKey, focused]);\n\n // Segments resize when a label morphs or labels collapse; the highlight stays locked to its segment.\n const keyList = keys.join(\"\\u0000\");\n useEffect(() => {\n const group = root.current;\n const el = highlight.current;\n if (!group || !el || typeof ResizeObserver === \"undefined\") return;\n // Work is deferred a frame: a callback that changes layout inside the observer's own notification reports a loop error.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const node = segmentIn(group, shown.current);\n if (!node) return;\n const [x, y, width, height] = boxOf(group, node);\n el.style.transition = FADE;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n });\n });\n observer.observe(group);\n segmentsIn(group).forEach((node) => observer.observe(node));\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [keyList]);\n\n // Labels collapse to icons when the row overflows, and come back once the container is wide enough for the full row again.\n const [overflowing, setCompact] = useState(false);\n const fullWidth = useRef(0);\n const blockedAt = useRef(0);\n const collapsible =\n collapseLabels &&\n !vertical &&\n items.some((item) => item.icon && !item.iconOnly && item.content === undefined);\n const compact = collapsible && overflowing;\n useEffect(() => {\n const group = root.current;\n const parent = group?.parentElement;\n if (!group || !parent || !collapsible || typeof ResizeObserver === \"undefined\") return;\n const check = () => {\n const box = getComputedStyle(parent);\n const available =\n parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);\n if (group.dataset.compact === undefined) {\n if (group.scrollWidth > group.clientWidth + 1) {\n fullWidth.current = group.scrollWidth + 2;\n blockedAt.current = available;\n setCompact(true);\n }\n } else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);\n };\n // Collapsing changes the layout being observed, so the check runs a frame later (see the highlight observer).\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(check);\n });\n observer.observe(group);\n observer.observe(parent);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [collapsible, keyList]);\n\n // Pressing tracks the segment under the finger, so touch gets the same highlight a pointer gets on hover.\n useEffect(() => {\n if (!pressed) return;\n const release = () => setPressed(null);\n window.addEventListener(\"pointerup\", release);\n window.addEventListener(\"pointercancel\", release);\n return () => {\n window.removeEventListener(\"pointerup\", release);\n window.removeEventListener(\"pointercancel\", release);\n };\n }, [pressed]);\n\n const hit = (event: PointerEvent) => {\n for (const node of segmentsIn(root.current))\n if (inside(node, event.clientX, event.clientY))\n return inert(node) ? null : (node.dataset.key ?? null);\n return null;\n };\n const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"touch\") setHover(hit(event));\n };\n const onPointerLeave = () => setHover(null);\n const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (event.button === 0) setPressed(hit(event));\n };\n\n // Keyboard focus shows the highlight; a mouse click focuses without it, so nothing stays lit after the pointer leaves.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement;\n const key = target.dataset.key;\n if (key === undefined) return;\n let visible = true;\n try {\n visible = target.matches(\":focus-visible\");\n } catch {\n /* older engines */\n }\n setFocused(visible ? key : null);\n };\n const onBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!root.current?.contains(event.relatedTarget)) setFocused(null);\n };\n\n // Tab visits every segment in order; arrow keys along the orientation, Home and End also move between them.\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return;\n // Disabled segments stay focusable (aria-disabled), so the keyboard can find them and hear why nothing happens.\n const list = segmentsIn(root.current).filter((node) => !(node as HTMLButtonElement).disabled);\n const index = list.findIndex((node) => node === document.activeElement);\n if (index < 0) return;\n const rtl = !vertical && getComputedStyle(root.current!).direction === \"rtl\";\n const forward = vertical ? \"ArrowDown\" : rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = vertical ? \"ArrowUp\" : rtl ? \"ArrowRight\" : \"ArrowLeft\";\n const last = list.length - 1;\n const target =\n event.key === forward\n ? index === last\n ? 0\n : index + 1\n : event.key === backward\n ? index === 0\n ? last\n : index - 1\n : event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? last\n : -1;\n if (target < 0) return;\n event.preventDefault();\n list[target]!.focus();\n };\n\n // A label that changes right after its segment is pressed, such as \"Copied\", is announced once; the quiet revert is not.\n const [announcement, setAnnouncement] = useState(\"\");\n const labels = useRef<Map<string, string> | null>(null);\n const activated = useRef({ key: \"\", at: 0 });\n const labelList = items.map((item) => item.id + \"\\u0000\" + item.label).join(\"\\u0001\");\n useEffect(() => {\n const previous = labels.current;\n labels.current = new Map(items.map((item) => [item.id, item.label]));\n if (!previous) return;\n const recent = performance.now() - activated.current.at < 1000;\n const changed = items.filter(\n (item) =>\n recent &&\n item.id === activated.current.key &&\n previous.has(item.id) &&\n previous.get(item.id) !== item.label &&\n item.content === undefined,\n );\n\n if (changed.length) setAnnouncement(changed.map((item) => item.label).join(\", \"));\n // labelList carries the only part of items this effect reads.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [labelList]);\n\n const select = (item: ButtonGroupItem) => {\n activated.current = { key: item.id, at: performance.now() };\n item.onSelect?.();\n };\n const quiet = (index: number) =>\n activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? \"\" : undefined;\n\n const segments = items.map((item, index) => {\n const off = item.isDisabled && !isDisabled;\n const named = item.iconOnly || item.content !== undefined || (compact && !!item.icon);\n const common = {\n className: cn(\"button-group-segment \" + focusRing),\n \"data-key\": item.id,\n \"data-quiet\": quiet(index),\n \"data-icon-only\": item.iconOnly ? \"\" : undefined,\n \"data-collapsible\":\n item.icon && !item.iconOnly && item.content === undefined ? \"\" : undefined,\n \"aria-label\": named ? item.label : undefined,\n title: item.iconOnly || (compact && item.icon) ? item.label : undefined,\n };\n const body = (\n <span className=\"button-group-content\">\n <SegmentContent item={item} />\n </span>\n );\n const name = named ? null : <span className=\"sr-only\">{item.label}</span>;\n if (item.href && !off && !isDisabled)\n return (\n <a key={item.id} {...common} href={item.href} onClick={() => select(item)}>\n {body}\n {name}\n </a>\n );\n return (\n <button\n key={item.id}\n {...common}\n type=\"button\"\n disabled={isDisabled}\n aria-disabled={off || undefined}\n onClick={off ? undefined : () => select(item)}\n >\n {body}\n {name}\n </button>\n );\n });\n\n return (\n <div\n ref={root}\n role=\"group\"\n aria-label={label}\n aria-disabled={isDisabled || undefined}\n data-orientation={orientation}\n data-variant={variant}\n data-size={size}\n data-compact={compact ? \"\" : undefined}\n onPointerMove={onPointerMove}\n onPointerLeave={onPointerLeave}\n onPointerDown={onPointerDown}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n className={cn(\"button-group group/bg\", className)}\n >\n <span\n ref={highlight}\n aria-hidden=\"true\"\n data-pressed={pressed && pressed === active ? \"\" : undefined}\n className=\"button-group-highlight\"\n />\n {segments}\n {menu ? (\n <MenuTrigger onOpenChange={setMenuOpen}>\n <AriaButton\n data-key={MENU}\n data-trigger=\"\"\n data-quiet={quiet(items.length)}\n aria-label={menu.label}\n isDisabled={isDisabled}\n className={cn(\"button-group-segment group/trigger \" + focusRing)}\n >\n <span className=\"button-group-content\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </AriaButton>\n <Menu\n placement={vertical ? \"bottom start\" : \"bottom end\"}\n offset={6}\n shouldFocusWrap\n className={cn(\"menu-motion \" + (vertical ? \"[--menu-ox:left]\" : \"[--menu-ox:right]\"))}\n >\n {menu.items.map((action, index) => (\n <MenuItem\n key={action.id}\n id={action.id}\n textValue={action.label}\n href={action.href}\n isDisabled={action.isDisabled}\n onAction={action.onSelect}\n data-i={Math.min(index, 4)}\n className={cn(\n \"menu-item-motion gap-2.5\" +\n (action.destructive\n ? \" text-destructive-strong data-[focused]:bg-[color-mix(in_oklab,var(--color-destructive-strong)_8%,var(--color-card))]\"\n : \"\"),\n )}\n >\n {action.icon ? <span aria-hidden=\"true\">{action.icon}</span> : null}\n {action.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { ButtonGroup, ButtonGroupItem, ButtonGroupMenu, ButtonGroupMenuItem, ButtonGroupOrientation, ButtonGroupProps, ButtonGroupSize, ButtonGroupVariant } from "./button-group.js";
|
|
2
|
+
export { ButtonGroup, type ButtonGroupItem, type ButtonGroupMenu, type ButtonGroupMenuItem, type ButtonGroupOrientation, type ButtonGroupProps, type ButtonGroupSize, type ButtonGroupVariant };
|
|
@@ -50,7 +50,7 @@ export declare const calendarClearVariants: (props?: import("class-variance-auth
|
|
|
50
50
|
* is a strikethrough.
|
|
51
51
|
*/
|
|
52
52
|
export declare const calendarCellVariants: (props?: ({
|
|
53
|
-
selection?: "single" | "range" | null | undefined;
|
|
53
|
+
selection?: "single" | "range" | "band" | null | undefined;
|
|
54
54
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
55
55
|
interface CalendarSharedProps {
|
|
56
56
|
/**
|
|
@@ -138,7 +138,7 @@ export interface CalendarRangeProps extends Omit<RangeCalendarProps<DateValue>,
|
|
|
138
138
|
* @example
|
|
139
139
|
* <Calendar label="Report date" value={date} onChange={setDate} clearable />
|
|
140
140
|
*/
|
|
141
|
-
declare
|
|
141
|
+
declare const Calendar$1: import("react").ForwardRefExoticComponent<CalendarProps$1 & import("react").RefAttributes<HTMLDivElement>>;
|
|
142
142
|
/**
|
|
143
143
|
* A calendar for picking a contiguous date range.
|
|
144
144
|
*
|
|
@@ -148,7 +148,7 @@ declare function Calendar$1({ className, label, labelHidden, visibleMonths, clea
|
|
|
148
148
|
* <CalendarRange label="Period" visibleMonths={3}
|
|
149
149
|
* value={range} onChange={setRange} clearable />
|
|
150
150
|
*/
|
|
151
|
-
export declare
|
|
151
|
+
export declare const CalendarRange: import("react").ForwardRefExoticComponent<CalendarRangeProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
152
152
|
//#endregion
|
|
153
153
|
export { Calendar$1 as Calendar, CalendarProps$1 as CalendarProps };
|
|
154
154
|
//# sourceMappingURL=calendar.d.ts.map
|