mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-24 00:27:47 +00:00
refactor(v5): migrate UI primitives from radix-ui to @base-ui/react
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "radix-lyra",
|
||||
"style": "base-lyra",
|
||||
"rsc": false,
|
||||
"tsx": false,
|
||||
"tailwind": {
|
||||
|
||||
Generated
+84
-1719
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -17,6 +17,7 @@
|
||||
"vite": "7.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.5.0",
|
||||
"@fontsource-variable/geist": "^5.2.9",
|
||||
"@inertiajs/react": "^3.3.0",
|
||||
"@inertiajs/vite": "^3.3.0",
|
||||
@@ -28,7 +29,6 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"playwright": "^1.58.2",
|
||||
"radix-ui": "^1.6.0",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
|
||||
@@ -53,6 +53,15 @@ export function AppNavbar({
|
||||
persistSelection(projectUuid, nextEnvironmentUuid);
|
||||
}
|
||||
|
||||
const projectItems = projects.map((project) => ({
|
||||
label: project.name,
|
||||
value: project.uuid,
|
||||
}));
|
||||
const environmentItems = (selectedProject?.environments ?? []).map((environment) => ({
|
||||
label: environment.name,
|
||||
value: environment.uuid,
|
||||
}));
|
||||
|
||||
return (
|
||||
<header className="fixed inset-x-0 top-0 z-40 border-b border-border bg-background">
|
||||
<nav className="flex h-16 items-center gap-4 px-6" aria-label="Main navigation">
|
||||
@@ -65,7 +74,12 @@ export function AppNavbar({
|
||||
</a>
|
||||
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<Select value={selectedProject?.uuid ?? ''} onValueChange={selectProject} disabled={projects.length === 0}>
|
||||
<Select
|
||||
items={projectItems}
|
||||
value={selectedProject?.uuid ?? ''}
|
||||
onValueChange={selectProject}
|
||||
disabled={projects.length === 0}
|
||||
>
|
||||
<SelectTrigger aria-label="Select a project" variant="ghost" className="max-w-[10rem]">
|
||||
<SelectValue placeholder="Select a project" />
|
||||
</SelectTrigger>
|
||||
@@ -83,6 +97,7 @@ export function AppNavbar({
|
||||
<span className="text-muted-foreground">/</span>
|
||||
|
||||
<Select
|
||||
items={environmentItems}
|
||||
value={selectedEnvironment?.uuid ?? ''}
|
||||
onValueChange={selectEnvironment}
|
||||
disabled={!selectedProject || (selectedProject.environments ?? []).length === 0}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from "react"
|
||||
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
||||
import { cva } from "class-variance-authority";
|
||||
import { Slot } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
@@ -43,16 +42,11 @@ function Button({
|
||||
className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
asChild = false,
|
||||
...props
|
||||
}) {
|
||||
const Comp = asChild ? Slot.Root : "button"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
<ButtonPrimitive
|
||||
data-slot="button"
|
||||
data-variant={variant}
|
||||
data-size={size}
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props} />
|
||||
);
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import * as React from "react"
|
||||
import { Select as SelectPrimitive } from "radix-ui"
|
||||
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { CaretDownIcon, CheckIcon, CaretUpIcon } from "@phosphor-icons/react"
|
||||
|
||||
function Select({
|
||||
...props
|
||||
}) {
|
||||
return <SelectPrimitive.Root data-slot="select" {...props} />;
|
||||
}
|
||||
const Select = SelectPrimitive.Root
|
||||
|
||||
function SelectGroup({
|
||||
className,
|
||||
@@ -23,9 +19,15 @@ function SelectGroup({
|
||||
}
|
||||
|
||||
function SelectValue({
|
||||
className,
|
||||
...props
|
||||
}) {
|
||||
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
|
||||
return (
|
||||
<SelectPrimitive.Value
|
||||
data-slot="select-value"
|
||||
className={cn("flex flex-1 text-left", className)}
|
||||
{...props} />
|
||||
);
|
||||
}
|
||||
|
||||
function SelectTrigger({
|
||||
@@ -39,6 +41,7 @@ function SelectTrigger({
|
||||
<SelectPrimitive.Trigger
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"flex w-fit items-center justify-between gap-1.5 rounded-none whitespace-nowrap transition-colors outline-none select-none focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 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",
|
||||
variant === "default" && "border border-input bg-transparent py-2 pr-2 pl-2.5 text-xs focus-visible:border-ring dark:bg-input/30 dark:hover:bg-input/50",
|
||||
@@ -47,9 +50,10 @@ function SelectTrigger({
|
||||
)}
|
||||
{...props}>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
||||
</SelectPrimitive.Icon>
|
||||
<SelectPrimitive.Icon
|
||||
render={
|
||||
<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
||||
} />
|
||||
</SelectPrimitive.Trigger>
|
||||
);
|
||||
}
|
||||
@@ -57,34 +61,36 @@ function SelectTrigger({
|
||||
function SelectContent({
|
||||
className,
|
||||
children,
|
||||
position = "item-aligned",
|
||||
position,
|
||||
side = "bottom",
|
||||
sideOffset = 4,
|
||||
align = "center",
|
||||
alignOffset = 0,
|
||||
alignItemWithTrigger = position === "popper" ? false : true,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
data-align-trigger={position === "item-aligned"}
|
||||
className={cn(
|
||||
"relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
<SelectPrimitive.Positioner
|
||||
side={side}
|
||||
sideOffset={sideOffset}
|
||||
align={align}
|
||||
{...props}>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
data-position={position}
|
||||
alignOffset={alignOffset}
|
||||
alignItemWithTrigger={alignItemWithTrigger}
|
||||
className="isolate z-50">
|
||||
<SelectPrimitive.Popup
|
||||
data-slot="select-content"
|
||||
data-align-trigger={alignItemWithTrigger}
|
||||
className={cn(
|
||||
"data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
|
||||
position === "popper" && ""
|
||||
)}>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
"relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.List>{children}</SelectPrimitive.List>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Popup>
|
||||
</SelectPrimitive.Positioner>
|
||||
</SelectPrimitive.Portal>
|
||||
);
|
||||
}
|
||||
@@ -94,7 +100,7 @@ function SelectLabel({
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.Label
|
||||
<SelectPrimitive.GroupLabel
|
||||
data-slot="select-label"
|
||||
className={cn("px-2 py-2 text-xs text-muted-foreground", className)}
|
||||
{...props} />
|
||||
@@ -114,13 +120,16 @@ function SelectItem({
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
<span
|
||||
className="pointer-events-none absolute right-2 flex size-4 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="pointer-events-none" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
<SelectPrimitive.ItemText className="flex flex-1 shrink-0 gap-2 whitespace-nowrap">
|
||||
{children}
|
||||
</SelectPrimitive.ItemText>
|
||||
<SelectPrimitive.ItemIndicator
|
||||
render={
|
||||
<span
|
||||
className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
||||
}>
|
||||
<CheckIcon className="pointer-events-none" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</SelectPrimitive.Item>
|
||||
);
|
||||
}
|
||||
@@ -142,15 +151,15 @@ function SelectScrollUpButton({
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
<SelectPrimitive.ScrollUpArrow
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
<CaretUpIcon />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
</SelectPrimitive.ScrollUpArrow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,15 +168,15 @@ function SelectScrollDownButton({
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
<SelectPrimitive.ScrollDownArrow
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
<CaretDownIcon />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
</SelectPrimitive.ScrollDownArrow>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Separator as SeparatorPrimitive } from "radix-ui"
|
||||
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
orientation = "horizontal",
|
||||
decorative = true,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
<SeparatorPrimitive
|
||||
data-slot="separator"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
|
||||
|
||||
@@ -380,12 +380,15 @@ it('defines a ghost variant for compact v5 select triggers', function () {
|
||||
$select = file_get_contents(resource_path('js/v5/components/ui/select.jsx'));
|
||||
|
||||
expect($select)
|
||||
->toContain('@base-ui/react/select')
|
||||
->not->toContain('radix-ui')
|
||||
->toContain('variant = "default"')
|
||||
->toContain('variant === "default"')
|
||||
->toContain('variant === "ghost"')
|
||||
->toContain('border-transparent')
|
||||
->toContain('h-auto')
|
||||
->toContain('text-sm');
|
||||
->toContain('text-sm')
|
||||
->toContain('position === "popper" ? false : true');
|
||||
});
|
||||
|
||||
it('uses the requested shadcn preset configuration for v5', function () {
|
||||
@@ -393,7 +396,7 @@ it('uses the requested shadcn preset configuration for v5', function () {
|
||||
$css = file_get_contents(resource_path('css/v5/app.css'));
|
||||
|
||||
expect($components['style'])
|
||||
->toBe('radix-lyra')
|
||||
->toBe('base-lyra')
|
||||
->and($components['iconLibrary'])->toBe('phosphor')
|
||||
->and($components['tailwind']['css'])->toBe('resources/css/v5/app.css')
|
||||
->and($components['tailwind']['baseColor'])->toBe('zinc')
|
||||
|
||||
Reference in New Issue
Block a user