beez-ui 0.6.2 → 0.7.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 +134 -98
- package/README.md +274 -263
- package/dist/components/alert.js +1 -1
- package/dist/components/avatar.d.ts +2 -1
- package/dist/components/avatar.js +4 -3
- package/dist/components/calendar.js +102 -0
- package/dist/components/carousel.js +61 -48
- package/dist/components/checkbox.js +1 -1
- package/dist/components/data-table.js +34 -4
- package/dist/components/dropdown-menu.js +5 -2
- package/dist/components/filter-query-bar.js +11 -6
- package/dist/components/form.d.ts +5 -0
- package/dist/components/form.js +11 -9
- package/dist/components/input-group.js +1 -1
- package/dist/components/input.js +1 -1
- package/dist/components/pagination.d.ts +1 -0
- package/dist/components/pagination.js +9 -4
- package/dist/components/radio-group.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/sheet.js +12 -9
- package/dist/components/sidebar.js +9 -5
- package/dist/components/skeleton.d.ts +6 -0
- package/dist/components/skeleton.js +7 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/typing-animation.js +42 -15
- package/dist/lib/column-filter-value.d.ts +12 -0
- package/dist/lib/column-filter-value.js +52 -0
- package/dist/motion/glide-indicator.d.ts +37 -3
- package/dist/motion/glide-indicator.js +37 -3
- package/dist/motion/motion-slot.js +47 -18
- package/dist/motion/surface-keyframes.js +18 -6
- package/dist/motion/tokens.d.ts +24 -1
- package/dist/motion/tokens.js +23 -0
- package/dist/providers/beez-ui-provider.js +49 -17
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/dist/components/sheet.js
CHANGED
|
@@ -196,7 +196,7 @@ function SheetContent(t0) {
|
|
|
196
196
|
}
|
|
197
197
|
let t5;
|
|
198
198
|
if ($[7] !== className) {
|
|
199
|
-
t5 = cn("fixed z-50 flex flex-col gap-4 overflow-y-auto overscroll-contain data-[side=bottom]:max-h-dvh data-[side=top]:max-h-dvh bg-background bg-clip-padding text-sm shadow-lg
|
|
199
|
+
t5 = cn("fixed z-50 flex flex-col gap-4 overflow-y-auto overscroll-contain data-[side=bottom]:max-h-dvh data-[side=top]:max-h-dvh bg-background bg-clip-padding text-sm shadow-lg data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm ", className);
|
|
200
200
|
$[7] = className;
|
|
201
201
|
$[8] = t5;
|
|
202
202
|
} else {
|
|
@@ -207,14 +207,17 @@ function SheetContent(t0) {
|
|
|
207
207
|
t6 = showCloseButton && /* @__PURE__ */ _jsx(SheetPrimitive.Close, {
|
|
208
208
|
"data-slot": "sheet-close",
|
|
209
209
|
asChild: true,
|
|
210
|
-
children: /* @__PURE__ */
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
children: "
|
|
217
|
-
|
|
210
|
+
children: /* @__PURE__ */ _jsx(MotionSlot, {
|
|
211
|
+
kind: "delayed-pop",
|
|
212
|
+
children: /* @__PURE__ */ _jsxs(Button, {
|
|
213
|
+
variant: "ghost",
|
|
214
|
+
className: "absolute top-4 right-4",
|
|
215
|
+
size: "icon-sm",
|
|
216
|
+
children: [/* @__PURE__ */ _jsx(XIcon, {}), /* @__PURE__ */ _jsx("span", {
|
|
217
|
+
className: "sr-only",
|
|
218
|
+
children: "Close"
|
|
219
|
+
})]
|
|
220
|
+
})
|
|
218
221
|
})
|
|
219
222
|
});
|
|
220
223
|
$[9] = showCloseButton;
|
|
@@ -7,6 +7,7 @@ import { useIsMobile } from "../hooks/use-mobile.js";
|
|
|
7
7
|
import { useSidebarPersistence, saveSidebarPreference } from "../hooks/use-sidebar-persistence.js";
|
|
8
8
|
import { GlideSlot } from "../motion/glide-slot.js";
|
|
9
9
|
import { MotionSlot } from "../motion/motion-slot.js";
|
|
10
|
+
import { MOTION_EASE_DRAWER } from "../motion/tokens.js";
|
|
10
11
|
import { cn } from "../lib/utils.js";
|
|
11
12
|
import { SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME } from "../constants/sidebar.js";
|
|
12
13
|
import { Button } from "./button.js";
|
|
@@ -21,6 +22,8 @@ const SIDEBAR_WIDTH = "16rem";
|
|
|
21
22
|
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
22
23
|
const SIDEBAR_WIDTH_ICON = "3rem";
|
|
23
24
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
25
|
+
/** Drawer curve shared by the collapse transitions: quick start, long gentle settle. */
|
|
26
|
+
const SIDEBAR_EASE = `cubic-bezier(${MOTION_EASE_DRAWER.join(", ")})`;
|
|
24
27
|
const SIDEBAR_MENU_SKELETON_TEXT_WIDTH = "70%";
|
|
25
28
|
function subscribeToClientMount() {
|
|
26
29
|
return () => {};
|
|
@@ -158,6 +161,7 @@ function SidebarProvider(t0) {
|
|
|
158
161
|
t7 = {
|
|
159
162
|
"--sidebar-width": SIDEBAR_WIDTH,
|
|
160
163
|
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
|
164
|
+
"--sidebar-ease": SIDEBAR_EASE,
|
|
161
165
|
...style
|
|
162
166
|
};
|
|
163
167
|
$[26] = style;
|
|
@@ -339,7 +343,7 @@ function Sidebar(t0) {
|
|
|
339
343
|
const t5 = variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)";
|
|
340
344
|
let t6;
|
|
341
345
|
if ($[27] !== t5) {
|
|
342
|
-
t6 = cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-
|
|
346
|
+
t6 = cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-300 ease-(--sidebar-ease)", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", t5);
|
|
343
347
|
$[27] = t5;
|
|
344
348
|
$[28] = t6;
|
|
345
349
|
} else {
|
|
@@ -359,7 +363,7 @@ function Sidebar(t0) {
|
|
|
359
363
|
const t8 = variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l";
|
|
360
364
|
let t9;
|
|
361
365
|
if ($[31] !== className || $[32] !== t8) {
|
|
362
|
-
t9 = cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-
|
|
366
|
+
t9 = cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-300 ease-(--sidebar-ease) data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex", t8, className);
|
|
363
367
|
$[31] = className;
|
|
364
368
|
$[32] = t8;
|
|
365
369
|
$[33] = t9;
|
|
@@ -507,7 +511,7 @@ function SidebarRail(t0) {
|
|
|
507
511
|
const { toggleSidebar } = useSidebar();
|
|
508
512
|
let t1;
|
|
509
513
|
if ($[3] !== className) {
|
|
510
|
-
t1 = cn("absolute inset-y-0 z-20 hidden w-4 transition-all ease-
|
|
514
|
+
t1 = cn("absolute inset-y-0 z-20 hidden w-4 transition-all duration-300 ease-(--sidebar-ease) group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar", "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", className);
|
|
511
515
|
$[3] = className;
|
|
512
516
|
$[4] = t1;
|
|
513
517
|
} else {
|
|
@@ -812,7 +816,7 @@ function SidebarGroupLabel(t0) {
|
|
|
812
816
|
const Comp = asChild ? Slot.Root : "div";
|
|
813
817
|
let t2;
|
|
814
818
|
if ($[4] !== className) {
|
|
815
|
-
t2 = cn("flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-
|
|
819
|
+
t2 = cn("flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-300 ease-(--sidebar-ease) group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", className);
|
|
816
820
|
$[4] = className;
|
|
817
821
|
$[5] = t2;
|
|
818
822
|
} else {
|
|
@@ -996,7 +1000,7 @@ const sidebarMenuButtonVariants = cva("peer/menu-button group/menu-button flex w
|
|
|
996
1000
|
variants: {
|
|
997
1001
|
variant: {
|
|
998
1002
|
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
999
|
-
outline: "bg-background shadow-[
|
|
1003
|
+
outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
|
|
1000
1004
|
},
|
|
1001
1005
|
size: {
|
|
1002
1006
|
default: "h-8 text-sm",
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder with a soft shine that sweeps across it while content loads.
|
|
3
|
+
* The shine rests outside the box, so reduced motion shows a plain muted block.
|
|
4
|
+
* @param props - Native div properties and optional consumer overrides.
|
|
5
|
+
* @returns The placeholder element with its loading animation.
|
|
6
|
+
*/
|
|
1
7
|
declare function Skeleton({ className, ...props }: React.ComponentProps<"div">): import("react").JSX.Element;
|
|
2
8
|
export { Skeleton };
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { MotionSlot } from "../motion/motion-slot.js";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
3
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
|
+
/**
|
|
5
|
+
* Placeholder with a soft shine that sweeps across it while content loads.
|
|
6
|
+
* The shine rests outside the box, so reduced motion shows a plain muted block.
|
|
7
|
+
* @param props - Native div properties and optional consumer overrides.
|
|
8
|
+
* @returns The placeholder element with its loading animation.
|
|
9
|
+
*/
|
|
4
10
|
function Skeleton({ className, ...props }) {
|
|
5
11
|
return /* @__PURE__ */ _jsx(MotionSlot, {
|
|
6
12
|
kind: "skeleton",
|
|
7
13
|
children: /* @__PURE__ */ _jsx("div", {
|
|
8
14
|
"data-slot": "skeleton",
|
|
9
|
-
className: cn("rounded-md bg-muted", className),
|
|
15
|
+
className: cn("relative overflow-hidden rounded-md bg-muted after:pointer-events-none after:absolute after:inset-0 after:-translate-x-full after:bg-linear-to-r after:from-transparent after:via-foreground/6 after:to-transparent", className),
|
|
10
16
|
...props
|
|
11
17
|
})
|
|
12
18
|
});
|
|
@@ -36,7 +36,7 @@ function Switch(t0) {
|
|
|
36
36
|
kind: "thumb",
|
|
37
37
|
children: /* @__PURE__ */ _jsx(SwitchPrimitive.Thumb, {
|
|
38
38
|
"data-slot": "switch-thumb",
|
|
39
|
-
className: "pointer-events-none block rounded-full bg-background ring-0
|
|
39
|
+
className: "pointer-events-none block rounded-full bg-background shadow-sm ring-0 group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
|
|
40
40
|
})
|
|
41
41
|
});
|
|
42
42
|
$[6] = t3;
|
|
@@ -12,7 +12,7 @@ function Textarea({ className, ...props }) {
|
|
|
12
12
|
kind: "field",
|
|
13
13
|
children: /* @__PURE__ */ _jsx("textarea", {
|
|
14
14
|
"data-slot": "textarea",
|
|
15
|
-
className: cn("flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-
|
|
15
|
+
className: cn("flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-[color,background-color,border-color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
|
|
16
16
|
...props
|
|
17
17
|
})
|
|
18
18
|
});
|
|
@@ -3,6 +3,7 @@ import { c as _c } from "react/compiler-runtime";
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { motion } from "motion/react";
|
|
5
5
|
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
6
|
+
import { MOTION_TIMING } from "../motion/tokens.js";
|
|
6
7
|
import { cn } from "../lib/utils.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const motionElements = {
|
|
@@ -19,8 +20,23 @@ const motionElements = {
|
|
|
19
20
|
section: motion.section,
|
|
20
21
|
span: motion.span
|
|
21
22
|
};
|
|
23
|
+
/** Idle cursors fade out and back in, like a text editor's caret. */
|
|
24
|
+
const CURSOR_BLINK_OPACITY = [
|
|
25
|
+
1,
|
|
26
|
+
1,
|
|
27
|
+
0,
|
|
28
|
+
0,
|
|
29
|
+
1
|
|
30
|
+
];
|
|
31
|
+
const CURSOR_BLINK_TIMES = [
|
|
32
|
+
0,
|
|
33
|
+
.4,
|
|
34
|
+
.5,
|
|
35
|
+
.9,
|
|
36
|
+
1
|
|
37
|
+
];
|
|
22
38
|
export function TypingAnimation(t0) {
|
|
23
|
-
const $ = _c(
|
|
39
|
+
const $ = _c(68);
|
|
24
40
|
let children;
|
|
25
41
|
let className;
|
|
26
42
|
let deleteSpeed;
|
|
@@ -293,6 +309,7 @@ export function TypingAnimation(t0) {
|
|
|
293
309
|
const currentWordGraphemes = t19;
|
|
294
310
|
const isComplete = !loop && currentWordIndex === wordsToAnimate.length - 1 && currentCharIndex >= currentWordGraphemes.length && phase !== "deleting";
|
|
295
311
|
const shouldShowCursor = showCursor && !shouldReduceMotion && !isComplete && (hasMultipleWords || loop || currentCharIndex < currentWordGraphemes.length);
|
|
312
|
+
const isCursorIdle = !shouldStart || phase === "pause" || displayedText === "";
|
|
296
313
|
let t20;
|
|
297
314
|
if ($[51] !== cursorStyle) {
|
|
298
315
|
t20 = () => {
|
|
@@ -328,20 +345,30 @@ export function TypingAnimation(t0) {
|
|
|
328
345
|
t23 = $[56];
|
|
329
346
|
}
|
|
330
347
|
let t24;
|
|
331
|
-
if ($[57] !== blinkCursor || $[58] !== getCursorChar || $[59] !== shouldShowCursor) {
|
|
332
|
-
t24 = shouldShowCursor && /* @__PURE__ */ _jsx(
|
|
333
|
-
|
|
348
|
+
if ($[57] !== blinkCursor || $[58] !== getCursorChar || $[59] !== isCursorIdle || $[60] !== shouldShowCursor) {
|
|
349
|
+
t24 = shouldShowCursor && /* @__PURE__ */ _jsx(motion.span, {
|
|
350
|
+
"aria-hidden": "true",
|
|
351
|
+
"data-slot": "typing-animation-cursor",
|
|
352
|
+
className: "inline-block",
|
|
353
|
+
animate: { opacity: blinkCursor && isCursorIdle ? CURSOR_BLINK_OPACITY : 1 },
|
|
354
|
+
transition: blinkCursor && isCursorIdle ? {
|
|
355
|
+
duration: MOTION_TIMING.cursorBlink,
|
|
356
|
+
times: CURSOR_BLINK_TIMES,
|
|
357
|
+
repeat: Infinity,
|
|
358
|
+
ease: "easeInOut"
|
|
359
|
+
} : { duration: MOTION_TIMING.fast },
|
|
334
360
|
children: getCursorChar()
|
|
335
361
|
});
|
|
336
362
|
$[57] = blinkCursor;
|
|
337
363
|
$[58] = getCursorChar;
|
|
338
|
-
$[59] =
|
|
339
|
-
$[60] =
|
|
364
|
+
$[59] = isCursorIdle;
|
|
365
|
+
$[60] = shouldShowCursor;
|
|
366
|
+
$[61] = t24;
|
|
340
367
|
} else {
|
|
341
|
-
t24 = $[
|
|
368
|
+
t24 = $[61];
|
|
342
369
|
}
|
|
343
370
|
let t25;
|
|
344
|
-
if ($[
|
|
371
|
+
if ($[62] !== MotionComponent || $[63] !== displayedText || $[64] !== props || $[65] !== t23 || $[66] !== t24) {
|
|
345
372
|
t25 = /* @__PURE__ */ _jsxs(MotionComponent, {
|
|
346
373
|
ref: elementRef,
|
|
347
374
|
"data-slot": "typing-animation",
|
|
@@ -349,14 +376,14 @@ export function TypingAnimation(t0) {
|
|
|
349
376
|
...props,
|
|
350
377
|
children: [displayedText, t24]
|
|
351
378
|
});
|
|
352
|
-
$[
|
|
353
|
-
$[
|
|
354
|
-
$[
|
|
355
|
-
$[
|
|
356
|
-
$[
|
|
357
|
-
$[
|
|
379
|
+
$[62] = MotionComponent;
|
|
380
|
+
$[63] = displayedText;
|
|
381
|
+
$[64] = props;
|
|
382
|
+
$[65] = t23;
|
|
383
|
+
$[66] = t24;
|
|
384
|
+
$[67] = t25;
|
|
358
385
|
} else {
|
|
359
|
-
t25 = $[
|
|
386
|
+
t25 = $[67];
|
|
360
387
|
}
|
|
361
388
|
return t25;
|
|
362
389
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Normalizes text for accent- and case-insensitive comparisons. */
|
|
2
|
+
export declare function normalizeFilterToken(value: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Evalúa el valor de una celda contra un filtro de columna de la barra de filtros.
|
|
5
|
+
* Entiende los valores estructurados (`numberRange`, `enum`, `presence`, `yearMonthRange`)
|
|
6
|
+
* y, para texto plano, busca coincidencias parciales sin distinguir acentos ni mayúsculas.
|
|
7
|
+
* Una celda sin valor comparable no cumple los rangos; un filtro vacío deja pasar la fila.
|
|
8
|
+
* @param cellValue - Valor de la celda tal como lo devuelve el accessor de la columna.
|
|
9
|
+
* @param filterValue - Valor activo del filtro de esa columna.
|
|
10
|
+
* @returns `true` cuando la fila debe mostrarse.
|
|
11
|
+
*/
|
|
12
|
+
export declare function matchesColumnFilterValue(cellValue: unknown, filterValue: unknown): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { matchesAdvancedYearMonthRangeFilter } from "./year-month-filter.js";
|
|
2
|
+
const DIACRITICS_PATTERN = /[̀-ͯ]/g;
|
|
3
|
+
/** Normalizes text for accent- and case-insensitive comparisons. */
|
|
4
|
+
export function normalizeFilterToken(value) {
|
|
5
|
+
return value.normalize("NFD").replace(DIACRITICS_PATTERN, "").toLocaleLowerCase().trim();
|
|
6
|
+
}
|
|
7
|
+
/** Distinguishes the grammar's structured values from plain text written by the search input. */
|
|
8
|
+
function isColumnFilterValue(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && typeof value.kind === "string";
|
|
10
|
+
}
|
|
11
|
+
/** Reads numbers and numeric strings; anything else has no comparable value. */
|
|
12
|
+
function toFiniteNumber(value) {
|
|
13
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
14
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
15
|
+
const parsed = Number(value);
|
|
16
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
/** Treats `null`, `undefined`, blank strings and empty lists as a missing value. */
|
|
21
|
+
function isMissingValue(value) {
|
|
22
|
+
if (value == null) return true;
|
|
23
|
+
if (typeof value === "string") return value.trim() === "";
|
|
24
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Evalúa el valor de una celda contra un filtro de columna de la barra de filtros.
|
|
29
|
+
* Entiende los valores estructurados (`numberRange`, `enum`, `presence`, `yearMonthRange`)
|
|
30
|
+
* y, para texto plano, busca coincidencias parciales sin distinguir acentos ni mayúsculas.
|
|
31
|
+
* Una celda sin valor comparable no cumple los rangos; un filtro vacío deja pasar la fila.
|
|
32
|
+
* @param cellValue - Valor de la celda tal como lo devuelve el accessor de la columna.
|
|
33
|
+
* @param filterValue - Valor activo del filtro de esa columna.
|
|
34
|
+
* @returns `true` cuando la fila debe mostrarse.
|
|
35
|
+
*/
|
|
36
|
+
export function matchesColumnFilterValue(cellValue, filterValue) {
|
|
37
|
+
if (!isColumnFilterValue(filterValue)) {
|
|
38
|
+
const query = normalizeFilterToken(String(filterValue ?? ""));
|
|
39
|
+
return query === "" || normalizeFilterToken(String(cellValue ?? "")).includes(query);
|
|
40
|
+
}
|
|
41
|
+
switch (filterValue.kind) {
|
|
42
|
+
case "numberRange": {
|
|
43
|
+
const value = toFiniteNumber(cellValue);
|
|
44
|
+
if (value == null) return false;
|
|
45
|
+
if (filterValue.min != null && value < filterValue.min) return false;
|
|
46
|
+
return filterValue.max == null || value <= filterValue.max;
|
|
47
|
+
}
|
|
48
|
+
case "enum": return Array.isArray(cellValue) ? cellValue.some((item) => String(item) === filterValue.value) : String(cellValue ?? "") === filterValue.value;
|
|
49
|
+
case "presence": return filterValue.value === "hasValue" ? !isMissingValue(cellValue) : isMissingValue(cellValue);
|
|
50
|
+
case "yearMonthRange": return matchesAdvancedYearMonthRangeFilter(filterValue, toFiniteNumber(cellValue));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SPRING_LAYOUT, SPRING_TABS } from "./tokens.js";
|
|
1
|
+
import { SPRING_HIGHLIGHT, SPRING_LAYOUT, SPRING_TABS } from "./tokens.js";
|
|
2
2
|
/** Which part of the active item carries the highlight that must travel. */
|
|
3
3
|
type GlideSurface = "background" | "after";
|
|
4
4
|
/** Describes the items of one container and how their active state is exposed. */
|
|
@@ -13,7 +13,13 @@ export interface GlidePreset {
|
|
|
13
13
|
attributes: string[];
|
|
14
14
|
isActive: (item: Element) => boolean;
|
|
15
15
|
surface: (container: HTMLElement) => GlideSurface;
|
|
16
|
-
spring: typeof SPRING_LAYOUT | typeof SPRING_TABS;
|
|
16
|
+
spring: typeof SPRING_LAYOUT | typeof SPRING_TABS | typeof SPRING_HIGHLIGHT;
|
|
17
|
+
/**
|
|
18
|
+
* Highlights that follow the pointer (menus, listboxes) jump straight to the hovered item:
|
|
19
|
+
* the pointer already marks the position, so a travelling copy would only trail behind it.
|
|
20
|
+
* The glide is kept for keyboard navigation.
|
|
21
|
+
*/
|
|
22
|
+
instantOnPointer?: boolean;
|
|
17
23
|
}
|
|
18
24
|
export declare const GLIDE_PRESETS: {
|
|
19
25
|
tabs: {
|
|
@@ -42,7 +48,7 @@ export declare const GLIDE_PRESETS: {
|
|
|
42
48
|
readonly mass: 0.6;
|
|
43
49
|
};
|
|
44
50
|
};
|
|
45
|
-
|
|
51
|
+
pagination: {
|
|
46
52
|
itemSelector: string;
|
|
47
53
|
containerSelector: string;
|
|
48
54
|
attributes: string[];
|
|
@@ -55,6 +61,34 @@ export declare const GLIDE_PRESETS: {
|
|
|
55
61
|
readonly mass: 0.6;
|
|
56
62
|
};
|
|
57
63
|
};
|
|
64
|
+
suggestions: {
|
|
65
|
+
itemSelector: string;
|
|
66
|
+
containerSelector: string;
|
|
67
|
+
attributes: string[];
|
|
68
|
+
isActive: (item: Element) => boolean;
|
|
69
|
+
surface: () => "background";
|
|
70
|
+
spring: {
|
|
71
|
+
readonly type: "spring";
|
|
72
|
+
readonly stiffness: 700;
|
|
73
|
+
readonly damping: 45;
|
|
74
|
+
readonly mass: 0.5;
|
|
75
|
+
};
|
|
76
|
+
instantOnPointer: true;
|
|
77
|
+
};
|
|
78
|
+
menu: {
|
|
79
|
+
itemSelector: string;
|
|
80
|
+
containerSelector: string;
|
|
81
|
+
attributes: string[];
|
|
82
|
+
isActive: (item: Element) => boolean;
|
|
83
|
+
surface: () => "background";
|
|
84
|
+
spring: {
|
|
85
|
+
readonly type: "spring";
|
|
86
|
+
readonly stiffness: 700;
|
|
87
|
+
readonly damping: 45;
|
|
88
|
+
readonly mass: 0.5;
|
|
89
|
+
};
|
|
90
|
+
instantOnPointer: true;
|
|
91
|
+
};
|
|
58
92
|
};
|
|
59
93
|
export type GlidePresetName = keyof typeof GLIDE_PRESETS;
|
|
60
94
|
/** Marks the item whose own highlight is hidden while the travelling copy stands in for it. */
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* previous item and is removed once it lands, leaving the item's own styles in charge again.
|
|
5
5
|
*/
|
|
6
6
|
import { animate } from "motion/react";
|
|
7
|
-
import { GLIDE_CONTINUITY_MS, SPRING_LAYOUT, SPRING_TABS } from "./tokens.js";
|
|
7
|
+
import { GLIDE_CONTINUITY_MS, SPRING_HIGHLIGHT, SPRING_LAYOUT, SPRING_TABS } from "./tokens.js";
|
|
8
8
|
export const GLIDE_PRESETS = {
|
|
9
9
|
tabs: {
|
|
10
10
|
itemSelector: "[data-slot=\"tabs-trigger\"]",
|
|
@@ -22,13 +22,31 @@ export const GLIDE_PRESETS = {
|
|
|
22
22
|
surface: () => "background",
|
|
23
23
|
spring: SPRING_LAYOUT
|
|
24
24
|
},
|
|
25
|
+
pagination: {
|
|
26
|
+
itemSelector: "[data-slot=\"pagination-link\"]",
|
|
27
|
+
containerSelector: "[data-slot=\"pagination-content\"]",
|
|
28
|
+
attributes: ["aria-current"],
|
|
29
|
+
isActive: (item) => item.getAttribute("aria-current") === "page",
|
|
30
|
+
surface: () => "background",
|
|
31
|
+
spring: SPRING_LAYOUT
|
|
32
|
+
},
|
|
33
|
+
suggestions: {
|
|
34
|
+
itemSelector: "[role=\"option\"]",
|
|
35
|
+
containerSelector: "[role=\"listbox\"]",
|
|
36
|
+
attributes: ["aria-selected"],
|
|
37
|
+
isActive: (item) => item.getAttribute("aria-selected") === "true",
|
|
38
|
+
surface: () => "background",
|
|
39
|
+
spring: SPRING_HIGHLIGHT,
|
|
40
|
+
instantOnPointer: true
|
|
41
|
+
},
|
|
25
42
|
menu: {
|
|
26
43
|
itemSelector: "[role=\"menuitem\"],[role=\"menuitemcheckbox\"],[role=\"menuitemradio\"],[role=\"option\"]",
|
|
27
44
|
containerSelector: "[role=\"menu\"],[role=\"listbox\"]",
|
|
28
45
|
attributes: ["data-highlighted"],
|
|
29
46
|
isActive: (item) => item.hasAttribute("data-highlighted"),
|
|
30
47
|
surface: () => "background",
|
|
31
|
-
spring:
|
|
48
|
+
spring: SPRING_HIGHLIGHT,
|
|
49
|
+
instantOnPointer: true
|
|
32
50
|
}
|
|
33
51
|
};
|
|
34
52
|
/** Marks the item whose own highlight is hidden while the travelling copy stands in for it. */
|
|
@@ -136,6 +154,20 @@ export function attachGlideIndicator(container, preset) {
|
|
|
136
154
|
position: container.style.position,
|
|
137
155
|
isolation: container.style.isolation
|
|
138
156
|
};
|
|
157
|
+
// The last input decides whether a highlight change was driven by the pointer or the keyboard.
|
|
158
|
+
// Keyboard focus may live outside the container (a combobox input), so both are read from the
|
|
159
|
+
// document; the pointer only counts while it moves over this container.
|
|
160
|
+
let isPointerDriven = false;
|
|
161
|
+
const onPointerMove = () => {
|
|
162
|
+
isPointerDriven = true;
|
|
163
|
+
};
|
|
164
|
+
const onKeyDown = () => {
|
|
165
|
+
isPointerDriven = false;
|
|
166
|
+
};
|
|
167
|
+
if (preset.instantOnPointer) {
|
|
168
|
+
container.addEventListener("pointermove", onPointerMove);
|
|
169
|
+
container.ownerDocument.addEventListener("keydown", onKeyDown, true);
|
|
170
|
+
}
|
|
139
171
|
/** Puts the item's own highlight back and removes the travelling copy. */
|
|
140
172
|
function settle() {
|
|
141
173
|
animation?.cancel();
|
|
@@ -236,7 +268,7 @@ export function attachGlideIndicator(container, preset) {
|
|
|
236
268
|
const isContinuous = previous !== null && previous !== active && previous.isConnected && !preset.isActive(previous) && (lastActiveSeenAt === Number.NEGATIVE_INFINITY || now - lastActiveSeenAt <= GLIDE_CONTINUITY_MS);
|
|
237
269
|
lastActive = active;
|
|
238
270
|
lastActiveSeenAt = Number.NEGATIVE_INFINITY;
|
|
239
|
-
if (isContinuous) glide(previous, active);
|
|
271
|
+
if (isContinuous && !(preset.instantOnPointer && isPointerDriven)) glide(previous, active);
|
|
240
272
|
else settle();
|
|
241
273
|
});
|
|
242
274
|
observer.observe(container, {
|
|
@@ -247,6 +279,8 @@ export function attachGlideIndicator(container, preset) {
|
|
|
247
279
|
});
|
|
248
280
|
return () => {
|
|
249
281
|
observer.disconnect();
|
|
282
|
+
container.removeEventListener("pointermove", onPointerMove);
|
|
283
|
+
container.ownerDocument.removeEventListener("keydown", onKeyDown, true);
|
|
250
284
|
settle();
|
|
251
285
|
};
|
|
252
286
|
}
|
|
@@ -6,7 +6,7 @@ import { Slot } from "radix-ui";
|
|
|
6
6
|
import { animate, hover, press, usePresence, frame } from "motion/react";
|
|
7
7
|
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
8
8
|
import { HOVER_CAPABLE_QUERY, MILLISECONDS_PER_SECOND } from "../constants/motion.js";
|
|
9
|
-
import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE, MOTION_INVALID_SHAKE_PX, MOTION_ITEM_CHECK_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_LIST_ITEM_DISTANCE, MOTION_LIST_STAGGER_LIMIT, MOTION_EASE_IN_OUT, SPRING_CHEVRON, SPRING_PANEL, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
|
|
9
|
+
import { MOTION_EASE, MOTION_TIMING, MOTION_PRESS_SCALE, MOTION_SUBTLE_PRESS_SCALE, MOTION_TOGGLE_PRESS_SCALE, MOTION_HOVER_SCALE, MOTION_THUMB_SQUISH_SCALE, MOTION_CONTENT_DISTANCE, MOTION_INVALID_SHAKE_PX, MOTION_ITEM_CHECK_SCALE, MOTION_ICON_SWAP_SCALE, MOTION_CLOSE_BUTTON_SCALE, MOTION_LIST_ITEM_DISTANCE, MOTION_LIST_STAGGER_LIMIT, MOTION_EASE_IN_OUT, MOTION_REVEAL_BLUR_PX, MOTION_REVEAL_SCALE, MOTION_SHIMMER_TRANSLATE, MOTION_CARD_SHADOW_LIFT, MOTION_CARD_SHADOW_REST, SPRING_CHEVRON, SPRING_PANEL, SPRING_PRESS, SPRING_THUMB } from "./tokens.js";
|
|
10
10
|
import { surfaceEnter, surfaceExit } from "./surface-keyframes.js";
|
|
11
11
|
import { useAttachedElement } from "./use-attached-element.js";
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -277,16 +277,14 @@ export function MotionSlot(t0) {
|
|
|
277
277
|
});
|
|
278
278
|
return dispose;
|
|
279
279
|
}
|
|
280
|
-
if (kind === "skeleton") {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
ease: "easeInOut"
|
|
289
|
-
}), ["opacity"]);
|
|
280
|
+
if (kind === "skeleton" && typeof element.animate === "function") {
|
|
281
|
+
const shimmer = element.animate({ translate: [...MOTION_SHIMMER_TRANSLATE] }, {
|
|
282
|
+
duration: MOTION_TIMING.skeleton * MILLISECONDS_PER_SECOND,
|
|
283
|
+
easing: `cubic-bezier(${MOTION_EASE_IN_OUT.join(", ")})`,
|
|
284
|
+
iterations: Infinity,
|
|
285
|
+
pseudoElement: "::after"
|
|
286
|
+
});
|
|
287
|
+
cleanups.push(() => shimmer.cancel());
|
|
290
288
|
} else {
|
|
291
289
|
if (kind === "icon-swap") {
|
|
292
290
|
play({
|
|
@@ -398,6 +396,13 @@ export function MotionSlot(t0) {
|
|
|
398
396
|
}));
|
|
399
397
|
}
|
|
400
398
|
}
|
|
399
|
+
let restingShadow = "none";
|
|
400
|
+
const withRestingShadow = (shadow) => {
|
|
401
|
+
if (!element.style.boxShadow) {
|
|
402
|
+
restingShadow = getComputedStyle(element).boxShadow;
|
|
403
|
+
}
|
|
404
|
+
return restingShadow === "none" ? shadow : `${restingShadow}, ${shadow}`;
|
|
405
|
+
};
|
|
401
406
|
if (kind === "card" || kind === "link" || kind === "icon") {
|
|
402
407
|
cleanups.push(hover(element, () => {
|
|
403
408
|
if (!isEnabled(element) || !canHover() || kind === "card" && !element.querySelector("a,button")) {
|
|
@@ -405,7 +410,9 @@ export function MotionSlot(t0) {
|
|
|
405
410
|
}
|
|
406
411
|
const keep = { restoreOnComplete: false };
|
|
407
412
|
if (kind === "card") {
|
|
408
|
-
|
|
413
|
+
const rest = withRestingShadow(MOTION_CARD_SHADOW_REST);
|
|
414
|
+
const from = element.style.boxShadow ? getComputedStyle(element).boxShadow : rest;
|
|
415
|
+
play({ boxShadow: [from, withRestingShadow(MOTION_CARD_SHADOW_LIFT)] }, tween(MOTION_TIMING.enter), keep);
|
|
409
416
|
} else {
|
|
410
417
|
if (kind === "icon") {
|
|
411
418
|
play({ transform: ["rotate(0deg) scale(1)", "rotate(-3deg) scale(1.025)"] }, SPRING_PRESS, keep);
|
|
@@ -415,7 +422,7 @@ export function MotionSlot(t0) {
|
|
|
415
422
|
}
|
|
416
423
|
return () => {
|
|
417
424
|
if (kind === "card") {
|
|
418
|
-
play({ boxShadow:
|
|
425
|
+
play({ boxShadow: [getComputedStyle(element).boxShadow, withRestingShadow(MOTION_CARD_SHADOW_REST)] }, tween(MOTION_TIMING.fast));
|
|
419
426
|
} else {
|
|
420
427
|
if (kind === "icon") {
|
|
421
428
|
play({ transform: [currentTransform(element), "rotate(0deg) scale(1)"] }, SPRING_PRESS);
|
|
@@ -544,6 +551,28 @@ export function MotionSlot(t0) {
|
|
|
544
551
|
}
|
|
545
552
|
cleanups.push(() => observer_1.disconnect());
|
|
546
553
|
}
|
|
554
|
+
if (kind === "reveal") {
|
|
555
|
+
const isLoading = () => element.hasAttribute("data-loading");
|
|
556
|
+
let wasLoading = isLoading();
|
|
557
|
+
const observer_2 = new MutationObserver(() => {
|
|
558
|
+
const loading = isLoading();
|
|
559
|
+
const loaded = wasLoading && !loading && !element.hasAttribute("data-error");
|
|
560
|
+
wasLoading = loading;
|
|
561
|
+
if (!loaded) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
play({
|
|
565
|
+
opacity: [0, 1],
|
|
566
|
+
transform: [`scale(${MOTION_REVEAL_SCALE})`, "scale(1)"],
|
|
567
|
+
filter: [`blur(${MOTION_REVEAL_BLUR_PX}px)`, "blur(0px)"]
|
|
568
|
+
}, tween(MOTION_TIMING.reveal));
|
|
569
|
+
});
|
|
570
|
+
observer_2.observe(element, {
|
|
571
|
+
attributes: true,
|
|
572
|
+
attributeFilter: ["data-loading", "data-error"]
|
|
573
|
+
});
|
|
574
|
+
cleanups.push(() => observer_2.disconnect());
|
|
575
|
+
}
|
|
547
576
|
if (kind === "thumb") {
|
|
548
577
|
const restingOffset = () => {
|
|
549
578
|
if (!element.getClientRects().length) {
|
|
@@ -565,7 +594,7 @@ export function MotionSlot(t0) {
|
|
|
565
594
|
element.parentElement?.removeEventListener("pointerenter", remeasure);
|
|
566
595
|
element.parentElement?.removeEventListener("focusin", remeasure);
|
|
567
596
|
});
|
|
568
|
-
const
|
|
597
|
+
const observer_3 = new MutationObserver(() => {
|
|
569
598
|
const glideOffset = translateXOf(getComputedStyle(element).transform);
|
|
570
599
|
cancel(["transform"]);
|
|
571
600
|
restore(["transform"]);
|
|
@@ -576,11 +605,11 @@ export function MotionSlot(t0) {
|
|
|
576
605
|
play({ transform: [`translateX(${delta_0}px)`, "translateX(0px)"] }, SPRING_THUMB);
|
|
577
606
|
}
|
|
578
607
|
});
|
|
579
|
-
|
|
608
|
+
observer_3.observe(element, {
|
|
580
609
|
attributes: true,
|
|
581
610
|
attributeFilter: ["data-state"]
|
|
582
611
|
});
|
|
583
|
-
cleanups.push(() =>
|
|
612
|
+
cleanups.push(() => observer_3.disconnect());
|
|
584
613
|
const root = element.parentElement;
|
|
585
614
|
if (root && typeof element.animate === "function") {
|
|
586
615
|
const squishTiming = {
|
|
@@ -597,9 +626,9 @@ export function MotionSlot(t0) {
|
|
|
597
626
|
squish?.cancel();
|
|
598
627
|
squish = element.animate({ scale: ["1", String(MOTION_THUMB_SQUISH_SCALE)] }, squishTiming);
|
|
599
628
|
return () => {
|
|
600
|
-
const
|
|
629
|
+
const from_0 = getComputedStyle(element).scale;
|
|
601
630
|
squish?.cancel();
|
|
602
|
-
const release = element.animate({ scale: [
|
|
631
|
+
const release = element.animate({ scale: [from_0 === "none" ? "1" : from_0, "1"] }, squishTiming);
|
|
603
632
|
squish = release;
|
|
604
633
|
release.finished.then(() => {
|
|
605
634
|
if (squish === release) {
|