kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1"use client";
2
3import { mergeProps } from "@base-ui/react/merge-props";
4import { Select as SelectPrimitive } from "@base-ui/react/select";
5import { useRender } from "@base-ui/react/use-render";
6import { cva, type VariantProps } from "class-variance-authority";
7import {
8 ChevronDownIcon,
9 ChevronsUpDownIcon,
10 ChevronUpIcon,
11} from "lucide-react";
12import type * as React from "react";
13
14import { cn } from "@/lib/cn";
15
16const Select = SelectPrimitive.Root;
17
18const selectTriggerVariants = cva(
19 "relative inline-flex min-h-9 w-full min-w-36 cursor-pointer 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",
20 {
21 defaultVariants: {
22 size: "default",
23 },
24 variants: {
25 size: {
26 default: "",
27 lg: "min-h-10 sm:min-h-9",
28 sm: "min-h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:min-h-7",
29 },
30 },
31 },
32);
33
34const selectTriggerIconClassName = "-me-1 size-4.5 opacity-80 sm:size-4";
35
36interface SelectButtonProps extends useRender.ComponentProps<"button"> {
37 size?: VariantProps<typeof selectTriggerVariants>["size"];
38}
39
40function SelectButton({
41 className,
42 size,
43 render,
44 children,
45 ...props
46}: SelectButtonProps) {
47 const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
48 render ? undefined : "button";
49
50 const defaultProps = {
51 children: (
52 <>
53 <span className="flex-1 truncate in-data-placeholder:text-muted-foreground/72">
54 {children}
55 </span>
56 <ChevronsUpDownIcon className={selectTriggerIconClassName} />
57 </>
58 ),
59 className: cn(selectTriggerVariants({ size }), "min-w-none", className),
60 "data-slot": "select-button",
61 type: typeValue,
62 };
63
64 return useRender({
65 defaultTagName: "button",
66 props: mergeProps<"button">(defaultProps, props),
67 render,
68 });
69}
70
71function SelectTrigger({
72 className,
73 size = "default",
74 children,
75 ...props
76}: SelectPrimitive.Trigger.Props & VariantProps<typeof selectTriggerVariants>) {
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
91function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
92 return (
93 <SelectPrimitive.Value
94 className={cn(
95 "flex-1 truncate data-placeholder:text-muted-foreground",
96 className,
97 )}
98 data-slot="select-value"
99 {...props}
100 />
101 );
102}
103
104function SelectPopup({
105 className,
106 children,
107 side = "bottom",
108 sideOffset = 4,
109 align = "start",
110 alignOffset = 0,
111 alignItemWithTrigger = true,
112 anchor,
113 ...props
114}: SelectPrimitive.Popup.Props & {
115 side?: SelectPrimitive.Positioner.Props["side"];
116 sideOffset?: SelectPrimitive.Positioner.Props["sideOffset"];
117 align?: SelectPrimitive.Positioner.Props["align"];
118 alignOffset?: SelectPrimitive.Positioner.Props["alignOffset"];
119 alignItemWithTrigger?: SelectPrimitive.Positioner.Props["alignItemWithTrigger"];
120 anchor?: SelectPrimitive.Positioner.Props["anchor"];
121}) {
122 return (
123 <SelectPrimitive.Portal>
124 <SelectPrimitive.Positioner
125 align={align}
126 alignItemWithTrigger={alignItemWithTrigger}
127 alignOffset={alignOffset}
128 anchor={anchor}
129 className="z-50 select-none"
130 data-slot="select-positioner"
131 side={side}
132 sideOffset={sideOffset}
133 >
134 <SelectPrimitive.Popup
135 className="origin-(--transform-origin) text-foreground transition-[scale,opacity,transform] duration-150 ease-out data-starting-style:scale-98 data-starting-style:opacity-0 data-ending-style:scale-98 data-ending-style:opacity-0 data-[side=top]:data-starting-style:translate-y-1 data-[side=bottom]:data-starting-style:-translate-y-1 data-[side=left]:data-starting-style:translate-x-1 data-[side=right]:data-starting-style:-translate-x-1"
136 data-slot="select-popup"
137 {...props}
138 >
139 <SelectPrimitive.ScrollUpArrow
140 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"
141 data-slot="select-scroll-up-arrow"
142 >
143 <ChevronUpIcon className="relative size-4.5 sm:size-4" />
144 </SelectPrimitive.ScrollUpArrow>
145 <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%)]">
146 <SelectPrimitive.List
147 className={cn(
148 "max-h-(--available-height) overflow-y-auto p-1",
149 className,
150 )}
151 data-slot="select-list"
152 >
153 {children}
154 </SelectPrimitive.List>
155 </div>
156 <SelectPrimitive.ScrollDownArrow
157 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"
158 data-slot="select-scroll-down-arrow"
159 >
160 <ChevronDownIcon className="relative size-4.5 sm:size-4" />
161 </SelectPrimitive.ScrollDownArrow>
162 </SelectPrimitive.Popup>
163 </SelectPrimitive.Positioner>
164 </SelectPrimitive.Portal>
165 );
166}
167
168function SelectItem({
169 className,
170 children,
171 ...props
172}: SelectPrimitive.Item.Props) {
173 return (
174 <SelectPrimitive.Item
175 className={cn(
176 "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",
177 className,
178 )}
179 data-slot="select-item"
180 {...props}
181 >
182 <SelectPrimitive.ItemIndicator className="col-start-1">
183 <svg
184 aria-hidden="true"
185 fill="none"
186 height="24"
187 stroke="currentColor"
188 strokeLinecap="round"
189 strokeLinejoin="round"
190 strokeWidth="2"
191 viewBox="0 0 24 24"
192 width="24"
193 xmlns="http://www.w3.org/1500/svg"
194 >
195 <path d="M5.252 12.7 10.2 18.63 18.748 5.37" />
196 </svg>
197 </SelectPrimitive.ItemIndicator>
198 <SelectPrimitive.ItemText className="col-start-2 min-w-0">
199 {children}
200 </SelectPrimitive.ItemText>
201 </SelectPrimitive.Item>
202 );
203}
204
205function SelectSeparator({
206 className,
207 ...props
208}: SelectPrimitive.Separator.Props) {
209 return (
210 <SelectPrimitive.Separator
211 className={cn("mx-2 my-1 h-px bg-border", className)}
212 data-slot="select-separator"
213 {...props}
214 />
215 );
216}
217
218function SelectGroup(props: SelectPrimitive.Group.Props) {
219 return <SelectPrimitive.Group data-slot="select-group" {...props} />;
220}
221
222function SelectGroupLabel(props: SelectPrimitive.GroupLabel.Props) {
223 return (
224 <SelectPrimitive.GroupLabel
225 className="px-2 py-1.5 font-medium text-muted-foreground text-xs"
226 data-slot="select-group-label"
227 {...props}
228 />
229 );
230}
231
232export {
233 Select,
234 SelectButton,
235 SelectGroup,
236 SelectGroupLabel,
237 SelectItem,
238 SelectPopup,
239 SelectPopup as SelectContent,
240 SelectSeparator,
241 SelectTrigger,
242 SelectValue,
243 selectTriggerVariants,
244};