@exegia/corpora-ui 0.17.0 → 0.19.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 (114) hide show
  1. package/dist-lib/components/blocks/nav/sidebar-block.d.ts +1 -1
  2. package/dist-lib/components/blocks/scaffold/__tests__/scaffold.test.d.ts +1 -0
  3. package/dist-lib/components/blocks/scaffold/constants.d.ts +23 -0
  4. package/dist-lib/components/blocks/scaffold/index.d.ts +27 -0
  5. package/dist-lib/components/blocks/scaffold/panel-close-button.d.ts +4 -0
  6. package/dist-lib/components/blocks/scaffold/panel-menu-button.d.ts +8 -0
  7. package/dist-lib/components/blocks/scaffold/scaffold-actions.d.ts +10 -0
  8. package/dist-lib/components/blocks/scaffold/scaffold-canvas.d.ts +7 -0
  9. package/dist-lib/components/blocks/scaffold/scaffold-context.d.ts +5 -0
  10. package/dist-lib/components/blocks/scaffold/scaffold-inspector.d.ts +9 -0
  11. package/dist-lib/components/blocks/scaffold/scaffold-main.d.ts +8 -0
  12. package/dist-lib/components/blocks/scaffold/scaffold-panel.d.ts +10 -0
  13. package/dist-lib/components/blocks/scaffold/scaffold-root.d.ts +8 -0
  14. package/dist-lib/components/blocks/scaffold/scaffold-sidebar.d.ts +7 -0
  15. package/dist-lib/components/blocks/scaffold/scaffold-sub-panel.d.ts +3 -0
  16. package/dist-lib/components/blocks/scaffold/scaffold-tab.d.ts +9 -0
  17. package/dist-lib/components/blocks/scaffold/type.d.ts +135 -0
  18. package/dist-lib/components/blocks/scaffold/use-scaffold.d.ts +8 -0
  19. package/dist-lib/components/blocks/scaffold/utils.d.ts +21 -0
  20. package/dist-lib/components/blocks/shell/animated-panel-inset.d.ts +2 -0
  21. package/dist-lib/components/blocks/shell/animated-panel-provider.d.ts +2 -0
  22. package/dist-lib/components/blocks/shell/animated-panel-trigger.d.ts +2 -0
  23. package/dist-lib/components/blocks/shell/animated-panel.d.ts +2 -0
  24. package/dist-lib/components/blocks/shell/animated-sidebar-content.d.ts +2 -0
  25. package/dist-lib/components/blocks/shell/animated-sidebar-footer.d.ts +2 -0
  26. package/dist-lib/components/blocks/shell/animated-sidebar-group-content.d.ts +2 -0
  27. package/dist-lib/components/blocks/shell/animated-sidebar-group-label.d.ts +2 -0
  28. package/dist-lib/components/blocks/shell/animated-sidebar-group.d.ts +2 -0
  29. package/dist-lib/components/blocks/shell/animated-sidebar-header.d.ts +2 -0
  30. package/dist-lib/components/blocks/shell/animated-sidebar-menu-button.d.ts +3 -0
  31. package/dist-lib/components/blocks/shell/animated-sidebar-menu-item.d.ts +2 -0
  32. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-button.d.ts +3 -0
  33. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub-item.d.ts +2 -0
  34. package/dist-lib/components/blocks/shell/animated-sidebar-menu-sub.d.ts +2 -0
  35. package/dist-lib/components/blocks/shell/animated-sidebar-menu.d.ts +2 -0
  36. package/dist-lib/components/blocks/shell/index.d.ts +16 -0
  37. package/dist-lib/components/blocks/shell/type.d.ts +90 -4
  38. package/dist-lib/components/blocks/shell/utils.d.ts +37 -0
  39. package/dist-lib/components/motion/index.d.ts +1 -0
  40. package/dist-lib/components/motion/overflow-actions.d.ts +35 -0
  41. package/dist-lib/components/ui/button-group.d.ts +11 -0
  42. package/dist-lib/components/ui/glasscn/glass-button-group.d.ts +5 -0
  43. package/dist-lib/components/ui/glasscn/glass-container.d.ts +7 -0
  44. package/dist-lib/components/ui/glasscn/glass-separator.d.ts +5 -0
  45. package/dist-lib/components/ui/group.d.ts +17 -0
  46. package/dist-lib/components/ui/separator.d.ts +2 -3
  47. package/dist-lib/components/ui/sheet.d.ts +14 -0
  48. package/dist-lib/components/ui/tooltip.d.ts +6 -0
  49. package/dist-lib/index.d.ts +2 -1
  50. package/dist-lib/index.js +1631 -1101
  51. package/dist-lib/index.js.map +1 -1
  52. package/dist-lib/lib/ease.d.ts +3 -0
  53. package/dist-lib/lib/hooks/use-hover-capable.d.ts +6 -0
  54. package/package.json +2 -1
  55. package/src/components/beste/piece/browser-frame.tsx +2 -2
  56. package/src/components/blocks/nav/sidebar-block.tsx +5 -5
  57. package/src/components/blocks/nav/sidebar-nav-row.tsx +4 -5
  58. package/src/components/blocks/scaffold/__tests__/scaffold.test.tsx +286 -0
  59. package/src/components/blocks/scaffold/constants.ts +32 -0
  60. package/src/components/blocks/scaffold/index.ts +41 -0
  61. package/src/components/blocks/scaffold/panel-close-button.tsx +36 -0
  62. package/src/components/blocks/scaffold/panel-menu-button.tsx +218 -0
  63. package/src/components/blocks/scaffold/scaffold-actions.tsx +88 -0
  64. package/src/components/blocks/scaffold/scaffold-canvas.tsx +29 -0
  65. package/src/components/blocks/scaffold/scaffold-context.ts +18 -0
  66. package/src/components/blocks/scaffold/scaffold-inspector.tsx +80 -0
  67. package/src/components/blocks/scaffold/scaffold-main.tsx +28 -0
  68. package/src/components/blocks/scaffold/scaffold-panel.tsx +88 -0
  69. package/src/components/blocks/scaffold/scaffold-root.tsx +68 -0
  70. package/src/components/blocks/scaffold/scaffold-sidebar.tsx +31 -0
  71. package/src/components/blocks/scaffold/scaffold-sub-panel.tsx +51 -0
  72. package/src/components/blocks/scaffold/scaffold-tab.tsx +66 -0
  73. package/src/components/blocks/scaffold/type.ts +172 -0
  74. package/src/components/blocks/scaffold/use-scaffold.ts +48 -0
  75. package/src/components/blocks/scaffold/utils.ts +46 -0
  76. package/src/components/blocks/shell/animated-panel-inset.tsx +23 -0
  77. package/src/components/blocks/shell/animated-panel-provider.tsx +155 -0
  78. package/src/components/blocks/shell/animated-panel-trigger.tsx +42 -0
  79. package/src/components/blocks/shell/animated-panel.tsx +135 -0
  80. package/src/components/blocks/shell/animated-sidebar-content.tsx +19 -0
  81. package/src/components/blocks/shell/animated-sidebar-footer.tsx +19 -0
  82. package/src/components/blocks/shell/animated-sidebar-group-content.tsx +16 -0
  83. package/src/components/blocks/shell/animated-sidebar-group-label.tsx +29 -0
  84. package/src/components/blocks/shell/animated-sidebar-group.tsx +16 -0
  85. package/src/components/blocks/shell/animated-sidebar-header.tsx +16 -0
  86. package/src/components/blocks/shell/animated-sidebar-menu-button.tsx +151 -0
  87. package/src/components/blocks/shell/animated-sidebar-menu-item.tsx +20 -0
  88. package/src/components/blocks/shell/animated-sidebar-menu-sub-button.tsx +85 -0
  89. package/src/components/blocks/shell/animated-sidebar-menu-sub-item.tsx +19 -0
  90. package/src/components/blocks/shell/animated-sidebar-menu-sub.tsx +44 -0
  91. package/src/components/blocks/shell/animated-sidebar-menu.tsx +26 -0
  92. package/src/components/blocks/shell/index.ts +18 -0
  93. package/src/components/blocks/shell/shell-layout.tsx +28 -36
  94. package/src/components/blocks/shell/type.ts +112 -6
  95. package/src/components/blocks/shell/use-shell-panels.ts +1 -1
  96. package/src/components/blocks/shell/utils.ts +142 -1
  97. package/src/components/docs/demo-controls.tsx +1 -1
  98. package/src/components/motion/index.ts +0 -0
  99. package/src/components/motion/overflow-actions.tsx +376 -0
  100. package/src/components/ui/button-group.tsx +88 -0
  101. package/src/components/ui/glasscn/glass-button-group.tsx +48 -0
  102. package/src/components/ui/glasscn/glass-container.tsx +125 -0
  103. package/src/components/ui/glasscn/glass-separator.tsx +39 -0
  104. package/src/components/ui/glasscn/liquid-glass.tsx +250 -210
  105. package/src/components/ui/group.tsx +92 -0
  106. package/src/components/ui/separator.tsx +11 -11
  107. package/src/components/ui/sheet.tsx +135 -0
  108. package/src/components/ui/tooltip.tsx +66 -0
  109. package/src/index.css +1 -1
  110. package/src/index.ts +13 -1
  111. package/src/lib/ease.ts +26 -0
  112. package/src/lib/hooks/use-hover-capable.ts +23 -0
  113. package/dist-lib/components/motion/animated-sidebar.d.ts +0 -106
  114. package/src/components/motion/animated-sidebar.tsx +0 -1171
@@ -0,0 +1,376 @@
1
+ "use client";
2
+ // beui.dev/components/blocks/overflow-actions
3
+
4
+ import { MoreHorizontal, X } from "lucide-react";
5
+ import {
6
+ AnimatePresence,
7
+ motion,
8
+ useReducedMotion,
9
+ type Transition,
10
+ type Variants,
11
+ } from "motion/react";
12
+ import {
13
+ useCallback,
14
+ useId,
15
+ useLayoutEffect,
16
+ useRef,
17
+ useState,
18
+ type ReactNode,
19
+ } from "react";
20
+ import { EASE_OUT } from "@/lib/ease";
21
+ import { useHoverCapable } from "@/lib/hooks/use-hover-capable";
22
+ import { cn } from "@/lib/utils";
23
+
24
+ export type OverflowActionsSize = "sm" | "md";
25
+
26
+ export type OverflowActionItem = {
27
+ id: string;
28
+ label: ReactNode;
29
+ icon?: ReactNode;
30
+ onClick?: () => void;
31
+ disabled?: boolean;
32
+ ariaLabel?: string;
33
+ };
34
+
35
+ export type OverflowActionsClassNames = {
36
+ root?: string;
37
+ track?: string;
38
+ action?: string;
39
+ primaryAction?: string;
40
+ overflowAction?: string;
41
+ toggle?: string;
42
+ icon?: string;
43
+ label?: string;
44
+ };
45
+
46
+ export interface OverflowActionsProps {
47
+ primaryActions: OverflowActionItem[];
48
+ overflowActions: OverflowActionItem[];
49
+ expanded?: boolean;
50
+ defaultExpanded?: boolean;
51
+ onExpandedChange?: (expanded: boolean) => void;
52
+ onAction?: (item: OverflowActionItem) => void;
53
+ collapseOnAction?: boolean;
54
+ size?: OverflowActionsSize;
55
+ openLabel?: string;
56
+ closeLabel?: string;
57
+ className?: string;
58
+ classNames?: OverflowActionsClassNames;
59
+ }
60
+
61
+ // This needs a softer layout spring than the app defaults, so the overflow group
62
+ // stays visually attached to the toggle while entering and leaving.
63
+ const SHELL_TRANSITION: Transition = {
64
+ type: "spring",
65
+ stiffness: 220,
66
+ damping: 17,
67
+ mass: 0.85,
68
+ };
69
+
70
+ const ICON_VARIANTS: Variants = {
71
+ hidden: { opacity: 0, filter: "blur(3px)" },
72
+ visible: {
73
+ opacity: 1,
74
+ filter: "blur(0px)",
75
+ transition: { duration: 0.18, ease: EASE_OUT },
76
+ },
77
+ exit: {
78
+ opacity: 0,
79
+ filter: "blur(3px)",
80
+ transition: { duration: 0.18, ease: EASE_OUT },
81
+ },
82
+ };
83
+
84
+ const OVERFLOW_ACTION_VARIANTS: Variants = {
85
+ hidden: { opacity: 0, filter: "blur(4px)" },
86
+ visible: { opacity: 1, filter: "blur(0px)" },
87
+ exit: { opacity: 0, filter: "blur(4px)" },
88
+ };
89
+
90
+ const TRACK_SIZE_CLASS: Record<OverflowActionsSize, string> = {
91
+ sm: "gap-1 p-1 text-xs",
92
+ md: "gap-1.5 p-1.5 text-sm",
93
+ };
94
+
95
+ const GROUP_GAP_CLASS: Record<OverflowActionsSize, string> = {
96
+ sm: "gap-1",
97
+ md: "gap-1.5",
98
+ };
99
+
100
+ const ACTION_SIZE_CLASS: Record<OverflowActionsSize, string> = {
101
+ sm: "h-8 min-w-8 gap-1.5 px-3",
102
+ md: "h-9 min-w-9 gap-2 px-3.5",
103
+ };
104
+
105
+ const TOGGLE_SIZE_CLASS: Record<OverflowActionsSize, string> = {
106
+ sm: "h-8 w-8",
107
+ md: "h-9 w-9",
108
+ };
109
+
110
+ const ICON_SIZE_CLASS: Record<OverflowActionsSize, string> = {
111
+ sm: "h-3.5 w-3.5",
112
+ md: "h-4 w-4",
113
+ };
114
+
115
+ function useControllableExpanded({
116
+ expanded,
117
+ defaultExpanded,
118
+ onExpandedChange,
119
+ }: {
120
+ expanded?: boolean;
121
+ defaultExpanded?: boolean;
122
+ onExpandedChange?: (expanded: boolean) => void;
123
+ }) {
124
+ const [internalExpanded, setInternalExpanded] = useState(
125
+ defaultExpanded ?? false,
126
+ );
127
+ const isControlled = expanded !== undefined;
128
+ const value = expanded ?? internalExpanded;
129
+
130
+ const setValue = useCallback(
131
+ (next: boolean) => {
132
+ if (!isControlled) setInternalExpanded(next);
133
+ onExpandedChange?.(next);
134
+ },
135
+ [isControlled, onExpandedChange],
136
+ );
137
+
138
+ return [value, setValue] as const;
139
+ }
140
+
141
+ export function OverflowActions({
142
+ primaryActions,
143
+ overflowActions,
144
+ expanded,
145
+ defaultExpanded = false,
146
+ onExpandedChange,
147
+ onAction,
148
+ collapseOnAction = false,
149
+ size = "md",
150
+ openLabel = "Show extra actions",
151
+ closeLabel = "Hide extra actions",
152
+ className,
153
+ classNames,
154
+ }: OverflowActionsProps) {
155
+ const reduce = useReducedMotion();
156
+ const canHover = useHoverCapable();
157
+ const overflowId = useId();
158
+ const overflowWrapperRef = useRef<HTMLDivElement>(null);
159
+ const overflowWrapperLeftRef = useRef(0);
160
+ const [isExpanded, setIsExpanded] = useControllableExpanded({
161
+ expanded,
162
+ defaultExpanded,
163
+ onExpandedChange,
164
+ });
165
+
166
+ const transition = reduce ? { duration: 0 } : SHELL_TRANSITION;
167
+
168
+ useLayoutEffect(() => {
169
+ const overflowNode = overflowWrapperRef.current;
170
+ if (!overflowNode) return;
171
+
172
+ if (!isExpanded) {
173
+ overflowNode.style.left = `${
174
+ overflowWrapperLeftRef.current -
175
+ overflowNode.getBoundingClientRect().left
176
+ }px`;
177
+ return;
178
+ }
179
+
180
+ overflowNode.style.left = "";
181
+ overflowWrapperLeftRef.current = overflowNode.getBoundingClientRect().left;
182
+ }, [isExpanded]);
183
+
184
+ const handleAction = (item: OverflowActionItem) => {
185
+ item.onClick?.();
186
+ onAction?.(item);
187
+ if (collapseOnAction) setIsExpanded(false);
188
+ };
189
+
190
+ return (
191
+ <motion.div
192
+ layout
193
+ transition={transition}
194
+ className={cn("inline-flex", classNames?.root, className)}
195
+ >
196
+ <motion.div
197
+ layout
198
+ transition={transition}
199
+ className={cn(
200
+ "relative inline-flex items-center overflow-hidden rounded-full border border-border bg-card",
201
+ TRACK_SIZE_CLASS[size],
202
+ classNames?.track,
203
+ )}
204
+ >
205
+ <motion.div
206
+ layout
207
+ transition={transition}
208
+ className={cn("inline-flex items-center", GROUP_GAP_CLASS[size])}
209
+ >
210
+ {primaryActions.map((item) => (
211
+ <ActionButton
212
+ key={item.id}
213
+ item={item}
214
+ size={size}
215
+ reduce={reduce}
216
+ canHover={canHover}
217
+ onAction={handleAction}
218
+ layoutTransition={transition}
219
+ className={cn(classNames?.action, classNames?.primaryAction)}
220
+ iconClassName={classNames?.icon}
221
+ labelClassName={classNames?.label}
222
+ />
223
+ ))}
224
+ </motion.div>
225
+
226
+ <AnimatePresence mode="popLayout" initial={false}>
227
+ {isExpanded ? (
228
+ <motion.div
229
+ key="overflow-actions"
230
+ ref={overflowWrapperRef}
231
+ id={overflowId}
232
+ layout
233
+ aria-hidden={!isExpanded}
234
+ transition={transition}
235
+ className={cn(
236
+ "relative inline-flex w-max items-center",
237
+ GROUP_GAP_CLASS[size],
238
+ )}
239
+ >
240
+ {overflowActions.map((item) => (
241
+ <ActionButton
242
+ key={item.id}
243
+ item={item}
244
+ size={size}
245
+ reduce={reduce}
246
+ canHover={canHover}
247
+ overflow
248
+ visible={isExpanded}
249
+ variants={OVERFLOW_ACTION_VARIANTS}
250
+ onAction={handleAction}
251
+ layoutTransition={transition}
252
+ className={cn(classNames?.action, classNames?.overflowAction)}
253
+ iconClassName={classNames?.icon}
254
+ labelClassName={classNames?.label}
255
+ />
256
+ ))}
257
+ </motion.div>
258
+ ) : null}
259
+ </AnimatePresence>
260
+
261
+ <motion.button
262
+ type="button"
263
+ layout
264
+ aria-expanded={isExpanded}
265
+ aria-controls={isExpanded ? overflowId : undefined}
266
+ aria-label={isExpanded ? closeLabel : openLabel}
267
+ title={isExpanded ? closeLabel : openLabel}
268
+ onClick={() => setIsExpanded(!isExpanded)}
269
+ whileTap={reduce ? undefined : { scale: 0.96 }}
270
+ whileHover={reduce || !canHover ? undefined : { scale: 1.03 }}
271
+ transition={transition}
272
+ className={cn(
273
+ "relative inline-grid shrink-0 place-items-center rounded-full bg-primary text-primary-foreground outline-none disabled:pointer-events-none disabled:opacity-50",
274
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
275
+ TOGGLE_SIZE_CLASS[size],
276
+ classNames?.toggle,
277
+ )}
278
+ >
279
+ <AnimatePresence mode="popLayout" initial={false}>
280
+ <motion.span
281
+ key={isExpanded ? "close" : "open"}
282
+ variants={ICON_VARIANTS}
283
+ initial={reduce ? { opacity: 0 } : "hidden"}
284
+ animate={reduce ? { opacity: 1 } : "visible"}
285
+ exit={reduce ? { opacity: 0 } : "exit"}
286
+ className="inline-grid place-items-center"
287
+ >
288
+ {isExpanded ? (
289
+ <X className={ICON_SIZE_CLASS[size]} />
290
+ ) : (
291
+ <MoreHorizontal className={ICON_SIZE_CLASS[size]} />
292
+ )}
293
+ </motion.span>
294
+ </AnimatePresence>
295
+ </motion.button>
296
+ </motion.div>
297
+ </motion.div>
298
+ );
299
+ }
300
+
301
+ function ActionButton({
302
+ item,
303
+ size,
304
+ reduce,
305
+ canHover,
306
+ overflow,
307
+ visible = true,
308
+ variants,
309
+ onAction,
310
+ layoutTransition,
311
+ className,
312
+ iconClassName,
313
+ labelClassName,
314
+ }: {
315
+ item: OverflowActionItem;
316
+ size: OverflowActionsSize;
317
+ reduce: boolean | null;
318
+ canHover: boolean;
319
+ overflow?: boolean;
320
+ visible?: boolean;
321
+ variants?: Variants;
322
+ onAction: (item: OverflowActionItem) => void;
323
+ layoutTransition: Transition;
324
+ className?: string;
325
+ iconClassName?: string;
326
+ labelClassName?: string;
327
+ }) {
328
+ const label = typeof item.label === "string" ? item.label : undefined;
329
+
330
+ return (
331
+ <motion.span
332
+ layout="position"
333
+ variants={variants}
334
+ initial={variants ? (reduce ? { opacity: 0 } : "hidden") : undefined}
335
+ animate={variants ? (reduce ? { opacity: 1 } : "visible") : undefined}
336
+ exit={variants ? (reduce ? { opacity: 0 } : "exit") : undefined}
337
+ whileTap={reduce || item.disabled ? undefined : { scale: 0.97 }}
338
+ whileHover={
339
+ reduce || !canHover || item.disabled ? undefined : { scale: 1.008 }
340
+ }
341
+ transition={layoutTransition}
342
+ className="inline-flex shrink-0"
343
+ >
344
+ <button
345
+ type="button"
346
+ disabled={item.disabled}
347
+ aria-label={item.ariaLabel}
348
+ tabIndex={overflow && !visible ? -1 : undefined}
349
+ title={label}
350
+ onClick={() => onAction(item)}
351
+ className={cn(
352
+ "inline-flex shrink-0 items-center justify-center rounded-full bg-background font-medium text-foreground outline-none",
353
+ "disabled:pointer-events-none disabled:opacity-45",
354
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
355
+ ACTION_SIZE_CLASS[size],
356
+ className,
357
+ )}
358
+ >
359
+ {item.icon ? (
360
+ <span
361
+ className={cn(
362
+ "inline-flex shrink-0 items-center justify-center",
363
+ ICON_SIZE_CLASS[size],
364
+ iconClassName,
365
+ )}
366
+ >
367
+ {item.icon}
368
+ </span>
369
+ ) : null}
370
+ <span className={cn("whitespace-nowrap", labelClassName)}>
371
+ {item.label}
372
+ </span>
373
+ </button>
374
+ </motion.span>
375
+ );
376
+ }
@@ -0,0 +1,88 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { motion, type HTMLMotionProps } from "framer-motion"
5
+
6
+ import { cn } from "@/lib/utils"
7
+ import { Separator } from "@/components/ui/separator"
8
+
9
+ const buttonGroupVariants = cva(
10
+ "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
11
+ {
12
+ variants: {
13
+ orientation: {
14
+ horizontal:
15
+ "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
16
+ vertical:
17
+ "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ orientation: "horizontal",
22
+ },
23
+ }
24
+ )
25
+
26
+ function ButtonGroup({
27
+ className,
28
+ orientation,
29
+ ...props
30
+ }: HTMLMotionProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
31
+ return (
32
+ <motion.div
33
+ role="group"
34
+ data-slot="button-group"
35
+ data-orientation={orientation}
36
+ className={cn(buttonGroupVariants({ orientation }), className)}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function ButtonGroupText({
43
+ className,
44
+ render,
45
+ ...props
46
+ }: useRender.ComponentProps<"div">) {
47
+ return useRender({
48
+ defaultTagName: "div",
49
+ props: mergeProps<"div">(
50
+ {
51
+ className: cn(
52
+ "flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
53
+ className
54
+ ),
55
+ },
56
+ props
57
+ ),
58
+ render,
59
+ state: {
60
+ slot: "button-group-text",
61
+ },
62
+ })
63
+ }
64
+
65
+ function ButtonGroupSeparator({
66
+ className,
67
+ orientation = "vertical",
68
+ ...props
69
+ }: React.ComponentProps<typeof Separator>) {
70
+ return (
71
+ <Separator
72
+ data-slot="button-group-separator"
73
+ orientation={orientation}
74
+ className={cn(
75
+ "relative self-stretch bg-input data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
76
+ className
77
+ )}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ export {
84
+ ButtonGroup,
85
+ ButtonGroupSeparator,
86
+ ButtonGroupText,
87
+ buttonGroupVariants,
88
+ }
@@ -0,0 +1,48 @@
1
+ "use client"
2
+
3
+ import {
4
+ type FrostGlassVariantProp,
5
+ glassVariantStyles,
6
+ } from "@/lib/glass-variants"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ import { ButtonGroup } from "../button-group"
10
+ import { LiquidGlass } from "./liquid-glass"
11
+
12
+ type GlassButtonGroupProps = React.ComponentProps<typeof ButtonGroup> &
13
+ FrostGlassVariantProp
14
+
15
+ function GlassButtonGroup({
16
+ className,
17
+ glassVariant = "liquid-refract",
18
+ children,
19
+ ...props
20
+ }: GlassButtonGroupProps) {
21
+ if (glassVariant === "liquid-refract") {
22
+ return (
23
+ <LiquidGlass className={cn("", className)}>
24
+ <ButtonGroup
25
+ data-slot="glass-button-group"
26
+ data-glass-variant={glassVariant}
27
+ className={cn("bg-transparent", className)}
28
+ {...props}
29
+ >
30
+ {children}
31
+ </ButtonGroup>
32
+ </LiquidGlass>
33
+ )
34
+ }
35
+
36
+ return (
37
+ <ButtonGroup
38
+ data-slot="glass-button-group"
39
+ data-glass-variant={glassVariant}
40
+ className={cn("rounded-lg", glassVariantStyles[glassVariant], className)}
41
+ {...props}
42
+ >
43
+ {children}
44
+ </ButtonGroup>
45
+ )
46
+ }
47
+
48
+ export { GlassButtonGroup }
@@ -0,0 +1,125 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import {
6
+ type FrostGlassVariant,
7
+ type FrostGlassVariantProp,
8
+ glassVariantStyles,
9
+ } from "@/lib/glass-variants"
10
+ import { cn } from "@/lib/utils"
11
+ import { LiquidGlass } from "./liquid-glass"
12
+
13
+ const glassSidebarVariableStyles: Record<FrostGlassVariant, string> = {
14
+ clear: [
15
+ "[--sidebar:rgba(255,255,255,0.18)]",
16
+ "[--sidebar-foreground:oklch(0.145_0_0)]",
17
+ "[--sidebar-accent:rgba(255,255,255,0.48)]",
18
+ "[--sidebar-accent-foreground:oklch(0.145_0_0)]",
19
+ "[--sidebar-border:rgba(255,255,255,0.34)]",
20
+ "[--sidebar-ring:rgba(255,255,255,0.38)]",
21
+ "dark:[--sidebar:rgba(0,0,0,0.28)]",
22
+ "dark:[--sidebar-foreground:oklch(0.985_0_0)]",
23
+ "dark:[--sidebar-accent:rgba(255,255,255,0.1)]",
24
+ "dark:[--sidebar-accent-foreground:oklch(0.985_0_0)]",
25
+ "dark:[--sidebar-border:rgba(255,255,255,0.12)]",
26
+ "dark:[--sidebar-ring:rgba(255,255,255,0.16)]",
27
+ ].join(" "),
28
+ frosted: [
29
+ "[--sidebar:rgba(255,255,255,0.4)]",
30
+ "[--sidebar-foreground:oklch(0.145_0_0)]",
31
+ "[--sidebar-accent:rgba(255,255,255,0.62)]",
32
+ "[--sidebar-accent-foreground:oklch(0.145_0_0)]",
33
+ "[--sidebar-border:rgba(255,255,255,0.3)]",
34
+ "[--sidebar-ring:rgba(255,255,255,0.32)]",
35
+ "dark:[--sidebar:rgba(0,0,0,0.38)]",
36
+ "dark:[--sidebar-foreground:oklch(0.985_0_0)]",
37
+ "dark:[--sidebar-accent:rgba(255,255,255,0.14)]",
38
+ "dark:[--sidebar-accent-foreground:oklch(0.985_0_0)]",
39
+ "dark:[--sidebar-border:rgba(255,255,255,0.1)]",
40
+ "dark:[--sidebar-ring:rgba(255,255,255,0.14)]",
41
+ ].join(" "),
42
+ subtle: [
43
+ "[--sidebar:rgba(255,255,255,0.14)]",
44
+ "[--sidebar-foreground:oklch(0.145_0_0)]",
45
+ "[--sidebar-accent:rgba(255,255,255,0.28)]",
46
+ "[--sidebar-accent-foreground:oklch(0.145_0_0)]",
47
+ "[--sidebar-border:rgba(0,0,0,0.05)]",
48
+ "[--sidebar-ring:rgba(255,255,255,0.28)]",
49
+ "dark:[--sidebar:rgba(255,255,255,0.05)]",
50
+ "dark:[--sidebar-foreground:oklch(0.985_0_0)]",
51
+ "dark:[--sidebar-accent:rgba(255,255,255,0.08)]",
52
+ "dark:[--sidebar-accent-foreground:oklch(0.985_0_0)]",
53
+ "dark:[--sidebar-border:rgba(255,255,255,0.08)]",
54
+ "dark:[--sidebar-ring:rgba(255,255,255,0.12)]",
55
+ ].join(" "),
56
+ liquid: [
57
+ "[--sidebar:rgba(255,255,255,0.22)]",
58
+ "[--sidebar-foreground:oklch(0.145_0_0)]",
59
+ "[--sidebar-accent:rgba(255,255,255,0.55)]",
60
+ "[--sidebar-accent-foreground:oklch(0.145_0_0)]",
61
+ "[--sidebar-border:rgba(255,255,255,0.42)]",
62
+ "[--sidebar-ring:rgba(255,255,255,0.45)]",
63
+ "dark:[--sidebar:rgba(255,255,255,0.06)]",
64
+ "dark:[--sidebar-foreground:oklch(0.985_0_0)]",
65
+ "dark:[--sidebar-accent:rgba(255,255,255,0.12)]",
66
+ "dark:[--sidebar-accent-foreground:oklch(0.985_0_0)]",
67
+ "dark:[--sidebar-border:rgba(255,255,255,0.16)]",
68
+ "dark:[--sidebar-ring:rgba(255,255,255,0.20)]",
69
+ ].join(" "),
70
+ "liquid-refract": [
71
+ "[--sidebar:rgba(255,255,255,0.18)]",
72
+ "[--sidebar-foreground:oklch(0.145_0_0)]",
73
+ "[--sidebar-accent:rgba(255,255,255,0.48)]",
74
+ "[--sidebar-accent-foreground:oklch(0.145_0_0)]",
75
+ "[--sidebar-border:rgba(255,255,255,0.34)]",
76
+ "[--sidebar-ring:rgba(255,255,255,0.38)]",
77
+ "dark:[--sidebar-foreground:oklch(0.985_0_0)]",
78
+ "dark:[--sidebar-accent:rgba(255,255,255,0.12)]",
79
+ "dark:[--sidebar-accent-foreground:oklch(0.985_0_0)]",
80
+ "dark:[--sidebar-border:rgba(255,255,255,0.15)]",
81
+ "dark:[--sidebar-ring:rgba(255,255,255,0.18)]",
82
+ ].join(" "),
83
+ }
84
+
85
+ type GlassSidebarProps = React.HTMLAttributes<HTMLDivElement> &
86
+ FrostGlassVariantProp
87
+
88
+ function getGlassSidebarStandaloneClasses(glassVariant: FrostGlassVariant) {
89
+ return cn(
90
+ glassVariantStyles[glassVariant],
91
+ glassSidebarVariableStyles[glassVariant],
92
+ "overflow-hidden",
93
+ "shadow-[0_24px_80px_rgba(15,23,42,0.08)] dark:shadow-[0_24px_80px_rgba(0,0,0,0.28)]"
94
+ )
95
+ }
96
+
97
+ function GlassContainer({
98
+ glassVariant = "liquid-refract",
99
+ className,
100
+ children,
101
+ ...props
102
+ }: GlassSidebarProps) {
103
+ return (
104
+ <LiquidGlass
105
+ className={cn("rounded-[1.75rem]", className)}
106
+ blur={5}
107
+ refraction={50}
108
+ >
109
+ <div
110
+ className={cn(
111
+ getGlassSidebarStandaloneClasses(glassVariant),
112
+ glassVariant === "liquid-refract" &&
113
+ "border-0 bg-transparent shadow-none",
114
+ className
115
+ )}
116
+ {...props}
117
+ >
118
+ {children}
119
+ </div>
120
+ </LiquidGlass>
121
+ )
122
+ }
123
+
124
+ export { GlassContainer, glassSidebarVariableStyles }
125
+ export type { FrostGlassVariant } from "@/lib/glass-variants"
@@ -0,0 +1,39 @@
1
+ "use client"
2
+
3
+ import {
4
+ type FrostGlassVariant,
5
+ type FrostGlassVariantProp,
6
+ } from "@/lib/glass-variants"
7
+ import { cn } from "@/lib/utils"
8
+
9
+ import { Separator } from "../separator"
10
+
11
+ const separatorVariantStyles: Record<FrostGlassVariant, string> = {
12
+ clear: "bg-white/[0.5] dark:bg-white/[0.12]",
13
+ frosted: "bg-white/[0.4] dark:bg-white/10",
14
+ subtle: "bg-black/[0.05] dark:bg-white/[0.08]",
15
+ liquid:
16
+ "bg-gradient-to-r from-white/0 via-white/60 to-white/0 dark:via-white/20",
17
+ "liquid-refract":
18
+ "bg-gradient-to-r from-white/0 via-white/70 to-white/0 dark:via-white/25",
19
+ }
20
+
21
+ type GlassSeparatorProps = React.ComponentProps<typeof Separator> &
22
+ FrostGlassVariantProp
23
+
24
+ function GlassSeparator({
25
+ className,
26
+ glassVariant = "liquid-refract",
27
+ ...props
28
+ }: GlassSeparatorProps) {
29
+ return (
30
+ <Separator
31
+ data-slot="glass-separator"
32
+ data-glass-variant={glassVariant}
33
+ className={cn(separatorVariantStyles[glassVariant], className)}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ export { GlassSeparator }