@exegia/corpora-ui 0.10.0 → 0.11.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 (65) hide show
  1. package/dist-lib/components/agents/ai-sidebar.d.ts +2 -0
  2. package/dist-lib/components/agents/index.d.ts +12 -0
  3. package/dist-lib/components/agents/marquee-label.d.ts +4 -0
  4. package/dist-lib/components/agents/sidebar-icon.d.ts +2 -0
  5. package/dist-lib/components/agents/sidebar-row.d.ts +2 -0
  6. package/dist-lib/components/agents/type.d.ts +77 -0
  7. package/dist-lib/components/agents/use-ai-sidebar.d.ts +31 -0
  8. package/dist-lib/components/agents/utils.d.ts +16 -0
  9. package/dist-lib/components/beste/piece/browser-frame.d.ts +2 -0
  10. package/dist-lib/components/beste/piece/type.d.ts +9 -0
  11. package/dist-lib/components/beste/piece/utils.d.ts +3 -0
  12. package/dist-lib/components/blocks/auth/signup-block.d.ts +5 -5
  13. package/dist-lib/components/blocks/nav/sidebar-block.d.ts +3 -56
  14. package/dist-lib/components/blocks/nav/sidebar-nav-row.d.ts +3 -0
  15. package/dist-lib/components/blocks/nav/types.d.ts +60 -0
  16. package/dist-lib/components/blocks/nav/utils.d.ts +1 -0
  17. package/dist-lib/components/blocks/profile/profile-card-block.d.ts +6 -1
  18. package/dist-lib/components/blocks/shell/index.d.ts +1 -0
  19. package/dist-lib/components/blocks/shell/shell-layout.d.ts +3 -0
  20. package/dist-lib/components/blocks/shell/type.d.ts +26 -0
  21. package/dist-lib/components/blocks/shell/utils.d.ts +1 -0
  22. package/dist-lib/components/composed/social-providers.d.ts +9 -1
  23. package/dist-lib/components/docs/demo-controls.d.ts +4 -4
  24. package/dist-lib/components/motion/animated-sidebar.d.ts +12 -1
  25. package/dist-lib/components/motion/popover-morph.d.ts +35 -0
  26. package/dist-lib/components/motion/popover-position.d.ts +15 -0
  27. package/dist-lib/components/ui/badge.d.ts +7 -0
  28. package/dist-lib/components/ui/select.d.ts +30 -0
  29. package/dist-lib/index.d.ts +1 -0
  30. package/dist-lib/index.js +644 -524
  31. package/dist-lib/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/components/agents/ai-sidebar.tsx +116 -0
  34. package/src/components/agents/index.ts +14 -0
  35. package/src/components/agents/marquee-label.tsx +59 -0
  36. package/src/components/agents/sidebar-icon.tsx +14 -0
  37. package/src/components/agents/sidebar-row.tsx +271 -0
  38. package/src/components/agents/type.ts +89 -0
  39. package/src/components/agents/use-ai-sidebar.ts +386 -0
  40. package/src/components/agents/utils.ts +142 -0
  41. package/src/components/beste/piece/browser-frame.tsx +55 -0
  42. package/src/components/beste/piece/type.ts +11 -0
  43. package/src/components/beste/piece/utils.ts +21 -0
  44. package/src/components/blocks/auth/linked-accounts-block.tsx +55 -57
  45. package/src/components/blocks/auth/passkey-manager-block.tsx +88 -81
  46. package/src/components/blocks/auth/signup-block.tsx +6 -6
  47. package/src/components/blocks/nav/sidebar-block.tsx +25 -155
  48. package/src/components/blocks/nav/sidebar-nav-row.tsx +79 -0
  49. package/src/components/blocks/nav/types.ts +65 -0
  50. package/src/components/blocks/nav/utils.ts +4 -0
  51. package/src/components/blocks/profile/profile-card-block.tsx +17 -6
  52. package/src/components/blocks/shell/index.ts +1 -0
  53. package/src/components/blocks/shell/shell-layout.tsx +115 -0
  54. package/src/components/blocks/shell/type.ts +50 -0
  55. package/src/components/blocks/shell/utils.ts +2 -0
  56. package/src/components/composed/social-providers.tsx +83 -27
  57. package/src/components/docs/component-preview.tsx +13 -12
  58. package/src/components/docs/demo-controls.tsx +37 -21
  59. package/src/components/motion/animated-sidebar.tsx +392 -364
  60. package/src/components/motion/popover-morph.tsx +292 -0
  61. package/src/components/motion/popover-position.ts +86 -0
  62. package/src/components/ui/badge.tsx +52 -0
  63. package/src/components/ui/select.tsx +257 -0
  64. package/src/index.css +18 -0
  65. package/src/index.ts +1 -0
@@ -0,0 +1,292 @@
1
+ "use client";
2
+
3
+ import { AnimatePresence, motion, useReducedMotion } from "motion/react";
4
+ import {
5
+ cloneElement,
6
+ createContext,
7
+ isValidElement,
8
+ type ReactElement,
9
+ type ReactNode,
10
+ type Ref,
11
+ useCallback,
12
+ useContext,
13
+ useEffect,
14
+ useId,
15
+ useMemo,
16
+ useRef,
17
+ useState,
18
+ } from "react";
19
+ import { createPortal } from "react-dom";
20
+ import { usePopoverPortalPosition } from "@/components/motion/popover-position";
21
+ import { EASE_OUT, SPRING_PANEL } from "@/lib/ease";
22
+ import { cn } from "@/lib/utils";
23
+
24
+ type Side = "top" | "bottom";
25
+ type Align = "start" | "end";
26
+
27
+ type MorphContextValue = {
28
+ open: boolean;
29
+ setOpen: (open: boolean) => void;
30
+ toggle: () => void;
31
+ triggerId: string;
32
+ contentId: string;
33
+ triggerRef: React.MutableRefObject<HTMLElement | null>;
34
+ setContentRef: (node: HTMLDivElement | null) => void;
35
+ contentRef: React.MutableRefObject<HTMLDivElement | null>;
36
+ };
37
+
38
+ const MorphContext = createContext<MorphContextValue | null>(null);
39
+
40
+ function useMorphContext(component: string) {
41
+ const ctx = useContext(MorphContext);
42
+ if (!ctx) throw new Error(`${component} must be used within <MorphPopover>`);
43
+ return ctx;
44
+ }
45
+
46
+ export interface MorphPopoverProps {
47
+ children: ReactNode;
48
+ /** Controlled open state. */
49
+ open?: boolean;
50
+ /** Uncontrolled initial open state. */
51
+ defaultOpen?: boolean;
52
+ onOpenChange?: (open: boolean) => void;
53
+ className?: string;
54
+ }
55
+
56
+ /**
57
+ * A popover whose panel morphs open from the trigger corner: it's laid out at
58
+ * full size but clipped to the corner nearest the trigger, then unclips as one
59
+ * piece. Closes on outside pointer / Escape. Controlled or uncontrolled.
60
+ */
61
+ export function MorphPopover({
62
+ children,
63
+ open: controlledOpen,
64
+ defaultOpen = false,
65
+ onOpenChange,
66
+ className,
67
+ }: MorphPopoverProps) {
68
+ const baseId = useId();
69
+ const rootRef = useRef<HTMLDivElement>(null);
70
+ const triggerRef = useRef<HTMLElement | null>(null);
71
+ const contentRef = useRef<HTMLDivElement | null>(null);
72
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
73
+ const controlled = controlledOpen !== undefined;
74
+ const open = controlled ? controlledOpen : internalOpen;
75
+
76
+ const setOpen = useCallback(
77
+ (next: boolean) => {
78
+ if (!controlled) setInternalOpen(next);
79
+ onOpenChange?.(next);
80
+ },
81
+ [controlled, onOpenChange],
82
+ );
83
+ const toggle = useCallback(() => setOpen(!open), [setOpen, open]);
84
+
85
+ useEffect(() => {
86
+ if (!open) return;
87
+ const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
88
+ const onPointer = (e: PointerEvent) => {
89
+ const target = e.target as Node;
90
+ if (
91
+ rootRef.current &&
92
+ !rootRef.current.contains(target) &&
93
+ !contentRef.current?.contains(target)
94
+ )
95
+ setOpen(false);
96
+ };
97
+ window.addEventListener("keydown", onKey);
98
+ window.addEventListener("pointerdown", onPointer);
99
+ return () => {
100
+ window.removeEventListener("keydown", onKey);
101
+ window.removeEventListener("pointerdown", onPointer);
102
+ };
103
+ }, [open, setOpen]);
104
+
105
+ const ctx = useMemo<MorphContextValue>(
106
+ () => ({
107
+ open,
108
+ setOpen,
109
+ toggle,
110
+ triggerId: `${baseId}-trigger`,
111
+ contentId: `${baseId}-content`,
112
+ triggerRef,
113
+ setContentRef: (node) => {
114
+ contentRef.current = node;
115
+ },
116
+ contentRef,
117
+ }),
118
+ [open, setOpen, toggle, baseId],
119
+ );
120
+
121
+ return (
122
+ <MorphContext.Provider value={ctx}>
123
+ <div ref={rootRef} className={cn("relative inline-flex", className)}>
124
+ {children}
125
+ </div>
126
+ </MorphContext.Provider>
127
+ );
128
+ }
129
+
130
+ export interface MorphPopoverTriggerProps {
131
+ children: ReactElement;
132
+ }
133
+
134
+ function mergeRefs<T>(...refs: Array<Ref<T> | undefined>) {
135
+ return (node: T | null) => {
136
+ for (const ref of refs) {
137
+ if (typeof ref === "function") ref(node);
138
+ else if (ref && typeof ref === "object")
139
+ (ref as React.MutableRefObject<T | null>).current = node;
140
+ }
141
+ };
142
+ }
143
+
144
+ /** Wraps a single element, toggling the popover on click. */
145
+ export function MorphPopoverTrigger({ children }: MorphPopoverTriggerProps) {
146
+ const ctx = useMorphContext("MorphPopoverTrigger");
147
+ if (!isValidElement(children)) return children;
148
+
149
+ const child = children as ReactElement<Record<string, unknown>>;
150
+ const childOnClick = child.props.onClick as
151
+ | ((e: unknown) => void)
152
+ | undefined;
153
+ const childRef = (child.props as { ref?: Ref<HTMLElement> }).ref;
154
+
155
+ return cloneElement(child, {
156
+ id: ctx.triggerId,
157
+ ref: mergeRefs(childRef, (node: HTMLElement | null) => {
158
+ ctx.triggerRef.current = node;
159
+ }),
160
+ onClick: (e: unknown) => {
161
+ childOnClick?.(e);
162
+ ctx.toggle();
163
+ },
164
+ "aria-haspopup": "dialog",
165
+ "aria-expanded": ctx.open,
166
+ "aria-controls": ctx.open ? ctx.contentId : undefined,
167
+ });
168
+ }
169
+
170
+ const originFor = (side: Side, align: Align) =>
171
+ `${side === "bottom" ? "top" : "bottom"} ${align === "end" ? "right" : "left"}`;
172
+
173
+ // A clip that hides everything but the corner nearest the trigger, so the
174
+ // panel appears to grow out of it. inset(top right bottom left).
175
+ function clipHidden(side: Side, align: Align, radius: number) {
176
+ const top = side === "bottom" ? "0%" : "92%";
177
+ const bottom = side === "bottom" ? "92%" : "0%";
178
+ const right = align === "end" ? "0%" : "92%";
179
+ const left = align === "end" ? "92%" : "0%";
180
+ return `inset(${top} ${right} ${bottom} ${left} round ${radius}px)`;
181
+ }
182
+ const clipShown = (radius: number) => `inset(0% 0% 0% 0% round ${radius}px)`;
183
+
184
+ // Preserve the original spring character on the wrapper, but tween the complex
185
+ // clip-path so it cannot snap when the spring resolves its final distance.
186
+ const MORPH_CLIP_TRANSITION = { duration: 0.32, ease: EASE_OUT } as const;
187
+
188
+ export interface MorphPopoverContentProps {
189
+ children: ReactNode;
190
+ side?: Side;
191
+ align?: Align;
192
+ /** Gap between trigger and panel, in px. Default 8. */
193
+ sideOffset?: number;
194
+ /** Panel corner radius, in px. Default 16. */
195
+ radius?: number;
196
+ className?: string;
197
+ }
198
+
199
+ export function MorphPopoverContent({
200
+ children,
201
+ side = "bottom",
202
+ align = "end",
203
+ sideOffset = 8,
204
+ radius = 16,
205
+ className,
206
+ }: MorphPopoverContentProps) {
207
+ const ctx = useMorphContext("MorphPopoverContent");
208
+ const { contentId, setContentRef, triggerId } = ctx;
209
+ const reduce = useReducedMotion() ?? false;
210
+ const [portalReady, setPortalReady] = useState(false);
211
+ const layout = usePopoverPortalPosition(
212
+ ctx.triggerRef,
213
+ ctx.contentRef,
214
+ portalReady && ctx.open,
215
+ );
216
+
217
+ useEffect(() => setPortalReady(true), []);
218
+ const left = layout
219
+ ? align === "end"
220
+ ? layout.trigger.left + layout.trigger.width - layout.content.width
221
+ : layout.trigger.left
222
+ : 0;
223
+ const top = layout
224
+ ? side === "bottom"
225
+ ? layout.trigger.top + layout.trigger.height + sideOffset
226
+ : layout.trigger.top - layout.content.height - sideOffset
227
+ : 0;
228
+
229
+ // Both directions travel between the exact same hidden/show states. Exit
230
+ // targets "hidden" directly instead of introducing separate choreography.
231
+ const wrap = reduce
232
+ ? undefined
233
+ : {
234
+ hidden: { opacity: 0, scale: 0.96, transition: SPRING_PANEL },
235
+ show: { opacity: 1, scale: 1, transition: SPRING_PANEL },
236
+ };
237
+ const clip = reduce
238
+ ? undefined
239
+ : {
240
+ hidden: {
241
+ clipPath: clipHidden(side, align, radius),
242
+ transition: MORPH_CLIP_TRANSITION,
243
+ },
244
+ show: {
245
+ clipPath: clipShown(radius),
246
+ transition: MORPH_CLIP_TRANSITION,
247
+ },
248
+ };
249
+
250
+ // Keep the server and first client render identical, then mount the portal.
251
+ if (!portalReady) return null;
252
+
253
+ return createPortal(
254
+ <AnimatePresence>
255
+ {ctx.open ? (
256
+ <motion.div
257
+ data-morph-popover-portal=""
258
+ // Wrapper carries the shadow as a drop-shadow filter, which hugs the
259
+ // clipped shape below (box-shadow would just get clipped away).
260
+ variants={wrap}
261
+ initial={reduce ? { opacity: 0 } : "hidden"}
262
+ animate={reduce ? { opacity: 1 } : "show"}
263
+ exit={reduce ? { opacity: 0 } : "hidden"}
264
+ transition={reduce ? { duration: 0.12 } : undefined}
265
+ style={{
266
+ left,
267
+ top,
268
+ visibility: layout ? "visible" : "hidden",
269
+ transformOrigin: originFor(side, align),
270
+ }}
271
+ className="fixed z-[9999] [filter:drop-shadow(0_10px_18px_rgba(0,0,0,0.14))]"
272
+ >
273
+ <motion.div
274
+ ref={setContentRef}
275
+ id={contentId}
276
+ role="dialog"
277
+ aria-labelledby={triggerId}
278
+ variants={clip}
279
+ style={{ borderRadius: radius }}
280
+ className={cn(
281
+ "overflow-hidden border border-border bg-background",
282
+ className,
283
+ )}
284
+ >
285
+ {children}
286
+ </motion.div>
287
+ </motion.div>
288
+ ) : null}
289
+ </AnimatePresence>,
290
+ document.body,
291
+ );
292
+ }
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ import {
4
+ type MutableRefObject,
5
+ useCallback,
6
+ useLayoutEffect,
7
+ useState,
8
+ } from "react";
9
+
10
+ export type PortalLayout = {
11
+ trigger: {
12
+ left: number;
13
+ top: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+ content: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ };
22
+
23
+ function sameLayout(a: PortalLayout | null, b: PortalLayout) {
24
+ return (
25
+ a?.trigger.left === b.trigger.left &&
26
+ a.trigger.top === b.trigger.top &&
27
+ a.trigger.width === b.trigger.width &&
28
+ a.trigger.height === b.trigger.height &&
29
+ a.content.width === b.content.width &&
30
+ a.content.height === b.content.height
31
+ );
32
+ }
33
+
34
+ /** Measures a trigger and portalled panel in viewport coordinates. */
35
+ export function usePopoverPortalPosition<
36
+ TriggerElement extends HTMLElement,
37
+ ContentElement extends HTMLElement,
38
+ >(
39
+ triggerRef: MutableRefObject<TriggerElement | null>,
40
+ contentRef: MutableRefObject<ContentElement | null>,
41
+ active: boolean,
42
+ ) {
43
+ const [layout, setLayout] = useState<PortalLayout | null>(null);
44
+
45
+ const update = useCallback(() => {
46
+ const trigger = triggerRef.current;
47
+ const content = contentRef.current;
48
+ if (!trigger || !content) return;
49
+
50
+ const rect = trigger.getBoundingClientRect();
51
+ const next: PortalLayout = {
52
+ trigger: {
53
+ left: rect.left,
54
+ top: rect.top,
55
+ width: rect.width,
56
+ height: rect.height,
57
+ },
58
+ content: {
59
+ width: content.offsetWidth,
60
+ height: content.offsetHeight,
61
+ },
62
+ };
63
+ setLayout((current) => (sameLayout(current, next) ? current : next));
64
+ }, [contentRef, triggerRef]);
65
+
66
+ useLayoutEffect(() => {
67
+ update();
68
+ if (!active) return;
69
+
70
+ const trigger = triggerRef.current;
71
+ const content = contentRef.current;
72
+ const observer = new ResizeObserver(update);
73
+ if (trigger) observer.observe(trigger);
74
+ if (content) observer.observe(content);
75
+
76
+ window.addEventListener("scroll", update, true);
77
+ window.addEventListener("resize", update);
78
+ return () => {
79
+ observer.disconnect();
80
+ window.removeEventListener("scroll", update, true);
81
+ window.removeEventListener("resize", update);
82
+ };
83
+ }, [active, contentRef, triggerRef, update]);
84
+
85
+ return layout;
86
+ }
@@ -0,0 +1,52 @@
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
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-[0.625rem] font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-2.5!",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ secondary:
14
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
15
+ destructive:
16
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
17
+ outline:
18
+ "border-border bg-input/20 text-foreground dark:bg-input/30 [a]:hover:bg-muted [a]:hover:text-muted-foreground",
19
+ ghost:
20
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Badge({
31
+ className,
32
+ variant = "default",
33
+ render,
34
+ ...props
35
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
36
+ return useRender({
37
+ defaultTagName: "span",
38
+ props: mergeProps<"span">(
39
+ {
40
+ className: cn(badgeVariants({ variant }), className),
41
+ },
42
+ props
43
+ ),
44
+ render,
45
+ state: {
46
+ slot: "badge",
47
+ variant,
48
+ },
49
+ })
50
+ }
51
+
52
+ export { Badge, badgeVariants }
@@ -0,0 +1,257 @@
1
+ "use client";
2
+
3
+ import { mergeProps } from "@base-ui/react/merge-props";
4
+ import { Select as SelectPrimitive } from "@base-ui/react/select";
5
+ import { useRender } from "@base-ui/react/use-render";
6
+ import { cva, type VariantProps } from "class-variance-authority";
7
+ import {
8
+ ChevronDownIcon,
9
+ ChevronsUpDownIcon,
10
+ ChevronUpIcon,
11
+ } from "lucide-react";
12
+ import type * as React from "react";
13
+ import { cn } from "@/lib/utils";
14
+
15
+ export const Select: typeof SelectPrimitive.Root = SelectPrimitive.Root;
16
+
17
+ export const selectTriggerVariants = cva(
18
+ "relative inline-flex min-h-9 w-full min-w-36 select-none items-center justify-between gap-2 rounded-lg border border-input bg-background not-dark:bg-clip-padding px-[calc(--spacing(3)-1px)] text-left text-base text-foreground shadow-xs/5 outline-none ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_1px_--theme(--color-black/4%)] pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 focus-visible:border-ring focus-visible:ring-[3px] aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-8 sm:text-sm dark:bg-input/32 dark:aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-visible:not-aria-invalid:not-data-pressed:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 [[data-disabled],:focus-visible,[aria-invalid],[data-pressed]]:shadow-none",
19
+ {
20
+ defaultVariants: {
21
+ size: "default",
22
+ },
23
+ variants: {
24
+ size: {
25
+ default: "",
26
+ lg: "min-h-10 sm:min-h-9",
27
+ sm: "min-h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:min-h-7",
28
+ },
29
+ },
30
+ },
31
+ );
32
+
33
+ export const selectTriggerIconClassName = "-me-1 size-4.5 opacity-80 sm:size-4";
34
+
35
+ export interface SelectButtonProps extends useRender.ComponentProps<"button"> {
36
+ size?: VariantProps<typeof selectTriggerVariants>["size"];
37
+ }
38
+
39
+ export function SelectButton({
40
+ className,
41
+ size,
42
+ render,
43
+ children,
44
+ ...props
45
+ }: SelectButtonProps): React.ReactElement {
46
+ const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
47
+ render ? undefined : "button";
48
+
49
+ const defaultProps = {
50
+ children: (
51
+ <>
52
+ <span className="flex-1 truncate in-data-placeholder:text-muted-foreground/72">
53
+ {children}
54
+ </span>
55
+ <ChevronsUpDownIcon className={selectTriggerIconClassName} />
56
+ </>
57
+ ),
58
+ className: cn(selectTriggerVariants({ size }), "min-w-0", className),
59
+ "data-slot": "select-button",
60
+ type: typeValue,
61
+ };
62
+
63
+ return useRender({
64
+ defaultTagName: "button",
65
+ props: mergeProps<"button">(defaultProps, props),
66
+ render,
67
+ });
68
+ }
69
+
70
+ export function SelectTrigger({
71
+ className,
72
+ size = "default",
73
+ children,
74
+ ...props
75
+ }: SelectPrimitive.Trigger.Props &
76
+ VariantProps<typeof selectTriggerVariants>): React.ReactElement {
77
+ return (
78
+ <SelectPrimitive.Trigger
79
+ className={cn(selectTriggerVariants({ size }), className)}
80
+ data-slot="select-trigger"
81
+ {...props}
82
+ >
83
+ {children}
84
+ <SelectPrimitive.Icon data-slot="select-icon">
85
+ <ChevronsUpDownIcon className={selectTriggerIconClassName} />
86
+ </SelectPrimitive.Icon>
87
+ </SelectPrimitive.Trigger>
88
+ );
89
+ }
90
+
91
+ export function SelectValue({
92
+ className,
93
+ ...props
94
+ }: SelectPrimitive.Value.Props): React.ReactElement {
95
+ return (
96
+ <SelectPrimitive.Value
97
+ className={cn(
98
+ "flex-1 truncate data-placeholder:text-muted-foreground",
99
+ className,
100
+ )}
101
+ data-slot="select-value"
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ export function SelectPopup({
108
+ className,
109
+ children,
110
+ side = "bottom",
111
+ sideOffset = 4,
112
+ align = "start",
113
+ alignOffset = 0,
114
+ alignItemWithTrigger = true,
115
+ anchor,
116
+ portalProps,
117
+ ...props
118
+ }: SelectPrimitive.Popup.Props & {
119
+ portalProps?: SelectPrimitive.Portal.Props;
120
+ side?: SelectPrimitive.Positioner.Props["side"];
121
+ sideOffset?: SelectPrimitive.Positioner.Props["sideOffset"];
122
+ align?: SelectPrimitive.Positioner.Props["align"];
123
+ alignOffset?: SelectPrimitive.Positioner.Props["alignOffset"];
124
+ alignItemWithTrigger?: SelectPrimitive.Positioner.Props["alignItemWithTrigger"];
125
+ anchor?: SelectPrimitive.Positioner.Props["anchor"];
126
+ }): React.ReactElement {
127
+ return (
128
+ <SelectPrimitive.Portal {...portalProps}>
129
+ <SelectPrimitive.Positioner
130
+ align={align}
131
+ alignItemWithTrigger={alignItemWithTrigger}
132
+ alignOffset={alignOffset}
133
+ anchor={anchor}
134
+ className="z-50 select-none"
135
+ data-slot="select-positioner"
136
+ side={side}
137
+ sideOffset={sideOffset}
138
+ >
139
+ <SelectPrimitive.Popup
140
+ className="origin-(--transform-origin) text-foreground outline-none"
141
+ data-slot="select-popup"
142
+ {...props}
143
+ >
144
+ <SelectPrimitive.ScrollUpArrow
145
+ className="top-0 z-50 flex h-6 w-full cursor-default items-center justify-center before:pointer-events-none before:absolute before:inset-x-px before:top-px before:h-[200%] before:rounded-t-[calc(var(--radius-lg)-1px)] before:bg-linear-to-b before:from-50% before:from-popover"
146
+ data-slot="select-scroll-up-arrow"
147
+ >
148
+ <ChevronUpIcon className="relative size-4.5 sm:size-4" />
149
+ </SelectPrimitive.ScrollUpArrow>
150
+ <div className="relative h-full min-w-(--anchor-width) rounded-lg border bg-popover not-dark:bg-clip-padding shadow-lg/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]">
151
+ <SelectPrimitive.List
152
+ className={cn(
153
+ "max-h-(--available-height) overflow-y-auto p-1",
154
+ className,
155
+ )}
156
+ data-slot="select-list"
157
+ >
158
+ {children}
159
+ </SelectPrimitive.List>
160
+ </div>
161
+ <SelectPrimitive.ScrollDownArrow
162
+ className="bottom-0 z-50 flex h-6 w-full cursor-default items-center justify-center before:pointer-events-none before:absolute before:inset-x-px before:bottom-px before:h-[200%] before:rounded-b-[calc(var(--radius-lg)-1px)] before:bg-linear-to-t before:from-50% before:from-popover"
163
+ data-slot="select-scroll-down-arrow"
164
+ >
165
+ <ChevronDownIcon className="relative size-4.5 sm:size-4" />
166
+ </SelectPrimitive.ScrollDownArrow>
167
+ </SelectPrimitive.Popup>
168
+ </SelectPrimitive.Positioner>
169
+ </SelectPrimitive.Portal>
170
+ );
171
+ }
172
+
173
+ export function SelectItem({
174
+ className,
175
+ children,
176
+ ...props
177
+ }: SelectPrimitive.Item.Props): React.ReactElement {
178
+ return (
179
+ <SelectPrimitive.Item
180
+ className={cn(
181
+ "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
182
+ className,
183
+ )}
184
+ data-slot="select-item"
185
+ {...props}
186
+ >
187
+ <SelectPrimitive.ItemIndicator className="col-start-1">
188
+ <svg
189
+ aria-hidden="true"
190
+ fill="none"
191
+ height="24"
192
+ stroke="currentColor"
193
+ strokeLinecap="round"
194
+ strokeLinejoin="round"
195
+ strokeWidth="2"
196
+ viewBox="0 0 24 24"
197
+ width="24"
198
+ xmlns="http://www.w3.org/2000/svg"
199
+ >
200
+ <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
201
+ </svg>
202
+ </SelectPrimitive.ItemIndicator>
203
+ <SelectPrimitive.ItemText className="col-start-2 min-w-0">
204
+ {children}
205
+ </SelectPrimitive.ItemText>
206
+ </SelectPrimitive.Item>
207
+ );
208
+ }
209
+
210
+ export function SelectSeparator({
211
+ className,
212
+ ...props
213
+ }: SelectPrimitive.Separator.Props): React.ReactElement {
214
+ return (
215
+ <SelectPrimitive.Separator
216
+ className={cn("mx-2 my-1 h-px bg-border", className)}
217
+ data-slot="select-separator"
218
+ {...props}
219
+ />
220
+ );
221
+ }
222
+
223
+ export function SelectGroup(
224
+ props: SelectPrimitive.Group.Props,
225
+ ): React.ReactElement {
226
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
227
+ }
228
+
229
+ export function SelectLabel({
230
+ className,
231
+ ...props
232
+ }: SelectPrimitive.Label.Props): React.ReactElement {
233
+ return (
234
+ <SelectPrimitive.Label
235
+ className={cn(
236
+ "not-in-data-[slot=field]:mb-2 inline-flex cursor-default items-center gap-2 font-medium text-base/4.5 text-foreground sm:text-sm/4",
237
+ className,
238
+ )}
239
+ data-slot="select-label"
240
+ {...props}
241
+ />
242
+ );
243
+ }
244
+
245
+ export function SelectGroupLabel(
246
+ props: SelectPrimitive.GroupLabel.Props,
247
+ ): React.ReactElement {
248
+ return (
249
+ <SelectPrimitive.GroupLabel
250
+ className="px-2 py-1.5 font-medium text-muted-foreground text-xs"
251
+ data-slot="select-group-label"
252
+ {...props}
253
+ />
254
+ );
255
+ }
256
+
257
+ export { SelectPrimitive, SelectPopup as SelectContent };