Color Picker
Selecteur de couleur en popover avec des echantillons, un curseur de teinte et une saisie hexadecimale. Installez Color Picker depuis le registre VLLNT UI avec la CLI shadcn.
Selecteur de couleur en popover avec des echantillons, un curseur de teinte et une saisie hexadecimale. Fait partie de la famille form dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.
Installation
Ajoutez Color Picker a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/color-picker.jsonSource
"use client";
import * as React from "react";
import { cn } from "../../lib/utils";
import { Button } from "../button/button";
import { Input } from "../input/input";
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
const presetSwatches: string[] = [
"#ef4444",
"#f97316",
"#f59e0b",
"#84cc16",
"#22c55e",
"#14b8a6",
"#3b82f6",
"#6366f1",
"#a855f7",
"#ec4899",
"#64748b",
"#0f172a",
];
function hexPart(value: number): string {
return value.toString(16).padStart(2, "0");
}
function toHex(red: number, green: number, blue: number): string {
return `#${hexPart(red)}${hexPart(green)}${hexPart(blue)}`;
}
function hueChannel(hue: number, offset: number): number {
const k = (offset + hue / 30) % 12;
const amount = 0.45;
const value = 0.5 - amount * Math.max(-1, Math.min(k - 3, 9 - k, 1));
return Math.round(value * 255);
}
function hueToHex(hue: number): string {
return toHex(hueChannel(hue, 0), hueChannel(hue, 8), hueChannel(hue, 4));
}
type SwatchGridProps = {
onSelect: (value: string) => void;
selected: string;
swatches: string[];
};
function SwatchGrid({ onSelect, selected, swatches }: SwatchGridProps) {
return (
<div className="grid grid-cols-6 gap-2" role="group">
{swatches.map((swatch) => (
<button
aria-label={swatch}
aria-pressed={swatch.toLowerCase() === selected.toLowerCase()}
className={cn(
"size-7 rounded-md border outline-none ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
swatch.toLowerCase() === selected.toLowerCase() &&
"ring-2 ring-ring",
)}
key={swatch}
onClick={() => {
onSelect(swatch);
}}
style={{ backgroundColor: swatch }}
type="button"
/>
))}
</div>
);
}
/** Popover colour picker with swatches, a hue slider, and a hex input. */
export type ColorPickerProps = {
className?: string;
defaultValue?: string;
onValueChange?: (value: string) => void;
swatches?: string[];
value?: string;
};
const ColorPicker = ({
className,
defaultValue = "#3b82f6",
onValueChange,
ref,
swatches = presetSwatches,
value,
}: ColorPickerProps & { ref?: React.Ref<HTMLButtonElement> }) => {
const [internalValue, setInternalValue] = React.useState(defaultValue);
const currentValue = value ?? internalValue;
const update = (next: string) => {
if (value === undefined) {
setInternalValue(next);
}
onValueChange?.(next);
};
return (
<Popover>
<PopoverTrigger asChild>
<Button
className={cn("w-full justify-start gap-2 font-normal", className)}
ref={ref}
variant="outline"
>
<span
aria-hidden
className="size-4 rounded-sm border"
style={{ backgroundColor: currentValue }}
/>
<span className="uppercase tabular-nums">{currentValue}</span>
</Button>
</PopoverTrigger>
<PopoverContent align="start" className="w-56 space-y-3">
<SwatchGrid
onSelect={update}
selected={currentValue}
swatches={swatches}
/>
<input
aria-label="Hue"
className="h-3 w-full cursor-pointer appearance-none rounded-full"
max={360}
min={0}
onChange={(event) => {
update(hueToHex(Number(event.target.value)));
}}
style={{
background:
"linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)",
}}
type="range"
/>
<Input
aria-label="Hex value"
onChange={(event) => {
update(event.target.value);
}}
value={currentValue}
/>
</PopoverContent>
</Popover>
);
};
ColorPicker.displayName = "ColorPicker";
export { ColorPicker };
Stories
Explorez chaque variante et etat dans le Storybook interactif :
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.