From e4fe7f05a6cf4ec6d1430ec59bf73cace83063f3 Mon Sep 17 00:00:00 2001
From: ronload <91997734+ronload@users.noreply.github.com>
Date: Wed, 15 Apr 2026 00:29:33 +0800
Subject: [PATCH 1/4] refactor(form): use shadcn native combobox instead
customize combobox
---
src/components/road-combobox.tsx | 98 +++++-----
src/components/ui/button.tsx | 14 +-
src/components/ui/combobox.tsx | 297 ++++++++++++++++++++++++++++++
src/components/ui/input-group.tsx | 54 +++---
src/components/ui/input.tsx | 14 +-
src/components/ui/textarea.tsx | 12 +-
6 files changed, 386 insertions(+), 103 deletions(-)
create mode 100644 src/components/ui/combobox.tsx
diff --git a/src/components/road-combobox.tsx b/src/components/road-combobox.tsx
index da9801d..713e74c 100644
--- a/src/components/road-combobox.tsx
+++ b/src/components/road-combobox.tsx
@@ -1,21 +1,15 @@
"use client";
-import { useState } from "react";
import type { Road } from "@/lib/types";
import {
- Command,
- CommandEmpty,
- CommandGroup,
- CommandInput,
- CommandItem,
- CommandList,
-} from "@/components/ui/command";
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover";
-import { ChevronsUpDown, Loader2 } from "lucide-react";
+ Combobox,
+ ComboboxContent,
+ ComboboxEmpty,
+ ComboboxInput,
+ ComboboxItem,
+ ComboboxList,
+} from "@/components/ui/combobox";
+import { Loader2 } from "lucide-react";
interface RoadComboboxProps {
roads: Road[];
@@ -32,54 +26,46 @@ export function RoadCombobox({
disabled,
loading,
}: RoadComboboxProps) {
- const [open, setOpen] = useState(false);
-
return (
-
-
+
+ {"載入中..."}
+
+ ) : (
+ onChange(val ?? null)}
+ itemToStringLabel={(road) => road.name}
+ filter={(road, query) => {
+ const q = query.toLowerCase();
+ return (
+ road.name.toLowerCase().includes(q) ||
+ road.en.toLowerCase().includes(q)
+ );
+ }}
>
- {loading ? (
-
-
- {"載入中..."}
-
- ) : value ? (
- {value.name}
- ) : (
- {"請選擇路/街..."}
- )}
-
-
-
-
-
-
- {"查無結果"}
-
- {roads.map((road) => (
- {
- onChange(road);
- setOpen(false);
- }}
- data-checked={value?.name === road.name ? true : undefined}
- >
- {road.name}
-
- ))}
-
-
-
-
-
+
+
+ {"查無結果"}
+
+ {(road: Road) => (
+
+ {road.name}
+
+ )}
+
+
+
+ )}
);
}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index cc44df6..09df753 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -1,7 +1,7 @@
-import { Button as ButtonPrimitive } from "@base-ui/react/button";
-import { cva, type VariantProps } from "class-variance-authority";
+import { Button as ButtonPrimitive } from "@base-ui/react/button"
+import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils";
+import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -37,8 +37,8 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
- },
-);
+ }
+)
function Button({
className,
@@ -52,7 +52,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
- );
+ )
}
-export { Button, buttonVariants };
+export { Button, buttonVariants }
diff --git a/src/components/ui/combobox.tsx b/src/components/ui/combobox.tsx
new file mode 100644
index 0000000..39e2b93
--- /dev/null
+++ b/src/components/ui/combobox.tsx
@@ -0,0 +1,297 @@
+"use client"
+
+import * as React from "react"
+import { Combobox as ComboboxPrimitive } from "@base-ui/react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/components/ui/input-group"
+import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
+
+const Combobox = ComboboxPrimitive.Root
+
+function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
+ return
+}
+
+function ComboboxTrigger({
+ className,
+ children,
+ ...props
+}: ComboboxPrimitive.Trigger.Props) {
+ return (
+
+ {children}
+
+
+ )
+}
+
+function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
+ return (
+ }
+ className={cn(className)}
+ {...props}
+ >
+
+
+ )
+}
+
+function ComboboxInput({
+ className,
+ children,
+ disabled = false,
+ showTrigger = true,
+ showClear = false,
+ ...props
+}: ComboboxPrimitive.Input.Props & {
+ showTrigger?: boolean
+ showClear?: boolean
+}) {
+ return (
+
+ }
+ {...props}
+ />
+
+ {showTrigger && (
+ }
+ data-slot="input-group-button"
+ className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
+ disabled={disabled}
+ />
+ )}
+ {showClear && }
+
+ {children}
+
+ )
+}
+
+function ComboboxContent({
+ className,
+ side = "bottom",
+ sideOffset = 6,
+ align = "start",
+ alignOffset = 0,
+ anchor,
+ ...props
+}: ComboboxPrimitive.Popup.Props &
+ Pick<
+ ComboboxPrimitive.Positioner.Props,
+ "side" | "align" | "sideOffset" | "alignOffset" | "anchor"
+ >) {
+ return (
+
+
+
+
+
+ )
+}
+
+function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
+ return (
+
+ )
+}
+
+function ComboboxItem({
+ className,
+ children,
+ ...props
+}: ComboboxPrimitive.Item.Props) {
+ return (
+
+ {children}
+
+ }
+ >
+
+
+
+ )
+}
+
+function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
+ return (
+
+ )
+}
+
+function ComboboxLabel({
+ className,
+ ...props
+}: ComboboxPrimitive.GroupLabel.Props) {
+ return (
+
+ )
+}
+
+function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
+ return (
+
+ )
+}
+
+function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
+ return (
+
+ )
+}
+
+function ComboboxSeparator({
+ className,
+ ...props
+}: ComboboxPrimitive.Separator.Props) {
+ return (
+
+ )
+}
+
+function ComboboxChips({
+ className,
+ ...props
+}: React.ComponentPropsWithRef &
+ ComboboxPrimitive.Chips.Props) {
+ return (
+
+ )
+}
+
+function ComboboxChip({
+ className,
+ children,
+ showRemove = true,
+ ...props
+}: ComboboxPrimitive.Chip.Props & {
+ showRemove?: boolean
+}) {
+ return (
+
+ {children}
+ {showRemove && (
+ }
+ className="-ml-1 opacity-50 hover:opacity-100"
+ data-slot="combobox-chip-remove"
+ >
+
+
+ )}
+
+ )
+}
+
+function ComboboxChipsInput({
+ className,
+ ...props
+}: ComboboxPrimitive.Input.Props) {
+ return (
+
+ )
+}
+
+function useComboboxAnchor() {
+ return React.useRef(null)
+}
+
+export {
+ Combobox,
+ ComboboxInput,
+ ComboboxContent,
+ ComboboxList,
+ ComboboxItem,
+ ComboboxGroup,
+ ComboboxLabel,
+ ComboboxCollection,
+ ComboboxEmpty,
+ ComboboxSeparator,
+ ComboboxChips,
+ ComboboxChip,
+ ComboboxChipsInput,
+ ComboboxTrigger,
+ ComboboxValue,
+ useComboboxAnchor,
+}
diff --git a/src/components/ui/input-group.tsx b/src/components/ui/input-group.tsx
index 989c2ec..da8f1dd 100644
--- a/src/components/ui/input-group.tsx
+++ b/src/components/ui/input-group.tsx
@@ -1,12 +1,12 @@
-"use client";
+"use client"
-import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
-import { cn } from "@/lib/utils";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Textarea } from "@/components/ui/textarea";
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Textarea } from "@/components/ui/textarea"
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
@@ -14,12 +14,12 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group"
role="group"
className={cn(
- "group/input-group border-input has-disabled:bg-input/50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 relative flex h-8 w-full min-w-0 items-center rounded-lg border transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
- className,
+ "group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
+ className
)}
{...props}
/>
- );
+ )
}
const inputGroupAddonVariants = cva(
@@ -40,8 +40,8 @@ const inputGroupAddonVariants = cva(
defaultVariants: {
align: "inline-start",
},
- },
-);
+ }
+)
function InputGroupAddon({
className,
@@ -56,13 +56,13 @@ function InputGroupAddon({
className={cn(inputGroupAddonVariants({ align }), className)}
onClick={(e) => {
if ((e.target as HTMLElement).closest("button")) {
- return;
+ return
}
- e.currentTarget.parentElement?.querySelector("input")?.focus();
+ e.currentTarget.parentElement?.querySelector("input")?.focus()
}}
{...props}
/>
- );
+ )
}
const inputGroupButtonVariants = cva(
@@ -80,8 +80,8 @@ const inputGroupButtonVariants = cva(
defaultVariants: {
size: "xs",
},
- },
-);
+ }
+)
function InputGroupButton({
className,
@@ -91,7 +91,7 @@ function InputGroupButton({
...props
}: Omit, "size" | "type"> &
VariantProps & {
- type?: "button" | "submit" | "reset";
+ type?: "button" | "submit" | "reset"
}) {
return (
- );
+ )
}
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
return (
- );
+ )
}
function InputGroupInput({
@@ -125,11 +125,11 @@ function InputGroupInput({
data-slot="input-group-control"
className={cn(
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
- className,
+ className
)}
{...props}
/>
- );
+ )
}
function InputGroupTextarea({
@@ -141,11 +141,11 @@ function InputGroupTextarea({
data-slot="input-group-control"
className={cn(
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
- className,
+ className
)}
{...props}
/>
- );
+ )
}
export {
@@ -155,4 +155,4 @@ export {
InputGroupText,
InputGroupInput,
InputGroupTextarea,
-};
+}
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
index da4b177..7d21bab 100644
--- a/src/components/ui/input.tsx
+++ b/src/components/ui/input.tsx
@@ -1,7 +1,7 @@
-import * as React from "react";
-import { Input as InputPrimitive } from "@base-ui/react/input";
+import * as React from "react"
+import { Input as InputPrimitive } from "@base-ui/react/input"
-import { cn } from "@/lib/utils";
+import { cn } from "@/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
@@ -9,12 +9,12 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
- "border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-8 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
- className,
+ "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
+ className
)}
{...props}
/>
- );
+ )
}
-export { Input };
+export { Input }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
index 4168805..04d27f7 100644
--- a/src/components/ui/textarea.tsx
+++ b/src/components/ui/textarea.tsx
@@ -1,18 +1,18 @@
-import * as React from "react";
+import * as React from "react"
-import { cn } from "@/lib/utils";
+import { cn } from "@/lib/utils"
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
- );
+ )
}
-export { Textarea };
+export { Textarea }
From ab5d50180f33962c84ab0bb1f99ede39d6a5cf74 Mon Sep 17 00:00:00 2001
From: ronload <91997734+ronload@users.noreply.github.com>
Date: Wed, 15 Apr 2026 00:32:40 +0800
Subject: [PATCH 2/4] style: format code style
---
src/components/road-combobox.tsx | 5 +-
src/components/ui/button.tsx | 14 ++---
src/components/ui/combobox.tsx | 85 ++++++++++++++++---------------
src/components/ui/input-group.tsx | 54 ++++++++++----------
src/components/ui/input.tsx | 14 ++---
src/components/ui/textarea.tsx | 12 ++---
6 files changed, 92 insertions(+), 92 deletions(-)
diff --git a/src/components/road-combobox.tsx b/src/components/road-combobox.tsx
index 713e74c..e0da4fc 100644
--- a/src/components/road-combobox.tsx
+++ b/src/components/road-combobox.tsx
@@ -50,10 +50,7 @@ export function RoadCombobox({
);
}}
>
-
+
{"查無結果"}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 09df753..cc44df6 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -1,7 +1,7 @@
-import { Button as ButtonPrimitive } from "@base-ui/react/button"
-import { cva, type VariantProps } from "class-variance-authority"
+import { Button as ButtonPrimitive } from "@base-ui/react/button";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@@ -37,8 +37,8 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
- }
-)
+ },
+);
function Button({
className,
@@ -52,7 +52,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
- )
+ );
}
-export { Button, buttonVariants }
+export { Button, buttonVariants };
diff --git a/src/components/ui/combobox.tsx b/src/components/ui/combobox.tsx
index 39e2b93..3767a85 100644
--- a/src/components/ui/combobox.tsx
+++ b/src/components/ui/combobox.tsx
@@ -1,22 +1,22 @@
-"use client"
+"use client";
-import * as React from "react"
-import { Combobox as ComboboxPrimitive } from "@base-ui/react"
+import * as React from "react";
+import { Combobox as ComboboxPrimitive } from "@base-ui/react";
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
-} from "@/components/ui/input-group"
-import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
+} from "@/components/ui/input-group";
+import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react";
-const Combobox = ComboboxPrimitive.Root
+const Combobox = ComboboxPrimitive.Root;
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
- return
+ return ;
}
function ComboboxTrigger({
@@ -31,9 +31,9 @@ function ComboboxTrigger({
{...props}
>
{children}
-
+
- )
+ );
}
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
@@ -46,7 +46,7 @@ function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
>
- )
+ );
}
function ComboboxInput({
@@ -57,8 +57,8 @@ function ComboboxInput({
showClear = false,
...props
}: ComboboxPrimitive.Input.Props & {
- showTrigger?: boolean
- showClear?: boolean
+ showTrigger?: boolean;
+ showClear?: boolean;
}) {
return (
@@ -81,7 +81,7 @@ function ComboboxInput({
{children}
- )
+ );
}
function ComboboxContent({
@@ -110,12 +110,15 @@ function ComboboxContent({
- )
+ );
}
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
@@ -124,11 +127,11 @@ function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
data-slot="combobox-list"
className={cn(
"no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function ComboboxItem({
@@ -140,8 +143,8 @@ function ComboboxItem({
@@ -154,7 +157,7 @@ function ComboboxItem({
- )
+ );
}
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
@@ -164,7 +167,7 @@ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
className={cn(className)}
{...props}
/>
- )
+ );
}
function ComboboxLabel({
@@ -174,16 +177,16 @@ function ComboboxLabel({
return (
- )
+ );
}
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
return (
- )
+ );
}
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
@@ -191,12 +194,12 @@ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
- )
+ );
}
function ComboboxSeparator({
@@ -206,10 +209,10 @@ function ComboboxSeparator({
return (
- )
+ );
}
function ComboboxChips({
@@ -221,12 +224,12 @@ function ComboboxChips({
- )
+ );
}
function ComboboxChip({
@@ -235,14 +238,14 @@ function ComboboxChip({
showRemove = true,
...props
}: ComboboxPrimitive.Chip.Props & {
- showRemove?: boolean
+ showRemove?: boolean;
}) {
return (
@@ -257,7 +260,7 @@ function ComboboxChip({
)}
- )
+ );
}
function ComboboxChipsInput({
@@ -270,11 +273,11 @@ function ComboboxChipsInput({
className={cn("min-w-16 flex-1 outline-none", className)}
{...props}
/>
- )
+ );
}
function useComboboxAnchor() {
- return React.useRef(null)
+ return React.useRef(null);
}
export {
@@ -294,4 +297,4 @@ export {
ComboboxTrigger,
ComboboxValue,
useComboboxAnchor,
-}
+};
diff --git a/src/components/ui/input-group.tsx b/src/components/ui/input-group.tsx
index da8f1dd..989c2ec 100644
--- a/src/components/ui/input-group.tsx
+++ b/src/components/ui/input-group.tsx
@@ -1,12 +1,12 @@
-"use client"
+"use client";
-import * as React from "react"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
-import { Textarea } from "@/components/ui/textarea"
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Textarea } from "@/components/ui/textarea";
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
@@ -14,12 +14,12 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group"
role="group"
className={cn(
- "group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
- className
+ "group/input-group border-input has-disabled:bg-input/50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 relative flex h-8 w-full min-w-0 items-center rounded-lg border transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
+ className,
)}
{...props}
/>
- )
+ );
}
const inputGroupAddonVariants = cva(
@@ -40,8 +40,8 @@ const inputGroupAddonVariants = cva(
defaultVariants: {
align: "inline-start",
},
- }
-)
+ },
+);
function InputGroupAddon({
className,
@@ -56,13 +56,13 @@ function InputGroupAddon({
className={cn(inputGroupAddonVariants({ align }), className)}
onClick={(e) => {
if ((e.target as HTMLElement).closest("button")) {
- return
+ return;
}
- e.currentTarget.parentElement?.querySelector("input")?.focus()
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
}}
{...props}
/>
- )
+ );
}
const inputGroupButtonVariants = cva(
@@ -80,8 +80,8 @@ const inputGroupButtonVariants = cva(
defaultVariants: {
size: "xs",
},
- }
-)
+ },
+);
function InputGroupButton({
className,
@@ -91,7 +91,7 @@ function InputGroupButton({
...props
}: Omit, "size" | "type"> &
VariantProps & {
- type?: "button" | "submit" | "reset"
+ type?: "button" | "submit" | "reset";
}) {
return (
- )
+ );
}
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
return (
- )
+ );
}
function InputGroupInput({
@@ -125,11 +125,11 @@ function InputGroupInput({
data-slot="input-group-control"
className={cn(
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
function InputGroupTextarea({
@@ -141,11 +141,11 @@ function InputGroupTextarea({
data-slot="input-group-control"
className={cn(
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
- className
+ className,
)}
{...props}
/>
- )
+ );
}
export {
@@ -155,4 +155,4 @@ export {
InputGroupText,
InputGroupInput,
InputGroupTextarea,
-}
+};
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
index 7d21bab..da4b177 100644
--- a/src/components/ui/input.tsx
+++ b/src/components/ui/input.tsx
@@ -1,7 +1,7 @@
-import * as React from "react"
-import { Input as InputPrimitive } from "@base-ui/react/input"
+import * as React from "react";
+import { Input as InputPrimitive } from "@base-ui/react/input";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
@@ -9,12 +9,12 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
- "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
- className
+ "border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-8 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
+ className,
)}
{...props}
/>
- )
+ );
}
-export { Input }
+export { Input };
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
index 04d27f7..4168805 100644
--- a/src/components/ui/textarea.tsx
+++ b/src/components/ui/textarea.tsx
@@ -1,18 +1,18 @@
-import * as React from "react"
+import * as React from "react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
- )
+ );
}
-export { Textarea }
+export { Textarea };
From 294dd6a58df914d158c94f8daee8d093531023e6 Mon Sep 17 00:00:00 2001
From: ronload <91997734+ronload@users.noreply.github.com>
Date: Wed, 15 Apr 2026 00:35:12 +0800
Subject: [PATCH 3/4] style: format code style
---
src/components/road-combobox.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/road-combobox.tsx b/src/components/road-combobox.tsx
index e0da4fc..871f5aa 100644
--- a/src/components/road-combobox.tsx
+++ b/src/components/road-combobox.tsx
@@ -40,7 +40,7 @@ export function RoadCombobox({
onChange(val ?? null)}
+ onValueChange={(val) => { onChange(val ?? null); }}
itemToStringLabel={(road) => road.name}
filter={(road, query) => {
const q = query.toLowerCase();
From b03fd5cd06de47a9601a19b7ea09ff76b82ee806 Mon Sep 17 00:00:00 2001
From: ronload <91997734+ronload@users.noreply.github.com>
Date: Wed, 15 Apr 2026 00:39:20 +0800
Subject: [PATCH 4/4] style: format code style
---
src/components/road-combobox.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/components/road-combobox.tsx b/src/components/road-combobox.tsx
index 871f5aa..a985ce2 100644
--- a/src/components/road-combobox.tsx
+++ b/src/components/road-combobox.tsx
@@ -40,7 +40,9 @@ export function RoadCombobox({
{ onChange(val ?? null); }}
+ onValueChange={(val) => {
+ onChange(val ?? null);
+ }}
itemToStringLabel={(road) => road.name}
filter={(road, query) => {
const q = query.toLowerCase();