Updated Complete Tailwindcss Classes

This commit is contained in:
2025-08-16 20:48:43 +05:30
parent e1c669ef5d
commit dd8a0cc9dc
234 changed files with 225 additions and 1181960 deletions
+56 -55
View File
@@ -35,100 +35,101 @@ const {
}: Props = Astro.props;
const base =
"inline-flex items-center justify-center gap-2 font-medium transition-colors " +
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 " +
"disabled:opacity-50 disabled:pointer-events-none";
"wr:inline-flex wr:items-center wr:justify-center wr:gap-2 wr:font-medium wr:transition-colors " +
"wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-offset-2 " +
"wr:disabled:opacity-50 wr:disabled:pointer-events-none";
const sizeClass: Record<Size, string> = {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-11 px-5 text-base",
icon: "h-10 w-10 p-0",
sm: "wr:h-8 wr:px-3 wr:text-sm",
md: "wr:h-10 wr:px-4 wr:text-sm",
lg: "wr:h-11 wr:px-5 wr:text-base",
icon: "wr:h-10 wr:w-10 wr:p-0",
};
const radius = pill ? "rounded-full" : "rounded-md";
const radius = pill ? "wr:rounded-full" : "wr:rounded-md";
const styles: Record<Color, Record<Variant, string>> = {
primary: {
solid: "bg-primary text-primary-foreground hover:bg-primary/90 border border-transparent focus-visible:ring-primary/40",
solid: "wr:bg-primary wr:text-primary-foreground wr:hover:bg-primary/90 wr:border wr:border-transparent wr:focus-visible:ring-primary/40",
outline:
"border border-primary text-primary hover:bg-primary/10 focus-visible:ring-primary/40",
ghost: "text-primary hover:bg-primary/10 border border-transparent focus-visible:ring-primary/30",
soft: "bg-primary/15 text-primary hover:bg-primary/20 border border-transparent focus-visible:ring-primary/30",
link: "text-primary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-primary/40",
"wr:border wr:border-primary wr:text-primary wr:hover:bg-primary/10 wr:focus-visible:ring-primary/40",
ghost: "wr:text-primary wr:hover:bg-primary/10 wr:border wr:border-transparent wr:focus-visible:ring-primary/30",
soft: "wr:bg-primary/15 wr:text-primary wr:hover:bg-primary/20 wr:border wr:border-transparent wr:focus-visible:ring-primary/30",
link: "wr:text-primary wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-primary/40",
},
secondary: {
solid: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border border-transparent focus-visible:ring-secondary/40",
solid: "wr:bg-secondary wr:text-secondary-foreground wr:hover:bg-secondary/90 wr:border wr:border-transparent wr:focus-visible:ring-secondary/40",
outline:
"border border-secondary text-secondary hover:bg-secondary/10 focus-visible:ring-secondary/40",
ghost: "text-secondary hover:bg-secondary/10 border border-transparent focus-visible:ring-secondary/30",
soft: "bg-secondary/15 text-secondary hover:bg-secondary/20 border border-transparent focus-visible:ring-secondary/30",
link: "text-secondary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-secondary/40",
"wr:border wr:border-secondary wr:text-secondary wr:hover:bg-secondary/10 wr:focus-visible:ring-secondary/40",
ghost: "wr:text-secondary wr:hover:bg-secondary/10 wr:border wr:border-transparent wr:focus-visible:ring-secondary/30",
soft: "wr:bg-secondary/15 wr:text-secondary wr:hover:bg-secondary/20 wr:border wr:border-transparent wr:focus-visible:ring-secondary/30",
link: "wr:text-secondary wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-secondary/40",
},
tertiary: {
solid: "bg-tertiary text-tertiary-foreground hover:bg-tertiary/90 border border-transparent focus-visible:ring-tertiary/40",
solid: "wr:bg-tertiary wr:text-tertiary-foreground wr:hover:bg-tertiary/90 wr:border wr:border-transparent wr:focus-visible:ring-tertiary/40",
outline:
"border border-tertiary text-tertiary hover:bg-tertiary/10 focus-visible:ring-tertiary/40",
ghost: "text-tertiary hover:bg-tertiary/10 border border-transparent focus-visible:ring-tertiary/30",
soft: "bg-tertiary/15 text-tertiary hover:bg-tertiary/20 border border-transparent focus-visible:ring-tertiary/30",
link: "text-tertiary underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-tertiary/40",
"wr:border wr:border-tertiary wr:text-tertiary wr:hover:bg-tertiary/10 wr:focus-visible:ring-tertiary/40",
ghost: "wr:text-tertiary wr:hover:bg-tertiary/10 wr:border wr:border-transparent wr:focus-visible:ring-tertiary/30",
soft: "wr:bg-tertiary/15 wr:text-tertiary wr:hover:bg-tertiary/20 wr:border wr:border-transparent wr:focus-visible:ring-tertiary/30",
link: "wr:text-tertiary wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-tertiary/40",
},
accent: {
solid: "bg-accent text-accent-foreground hover:bg-accent/90 border border-transparent focus-visible:ring-accent/40",
solid: "wr:bg-accent wr:text-accent-foreground wr:hover:bg-accent/90 wr:border wr:border-transparent wr:focus-visible:ring-accent/40",
outline:
"border border-accent text-accent hover:bg-accent/10 focus-visible:ring-accent/40",
ghost: "text-accent hover:bg-accent/10 border border-transparent focus-visible:ring-accent/30",
soft: "bg-accent/15 text-accent hover:bg-accent/20 border border-transparent focus-visible:ring-accent/30",
link: "text-accent underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-accent/40",
"wr:border wr:border-accent wr:text-accent wr:hover:bg-accent/10 wr:focus-visible:ring-accent/40",
ghost: "wr:text-accent wr:hover:bg-accent/10 wr:border wr:border-transparent wr:focus-visible:ring-accent/30",
soft: "wr:bg-accent/15 wr:text-accent wr:hover:bg-accent/20 wr:border wr:border-transparent wr:focus-visible:ring-accent/30",
link: "wr:text-accent wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-accent/40",
},
info: {
solid: "bg-info text-info-foreground hover:bg-info/90 border border-transparent focus-visible:ring-info/40",
solid: "wr:bg-info wr:text-info-foreground wr:hover:bg-info/90 wr:border wr:border-transparent wr:focus-visible:ring-info/40",
outline:
"border border-info text-info hover:bg-info/10 focus-visible:ring-info/40",
ghost: "text-info hover:bg-info/10 border border-transparent focus-visible:ring-info/30",
soft: "bg-info/15 text-info hover:bg-info/20 border border-transparent focus-visible:ring-info/30",
link: "text-info underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-info/40",
"wr:border wr:border-info wr:text-info wr:hover:bg-info/10 wr:focus-visible:ring-info/40",
ghost: "wr:text-info wr:hover:bg-info/10 wr:border wr:border-transparent wr:focus-visible:ring-info/30",
soft: "wr:bg-info/15 wr:text-info wr:hover:bg-info/20 wr:border wr:border-transparent wr:focus-visible:ring-info/30",
link: "wr:text-info wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-info/40",
},
success: {
solid: "bg-success text-success-foreground hover:bg-success/90 border border-transparent focus-visible:ring-success/40",
solid: "wr:bg-success wr:text-success-foreground wr:hover:bg-success/90 wr:border wr:border-transparent wr:focus-visible:ring-success/40",
outline:
"border border-success text-success hover:bg-success/10 focus-visible:ring-success/40",
ghost: "text-success hover:bg-success/10 border border-transparent focus-visible:ring-success/30",
soft: "bg-success/15 text-success hover:bg-success/20 border border-transparent focus-visible:ring-success/30",
link: "text-success underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-success/40",
"wr:border wr:border-success wr:text-success wr:hover:bg-success/10 wr:focus-visible:ring-success/40",
ghost: "wr:text-success wr:hover:bg-success/10 wr:border wr:border-transparent wr:focus-visible:ring-success/30",
soft: "wr:bg-success/15 wr:text-success wr:hover:bg-success/20 wr:border wr:border-transparent wr:focus-visible:ring-success/30",
link: "wr:text-success wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-success/40",
},
warning: {
solid: "bg-warning text-warning-foreground hover:bg-warning/90 border border-transparent focus-visible:ring-warning/40",
solid: "wr:bg-warning wr:text-warning-foreground wr:hover:bg-warning/90 wr:border wr:border-transparent wr:focus-visible:ring-warning/40",
outline:
"border border-warning text-warning hover:bg-warning/10 focus-visible:ring-warning/40",
ghost: "text-warning hover:bg-warning/10 border border-transparent focus-visible:ring-warning/30",
soft: "bg-warning/14 text-warning hover:bg-warning/20 border border-transparent focus-visible:ring-warning/30",
link: "text-warning underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-warning/40",
"wr:border wr:border-warning wr:text-warning wr:hover:bg-warning/10 wr:focus-visible:ring-warning/40",
ghost: "wr:text-warning wr:hover:bg-warning/10 wr:border wr:border-transparent wr:focus-visible:ring-warning/30",
soft: "wr:bg-warning/14 wr:text-warning wr:hover:bg-warning/20 wr:border wr:border-transparent wr:focus-visible:ring-warning/30",
link: "wr:text-warning wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-warning/40",
},
danger: {
solid: "bg-danger text-danger-foreground hover:bg-danger/90 border border-transparent focus-visible:ring-danger/40",
solid: "wr:bg-danger wr:text-danger-foreground wr:hover:bg-danger/90 wr:border wr:border-transparent wr:focus-visible:ring-danger/40",
outline:
"border border-danger text-danger hover:bg-danger/10 focus-visible:ring-danger/40",
ghost: "text-danger hover:bg-danger/10 border border-transparent focus-visible:ring-danger/30",
soft: "bg-danger/15 text-danger hover:bg-danger/20 border border-transparent focus-visible:ring-danger/30",
link: "text-danger underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-danger/40",
"wr:border wr:border-danger wr:text-danger wr:hover:bg-danger/10 wr:focus-visible:ring-danger/40",
ghost: "wr:text-danger wr:hover:bg-danger/10 wr:border wr:border-transparent wr:focus-visible:ring-danger/30",
soft: "wr:bg-danger/15 wr:text-danger wr:hover:bg-danger/20 wr:border wr:border-transparent wr:focus-visible:ring-danger/30",
link: "wr:text-danger wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-danger/40",
},
neutral: {
solid: "bg-neutral text-neutral-foreground hover:bg-neutral/90 border border-transparent focus-visible:ring-neutral/40",
solid: "wr:bg-neutral wr:text-neutral-foreground wr:hover:bg-neutral/90 wr:border wr:border-transparent wr:focus-visible:ring-neutral/40",
outline:
"border border-neutral text-neutral hover:bg-neutral/10 focus-visible:ring-neutral/40",
ghost: "text-neutral hover:bg-neutral/10 border border-transparent focus-visible:ring-neutral/30",
soft: "bg-neutral/15 text-neutral hover:bg-neutral/20 border border-transparent focus-visible:ring-neutral/30",
link: "text-neutral underline underline-offset-4 p-0 h-auto border-0 focus-visible:ring-neutral/40",
"wr:border wr:border-neutral wr:text-neutral wr:hover:bg-neutral/10 wr:focus-visible:ring-neutral/40",
ghost: "wr:text-neutral wr:hover:bg-neutral/10 wr:border wr:border-transparent wr:focus-visible:ring-neutral/30",
soft: "wr:bg-neutral/15 wr:text-neutral wr:hover:bg-neutral/20 wr:border wr:border-transparent wr:focus-visible:ring-neutral/30",
link: "wr:text-neutral wr:underline wr:underline-offset-4 wr:p-0 wr:h-auto wr:border-0 wr:focus-visible:ring-neutral/40",
},
};
const variantColor = styles[color][variant];
const sizeKls = variant === "link" ? "" : sizeClass[size];
const iconSize = size === "sm" ? "size-4" : size === "lg" ? "size-5" : "size-5";
const iconSize =
size === "sm" ? "wr:size-4" : size === "lg" ? "wr:size-5" : "wr:size-5";
---
<button class={cn(base, radius, sizeKls, variantColor, className)} {...rest}>
{icon && <Icon name={icon} class={cn(iconSize, "shrink-0")} />}
{icon && <Icon name={icon} class={cn(iconSize, "wr:shrink-0")} />}
<slot />
</button>
+76 -63
View File
@@ -53,47 +53,48 @@ const descId = p.description ? `${fieldId}-desc` : undefined;
const errId = `${fieldId}-err`;
const inputBase = cva(
"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",
"wr:py-2.5 wr:sm:py-3 wr:px-4 wr:block wr:w-full wr:border-gray-200 wr:rounded-lg wr:sm:text-sm wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:dark:placeholder-neutral-500 wr:dark:focus:ring-neutral-600",
{
variants: {
size: {
sm: "h-10 px-3 text-sm",
md: "h-11 px-3 text-sm",
lg: "h-12 px-4 text-base",
sm: "wr:h-10 wr:px-3 wr:text-sm",
md: "wr:h-11 wr:px-3 wr:text-sm",
lg: "wr:h-12 wr:px-4 wr:text-base",
},
withIconLeft: { true: "pl-10" },
withIconRight: { true: "pr-10" },
withIconLeft: { true: "wr:pl-10" },
withIconRight: { true: "wr:pr-10" },
},
},
);
const selectBase = cva(
"py-3 px-4 pe-9 block w-full border-gray-200 rounded-lg 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",
"wr:py-3 wr:px-4 wr:pe-9 wr:block wr:w-full wr:border-gray-200 wr:rounded-lg wr:text-sm wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:dark:placeholder-neutral-500 wr:dark:focus:ring-neutral-600",
{
variants: {
size: {
sm: "h-10 px-3 text-sm",
md: "h-11 px-3 text-sm",
lg: "h-12 px-4 text-base",
sm: "wr:h-10 wr:px-3 wr:text-sm",
md: "wr:h-11 wr:px-3 wr:text-sm",
lg: "wr:h-12 wr:px-4 wr:text-base",
},
withIconLeft: { true: "pl-10" },
withIconRight: { true: "pr-10" },
withIconLeft: { true: "wr:pl-10" },
withIconRight: { true: "wr:pr-10" },
},
},
);
const iconSize =
p.size === "sm" ? "size-4" : p.size === "lg" ? "size-5" : "size-5";
const labelCls = "block text-sm font-medium mb-2 dark:text-white";
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";
p.size === "sm" ? "wr:size-4" : p.size === "lg" ? "wr:size-5" : "wr:size-5";
const labelCls =
"wr:block wr:text-sm wr:font-medium wr:mb-2 wr:dark:text-white";
const helpCls = "wr:mt-2 wr:text-sm wr:text-gray-500 wr:dark:text-neutral-500";
const errCls = "wr:text-sm wr:text-red-600 wr:mt-2";
const groupCls = "wr:relative wr:flex wr:items-stretch";
const addonBase =
"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";
"wr:px-4 wr:inline-flex wr:items-center wr:min-w-fit wr:rounded-md wr:border wr:border-gray-200 wr:bg-gray-50 wr:text-sm wr:text-gray-500 wr:dark:bg-neutral-700 wr:dark:border-neutral-700 wr:dark:text-neutral-400";
const controlWrap = "wr:relative";
---
<div class={cn("space-y-1.5", p.class)} data-field={p.name}>
<div class={cn("wr:space-y-1.5", p.class)} data-field={p.name}>
{
p.label &&
[
@@ -108,7 +109,8 @@ const controlWrap = "relative";
"select",
].includes(p.kind ?? "text") && (
<label for={fieldId} class={labelCls}>
{p.label} {p.required && <span class="text-danger">*</span>}
{p.label}{" "}
{p.required && <span class="wr:text-danger">*</span>}
</label>
)
}
@@ -119,14 +121,16 @@ const controlWrap = "relative";
) && (
<div class={groupCls}>
{p.prefixText && (
<span class={cn(addonBase, "border-r-0 rounded-r-none")}>
<span
class={cn(addonBase, "wr:border-r-0 wr:rounded-r-none")}
>
{p.prefixText}
</span>
)}
<div class={cn(controlWrap, "flex-1")}>
<div class={cn(controlWrap, "wr:flex-1")}>
{p.iconLeft && (
<span class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-muted-foreground">
<span class="wr:absolute wr:inset-y-0 wr:left-0 wr:flex wr:items-center wr:pl-3 wr:pointer-events-none wr:text-muted-foreground">
<Icon name={p.iconLeft} class={iconSize} />
</span>
)}
@@ -142,10 +146,10 @@ const controlWrap = "relative";
withIconRight:
!!p.iconRight || p.kind === "password",
}),
p.prefixText && "rounded-l-none",
p.suffixText && "rounded-r-none",
p.prefixText && "wr:rounded-l-none",
p.suffixText && "wr:rounded-r-none",
p.error &&
"border-danger focus-visible:ring-danger/40",
"wr:border-danger wr:focus-visible:ring-danger/40",
)}
data-control
placeholder={p.placeholder}
@@ -168,7 +172,7 @@ const controlWrap = "relative";
{p.kind === "password" && (
<button
type="button"
class="absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground hover:text-foreground"
class="wr:absolute wr:inset-y-0 wr:right-0 wr:pr-3 wr:flex wr:items-center wr:text-muted-foreground hover:wr:text-foreground"
onclick={`const i = this.previousElementSibling; i.type = i.type === 'password' ? 'text' : 'password';`}
>
<Icon name="mdi:eye" class={iconSize} />
@@ -176,14 +180,16 @@ const controlWrap = "relative";
)}
{p.iconRight && (
<span class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground">
<span class="wr:absolute wr:inset-y-0 wr:right-0 wr:pr-3 wr:flex wr:items-center wr:pointer-events-none wr:text-muted-foreground">
<Icon name={p.iconRight} class={iconSize} />
</span>
)}
</div>
{p.suffixText && (
<span class={cn(addonBase, "border-l-0 rounded-l-none")}>
<span
class={cn(addonBase, "wr:border-l-0 wr:rounded-l-none")}
>
{p.suffixText}
</span>
)}
@@ -195,14 +201,16 @@ const controlWrap = "relative";
p.kind === "file" && (
<div class={groupCls}>
{p.prefixText && (
<span class={cn(addonBase, "border-r-0 rounded-r-none")}>
<span
class={cn(addonBase, "wr:border-r-0 wr:rounded-r-none")}
>
{p.prefixText}
</span>
)}
<div class={cn(controlWrap, "flex-1")}>
<div class={cn(controlWrap, "wr:flex-1")}>
{p.iconLeft && (
<span class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-muted-foreground">
<span class="wr:absolute wr:inset-y-0 wr:left-0 wr:flex wr:items-center wr:pl-3 wr:pointer-events-none wr:text-muted-foreground">
<Icon name={p.iconLeft} class={iconSize} />
</span>
)}
@@ -212,11 +220,11 @@ const controlWrap = "relative";
name={p.name}
type={(p.kind as any) ?? "text"}
class={cn(
"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",
"wr:block wr:w-full wr:border wr:border-gray-200 wr:shadow-sm wr:rounded-lg wr:text-sm wr:focus:z-10 wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:file:bg-gray-50 wr:file:border-0 wr:file:me-4 wr:file:py-3 wr:file:px-4 wr:dark:file:bg-neutral-700 wr:dark:file:text-neutral-400",
p.prefixText && "wr:rounded-l-none",
p.suffixText && "wr:rounded-r-none",
p.error &&
"border-danger focus-visible:ring-danger/40",
"wr:border-danger wr:focus-visible:ring-danger/40",
)}
data-control
multiple
@@ -238,14 +246,16 @@ const controlWrap = "relative";
/>
{p.iconRight && (
<span class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-muted-foreground">
<span class="wr:absolute wr:inset-y-0 wr:right-0 wr:pr-3 wr:flex wr:items-center wr:pointer-events-none wr:text-muted-foreground">
<Icon name={p.iconRight} class={iconSize} />
</span>
)}
</div>
{p.suffixText && (
<span class={cn(addonBase, "border-l-0 rounded-l-none")}>
<span
class={cn(addonBase, "wr:border-l-0 wr:rounded-l-none")}
>
{p.suffixText}
</span>
)}
@@ -271,13 +281,14 @@ const controlWrap = "relative";
.join(" ") || undefined
}
class={cn(
"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",
"wr:py-2.5 wr:sm:py-3 wr:px-4 wr:block wr:w-full wr:border-gray-200 wr:rounded-lg wr:sm:text-sm wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:dark:placeholder-neutral-500 wr:dark:focus:ring-neutral-600",
p.size === "sm"
? "text-sm p-3"
? "wr:text-sm wr:p-3"
: p.size === "lg"
? "text-base p-4"
: "text-sm p-3",
p.error && "border-danger focus-visible:ring-danger/40",
? "wr:text-base wr:p-4"
: "wr:text-sm wr:p-3",
p.error &&
"wr:border-danger wr:focus-visible:ring-danger/40",
)}
data-initial={typeof p.value === "string" ? p.value : ""}
/>
@@ -303,8 +314,9 @@ const controlWrap = "relative";
}
class={cn(
selectBase({ size: p.size ?? "md" }),
"appearance-none bg-none pr-9",
p.error && "border-danger focus-visible:ring-danger/40",
"wr:appearance-none wr:bg-none wr:pr-9",
p.error &&
"wr:border-danger wr:focus-visible:ring-danger/40",
)}
>
{!p.multiple && p.placeholder && (
@@ -326,7 +338,7 @@ const controlWrap = "relative";
</option>
))}
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 pr-3 flex items-center text-muted-foreground">
<span class="wr:pointer-events-none wr:absolute wr:inset-y-0 wr:right-0 wr:pr-3 wr:flex wr:items-center wr:text-muted-foreground">
<Icon name="mdi:chevron-down" class={iconSize} />
</span>
</div>
@@ -335,12 +347,12 @@ const controlWrap = "relative";
{
p.kind === "checkbox" && (
<label class="flex items-center gap-3">
<label class="wr:flex wr:items-center wr:gap-3">
<input
id={fieldId}
name={p.name}
type="checkbox"
class="shrink-0 mt-0.5 border-gray-200 rounded-sm text-blue-600 focus:ring-blue-500 checked:border-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
class="wr:shrink-0 wr:mt-0.5 wr:border-gray-200 wr:rounded-sm wr:text-blue-600 wr:focus:ring-blue-500 wr:checked:border-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:checked:bg-blue-500 wr:dark:checked:border-blue-500 wr:dark:focus:ring-offset-gray-800"
checked={!!p.value}
disabled={p.disabled}
data-control
@@ -353,7 +365,9 @@ const controlWrap = "relative";
/>
<div>
{p.label && (
<span class="text-sm text-foreground">{p.label}</span>
<span class="wr:text-sm wr:text-foreground">
{p.label}
</span>
)}
{p.description && (
<p id={descId} class={helpCls}>
@@ -367,25 +381,25 @@ const controlWrap = "relative";
{
p.kind === "radio" && (
<fieldset class="space-y-2">
<fieldset class="wr:space-y-2">
{p.label && (
<legend class="text-sm font-medium text-foreground">
<legend class="wr:text-sm wr:font-medium wr:text-foreground">
{p.label}
</legend>
)}
<div class="space-x-3">
<div class="wr:space-x-3">
{(p.options ?? []).map((opt) => (
<label class="inline-flex items-center gap-2">
<label class="wr:inline-flex wr:items-center wr:gap-2">
<input
type="radio"
name={p.name}
value={opt.value}
checked={String(p.value) === opt.value}
class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 checked:border-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800"
class="wr:shrink-0 wr:mt-0.5 wr:border-gray-200 wr:rounded-full wr:text-blue-600 wr:focus:ring-blue-500 wr:checked:border-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:checked:bg-blue-500 wr:dark:checked:border-blue-500 wr:dark:focus:ring-offset-gray-800"
disabled={opt.disabled || p.disabled}
data-control
/>
<span class="text-sm text-foreground">
<span class="wr:text-sm wr:text-foreground">
{opt.label}
</span>
</label>
@@ -397,24 +411,24 @@ const controlWrap = "relative";
{
p.kind === "switch" && (
<div class="flex items-center gap-3">
<label class="relative inline-block w-11 h-6 cursor-pointer">
<div class="wr:flex wr:items-center wr:gap-3">
<label class="wr:relative wr:inline-block wr:w-11 wr:h-6 wr:cursor-pointer">
<input
id={fieldId}
name={p.name}
type="checkbox"
class="sr-only peer"
class="wr:sr-only wr:peer"
checked={!!p.value}
disabled={p.disabled}
data-control
/>
<span class="absolute inset-0 bg-gray-200 rounded-full transition-colors duration-200 ease-in-out peer-checked:bg-blue-600 dark:bg-neutral-700 dark:peer-checked:bg-blue-500 peer-disabled:opacity-50 peer-disabled:pointer-events-none" />
<span class="absolute top-1/2 start-0.5 -translate-y-1/2 size-5 bg-white rounded-full shadow-xs transition-transform duration-200 ease-in-out peer-checked:translate-x-full dark:bg-neutral-400 dark:peer-checked:bg-white" />
<span class="wr:absolute wr:inset-0 wr:bg-gray-200 wr:rounded-full wr:transition-colors wr:duration-200 wr:ease-in-out wr:peer-checked:bg-blue-600 wr:dark:bg-neutral-700 wr:dark:peer-checked:bg-blue-500 wr:peer-disabled:opacity-50 wr:peer-disabled:pointer-events-none" />
<span class="wr:absolute wr:top-1/2 wr:start-0.5 wr:-translate-y-1/2 wr:size-5 wr:bg-white wr:rounded-full wr:shadow-xs wr:transition-transform wr:duration-200 wr:ease-in-out wr:peer-checked:translate-x-full wr:dark:bg-neutral-400 wr:dark:peer-checked:bg-white" />
</label>
{p.label && (
<label
for={fieldId}
class="text-sm text-gray-500 dark:text-neutral-400"
class="wr:text-sm wr:text-gray-500 wr:dark:text-neutral-400"
>
{p.label}
</label>
@@ -431,6 +445,5 @@ const controlWrap = "relative";
)
}
<!-- Always-present error container (client fills it) -->
<p id={errId} class={errCls} hidden>{p.error ?? ""}</p>
</div>
+20 -11
View File
@@ -34,20 +34,22 @@ const {
}: Props = Astro.props;
const gridCls =
columns === 2 ? "grid grid-cols-1 sm:grid-cols-2 gap-4" : "space-y-4";
columns === 2
? "wr:grid wr:grid-cols-1 wr:sm:grid-cols-2 wr:gap-4"
: "wr:space-y-4";
const actionsJustify =
actionsAlign === "start"
? "justify-start"
? "wr:justify-start"
: actionsAlign === "center"
? "justify-center"
: "justify-end";
? "wr:justify-center"
: "wr:justify-end";
const formId = `f-${Math.random().toString(36).slice(2)}`;
---
<form
id={formId}
class={cn("space-y-4", className)}
class={cn("wr:space-y-4", className)}
{...rest}
data-schema={schemaName ?? ""}
data-onsubmit={onSubmit ?? ""}
@@ -58,12 +60,14 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
<div>
{
title && (
<h2 class="text-lg font-semibold text-foreground">{title}</h2>
<h2 class="wr:text-lg wr:font-semibold wr:text-foreground">
{title}
</h2>
)
}
{
description && (
<p class="text-sm text-muted-foreground mt-1 text-gray-600 dark:text-neutral-400">
<p class="wr:text-sm wr:text-muted-foreground wr:mt-1 wr:text-gray-600 wr:dark:text-neutral-400">
{description}
</p>
)
@@ -74,7 +78,7 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
<slot />
</div>
<div class={cn("flex gap-3", actionsJustify)}>
<div class={cn("wr:flex wr:gap-3", actionsJustify)}>
<Button type="submit" variant="solid" color="primary" icon={submitIcon}>
{submitLabel}
</Button>
@@ -82,11 +86,16 @@ const formId = `f-${Math.random().toString(36).slice(2)}`;
</div>
</form>
<!-- Client validation & submit hook (no zod import here) -->
<script type="module" is:inline define:vars={{ formId, showSuccesInput }}>
const start = () => {
const INVALID_KLS = ["!border-danger", "focus-visible:ring-danger/40"];
const VALID_KLS = ["!border-success", "focus-visible:ring-success/40"];
const INVALID_KLS = [
"wr:!border-danger",
"wr:focus-visible:ring-danger/40",
];
const VALID_KLS = [
"wr:!border-success",
"wr:focus-visible:ring-success/40",
];
const form = document.getElementById(formId);
if (!form || form.dataset.__afInit) return;
-39
View File
@@ -1,39 +0,0 @@
---
import type { HTMLAttributes } from "astro/types";
import { cn } from "../utils/cn";
import { cva, type VariantProps } from "class-variance-authority";
import { getDynamicIcon } from "../utils/icon";
const iconClass = cva("text-current", {
variants: {
size: {
sm: "size-4",
md: "size-6",
lg: "size-8",
xl: "size-14",
},
},
defaultVariants: {
size: "md",
},
});
interface Props extends HTMLAttributes<"svg">, VariantProps<typeof iconClass> {
size?: "sm" | "md" | "lg" | "xl";
name: string;
}
const { size = "md", name, class: className, ...rest }: Props = Astro.props;
const svg = await getDynamicIcon(name);
---
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class={cn(iconClass({ size, className }))}
{...rest}
>
<Fragment set:html={svg} />
</svg>
+50 -45
View File
@@ -43,36 +43,36 @@ const summarySSR = selected.size
const sizeCls =
p.size === "sm"
? "h-9 px-3 text-sm"
? "wr:h-9 wr:px-3 wr:text-sm"
: p.size === "lg"
? "h-11 px-4 text-base"
: "h-10 px-3 text-sm";
? "wr:h-11 wr:px-4 wr:text-base"
: "wr:h-10 wr:px-3 wr: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";
"wr:py-2.5 wr:sm:py-3 wr:px-4 wr:block wr:w-full wr:border-gray-200 wr:rounded-lg wr:sm:text-sm " +
"wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none " +
"wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:dark:placeholder-neutral-500 wr: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";
"wr:inline-flex wr:items-center wr:gap-1 wr:rounded-full wr:px-2 wr:py-0.5 wr:text-xs wr:bg-muted wr:text-foreground";
const containerId = `ms-${Math.random().toString(36).slice(2)}`;
const displayMode = p.display ?? "text";
---
<div class={cn("space-y-1.5", p.class)} data-field={p.name} id={containerId}>
<div class={cn("wr:space-y-1.5", p.class)} data-field={p.name} id={containerId}>
{
p.label && (
<label
for={fieldId}
class="mb-1.5 block text-sm font-medium text-foreground"
class="wr:mb-1.5 wr:block wr:text-sm wr:font-medium wr:text-foreground"
>
{p.label} {p.required && <span class="text-danger">*</span>}
{p.label} {p.required && <span class="wr:text-danger">*</span>}
</label>
)
}
<div class="relative">
<div class="wr:relative">
<input
id={fieldId}
type="text"
@@ -85,8 +85,8 @@ const displayMode = p.display ?? "text";
class={cn(
controlCls,
sizeCls,
"pr-10 cursor-pointer",
displayMode === "chips" && "pl-3",
"wr:pr-10 wr:cursor-pointer",
displayMode === "chips" && "wr:pl-3",
)}
placeholder={p.placeholder ?? "Select..."}
value={displayMode === "text" ? summarySSR : ""}
@@ -95,58 +95,60 @@ const displayMode = p.display ?? "text";
{...p.disabled ? { disabled: true } : {}}
/>
<div class="absolute inset-y-0 right-0 flex items-center gap-1 pr-2">
<div
class="wr:absolute wr:inset-y-0 wr:right-0 wr:flex wr:items-center wr:gap-1 wr:pr-2"
>
{
p.clearable !== false && !p.disabled && (
<button
type="button"
class={cn(
"p-1 rounded hover:bg-muted",
selected.size === 0 && "hidden",
"wr:p-1 wr:rounded wr:hover:bg-muted",
selected.size === 0 && "wr:hidden",
)}
data-ms-clear
aria-label="Clear selection"
>
<Icon name="mdi:close" class="size-4" />
<Icon name="mdi:close" class="wr:size-4" />
</button>
)
}
<button
type="button"
class="p-1 rounded hover:bg-muted"
class="wr:p-1 wr:rounded wr:hover:bg-muted"
data-ms-toggle
aria-label="Toggle menu"
>
<Icon name="mdi:chevron-down" class="size-5" />
<Icon name="mdi:chevron-down" class="wr:size-5" />
</button>
</div>
<div
data-ms-chips
class={cn(
"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 flex flex-wrap gap-1 pr-14",
displayMode === "chips" ? "" : "hidden",
"wr:pointer-events-none wr:absolute wr:left-3 wr:top-1/2 wr:-translate-y-1/2 wr:flex wr:flex-wrap wr:gap-1 wr:pr-14",
displayMode === "chips" ? "" : "wr:hidden",
)}
>
</div>
<div
class="absolute border-border bg-popover text-popover-foreground shadow-lg hidden mt-2 z-50 w-full max-h-72 p-1 space-y-0.5 bg-white border border-gray-200 rounded-lg overflow-hidden overflow-y-auto [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-track]:bg-gray-100 [&::-webkit-scrollbar-thumb]:bg-gray-300 dark:[&::-webkit-scrollbar-track]:bg-neutral-700 dark:[&::-webkit-scrollbar-thumb]:bg-neutral-500 dark:bg-neutral-900 dark:border-neutral-700"
class="wr:absolute wr:border-border wr:bg-popover wr:text-popover-foreground wr:shadow-lg wr:hidden wr:mt-2 wr:z-50 wr:w-full wr:max-h-72 wr:p-1 wr:space-y-0.5 wr:bg-white wr:border wr:border-gray-200 wr:rounded-lg wr:overflow-hidden wr:overflow-y-auto wr:[&::-webkit-scrollbar]:w-2 wr:[&::-webkit-scrollbar-thumb]:rounded-full wr:[&::-webkit-scrollbar-track]:bg-gray-100 wr:[&::-webkit-scrollbar-thumb]:bg-gray-300 wr:dark:[&::-webkit-scrollbar-track]:bg-neutral-700 wr:dark:[&::-webkit-scrollbar-thumb]:bg-neutral-500 wr:dark:bg-neutral-900 wr:dark:border-neutral-700"
id={`${containerId}-panel`}
>
{
p.searchable !== false && (
<div class="p-2 border-b border-border">
<div class="relative">
<div class="wr:p-2 wr:border-b wr:border-border">
<div class="wr:relative">
<Icon
name="mdi:magnify"
class="absolute left-2 top-1/2 -translate-y-1/2 size-4 text-muted-foreground"
class="wr:absolute wr:left-2 wr:top-1/2 wr:-translate-y-1/2 wr:size-4 wr:text-muted-foreground"
/>
<input
type="text"
class={cn(
"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",
"h-9 pl-8 pr-2 text-sm",
"wr:py-2.5 wr:sm:py-3 wr:px-4 wr:block wr:w-full wr:border-gray-200 wr:rounded-lg wr:sm:text-sm wr:focus:border-blue-500 wr:focus:ring-blue-500 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-900 wr:dark:border-neutral-700 wr:dark:text-neutral-400 wr:dark:placeholder-neutral-500 wr:dark:focus:ring-neutral-600",
"wr:h-9 wr:pl-8 wr:pr-2 wr:text-sm",
)}
placeholder="Search…"
data-ms-search
@@ -161,15 +163,16 @@ const displayMode = p.display ?? "text";
role="listbox"
aria-multiselectable="true"
id={`${containerId}-listbox`}
class="max-h-56 overflow-auto p-1"
class="wr:max-h-56 wr:overflow-auto wr:p-1"
data-ms-list
>
{
p.options.map((opt) => (
<li
class={cn(
"flex items-center gap-2 px-2 py-2 rounded hover:bg-muted",
opt.disabled && "opacity-60 cursor-not-allowed",
"wr:flex wr:items-center wr:gap-2 wr:px-2 wr:py-2 wr:rounded wr:hover:bg-muted",
opt.disabled &&
"wr:opacity-60 wr:cursor-not-allowed",
)}
data-ms-item
data-label={opt.label.toLowerCase()}
@@ -180,36 +183,36 @@ const displayMode = p.display ?? "text";
value={opt.value}
checked={selected.has(opt.value)}
disabled={p.disabled || opt.disabled}
class="size-4 rounded border-border text-primary accent-primary focus:ring-primary/40"
class="wr:size-4 wr:rounded wr:border-border wr:text-primary wr:accent-primary wr:focus:ring-primary/40"
data-control
/>
<span class="text-sm">{opt.label}</span>
<span class="wr:text-sm">{opt.label}</span>
</li>
))
}
</ul>
<div
class="flex items-center justify-between gap-2 p-2 border-t border-border"
class="wr:flex wr:items-center wr:justify-between wr:gap-2 wr:p-2 wr:border-t wr:border-border"
>
<button
type="button"
class="text-sm text-primary hover:underline disabled:opacity-50"
class="wr:text-sm wr:text-primary wr:hover:underline wr:disabled:opacity-50"
data-ms-select-all
>
Select all
</button>
<div class="flex items-center gap-2">
<div class="wr:flex wr:items-center wr:gap-2">
<button
type="button"
class="text-sm text-muted-foreground hover:underline"
class="wr:text-sm wr:text-muted-foreground wr:hover:underline"
data-ms-cancel
>
Cancel
</button>
<button
type="button"
class="text-sm text-primary hover:underline"
class="wr:text-sm wr:text-primary wr:hover:underline"
data-ms-apply
>
Apply
@@ -223,13 +226,15 @@ const displayMode = p.display ?? "text";
p.description && (
<p
id={descId}
class="mt-2 text-sm text-gray-500 dark:text-neutral-500"
class="wr:mt-2 wr:text-sm wr:text-gray-500 wr:dark:text-neutral-500"
>
{p.description}
</p>
)
}
<p id={errId} class="mt-1 text-xs text-danger" hidden>{p.error ?? ""}</p>
<p id={errId} class="wr:mt-1 wr:text-xs wr:text-danger" hidden>
{p.error ?? ""}
</p>
</div>
<script
@@ -265,21 +270,21 @@ const displayMode = p.display ?? "text";
function setExpanded(v, { focusSearch = false } = {}) {
open = v;
input.setAttribute("aria-expanded", String(open));
panel.classList.toggle("hidden", !open);
panel.classList.toggle("wr:hidden", !open);
if (open && focusSearch) sch?.focus();
}
function makeChip(label) {
const span = document.createElement("span");
span.className =
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs bg-muted text-foreground";
"wr:inline-flex wr:items-center wr:gap-1 wr:rounded-full wr:px-2 wr:py-0.5 wr:text-xs wr:bg-muted wr:text-foreground";
span.textContent = label;
return span;
}
function toggleClear(show) {
if (!btnClr) return;
btnClr.classList.toggle("hidden", !show);
btnClr.classList.toggle("wr:hidden", !show);
}
function updateSummary() {
@@ -301,7 +306,7 @@ const displayMode = p.display ?? "text";
input.placeholder = labels.length ? "" : origPlaceholder;
if (chips) {
chips.innerHTML = "";
chips.classList.add("hidden");
chips.classList.add("wr:hidden");
}
} else {
input.value = "";
@@ -314,7 +319,7 @@ const displayMode = p.display ?? "text";
.forEach((lab) => chips.appendChild(makeChip(lab)));
const extra = labels.length - max;
if (extra > 0) chips.appendChild(makeChip(`+${extra}`));
chips.classList.toggle("hidden", labels.length === 0);
chips.classList.toggle("wr:hidden", labels.length === 0);
}
toggleClear(labels.length > 0);
@@ -376,7 +381,7 @@ const displayMode = p.display ?? "text";
const q = sch.value.trim().toLowerCase();
root.querySelectorAll("[data-ms-item]").forEach((li) => {
const m = (li.getAttribute("data-label") || "").includes(q);
li.classList.toggle("hidden", !m);
li.classList.toggle("wr:hidden", !m);
});
});