This commit is contained in:
2026-03-22 17:03:54 +08:00
parent 8e76dd7a7b
commit 5c6d8c6b92
49 changed files with 203 additions and 1442 deletions

View File

@@ -12,7 +12,7 @@
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"@defgov/ui-headless": "workspace:*"
"@defgov/ui": "workspace:*"
},
"devDependencies": {
"@eslint/js": "^9.39.1",

View File

@@ -1,16 +1,10 @@
import { ThemeProvider } from "@defgov/ui";
import { ButtonGallery } from "./gallery/ButtonGallery";
import { CheckboxGallery } from "./gallery/CheckboxGallery";
import { RadioGallery } from "./gallery/RadioGallery";
import { TooltipGallery } from "./gallery/TootipGallery";
const App = () => {
return (
<ThemeProvider>
<ButtonGallery />
<CheckboxGallery />
<RadioGallery />
<TooltipGallery />
</ThemeProvider>
);
};

View File

@@ -1,14 +0,0 @@
import { Checkbox } from "@defgov/ui";
import { InnerWrap } from "../common/InnerWrap";
import { OuterWrap } from "../common/OuterWrap";
export const CheckboxGallery = () => {
return (
<OuterWrap>
<InnerWrap>
<Checkbox size="xs">Apple</Checkbox>
<Checkbox>Apple</Checkbox>
</InnerWrap>
</OuterWrap>
);
};

View File

@@ -1,22 +0,0 @@
import { Radio, RadioGroup } from "@defgov/ui";
import { InnerWrap } from "../common/InnerWrap";
import { OuterWrap } from "../common/OuterWrap";
export const RadioGallery = () => {
return (
<OuterWrap>
<InnerWrap>
<RadioGroup name="fruit" size="xs">
<Radio value="apple">Apple</Radio>
<Radio value="orange">Orange</Radio>
</RadioGroup>
</InnerWrap>
<InnerWrap>
<RadioGroup name="fruit" direction="vertical" label="Fruit">
<Radio value="apple">Apple</Radio>
<Radio value="orange">Orange</Radio>
</RadioGroup>
</InnerWrap>
</OuterWrap>
);
};

View File

@@ -1,15 +0,0 @@
import { Button, CutSvg, Tooltip } from "@defgov/ui";
import { OuterWrap } from "../common/OuterWrap";
import { InnerWrap } from "../common/InnerWrap";
export const TooltipGallery = () => {
return (
<OuterWrap>
<InnerWrap>
<Tooltip title="cut">
<Button iconSvg={<CutSvg />} iconOnly={true}></Button>
</Tooltip>
</InnerWrap>
</OuterWrap>
);
};

View File

@@ -9,7 +9,7 @@
"devDependencies": {
"turbo": "^2.8.0"
},
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
"engines": {
"node": ">=20"
}

View File

@@ -40,7 +40,7 @@
"vite-plugin-dts": "^4.5.4"
},
"dependencies": {
"@floating-ui/react": "^0.27.18",
"@base-ui/react": "^1.3.0",
"@tailwindcss/vite": "^4.2.1",
"react": "^19.2.4",
"react-dom": "^19.2.4",

View File

@@ -1,8 +1,7 @@
import React from "react";
import { useThemeContext } from "../ThemeProvider/useThemeContext";
import { useThemeContext } from "./ThemeProvider/useThemeContext";
import { cn } from "tailwind-variants";
import type { CommonProps } from "@/common/CommonProps";
import { boxRoot } from "./Box.recipe";
// 别名<约束>=值
// 千万不要 C = As extend React.ElementType这样子连等号会切断推导
@@ -29,7 +28,7 @@ const Box = <C extends React.ElementType = "div">(
throw new Error("Box must be used within a ThemeProvider");
}
const boxRootClass = cn(themeClass, boxRoot(), className);
const boxRootClass = cn(themeClass, className);
return (
<Component ref={ref} className={boxRootClass} {...(rest as any)}>

View File

@@ -0,0 +1,45 @@
"use client";
import * as BUI from "@base-ui/react/button";
import { cn } from "tailwind-variants";
import type { CommonProps } from "@/common/CommonProps";
import { itemRootRecipe } from "@/styles/recipe/ItemRoot.recipe";
import { variantRecipe } from "@/styles/recipe/variant.recipe";
import type { ReactNode } from "react";
import { inlineRootRecipe } from "@/styles/recipe/IinlineRoot.recipe";
type ButtonProps = CommonProps & {
size?: "md" | "lg" | "xl";
variant?: "filled" | "outline" | "subtle";
loading?: boolean;
icon?: ReactNode;
iconOnly?: boolean;
hideIcon?: boolean;
};
export const Button = (props: ButtonProps) => {
const {
className,
children,
size = "md",
variant = "filled",
loading,
disabled,
icon,
iconOnly,
hideIcon,
} = props;
const buttonCls = cn(
itemRootRecipe({ size }),
variantRecipe({ variant, disabled: loading || disabled }),
className,
);
const iconCls = cn(inlineRootRecipe({ size, iconOnly }));
return (
<BUI.Button className={buttonCls} disabled={loading || disabled}>
{children}
</BUI.Button>
);
};

View File

@@ -23,14 +23,14 @@ export * from './assets/svg/VolumeLowSvg.tsx';
export * from './assets/svg/VolumeMediumSvg.tsx';
export * from './assets/svg/VolumeMuteSvg.tsx';
export * from './common/CommonProps.ts';
export * from './lv1-fundamental/Box/Box.tsx';
export * from './common/Box.tsx';
export * from './lv1-fundamental/Slot/Slot.tsx';
export * from './lv1-fundamental/ThemeProvider/ThemeContext.ts';
export * from './lv1-fundamental/ThemeProvider/ThemeProvider.tsx';
export * from './lv1-fundamental/ThemeProvider/useThemeContext.ts';
export * from './common/ThemeProvider/ThemeContext.ts';
export * from './common/ThemeProvider/ThemeProvider.tsx';
export * from './common/ThemeProvider/useThemeContext.ts';
export * from './lv2-sized/Root/RootInline.recipe';
export * from './lv2-sized/Root/RootInline.ts';
export * from './lv2-sized/ItemRoot/ItemRoot.recipe.ts';
export * from './styles/recipe/ItemRoot.recipe.ts';
export * from './lv2-sized/ItemRoot/ItemRoot.tsx';
export * from './lv2-sized/Section/Section.recipe.ts';
export * from './lv2-sized/Section/Section.tsx';

View File

@@ -1,5 +0,0 @@
import { tv } from "tailwind-variants";
export const boxRoot = tv({
base: "flex justify-center items-center",
});

View File

@@ -1,75 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { cn } from "tailwind-variants";
import Box, { type PolymorphicProps } from "@/lv1-fundamental/Box/Box";
import { inlineRootRecipe } from "./IinlineRoot.recipe";
interface ItemInlineRootProps<C extends React.ElementType> extends CommonProps {
as?: C;
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
shape?: "square" | "rounded" | "circle";
variant?: "filled" | "outline" | "subtle";
brand?: "success" | "danger" | "info" | "warning" | "emphasis";
iconOnly?: boolean;
}
const ItemInlineRoot = <C extends React.ElementType = "span">(
props: PolymorphicProps<C, ItemInlineRootProps<C>>,
ref?: React.ComponentPropsWithRef<C>["ref"],
) => {
const {
as = "span",
size = "sm",
shape = "square",
brand,
iconOnly = false,
variant,
onClick,
disabled = false,
className,
children,
...rest
} = props;
const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
if (disabled) {
e.preventDefault();
return;
}
onClick?.(e);
};
const itemInlineRootClass = cn(
inlineRootRecipe({
variant,
size,
shape,
iconOnly,
disabled,
brand,
}),
className,
);
return (
<Box
as={as}
className={itemInlineRootClass}
onClick={handleClick}
disabled={disabled}
ref={ref}
{...(rest as any)}
>
{children}
</Box>
);
};
ItemInlineRoot.displayName = "ItemInlineRoot";
export type ItemInlineRootComponent = <C extends React.ElementType = "span">(
props: PolymorphicProps<C, ItemInlineRootProps<C>> & {
ref?: React.ComponentPropsWithRef<C>["ref"];
},
) => React.ReactElement | null;
export default ItemInlineRoot as ItemInlineRootComponent;

View File

@@ -1,81 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { itemRootRecipe } from "./ItemRoot.recipe";
import { cn } from "tailwind-variants";
import Box, { type PolymorphicProps } from "@/lv1-fundamental/Box/Box";
interface ItemRootProps<C extends React.ElementType> extends CommonProps {
as?: C;
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
shape?: "square" | "rounded" | "circle";
variant?: "filled" | "outline" | "subtle";
brand?: "success" | "danger" | "info" | "warning" | "emphasis";
hasShadow?: boolean;
iconOnly?: boolean;
}
const ItemRoot = <C extends React.ElementType = "div">(
props: PolymorphicProps<C, ItemRootProps<C>>,
ref?: React.ComponentPropsWithRef<C>["ref"],
) => {
const {
as = "div",
size = "sm",
shape = "rounded",
brand: propBrand,
iconOnly = false,
variant,
hasShadow = false,
onClick,
disabled = false,
className,
children,
...rest
} = props;
const brand =
variant == "filled" && propBrand == undefined ? "info" : propBrand;
const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
if (disabled) {
e.preventDefault();
return;
}
onClick?.(e);
};
const itemRootClass = cn(
itemRootRecipe({
variant,
size,
shape,
iconOnly,
disabled,
hasShadow,
brand,
}),
className,
);
return (
<Box
as={as}
className={itemRootClass}
onClick={handleClick}
disabled={disabled}
ref={ref}
{...(rest as any)}
>
{children}
</Box>
);
};
ItemRoot.displayName = "ItemRoot";
export type ItemRootComponent = <C extends React.ElementType = "div">(
props: PolymorphicProps<C, ItemRootProps<C>> & {
ref?: React.ComponentPropsWithRef<C>["ref"];
},
) => React.ReactElement | null;
export default ItemRoot as ItemRootComponent;

View File

@@ -1,12 +0,0 @@
import { tv } from "tailwind-variants";
export const sectionRoot = tv({
base: "",
variants: {
align: {
start: "justify-start",
center: "justify-center",
end: "justify-end",
},
},
});

View File

@@ -1,23 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef } from "react";
import { sectionRoot } from "./Section.recipe";
import Box from "@/lv1-fundamental/Box/Box";
import { cn } from "tailwind-variants";
type SectionProps = CommonProps & {
align?: "start" | "center" | "end";
};
export const Section = forwardRef<HTMLDivElement, SectionProps>(
(props, ref) => {
const { className, align, children, ...rest } = props;
const sectionRootClass = cn(sectionRoot({ align }), className);
return (
<Box className={sectionRootClass} ref={ref} {...rest}>
{children}
</Box>
);
},
);

View File

@@ -1,19 +0,0 @@
import { tv } from "tailwind-variants";
export const iconRoot = tv({
base: "dg-icon",
});
export const iconSvg = tv({
base: "dg-icon-svg",
variants: {
size: {
xs: "dg-icon-svg_size--xs",
sm: "dg-icon-svg_size--sm",
md: "dg-icon-svg_size--md",
lg: "dg-icon-svg_size--lg",
xl: "dg-icon-svg_size--xl",
"2xl": "dg-icon-svg_size--2xl",
},
},
});

View File

@@ -1,25 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef } from "react";
import { iconRoot, iconSvg } from "./Icon.recipe.ts";
import { Slot } from "@/lv1-fundamental/Slot/Slot";
import { cn } from "tailwind-variants";
import InlineRoot from "@/lv2-sized/InlineRoot/InlineRoot.tsx";
type IconProps = CommonProps & {
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
};
export const Icon = forwardRef<HTMLSpanElement, IconProps>((props, ref) => {
const { size, children, className, ...rest } = props;
const iconRootClass = cn(iconRoot(), className);
const iconSvgClass = iconSvg({ size });
return (
<InlineRoot size={size} className={iconRootClass} ref={ref} {...rest}>
<Slot className={iconSvgClass}>{children}</Slot>
</InlineRoot>
);
});
Icon.displayName = "Icon";

View File

@@ -1,5 +0,0 @@
import { tv } from "tailwind-variants";
export const indicatorInput = tv({ base: "pointer-none" });
export const indicatorBoxSvg = tv({ base: "absolute z-10" });
export const indicatorCheckSvg = tv({ base: "absolute z-20" });

View File

@@ -1,92 +0,0 @@
import { forwardRef, type ReactNode } from "react";
import {
// indicatorBoxSvg,
// indicatorCheckSvg,
indicatorInput,
} from "./Indicator.recipe";
import type { CommonProps } from "@/common/CommonProps";
import InlineRoot from "@/lv2-sized/InlineRoot/InlineRoot";
// import { CheckIndicatorOutlineSvg } from "@/assets/svg/CheckIndicatorOutlineSvg";
// import { RadioIndicatorOutlineSvg } from "@/assets/svg/RadioIndicatorOutlineSvg";
// import { RadioIndicatorSvg } from "@/assets/svg/RadioIndicatorSvg";
// import { CheckIndicatorSvg } from "@/assets/svg/CheckIndicatorSvg";
type IndicatorProps = CommonProps & {
size?: "xs" | "sm";
type?: "checkbox" | "radio";
boxSvg?: ReactNode;
checkSvg?: ReactNode;
checked?: boolean;
id?: string;
};
export const Indicator = forwardRef<HTMLInputElement, IndicatorProps>(
(props, ref) => {
const {
size = "sm",
type = "checkbox",
// boxSvg,
// checkSvg,
checked,
id,
disabled,
} = props;
// const currentBoxSvg = boxSvg ? (
// boxSvg
// ) : type == "checkbox" ? (
// <CheckIndicatorOutlineSvg />
// ) : (
// <RadioIndicatorOutlineSvg />
// );
// const currentCheckSvg = checkSvg ? (
// checkSvg
// ) : type == "checkbox" ? (
// <CheckIndicatorSvg />
// ) : (
// <RadioIndicatorSvg />
// );
const indicatorInputClass = indicatorInput();
// const indicatorBoxSvgClass = indicatorBoxSvg();
// const indicatorCheckSvgClass = indicatorCheckSvg();
return (
<InlineRoot size={size} iconOnly={true}>
<InlineRoot
as="input"
id={id}
type={type}
size={size}
ref={ref}
iconOnly={true}
checked={checked} // 必须显式绑定,但还需要阻止默认行为
// 此处仅用于阻止默认行为,不要处理 click 逻辑
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
e.preventDefault(); // 阻止默认行为,防止闪烁
return false;
}}
// 此处仅用于阻止默认行为,不要处理 keydown 逻辑
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
e.preventDefault(); // 阻止默认行为,防止闪烁
return false;
}}
disabled={disabled} // 交互类 disabled 需要手动传入,而不是依靠父元素灰色滤镜
className={indicatorInputClass}
/>
{/* <Icon className={indicatorBoxSvgClass} size={size}>
{currentBoxSvg}
</Icon>
{checked && (
<Icon className={indicatorCheckSvgClass} size={size}>
{currentCheckSvg}
</Icon>
)} */}
</InlineRoot>
);
},
);
Indicator.displayName = "Indicator";

View File

@@ -1,30 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-label {
@include add-specificity(3) {
}
}
.dg-label_align--start {
@include add-specificity(3) {
@include justify-start;
}
}
.dg-label_align--center {
@include add-specificity(3) {
@include justify-center;
}
}
.dg-label_align--end {
@include add-specificity(3) {
@include justify-end;
}
}
.dg-label_is-subtext--true {
@include add-specificity(3) {
@include color-subtext;
}
}
.dg-label_is-subtext--false {
@include add-specificity(3) {
}
}

View File

@@ -1,16 +0,0 @@
import { tv } from "tailwind-variants";
export const labelRoot = tv({
base: "dg-label",
variants: {
align: {
start: "dg-label_align--start",
center: "dg-label_align--center",
end: "dg-label_align--end",
},
isSubText: {
true: "dg-label_is-subtext--true",
false: "dg-label_is-subtext--false",
},
},
});

View File

@@ -1,39 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef } from "react";
import { cn } from "tailwind-variants";
import { labelRoot } from "./Label.style";
import { RootInline } from "@/lv2-sized/Root/RootInline";
type LabelProps = CommonProps & {
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
align?: "start" | "center" | "end";
isSubText?: boolean;
for?: string;
};
export const Label = forwardRef<HTMLLabelElement, LabelProps>((props, ref) => {
const {
size,
isSubText = false,
align,
children,
className,
...rest
} = props;
const labelRootClass = cn(labelRoot({ align, isSubText }), className);
return (
<RootInline
as="label"
size={size}
className={labelRootClass}
ref={ref}
{...rest}
>
{children}
</RootInline>
);
});
Label.displayName = "Label";

View File

@@ -1,7 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-tooltip-dropdown {
@include add-specificity(3) {
border: 1px solid;
}
}

View File

@@ -1,70 +0,0 @@
import { Slot } from "@/lv1-fundamental/Slot/Slot";
import Root from "@/lv2-sized/ItemRoot/ItemRoot";
import type { CommonProps } from "@/common/CommonProps";
import {
autoUpdate,
flip,
FloatingPortal,
offset,
shift,
useDismiss,
useFloating,
useFocus,
useHover,
useInteractions,
useRole,
type Placement,
} from "@floating-ui/react";
import { useState, type ReactNode } from "react";
type TooltipProps = CommonProps & {
title?: ReactNode;
placement?: Placement;
};
export const Tooltip = (props: TooltipProps) => {
const { children, placement = "top", title } = props;
const [open, setOpen] = useState(false);
const { refs, floatingStyles, context } = useFloating({
open: open,
onOpenChange: setOpen,
placement: placement,
middleware: [offset(4), flip(), shift()],
whileElementsMounted: autoUpdate,
});
const hover = useHover(context, {
delay: { open: 0, close: 0 },
});
const focus = useFocus(context);
const dismiss = useDismiss(context);
const role = useRole(context, { role: "tooltip" });
const { getReferenceProps, getFloatingProps } = useInteractions([
hover,
focus,
dismiss,
role,
]);
return (
<>
<Slot ref={refs.setReference} {...getReferenceProps()}>
{children}
</Slot>
{open && (
<FloatingPortal>
<Root
size="fit"
ref={refs.setFloating}
style={floatingStyles}
hasShadow={true}
{...getFloatingProps()}
>
{title}
</Root>
</FloatingPortal>
)}
</>
);
};

View File

@@ -1,31 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-button {
@include add-specificity(4) {
padding-block: 0;
}
}
.dg-button-content {
@include add-specificity(4) {
}
}
.dg-button_isloading--true {
@include add-specificity(4) {
@include loading-true;
}
}
.dg-button_isloading--false {
@include add-specificity(4) {
@include loading-false;
}
}
.dg-button-loading-icon {
@include add-specificity(4) {
@include absolute;
}
}
.dg-button-icon {
@include add-specificity(4) {
}
}

View File

@@ -1,93 +0,0 @@
import Box from "@/lv1-fundamental/Box/Box";
import Root from "@/lv2-sized/ItemRoot/ItemRoot";
import { Icon } from "@/lv3-partial/Icon/Icon";
import { Label } from "@/lv3-partial/Label/Label";
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef } from "react";
import {
buttonContent,
buttonIcon,
buttonLoadingIcon,
buttonRoot,
} from "./Button.recipe";
import { cn } from "tailwind-variants";
import { SpinnerSvg } from "@/assets/svg/SpinnerSvg";
type ButtonProps = CommonProps & {
size?: "xs" | "sm" | "md" | "lg";
shape?: "circle" | "rounded" | "square";
variant?: "filled" | "outline" | "subtle";
isLoading?: boolean;
loadingIcon?: React.ReactNode;
iconSvg?: React.ReactNode;
iconOnly?: boolean;
hideIcon?: boolean; // not a state just a attribute
onClick?: () => void;
};
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const {
size = "md",
shape = "rounded",
variant = "filled",
iconOnly = false,
iconSvg,
hideIcon = false,
isLoading,
loadingIcon,
children,
className,
disabled,
...rest
} = props;
const buttonRootClass = cn(buttonRoot({ iconOnly }), className);
const buttonIconClass = buttonIcon();
const buttonContentClass =
(isLoading != undefined && buttonContent({ isLoading })) || "";
const buttonLoadingIconClass = buttonLoadingIcon({
isLoading,
});
return (
<Root
ref={ref}
as="button"
size={size}
variant={variant}
shape={shape}
iconOnly={iconOnly}
className={buttonRootClass}
{...rest}
>
<Box className={buttonContentClass}>
{iconOnly && (
// if iconOnly
<Icon size={size} className={buttonIconClass}>
{iconSvg && !hideIcon ? iconSvg : null}
</Icon>
)}
{!iconOnly &&
// if not iconOnly
iconSvg &&
!hideIcon ? (
<Icon size={size}>{iconSvg}</Icon>
) : null}
{!iconOnly ? <Label size={size}>{children}</Label> : null}
</Box>
{isLoading != undefined && (
<Icon size={size} className={buttonLoadingIconClass}>
<SpinnerSvg />
</Icon>
)}
</Root>
);
},
);
Button.displayName = "Button";

View File

@@ -1,8 +0,0 @@
import { tv } from "tailwind-variants";
export const checkboxRoot = tv({
base: "dg-checkbox",
});
export const checkboxLabel = tv({
base: "dg-checkbox-label",
});

View File

@@ -1,13 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-checkbox {
@include add-specificity(4) {
cursor: pointer;
padding-block: 0;
}
}
.dg-checkbox-label {
@include add-specificity(4) {
cursor: pointer;
}
}

View File

@@ -1,103 +0,0 @@
import { RootInline } from "@/lv2-sized/Root/RootInline";
import { Icon } from "@/lv3-partial/Icon/Icon";
import { Label } from "@/lv3-partial/Label/Label";
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef, useState, type ReactNode } from "react";
import { cn } from "tailwind-variants";
import { checkboxLabel, checkboxRoot } from "./Checkbox.recipe";
import Root from "@/lv2-sized/ItemRoot/ItemRoot";
import { Indicator } from "@/lv3-partial/Indicator/Indicator";
type CheckboxProps = CommonProps & {
id?: string;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (v: boolean) => void;
size?: "xs" | "sm";
isPlaceholder?: boolean;
indicatorBoxSvg?: ReactNode;
indicatorCheckSvg?: ReactNode;
iconSvg?: ReactNode;
};
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
(props, ref) => {
const {
id,
checked: controllerChecked,
defaultChecked = false,
onChange,
size = "sm",
isPlaceholder = false,
indicatorBoxSvg,
indicatorCheckSvg,
iconSvg,
className,
children,
disabled = false,
...rest
} = props;
const isControlled = controllerChecked !== undefined;
const [innerChecked, setInnerChecked] = useState(defaultChecked ?? false);
const currentChecked = isControlled ? controllerChecked : innerChecked;
const handleClick = (_e: React.MouseEvent<HTMLInputElement>) => {
// 若受控,点击不变
if (isControlled) {
return false;
}
// 若不受控,点击变更
const next = !currentChecked;
setInnerChecked(next);
onChange?.(next);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
// 若受控,点击不变
if (isControlled) {
return false;
}
// 若不受控,点击变更
const next = !currentChecked;
if (e.key === " " || e.key === "Enter") {
setInnerChecked(next);
}
onChange?.(next);
};
const checkboxRootClass = cn(checkboxRoot(), className);
const checkboxLabelClass = checkboxLabel();
return (
<Root
size={size}
className={checkboxRootClass}
onClick={handleClick}
onKeyDown={handleKeyDown}
disabled={disabled} // 不同于 input 的 disabled此处仅提供灰色滤镜
{...rest}
>
{isPlaceholder ? (
<RootInline size={size} iconOnly={true} />
) : (
<Indicator
size={size}
disabled={disabled}
type="checkbox"
checked={currentChecked}
ref={ref}
/>
)}
{iconSvg ? <Icon size={size}>{iconSvg}</Icon> : null}
<Label size={size} for={id} className={checkboxLabelClass}>
{children}
</Label>
</Root>
);
},
);
Checkbox.displayName = "Checkbox";

View File

@@ -1,9 +0,0 @@
import { tv } from "tailwind-variants";
export const radioRoot = tv({
base: "dg-radio",
});
export const radioLabel = tv({
base: "dg-radio-label",
});

View File

@@ -1,13 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-radio {
@include add-specificity(4) {
cursor: pointer;
padding-block: 0;
}
}
.dg-radio-label {
@include add-specificity(4) {
cursor: pointer;
}
}

View File

@@ -1,114 +0,0 @@
import { RootInline } from "@/lv2-sized/Root/RootInline";
import { Icon } from "@/lv3-partial/Icon/Icon";
import { Label } from "@/lv3-partial/Label/Label";
import type { CommonProps } from "@/common/CommonProps";
import { forwardRef, useContext, useState, type ReactNode } from "react";
import { cn } from "tailwind-variants";
import { radioLabel, radioRoot } from "./Radio.recipe";
import { RadioGroupContext } from "./RadioGroupContext";
import { Indicator } from "@/lv3-partial/Indicator/Indicator";
import Root from "@/lv2-sized/ItemRoot/ItemRoot";
type RadioProps = CommonProps & {
id?: string;
name?: string;
value: string;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (name: string, value: boolean) => void;
size?: "xs" | "sm";
isPlaceholder?: boolean;
indicatorBoxSvg?: ReactNode;
indicatorCheckSvg?: ReactNode;
iconSvg?: ReactNode;
};
export const Radio = forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
const {
checked: controllerChecked,
defaultChecked = false,
onChange,
size = "sm",
isPlaceholder = false,
indicatorBoxSvg,
indicatorCheckSvg,
iconSvg,
className,
children,
disabled = false,
id,
name,
value,
...rest
} = props;
const ctx = useContext(RadioGroupContext);
if (!ctx) {
throw new Error("Radio must be used within a RadioGroup");
}
const isControlled = controllerChecked !== undefined;
const [innerChecked, setInnerChecked] = useState(defaultChecked ?? false);
const currentChecked = isControlled ? controllerChecked : innerChecked;
const currentSize = ctx.size ?? size;
const currentName = ctx.name ?? name;
const handleClick = (_e: React.MouseEvent<HTMLInputElement>) => {
// 若受控,点击不变
if (isControlled) {
return false;
}
// 若不受控,点击变更
const next = !currentChecked;
setInnerChecked(next);
onChange?.(currentName, next);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
// 若受控,点击不变
if (isControlled) {
return false;
}
// 若不受控,点击变更
const next = !currentChecked;
if (e.key === " " || e.key === "Enter") {
setInnerChecked(next);
}
onChange?.(currentName, next);
};
const radioRootClass = cn(radioRoot(), className);
const radioLabelClass = radioLabel();
return (
<Root
size={currentSize}
className={radioRootClass}
onClick={handleClick}
onKeyDown={handleKeyDown}
disabled={disabled} // 不同于 input 的 disabled此处仅提供灰色滤镜
{...rest}
>
{isPlaceholder ? (
<RootInline size={currentSize} iconOnly={true} />
) : (
<Indicator
size={currentSize}
disabled={disabled}
type="radio"
checked={currentChecked}
ref={ref}
/>
)}
{iconSvg ? <Icon size={currentSize}>{iconSvg}</Icon> : null}
<Label size={currentSize} for={id} className={radioLabelClass}>
{children}
</Label>
</Root>
);
});
Radio.displayName = "Radio";

View File

@@ -1,15 +0,0 @@
import { tv } from "tailwind-variants";
export const radioGroupRoot = tv({
base: "dg-radio-group",
variants: {
direction: {
horizontal: "dg-radio-group_direction--horizontal",
vertical: "dg-radio-group_direction--vertical",
},
},
});
export const radioGroupList = tv({
base: "dg-radio-group-list",
});

View File

@@ -1,24 +0,0 @@
@use "../../styles/index.scss" as *;
.dg-radio-group {
@include add-specificity(4) {
@include flex-col;
}
}
.dg-radio-group_direction--horizontal {
@include add-specificity(4) {
@include flex-row;
}
}
.dg-radio-group_direction--vertical {
@include add-specificity(4) {
@include flex-col;
}
}
.dg-radio-group-list {
@include add-specificity(4) {
@include flex-col;
@include justify-start;
@include items-start;
}
}

View File

@@ -1,59 +0,0 @@
import type { CommonProps } from "@/common/CommonProps";
import { RadioGroupContext } from "./RadioGroupContext";
import { useState } from "react";
import { Label } from "@/lv3-partial/Label/Label";
import Root from "@/lv2-sized/ItemRoot/ItemRoot";
import { radioGroupList, radioGroupRoot } from "./RadioGroup.recipe";
import Box from "@/lv1-fundamental/Box/Box";
type RadioGroupProps = CommonProps & {
name: string;
label?: string;
defaultValue?: string;
onChange?: (value: string) => void;
size?: "xs" | "sm";
direction?: "vertical" | "horizontal";
};
export const RadioGroup = (props: RadioGroupProps) => {
const {
name,
defaultValue,
onChange,
label,
size = "sm",
direction = "horizontal",
className,
children,
...rest
} = props;
const [value, setValue] = useState(defaultValue ?? "");
const handleChange = (v: string) => {
setValue(v);
onChange?.(v);
};
const radioGroupRootClass = radioGroupRoot({ direction });
const radioGroupListClass = radioGroupList();
return (
<RadioGroupContext.Provider
value={{ value: value, onChange: handleChange, name, size }}
>
<Root className={radioGroupRootClass} {...rest}>
<Box className={radioGroupListClass}>
{label && (
<Root size={size}>
<Label size={size}>
{label}
</Label>
</Root>
)}
{children}
</Box>
</Root>
</RadioGroupContext.Provider>
);
};

View File

@@ -1,12 +0,0 @@
import { createContext } from "react";
export type RadioGroupContextType = {
name: string;
onChange: (v: string) => void;
value: string;
size: "xs" | "sm";
};
export const RadioGroupContext = createContext<RadioGroupContextType | null>(
null,
);

View File

@@ -16,11 +16,6 @@ export const itemRootRecipe = tv({
rounded: "",
circle: "rounded-full",
},
variant: {
filled: "variant-filled",
outline: "variant-outline",
subtle: "variant-subtle",
},
brand: {
success: "brand-success",
danger: "brand-danger",
@@ -103,21 +98,5 @@ export const itemRootRecipe = tv({
size: "2xl",
class: "w-2xl",
},
// --------------------------------------------------
{
disabled: true,
variant: "filled",
class: "variant-filled-disabled",
},
{
disabled: true,
variant: "outline",
class: "variant-outline-disabled",
},
{
disabled: true,
variant: "subtle",
class: "variant-subtle-disabled",
},
],
});

View File

@@ -0,0 +1,32 @@
import { tv } from "tailwind-variants";
export const variantRecipe = tv({
variants: {
variant: {
filled: "variant-filled",
outline: "variant-outline",
subtle: "variant-subtle",
},
disabled: {
true: "",
false: "",
},
},
compoundVariants: [
{
disabled: true,
variant: "filled",
class: "variant-filled-disabled",
},
{
disabled: true,
variant: "outline",
class: "variant-outline-disabled",
},
{
disabled: true,
variant: "subtle",
class: "variant-subtle-disabled",
},
],
});

View File

@@ -1,11 +1,11 @@
@theme {
--color-transparent: transparent;
--danger-fg: var(--base-fg);
--danger-bg-low-hover: var(--color-red-100);
--danger-bg-low-active: var(--color-red-200);
--danger-bg: var(--color-red-600);
--danger-bg-high-hover: var(--color-red-700);
--danger-bg-high-active: var(--color-red-800);
--danger-border-color: var(--color-transparent);
--success-fg: var(--base-fg);
--success-bg-low-hover: var(--color-green-100);
--success-bg-low-active: var(--color-green-200);
@@ -18,19 +18,16 @@
--info-bg: var(--color-sky-600);
--info-bg-high-hover: var(--color-sky-700);
--info-bg-high-active: var(--color-sky-800);
--info-border-color: var(--color-transparent);
--warning-fg: var(--base-fg);
--warning-bg-low-hover: var(--color-amber-100);
--warning-bg-low-active: var(--color-amber-200);
--warning-bg: var(--color-amber-600);
--warning-bg-high-hover: var(--color-amber-700);
--warning-bg-high-active: var(--color-amber-800);
--warning-border-color: var(--color-transparent);
--emphasis-fg: var(--base-fg);
--emphasis-bg-low-hover: var(--color-gray-100);
--emphasis-bg-low-active: var(--color-gray-200);
--emphasis-bg: var(--color-gray-600);
--emphasis-bgr-high-hover: var(--color-gray-700);
--emphasis-bg-high-active: var(--color-gray-800);
--emphasis-border-color: var(--color-transparent);
--default-fg: var(--base-fg);
--default-bg-low-hover: var(--color-gray-100);
--default-bg-low-active: var(--color-gray-200);
--default-bg: var(--color-gray-600);
--default-bgr-high-hover: var(--color-gray-700);
--default-bg-high-active: var(--color-gray-800);
}

View File

@@ -1,25 +1,25 @@
@theme {
--filled-fg: var(--color-white);
--filled-fg-hover: var(--brand-fg);
--filled-fg-active: var(--brand-fg);
--filled-fg-hover: var(--color-white);
--filled-fg-active: var(--color-white);
--filled-bg: var(--brand-bg);
--filled-bg-hover: var(--brand-bg-high-hover);
--filled-bg-active: var(--brand-bg-high-active);
--filled-border-color: var(--brand-border-color);
--filled-border-color: var(--color-transparent);
--outline-fg: var(--brand-bg);
--outline-fg: var(--base-fg);
--outline-fg-hover: var(--brand-bg);
--outline-fg-active: var(--brand-bg);
--outline-bg: var(--brand-border-color);
--outline-bg: var(--color-transparent);
--outline-bg-hover: var(--brand-bg-low-hover);
--outline-bg-active: var(--brand-bg-low-active);
--outline-border-color: var(--brand-bg);
--subtle-fg: var(--brand-bg);
--subtle-fg: var(--base-fg);
--subtle-fg-hover: var(--brand-bg);
--subtle-fg-active: var(--brand-bg);
--subtle-bg: var(--brand-border-color);
--subtle-bg: var(--color-transparent);
--subtle-bg-hover: var(--brand-bg-low-hover);
--subtle-bg-active: var(--brand-bg-low-active);
--subtle-border-color: var(--brand-border-color);
--subtle-border-color: var(--color-transparent);
}

View File

@@ -1,45 +1,35 @@
@utility brand-info {
--brand-fg: var(--info-fg);
--brand-bg-low-hover: var(--info-bg-low-hover);
--brand-bg-low-active: var(--info-bg-low-active);
--brand-bg: var(--info-bg);
--brand-bg-high-hover: var(--info-bg-high-hover);
--brand-bg-high-active: var(--info-bg-high-active);
--brand-border-color: var(--info-border-color);
}
@utility brand-danger {
--brand-fg: var(--danger-fg);
--brand-bg-low-hover: var(--danger-bg-low-hover);
--brand-bg-low-active: var(--danger-bg-low-active);
--brand-bg: var(--danger-bg);
--brand-bg-high-hover: var(--danger-bg-high-hover);
--brand-bg-high-active: var(--danger-bg-high-active);
--brand-border-color: var(--danger-border-color);
}
@utility brand-success {
--brand-fg: var(--success-fg);
--brand-bg-low-hover: var(--success-bg-low-hover);
--brand-bg-low-active: var(--success-bg-low-active);
--brand-bg: var(--success-bg);
--brand-bg-high-hover: var(--success-bg-high-hover);
--brand-bg-high-active: var(--success-bg-high-active);
--brand-border-color: var(--success-border-color);
}
@utility brand-warning {
--brand-fg: var(--warning-fg);
--brand-bg-low-hover: var(--warning-bg-low-hover);
--brand-bg-low-active: var(--warning-bg-low-active);
--brand-bg: var(--warning-bg);
--brand-bg-high-hover: var(--warning-bg-high-hover);
--brand-bg-high-active: var(--warning-bg-high-active);
--brand-border-color: var(--warning-border-color);
}
@utility brand-emphasis {
--brand-fg: var(--emphasis-fg);
--brand-bg-low-hover: var(--emphasis-bg-low-hover);
--brand-bg-low-active: var(--emphasis-bg-low-active);
--brand-bg: var(--emphasis-bg);
--brand-bg-high-hover: var(--emphasis-bg-high-hover);
--brand-bg-high-active: var(--emphasis-bg-high-active);
--brand-border-color: var(--emphasis-border-color);
@utility brand-default {
--brand-bg-low-hover: var(--default-bg-low-hover);
--brand-bg-low-active: var(--default-bg-low-active);
--brand-bg: var(--default-bg);
--brand-bg-high-hover: var(--default-bg-high-hover);
--brand-bg-high-active: var(--default-bg-high-active);
}

View File

@@ -5,14 +5,17 @@
&:hover {
background-color: var(--filled-bg-hover);
color: var(--filid-fg-hover);
}
&:active {
background-color: var(--filled-bg-active);
color: var(--filid-fg-active);
}
&:focus-visible {
background-color: var(--filled-bg-hover);
color: var(--filid-fg-hover);
}
}
@@ -23,14 +26,17 @@
&:hover {
background-color: var(--outline-bg-hover);
color: var(--outline-fg-hover);
}
&:active {
background-color: var(--outline-bg-active);
color: var(--outline-fg-active);
}
&:focus-visible {
background-color: var(--outline-bg-hover);
color: var(--outline-fg-hover);
}
}
@@ -41,14 +47,17 @@
&:hover {
background-color: var(--subtle-bg-hover);
color: var(--subtle-fg-hover);
}
&:active {
background-color: var(--subtle-bg-active);
color: var(--subtle-fg-active);
}
&:focus-visible {
background-color: var(--subtle-bg-hover);
color: var(--subtle-fg-hover);
}
}

412
pnpm-lock.yaml generated
View File

@@ -14,9 +14,9 @@ importers:
apps/ui-site:
dependencies:
'@defgov/ui-headless':
'@defgov/ui':
specifier: workspace:*
version: link:../../packages/ui-headless
version: link:../../packages/ui
react:
specifier: ^19.2.0
version: 19.2.4
@@ -61,63 +61,11 @@ importers:
specifier: ^7.3.1
version: 7.3.1(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
packages/headless-ui:
dependencies:
'@floating-ui/react-dom':
specifier: ^2.1.7
version: 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@floating-ui/utils':
specifier: ^0.2.10
version: 0.2.10
devDependencies:
'@tailwindcss/vite':
specifier: ^4.2.1
version: 4.2.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
'@tsconfig/vite-react':
specifier: ^7.0.2
version: 7.0.2
'@types/node':
specifier: ^25.3.3
version: 25.3.3
'@types/react':
specifier: ^19.2.10
version: 19.2.14
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: ^5.1.4
version: 5.1.4(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
glob:
specifier: ^13.0.3
version: 13.0.3
react:
specifier: ^19.2.4
version: 19.2.4
react-dom:
specifier: ^19.2.4
version: 19.2.4(react@19.2.4)
tailwindcss:
specifier: ^4.2.1
version: 4.2.1
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@25.3.3)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^7.3.1
version: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
vite-plugin-dts:
specifier: ^4.5.4
version: 4.5.4(@types/node@25.3.3)(rollup@4.57.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
packages/ui:
dependencies:
'@floating-ui/react':
specifier: ^0.27.18
version: 0.27.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@base-ui/react':
specifier: ^1.3.0
version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tailwindcss/vite':
specifier: ^4.2.1
version: 4.2.1(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
@@ -174,101 +122,6 @@ importers:
specifier: ^4.5.4
version: 4.5.4(@types/node@25.2.0)(rollup@4.57.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
packages/ui-headless:
dependencies:
'@floating-ui/react':
specifier: ^0.27.18
version: 0.27.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tailwindcss/vite':
specifier: ^4.2.1
version: 4.2.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
react:
specifier: ^19.2.4
version: 19.2.4
react-dom:
specifier: ^19.2.4
version: 19.2.4(react@19.2.4)
tailwind-merge:
specifier: ^3.5.0
version: 3.5.0
tailwind-variants:
specifier: ^3.2.2
version: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
tailwindcss:
specifier: ^4.2.1
version: 4.2.1
devDependencies:
'@tsconfig/vite-react':
specifier: ^7.0.2
version: 7.0.2
'@types/node':
specifier: ^25.1.0
version: 25.3.3
'@types/react':
specifier: ^19.2.10
version: 19.2.14
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: ^5.1.2
version: 5.1.4(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
autoprefixer:
specifier: ^10.4.24
version: 10.4.24(postcss@8.5.6)
glob:
specifier: ^13.0.3
version: 13.0.3
prettier:
specifier: ^3.8.1
version: 3.8.1
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@25.3.3)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^7.3.1
version: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
vite-plugin-dts:
specifier: ^4.5.4
version: 4.5.4(@types/node@25.3.3)(rollup@4.57.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))
packages/ui-headless-style:
dependencies:
tailwind-merge:
specifier: ^3.5.0
version: 3.5.0
tailwind-variants:
specifier: ^3.2.2
version: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
packages/ui-headless-tailwind-style:
dependencies:
tailwind-merge:
specifier: ^3.5.0
version: 3.5.0
tailwind-variants:
specifier: ^3.2.2
version: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
tailwindcss:
specifier: '>=4.2.1 <5.0.0'
version: 4.2.1
devDependencies:
'@types/node':
specifier: ^25.3.3
version: 25.3.3
glob:
specifier: ^13.0.3
version: 13.0.3
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@25.3.3)(typescript@5.9.3)
typescript:
specifier: ^5.9.3
version: 5.9.3
packages:
'@babel/code-frame@7.29.0':
@@ -342,6 +195,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.29.2':
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
'@babel/template@7.28.6':
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
engines: {node: '>=6.9.0'}
@@ -354,6 +211,27 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
'@base-ui/react@1.3.0':
resolution: {integrity: sha512-FwpKqZbPz14AITp1CVgf4AjhKPe1OeeVKSBMdgD10zbFlj3QSWelmtCMLi2+/PFZZcIm3l87G7rwtCZJwHyXWA==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@types/react': ^17 || ^18 || ^19
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
'@types/react':
optional: true
'@base-ui/utils@0.2.6':
resolution: {integrity: sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==}
peerDependencies:
'@types/react': ^17 || ^18 || ^19
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
'@types/react':
optional: true
'@cspotcode/source-map-support@0.8.1':
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
@@ -552,26 +430,20 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.7.4':
resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==}
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
'@floating-ui/dom@1.7.5':
resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==}
'@floating-ui/dom@1.7.6':
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
'@floating-ui/react-dom@2.1.7':
resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==}
'@floating-ui/react-dom@2.1.8':
resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@floating-ui/react@0.27.18':
resolution: {integrity: sha512-xJWJxvmy3a05j643gQt+pRbht5XnTlGpsEsAPnMi5F5YTOEEJymA90uZKBD8OvIv5XvZ1qi4GcccSlqT3Bq44Q==}
peerDependencies:
react: '>=17.0.0'
react-dom: '>=17.0.0'
'@floating-ui/utils@0.2.10':
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
'@floating-ui/utils@0.2.11':
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
@@ -1029,9 +901,6 @@ packages:
'@types/node@25.2.0':
resolution: {integrity: sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==}
'@types/node@25.3.3':
resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==}
'@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
peerDependencies:
@@ -1801,6 +1670,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
reselect@5.1.1:
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -1984,9 +1856,6 @@ packages:
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
@@ -2000,6 +1869,11 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
@@ -2178,6 +2052,8 @@ snapshots:
'@babel/core': 7.29.0
'@babel/helper-plugin-utils': 7.28.6
'@babel/runtime@7.29.2': {}
'@babel/template@7.28.6':
dependencies:
'@babel/code-frame': 7.29.0
@@ -2201,6 +2077,30 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
'@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@babel/runtime': 7.29.2
'@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@floating-ui/utils': 0.2.11
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
tabbable: 6.4.0
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
'@types/react': 19.2.14
'@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@babel/runtime': 7.29.2
'@floating-ui/utils': 0.2.11
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
reselect: 5.1.1
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
'@types/react': 19.2.14
'@cspotcode/source-map-support@0.8.1':
dependencies:
'@jridgewell/trace-mapping': 0.3.9
@@ -2329,30 +2229,22 @@ snapshots:
'@eslint/core': 0.17.0
levn: 0.4.1
'@floating-ui/core@1.7.4':
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.10
'@floating-ui/utils': 0.2.11
'@floating-ui/dom@1.7.5':
'@floating-ui/dom@1.7.6':
dependencies:
'@floating-ui/core': 1.7.4
'@floating-ui/utils': 0.2.10
'@floating-ui/core': 1.7.5
'@floating-ui/utils': 0.2.11
'@floating-ui/react-dom@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
'@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@floating-ui/dom': 1.7.5
'@floating-ui/dom': 1.7.6
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
'@floating-ui/react@0.27.18(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@floating-ui/react-dom': 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@floating-ui/utils': 0.2.10
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
tabbable: 6.4.0
'@floating-ui/utils@0.2.10': {}
'@floating-ui/utils@0.2.11': {}
'@humanfs/core@0.19.1': {}
@@ -2399,14 +2291,6 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@microsoft/api-extractor-model@7.33.1(@types/node@25.3.3)':
dependencies:
'@microsoft/tsdoc': 0.16.0
'@microsoft/tsdoc-config': 0.18.0
'@rushstack/node-core-library': 5.20.1(@types/node@25.3.3)
transitivePeerDependencies:
- '@types/node'
'@microsoft/api-extractor@7.57.3(@types/node@25.2.0)':
dependencies:
'@microsoft/api-extractor-model': 7.33.1(@types/node@25.2.0)
@@ -2426,25 +2310,6 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@microsoft/api-extractor@7.57.3(@types/node@25.3.3)':
dependencies:
'@microsoft/api-extractor-model': 7.33.1(@types/node@25.3.3)
'@microsoft/tsdoc': 0.16.0
'@microsoft/tsdoc-config': 0.18.0
'@rushstack/node-core-library': 5.20.1(@types/node@25.3.3)
'@rushstack/rig-package': 0.7.1
'@rushstack/terminal': 0.22.1(@types/node@25.3.3)
'@rushstack/ts-command-line': 5.3.1(@types/node@25.3.3)
diff: 8.0.3
lodash: 4.17.23
minimatch: 10.2.1
resolve: 1.22.11
semver: 7.5.4
source-map: 0.6.1
typescript: 5.8.2
transitivePeerDependencies:
- '@types/node'
'@microsoft/tsdoc-config@0.18.0':
dependencies:
'@microsoft/tsdoc': 0.16.0
@@ -2613,27 +2478,10 @@ snapshots:
optionalDependencies:
'@types/node': 25.2.0
'@rushstack/node-core-library@5.20.1(@types/node@25.3.3)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
ajv-formats: 3.0.1(ajv@8.13.0)
fs-extra: 11.3.3
import-lazy: 4.0.0
jju: 1.4.0
resolve: 1.22.11
semver: 7.5.4
optionalDependencies:
'@types/node': 25.3.3
'@rushstack/problem-matcher@0.2.1(@types/node@25.2.0)':
optionalDependencies:
'@types/node': 25.2.0
'@rushstack/problem-matcher@0.2.1(@types/node@25.3.3)':
optionalDependencies:
'@types/node': 25.3.3
'@rushstack/rig-package@0.7.1':
dependencies:
resolve: 1.22.11
@@ -2647,14 +2495,6 @@ snapshots:
optionalDependencies:
'@types/node': 25.2.0
'@rushstack/terminal@0.22.1(@types/node@25.3.3)':
dependencies:
'@rushstack/node-core-library': 5.20.1(@types/node@25.3.3)
'@rushstack/problem-matcher': 0.2.1(@types/node@25.3.3)
supports-color: 8.1.1
optionalDependencies:
'@types/node': 25.3.3
'@rushstack/ts-command-line@5.3.1(@types/node@25.2.0)':
dependencies:
'@rushstack/terminal': 0.22.1(@types/node@25.2.0)
@@ -2664,15 +2504,6 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@rushstack/ts-command-line@5.3.1(@types/node@25.3.3)':
dependencies:
'@rushstack/terminal': 0.22.1(@types/node@25.3.3)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.2
transitivePeerDependencies:
- '@types/node'
'@tailwindcss/node@4.2.1':
dependencies:
'@jridgewell/remapping': 2.3.5
@@ -2741,13 +2572,6 @@ snapshots:
tailwindcss: 4.2.1
vite: 7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
'@tailwindcss/vite@4.2.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))':
dependencies:
'@tailwindcss/node': 4.2.1
'@tailwindcss/oxide': 4.2.1
tailwindcss: 4.2.1
vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
'@tsconfig/node10@1.0.12': {}
'@tsconfig/node12@1.0.11': {}
@@ -2793,10 +2617,6 @@ snapshots:
dependencies:
undici-types: 7.16.0
'@types/node@25.3.3':
dependencies:
undici-types: 7.18.2
'@types/react-dom@19.2.3(@types/react@19.2.14)':
dependencies:
'@types/react': 19.2.14
@@ -2920,18 +2740,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
'@rolldown/pluginutils': 1.0.0-rc.3
'@types/babel__core': 7.20.5
react-refresh: 0.18.0
vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
transitivePeerDependencies:
- supports-color
'@volar/language-core@2.4.28':
dependencies:
'@volar/source-map': 2.4.28
@@ -3590,6 +3398,8 @@ snapshots:
require-from-string@2.0.2: {}
reselect@5.1.1: {}
resolve-from@4.0.0: {}
resolve@1.22.11:
@@ -3715,24 +3525,6 @@ snapshots:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
ts-node@10.9.2(@types/node@25.3.3)(typescript@5.9.3):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.12
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 25.3.3
acorn: 8.15.0
acorn-walk: 8.3.4
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.4
make-error: 1.3.6
typescript: 5.9.3
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
turbo-darwin-64@2.8.3:
optional: true
@@ -3783,8 +3575,6 @@ snapshots:
undici-types@7.16.0: {}
undici-types@7.18.2: {}
universalify@2.0.1: {}
update-browserslist-db@1.2.3(browserslist@4.28.1):
@@ -3797,6 +3587,10 @@ snapshots:
dependencies:
punycode: 2.3.1
use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
v8-compile-cache-lib@3.0.1: {}
vite-plugin-dts@4.5.4(@types/node@25.2.0)(rollup@4.57.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.2.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)):
@@ -3818,25 +3612,6 @@ snapshots:
- rollup
- supports-color
vite-plugin-dts@4.5.4(@types/node@25.3.3)(rollup@4.57.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)):
dependencies:
'@microsoft/api-extractor': 7.57.3(@types/node@25.3.3)
'@rollup/pluginutils': 5.3.0(rollup@4.57.1)
'@volar/typescript': 2.4.28
'@vue/language-core': 2.2.0(typescript@5.9.3)
compare-versions: 6.1.1
debug: 4.4.3
kolorist: 1.8.0
local-pkg: 1.1.2
magic-string: 0.30.21
typescript: 5.9.3
optionalDependencies:
vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)
transitivePeerDependencies:
- '@types/node'
- rollup
- supports-color
vite@7.3.1(@types/node@24.10.13)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3):
dependencies:
esbuild: 0.27.2
@@ -3867,21 +3642,6 @@ snapshots:
lightningcss: 1.31.1
sass: 1.97.3
vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.57.1
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.3.3
fsevents: 2.3.3
jiti: 2.6.1
lightningcss: 1.31.1
sass: 1.97.3
vscode-uri@3.1.0: {}
which@2.0.2: