diff --git a/package-lock.json b/package-lock.json index 036095a..1204bc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@iconify/json": "^2.2.373", "@iconify/utils": "^3.0.1", "@tailwindcss/forms": "^0.5.10", + "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.3.1" }, @@ -2686,6 +2687,19 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/cli-boxes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", diff --git a/package.json b/package.json index 409a968..01b5151 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@iconify/json": "^2.2.373", "@iconify/utils": "^3.0.1", "@tailwindcss/forms": "^0.5.10", + "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.3.1" } diff --git a/src/components/Field.astro b/src/components/Field.astro index 462dc34..47ce164 100644 --- a/src/components/Field.astro +++ b/src/components/Field.astro @@ -89,7 +89,7 @@ const helpCls = "mt-2 text-sm text-gray-500 dark:text-neutral-500"; const errCls = "text-sm text-red-600 mt-2"; const groupCls = "relative flex items-stretch"; const addonBase = - "px-4 inline-flex items-center min-w-fit rounded-s-md border border-e-0 border-gray-200 bg-gray-50 text-sm text-gray-500 dark:bg-neutral-700 dark:border-neutral-700 dark:text-neutral-400"; + "px-4 inline-flex items-center min-w-fit rounded-md border border-gray-200 bg-gray-50 text-sm text-gray-500 dark:bg-neutral-700 dark:border-neutral-700 dark:text-neutral-400"; const controlWrap = "relative"; --- @@ -144,8 +144,6 @@ const controlWrap = "relative"; }), p.prefixText && "rounded-l-none", p.suffixText && "rounded-r-none", - p.iconLeft && "rounded-l-none", - p.iconRight && "rounded-r-none", p.error && "border-danger focus-visible:ring-danger/40", )} @@ -217,8 +215,6 @@ const controlWrap = "relative"; "block w-full border border-gray-200 shadow-sm rounded-lg text-sm focus:z-10 focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 file:bg-gray-50 file:border-0 file:me-4 file:py-3 file:px-4 dark:file:bg-neutral-700 dark:file:text-neutral-400", p.prefixText && "rounded-l-none", p.suffixText && "rounded-r-none", - p.iconLeft && "rounded-l-none", - p.iconRight && "rounded-r-none", p.error && "border-danger focus-visible:ring-danger/40", )} @@ -281,8 +277,6 @@ const controlWrap = "relative"; : p.size === "lg" ? "text-base p-4" : "text-sm p-3", - p.iconLeft && "rounded-l-none", - p.iconRight && "rounded-r-none", p.error && "border-danger focus-visible:ring-danger/40", )} data-initial={typeof p.value === "string" ? p.value : ""} @@ -309,9 +303,7 @@ const controlWrap = "relative"; } class={cn( selectBase({ size: p.size ?? "md" }), - "appearance-none bg-[length:16px_16px] pr-9", - p.iconLeft && "rounded-l-none", - p.iconRight && "rounded-r-none", + "appearance-none bg-none pr-9", p.error && "border-danger focus-visible:ring-danger/40", )} > diff --git a/src/components/MultiSelect.astro b/src/components/MultiSelect.astro index 330edae..d409e85 100644 --- a/src/components/MultiSelect.astro +++ b/src/components/MultiSelect.astro @@ -18,12 +18,13 @@ interface Props extends HTMLAttributes<"div"> { required?: boolean; disabled?: boolean; options: Option[]; - value?: string[]; // initial selected values - placeholder?: string; // shown when empty - searchable?: boolean; // show search box in dropdown - clearable?: boolean; // show clear (x) button - maxSelections?: number; // optional selection cap + value?: string[]; + placeholder?: string; + searchable?: boolean; + clearable?: boolean; + maxSelections?: number; size?: "sm" | "md" | "lg"; + display?: "text" | "chips"; class?: string; } @@ -32,8 +33,9 @@ const p = Astro.props as Props; const fieldId = p.id ?? p.name; const descId = p.description ? `${fieldId}-desc` : undefined; const errId = `${fieldId}-err`; + const selected = new Set(p.value ?? []); -const summary = selected.size +const summarySSR = selected.size ? Array.from(selected) .map((v) => p.options.find((o) => o.value === v)?.label ?? v) .join(", ") @@ -47,11 +49,15 @@ const sizeCls = : "h-10 px-3 text-sm"; const controlCls = - "py-2.5 sm:py-3 px-4 block w-full border-gray-200 rounded-lg sm:text-sm focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600"; + "py-2.5 sm:py-3 px-4 block w-full border-gray-200 rounded-lg sm:text-sm " + + "focus:border-blue-500 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none " + + "dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder-neutral-500 dark:focus:ring-neutral-600"; const chipBase = "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs bg-muted text-foreground"; + const containerId = `ms-${Math.random().toString(36).slice(2)}`; +const displayMode = p.display ?? "text"; ---