@waveso/ui 0.15.1 → 0.16.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.
Files changed (85) hide show
  1. package/README.md +13 -12
  2. package/dist/accordion.js +2 -2
  3. package/dist/action-bar.js +4 -4
  4. package/dist/alert-dialog.js +5 -5
  5. package/dist/alert.d.ts +8 -1
  6. package/dist/alert.js +13 -4
  7. package/dist/animate.d.ts +1 -1
  8. package/dist/animate.js +2 -9
  9. package/dist/autocomplete.js +3 -3
  10. package/dist/avatar.d.ts +1 -1
  11. package/dist/avatar.js +4 -4
  12. package/dist/badge.d.ts +1 -1
  13. package/dist/badge.js +6 -6
  14. package/dist/button-group.d.ts +4 -2
  15. package/dist/button-group.js +5 -4
  16. package/dist/button.d.ts +1 -1
  17. package/dist/button.js +6 -6
  18. package/dist/card.d.ts +11 -4
  19. package/dist/card.js +27 -14
  20. package/dist/checkbox.js +2 -2
  21. package/dist/combobox.js +4 -4
  22. package/dist/context-menu.d.ts +2 -1
  23. package/dist/context-menu.js +14 -14
  24. package/dist/dialog.js +5 -5
  25. package/dist/drawer.js +1 -1
  26. package/dist/encrypted-text.d.ts +15 -1
  27. package/dist/encrypted-text.js +205 -67
  28. package/dist/field.d.ts +21 -3
  29. package/dist/field.js +36 -8
  30. package/dist/fieldset.d.ts +14 -0
  31. package/dist/fieldset.js +26 -0
  32. package/dist/form.d.ts +14 -14
  33. package/dist/form.js +7 -4
  34. package/dist/glyph-matrix.d.ts +25 -0
  35. package/dist/glyph-matrix.js +266 -0
  36. package/dist/gradient-reveal-text.d.ts +3 -3
  37. package/dist/gradient-reveal-text.js +8 -8
  38. package/dist/input-group.js +1 -1
  39. package/dist/input-otp.d.ts +3 -3
  40. package/dist/input-otp.js +2 -2
  41. package/dist/input.js +1 -1
  42. package/dist/item.d.ts +1 -1
  43. package/dist/item.js +17 -11
  44. package/dist/kbd.d.ts +1 -1
  45. package/dist/kbd.js +2 -2
  46. package/dist/label.js +1 -1
  47. package/dist/lib/merge-refs.d.ts +9 -0
  48. package/dist/lib/merge-refs.js +16 -0
  49. package/dist/lib/utils.js +7 -0
  50. package/dist/masonry.d.ts +1 -1
  51. package/dist/masonry.js +4 -2
  52. package/dist/menu.d.ts +2 -1
  53. package/dist/menu.js +7 -7
  54. package/dist/menubar.js +1 -1
  55. package/dist/pagination.js +1 -1
  56. package/dist/popover.js +1 -1
  57. package/dist/preview-card.js +1 -1
  58. package/dist/progress.js +2 -2
  59. package/dist/radio-group.js +1 -1
  60. package/dist/radio.js +2 -2
  61. package/dist/resizable.d.ts +43 -0
  62. package/dist/resizable.js +53 -0
  63. package/dist/scroll-area.d.ts +13 -2
  64. package/dist/scroll-area.js +26 -7
  65. package/dist/select.js +4 -4
  66. package/dist/separator.js +1 -1
  67. package/dist/sidebar.js +12 -12
  68. package/dist/slider.js +3 -3
  69. package/dist/spinner.d.ts +7 -0
  70. package/dist/spinner.js +24 -6
  71. package/dist/styles.css +575 -182
  72. package/dist/switch.js +6 -6
  73. package/dist/table.d.ts +7 -1
  74. package/dist/table.js +40 -9
  75. package/dist/tabs.js +3 -3
  76. package/dist/textarea.js +1 -1
  77. package/dist/toast.js +4 -4
  78. package/dist/toggle-group.js +3 -3
  79. package/dist/toggle.d.ts +1 -1
  80. package/dist/toggle.js +11 -6
  81. package/dist/tooltip.d.ts +3 -1
  82. package/dist/tooltip.js +10 -6
  83. package/dist/video-text.d.ts +25 -0
  84. package/dist/video-text.js +147 -0
  85. 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-edge focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive dark:data-unchecked:bg-edge/80 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:cursor-not-allowed data-disabled:opacity-50", {
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-[18.4px] w-[32px]",
10
- sm: "h-[14px] w-[24px]"
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-foundation dark:data-unchecked:bg-contrast dark:data-checked:bg-white pointer-events-none block rounded-full ring-0 transition-transform data-checked:translate-x-[calc(100%-2px)] data-unchecked:translate-x-0", {
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: "size-4",
17
- sm: "size-3"
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-label": "Table",
9
- tabIndex: 0,
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-edge", className),
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-edge font-medium [&>tr]:last:border-b-0", className),
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-secondary/50 aria-selected:bg-secondary border-b border-edge transition-colors", className),
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-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
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-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className),
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-[3px] text-muted group-data-[orientation=horizontal]/tabs:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", {
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-secondary",
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-contrast/60 hover:text-contrast dark:text-muted dark:hover:text-contrast relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-sm border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap motion-color focus-visible:border-focus focus-visible:ring-focus/50 focus-visible:outline-focus focus-visible:ring-3 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "data-active:bg-foundation data-active:text-contrast data-active:shadow-sm dark:data-active:text-contrast dark:data-active:border-edge dark:data-active:bg-edge/30", "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:shadow-none dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent", "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:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", className),
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-edge hover:border-focus focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive 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", {
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 animate-spin",
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-destructive"
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-secondary inline-flex h-7 items-center rounded-md border border-line 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),
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",
@@ -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: 0,
12
+ spacing: 2,
13
13
  orientation: "horizontal"
14
14
  });
15
- function ToggleGroup({ className, variant, size, spacing = 0, orientation = "horizontal", style, children, ...props }) {
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:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-[orientation=horizontal]/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-b-md 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({
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 hover:text-contrast focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive data-pressed:bg-secondary gap-1 rounded-md text-sm font-medium motion-color [&_svg:not([class*='size-'])]:size-4 group/toggle hover:bg-secondary inline-flex items-center justify-center whitespace-nowrap outline-hidden focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
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-transparent",
11
- outline: "border-edge hover:bg-secondary border bg-transparent"
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-8 min-w-8 px-2",
15
- sm: "h-7 min-w-7 rounded-sm px-1.5 text-[0.8rem]",
16
- lg: "h-9 min-w-9 px-2.5"
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 = 4, align = "center", alignOffset = 0, anchor, children, ...props }) {
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__ */ jsx(Tooltip$1.Popup, {
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 origin-(--transform-origin) rounded-sm px-3 py-1.5 text-xs", className),
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.15.1",
3
+ "version": "0.16.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",