beez-ui 2.3.1 → 2.5.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/CHANGELOG.md +17 -0
- package/README.md +4 -3
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/components/action-button.d.ts +25 -0
- package/dist/components/action-button.js +264 -0
- package/dist/components/button-label.js +5 -124
- package/dist/components/field.d.ts +51 -0
- package/dist/components/field.js +700 -0
- package/dist/components/input-group.js +3 -3
- package/dist/components/input.d.ts +16 -2
- package/dist/components/input.js +117 -10
- package/dist/components/morph-text.d.ts +14 -0
- package/dist/components/morph-text.js +210 -0
- package/dist/components/split-button.d.ts +36 -0
- package/dist/components/split-button.js +306 -0
- package/dist/components/textarea.d.ts +13 -5
- package/dist/components/textarea.js +107 -12
- package/dist/constants/action-button.d.ts +20 -0
- package/dist/constants/action-button.js +17 -0
- package/dist/constants/field.d.ts +24 -0
- package/dist/constants/field.js +24 -0
- package/dist/constants/morph-text.d.ts +13 -0
- package/dist/constants/morph-text.js +13 -0
- package/dist/constants/split-button.d.ts +7 -0
- package/dist/constants/split-button.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/motion/arc-swap.d.ts +22 -0
- package/dist/motion/arc-swap.js +49 -0
- package/dist/motion/arc-tokens.d.ts +11 -0
- package/dist/motion/arc-tokens.js +9 -0
- package/dist/motion/use-morph-width.d.ts +13 -0
- package/dist/motion/use-morph-width.js +59 -0
- package/dist/styles.css +1 -1
- package/package.json +11 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as _c } from "react/compiler-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Arc's SplitButton (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md) built from beez-ui's `Button` and
|
|
5
|
+
* `DropdownMenu`: a main action whose icon and label morph in place, joined to a menu of related actions. The menu
|
|
6
|
+
* half never scales on press, so the menu opens from a still anchor.
|
|
7
|
+
*/
|
|
8
|
+
import { isValidElement, useRef } from "react";
|
|
9
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
10
|
+
import { ChevronDown } from "lucide-react";
|
|
11
|
+
import { SPLIT_BUTTON_SIZE_BY_VARIANT } from "../constants/split-button.js";
|
|
12
|
+
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
13
|
+
import { cn } from "../lib/utils.js";
|
|
14
|
+
import { ARC_ICON_ENTER as iconEnter, ARC_ICON_EXIT as iconExit, ARC_ICON_TRANSITION as iconTransition, ARC_REDUCED_ENTER as reducedEnter, ARC_REDUCED_EXIT as reducedExit, ARC_REDUCED_TRANSITION as reducedTransition, ARC_SWAP_REST as restState } from "../motion/arc-swap.js";
|
|
15
|
+
import { useMorphWidth } from "../motion/use-morph-width.js";
|
|
16
|
+
import { Button } from "./button.js";
|
|
17
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "./dropdown-menu.js";
|
|
18
|
+
import { MorphText } from "./morph-text.js";
|
|
19
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
|
+
/**
|
|
21
|
+
* Names an icon element, so swapping Copy for Check morphs while a re-render of the same icon stays still.
|
|
22
|
+
* @param icon - Icon node.
|
|
23
|
+
* @returns Element name, the text of a text node, or an empty string.
|
|
24
|
+
*/
|
|
25
|
+
function getIconKey(icon) {
|
|
26
|
+
if (!isValidElement(icon)) return icon == null || typeof icon === "boolean" ? "" : String(icon);
|
|
27
|
+
const elementType = icon.type;
|
|
28
|
+
return typeof elementType === "string" ? elementType : elementType?.displayName ?? elementType?.name ?? "icon";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Visible content of the main action: the icon and the morphing label, inside a slot whose width follows on a
|
|
32
|
+
* spring when either changes.
|
|
33
|
+
* @param props - Label, icon and whether the user prefers reduced motion.
|
|
34
|
+
* @returns Content hidden from assistive tech; the button names itself through `aria-label`.
|
|
35
|
+
*/
|
|
36
|
+
function SplitButtonMainContent(t0) {
|
|
37
|
+
const $ = _c(17);
|
|
38
|
+
const { label, icon, reduceMotion } = t0;
|
|
39
|
+
const contentRef = useRef(null);
|
|
40
|
+
let t1;
|
|
41
|
+
if ($[0] !== icon) {
|
|
42
|
+
t1 = getIconKey(icon);
|
|
43
|
+
$[0] = icon;
|
|
44
|
+
$[1] = t1;
|
|
45
|
+
} else {
|
|
46
|
+
t1 = $[1];
|
|
47
|
+
}
|
|
48
|
+
const iconKey = t1;
|
|
49
|
+
const width = useMorphWidth(contentRef, `${iconKey}|${label}`, reduceMotion);
|
|
50
|
+
let t2;
|
|
51
|
+
if ($[2] !== width) {
|
|
52
|
+
t2 = { width };
|
|
53
|
+
$[2] = width;
|
|
54
|
+
$[3] = t2;
|
|
55
|
+
} else {
|
|
56
|
+
t2 = $[3];
|
|
57
|
+
}
|
|
58
|
+
let t3;
|
|
59
|
+
if ($[4] !== icon || $[5] !== iconKey || $[6] !== reduceMotion) {
|
|
60
|
+
t3 = icon ? /* @__PURE__ */ _jsx("span", {
|
|
61
|
+
className: "inline-grid flex-none place-items-center",
|
|
62
|
+
children: /* @__PURE__ */ _jsx(AnimatePresence, {
|
|
63
|
+
initial: false,
|
|
64
|
+
children: /* @__PURE__ */ _jsx(motion.span, {
|
|
65
|
+
className: "col-start-1 row-start-1 inline-flex items-center justify-center",
|
|
66
|
+
initial: reduceMotion ? reducedEnter : iconEnter,
|
|
67
|
+
animate: restState,
|
|
68
|
+
exit: reduceMotion ? reducedExit : iconExit,
|
|
69
|
+
transition: reduceMotion ? reducedTransition : iconTransition,
|
|
70
|
+
children: icon
|
|
71
|
+
}, iconKey)
|
|
72
|
+
})
|
|
73
|
+
}) : null;
|
|
74
|
+
$[4] = icon;
|
|
75
|
+
$[5] = iconKey;
|
|
76
|
+
$[6] = reduceMotion;
|
|
77
|
+
$[7] = t3;
|
|
78
|
+
} else {
|
|
79
|
+
t3 = $[7];
|
|
80
|
+
}
|
|
81
|
+
let t4;
|
|
82
|
+
if ($[8] !== label || $[9] !== reduceMotion) {
|
|
83
|
+
t4 = /* @__PURE__ */ _jsx(MorphText, {
|
|
84
|
+
text: label,
|
|
85
|
+
reduceMotion
|
|
86
|
+
});
|
|
87
|
+
$[8] = label;
|
|
88
|
+
$[9] = reduceMotion;
|
|
89
|
+
$[10] = t4;
|
|
90
|
+
} else {
|
|
91
|
+
t4 = $[10];
|
|
92
|
+
}
|
|
93
|
+
let t5;
|
|
94
|
+
if ($[11] !== t3 || $[12] !== t4) {
|
|
95
|
+
t5 = /* @__PURE__ */ _jsxs("span", {
|
|
96
|
+
ref: contentRef,
|
|
97
|
+
className: "inline-flex flex-none items-center gap-2 whitespace-nowrap",
|
|
98
|
+
children: [t3, t4]
|
|
99
|
+
});
|
|
100
|
+
$[11] = t3;
|
|
101
|
+
$[12] = t4;
|
|
102
|
+
$[13] = t5;
|
|
103
|
+
} else {
|
|
104
|
+
t5 = $[13];
|
|
105
|
+
}
|
|
106
|
+
let t6;
|
|
107
|
+
if ($[14] !== t2 || $[15] !== t5) {
|
|
108
|
+
t6 = /* @__PURE__ */ _jsx(motion.span, {
|
|
109
|
+
"data-slot": "split-button-label",
|
|
110
|
+
className: "inline-flex min-w-0 items-center data-morphing:[clip-path:inset(-50%_calc(var(--spacing)*-2))]",
|
|
111
|
+
style: t2,
|
|
112
|
+
"aria-hidden": "true",
|
|
113
|
+
children: t5
|
|
114
|
+
});
|
|
115
|
+
$[14] = t2;
|
|
116
|
+
$[15] = t5;
|
|
117
|
+
$[16] = t6;
|
|
118
|
+
} else {
|
|
119
|
+
t6 = $[16];
|
|
120
|
+
}
|
|
121
|
+
return t6;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A main action joined to a menu of related actions, such as Copy page with Copy as Markdown and Open in a new tab.
|
|
125
|
+
* The main half announces label changes politely; the menu half opens a `DropdownMenu` aligned to its end.
|
|
126
|
+
* @param props - Main action, its icon, the menu actions, the variant and accessible names.
|
|
127
|
+
* @returns The joined pair of buttons and their menu.
|
|
128
|
+
*/
|
|
129
|
+
function SplitButton(t0) {
|
|
130
|
+
const $ = _c(39);
|
|
131
|
+
const { label, actions, onClick, disabled, icon, variant: t1, menuLabel, className } = t0;
|
|
132
|
+
const variant = t1 === undefined ? "primary" : t1;
|
|
133
|
+
const reduceMotion = usePrefersReducedMotion();
|
|
134
|
+
const size = SPLIT_BUTTON_SIZE_BY_VARIANT[variant];
|
|
135
|
+
let t2;
|
|
136
|
+
if ($[0] !== className) {
|
|
137
|
+
t2 = cn("inline-flex items-stretch", className);
|
|
138
|
+
$[0] = className;
|
|
139
|
+
$[1] = t2;
|
|
140
|
+
} else {
|
|
141
|
+
t2 = $[1];
|
|
142
|
+
}
|
|
143
|
+
let t3;
|
|
144
|
+
if ($[2] !== icon || $[3] !== label || $[4] !== reduceMotion) {
|
|
145
|
+
t3 = /* @__PURE__ */ _jsx(SplitButtonMainContent, {
|
|
146
|
+
label,
|
|
147
|
+
icon,
|
|
148
|
+
reduceMotion
|
|
149
|
+
});
|
|
150
|
+
$[2] = icon;
|
|
151
|
+
$[3] = label;
|
|
152
|
+
$[4] = reduceMotion;
|
|
153
|
+
$[5] = t3;
|
|
154
|
+
} else {
|
|
155
|
+
t3 = $[5];
|
|
156
|
+
}
|
|
157
|
+
let t4;
|
|
158
|
+
if ($[6] !== disabled || $[7] !== label || $[8] !== onClick || $[9] !== size || $[10] !== t3 || $[11] !== variant) {
|
|
159
|
+
t4 = /* @__PURE__ */ _jsx(Button, {
|
|
160
|
+
variant,
|
|
161
|
+
size,
|
|
162
|
+
"aria-label": label,
|
|
163
|
+
className: "rounded-r-none border-r-0",
|
|
164
|
+
disabled,
|
|
165
|
+
onClick,
|
|
166
|
+
children: t3
|
|
167
|
+
});
|
|
168
|
+
$[6] = disabled;
|
|
169
|
+
$[7] = label;
|
|
170
|
+
$[8] = onClick;
|
|
171
|
+
$[9] = size;
|
|
172
|
+
$[10] = t3;
|
|
173
|
+
$[11] = variant;
|
|
174
|
+
$[12] = t4;
|
|
175
|
+
} else {
|
|
176
|
+
t4 = $[12];
|
|
177
|
+
}
|
|
178
|
+
const t5 = menuLabel ?? `Más acciones de ${label}`;
|
|
179
|
+
const t6 = variant === "primary" ? "border-l-[color-mix(in_oklab,var(--background)_18%,var(--foreground))]" : "w-(--control-height-sm) border-l-border";
|
|
180
|
+
let t7;
|
|
181
|
+
if ($[13] !== t6) {
|
|
182
|
+
t7 = cn("w-(--control-height-md) rounded-l-none px-0 [&_svg]:transition-transform [&_svg]:duration-(--duration-fast) data-[state=open]:[&_svg]:rotate-180 motion-reduce:[&_svg]:transition-none", t6);
|
|
183
|
+
$[13] = t6;
|
|
184
|
+
$[14] = t7;
|
|
185
|
+
} else {
|
|
186
|
+
t7 = $[14];
|
|
187
|
+
}
|
|
188
|
+
let t8;
|
|
189
|
+
if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
|
|
190
|
+
t8 = /* @__PURE__ */ _jsx(ChevronDown, {
|
|
191
|
+
strokeWidth: 1.75,
|
|
192
|
+
"aria-hidden": "true"
|
|
193
|
+
});
|
|
194
|
+
$[15] = t8;
|
|
195
|
+
} else {
|
|
196
|
+
t8 = $[15];
|
|
197
|
+
}
|
|
198
|
+
let t9;
|
|
199
|
+
if ($[16] !== size || $[17] !== t5 || $[18] !== t7 || $[19] !== variant) {
|
|
200
|
+
t9 = /* @__PURE__ */ _jsx(Button, {
|
|
201
|
+
variant,
|
|
202
|
+
size,
|
|
203
|
+
"aria-label": t5,
|
|
204
|
+
className: t7,
|
|
205
|
+
children: t8
|
|
206
|
+
});
|
|
207
|
+
$[16] = size;
|
|
208
|
+
$[17] = t5;
|
|
209
|
+
$[18] = t7;
|
|
210
|
+
$[19] = variant;
|
|
211
|
+
$[20] = t9;
|
|
212
|
+
} else {
|
|
213
|
+
t9 = $[20];
|
|
214
|
+
}
|
|
215
|
+
let t10;
|
|
216
|
+
if ($[21] !== disabled || $[22] !== t9) {
|
|
217
|
+
t10 = /* @__PURE__ */ _jsx(DropdownMenuTrigger, {
|
|
218
|
+
asChild: true,
|
|
219
|
+
disabled,
|
|
220
|
+
children: t9
|
|
221
|
+
});
|
|
222
|
+
$[21] = disabled;
|
|
223
|
+
$[22] = t9;
|
|
224
|
+
$[23] = t10;
|
|
225
|
+
} else {
|
|
226
|
+
t10 = $[23];
|
|
227
|
+
}
|
|
228
|
+
let t11;
|
|
229
|
+
if ($[24] !== label) {
|
|
230
|
+
t11 = /* @__PURE__ */ _jsx("span", {
|
|
231
|
+
className: "sr-only",
|
|
232
|
+
"aria-live": "polite",
|
|
233
|
+
children: label
|
|
234
|
+
});
|
|
235
|
+
$[24] = label;
|
|
236
|
+
$[25] = t11;
|
|
237
|
+
} else {
|
|
238
|
+
t11 = $[25];
|
|
239
|
+
}
|
|
240
|
+
let t12;
|
|
241
|
+
if ($[26] !== t10 || $[27] !== t11 || $[28] !== t2 || $[29] !== t4 || $[30] !== variant) {
|
|
242
|
+
t12 = /* @__PURE__ */ _jsxs("div", {
|
|
243
|
+
role: "group",
|
|
244
|
+
"data-slot": "split-button",
|
|
245
|
+
"data-variant": variant,
|
|
246
|
+
className: t2,
|
|
247
|
+
children: [
|
|
248
|
+
t4,
|
|
249
|
+
t10,
|
|
250
|
+
t11
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
$[26] = t10;
|
|
254
|
+
$[27] = t11;
|
|
255
|
+
$[28] = t2;
|
|
256
|
+
$[29] = t4;
|
|
257
|
+
$[30] = variant;
|
|
258
|
+
$[31] = t12;
|
|
259
|
+
} else {
|
|
260
|
+
t12 = $[31];
|
|
261
|
+
}
|
|
262
|
+
let t13;
|
|
263
|
+
if ($[32] !== actions) {
|
|
264
|
+
t13 = actions.map(_temp);
|
|
265
|
+
$[32] = actions;
|
|
266
|
+
$[33] = t13;
|
|
267
|
+
} else {
|
|
268
|
+
t13 = $[33];
|
|
269
|
+
}
|
|
270
|
+
let t14;
|
|
271
|
+
if ($[34] !== t13) {
|
|
272
|
+
t14 = /* @__PURE__ */ _jsx(DropdownMenuContent, {
|
|
273
|
+
align: "end",
|
|
274
|
+
sideOffset: 4,
|
|
275
|
+
collisionPadding: 12,
|
|
276
|
+
loop: true,
|
|
277
|
+
children: t13
|
|
278
|
+
});
|
|
279
|
+
$[34] = t13;
|
|
280
|
+
$[35] = t14;
|
|
281
|
+
} else {
|
|
282
|
+
t14 = $[35];
|
|
283
|
+
}
|
|
284
|
+
let t15;
|
|
285
|
+
if ($[36] !== t12 || $[37] !== t14) {
|
|
286
|
+
t15 = /* @__PURE__ */ _jsxs(DropdownMenu, { children: [t12, t14] });
|
|
287
|
+
$[36] = t12;
|
|
288
|
+
$[37] = t14;
|
|
289
|
+
$[38] = t15;
|
|
290
|
+
} else {
|
|
291
|
+
t15 = $[38];
|
|
292
|
+
}
|
|
293
|
+
return t15;
|
|
294
|
+
}
|
|
295
|
+
function _temp(action) {
|
|
296
|
+
return /* @__PURE__ */ _jsxs(DropdownMenuItem, {
|
|
297
|
+
variant: action.destructive ? "destructive" : "default",
|
|
298
|
+
disabled: action.disabled,
|
|
299
|
+
onSelect: action.onSelect,
|
|
300
|
+
children: [action.icon ? /* @__PURE__ */ _jsx("span", {
|
|
301
|
+
"aria-hidden": "true",
|
|
302
|
+
children: action.icon
|
|
303
|
+
}) : null, action.label]
|
|
304
|
+
}, action.label);
|
|
305
|
+
}
|
|
306
|
+
export { SplitButton };
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Arc's Textarea (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): a multi-line field on Arc's tokens with
|
|
3
|
+
* a 110px minimum height and a vertical resize handle. With a `label`, `description` or `error` it renders Arc's
|
|
4
|
+
* labelled field, where a live count in the description rolls its digits; without them it renders the bare control.
|
|
6
5
|
*/
|
|
7
|
-
|
|
6
|
+
import type { ComponentProps } from "react";
|
|
7
|
+
import type { FieldCopyProps } from "./field.js";
|
|
8
|
+
export type TextareaProps = ComponentProps<"textarea"> & FieldCopyProps;
|
|
9
|
+
/**
|
|
10
|
+
* Free-form multi-line text, controlled or uncontrolled like a native textarea.
|
|
11
|
+
* @param textareaProps - Optional field copy plus every native textarea attribute, including `ref`, `rows` and `maxLength`.
|
|
12
|
+
* @returns The labelled field when `label`, `description` or `error` is passed (even as `undefined`), otherwise the
|
|
13
|
+
* bare control.
|
|
14
|
+
*/
|
|
15
|
+
declare function Textarea(textareaProps: TextareaProps): import("react").JSX.Element;
|
|
8
16
|
export { Textarea };
|
|
@@ -1,20 +1,115 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as _c } from "react/compiler-runtime";
|
|
2
3
|
import { MotionSlot } from "../motion/motion-slot.js";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { Field, usesFieldCopy, useFieldAria } from "./field.js";
|
|
4
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
/** Same focus language as `Input`: the border answers alone and the box never changes size. */
|
|
8
|
+
const textareaClassName = "flex min-h-[110px] w-full resize-y rounded-(--radius-control) border border-border-strong bg-surface p-(--space-3) font-sans text-base leading-(--leading-body) tracking-(--tracking-body) text-foreground outline-none transition-[color,background-color,border-color] duration-(--duration-fast) ease-(--ease-standard) motion-reduce:transition-none placeholder:text-text-muted hover-fine:not-disabled:not-aria-invalid:border-foreground focus-visible:border-foreground aria-invalid:border-danger disabled:cursor-not-allowed disabled:bg-surface-muted disabled:opacity-60 md:text-sm";
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @param
|
|
8
|
-
* @returns The
|
|
10
|
+
* Free-form multi-line text, controlled or uncontrolled like a native textarea.
|
|
11
|
+
* @param textareaProps - Optional field copy plus every native textarea attribute, including `ref`, `rows` and `maxLength`.
|
|
12
|
+
* @returns The labelled field when `label`, `description` or `error` is passed (even as `undefined`), otherwise the
|
|
13
|
+
* bare control.
|
|
9
14
|
*/
|
|
10
|
-
function Textarea(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
function Textarea(textareaProps) {
|
|
16
|
+
const $ = _c(26);
|
|
17
|
+
let className;
|
|
18
|
+
let description;
|
|
19
|
+
let error;
|
|
20
|
+
let id;
|
|
21
|
+
let label;
|
|
22
|
+
let props;
|
|
23
|
+
if ($[0] !== textareaProps) {
|
|
24
|
+
({className, label, description, error, id, ...props} = textareaProps);
|
|
25
|
+
$[0] = textareaProps;
|
|
26
|
+
$[1] = className;
|
|
27
|
+
$[2] = description;
|
|
28
|
+
$[3] = error;
|
|
29
|
+
$[4] = id;
|
|
30
|
+
$[5] = label;
|
|
31
|
+
$[6] = props;
|
|
32
|
+
} else {
|
|
33
|
+
className = $[1];
|
|
34
|
+
description = $[2];
|
|
35
|
+
error = $[3];
|
|
36
|
+
id = $[4];
|
|
37
|
+
label = $[5];
|
|
38
|
+
props = $[6];
|
|
39
|
+
}
|
|
40
|
+
let t0;
|
|
41
|
+
if ($[7] !== description || $[8] !== error || $[9] !== id || $[10] !== props) {
|
|
42
|
+
t0 = {
|
|
43
|
+
id,
|
|
44
|
+
description,
|
|
45
|
+
error,
|
|
16
46
|
...props
|
|
17
|
-
}
|
|
18
|
-
|
|
47
|
+
};
|
|
48
|
+
$[7] = description;
|
|
49
|
+
$[8] = error;
|
|
50
|
+
$[9] = id;
|
|
51
|
+
$[10] = props;
|
|
52
|
+
$[11] = t0;
|
|
53
|
+
} else {
|
|
54
|
+
t0 = $[11];
|
|
55
|
+
}
|
|
56
|
+
const aria = useFieldAria(t0);
|
|
57
|
+
const isField = usesFieldCopy(textareaProps);
|
|
58
|
+
let t1;
|
|
59
|
+
if ($[12] !== className) {
|
|
60
|
+
t1 = cn(textareaClassName, className);
|
|
61
|
+
$[12] = className;
|
|
62
|
+
$[13] = t1;
|
|
63
|
+
} else {
|
|
64
|
+
t1 = $[13];
|
|
65
|
+
}
|
|
66
|
+
const t2 = isField ? aria.controlId : id;
|
|
67
|
+
const t3 = aria["aria-describedby"];
|
|
68
|
+
const t4 = aria["aria-invalid"];
|
|
69
|
+
let t5;
|
|
70
|
+
if ($[14] !== props || $[15] !== t1 || $[16] !== t2 || $[17] !== t3 || $[18] !== t4) {
|
|
71
|
+
t5 = /* @__PURE__ */ _jsx(MotionSlot, {
|
|
72
|
+
kind: "field",
|
|
73
|
+
children: /* @__PURE__ */ _jsx("textarea", {
|
|
74
|
+
"data-slot": "textarea",
|
|
75
|
+
className: t1,
|
|
76
|
+
...props,
|
|
77
|
+
id: t2,
|
|
78
|
+
"aria-describedby": t3,
|
|
79
|
+
"aria-invalid": t4
|
|
80
|
+
})
|
|
81
|
+
});
|
|
82
|
+
$[14] = props;
|
|
83
|
+
$[15] = t1;
|
|
84
|
+
$[16] = t2;
|
|
85
|
+
$[17] = t3;
|
|
86
|
+
$[18] = t4;
|
|
87
|
+
$[19] = t5;
|
|
88
|
+
} else {
|
|
89
|
+
t5 = $[19];
|
|
90
|
+
}
|
|
91
|
+
const control = t5;
|
|
92
|
+
if (!isField) {
|
|
93
|
+
return control;
|
|
94
|
+
}
|
|
95
|
+
let t6;
|
|
96
|
+
if ($[20] !== aria || $[21] !== control || $[22] !== description || $[23] !== error || $[24] !== label) {
|
|
97
|
+
t6 = /* @__PURE__ */ _jsx(Field, {
|
|
98
|
+
label,
|
|
99
|
+
description,
|
|
100
|
+
error,
|
|
101
|
+
aria,
|
|
102
|
+
children: control
|
|
103
|
+
});
|
|
104
|
+
$[20] = aria;
|
|
105
|
+
$[21] = control;
|
|
106
|
+
$[22] = description;
|
|
107
|
+
$[23] = error;
|
|
108
|
+
$[24] = label;
|
|
109
|
+
$[25] = t6;
|
|
110
|
+
} else {
|
|
111
|
+
t6 = $[25];
|
|
112
|
+
}
|
|
113
|
+
return t6;
|
|
19
114
|
}
|
|
20
115
|
export { Textarea };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Tuning of `ActionButton`: its async states, reset delay and the icon that follows each state. */
|
|
2
|
+
/** Phases an action goes through: ready to run, running, and finished successfully. */
|
|
3
|
+
export type ActionButtonState = "idle" | "pending" | "success";
|
|
4
|
+
/** Value of each phase, exposed on the button as `data-action-state`, apart from `data-state`, which marks popup triggers that never press. */
|
|
5
|
+
export declare const ACTION_BUTTON_STATE: {
|
|
6
|
+
readonly idle: "idle";
|
|
7
|
+
readonly pending: "pending";
|
|
8
|
+
readonly success: "success";
|
|
9
|
+
};
|
|
10
|
+
/** Time the success state stays on the button before it returns to idle on its own. */
|
|
11
|
+
export declare const ACTION_BUTTON_DEFAULT_RESET_AFTER_MS = 2400;
|
|
12
|
+
/** Side, in pixels, of the slot that swaps the arrow, the spinner and the check. */
|
|
13
|
+
export declare const ACTION_BUTTON_ICON_SIZE_PX = 17;
|
|
14
|
+
/** Horizontal offsets, in pixels, of the arrow returning from behind and leaving forward. */
|
|
15
|
+
export declare const ACTION_BUTTON_ARROW_OFFSET_PX: {
|
|
16
|
+
readonly enter: -6;
|
|
17
|
+
readonly exit: 8;
|
|
18
|
+
};
|
|
19
|
+
/** Delay, in seconds, before the success check starts drawing itself. */
|
|
20
|
+
export declare const ACTION_BUTTON_CHECK_DRAW_DELAY_S = 0.05;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Value of each phase, exposed on the button as `data-action-state`, apart from `data-state`, which marks popup triggers that never press. */
|
|
2
|
+
export const ACTION_BUTTON_STATE = {
|
|
3
|
+
idle: "idle",
|
|
4
|
+
pending: "pending",
|
|
5
|
+
success: "success"
|
|
6
|
+
};
|
|
7
|
+
/** Time the success state stays on the button before it returns to idle on its own. */
|
|
8
|
+
export const ACTION_BUTTON_DEFAULT_RESET_AFTER_MS = 2400;
|
|
9
|
+
/** Side, in pixels, of the slot that swaps the arrow, the spinner and the check. */
|
|
10
|
+
export const ACTION_BUTTON_ICON_SIZE_PX = 17;
|
|
11
|
+
/** Horizontal offsets, in pixels, of the arrow returning from behind and leaving forward. */
|
|
12
|
+
export const ACTION_BUTTON_ARROW_OFFSET_PX = {
|
|
13
|
+
enter: -6,
|
|
14
|
+
exit: 8
|
|
15
|
+
};
|
|
16
|
+
/** Delay, in seconds, before the success check starts drawing itself. */
|
|
17
|
+
export const ACTION_BUTTON_CHECK_DRAW_DELAY_S = .05;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Tuning of the internal labelled field shared by `Input` and `Textarea`. */
|
|
2
|
+
/** Vertical offsets, in em, of changed message words rising in and lifting out. */
|
|
3
|
+
export declare const FIELD_MESSAGE_WORD_OFFSET_EM: {
|
|
4
|
+
readonly enter: 0.35;
|
|
5
|
+
readonly exit: -0.35;
|
|
6
|
+
};
|
|
7
|
+
/** Vertical travel, in em, of a digit rolling in or out of a count in the message. */
|
|
8
|
+
export declare const FIELD_MESSAGE_DIGIT_OFFSET_EM = 0.6;
|
|
9
|
+
/** A message word that rolls its digits instead of rising in: counts, decimals, ratios, times and percentages. */
|
|
10
|
+
export declare const FIELD_MESSAGE_NUMBER_PATTERN: RegExp;
|
|
11
|
+
/**
|
|
12
|
+
* Grouping separators removed before comparing two counts to pick the roll direction: a dot or comma followed by
|
|
13
|
+
* exactly three digits, so both `1.000` (es) and `1,000` (en) read as a thousand while `1,5` stays a decimal.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FIELD_MESSAGE_GROUP_SEPARATOR_PATTERN: RegExp;
|
|
16
|
+
/** Separators between the components of a time (`0:09`) or a ratio (`3/10`), compared one by one. */
|
|
17
|
+
export declare const FIELD_MESSAGE_COMPONENT_SEPARATOR_PATTERN: RegExp;
|
|
18
|
+
/** Decimal comma of locales such as Spanish, read as a decimal point once the grouping separators are gone. */
|
|
19
|
+
export declare const FIELD_MESSAGE_DECIMAL_COMMA = ",";
|
|
20
|
+
/** Suffixes of the ids that tie the helper and error rows to the control through `aria-describedby`. */
|
|
21
|
+
export declare const FIELD_MESSAGE_ID_SUFFIX: {
|
|
22
|
+
readonly description: "description";
|
|
23
|
+
readonly error: "error";
|
|
24
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Tuning of the internal labelled field shared by `Input` and `Textarea`. */
|
|
2
|
+
/** Vertical offsets, in em, of changed message words rising in and lifting out. */
|
|
3
|
+
export const FIELD_MESSAGE_WORD_OFFSET_EM = {
|
|
4
|
+
enter: .35,
|
|
5
|
+
exit: -.35
|
|
6
|
+
};
|
|
7
|
+
/** Vertical travel, in em, of a digit rolling in or out of a count in the message. */
|
|
8
|
+
export const FIELD_MESSAGE_DIGIT_OFFSET_EM = .6;
|
|
9
|
+
/** A message word that rolls its digits instead of rising in: counts, decimals, ratios, times and percentages. */
|
|
10
|
+
export const FIELD_MESSAGE_NUMBER_PATTERN = /^\d[\d.,/:]*%?$/;
|
|
11
|
+
/**
|
|
12
|
+
* Grouping separators removed before comparing two counts to pick the roll direction: a dot or comma followed by
|
|
13
|
+
* exactly three digits, so both `1.000` (es) and `1,000` (en) read as a thousand while `1,5` stays a decimal.
|
|
14
|
+
*/
|
|
15
|
+
export const FIELD_MESSAGE_GROUP_SEPARATOR_PATTERN = /[.,](?=\d{3}(?!\d))/g;
|
|
16
|
+
/** Separators between the components of a time (`0:09`) or a ratio (`3/10`), compared one by one. */
|
|
17
|
+
export const FIELD_MESSAGE_COMPONENT_SEPARATOR_PATTERN = /[:/]/;
|
|
18
|
+
/** Decimal comma of locales such as Spanish, read as a decimal point once the grouping separators are gone. */
|
|
19
|
+
export const FIELD_MESSAGE_DECIMAL_COMMA = ",";
|
|
20
|
+
/** Suffixes of the ids that tie the helper and error rows to the control through `aria-describedby`. */
|
|
21
|
+
export const FIELD_MESSAGE_ID_SUFFIX = {
|
|
22
|
+
description: "description",
|
|
23
|
+
error: "error"
|
|
24
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Tuning of the internal letter-by-letter label morph shared by `ActionButton` and `SplitButton`. */
|
|
2
|
+
/** Longest entrance delay a single letter gets, so long labels still finish their morph quickly. */
|
|
3
|
+
export declare const MORPH_TEXT_MAX_GLYPH_DELAY_S = 0.1;
|
|
4
|
+
/**
|
|
5
|
+
* Shared leading or trailing runs shorter than this are replaced instead of kept, so a one-letter coincidence does
|
|
6
|
+
* not leave a lonely letter standing while the rest of the label morphs.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MORPH_TEXT_MIN_SHARED_RUN = 2;
|
|
9
|
+
/** Vertical offsets, in pixels, of letters rising in and lifting out of the label. */
|
|
10
|
+
export declare const MORPH_TEXT_GLYPH_OFFSET_PX: {
|
|
11
|
+
readonly enter: 5;
|
|
12
|
+
readonly exit: -4;
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Tuning of the internal letter-by-letter label morph shared by `ActionButton` and `SplitButton`. */
|
|
2
|
+
/** Longest entrance delay a single letter gets, so long labels still finish their morph quickly. */
|
|
3
|
+
export const MORPH_TEXT_MAX_GLYPH_DELAY_S = .1;
|
|
4
|
+
/**
|
|
5
|
+
* Shared leading or trailing runs shorter than this are replaced instead of kept, so a one-letter coincidence does
|
|
6
|
+
* not leave a lonely letter standing while the rest of the label morphs.
|
|
7
|
+
*/
|
|
8
|
+
export const MORPH_TEXT_MIN_SHARED_RUN = 2;
|
|
9
|
+
/** Vertical offsets, in pixels, of letters rising in and lifting out of the label. */
|
|
10
|
+
export const MORPH_TEXT_GLYPH_OFFSET_PX = {
|
|
11
|
+
enter: 5,
|
|
12
|
+
exit: -4
|
|
13
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** `primary` for the main action of a surface, `secondary` for a quieter, smaller one. */
|
|
2
|
+
export type SplitButtonVariant = "primary" | "secondary";
|
|
3
|
+
/** Height of both halves for each variant: the secondary split button is compact, as in Arc. */
|
|
4
|
+
export declare const SPLIT_BUTTON_SIZE_BY_VARIANT: {
|
|
5
|
+
readonly primary: "md";
|
|
6
|
+
readonly secondary: "sm";
|
|
7
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { useTheme } from "next-themes";
|
|
|
3
3
|
export { Link, type LinkProps } from "./components/link.js";
|
|
4
4
|
export { BeezUIProvider, type BeezUIProviderProps } from "./providers/native-provider.js";
|
|
5
5
|
export type { BeezUIComponents, BeezImageProps, BeezLinkProps } from "./providers/beez-ui-provider.js";
|
|
6
|
+
export * from "./components/action-button.js";
|
|
6
7
|
export * from "./components/alert-dialog.js";
|
|
7
8
|
export * from "./components/avatar.js";
|
|
8
9
|
export * from "./components/badge.js";
|
|
@@ -21,6 +22,7 @@ export * from "./components/separator.js";
|
|
|
21
22
|
export * from "./components/sheet.js";
|
|
22
23
|
export * from "./components/sidebar.js";
|
|
23
24
|
export * from "./components/skeleton.js";
|
|
25
|
+
export * from "./components/split-button.js";
|
|
24
26
|
export * from "./components/switch.js";
|
|
25
27
|
export * from "./components/table.js";
|
|
26
28
|
export * from "./components/tabs.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export { useTheme } from "next-themes";
|
|
3
3
|
export { Link } from "./components/link.js";
|
|
4
4
|
export { BeezUIProvider } from "./providers/native-provider.js";
|
|
5
|
+
export * from "./components/action-button.js";
|
|
5
6
|
export * from "./components/alert-dialog.js";
|
|
6
7
|
export * from "./components/avatar.js";
|
|
7
8
|
export * from "./components/badge.js";
|
|
@@ -20,6 +21,7 @@ export * from "./components/separator.js";
|
|
|
20
21
|
export * from "./components/sheet.js";
|
|
21
22
|
export * from "./components/sidebar.js";
|
|
22
23
|
export * from "./components/skeleton.js";
|
|
24
|
+
export * from "./components/split-button.js";
|
|
23
25
|
export * from "./components/switch.js";
|
|
24
26
|
export * from "./components/table.js";
|
|
25
27
|
export * from "./components/tabs.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swap presets adapted from Arc (https://uiarc.dev), Copyright (c) 2026 Elia Kuratli, MIT License.
|
|
3
|
+
* The full license text ships in THIRD_PARTY_NOTICES.md. Internal: shared by the Arc-derived buttons to swap icons
|
|
4
|
+
* and labels in place.
|
|
5
|
+
*/
|
|
6
|
+
import type { TargetAndTransition, Transition } from "motion/react";
|
|
7
|
+
/** Resting pose every swapped element settles into. */
|
|
8
|
+
export declare const ARC_SWAP_REST: TargetAndTransition;
|
|
9
|
+
/** Outgoing elements leave faster than incoming ones arrive. */
|
|
10
|
+
export declare const ARC_SWAP_EXIT_TRANSITION: Transition;
|
|
11
|
+
/** An incoming icon grows out of a slight blur. */
|
|
12
|
+
export declare const ARC_ICON_ENTER: TargetAndTransition;
|
|
13
|
+
/** An outgoing icon shrinks back into the same blur. */
|
|
14
|
+
export declare const ARC_ICON_EXIT: TargetAndTransition;
|
|
15
|
+
/** Scale rides the spring; opacity and blur tween so blur never overshoots below zero. */
|
|
16
|
+
export declare const ARC_ICON_TRANSITION: Transition;
|
|
17
|
+
/** With reduced motion, every swap is an instant fade. */
|
|
18
|
+
export declare const ARC_REDUCED_TRANSITION: Transition;
|
|
19
|
+
/** Reduced-motion entrance: the resting pose, only faded out. */
|
|
20
|
+
export declare const ARC_REDUCED_ENTER: TargetAndTransition;
|
|
21
|
+
/** Reduced-motion exit: an instant fade. */
|
|
22
|
+
export declare const ARC_REDUCED_EXIT: TargetAndTransition;
|