@waveso/ui 0.15.1 → 0.17.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 +13 -12
- package/dist/accordion.js +2 -2
- package/dist/action-bar.js +4 -4
- package/dist/alert-dialog.js +5 -5
- package/dist/alert.d.ts +8 -1
- package/dist/alert.js +13 -4
- package/dist/animate.d.ts +1 -1
- package/dist/animate.js +2 -9
- package/dist/autocomplete.js +3 -3
- package/dist/avatar.d.ts +1 -1
- package/dist/avatar.js +4 -4
- package/dist/badge.d.ts +1 -1
- package/dist/badge.js +6 -6
- package/dist/button-group.d.ts +4 -2
- package/dist/button-group.js +5 -4
- package/dist/button.d.ts +1 -1
- package/dist/button.js +6 -6
- package/dist/card.d.ts +11 -4
- package/dist/card.js +27 -14
- package/dist/checkbox.js +2 -2
- package/dist/combobox.js +4 -4
- package/dist/context-menu.d.ts +2 -1
- package/dist/context-menu.js +14 -14
- package/dist/dialog.js +5 -5
- package/dist/drawer.js +1 -1
- package/dist/encrypted-text.d.ts +15 -1
- package/dist/encrypted-text.js +205 -67
- package/dist/field.d.ts +21 -3
- package/dist/field.js +36 -8
- package/dist/fieldset.d.ts +14 -0
- package/dist/fieldset.js +26 -0
- package/dist/form.d.ts +14 -14
- package/dist/form.js +7 -4
- package/dist/glyph-matrix.d.ts +25 -0
- package/dist/glyph-matrix.js +266 -0
- package/dist/gradient-reveal-text.d.ts +3 -3
- package/dist/gradient-reveal-text.js +8 -8
- package/dist/input-group.js +1 -1
- package/dist/input-otp.d.ts +3 -3
- package/dist/input-otp.js +2 -2
- package/dist/input.js +1 -1
- package/dist/item.d.ts +1 -1
- package/dist/item.js +17 -11
- package/dist/kbd.d.ts +1 -1
- package/dist/kbd.js +2 -2
- package/dist/label.js +1 -1
- package/dist/lib/merge-refs.d.ts +9 -0
- package/dist/lib/merge-refs.js +16 -0
- package/dist/lib/utils.js +7 -0
- package/dist/masonry.d.ts +1 -1
- package/dist/masonry.js +4 -2
- package/dist/menu.d.ts +2 -1
- package/dist/menu.js +7 -7
- package/dist/menubar.js +1 -1
- package/dist/pagination.js +1 -1
- package/dist/popover.js +1 -1
- package/dist/preview-card.js +1 -1
- package/dist/progress.js +2 -2
- package/dist/radio-group.js +1 -1
- package/dist/radio.js +2 -2
- package/dist/resizable.d.ts +43 -0
- package/dist/resizable.js +53 -0
- package/dist/scroll-area.d.ts +13 -2
- package/dist/scroll-area.js +26 -7
- package/dist/select.js +4 -4
- package/dist/separator.js +1 -1
- package/dist/sidebar.js +12 -12
- package/dist/slider.js +3 -3
- package/dist/spinner.d.ts +7 -0
- package/dist/spinner.js +24 -6
- package/dist/styles.css +581 -194
- package/dist/swipe-deck.d.ts +98 -0
- package/dist/swipe-deck.js +931 -0
- package/dist/switch.js +6 -6
- package/dist/table.d.ts +7 -1
- package/dist/table.js +40 -9
- package/dist/tabs.js +3 -3
- package/dist/textarea.js +1 -1
- package/dist/toast.js +4 -4
- package/dist/toggle-group.js +3 -3
- package/dist/toggle.d.ts +1 -1
- package/dist/toggle.js +11 -6
- package/dist/tooltip.d.ts +3 -1
- package/dist/tooltip.js +10 -6
- package/dist/video-text.d.ts +25 -0
- package/dist/video-text.js +147 -0
- package/package.json +6 -1
package/dist/switch.js
CHANGED
|
@@ -4,17 +4,17 @@ import { jsx } from "react/jsx-runtime";
|
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Switch as Switch$1 } from "@base-ui/react/switch";
|
|
6
6
|
//#region src/switch.tsx
|
|
7
|
-
const switchVariants = cva("cursor-clickable data-checked:bg-primary data-unchecked:bg-
|
|
7
|
+
const switchVariants = cva("@container cursor-clickable data-checked:bg-primary data-unchecked:bg-shade data-unchecked:hover:border-focus focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-danger/30 aria-invalid:border-danger peer relative inline-flex shrink-0 items-center rounded-full border border-transparent motion-color outline-hidden after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 aria-invalid:ring-3 data-disabled:pointer-events-none data-disabled:opacity-30", {
|
|
8
8
|
variants: { size: {
|
|
9
|
-
default: "h-
|
|
10
|
-
sm: "h-
|
|
9
|
+
default: "h-6 w-11 p-1",
|
|
10
|
+
sm: "h-4 w-7 p-0.5"
|
|
11
11
|
} },
|
|
12
12
|
defaultVariants: { size: "default" }
|
|
13
13
|
});
|
|
14
|
-
const switchThumbVariants = cva("bg-
|
|
14
|
+
const switchThumbVariants = cva("bg-white pointer-events-none block rounded-full ring-0 transition-transform data-checked:translate-x-[calc(100cqw_-_100%)] rtl:data-checked:-translate-x-[calc(100cqw_-_100%)] data-unchecked:translate-x-0 rtl:data-unchecked:-translate-x-0", {
|
|
15
15
|
variants: { size: {
|
|
16
|
-
default: "
|
|
17
|
-
sm: "
|
|
16
|
+
default: "h-4 w-6",
|
|
17
|
+
sm: "h-3 w-4"
|
|
18
18
|
} },
|
|
19
19
|
defaultVariants: { size: "default" }
|
|
20
20
|
});
|
package/dist/table.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ type TableRowProps = React.ComponentProps<"tr">;
|
|
|
8
8
|
type TableHeadProps = React.ComponentProps<"th">;
|
|
9
9
|
type TableCellProps = React.ComponentProps<"td">;
|
|
10
10
|
type TableCaptionProps = React.ComponentProps<"caption">;
|
|
11
|
+
/**
|
|
12
|
+
* A data table. When it's wider than its container it scrolls sideways, and
|
|
13
|
+
* only then does the container become a focusable, named region, so keyboard
|
|
14
|
+
* users can scroll it: a table that fits isn't a stray tab stop. The region
|
|
15
|
+
* takes its name from the `TableCaption`, or from the table's `aria-label`.
|
|
16
|
+
*/
|
|
11
17
|
declare function Table({ className, ...props }: TableProps): React.JSX.Element;
|
|
12
18
|
declare function TableHeader({ className, ...props }: TableHeaderProps): React.JSX.Element;
|
|
13
19
|
declare function TableBody({ className, ...props }: TableBodyProps): React.JSX.Element;
|
|
@@ -15,6 +21,6 @@ declare function TableFooter({ className, ...props }: TableFooterProps): React.J
|
|
|
15
21
|
declare function TableRow({ className, ...props }: TableRowProps): React.JSX.Element;
|
|
16
22
|
declare function TableHead({ className, ...props }: TableHeadProps): React.JSX.Element;
|
|
17
23
|
declare function TableCell({ className, ...props }: TableCellProps): React.JSX.Element;
|
|
18
|
-
declare function TableCaption({ className, ...props }: TableCaptionProps): React.JSX.Element;
|
|
24
|
+
declare function TableCaption({ className, id, ...props }: TableCaptionProps): React.JSX.Element;
|
|
19
25
|
//#endregion
|
|
20
26
|
export { Table, TableBody, type TableBodyProps, TableCaption, type TableCaptionProps, TableCell, type TableCellProps, TableFooter, type TableFooterProps, TableHead, type TableHeadProps, TableHeader, type TableHeaderProps, type TableProps, TableRow, type TableRowProps };
|
package/dist/table.js
CHANGED
|
@@ -1,14 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { cn } from "./lib/utils.js";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import * as React from "react";
|
|
3
5
|
//#region src/table.tsx
|
|
6
|
+
/**
|
|
7
|
+
* A data table. When it's wider than its container it scrolls sideways, and
|
|
8
|
+
* only then does the container become a focusable, named region, so keyboard
|
|
9
|
+
* users can scroll it: a table that fits isn't a stray tab stop. The region
|
|
10
|
+
* takes its name from the `TableCaption`, or from the table's `aria-label`.
|
|
11
|
+
*/
|
|
4
12
|
function Table({ className, ...props }) {
|
|
13
|
+
const containerRef = React.useRef(null);
|
|
14
|
+
const tableRef = React.useRef(null);
|
|
15
|
+
const [isScrollable, setIsScrollable] = React.useState(false);
|
|
16
|
+
const [captionId, setCaptionId] = React.useState();
|
|
17
|
+
React.useLayoutEffect(() => {
|
|
18
|
+
const container = containerRef.current;
|
|
19
|
+
const table = tableRef.current;
|
|
20
|
+
if (!container || !table) return;
|
|
21
|
+
const measure = () => {
|
|
22
|
+
setIsScrollable(container.scrollWidth > container.clientWidth);
|
|
23
|
+
setCaptionId(table.querySelector(":scope > caption")?.id || void 0);
|
|
24
|
+
};
|
|
25
|
+
measure();
|
|
26
|
+
const observer = new ResizeObserver(measure);
|
|
27
|
+
observer.observe(container);
|
|
28
|
+
observer.observe(table);
|
|
29
|
+
return () => observer.disconnect();
|
|
30
|
+
}, []);
|
|
5
31
|
return /* @__PURE__ */ jsx("div", {
|
|
32
|
+
ref: containerRef,
|
|
6
33
|
"data-slot": "table-container",
|
|
7
|
-
role: "region",
|
|
8
|
-
"aria-
|
|
9
|
-
|
|
34
|
+
role: isScrollable ? "region" : void 0,
|
|
35
|
+
"aria-labelledby": isScrollable ? captionId : void 0,
|
|
36
|
+
"aria-label": isScrollable && !captionId ? props["aria-label"] : void 0,
|
|
37
|
+
tabIndex: isScrollable ? 0 : void 0,
|
|
10
38
|
className: "relative w-full overflow-x-auto focus-visible:outline-hidden focus-visible:ring-3 focus-visible:ring-focus/50",
|
|
11
39
|
children: /* @__PURE__ */ jsx("table", {
|
|
40
|
+
ref: tableRef,
|
|
12
41
|
"data-slot": "table",
|
|
13
42
|
className: cn("w-full caption-bottom text-sm", className),
|
|
14
43
|
...props
|
|
@@ -18,7 +47,7 @@ function Table({ className, ...props }) {
|
|
|
18
47
|
function TableHeader({ className, ...props }) {
|
|
19
48
|
return /* @__PURE__ */ jsx("thead", {
|
|
20
49
|
"data-slot": "table-header",
|
|
21
|
-
className: cn("[&_tr]:border-b [&_tr]:border-
|
|
50
|
+
className: cn("[&_tr]:border-b [&_tr]:border-contour", className),
|
|
22
51
|
...props
|
|
23
52
|
});
|
|
24
53
|
}
|
|
@@ -32,33 +61,35 @@ function TableBody({ className, ...props }) {
|
|
|
32
61
|
function TableFooter({ className, ...props }) {
|
|
33
62
|
return /* @__PURE__ */ jsx("tfoot", {
|
|
34
63
|
"data-slot": "table-footer",
|
|
35
|
-
className: cn("bg-secondary/50 border-t border-
|
|
64
|
+
className: cn("bg-secondary/50 border-t border-contour font-medium [&>tr]:last:border-b-0", className),
|
|
36
65
|
...props
|
|
37
66
|
});
|
|
38
67
|
}
|
|
39
68
|
function TableRow({ className, ...props }) {
|
|
40
69
|
return /* @__PURE__ */ jsx("tr", {
|
|
41
70
|
"data-slot": "table-row",
|
|
42
|
-
className: cn("hover:bg-
|
|
71
|
+
className: cn("hover:bg-highlight data-selected:bg-primary data-selected:text-white data-selected:**:data-[slot=checkbox]:border-white data-selected:**:focus-visible:ring-white/50 border-b border-contour motion-color", className),
|
|
43
72
|
...props
|
|
44
73
|
});
|
|
45
74
|
}
|
|
46
75
|
function TableHead({ className, ...props }) {
|
|
47
76
|
return /* @__PURE__ */ jsx("th", {
|
|
48
77
|
"data-slot": "table-head",
|
|
49
|
-
className: cn("text-contrast h-
|
|
78
|
+
className: cn("text-contrast h-12 px-3 text-start align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pe-0", className),
|
|
50
79
|
...props
|
|
51
80
|
});
|
|
52
81
|
}
|
|
53
82
|
function TableCell({ className, ...props }) {
|
|
54
83
|
return /* @__PURE__ */ jsx("td", {
|
|
55
84
|
"data-slot": "table-cell",
|
|
56
|
-
className: cn("p-
|
|
85
|
+
className: cn("p-3 align-middle whitespace-nowrap [&:has([role=checkbox])]:pe-0", className),
|
|
57
86
|
...props
|
|
58
87
|
});
|
|
59
88
|
}
|
|
60
|
-
function TableCaption({ className, ...props }) {
|
|
89
|
+
function TableCaption({ className, id, ...props }) {
|
|
90
|
+
const generatedId = React.useId();
|
|
61
91
|
return /* @__PURE__ */ jsx("caption", {
|
|
92
|
+
id: id ?? generatedId,
|
|
62
93
|
"data-slot": "table-caption",
|
|
63
94
|
className: cn("text-muted mt-4 text-sm", className),
|
|
64
95
|
...props
|
package/dist/tabs.js
CHANGED
|
@@ -4,9 +4,9 @@ import { jsx } from "react/jsx-runtime";
|
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Tabs as Tabs$1 } from "@base-ui/react/tabs";
|
|
6
6
|
//#region src/tabs.tsx
|
|
7
|
-
const tabsListVariants = cva("group/tabs-list inline-flex w-fit items-center justify-center rounded-md p-
|
|
7
|
+
const tabsListVariants = cva("group/tabs-list inline-flex w-fit items-center justify-center rounded-md p-1 text-muted group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", {
|
|
8
8
|
variants: { variant: {
|
|
9
|
-
default: "bg-
|
|
9
|
+
default: "bg-shade",
|
|
10
10
|
line: "gap-1 bg-transparent"
|
|
11
11
|
} },
|
|
12
12
|
defaultVariants: { variant: "default" }
|
|
@@ -30,7 +30,7 @@ function TabsList({ className, variant = "default", ...props }) {
|
|
|
30
30
|
function TabsTrigger({ className, ...props }) {
|
|
31
31
|
return /* @__PURE__ */ jsx(Tabs$1.Tab, {
|
|
32
32
|
"data-slot": "tabs-trigger",
|
|
33
|
-
className: cn("cursor-clickable text-
|
|
33
|
+
className: cn("cursor-clickable text-muted hover:text-contrast relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-2 rounded-sm border border-transparent px-3 py-1 has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 text-sm font-medium whitespace-nowrap motion-color outline-hidden focus-visible:border-focus focus-visible:ring-focus/50 focus-visible:ring-3 data-disabled:pointer-events-none data-disabled:opacity-30 group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start group-data-[orientation=vertical]/tabs:py-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "data-active:bg-primary data-active:text-white", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent group-data-[variant=line]/tabs-list:data-active:text-contrast", "after:bg-contrast after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-end-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", className),
|
|
34
34
|
...props
|
|
35
35
|
});
|
|
36
36
|
}
|
package/dist/textarea.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cn } from "./lib/utils.js";
|
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
//#region src/textarea.tsx
|
|
6
|
-
const textareaVariants = cva("bg-
|
|
6
|
+
const textareaVariants = cva("bg-shade hover:border-focus focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-danger/30 aria-invalid:border-danger resize-y resize-handle disabled:pointer-events-none placeholder:text-soft flex field-sizing-content w-full rounded-md border border-transparent motion-color outline-hidden focus-visible:ring-3 disabled:opacity-30 aria-invalid:ring-3", {
|
|
7
7
|
variants: { size: {
|
|
8
8
|
xs: "min-h-12 rounded-sm px-2 py-1 text-xs",
|
|
9
9
|
sm: "min-h-14 px-3 py-1.5 text-sm",
|
package/dist/toast.js
CHANGED
|
@@ -8,11 +8,11 @@ import { Toast as Toast$1 } from "@base-ui/react/toast";
|
|
|
8
8
|
//#region src/toast.tsx
|
|
9
9
|
const toastRootClass = "bg-elevated text-contrast ring-contrast/10 rounded-md p-4 text-sm shadow-md ring-1 outline-hidden";
|
|
10
10
|
const toastIconVariants = cva("mt-0.5 size-4 shrink-0", { variants: { type: {
|
|
11
|
-
loading: "text-muted
|
|
11
|
+
loading: "text-muted motion-spin-sm",
|
|
12
12
|
success: "text-success",
|
|
13
13
|
info: "text-info",
|
|
14
14
|
warning: "text-warning",
|
|
15
|
-
error: "text-
|
|
15
|
+
error: "text-danger"
|
|
16
16
|
} } });
|
|
17
17
|
const toastViewportVariants = cva("fixed z-[100] flex w-full outline-hidden sm:max-w-sm", {
|
|
18
18
|
variants: { position: {
|
|
@@ -113,14 +113,14 @@ function ToastDescription({ className, ...props }) {
|
|
|
113
113
|
function ToastAction({ className, ...props }) {
|
|
114
114
|
return /* @__PURE__ */ jsx(Toast$1.Action, {
|
|
115
115
|
"data-slot": "toast-action",
|
|
116
|
-
className: cn("hover:bg-
|
|
116
|
+
className: cn("hover:bg-highlight inline-flex h-7 items-center rounded-md border border-contour px-2.5 text-xs font-medium transition-colors outline-hidden focus-visible:border-focus focus-visible:ring-focus/50 focus-visible:ring-3", className),
|
|
117
117
|
...props
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
function ToastClose({ className, children, ...props }) {
|
|
121
121
|
return /* @__PURE__ */ jsx(Toast$1.Close, {
|
|
122
122
|
"data-slot": "toast-close",
|
|
123
|
-
className: cn("text-muted hover:text-contrast shrink-0 rounded-md p-0.5 transition-colors outline-hidden focus-visible:ring-focus/50 focus-visible:ring-3", className),
|
|
123
|
+
className: cn("text-muted hover:bg-highlight hover:text-contrast shrink-0 rounded-md p-0.5 transition-colors outline-hidden focus-visible:ring-focus/50 focus-visible:ring-3", className),
|
|
124
124
|
...props,
|
|
125
125
|
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CloseIcon, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
126
126
|
className: "sr-only",
|
package/dist/toggle-group.js
CHANGED
|
@@ -9,10 +9,10 @@ import { ToggleGroup as ToggleGroup$1 } from "@base-ui/react/toggle-group";
|
|
|
9
9
|
const ToggleGroupContext = React.createContext({
|
|
10
10
|
size: void 0,
|
|
11
11
|
variant: void 0,
|
|
12
|
-
spacing:
|
|
12
|
+
spacing: 2,
|
|
13
13
|
orientation: "horizontal"
|
|
14
14
|
});
|
|
15
|
-
function ToggleGroup({ className, variant, size, spacing =
|
|
15
|
+
function ToggleGroup({ className, variant, size, spacing = 2, orientation = "horizontal", style, children, ...props }) {
|
|
16
16
|
const contextValue = React.useMemo(() => ({
|
|
17
17
|
variant,
|
|
18
18
|
size,
|
|
@@ -49,7 +49,7 @@ function ToggleGroupItem({ className, children, variant = "default", size = "def
|
|
|
49
49
|
"data-variant": context.variant ?? variant,
|
|
50
50
|
"data-size": context.size ?? size,
|
|
51
51
|
"data-spacing": context.spacing,
|
|
52
|
-
className: cn("shrink-0 group-data-[spacing=0]/toggle-group:
|
|
52
|
+
className: cn("shrink-0 group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:not-first:rounded-l-none group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:not-last:rounded-r-none group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:not-first:rounded-t-none group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:not-last:rounded-b-none group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", toggleVariants({
|
|
53
53
|
variant: context.variant ?? variant,
|
|
54
54
|
size: context.size ?? size
|
|
55
55
|
}), className),
|
package/dist/toggle.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Toggle as Toggle$1 } from "@base-ui/react/toggle";
|
|
|
4
4
|
//#region src/toggle.d.ts
|
|
5
5
|
declare const toggleVariants: (props?: ({
|
|
6
6
|
variant?: "default" | "outline" | null | undefined;
|
|
7
|
-
size?: "default" | "lg" | "sm" | null | undefined;
|
|
7
|
+
size?: "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | null | undefined;
|
|
8
8
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
9
9
|
type ToggleProps = React.ComponentProps<typeof Toggle$1> & VariantProps<typeof toggleVariants>;
|
|
10
10
|
declare function Toggle({ className, variant, size, ...props }: ToggleProps): React.JSX.Element;
|
package/dist/toggle.js
CHANGED
|
@@ -4,16 +4,21 @@ import { jsx } from "react/jsx-runtime";
|
|
|
4
4
|
import { cva } from "class-variance-authority";
|
|
5
5
|
import { Toggle as Toggle$1 } from "@base-ui/react/toggle";
|
|
6
6
|
//#region src/toggle.tsx
|
|
7
|
-
const toggleVariants = cva("cursor-clickable
|
|
7
|
+
const toggleVariants = cva("cursor-clickable focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-danger/30 aria-invalid:border-danger rounded-md border border-transparent bg-clip-padding font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap motion-color data-disabled:pointer-events-none data-disabled:opacity-30 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-hidden group/toggle select-none active:scale-[0.97]", {
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
10
|
-
default: "bg-
|
|
11
|
-
outline: "border-
|
|
10
|
+
default: "text-contrast hover:bg-highlight data-pressed:bg-primary data-pressed:text-white data-pressed:hover:bg-primary/80 not-supports-backdrop-filter:data-pressed:hover:bg-primary/90 data-pressed:hover:backdrop-blur-xl data-pressed:hover:backdrop-saturate-150",
|
|
11
|
+
outline: "border-shade bg-foundation hover:bg-highlight hover:text-contrast data-pressed:border-primary data-pressed:bg-primary data-pressed:text-white data-pressed:hover:bg-primary/80 not-supports-backdrop-filter:data-pressed:hover:bg-primary/90 data-pressed:hover:backdrop-blur-xl data-pressed:hover:backdrop-saturate-150"
|
|
12
12
|
},
|
|
13
13
|
size: {
|
|
14
|
-
default: "h-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
default: "h-9 min-w-9 gap-1.5 px-3 text-base has-data-[icon=inline-end]:pe-2.5 has-data-[icon=inline-start]:ps-2.5",
|
|
15
|
+
xs: "h-6 min-w-6 gap-1 rounded-sm px-2 text-xs has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2 [&_svg:not([class*='size-'])]:size-3",
|
|
16
|
+
sm: "h-8 min-w-8 gap-1 px-3 text-sm has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2",
|
|
17
|
+
lg: "h-11 min-w-11 gap-1.5 px-4 text-lg has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3",
|
|
18
|
+
icon: "size-9",
|
|
19
|
+
"icon-xs": "size-6 rounded-sm in-data-[slot=toggle-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
20
|
+
"icon-sm": "size-8",
|
|
21
|
+
"icon-lg": "size-11"
|
|
17
22
|
}
|
|
18
23
|
},
|
|
19
24
|
defaultVariants: {
|
package/dist/tooltip.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ type TooltipContentProps = React.ComponentProps<typeof Tooltip$1.Popup> & {
|
|
|
11
11
|
alignOffset?: React.ComponentProps<typeof Tooltip$1.Positioner>["alignOffset"];
|
|
12
12
|
anchor?: React.ComponentProps<typeof Tooltip$1.Positioner>["anchor"];
|
|
13
13
|
positionerClassName?: string;
|
|
14
|
+
/** Render the pointer arrow. Defaults to `false`. */
|
|
15
|
+
arrow?: boolean;
|
|
14
16
|
};
|
|
15
17
|
/**
|
|
16
18
|
* Inherits Base UI's 600ms open delay (hover-intent) — we deliberately
|
|
@@ -26,6 +28,6 @@ declare function TooltipProvider({ ...props }: TooltipProviderProps): React.JSX.
|
|
|
26
28
|
*/
|
|
27
29
|
declare function Tooltip({ disableHoverablePopup, ...props }: TooltipProps): React.JSX.Element;
|
|
28
30
|
declare function TooltipTrigger({ children, ...props }: TooltipTriggerProps): React.JSX.Element;
|
|
29
|
-
declare function TooltipContent({ className, positionerClassName, side, sideOffset, align, alignOffset, anchor, children, ...props }: TooltipContentProps): React.JSX.Element;
|
|
31
|
+
declare function TooltipContent({ className, positionerClassName, side, sideOffset, align, alignOffset, anchor, arrow, children, ...props }: TooltipContentProps): React.JSX.Element;
|
|
30
32
|
//#endregion
|
|
31
33
|
export { Tooltip, TooltipContent, type TooltipContentProps, type TooltipProps, TooltipProvider, type TooltipProviderProps, TooltipTrigger, type TooltipTriggerProps };
|
package/dist/tooltip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "./lib/utils.js";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
|
|
6
6
|
//#region src/tooltip.tsx
|
|
@@ -41,20 +41,24 @@ function TooltipTrigger({ children, ...props }) {
|
|
|
41
41
|
children
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
-
function TooltipContent({ className, positionerClassName, side = "top", sideOffset
|
|
44
|
+
function TooltipContent({ className, positionerClassName, side = "top", sideOffset, align = "center", alignOffset = 0, anchor, arrow = false, children, ...props }) {
|
|
45
|
+
const resolvedSideOffset = sideOffset ?? (arrow ? 12 : 8);
|
|
45
46
|
return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(Tooltip$1.Positioner, {
|
|
46
47
|
"data-slot": "tooltip-positioner",
|
|
47
48
|
anchor,
|
|
48
49
|
align,
|
|
49
50
|
alignOffset,
|
|
50
51
|
side,
|
|
51
|
-
sideOffset,
|
|
52
|
+
sideOffset: resolvedSideOffset,
|
|
52
53
|
className: cn("isolate z-50", positionerClassName),
|
|
53
|
-
children: /* @__PURE__ */
|
|
54
|
+
children: /* @__PURE__ */ jsxs(Tooltip$1.Popup, {
|
|
54
55
|
"data-slot": "tooltip-content",
|
|
55
|
-
className: cn("motion-pop-md bg-surface-inverse text-contrast-inverse z-50 w-fit max-w-xs
|
|
56
|
+
className: cn("motion-pop-md bg-surface-inverse text-contrast-inverse z-50 w-fit max-w-xs rounded-sm px-3 py-1.5 text-xs", "inline-flex items-center gap-1.5 has-data-[slot=kbd]:pe-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-lg", className),
|
|
56
57
|
...props,
|
|
57
|
-
children
|
|
58
|
+
children: [children, arrow ? /* @__PURE__ */ jsx(Tooltip$1.Arrow, {
|
|
59
|
+
"data-slot": "tooltip-arrow",
|
|
60
|
+
className: "bg-surface-inverse size-2.5 rounded-[2px] translate-y-[calc(-50%-2px)] rotate-45 data-[side=bottom]:top-1 data-[side=inline-end]:-start-1 data-[side=inline-end]:translate-x-[1.5px] rtl:data-[side=inline-end]:-translate-x-[1.5px] data-[side=inline-end]:translate-y-0 data-[side=inline-start]:-end-1 data-[side=inline-start]:translate-x-[-1.5px] rtl:data-[side=inline-start]:-translate-x-[-1.5px] data-[side=inline-start]:translate-y-0 data-[side=left]:-right-1 data-[side=left]:translate-x-[-1.5px] rtl:data-[side=left]:-translate-x-[-1.5px] data-[side=left]:translate-y-0 data-[side=right]:-left-1 data-[side=right]:translate-x-[1.5px] rtl:data-[side=right]:-translate-x-[1.5px] data-[side=right]:translate-y-0 data-[side=top]:-bottom-2.5"
|
|
61
|
+
}) : null]
|
|
58
62
|
})
|
|
59
63
|
}) });
|
|
60
64
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
//#region src/video-text.d.ts
|
|
4
|
+
interface VideoTextProps extends Omit<useRender.ComponentProps<"div">, "children"> {
|
|
5
|
+
/** Video source URL. */
|
|
6
|
+
src: string;
|
|
7
|
+
/** The text the video shows through. Rendered as real, selectable text. */
|
|
8
|
+
text: string;
|
|
9
|
+
/** Image shown in the letters until the video can play. */
|
|
10
|
+
poster?: string;
|
|
11
|
+
autoPlay?: boolean;
|
|
12
|
+
muted?: boolean;
|
|
13
|
+
loop?: boolean;
|
|
14
|
+
preload?: "auto" | "metadata" | "none";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Text cut out of a playing video. The text is real: it wraps, aligns and
|
|
18
|
+
* takes its font from the usual classes (`text-*`, `font-*`, web fonts
|
|
19
|
+
* included), and `render` can make it a heading. Each word is measured and
|
|
20
|
+
* copied into an SVG clip path that the video is clipped to. Until that's
|
|
21
|
+
* measured, and while the video loads, the letters show in the text color.
|
|
22
|
+
*/
|
|
23
|
+
declare function VideoText({ src, text, poster, autoPlay, muted, loop, preload, className, render, ref, ...props }: VideoTextProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { VideoText, type VideoTextProps };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "./lib/utils.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
6
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
7
|
+
//#region src/video-text.tsx
|
|
8
|
+
function isSameLayout(a, b) {
|
|
9
|
+
return a.length === b.length && a.every((box, index) => box.x === b[index]?.x && box.y === b[index]?.y);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Text cut out of a playing video. The text is real: it wraps, aligns and
|
|
13
|
+
* takes its font from the usual classes (`text-*`, `font-*`, web fonts
|
|
14
|
+
* included), and `render` can make it a heading. Each word is measured and
|
|
15
|
+
* copied into an SVG clip path that the video is clipped to. Until that's
|
|
16
|
+
* measured, and while the video loads, the letters show in the text color.
|
|
17
|
+
*/
|
|
18
|
+
function VideoText({ src, text, poster, autoPlay = true, muted = true, loop = true, preload = "auto", className, render, ref, ...props }) {
|
|
19
|
+
const rootRef = React.useRef(null);
|
|
20
|
+
const labelRef = React.useRef(null);
|
|
21
|
+
const baselineRef = React.useRef(null);
|
|
22
|
+
const videoRef = React.useRef(null);
|
|
23
|
+
const [boxes, setBoxes] = React.useState([]);
|
|
24
|
+
const clipId = `video-text-${React.useId().replace(/[^\w-]/g, "")}`;
|
|
25
|
+
const clipPath = `url(#${clipId})`;
|
|
26
|
+
const words = React.useMemo(() => text.split(/\s+/).filter(Boolean), [text]);
|
|
27
|
+
const isMeasured = words.length > 0 && boxes.length === words.length;
|
|
28
|
+
React.useLayoutEffect(() => {
|
|
29
|
+
const root = rootRef.current;
|
|
30
|
+
const label = labelRef.current;
|
|
31
|
+
const baseline = baselineRef.current;
|
|
32
|
+
if (!root || !label || !baseline) return;
|
|
33
|
+
const measure = () => {
|
|
34
|
+
const rootRect = root.getBoundingClientRect();
|
|
35
|
+
const scale = root.offsetWidth ? rootRect.width / root.offsetWidth : 1;
|
|
36
|
+
const left = rootRect.left + root.clientLeft * scale;
|
|
37
|
+
const top = rootRect.top + root.clientTop * scale;
|
|
38
|
+
const spans = label.querySelectorAll("[data-slot=\"video-text-word\"]");
|
|
39
|
+
const first = spans[0]?.getBoundingClientRect();
|
|
40
|
+
const offset = first ? baseline.getBoundingClientRect().top - first.top : 0;
|
|
41
|
+
const next = words.map((_, index) => {
|
|
42
|
+
const rect = spans[index]?.getBoundingClientRect();
|
|
43
|
+
return rect ? {
|
|
44
|
+
x: (rect.left - left) / scale,
|
|
45
|
+
y: (rect.top + offset - top) / scale
|
|
46
|
+
} : {
|
|
47
|
+
x: 0,
|
|
48
|
+
y: 0
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
setBoxes((prev) => isSameLayout(prev, next) ? prev : next);
|
|
52
|
+
};
|
|
53
|
+
measure();
|
|
54
|
+
const observer = new ResizeObserver(measure);
|
|
55
|
+
observer.observe(root);
|
|
56
|
+
document.fonts.addEventListener("loadingdone", measure);
|
|
57
|
+
return () => {
|
|
58
|
+
observer.disconnect();
|
|
59
|
+
document.fonts.removeEventListener("loadingdone", measure);
|
|
60
|
+
};
|
|
61
|
+
}, [words]);
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
const video = videoRef.current;
|
|
64
|
+
if (!video) return;
|
|
65
|
+
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
66
|
+
const hold = () => {
|
|
67
|
+
if (reducedMotion.matches) video.pause();
|
|
68
|
+
};
|
|
69
|
+
const sync = () => {
|
|
70
|
+
if (reducedMotion.matches) video.pause();
|
|
71
|
+
else if (video.autoplay) video.play().catch(() => {});
|
|
72
|
+
};
|
|
73
|
+
hold();
|
|
74
|
+
video.addEventListener("play", hold);
|
|
75
|
+
reducedMotion.addEventListener("change", sync);
|
|
76
|
+
return () => {
|
|
77
|
+
video.removeEventListener("play", hold);
|
|
78
|
+
reducedMotion.removeEventListener("change", sync);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
82
|
+
/* @__PURE__ */ jsxs("span", {
|
|
83
|
+
ref: labelRef,
|
|
84
|
+
"data-slot": "video-text-label",
|
|
85
|
+
className: cn(isMeasured && "text-transparent"),
|
|
86
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
87
|
+
ref: baselineRef,
|
|
88
|
+
"data-slot": "video-text-baseline",
|
|
89
|
+
className: "inline-block size-0 align-baseline"
|
|
90
|
+
}), words.map((word, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [index > 0 && " ", /* @__PURE__ */ jsx("span", {
|
|
91
|
+
"data-slot": "video-text-word",
|
|
92
|
+
children: word
|
|
93
|
+
})] }, `${index}-${word}`))]
|
|
94
|
+
}),
|
|
95
|
+
/* @__PURE__ */ jsx("svg", {
|
|
96
|
+
"data-slot": "video-text-defs",
|
|
97
|
+
"aria-hidden": "true",
|
|
98
|
+
className: "absolute size-0 overflow-hidden [direction:ltr]",
|
|
99
|
+
children: /* @__PURE__ */ jsx("clipPath", {
|
|
100
|
+
id: clipId,
|
|
101
|
+
"data-slot": "video-text-clip",
|
|
102
|
+
clipPathUnits: "userSpaceOnUse",
|
|
103
|
+
children: words.map((word, index) => /* @__PURE__ */ jsx("text", {
|
|
104
|
+
"data-slot": "video-text-clip-word",
|
|
105
|
+
x: boxes[index]?.x ?? 0,
|
|
106
|
+
y: boxes[index]?.y ?? 0,
|
|
107
|
+
children: word
|
|
108
|
+
}, `${index}-${word}`))
|
|
109
|
+
})
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ jsx("span", {
|
|
112
|
+
"data-slot": "video-text-mask",
|
|
113
|
+
"aria-hidden": "true",
|
|
114
|
+
className: cn("pointer-events-none absolute inset-0 bg-current", !isMeasured && "invisible"),
|
|
115
|
+
style: {
|
|
116
|
+
clipPath,
|
|
117
|
+
WebkitClipPath: clipPath
|
|
118
|
+
},
|
|
119
|
+
children: /* @__PURE__ */ jsx("video", {
|
|
120
|
+
ref: videoRef,
|
|
121
|
+
"data-slot": "video-text-video",
|
|
122
|
+
className: "block size-full object-cover",
|
|
123
|
+
src,
|
|
124
|
+
poster,
|
|
125
|
+
autoPlay,
|
|
126
|
+
muted,
|
|
127
|
+
loop,
|
|
128
|
+
preload,
|
|
129
|
+
playsInline: true,
|
|
130
|
+
disablePictureInPicture: true,
|
|
131
|
+
disableRemotePlayback: true
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
] });
|
|
135
|
+
return useRender({
|
|
136
|
+
defaultTagName: "div",
|
|
137
|
+
ref: ref ? [rootRef, ref] : rootRef,
|
|
138
|
+
render,
|
|
139
|
+
props: mergeProps({
|
|
140
|
+
className: cn("relative font-sans font-bold", className),
|
|
141
|
+
children: content
|
|
142
|
+
}, props),
|
|
143
|
+
state: { slot: "video-text" }
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { VideoText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waveso/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Wave UI component library built on Base UI and Tailwind CSS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"react": "^19.0.0",
|
|
52
52
|
"react-dom": "^19.0.0",
|
|
53
53
|
"react-hook-form": "^7.0.0",
|
|
54
|
+
"react-resizable-panels": "^4.14.0",
|
|
54
55
|
"tailwind-merge": "^3.0.0",
|
|
55
56
|
"tailwindcss": "^4.0.0"
|
|
56
57
|
},
|
|
@@ -63,6 +64,9 @@
|
|
|
63
64
|
},
|
|
64
65
|
"react-hook-form": {
|
|
65
66
|
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"react-resizable-panels": {
|
|
69
|
+
"optional": true
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"devDependencies": {
|
|
@@ -91,6 +95,7 @@
|
|
|
91
95
|
"react": "^19.3.0",
|
|
92
96
|
"react-dom": "^19.3.0",
|
|
93
97
|
"react-hook-form": "^7.89.0",
|
|
98
|
+
"react-resizable-panels": "^4.14.6",
|
|
94
99
|
"storybook": "~10.6.0",
|
|
95
100
|
"tailwind-merge": "^3.7.0",
|
|
96
101
|
"tailwindcss": "^4.3.3",
|