@jbpark/ui-kit 8.1.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/Button.d.mts +48 -12
  2. package/dist/Button.d.mts.map +1 -1
  3. package/dist/Button.mjs +62 -11
  4. package/dist/Button.mjs.map +1 -1
  5. package/dist/Tag.d.mts +8 -11
  6. package/dist/Tag.d.mts.map +1 -1
  7. package/dist/Tag.mjs +8 -7
  8. package/dist/Tag.mjs.map +1 -1
  9. package/dist/components/atoms/date-picker.mjs +1 -1
  10. package/dist/components/atoms/float-button/float-button.mjs +1 -1
  11. package/dist/components/atoms/input/search.mjs +1 -1
  12. package/dist/components/atoms/radio/group.mjs +1 -1
  13. package/dist/components/atoms/radio/radio.d.mts +1 -1
  14. package/dist/components/organisms/drawer.mjs +1 -1
  15. package/dist/components/organisms/modal.d.mts +4 -5
  16. package/dist/components/organisms/modal.d.mts.map +1 -1
  17. package/dist/components/organisms/modal.mjs +5 -6
  18. package/dist/components/organisms/modal.mjs.map +1 -1
  19. package/dist/components/organisms/toast.d.mts +2 -8
  20. package/dist/components/organisms/toast.d.mts.map +1 -1
  21. package/dist/components/organisms/toast.mjs +4 -5
  22. package/dist/components/organisms/toast.mjs.map +1 -1
  23. package/dist/core/badge.d.mts +25 -2
  24. package/dist/core/badge.d.mts.map +1 -0
  25. package/dist/core/badge.mjs +47 -0
  26. package/dist/core/badge.mjs.map +1 -0
  27. package/dist/core/button.d.mts +14 -2
  28. package/dist/core/button.d.mts.map +1 -0
  29. package/dist/core/dialog.mjs +1 -1
  30. package/dist/core/field.d.mts +1 -1
  31. package/dist/lib/colors.d.mts +6 -31
  32. package/dist/lib/colors.d.mts.map +1 -1
  33. package/dist/style.css +25 -34
  34. package/package.json +1 -1
  35. package/dist/lib/chassis.mjs +0 -23
  36. package/dist/lib/chassis.mjs.map +0 -1
package/dist/Button.d.mts CHANGED
@@ -1,15 +1,43 @@
1
- import { DeprecatedPresetColor, PresetColor } from "./lib/colors.mjs";
1
+ import { Button as Button$1 } from "./core/button.mjs";
2
+ import "./core/index.mjs";
3
+ import { PresetColor } from "./lib/colors.mjs";
2
4
  import * as React$1 from "react";
3
5
  //#region src/components/atoms/button.d.ts
4
- type NativeButtonProps = React$1.ComponentProps<'button'> & {
5
- asChild?: boolean;
6
- };
7
- interface Props extends Omit<NativeButtonProps, 'size' | 'variant' | 'type'> {
6
+ declare const Core: typeof Button$1;
7
+ /**
8
+ * shadcn's own `variant` words, accepted on top of this component's own
9
+ * vocabulary rather than replacing it. They are sugar in exactly the sense
10
+ * `type` is: each expands to a `(color, variant)` pair, so a snippet copied
11
+ * from the shadcn docs renders as the nearest thing on this library's scale
12
+ * instead of erroring.
13
+ *
14
+ * `link` is deliberately absent: it already exists as one of this component's
15
+ * own variants and already renders what shadcn's `link` renders, so mapping it
16
+ * would change `variant="link"`'s resolved colour from `default` to `primary`
17
+ * for existing callers.
18
+ */
19
+ type ShadcnVariant = 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost';
20
+ /**
21
+ * shadcn's `size` words. The `icon*` entries additionally force the icon-only
22
+ * (square, unpadded) treatment, which this component otherwise infers from
23
+ * `icon` being set with no children — without that, the shadcn idiom of
24
+ * passing the icon as *children* (`<Button size="icon"><Trash /></Button>`)
25
+ * would render a normal padded button.
26
+ */
27
+ type ShadcnSize = 'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg';
28
+ interface Props extends Omit<React$1.ComponentProps<typeof Core>, 'size' | 'variant' | 'type'> {
8
29
  icon?: React$1.ReactNode;
9
30
  block?: boolean;
10
31
  danger?: boolean;
11
32
  disabled?: boolean;
12
- size?: 'small' | 'middle' | 'large';
33
+ /**
34
+ * Size on this library's antd-style scale. shadcn's words are accepted too
35
+ * and map onto that scale: `sm`/`xs` → `small`, `default` → `middle`,
36
+ * `lg` → `large`. There is no separate `xs` step here, so `xs` renders as
37
+ * `small`. The `icon*` entries pick the same sizes and additionally force
38
+ * the icon-only treatment.
39
+ */
40
+ size?: 'small' | 'middle' | 'large' | ShadcnSize;
13
41
  /**
14
42
  * Syntactic sugar for a `(color, variant)` pair. **Will follow `variant` and
15
43
  * `color` if provided** — `type` only fills in the axes you leave unset.
@@ -31,8 +59,19 @@ interface Props extends Omit<NativeButtonProps, 'size' | 'variant' | 'type'> {
31
59
  /**
32
60
  * Visual fill style, independent of `color`. Defaults to `'outlined'`.
33
61
  * Takes precedence over whatever `type` would have supplied.
62
+ *
63
+ * shadcn's words are accepted here as well and expand to a `(color, variant)`
64
+ * pair the same way `type` does — `destructive` ≡ `color="danger"
65
+ * variant="solid"`, `outline` ≡ `outlined`, `secondary` ≡ `filled`,
66
+ * `ghost` ≡ `text`, `default` ≡ `color="primary" variant="solid"`. An
67
+ * explicit `color` still wins over the one the word implies.
68
+ *
69
+ * ⚠️ `variant="default"` (shadcn: a filled primary button) and
70
+ * `type="default"` (antd: a plain outlined button) mean opposite things.
71
+ * They are different props, so nothing collides at runtime, but don't read
72
+ * one as the other.
34
73
  */
35
- variant?: 'solid' | 'outlined' | 'dashed' | 'filled' | 'text' | 'link';
74
+ variant?: 'solid' | 'outlined' | 'dashed' | 'filled' | 'text' | 'link' | ShadcnVariant;
36
75
  /**
37
76
  * Native `<button>` `type` (`button`/`submit`/`reset`), kept separate
38
77
  * from the antd-style `type` above. Defaults to `'button'` so a Button
@@ -44,12 +83,9 @@ interface Props extends Omit<NativeButtonProps, 'size' | 'variant' | 'type'> {
44
83
  * Color, independent of `variant`. Defaults to `'default'`, or to whatever
45
84
  * `type` maps to when `type` is set. `danger` overrides this.
46
85
  *
47
- * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings
48
- * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but
49
- * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no
50
- * successor; it is `orange` one lightness step darker).
86
+ * Preset hues use Tailwind's colour names (#342).
51
87
  */
52
- color?: PresetColor | DeprecatedPresetColor | 'default' | 'primary' | 'danger';
88
+ color?: PresetColor | 'default' | 'primary' | 'danger';
53
89
  loading?: boolean | {
54
90
  icon: React$1.ReactNode;
55
91
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;KAaK,oBAAoB,QAAM;EAC7B;;UAGe,cAAc,KAC7B;EAGA,OAAO,QAAM;EACb;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;EAkBA;;;;;EAKA;;;;;;EAMA;EACA;;;;;;;;;;EAUA,QACE,cAAc;EAChB;IAAsB,MAAM,QAAM;;;cAqF9B,WAAU,MAAA,WAAA,MAAA,SAAA,UAAA,MAAA,OAAA,OAAA,OAAA,UAAA,SAAA,QAAA,SAAA,UAAA,gBAAA,SAiBb,UAAK,QAAA,IAAA"}
1
+ {"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;;;cAaM,aAAI;;;;;;;;;;;;;KAcL;;;;;;;;KAUA;UAGY,cAAc,KAC7B,QAAM,sBAAsB;EAG5B,OAAO,QAAM;EACb;EACA;EACA;;;;;;;;EAQA,sCAAsC;;;;;;;;;;;;;;;;;;EAkBtC;;;;;;;;;;;;;;;;EAgBA,yEAOI;;;;;;EAMJ;EACA;;;;;;;EAOA,QAAQ;EACR;IAAsB,MAAM,QAAM;;;cAmH9B,WAAU,MAAA,WAAA,MAAA,SAAA,UAAA,MAAA,OAAA,OAAA,OAAA,UAAA,SAAA,QAAA,SAAA,UAAA,gBAAA,SAiBb,UAAK,QAAA,IAAA"}
package/dist/Button.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  "use client";
2
2
  import { cn } from "./utils.mjs";
3
3
  import { useConfig } from "./providers/config/context.mjs";
4
- import { INTERACTIVE_CHASSIS } from "./lib/chassis.mjs";
4
+ import { Button as Button$1 } from "./core/button.mjs";
5
5
  import "react";
6
- import { Slot } from "@radix-ui/react-slot";
6
+ import { Slottable } from "@radix-ui/react-slot";
7
7
  import { LoaderCircle } from "lucide-react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/components/atoms/button.tsx
10
- const BUTTON_CHASSIS = cn(INTERACTIVE_CHASSIS, "outline-none", "disabled:pointer-events-none disabled:opacity-50", "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4");
11
- const BUTTON_BASE = cn(BUTTON_CHASSIS, "gap-2 whitespace-nowrap font-medium transition-all", "has-[>svg]:px-3");
10
+ const Core = Button$1;
11
+ const BUTTON_BASE = cn("gap-2 whitespace-nowrap font-medium transition-all", "has-[>svg]:px-3");
12
12
  const variantClasses = {
13
13
  solid: cn("bg-primary text-primary-foreground", "hover:bg-primary/90"),
14
14
  outlined: cn("border border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]", "bg-background text-foreground", "hover:bg-accent"),
@@ -46,6 +46,51 @@ const typeToColorVariant = {
46
46
  variant: "link"
47
47
  }
48
48
  };
49
+ /**
50
+ * shadcn's variant words → the `(color, variant)` pair each stands for here.
51
+ * Same contract as `typeToColorVariant` above: the word only fills axes the
52
+ * caller left unset, so an explicit `color` still wins over the implied one.
53
+ */
54
+ const shadcnVariantToColorVariant = {
55
+ default: {
56
+ color: "primary",
57
+ variant: "solid"
58
+ },
59
+ destructive: {
60
+ color: "danger",
61
+ variant: "solid"
62
+ },
63
+ outline: {
64
+ color: "default",
65
+ variant: "outlined"
66
+ },
67
+ secondary: {
68
+ color: "default",
69
+ variant: "filled"
70
+ },
71
+ ghost: {
72
+ color: "default",
73
+ variant: "text"
74
+ }
75
+ };
76
+ /** shadcn's size words → this library's scale. */
77
+ const shadcnSizeToSize = {
78
+ default: "middle",
79
+ xs: "small",
80
+ sm: "small",
81
+ lg: "large",
82
+ icon: "middle",
83
+ "icon-xs": "small",
84
+ "icon-sm": "small",
85
+ "icon-lg": "large"
86
+ };
87
+ /** The `size` words that also mean "render this icon-only". */
88
+ const ICON_SIZES = /* @__PURE__ */ new Set([
89
+ "icon",
90
+ "icon-xs",
91
+ "icon-sm",
92
+ "icon-lg"
93
+ ]);
49
94
  const sizesClasses = {
50
95
  small: "h-6 px-3 text-sm",
51
96
  middle: "h-8 px-4 text-base",
@@ -64,18 +109,24 @@ const shapesClasses = {
64
109
  const Button = ({ icon, className, type, variant, htmlType = "button", size, color, shape, block = false, disabled, loading, danger, asChild = false, children, onMouseDown, ...props }) => {
65
110
  const { componentSize, defaultProps } = useConfig();
66
111
  const buttonDefaults = defaultProps?.button;
67
- const resolvedSize = size ?? componentSize ?? "middle";
68
112
  const resolvedShape = shape ?? buttonDefaults?.shape ?? "default";
69
- const iconOnly = icon && !children;
113
+ const variantAlias = variant && variant in shadcnVariantToColorVariant ? shadcnVariantToColorVariant[variant] : void 0;
114
+ const ownVariant = variantAlias?.variant ?? variant;
115
+ const resolvedSize = (size && size in shadcnSizeToSize ? shadcnSizeToSize[size] : size) ?? componentSize ?? "middle";
116
+ const iconOnly = size != null && ICON_SIZES.has(size) || !!(icon && !children);
70
117
  const typeDefaults = type ? typeToColorVariant[type] : void 0;
71
- const resolvedVariant = variant ?? typeDefaults?.variant ?? buttonDefaults?.variant ?? "outlined";
72
- const computedColor = danger ? "danger" : color ?? typeDefaults?.color ?? buttonDefaults?.color ?? "default";
118
+ const resolvedVariant = ownVariant ?? typeDefaults?.variant ?? buttonDefaults?.variant ?? "outlined";
119
+ const computedColor = danger ? "danger" : color ?? variantAlias?.color ?? typeDefaults?.color ?? buttonDefaults?.color ?? "default";
73
120
  const colored = computedColor !== "default";
74
121
  const isLoading = !!loading;
75
- const Comp = asChild ? Slot : "button";
76
122
  const displayIcon = loading ? typeof loading === "object" ? loading.icon : /* @__PURE__ */ jsx(LoaderCircle, { className: "animate-spin" }) : icon;
77
- return /* @__PURE__ */ jsxs(Comp, {
123
+ return /* @__PURE__ */ jsxs(Core, {
124
+ variant: null,
125
+ size: null,
126
+ asChild,
78
127
  "data-slot": "button",
128
+ "data-variant": resolvedVariant,
129
+ "data-size": resolvedSize,
79
130
  className: cn(BUTTON_BASE, "inline-flex items-center justify-center gap-x-2", "rounded-lg", "cursor-pointer", "h-auto py-0", "transition-all", variantClasses[resolvedVariant], sizesClasses[resolvedSize], iconOnly && ["p-0", iconClasses[resolvedSize]], shapesClasses[resolvedShape], block && "w-full", colored && (resolvedVariant === "solid" ? [
80
131
  "bg-(--btn-bg)",
81
132
  "hover:bg-(--btn-bg-hover)",
@@ -91,7 +142,7 @@ const Button = ({ icon, className, type, variant, htmlType = "button", size, col
91
142
  onMouseDown?.(e);
92
143
  },
93
144
  ...props,
94
- children: [displayIcon, children]
145
+ children: [displayIcon, /* @__PURE__ */ jsx(Slottable, { children })]
95
146
  });
96
147
  };
97
148
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":[],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\nimport { LoaderCircle } from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type DeprecatedPresetColor, type PresetColor } from '../../lib/colors';\n\ntype NativeButtonProps = React.ComponentProps<'button'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<\n NativeButtonProps,\n 'size' | 'variant' | 'type'\n> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n /**\n * Syntactic sugar for a `(color, variant)` pair. **Will follow `variant` and\n * `color` if provided** — `type` only fills in the axes you leave unset.\n *\n * | `type` | equivalent to |\n * | --------- | -------------------------------------- |\n * | `primary` | `color=\"primary\" variant=\"solid\"` |\n * | `default` | `color=\"default\" variant=\"outlined\"` |\n * | `dashed` | `color=\"default\" variant=\"dashed\"` |\n * | `text` | `color=\"default\" variant=\"text\"` |\n * | `link` | `color=\"primary\" variant=\"link\"` |\n *\n * So `<Button type=\"primary\">` is the same as\n * `<Button color=\"primary\" variant=\"solid\">`, and\n * `<Button type=\"primary\" variant=\"filled\">` keeps the primary color but\n * renders filled. `danger` still overrides the resolved color.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\n /**\n * Visual fill style, independent of `color`. Defaults to `'outlined'`.\n * Takes precedence over whatever `type` would have supplied.\n */\n variant?: 'solid' | 'outlined' | 'dashed' | 'filled' | 'text' | 'link';\n /**\n * Native `<button>` `type` (`button`/`submit`/`reset`), kept separate\n * from the antd-style `type` above. Defaults to `'button'` so a Button\n * placed inside a `<form>` doesn't submit it unless explicitly opted in.\n */\n htmlType?: 'button' | 'submit' | 'reset';\n shape?: 'default' | 'circle' | 'round';\n /**\n * Color, independent of `variant`. Defaults to `'default'`, or to whatever\n * `type` maps to when `type` is set. `danger` overrides this.\n *\n * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings\n * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but\n * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no\n * successor; it is `orange` one lightness step darker).\n */\n color?:\n PresetColor | DeprecatedPresetColor | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\n// Chassis absorbed from core/button's cva base (#278 ③ / #294 Phase 4). The\n// shared focus/aria set lives in INTERACTIVE_CHASSIS; a Button additionally\n// disables and sizes its icons. Kept as constants so the absorb relocates this\n// treatment rather than dropping it (the #177 class of silent regression the\n// #300/#301 net guards).\nconst BUTTON_CHASSIS = cn(\n INTERACTIVE_CHASSIS,\n 'outline-none',\n 'disabled:pointer-events-none disabled:opacity-50',\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n);\n\n// Presentation the atom builds on. The size/variant/shape classes below still\n// override the incidental leftovers exactly as they did when this wrapped\n// core/button, so the rendered output is unchanged (verified against a\n// pre-refactor render matrix).\nconst BUTTON_BASE = cn(\n BUTTON_CHASSIS,\n 'gap-2 whitespace-nowrap font-medium transition-all',\n 'has-[>svg]:px-3',\n);\n\nconst variantClasses: Record<string, string> = {\n // solid used to inherit its fill from core/button's default variant; carry it\n // explicitly now that the primitive is absorbed. A colored solid overrides\n // this with the --btn-* custom properties further down.\n solid: cn('bg-primary text-primary-foreground', 'hover:bg-primary/90'),\n outlined: cn(\n 'border border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n dashed: cn(\n 'border border-dashed border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n filled: cn('bg-muted text-foreground', 'hover:bg-muted/80'),\n text: cn('bg-transparent text-foreground', 'hover:bg-accent'),\n link: cn(\n 'bg-transparent text-primary',\n 'underline-offset-4 hover:underline',\n 'hover:bg-primary/10',\n ),\n};\n\n/**\n * antd-style syntactic sugar: each `type` expands to a `(color, variant)` pair.\n * Explicit `color`/`variant` props win over whatever the `type` maps to, so\n * `type` only fills in the axes the caller left unset.\n *\n * Mirrors antd: `<Button type=\"primary\">` ≡ `<Button color=\"primary\" variant=\"solid\">`.\n */\nconst typeToColorVariant: Record<\n NonNullable<Props['type']>,\n { color: NonNullable<Props['color']>; variant: NonNullable<Props['variant']> }\n> = {\n primary: { color: 'primary', variant: 'solid' },\n default: { color: 'default', variant: 'outlined' },\n dashed: { color: 'default', variant: 'dashed' },\n text: { color: 'default', variant: 'text' },\n link: { color: 'primary', variant: 'link' },\n};\n\nconst sizesClasses: Record<string, string> = {\n small: 'h-6 px-3 text-sm',\n middle: 'h-8 px-4 text-base',\n large: 'h-10 px-5 text-lg',\n};\n\nconst iconClasses: Record<string, string> = {\n small: `size-6 [&_svg:not([class*='size-'])]:size-3`,\n middle: `size-8 [&_svg:not([class*='size-'])]:size-4`,\n large: `size-10 [&_svg:not([class*='size-'])]:size-5`,\n};\n\nconst shapesClasses = {\n default: 'rounded-sm',\n circle: 'rounded-full',\n round: 'rounded-2xl',\n};\n\nconst Button = ({\n icon,\n className,\n type,\n variant,\n htmlType = 'button',\n size,\n color,\n shape,\n block = false,\n disabled,\n loading,\n danger,\n asChild = false,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const { componentSize, defaultProps } = useConfig();\n const buttonDefaults = defaultProps?.button as\n Partial<Pick<Props, 'shape' | 'variant' | 'color'>> | undefined;\n const resolvedSize = size ?? componentSize ?? 'middle';\n const resolvedShape = shape ?? buttonDefaults?.shape ?? 'default';\n const iconOnly = icon && !children;\n // `type` is syntactic sugar that expands to a (color, variant) pair; explicit\n // `color`/`variant` win over it, so `type` only fills the axes left unset.\n // A `Config` default sits below the call-site `type` (which is more specific)\n // but above the library built-in. `danger` still trumps everything, matching\n // antd. Defaults live here rather than on the parameters so `type` gets a\n // chance to supply them first.\n const typeDefaults = type ? typeToColorVariant[type] : undefined;\n const resolvedVariant =\n variant ?? typeDefaults?.variant ?? buttonDefaults?.variant ?? 'outlined';\n const computedColor = danger\n ? 'danger'\n : (color ?? typeDefaults?.color ?? buttonDefaults?.color ?? 'default');\n const colored = computedColor !== 'default';\n const isLoading = !!loading;\n\n const Comp = asChild ? Slot : 'button';\n\n const displayIcon = loading ? (\n typeof loading === 'object' ? (\n loading.icon\n ) : (\n <LoaderCircle className=\"animate-spin\" />\n )\n ) : (\n icon\n );\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(\n BUTTON_BASE,\n 'inline-flex items-center justify-center gap-x-2',\n 'rounded-lg',\n 'cursor-pointer',\n 'h-auto py-0',\n 'transition-all',\n variantClasses[resolvedVariant],\n sizesClasses[resolvedSize],\n iconOnly && ['p-0', iconClasses[resolvedSize]],\n shapesClasses[resolvedShape],\n block && 'w-full',\n colored &&\n (resolvedVariant === 'solid'\n ? [\n 'bg-(--btn-bg)',\n 'hover:bg-(--btn-bg-hover)',\n 'active:bg-(--btn-bg-active)',\n 'text-(--btn-fg)',\n 'border-(--btn-border)',\n ]\n : [\n 'text-(--btn-bg)',\n 'border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n //\n ]),\n className,\n //\n )}\n type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n data-color={computedColor}\n onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {children}\n </Comp>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;;AA2EA,MAAM,iBAAiB,GACrB,qBACA,gBACA,oDACA,mFACF;AAMA,MAAM,cAAc,GAClB,gBACA,sDACA,iBACF;AAEA,MAAM,iBAAyC;CAI7C,OAAO,GAAG,sCAAsC,qBAAqB;CACrE,UAAU,GACR,yEACA,iCACA,iBACF;CACA,QAAQ,GACN,uFACA,iCACA,iBACF;CACA,QAAQ,GAAG,4BAA4B,mBAAmB;CAC1D,MAAM,GAAG,kCAAkC,iBAAiB;CAC5D,MAAM,GACJ,+BACA,sCACA,qBACF;AACF;;;;;;;;AASA,MAAM,qBAGF;CACF,SAAS;EAAE,OAAO;EAAW,SAAS;CAAQ;CAC9C,SAAS;EAAE,OAAO;EAAW,SAAS;CAAW;CACjD,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAS;CAC9C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;CAC1C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;AAC5C;AAEA,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,UAAU,EACd,MACA,WACA,MACA,SACA,WAAW,UACX,MACA,OACA,OACA,QAAQ,OACR,UACA,SACA,QACA,UAAU,OACV,UACA,aACA,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,iBAAiB,UAAU;CAClD,MAAM,iBAAiB,cAAc;CAErC,MAAM,eAAe,QAAQ,iBAAiB;CAC9C,MAAM,gBAAgB,SAAS,gBAAgB,SAAS;CACxD,MAAM,WAAW,QAAQ,CAAC;CAO1B,MAAM,eAAe,OAAO,mBAAmB,QAAQ,KAAA;CACvD,MAAM,kBACJ,WAAW,cAAc,WAAW,gBAAgB,WAAW;CACjE,MAAM,gBAAgB,SAClB,WACC,SAAS,cAAc,SAAS,gBAAgB,SAAS;CAC9D,MAAM,UAAU,kBAAkB;CAClC,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,OAAO,UAAU,OAAO;CAE9B,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,cAAD,EAAc,WAAU,eAAgB,CAAA,IAG1C;CAGF,OACE,qBAAC,MAAD;EACE,aAAU;EACV,WAAW,GACT,aACA,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,eACb,YAAY,CAAC,OAAO,YAAY,aAAa,GAC7C,cAAc,gBACd,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;EACF,IACA,CACE,mBACA,gEAEF,IACN,SAEF;EACA,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,cAAY;EACZ,cAAa,MAAK;GAChB,cAAc,CAAC;EACjB;EACA,GAAI;EAtCN,UAAA,CAwCG,aACA,QACG;;AAEV"}
1
+ {"version":3,"file":"Button.mjs","names":["button.Button"],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slottable } from '@radix-ui/react-slot';\nimport { LoaderCircle } from 'lucide-react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { button } from '../../core';\nimport { type PresetColor } from '../../lib/colors';\n\nconst Core = button.Button;\n\n/**\n * shadcn's own `variant` words, accepted on top of this component's own\n * vocabulary rather than replacing it. They are sugar in exactly the sense\n * `type` is: each expands to a `(color, variant)` pair, so a snippet copied\n * from the shadcn docs renders as the nearest thing on this library's scale\n * instead of erroring.\n *\n * `link` is deliberately absent: it already exists as one of this component's\n * own variants and already renders what shadcn's `link` renders, so mapping it\n * would change `variant=\"link\"`'s resolved colour from `default` to `primary`\n * for existing callers.\n */\ntype ShadcnVariant =\n 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost';\n\n/**\n * shadcn's `size` words. The `icon*` entries additionally force the icon-only\n * (square, unpadded) treatment, which this component otherwise infers from\n * `icon` being set with no children — without that, the shadcn idiom of\n * passing the icon as *children* (`<Button size=\"icon\"><Trash /></Button>`)\n * would render a normal padded button.\n */\ntype ShadcnSize =\n 'default' | 'xs' | 'sm' | 'lg' | 'icon' | 'icon-xs' | 'icon-sm' | 'icon-lg';\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof Core>,\n 'size' | 'variant' | 'type'\n> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n /**\n * Size on this library's antd-style scale. shadcn's words are accepted too\n * and map onto that scale: `sm`/`xs` → `small`, `default` → `middle`,\n * `lg` → `large`. There is no separate `xs` step here, so `xs` renders as\n * `small`. The `icon*` entries pick the same sizes and additionally force\n * the icon-only treatment.\n */\n size?: 'small' | 'middle' | 'large' | ShadcnSize;\n /**\n * Syntactic sugar for a `(color, variant)` pair. **Will follow `variant` and\n * `color` if provided** — `type` only fills in the axes you leave unset.\n *\n * | `type` | equivalent to |\n * | --------- | -------------------------------------- |\n * | `primary` | `color=\"primary\" variant=\"solid\"` |\n * | `default` | `color=\"default\" variant=\"outlined\"` |\n * | `dashed` | `color=\"default\" variant=\"dashed\"` |\n * | `text` | `color=\"default\" variant=\"text\"` |\n * | `link` | `color=\"primary\" variant=\"link\"` |\n *\n * So `<Button type=\"primary\">` is the same as\n * `<Button color=\"primary\" variant=\"solid\">`, and\n * `<Button type=\"primary\" variant=\"filled\">` keeps the primary color but\n * renders filled. `danger` still overrides the resolved color.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\n /**\n * Visual fill style, independent of `color`. Defaults to `'outlined'`.\n * Takes precedence over whatever `type` would have supplied.\n *\n * shadcn's words are accepted here as well and expand to a `(color, variant)`\n * pair the same way `type` does — `destructive` ≡ `color=\"danger\"\n * variant=\"solid\"`, `outline` ≡ `outlined`, `secondary` ≡ `filled`,\n * `ghost` ≡ `text`, `default` ≡ `color=\"primary\" variant=\"solid\"`. An\n * explicit `color` still wins over the one the word implies.\n *\n * ⚠️ `variant=\"default\"` (shadcn: a filled primary button) and\n * `type=\"default\"` (antd: a plain outlined button) mean opposite things.\n * They are different props, so nothing collides at runtime, but don't read\n * one as the other.\n */\n variant?:\n | 'solid'\n | 'outlined'\n | 'dashed'\n | 'filled'\n | 'text'\n | 'link'\n | ShadcnVariant;\n /**\n * Native `<button>` `type` (`button`/`submit`/`reset`), kept separate\n * from the antd-style `type` above. Defaults to `'button'` so a Button\n * placed inside a `<form>` doesn't submit it unless explicitly opted in.\n */\n htmlType?: 'button' | 'submit' | 'reset';\n shape?: 'default' | 'circle' | 'round';\n /**\n * Color, independent of `variant`. Defaults to `'default'`, or to whatever\n * `type` maps to when `type` is set. `danger` overrides this.\n *\n * Preset hues use Tailwind's colour names (#342).\n */\n color?: PresetColor | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\n// Geometry, the focus/aria/disabled chassis and the svg sizing come from\n// `core/button` again (#278 ③ is reversed here — see the core membership rule\n// in CLAUDE.md). Unlike Tag, Button pins no core variant at all: it passes\n// `variant={null} size={null}`, which cva treats as \"skip this axis entirely\",\n// so the primitive contributes only its base string and the atom keeps full\n// ownership of every colour and size class below. That leaves nothing for the\n// `variant=\"…\"` smell check to flag.\n//\n// The base still carries `rounded-md` and `text-sm`; the `rounded-*` from\n// `shapesClasses` and the `text-*` from `sizesClasses` come later in the class\n// list and override them, exactly as they did when this was absorbed.\nconst BUTTON_BASE = cn(\n 'gap-2 whitespace-nowrap font-medium transition-all',\n 'has-[>svg]:px-3',\n);\n\nconst variantClasses: Record<string, string> = {\n // solid used to inherit its fill from core/button's default variant; carry it\n // explicitly now that the primitive is absorbed. A colored solid overrides\n // this with the --btn-* custom properties further down.\n solid: cn('bg-primary text-primary-foreground', 'hover:bg-primary/90'),\n outlined: cn(\n 'border border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n dashed: cn(\n 'border border-dashed border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n 'bg-background text-foreground',\n 'hover:bg-accent',\n ),\n filled: cn('bg-muted text-foreground', 'hover:bg-muted/80'),\n text: cn('bg-transparent text-foreground', 'hover:bg-accent'),\n link: cn(\n 'bg-transparent text-primary',\n 'underline-offset-4 hover:underline',\n 'hover:bg-primary/10',\n ),\n};\n\n/**\n * antd-style syntactic sugar: each `type` expands to a `(color, variant)` pair.\n * Explicit `color`/`variant` props win over whatever the `type` maps to, so\n * `type` only fills in the axes the caller left unset.\n *\n * Mirrors antd: `<Button type=\"primary\">` ≡ `<Button color=\"primary\" variant=\"solid\">`.\n */\nconst typeToColorVariant: Record<\n NonNullable<Props['type']>,\n { color: NonNullable<Props['color']>; variant: NonNullable<Props['variant']> }\n> = {\n primary: { color: 'primary', variant: 'solid' },\n default: { color: 'default', variant: 'outlined' },\n dashed: { color: 'default', variant: 'dashed' },\n text: { color: 'default', variant: 'text' },\n link: { color: 'primary', variant: 'link' },\n};\n\n/** The half of `variant` that is this component's own vocabulary. */\ntype OwnVariant = Exclude<NonNullable<Props['variant']>, ShadcnVariant>;\n\ntype OwnSize = 'small' | 'middle' | 'large';\n\n/**\n * shadcn's variant words → the `(color, variant)` pair each stands for here.\n * Same contract as `typeToColorVariant` above: the word only fills axes the\n * caller left unset, so an explicit `color` still wins over the implied one.\n */\nconst shadcnVariantToColorVariant: Record<\n ShadcnVariant,\n { color: NonNullable<Props['color']>; variant: OwnVariant }\n> = {\n default: { color: 'primary', variant: 'solid' },\n destructive: { color: 'danger', variant: 'solid' },\n outline: { color: 'default', variant: 'outlined' },\n secondary: { color: 'default', variant: 'filled' },\n ghost: { color: 'default', variant: 'text' },\n};\n\n/** shadcn's size words → this library's scale. */\nconst shadcnSizeToSize: Record<ShadcnSize, OwnSize> = {\n default: 'middle',\n xs: 'small',\n sm: 'small',\n lg: 'large',\n icon: 'middle',\n 'icon-xs': 'small',\n 'icon-sm': 'small',\n 'icon-lg': 'large',\n};\n\n/** The `size` words that also mean \"render this icon-only\". */\nconst ICON_SIZES = new Set<string>(['icon', 'icon-xs', 'icon-sm', 'icon-lg']);\n\nconst sizesClasses: Record<string, string> = {\n small: 'h-6 px-3 text-sm',\n middle: 'h-8 px-4 text-base',\n large: 'h-10 px-5 text-lg',\n};\n\nconst iconClasses: Record<string, string> = {\n small: `size-6 [&_svg:not([class*='size-'])]:size-3`,\n middle: `size-8 [&_svg:not([class*='size-'])]:size-4`,\n large: `size-10 [&_svg:not([class*='size-'])]:size-5`,\n};\n\nconst shapesClasses = {\n default: 'rounded-sm',\n circle: 'rounded-full',\n round: 'rounded-2xl',\n};\n\nconst Button = ({\n icon,\n className,\n type,\n variant,\n htmlType = 'button',\n size,\n color,\n shape,\n block = false,\n disabled,\n loading,\n danger,\n asChild = false,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const { componentSize, defaultProps } = useConfig();\n const buttonDefaults = defaultProps?.button as\n Partial<Pick<Props, 'shape' | 'variant' | 'color'>> | undefined;\n const resolvedShape = shape ?? buttonDefaults?.shape ?? 'default';\n // Both `variant` and `size` accept this library's own words *and* shadcn's.\n // Translate the shadcn ones first so everything below reasons in one\n // vocabulary; a word this library already owns (`link`, `text`, `small`, …)\n // is passed through untouched.\n const variantAlias =\n variant && variant in shadcnVariantToColorVariant\n ? shadcnVariantToColorVariant[variant as ShadcnVariant]\n : undefined;\n const ownVariant =\n variantAlias?.variant ?? (variant as OwnVariant | undefined);\n const ownSize =\n size && size in shadcnSizeToSize\n ? shadcnSizeToSize[size as ShadcnSize]\n : (size as OwnSize | undefined);\n const resolvedSize = ownSize ?? componentSize ?? 'middle';\n // A shadcn `size=\"icon*\"` says \"icon-only\" explicitly; otherwise it is\n // inferred from an `icon` with no children, which is how this component has\n // always detected it.\n const iconOnly =\n (size != null && ICON_SIZES.has(size)) || !!(icon && !children);\n // `type` is syntactic sugar that expands to a (color, variant) pair; explicit\n // `color`/`variant` win over it, so `type` only fills the axes left unset.\n // A shadcn `variant` word carries an implied colour and sits between the two:\n // more specific than `type`, less than an explicit `color`.\n // A `Config` default sits below the call-site `type` (which is more specific)\n // but above the library built-in. `danger` still trumps everything, matching\n // antd. Defaults live here rather than on the parameters so `type` gets a\n // chance to supply them first.\n const typeDefaults = type ? typeToColorVariant[type] : undefined;\n const resolvedVariant =\n ownVariant ??\n typeDefaults?.variant ??\n buttonDefaults?.variant ??\n 'outlined';\n const computedColor = danger\n ? 'danger'\n : (color ??\n variantAlias?.color ??\n typeDefaults?.color ??\n buttonDefaults?.color ??\n 'default');\n const colored = computedColor !== 'default';\n const isLoading = !!loading;\n\n const displayIcon = loading ? (\n typeof loading === 'object' ? (\n loading.icon\n ) : (\n <LoaderCircle className=\"animate-spin\" />\n )\n ) : (\n icon\n );\n\n return (\n <Core\n // cva skips an axis entirely when it is passed `null`, so the primitive\n // contributes only its base string and this atom keeps ownership of\n // every colour/size class.\n variant={null}\n size={null}\n asChild={asChild}\n data-slot=\"button\"\n data-variant={resolvedVariant}\n data-size={resolvedSize}\n className={cn(\n BUTTON_BASE,\n 'inline-flex items-center justify-center gap-x-2',\n 'rounded-lg',\n 'cursor-pointer',\n 'h-auto py-0',\n 'transition-all',\n variantClasses[resolvedVariant],\n sizesClasses[resolvedSize],\n iconOnly && ['p-0', iconClasses[resolvedSize]],\n shapesClasses[resolvedShape],\n block && 'w-full',\n colored &&\n (resolvedVariant === 'solid'\n ? [\n 'bg-(--btn-bg)',\n 'hover:bg-(--btn-bg-hover)',\n 'active:bg-(--btn-bg-active)',\n 'text-(--btn-fg)',\n 'border-(--btn-border)',\n ]\n : [\n 'text-(--btn-bg)',\n 'border-[color-mix(in_oklch,var(--btn-border),transparent_50%)]',\n //\n ]),\n className,\n //\n )}\n type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n data-color={computedColor}\n onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {/*\n A Button renders two children (the icon slot and the caller's content),\n but Radix's Slot accepts multiple children only when exactly one is\n marked `Slottable` — without this marker `asChild` threw\n \"Slot failed to slot onto its children\" for *every* call, icon or not.\n `Slottable` is a plain fragment when `asChild` is false, so the\n non-asChild markup is unchanged.\n */}\n <Slottable>{children}</Slottable>\n </Core>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;;AAaA,MAAM,OAAOA;AAgHb,MAAM,cAAc,GAClB,sDACA,iBACF;AAEA,MAAM,iBAAyC;CAI7C,OAAO,GAAG,sCAAsC,qBAAqB;CACrE,UAAU,GACR,yEACA,iCACA,iBACF;CACA,QAAQ,GACN,uFACA,iCACA,iBACF;CACA,QAAQ,GAAG,4BAA4B,mBAAmB;CAC1D,MAAM,GAAG,kCAAkC,iBAAiB;CAC5D,MAAM,GACJ,+BACA,sCACA,qBACF;AACF;;;;;;;;AASA,MAAM,qBAGF;CACF,SAAS;EAAE,OAAO;EAAW,SAAS;CAAQ;CAC9C,SAAS;EAAE,OAAO;EAAW,SAAS;CAAW;CACjD,QAAQ;EAAE,OAAO;EAAW,SAAS;CAAS;CAC9C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;CAC1C,MAAM;EAAE,OAAO;EAAW,SAAS;CAAO;AAC5C;;;;;;AAYA,MAAM,8BAGF;CACF,SAAS;EAAE,OAAO;EAAW,SAAS;CAAQ;CAC9C,aAAa;EAAE,OAAO;EAAU,SAAS;CAAQ;CACjD,SAAS;EAAE,OAAO;EAAW,SAAS;CAAW;CACjD,WAAW;EAAE,OAAO;EAAW,SAAS;CAAS;CACjD,OAAO;EAAE,OAAO;EAAW,SAAS;CAAO;AAC7C;;AAGA,MAAM,mBAAgD;CACpD,SAAS;CACT,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM;CACN,WAAW;CACX,WAAW;CACX,WAAW;AACb;;AAGA,MAAM,6BAAa,IAAI,IAAY;CAAC;CAAQ;CAAW;CAAW;AAAS,CAAC;AAE5E,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;AACT;AAEA,MAAM,UAAU,EACd,MACA,WACA,MACA,SACA,WAAW,UACX,MACA,OACA,OACA,QAAQ,OACR,UACA,SACA,QACA,UAAU,OACV,UACA,aACA,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,iBAAiB,UAAU;CAClD,MAAM,iBAAiB,cAAc;CAErC,MAAM,gBAAgB,SAAS,gBAAgB,SAAS;CAKxD,MAAM,eACJ,WAAW,WAAW,8BAClB,4BAA4B,WAC5B,KAAA;CACN,MAAM,aACJ,cAAc,WAAY;CAK5B,MAAM,gBAHJ,QAAQ,QAAQ,mBACZ,iBAAiB,QAChB,SACyB,iBAAiB;CAIjD,MAAM,WACH,QAAQ,QAAQ,WAAW,IAAI,IAAI,KAAM,CAAC,EAAE,QAAQ,CAAC;CASxD,MAAM,eAAe,OAAO,mBAAmB,QAAQ,KAAA;CACvD,MAAM,kBACJ,cACA,cAAc,WACd,gBAAgB,WAChB;CACF,MAAM,gBAAgB,SAClB,WACC,SACD,cAAc,SACd,cAAc,SACd,gBAAgB,SAChB;CACJ,MAAM,UAAU,kBAAkB;CAClC,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,cAAD,EAAc,WAAU,eAAgB,CAAA,IAG1C;CAGF,OACE,qBAAC,MAAD;EAIE,SAAS;EACT,MAAM;EACG;EACT,aAAU;EACV,gBAAc;EACd,aAAW;EACX,WAAW,GACT,aACA,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,eACb,YAAY,CAAC,OAAO,YAAY,aAAa,GAC7C,cAAc,gBACd,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;EACF,IACA,CACE,mBACA,gEAEF,IACN,SAEF;EACA,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,cAAY;EACZ,cAAa,MAAK;GAChB,cAAc,CAAC;EACjB;EACA,GAAI;EA9CN,UAAA,CAgDG,aASD,oBAAC,WAAD,EAAY,SAAoB,CAAA,CAC5B;;AAEV"}
package/dist/Tag.d.mts CHANGED
@@ -1,10 +1,10 @@
1
- import { DeprecatedPresetColor, PresetColor } from "./lib/colors.mjs";
1
+ import { Badge } from "./core/badge.mjs";
2
+ import "./core/index.mjs";
3
+ import { PresetColor } from "./lib/colors.mjs";
2
4
  import * as React$1 from "react";
3
5
  //#region src/components/atoms/tag.d.ts
4
- type NativeSpanProps = React$1.ComponentProps<'span'> & {
5
- asChild?: boolean;
6
- };
7
- interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {
6
+ declare const Core: typeof Badge;
7
+ interface Props extends Omit<React$1.ComponentProps<typeof Core>, 'variant' | 'color'> {
8
8
  /**
9
9
  * Fill style, aligned with `Button`'s vocabulary (#320). The pre-7.0
10
10
  * `'default'` spelling was removed — use `'filled'`, which renders
@@ -20,14 +20,11 @@ interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {
20
20
  * `success`/`warning` have no `Button` counterpart on purpose: a Tag marks
21
21
  * state, which is why antd's Tag also carries them while its Button does not.
22
22
  *
23
- * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings
24
- * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but
25
- * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no
26
- * successor; it is `orange` one lightness step darker).
23
+ * Preset hues use Tailwind's colour names (#342).
27
24
  */
28
- color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | PresetColor | DeprecatedPresetColor;
25
+ color?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | PresetColor;
29
26
  }
30
- declare const Tag: ({ className, variant, color, asChild, children, ...props }: Props) => React$1.JSX.Element;
27
+ declare const Tag: ({ className, variant, color, children, ...props }: Props) => React$1.JSX.Element;
31
28
  //#endregion
32
29
  export { Props, Tag as default };
33
30
  //# sourceMappingURL=Tag.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tag.d.mts","names":[],"sources":["../src/components/atoms/tag.tsx"],"mappings":";;;KAYK,kBAAkB,QAAM;EAC3B;;UAGe,cAAc,KAAK;;;;;;EAMlC;;;;;;;;;;;;;;;EAeA,mEAMI,cACA;;cA+BA,QAAO,WAAA,SAAA,OAAA,SAAA,aAAA,SAOV,UAAK,QAAA,IAAA"}
1
+ {"version":3,"file":"Tag.d.mts","names":[],"sources":["../src/components/atoms/tag.tsx"],"mappings":";;;;;cAUM,aAAI;UAEO,cAAc,KAC7B,QAAM,sBAAsB;;;;;;EAQ5B;;;;;;;;;;;;EAYA,mEAC6D;;cA0BzD,QAAO,WAAA,SAAA,OAAA,aAAA,SAAmD,UAAK,QAAA,IAAA"}
package/dist/Tag.mjs CHANGED
@@ -1,24 +1,25 @@
1
1
  "use client";
2
2
  import { cn } from "./utils.mjs";
3
3
  import { useConfig } from "./providers/config/context.mjs";
4
- import { INTERACTIVE_CHASSIS } from "./lib/chassis.mjs";
4
+ import { Badge } from "./core/badge.mjs";
5
5
  import "react";
6
- import { Slot } from "@radix-ui/react-slot";
7
6
  import { jsx } from "react/jsx-runtime";
8
7
  //#region src/components/atoms/tag.tsx
9
- const TAG_BASE = cn(INTERACTIVE_CHASSIS, "border w-fit whitespace-nowrap overflow-hidden", "text-xs font-medium gap-1 transition-[color,box-shadow]", "[&>svg]:size-3 [&>svg]:pointer-events-none", "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground");
8
+ const Core = Badge;
9
+ const CORE_VARIANT = "outline";
10
10
  const fillClasses = cn("border-transparent bg-(--tag-tint) text-(--tag-fg)");
11
11
  const outlinedClasses = cn("border-(--tag-bg) text-(--tag-fg)");
12
- const Tag = ({ className, variant, color, asChild = false, children, ...props }) => {
12
+ const Tag = ({ className, variant, color, children, ...props }) => {
13
13
  const { defaultProps } = useConfig();
14
14
  const tagDefaults = defaultProps?.tag;
15
15
  const resolvedVariant = variant ?? tagDefaults?.variant ?? "filled";
16
16
  const resolvedColor = color ?? tagDefaults?.color ?? "default";
17
- return /* @__PURE__ */ jsx(asChild ? Slot : "span", {
18
- "data-slot": "badge",
17
+ return /* @__PURE__ */ jsx(Core, {
18
+ variant: CORE_VARIANT,
19
+ "data-slot": "tag",
19
20
  "data-color": resolvedColor,
20
21
  "data-variant": resolvedVariant,
21
- className: cn(TAG_BASE, "rounded-full px-2.5 py-1", "text-xs font-medium", resolvedVariant === "outlined" ? outlinedClasses : fillClasses, className),
22
+ className: cn("rounded-full px-2.5 py-1", resolvedVariant === "outlined" ? outlinedClasses : fillClasses, className),
22
23
  ...props,
23
24
  children
24
25
  });
package/dist/Tag.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Tag.mjs","names":[],"sources":["../src/components/atoms/tag.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { INTERACTIVE_CHASSIS } from '../../lib/chassis';\nimport { type DeprecatedPresetColor, type PresetColor } from '../../lib/colors';\n\ntype NativeSpanProps = React.ComponentProps<'span'> & {\n asChild?: boolean;\n};\n\nexport interface Props extends Omit<NativeSpanProps, 'variant' | 'color'> {\n /**\n * Fill style, aligned with `Button`'s vocabulary (#320). The pre-7.0\n * `'default'` spelling was removed — use `'filled'`, which renders\n * identically.\n */\n variant?: 'filled' | 'outlined';\n /**\n * Colour. Every value — the semantic states (`primary`/`success`/`warning`/\n * `danger`) as well as the palette shared with `Button` (`blue`, `red`,\n * `amber`, …) — resolves through the same `data-color` + `--tag-*` system\n * (#320), so any of them can be re-themed the same way.\n *\n * `success`/`warning` have no `Button` counterpart on purpose: a Tag marks\n * state, which is why antd's Tag also carries them while its Button does not.\n *\n * Preset hues use Tailwind's colour names (#342). The pre-8.0 spellings\n * `magenta`/`geekblue`/`gold`/`volcano` still work and render unchanged, but\n * are deprecated — use `fuchsia`/`indigo`/`amber` instead (`volcano` has no\n * successor; it is `orange` one lightness step darker).\n */\n color?:\n | 'default'\n | 'primary'\n | 'success'\n | 'warning'\n | 'danger'\n | PresetColor\n | DeprecatedPresetColor;\n}\n\n// Base absorbed from core/badge's cva base + its `outline` variant, which Tag\n// always rendered as (#278 ③ / #294 Phase 4). The shared focus/aria/centering\n// set lives in INTERACTIVE_CHASSIS; a Tag adds badge geometry and its svg\n// sizing but — unlike Button — no `disabled:*` (a Tag is not disableable). The\n// rounded/padding leftovers are overridden by the atom classes below exactly as\n// before, so the rendered output is unchanged (verified against a pre-refactor\n// render matrix).\nconst TAG_BASE = cn(\n INTERACTIVE_CHASSIS,\n 'border w-fit whitespace-nowrap overflow-hidden',\n 'text-xs font-medium gap-1 transition-[color,box-shadow]',\n '[&>svg]:size-3 [&>svg]:pointer-events-none',\n 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',\n);\n\n// Every colour — semantic states and shared presets alike — resolves from the\n// `--tag-*` custom properties that `globals.css` sets per `data-color` (#320).\n// Before 7.0 only the presets took this path while the states carried bespoke\n// Tailwind classes, which meant `[data-color]` hooks and `--tag-*` overrides\n// worked on some colours but silently not on others.\n//\n// --tag-bg the hue; the border in `outlined`\n// --tag-fg text colour (defaults to --tag-bg)\n// --tag-tint the fill behind `filled` (defaults to a 10% --tag-bg wash)\nconst fillClasses = cn('border-transparent bg-(--tag-tint) text-(--tag-fg)');\n\nconst outlinedClasses = cn('border-(--tag-bg) text-(--tag-fg)');\n\nconst Tag = ({\n className,\n variant,\n color,\n asChild = false,\n children,\n ...props\n}: Props) => {\n const { defaultProps } = useConfig();\n const tagDefaults = defaultProps?.tag as\n Partial<Pick<Props, 'variant' | 'color'>> | undefined;\n // An explicit prop wins over a `Config` default, which wins over the built-in\n // — the same resolution order `Button` uses.\n const resolvedVariant = variant ?? tagDefaults?.variant ?? 'filled';\n const resolvedColor = color ?? tagDefaults?.color ?? 'default';\n const isOutlined = resolvedVariant === 'outlined';\n const Comp = asChild ? Slot : 'span';\n\n return (\n <Comp\n data-slot=\"badge\"\n // Emitted for every colour, not just the presets, so consumers can hook\n // `[data-color]` / `[data-variant]` uniformly. `data-variant` is what\n // lets `globals.css` give `default` a different text colour when\n // outlined without reintroducing a second class path here.\n data-color={resolvedColor}\n data-variant={resolvedVariant}\n className={cn(\n TAG_BASE,\n 'rounded-full px-2.5 py-1',\n 'text-xs font-medium',\n isOutlined ? outlinedClasses : fillClasses,\n className,\n //\n )}\n {...props}\n >\n {children}\n </Comp>\n );\n};\n\nexport default Tag;\n"],"mappings":";;;;;;;;AAsDA,MAAM,WAAW,GACf,qBACA,kDACA,2DACA,8CACA,wEACF;AAWA,MAAM,cAAc,GAAG,oDAAoD;AAE3E,MAAM,kBAAkB,GAAG,mCAAmC;AAE9D,MAAM,OAAO,EACX,WACA,SACA,OACA,UAAU,OACV,UACA,GAAG,YACQ;CACX,MAAM,EAAE,iBAAiB,UAAU;CACnC,MAAM,cAAc,cAAc;CAIlC,MAAM,kBAAkB,WAAW,aAAa,WAAW;CAC3D,MAAM,gBAAgB,SAAS,aAAa,SAAS;CAIrD,OACE,oBAHW,UAAU,OAAO,QAG5B;EACE,aAAU;EAKV,cAAY;EACZ,gBAAc;EACd,WAAW,GACT,UACA,4BACA,uBAfa,oBAAoB,aAgBpB,kBAAkB,aAC/B,SAEF;EACA,GAAI;EAEH;CACG,CAAA;AAEV"}
1
+ {"version":3,"file":"Tag.mjs","names":["badge.Badge"],"sources":["../src/components/atoms/tag.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { badge } from '../../core';\nimport { type PresetColor } from '../../lib/colors';\n\nconst Core = badge.Badge;\n\nexport interface Props extends Omit<\n React.ComponentProps<typeof Core>,\n 'variant' | 'color'\n> {\n /**\n * Fill style, aligned with `Button`'s vocabulary (#320). The pre-7.0\n * `'default'` spelling was removed — use `'filled'`, which renders\n * identically.\n */\n variant?: 'filled' | 'outlined';\n /**\n * Colour. Every value — the semantic states (`primary`/`success`/`warning`/\n * `danger`) as well as the palette shared with `Button` (`blue`, `red`,\n * `amber`, …) — resolves through the same `data-color` + `--tag-*` system\n * (#320), so any of them can be re-themed the same way.\n *\n * `success`/`warning` have no `Button` counterpart on purpose: a Tag marks\n * state, which is why antd's Tag also carries them while its Button does not.\n *\n * Preset hues use Tailwind's colour names (#342).\n */\n color?:\n 'default' | 'primary' | 'success' | 'warning' | 'danger' | PresetColor;\n}\n\n// Geometry, the focus/aria chassis and the svg sizing all come from\n// `core/badge` now (#278 ③ is reversed here — see the core membership rule in\n// CLAUDE.md). Tag pins the core `outline` variant because it supplies exactly\n// the neutral base Tag wants — a border plus `text-foreground` and the `[a&]`\n// hover treatment — and then repaints border/text/fill from the `--tag-*`\n// custom properties below. Pinning one core variant is deliberate and is why\n// the membership rule had to change; the alternative (absorbing the primitive)\n// is what left `core/badge` orphaned and three upstream releases stale.\nconst CORE_VARIANT = 'outline';\n\n// Every colour — semantic states and shared presets alike — resolves from the\n// `--tag-*` custom properties that `globals.css` sets per `data-color` (#320).\n// Before 7.0 only the presets took this path while the states carried bespoke\n// Tailwind classes, which meant `[data-color]` hooks and `--tag-*` overrides\n// worked on some colours but silently not on others.\n//\n// --tag-bg the hue; the border in `outlined`\n// --tag-fg text colour (defaults to --tag-bg)\n// --tag-tint the fill behind `filled` (defaults to a 10% --tag-bg wash)\nconst fillClasses = cn('border-transparent bg-(--tag-tint) text-(--tag-fg)');\n\nconst outlinedClasses = cn('border-(--tag-bg) text-(--tag-fg)');\n\nconst Tag = ({ className, variant, color, children, ...props }: Props) => {\n const { defaultProps } = useConfig();\n const tagDefaults = defaultProps?.tag as\n Partial<Pick<Props, 'variant' | 'color'>> | undefined;\n // An explicit prop wins over a `Config` default, which wins over the built-in\n // — the same resolution order `Button` uses.\n const resolvedVariant = variant ?? tagDefaults?.variant ?? 'filled';\n const resolvedColor = color ?? tagDefaults?.color ?? 'default';\n const isOutlined = resolvedVariant === 'outlined';\n\n return (\n <Core\n variant={CORE_VARIANT}\n // Overrides the core primitive's own `data-slot=\"badge\"`. The slot now\n // matches the public component name, which frees `badge` for the\n // antd-style Badge (a count/dot decorator, a different component from\n // this chip — shadcn's Badge is what antd calls a Tag).\n data-slot=\"tag\"\n // Emitted for every colour, not just the presets, so consumers can hook\n // `[data-color]` / `[data-variant]` uniformly. `data-variant` is what\n // lets `globals.css` give `default` a different text colour when\n // outlined without reintroducing a second class path here; it also\n // replaces the core primitive's own `data-variant`, which would\n // otherwise report the pinned core variant rather than Tag's.\n data-color={resolvedColor}\n data-variant={resolvedVariant}\n className={cn(\n 'rounded-full px-2.5 py-1',\n isOutlined ? outlinedClasses : fillClasses,\n className,\n //\n )}\n {...props}\n >\n {children}\n </Core>\n );\n};\n\nexport default Tag;\n"],"mappings":";;;;;;;AAUA,MAAM,OAAOA;AAmCb,MAAM,eAAe;AAWrB,MAAM,cAAc,GAAG,oDAAoD;AAE3E,MAAM,kBAAkB,GAAG,mCAAmC;AAE9D,MAAM,OAAO,EAAE,WAAW,SAAS,OAAO,UAAU,GAAG,YAAmB;CACxE,MAAM,EAAE,iBAAiB,UAAU;CACnC,MAAM,cAAc,cAAc;CAIlC,MAAM,kBAAkB,WAAW,aAAa,WAAW;CAC3D,MAAM,gBAAgB,SAAS,aAAa,SAAS;CAGrD,OACE,oBAAC,MAAD;EACE,SAAS;EAKT,aAAU;EAOV,cAAY;EACZ,gBAAc;EACd,WAAW,GACT,4BAnBa,oBAAoB,aAoBpB,kBAAkB,aAC/B,SAEF;EACA,GAAI;EAEH;CACG,CAAA;AAEV"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils.mjs";
3
- import Button from "../../Button.mjs";
4
3
  import { calendar_exports } from "../../core/calendar.mjs";
4
+ import Button from "../../Button.mjs";
5
5
  import Popover from "./popover.mjs";
6
6
  import { useState } from "react";
7
7
  import { CalendarIcon } from "lucide-react";
@@ -1,6 +1,6 @@
1
1
  import { cn } from "../../../utils.mjs";
2
- import Button from "../../../Button.mjs";
3
2
  import { OVERLAY_LAYER } from "../../../lib/z-layers.mjs";
3
+ import Button from "../../../Button.mjs";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/atoms/float-button/float-button.tsx
6
6
  const FloatButton = ({ className, children, ...props }) => {
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../../providers/config/context.mjs";
4
- import Button from "../../../Button.mjs";
5
4
  import { input_exports } from "../../../core/input.mjs";
5
+ import Button from "../../../Button.mjs";
6
6
  import { useRef, useState } from "react";
7
7
  import { CircleX, Search } from "lucide-react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils.mjs";
3
- import Button from "../../../Button.mjs";
4
3
  import { radio_group_exports } from "../../../core/radio-group.mjs";
4
+ import Button from "../../../Button.mjs";
5
5
  import { normalizeOptions, warnOnDuplicateOptionValues } from "../option-group.mjs";
6
6
  import { RadioGroupContext } from "./context.mjs";
7
7
  import Radio from "./radio.mjs";
@@ -1,6 +1,6 @@
1
- import { OptionValue } from "../option-group.mjs";
2
1
  import { RadioGroup } from "../../../core/radio-group.mjs";
3
2
  import "../../../core/index.mjs";
3
+ import { OptionValue } from "../option-group.mjs";
4
4
  //#region src/components/atoms/radio/radio.d.ts
5
5
  declare const Core: typeof RadioGroup;
6
6
  interface Props extends Omit<React.ComponentPropsWithoutRef<typeof Core>, 'onChange' | 'value'> {
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
- import Button from "../../Button.mjs";
5
4
  import { drawer_exports } from "../../core/drawer.mjs";
5
+ import Button from "../../Button.mjs";
6
6
  import { useEffect } from "react";
7
7
  import { X } from "lucide-react";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -30,12 +30,11 @@ type ModalStatus = 'info' | 'success' | 'error' | 'warning';
30
30
  interface StaticProps extends Props {
31
31
  status?: ModalStatus;
32
32
  /**
33
- * @deprecated Use `status` for the semantic state. Kept as an alias so
34
- * existing `type="info|success|error|warning"` calls keep working. `confirm`
35
- * is an interaction mode, not a status — call `Modal.confirm()` instead
36
- * (still accepted here as `type="confirm"` for back-compat).
33
+ * Interaction mode set by `Modal.confirm()` to request the two-button
34
+ * footer. `confirm` is a mode, not a status, so it rides its own axis rather
35
+ * than `status` — call `Modal.confirm()` rather than setting this directly.
37
36
  */
38
- type?: ModalStatus | 'confirm';
37
+ mode?: 'confirm';
39
38
  id?: string;
40
39
  icon?: React.ReactNode;
41
40
  container?: HTMLElement;
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS;;;;;;;EAOT,OAAO;EACP;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAwNa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
1
+ {"version":3,"file":"modal.d.mts","names":[],"sources":["../../../src/components/organisms/modal.tsx"],"mappings":";;UAiCiB;EACf;EACA;EACA;IAGM,YAAY,MAAM;IAClB;;EAEN,YAAY,MAAM;EAClB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,SAAS,MAAM;EACf;EACA;IACE;IACA;IACA;IACA;;EAEF,QAAQ,MAAM;EACd;EACA;EACA,YAAY;EACZ,WAAW,MAAM;EACjB;EACA;;KAGG;UAEK,oBAAoB;EAC5B,SAAS;;;;;;EAMT;EACA;EACA,OAAO,MAAM;EACb,YAAY;;cAGR;KAkBH,MAAA,cAAA,UAAA,WAAA,WAAA,YAAA,OAAA,OAAA,SAAA,QAAA,WAAA,UAAA,QAAA,YAAA,MAAA,aAAA,SAAA,QAAK,MAAA,IAAA;;;EAqNa,KAAA,OAAA;EAEG,QAAA,OAAA;EAEF,MAAA,OAAA;EAEE,QAAA,OAAA;EAIA,QAAA,OAAA"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../../utils.mjs";
3
3
  import { DEFAULT_LOCALE, useConfig } from "../../providers/config/context.mjs";
4
- import Button from "../../Button.mjs";
5
4
  import { dialog_exports } from "../../core/dialog.mjs";
5
+ import Button from "../../Button.mjs";
6
6
  import { createImperativeStack, isBrowser } from "./imperative-stack.mjs";
7
7
  import { useState } from "react";
8
8
  import { Check, CircleQuestionMark, Info, OctagonAlert, OctagonX } from "lucide-react";
@@ -65,13 +65,12 @@ const STATIC_ICONS = {
65
65
  warning: /* @__PURE__ */ jsx(OctagonAlert, { className: "text-yellow-400" }),
66
66
  confirm: /* @__PURE__ */ jsx(CircleQuestionMark, {})
67
67
  };
68
- const StaticModal = ({ id, status, type, title, content, okText, cancelText, container, icon, onOk, onCancel, ...props }) => {
68
+ const StaticModal = ({ id, status, mode, title, content, okText, cancelText, container, icon, onOk, onCancel, ...props }) => {
69
69
  const { locale } = useConfig();
70
70
  const [open, setOpen] = useState(true);
71
71
  const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;
72
72
  const resolvedCancelText = cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;
73
- const isConfirm = type === "confirm";
74
- const resolvedStatus = status ?? (isConfirm ? void 0 : type);
73
+ const isConfirm = mode === "confirm";
75
74
  const closeModal = (callback) => {
76
75
  callback?.();
77
76
  setOpen(false);
@@ -108,7 +107,7 @@ const StaticModal = ({ id, status, type, title, content, okText, cancelText, con
108
107
  },
109
108
  title: /* @__PURE__ */ jsxs("p", {
110
109
  className: cn("text-center text-lg leading-normal whitespace-pre-wrap", "flex items-center justify-center gap-x-2"),
111
- children: [icon || (isConfirm ? STATIC_ICONS.confirm : resolvedStatus && STATIC_ICONS[resolvedStatus]), title]
110
+ children: [icon || (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status]), title]
112
111
  }),
113
112
  container,
114
113
  onCancel: () => closeModal(onCancel),
@@ -146,7 +145,7 @@ Modal.warning = (props) => modalStack.render({
146
145
  ...props
147
146
  });
148
147
  Modal.confirm = (props) => modalStack.render({
149
- type: "confirm",
148
+ mode: "confirm",
150
149
  ...props
151
150
  });
152
151
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * @deprecated Use `status` for the semantic state. Kept as an alias so\n * existing `type=\"info|success|error|warning\"` calls keep working. `confirm`\n * is an interaction mode, not a status — call `Modal.confirm()` instead\n * (still accepted here as `type=\"confirm\"` for back-compat).\n */\n type?: ModalStatus | 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n onPointerDownOutside={event => {\n if (!resolvedMaskClosable) {\n event.preventDefault();\n }\n }}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n <Button onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n type,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status. The\n // semantic state comes from `status`, with `type` as its deprecated alias.\n const isConfirm = type === 'confirm';\n const resolvedStatus = status ?? (isConfirm ? undefined : type);\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n <Button className=\"col-span-3\" onClick={() => closeModal(onOk)}>\n {resolvedOkText}\n </Button>\n </div>\n ) : (\n <Button\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm\n ? STATIC_ICONS.confirm\n : resolvedStatus && STATIC_ICONS[resolvedStatus])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Radix already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Radix's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides the `type` axis (the only\n// place it lives) rather than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ type: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
1
+ {"version":3,"file":"modal.mjs","names":["dialog"],"sources":["../../../src/components/organisms/modal.tsx"],"sourcesContent":["'use client';\n\nimport React, { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport {\n Check,\n CircleQuestionMark,\n Info,\n OctagonAlert,\n OctagonX,\n} from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport { dialog } from '../../core';\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\nconst {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} = dialog;\n\nexport interface Props {\n open?: boolean;\n maskClosable?: boolean;\n closable?:\n | boolean\n | {\n closeIcon?: React.ReactNode;\n disabled?: boolean;\n };\n closeIcon?: React.ReactNode;\n title?: React.ReactNode;\n content?: React.ReactNode;\n footer?: React.ReactNode;\n className?: string;\n classNames?: {\n mask?: string;\n header?: string;\n body?: string;\n footer?: string;\n };\n style?: React.CSSProperties;\n okText?: string;\n cancelText?: string;\n container?: HTMLElement;\n children?: React.ReactNode;\n onOk?: () => void;\n onCancel?: () => void;\n}\n\ntype ModalStatus = 'info' | 'success' | 'error' | 'warning';\n\ninterface StaticProps extends Props {\n status?: ModalStatus;\n /**\n * Interaction mode set by `Modal.confirm()` to request the two-button\n * footer. `confirm` is a mode, not a status, so it rides its own axis rather\n * than `status` — call `Modal.confirm()` rather than setting this directly.\n */\n mode?: 'confirm';\n id?: string;\n icon?: React.ReactNode;\n container?: HTMLElement;\n}\n\nconst Modal = ({\n open = false,\n maskClosable,\n closable = false,\n closeIcon,\n className,\n classNames,\n style,\n title,\n content,\n footer,\n container,\n children,\n okText,\n cancelText,\n onOk,\n onCancel,\n ...props\n}: Props) => {\n const { locale, defaultProps } = useConfig();\n const modalDefaults = defaultProps?.modal as\n Partial<Pick<Props, 'maskClosable'>> | undefined;\n // Explicit prop > `Config` default > built-in, matching Button/Tag.\n const resolvedMaskClosable =\n maskClosable ?? modalDefaults?.maskClosable ?? false;\n\n if (typeof window === 'undefined') {\n return null;\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(open: boolean) => {\n if (!open) {\n onCancel?.();\n }\n }}\n {...props}\n >\n <DialogContent\n className={cn(\n 'rounded-lg p-4 max-sm:max-w-[80%]',\n 'border-none outline-none',\n className,\n )}\n classNames={{\n ...classNames,\n mask: cn('bg-black/60', classNames?.mask),\n }}\n style={style}\n closable={closable}\n closeIcon={closeIcon}\n container={container}\n onPointerDownOutside={event => {\n if (!resolvedMaskClosable) {\n event.preventDefault();\n }\n }}\n >\n {/**\n * @todo [Dialog & AlertDialog] fix: can't get id correctly in shadow dom\n * @see https://github.com/radix-ui/primitives/pull/3384\n **/}\n <DialogHeader\n className={cn(\n classNames?.header,\n !title && 'hidden',\n //\n )}\n >\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription className=\"hidden\" />\n </DialogHeader>\n <div className={cn('break-all', classNames?.body)}>\n {children ?? content}\n </div>\n {footer !== null && (\n <DialogFooter\n className={cn('flex-row justify-end gap-x-2', classNames?.footer)}\n >\n {footer || (\n <>\n <Button onClick={onOk}>\n {okText ?? locale.ok ?? DEFAULT_LOCALE.ok}\n </Button>\n <Button variant=\"outlined\" onClick={onCancel}>\n {cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel}\n </Button>\n </>\n )}\n </DialogFooter>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nconst STATIC_ICONS = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n confirm: <CircleQuestionMark />,\n};\n\nconst StaticModal = ({\n id,\n status,\n mode,\n title,\n content,\n okText,\n cancelText,\n container,\n icon,\n onOk,\n onCancel,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const { locale } = useConfig();\n const [open, setOpen] = useState(true);\n const resolvedOkText = okText ?? locale.ok ?? DEFAULT_LOCALE.ok;\n const resolvedCancelText =\n cancelText ?? locale.cancel ?? DEFAULT_LOCALE.cancel;\n // `confirm` is an interaction mode (two-button footer), not a status; the\n // semantic state comes from `status`.\n const isConfirm = mode === 'confirm';\n\n const closeModal = (callback?: () => void) => {\n callback?.();\n setOpen(false);\n setTimeout(() => {\n modalStack.destroy(id);\n }, 200);\n };\n\n const footer = isConfirm ? (\n <div className=\"grid w-full grid-cols-5 gap-x-2\">\n <Button\n variant=\"outlined\"\n className=\"col-span-2\"\n onClick={() => closeModal(onCancel)}\n >\n {resolvedCancelText}\n </Button>\n <Button className=\"col-span-3\" onClick={() => closeModal(onOk)}>\n {resolvedOkText}\n </Button>\n </div>\n ) : (\n <Button\n className={cn(\n 'grow',\n //\n )}\n onClick={() => closeModal(onOk)}\n >\n {resolvedOkText}\n </Button>\n );\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <Modal\n open={open}\n footer={footer}\n className={cn(\n 'z-10000',\n 'box-content w-100',\n 'rounded-3xl p-4',\n 'gap-y-0',\n )}\n classNames={{\n mask: 'z-10000 bg-black/[.6]',\n body: cn(\n 'text-black-70 whitespace-pre-wrap text-center',\n content && 'mt-3',\n ),\n footer: 'mt-9',\n }}\n title={\n <p\n className={cn(\n 'text-center text-lg leading-normal whitespace-pre-wrap',\n 'flex items-center justify-center gap-x-2',\n //\n )}\n >\n {icon ||\n (isConfirm ? STATIC_ICONS.confirm : status && STATIC_ICONS[status])}\n {title}\n </p>\n }\n container={container}\n onCancel={() => closeModal(onCancel)}\n {...props}\n >\n {content}\n </Modal>,\n modalStack.getRootElement(container)!,\n );\n};\n\n// No `role`/`aria-modal` here — this element is only a mount point for the\n// imperative stack, not the dialog itself. Radix already renders its own\n// `role=\"dialog\"`/`aria-modal=\"true\"` on the actual `DialogContent` via\n// portal once a modal is open. Setting it here as well would (a) leave\n// `aria-modal=\"true\"` on the page permanently, even with zero modals open,\n// hiding the rest of the document from screen readers, and (b) duplicate\n// Radix's own dialog role once one is open.\nconst modalStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.style.zIndex = '10000';\n el.style.position = 'absolute';\n return el;\n },\n StackItem: StaticModal,\n });\n\nModal.destroy = modalStack.destroy;\n\nModal.destroyAll = () => {\n modalStack.destroy();\n};\n\nModal.info = (props: StaticProps) =>\n modalStack.render({ status: 'info', ...props });\nModal.success = (props: StaticProps) =>\n modalStack.render({ status: 'success', ...props });\nModal.error = (props: StaticProps) =>\n modalStack.render({ status: 'error', ...props });\nModal.warning = (props: StaticProps) =>\n modalStack.render({ status: 'warning', ...props });\n// `confirm` is a mode, not a status, so it rides its own `mode` axis rather\n// than `status`.\nModal.confirm = (props: StaticProps) =>\n modalStack.render({ mode: 'confirm', ...props });\n\nexport default Modal;\n"],"mappings":"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"}
@@ -2,12 +2,6 @@
2
2
  type ToastStatus = 'info' | 'success' | 'error' | 'warning';
3
3
  interface Props {
4
4
  status?: ToastStatus;
5
- /**
6
- * @deprecated Use `status`. Kept as an alias so existing
7
- * `type="info|success|error|warning"` usage keeps working; `status` matches
8
- * the shared semantic-state vocabulary used by `Result`.
9
- */
10
- type?: ToastStatus;
11
5
  title: React.ReactNode;
12
6
  description?: React.ReactNode;
13
7
  icon?: React.ReactNode;
@@ -22,7 +16,7 @@ interface StaticProps extends Props {
22
16
  container?: HTMLElement;
23
17
  }
24
18
  declare const Toast: {
25
- ({ status, type, title, description, icon, closable, action, className, onClose }: Props): import("react").JSX.Element;
19
+ ({ status, title, description, icon, closable, action, className, onClose }: Props): import("react").JSX.Element;
26
20
  destroy: (id?: string) => void;
27
21
  destroyAll(): void;
28
22
  info(title: React.ReactNode, props?: TriggerOptions): string | undefined;
@@ -30,7 +24,7 @@ declare const Toast: {
30
24
  error(title: React.ReactNode, props?: TriggerOptions): string | undefined;
31
25
  warning(title: React.ReactNode, props?: TriggerOptions): string | undefined;
32
26
  };
33
- type TriggerOptions = Omit<StaticProps, 'status' | 'type' | 'title'>;
27
+ type TriggerOptions = Omit<StaticProps, 'status' | 'title'>;
34
28
  //#endregion
35
29
  export { Props, type StaticProps as ToastOptions, Toast as default };
36
30
  //# sourceMappingURL=toast.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;;;;;;EAMT,OAAO;EACP,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KAUH,QAAA,MAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAsHa,KAAA,OAAA,MAAM,WAAS,QAAU;EAEtB,QAAA,OAAA,MAAM,WAAS,QAAU;EAE3B,MAAA,OAAA,MAAM,WAAS,QAAU;EAEvB,QAAA,OAAA,MAAM,WAAS,QAAU;;KAR5C,iBAAiB,KAAK"}
1
+ {"version":3,"file":"toast.d.mts","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"mappings":";KAkBK;UAEY;EACf,SAAS;EACT,OAAO,MAAM;EACb,cAAc,MAAM;EACpB,OAAO,MAAM;EACb;EACA,SAAS,MAAM;EACf;EACA;;UAGQ,oBAAoB;EAC5B;EACA;EACA,YAAY;;cAUR;KASH,QAAA,OAAA,aAAA,MAAA,UAAA,QAAA,WAAA,WAAA,wBAAK,IAAA;;;EAoHa,KAAA,OAAA,MAAM,WAAS,QAAU;EAEtB,QAAA,OAAA,MAAM,WAAS,QAAU;EAE3B,MAAA,OAAA,MAAM,WAAS,QAAU;EAEvB,QAAA,OAAA,MAAM,WAAS,QAAU;;KAR5C,iBAAiB,KAAK"}
@@ -15,16 +15,15 @@ const STATUS_ICONS = {
15
15
  error: /* @__PURE__ */ jsx(OctagonX, { className: "text-red-400" }),
16
16
  warning: /* @__PURE__ */ jsx(OctagonAlert, { className: "text-yellow-400" })
17
17
  };
18
- const Toast = ({ status, type, title, description, icon, closable = true, action, className, onClose }) => {
18
+ const Toast = ({ status, title, description, icon, closable = true, action, className, onClose }) => {
19
19
  const { locale } = useConfig();
20
- const resolvedStatus = status ?? type;
21
20
  return /* @__PURE__ */ jsxs("div", {
22
- role: resolvedStatus === "error" ? "alert" : "status",
21
+ role: status === "error" ? "alert" : "status",
23
22
  className: cn("pointer-events-auto flex w-80 items-start gap-3 rounded-lg", "bg-background text-foreground border p-4 shadow-lg", className),
24
23
  children: [
25
- (icon || resolvedStatus) && /* @__PURE__ */ jsx("div", {
24
+ (icon || status) && /* @__PURE__ */ jsx("div", {
26
25
  className: "mt-0.5 shrink-0 [&_svg]:size-5",
27
- children: icon || resolvedStatus && STATUS_ICONS[resolvedStatus]
26
+ children: icon || status && STATUS_ICONS[status]
28
27
  }),
29
28
  /* @__PURE__ */ jsxs("div", {
30
29
  className: "flex-1 space-y-1",
@@ -1 +1 @@
1
- {"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n /**\n * @deprecated Use `status`. Kept as an alias so existing\n * `type=\"info|success|error|warning\"` usage keeps working; `status` matches\n * the shared semantic-state vocabulary used by `Result`.\n */\n type?: ToastStatus;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n closable?: boolean;\n action?: React.ReactNode;\n className?: string;\n onClose?: () => void;\n}\n\ninterface StaticProps extends Props {\n id?: string;\n duration?: number;\n container?: HTMLElement;\n}\n\nconst STATUS_ICONS: Record<ToastStatus, React.ReactNode> = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n};\n\nconst Toast = ({\n status,\n type,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n // `type` is the deprecated alias for `status`; prefer `status` when both set.\n const resolvedStatus = status ?? type;\n\n return (\n <div\n role={resolvedStatus === 'error' ? 'alert' : 'status'}\n className={cn(\n 'pointer-events-auto flex w-80 items-start gap-3 rounded-lg',\n 'bg-background text-foreground border p-4 shadow-lg',\n className,\n //\n )}\n >\n {(icon || resolvedStatus) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (resolvedStatus && STATUS_ICONS[resolvedStatus])}\n </div>\n )}\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium\">{title}</p>\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={<X />}\n onClick={onClose}\n />\n )}\n </div>\n );\n};\n\nconst DEFAULT_DURATION = 4000;\n\nconst StaticToast = ({\n id,\n duration = DEFAULT_DURATION,\n container,\n onClose,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const [visible, setVisible] = useState(true);\n\n const close = () => {\n onClose?.();\n setVisible(false);\n setTimeout(() => {\n toastStack.destroy(id);\n }, 200);\n };\n\n const { reset, clear } = useTimeout(close, duration || null);\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <div\n className={cn(\n 'transition-all duration-200',\n visible ? 'opacity-100' : 'translate-x-2 opacity-0',\n )}\n // Without this, a toast with a closable/action button (or one the\n // user is mid-read on) could auto-dismiss itself out from under\n // them — hovering or focusing anything inside pauses the timer,\n // moving away restarts it from the full duration.\n onMouseEnter={clear}\n onMouseLeave={reset}\n onFocus={clear}\n onBlur={reset}\n >\n <Toast {...props} onClose={close} />\n </div>,\n toastStack.getRootElement(container)!,\n );\n};\n\nconst toastStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.setAttribute('role', 'region');\n // Plain DOM, created outside any React tree — no Config to read a\n // locale override from until #226 gives the imperative stack a way\n // to inherit one.\n el.setAttribute('aria-label', 'Notifications');\n el.style.zIndex = '10000';\n el.style.position = 'fixed';\n el.style.right = '0';\n el.style.bottom = '0';\n el.style.display = 'flex';\n el.style.flexDirection = 'column-reverse';\n el.style.gap = '8px';\n el.style.padding = '16px';\n el.style.pointerEvents = 'none';\n return el;\n },\n StackItem: StaticToast,\n });\n\nToast.destroy = toastStack.destroy;\n\nToast.destroyAll = () => {\n toastStack.destroy();\n};\n\ntype TriggerOptions = Omit<StaticProps, 'status' | 'type' | 'title'>;\n\nToast.info = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'info', title, ...props });\nToast.success = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'success', title, ...props });\nToast.error = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'error', title, ...props });\nToast.warning = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'warning', title, ...props });\n\nexport default Toast;\nexport type { StaticProps as ToastOptions };\n"],"mappings":"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"}
1
+ {"version":3,"file":"toast.mjs","names":[],"sources":["../../../src/components/organisms/toast.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useTimeout } from '@jbpark/use-hooks';\nimport { Check, Info, OctagonAlert, OctagonX, X } from 'lucide-react';\n\nimport { DEFAULT_LOCALE, useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nimport Button from '../atoms/button';\nimport {\n type ImperativeStack,\n createImperativeStack,\n isBrowser,\n} from './imperative-stack';\n\ntype ToastStatus = 'info' | 'success' | 'error' | 'warning';\n\nexport interface Props {\n status?: ToastStatus;\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n closable?: boolean;\n action?: React.ReactNode;\n className?: string;\n onClose?: () => void;\n}\n\ninterface StaticProps extends Props {\n id?: string;\n duration?: number;\n container?: HTMLElement;\n}\n\nconst STATUS_ICONS: Record<ToastStatus, React.ReactNode> = {\n info: <Info className=\"text-blue-400\" />,\n success: <Check className=\"text-green-400\" />,\n error: <OctagonX className=\"text-red-400\" />,\n warning: <OctagonAlert className=\"text-yellow-400\" />,\n};\n\nconst Toast = ({\n status,\n title,\n description,\n icon,\n closable = true,\n action,\n className,\n onClose,\n}: Props) => {\n const { locale } = useConfig();\n\n return (\n <div\n role={status === 'error' ? 'alert' : 'status'}\n className={cn(\n 'pointer-events-auto flex w-80 items-start gap-3 rounded-lg',\n 'bg-background text-foreground border p-4 shadow-lg',\n className,\n //\n )}\n >\n {(icon || status) && (\n <div className=\"mt-0.5 shrink-0 [&_svg]:size-5\">\n {icon || (status && STATUS_ICONS[status])}\n </div>\n )}\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium\">{title}</p>\n {description && (\n <p className=\"text-muted-foreground text-sm\">{description}</p>\n )}\n {action && <div className=\"pt-1\">{action}</div>}\n </div>\n {closable && (\n <Button\n variant=\"text\"\n shape=\"circle\"\n size=\"small\"\n aria-label={locale.close ?? DEFAULT_LOCALE.close}\n icon={<X />}\n onClick={onClose}\n />\n )}\n </div>\n );\n};\n\nconst DEFAULT_DURATION = 4000;\n\nconst StaticToast = ({\n id,\n duration = DEFAULT_DURATION,\n container,\n onClose,\n ...props\n}: StaticProps & { id: string }): React.ReactPortal | null => {\n const [visible, setVisible] = useState(true);\n\n const close = () => {\n onClose?.();\n setVisible(false);\n setTimeout(() => {\n toastStack.destroy(id);\n }, 200);\n };\n\n const { reset, clear } = useTimeout(close, duration || null);\n\n if (!isBrowser) {\n return null;\n }\n\n return createPortal(\n <div\n className={cn(\n 'transition-all duration-200',\n visible ? 'opacity-100' : 'translate-x-2 opacity-0',\n )}\n // Without this, a toast with a closable/action button (or one the\n // user is mid-read on) could auto-dismiss itself out from under\n // them — hovering or focusing anything inside pauses the timer,\n // moving away restarts it from the full duration.\n onMouseEnter={clear}\n onMouseLeave={reset}\n onFocus={clear}\n onBlur={reset}\n >\n <Toast {...props} onClose={close} />\n </div>,\n toastStack.getRootElement(container)!,\n );\n};\n\nconst toastStack: ImperativeStack<StaticProps> =\n createImperativeStack<StaticProps>({\n createRootElement: () => {\n const el = document.createElement('div');\n el.setAttribute('role', 'region');\n // Plain DOM, created outside any React tree — no Config to read a\n // locale override from until #226 gives the imperative stack a way\n // to inherit one.\n el.setAttribute('aria-label', 'Notifications');\n el.style.zIndex = '10000';\n el.style.position = 'fixed';\n el.style.right = '0';\n el.style.bottom = '0';\n el.style.display = 'flex';\n el.style.flexDirection = 'column-reverse';\n el.style.gap = '8px';\n el.style.padding = '16px';\n el.style.pointerEvents = 'none';\n return el;\n },\n StackItem: StaticToast,\n });\n\nToast.destroy = toastStack.destroy;\n\nToast.destroyAll = () => {\n toastStack.destroy();\n};\n\ntype TriggerOptions = Omit<StaticProps, 'status' | 'title'>;\n\nToast.info = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'info', title, ...props });\nToast.success = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'success', title, ...props });\nToast.error = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'error', title, ...props });\nToast.warning = (title: React.ReactNode, props?: TriggerOptions) =>\n toastStack.render({ status: 'warning', title, ...props });\n\nexport default Toast;\nexport type { StaticProps as ToastOptions };\n"],"mappings":";;;;;;;;;;;AAqCA,MAAM,eAAqD;CACzD,MAAM,oBAAC,MAAD,EAAM,WAAU,gBAAiB,CAAA;CACvC,SAAS,oBAAC,OAAD,EAAO,WAAU,iBAAkB,CAAA;CAC5C,OAAO,oBAAC,UAAD,EAAU,WAAU,eAAgB,CAAA;CAC3C,SAAS,oBAAC,cAAD,EAAc,WAAU,kBAAmB,CAAA;AACtD;AAEA,MAAM,SAAS,EACb,QACA,OACA,aACA,MACA,WAAW,MACX,QACA,WACA,cACW;CACX,MAAM,EAAE,WAAW,UAAU;CAE7B,OACE,qBAAC,OAAD;EACE,MAAM,WAAW,UAAU,UAAU;EACrC,WAAW,GACT,8DACA,sDACA,SAEF;EAPF,UAAA;IASI,QAAQ,WACR,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,QAAS,UAAU,aAAa;GAC9B,CAAA;GAEP,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,KAAD;MAAG,WAAU;MAAuB,UAAA;KAAS,CAAA;KAC5C,eACC,oBAAC,KAAD;MAAG,WAAU;MAAiC,UAAA;KAAe,CAAA;KAE9D,UAAU,oBAAC,OAAD;MAAK,WAAU;MAAQ,UAAA;KAAY,CAAA;IAC3C;;GACJ,YACC,oBAAC,QAAD;IACE,SAAQ;IACR,OAAM;IACN,MAAK;IACL,cAAY,OAAO,SAAS,eAAe;IAC3C,MAAM,oBAAC,GAAD,CAAI,CAAA;IACV,SAAS;GACV,CAAA;EAEA;;AAET;AAEA,MAAM,mBAAmB;AAEzB,MAAM,eAAe,EACnB,IACA,WAAW,kBACX,WACA,SACA,GAAG,YACyD;CAC5D,MAAM,CAAC,SAAS,cAAc,SAAS,IAAI;CAE3C,MAAM,cAAc;EAClB,UAAU;EACV,WAAW,KAAK;EAChB,iBAAiB;GACf,WAAW,QAAQ,EAAE;EACvB,GAAG,GAAG;CACR;CAEA,MAAM,EAAE,OAAO,UAAU,WAAW,OAAO,YAAY,IAAI;CAE3D,IAAI,CAAC,WACH,OAAO;CAGT,OAAO,aACL,oBAAC,OAAD;EACE,WAAW,GACT,+BACA,UAAU,gBAAgB,yBAC5B;EAKA,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EAER,UAAA,oBAAC,OAAD;GAAO,GAAI;GAAO,SAAS;EAAQ,CAAA;CAChC,CAAA,GACL,WAAW,eAAe,SAAS,CACrC;AACF;AAEA,MAAM,aACJ,sBAAmC;CACjC,yBAAyB;EACvB,MAAM,KAAK,SAAS,cAAc,KAAK;EACvC,GAAG,aAAa,QAAQ,QAAQ;EAIhC,GAAG,aAAa,cAAc,eAAe;EAC7C,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,WAAW;EACpB,GAAG,MAAM,QAAQ;EACjB,GAAG,MAAM,SAAS;EAClB,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,GAAG,MAAM,MAAM;EACf,GAAG,MAAM,UAAU;EACnB,GAAG,MAAM,gBAAgB;EACzB,OAAO;CACT;CACA,WAAW;AACb,CAAC;AAEH,MAAM,UAAU,WAAW;AAE3B,MAAM,mBAAmB;CACvB,WAAW,QAAQ;AACrB;AAIA,MAAM,QAAQ,OAAwB,UACpC,WAAW,OAAO;CAAE,QAAQ;CAAQ;CAAO,GAAG;AAAM,CAAC;AACvD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC;AAC1D,MAAM,SAAS,OAAwB,UACrC,WAAW,OAAO;CAAE,QAAQ;CAAS;CAAO,GAAG;AAAM,CAAC;AACxD,MAAM,WAAW,OAAwB,UACvC,WAAW,OAAO;CAAE,QAAQ;CAAW;CAAO,GAAG;AAAM,CAAC"}
@@ -1,2 +1,25 @@
1
- import "react";
2
- import "class-variance-authority";
1
+ import * as React$1 from "react";
2
+ import { VariantProps } from "class-variance-authority";
3
+ //#region src/core/badge.d.ts
4
+ /**
5
+ * Vendored from shadcn's `new-york-v4` badge registry entry.
6
+ *
7
+ * Two deliberate local adaptations, both of which must survive the next sync:
8
+ * - upstream imports `Slot` from the unified `radix-ui` package; this repo
9
+ * installs the individual `@radix-ui/*` packages (13 of them) and has no
10
+ * `radix-ui` dependency, so the import stays on `@radix-ui/react-slot`.
11
+ * - `cn` resolves through `@repo/ui/utils` rather than upstream's `cn` alias.
12
+ *
13
+ * Everything else is upstream verbatim, including the `rounded-full` /
14
+ * `border-transparent` base and the `ghost` / `link` variants.
15
+ */
16
+ declare const badgeVariants: (props?: ({
17
+ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
18
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
19
+ type Props = React$1.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
20
+ asChild?: boolean;
21
+ };
22
+ declare function Badge({ className, variant, asChild, ...props }: Props): React$1.JSX.Element;
23
+ //#endregion
24
+ export { Badge, type Props, badgeVariants };
25
+ //# sourceMappingURL=badge.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.d.mts","names":[],"sources":["../../src/core/badge.tsx"],"mappings":";;;;;;;;;;;;;;;cAmBM,gBAAa;;6CA0BlB;KAEI,QAAQ,QAAM,yBACjB,oBAAoB;EAClB;;iBAGK,QACP,WACA,SACA,YACG,SACF,QAAK,QAAA,IAAA"}
@@ -0,0 +1,47 @@
1
+ import { cn } from "../utils.mjs";
2
+ import "react";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { cva } from "class-variance-authority";
6
+ //#region src/core/badge.tsx
7
+ /**
8
+ * Vendored from shadcn's `new-york-v4` badge registry entry.
9
+ *
10
+ * Two deliberate local adaptations, both of which must survive the next sync:
11
+ * - upstream imports `Slot` from the unified `radix-ui` package; this repo
12
+ * installs the individual `@radix-ui/*` packages (13 of them) and has no
13
+ * `radix-ui` dependency, so the import stays on `@radix-ui/react-slot`.
14
+ * - `cn` resolves through `@repo/ui/utils` rather than upstream's `cn` alias.
15
+ *
16
+ * Everything else is upstream verbatim, including the `rounded-full` /
17
+ * `border-transparent` base and the `ghost` / `link` variants.
18
+ */
19
+ const badgeVariants = cva(`inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden
20
+ rounded-full border border-transparent px-2 py-0.5 text-xs font-medium
21
+ whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring
22
+ focus-visible:ring-[3px] focus-visible:ring-ring/50
23
+ aria-invalid:border-destructive aria-invalid:ring-destructive/20
24
+ dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none
25
+ [&>svg]:size-3`, {
26
+ variants: { variant: {
27
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
28
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
29
+ destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
30
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
31
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
32
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
33
+ } },
34
+ defaultVariants: { variant: "default" }
35
+ });
36
+ function Badge({ className, variant = "default", asChild = false, ...props }) {
37
+ return /* @__PURE__ */ jsx(asChild ? Slot : "span", {
38
+ "data-slot": "badge",
39
+ "data-variant": variant,
40
+ className: cn(badgeVariants({ variant }), className),
41
+ ...props
42
+ });
43
+ }
44
+ //#endregion
45
+ export { Badge, badgeVariants };
46
+
47
+ //# sourceMappingURL=badge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badge.mjs","names":[],"sources":["../../src/core/badge.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps, cva } from 'class-variance-authority';\n\nimport { cn } from '@repo/ui/utils';\n\n/**\n * Vendored from shadcn's `new-york-v4` badge registry entry.\n *\n * Two deliberate local adaptations, both of which must survive the next sync:\n * - upstream imports `Slot` from the unified `radix-ui` package; this repo\n * installs the individual `@radix-ui/*` packages (13 of them) and has no\n * `radix-ui` dependency, so the import stays on `@radix-ui/react-slot`.\n * - `cn` resolves through `@repo/ui/utils` rather than upstream's `cn` alias.\n *\n * Everything else is upstream verbatim, including the `rounded-full` /\n * `border-transparent` base and the `ghost` / `link` variants.\n */\nconst badgeVariants = cva(\n `inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden\n rounded-full border border-transparent px-2 py-0.5 text-xs font-medium\n whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring\n focus-visible:ring-[3px] focus-visible:ring-ring/50\n aria-invalid:border-destructive aria-invalid:ring-destructive/20\n dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none\n [&>svg]:size-3`,\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90',\n secondary:\n 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',\n destructive:\n 'bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90',\n outline:\n 'border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',\n ghost: '[a&]:hover:bg-accent [a&]:hover:text-accent-foreground',\n link: 'text-primary underline-offset-4 [a&]:hover:underline',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\ntype Props = React.ComponentProps<'span'> &\n VariantProps<typeof badgeVariants> & {\n asChild?: boolean;\n };\n\nfunction Badge({\n className,\n variant = 'default',\n asChild = false,\n ...props\n}: Props) {\n const Comp = asChild ? Slot : 'span';\n\n return (\n <Comp\n data-slot=\"badge\"\n data-variant={variant}\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nexport { Badge, badgeVariants, type Props };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,MAAM,gBAAgB,IACpB;;;;;;mBAOA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,WACE;EACF,aACE;EACF,SACE;EACF,OAAO;EACP,MAAM;CACR,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CACF;AAOA,SAAS,MAAM,EACb,WACA,UAAU,WACV,UAAU,OACV,GAAG,SACK;CAGR,OACE,oBAHW,UAAU,OAAO,QAG5B;EACE,aAAU;EACV,gBAAc;EACd,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS;EACnD,GAAI;CACL,CAAA;AAEL"}
@@ -1,2 +1,14 @@
1
- import "react";
2
- import "class-variance-authority";
1
+ import * as React$1 from "react";
2
+ import { VariantProps } from "class-variance-authority";
3
+ //#region src/core/button.d.ts
4
+ declare const buttonVariants: (props?: ({
5
+ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
6
+ size?: "icon" | "default" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
7
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
+ type Props = React$1.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
9
+ asChild?: boolean;
10
+ };
11
+ declare function Button({ className, variant, size, asChild, ...props }: Props): React$1.JSX.Element;
12
+ //#endregion
13
+ export { Button, type Props, buttonVariants };
14
+ //# sourceMappingURL=button.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.d.mts","names":[],"sources":["../../src/core/button.tsx"],"mappings":";;;cAOM,iBAAc;;;6CAgCnB;KAEI,QAAQ,QAAM,2BACjB,oBAAoB;EAClB;;iBAGK,SACP,WACA,SACA,MACA,YACG,SACF,QAAK,QAAA,IAAA"}
@@ -2,8 +2,8 @@
2
2
  import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
3
3
  import { cn } from "../utils.mjs";
4
4
  import { useConfig } from "../providers/config/context.mjs";
5
- import Button from "../Button.mjs";
6
5
  import { OVERLAY_LAYER } from "../lib/z-layers.mjs";
6
+ import Button from "../Button.mjs";
7
7
  import "react";
8
8
  import { XIcon } from "lucide-react";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,2 +1,2 @@
1
1
  import "./label.mjs";
2
- import "class-variance-authority";
2
+ import { VariantProps } from "class-variance-authority";
@@ -5,41 +5,16 @@
5
5
  * `data-color`), so the same word means the same hue on either component
6
6
  * (#320).
7
7
  *
8
- * The names track **Tailwind's own palette names** (#342). They always did
9
- * track Tailwind's *values* — every preset was a Tailwind hue, just spelled
10
- * with antd's vocabulary — so four of them said one thing and rendered
11
- * another: `magenta` rendered Tailwind's `fuchsia`, `geekblue` rendered
12
- * `indigo`, `gold` rendered `amber`. Aligning the spelling makes the name and
13
- * the value agree, and lets `globals.css` resolve each entry straight from the
14
- * matching `--color-*` theme variable instead of a hand-copied hex.
8
+ * The names track **Tailwind's own palette names** (#342): every preset is a
9
+ * Tailwind hue spelled the way Tailwind spells it, so the name and the value
10
+ * agree and `globals.css` can resolve each entry straight from the matching
11
+ * `--color-*` theme variable instead of a hand-copied hex. The pre-8.0 antd
12
+ * spellings were removed in 9.0.0.
15
13
  *
16
14
  * Keep this list in sync with the `[data-color='…']` rules there.
17
15
  */
18
16
  declare const PRESET_COLORS: readonly ["blue", "purple", "cyan", "green", "fuchsia", "pink", "red", "orange", "yellow", "indigo", "lime", "amber"];
19
17
  type PresetColor = (typeof PRESET_COLORS)[number];
20
- /**
21
- * Pre-8.0 spellings, still accepted and still rendering exactly what they
22
- * rendered before — they are aliases, not a behaviour change:
23
- *
24
- * | deprecated | renders | canonical spelling |
25
- * | ---------- | ----------------- | ----------------------------- |
26
- * | `magenta` | `--color-fuchsia-500` | `fuchsia` |
27
- * | `geekblue` | `--color-indigo-500` | `indigo` |
28
- * | `gold` | `--color-amber-500` | `amber` |
29
- * | `volcano` | `--color-orange-600` | *(none — see below)* |
30
- *
31
- * `volcano` is the odd one out and has no canonical successor. Every other
32
- * preset is a distinct hue at Tailwind's `500` step, but `volcano` is
33
- * `orange-600` — the same hue as `orange`, one step darker. That makes it a
34
- * lightness variant masquerading as a hue, so it doesn't belong in a list
35
- * whose axis is "one entry per hue". It keeps rendering `orange-600` until the
36
- * next major; callers who want that exact shade can set `--btn-bg`/`--tag-bg`
37
- * directly.
38
- *
39
- * All four are removed in the next major.
40
- */
41
- declare const DEPRECATED_PRESET_COLORS: readonly ["magenta", "geekblue", "gold", "volcano"];
42
- type DeprecatedPresetColor = (typeof DEPRECATED_PRESET_COLORS)[number];
43
18
  //#endregion
44
- export { DEPRECATED_PRESET_COLORS, DeprecatedPresetColor, PRESET_COLORS, PresetColor };
19
+ export { PRESET_COLORS, PresetColor };
45
20
  //# sourceMappingURL=colors.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/lib/colors.ts"],"mappings":";;;;;;;;;;;;;;;;;cAgBa;KAeD,sBAAsB;;;;;;;;;;;;;;;;;;;;;;cAuBrB;KAOD,gCAAgC"}
1
+ {"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/lib/colors.ts"],"mappings":";;;;;;;;;;;;;;;cAca;KAeD,sBAAsB"}
package/dist/style.css CHANGED
@@ -103,54 +103,42 @@
103
103
  }
104
104
  }
105
105
  @layer components {
106
- :is([data-slot="button"], [data-slot="badge"])[data-color="blue"] {
106
+ :is([data-slot="button"], [data-slot="tag"])[data-color="blue"] {
107
107
  --preset-color: var(--color-blue-500);
108
108
  }
109
- :is([data-slot="button"], [data-slot="badge"])[data-color="purple"] {
109
+ :is([data-slot="button"], [data-slot="tag"])[data-color="purple"] {
110
110
  --preset-color: var(--color-purple-500);
111
111
  }
112
- :is([data-slot="button"], [data-slot="badge"])[data-color="cyan"] {
112
+ :is([data-slot="button"], [data-slot="tag"])[data-color="cyan"] {
113
113
  --preset-color: var(--color-cyan-500);
114
114
  }
115
- :is([data-slot="button"], [data-slot="badge"])[data-color="green"] {
115
+ :is([data-slot="button"], [data-slot="tag"])[data-color="green"] {
116
116
  --preset-color: var(--color-green-500);
117
117
  }
118
- :is([data-slot="button"], [data-slot="badge"])[data-color="fuchsia"] {
118
+ :is([data-slot="button"], [data-slot="tag"])[data-color="fuchsia"] {
119
119
  --preset-color: var(--color-fuchsia-500);
120
120
  }
121
- :is([data-slot="button"], [data-slot="badge"])[data-color="pink"] {
121
+ :is([data-slot="button"], [data-slot="tag"])[data-color="pink"] {
122
122
  --preset-color: var(--color-pink-500);
123
123
  }
124
- :is([data-slot="button"], [data-slot="badge"])[data-color="red"] {
124
+ :is([data-slot="button"], [data-slot="tag"])[data-color="red"] {
125
125
  --preset-color: var(--color-red-500);
126
126
  }
127
- :is([data-slot="button"], [data-slot="badge"])[data-color="orange"] {
127
+ :is([data-slot="button"], [data-slot="tag"])[data-color="orange"] {
128
128
  --preset-color: var(--color-orange-500);
129
129
  }
130
- :is([data-slot="button"], [data-slot="badge"])[data-color="yellow"] {
130
+ :is([data-slot="button"], [data-slot="tag"])[data-color="yellow"] {
131
131
  --preset-color: var(--color-yellow-500);
132
132
  }
133
- :is([data-slot="button"], [data-slot="badge"])[data-color="indigo"] {
133
+ :is([data-slot="button"], [data-slot="tag"])[data-color="indigo"] {
134
134
  --preset-color: var(--color-indigo-500);
135
135
  }
136
- :is([data-slot="button"], [data-slot="badge"])[data-color="lime"] {
136
+ :is([data-slot="button"], [data-slot="tag"])[data-color="lime"] {
137
137
  --preset-color: var(--color-lime-500);
138
138
  }
139
- :is([data-slot="button"], [data-slot="badge"])[data-color="amber"] {
139
+ :is([data-slot="button"], [data-slot="tag"])[data-color="amber"] {
140
140
  --preset-color: var(--color-amber-500);
141
141
  }
142
- :is([data-slot="button"], [data-slot="badge"])[data-color="magenta"] {
143
- --preset-color: var(--color-fuchsia-500);
144
- }
145
- :is([data-slot="button"], [data-slot="badge"])[data-color="geekblue"] {
146
- --preset-color: var(--color-indigo-500);
147
- }
148
- :is([data-slot="button"], [data-slot="badge"])[data-color="gold"] {
149
- --preset-color: var(--color-amber-500);
150
- }
151
- :is([data-slot="button"], [data-slot="badge"])[data-color="volcano"] {
152
- --preset-color: var(--color-orange-600);
153
- }
154
142
  [data-slot="button"][data-color] {
155
143
  --btn-bg: var(--preset-color);
156
144
  --btn-fg: white;
@@ -164,7 +152,7 @@
164
152
  --btn-bg-active: color-mix(in oklch, var(--btn-bg), black 22%);
165
153
  }
166
154
  }
167
- [data-slot="button"][data-color="yellow"], [data-slot="button"][data-color="lime"], [data-slot="button"][data-color="amber"], [data-slot="button"][data-color="gold"] {
155
+ [data-slot="button"][data-color="yellow"], [data-slot="button"][data-color="lime"], [data-slot="button"][data-color="amber"] {
168
156
  --btn-fg: black;
169
157
  }
170
158
  [data-slot="button"][data-color="default"] {
@@ -178,7 +166,7 @@
178
166
  [data-slot="button"][data-color="danger"] {
179
167
  --btn-bg: var(--destructive);
180
168
  }
181
- [data-slot="badge"][data-color] {
169
+ [data-slot="tag"][data-color] {
182
170
  --tag-bg: var(--preset-color);
183
171
  --tag-fg: var(--tag-bg);
184
172
  --tag-tint: var(--tag-bg);
@@ -186,32 +174,32 @@
186
174
  --tag-tint: color-mix(in oklab, var(--tag-bg), transparent 90%);
187
175
  }
188
176
  }
189
- [data-slot="badge"][data-color="default"] {
177
+ [data-slot="tag"][data-color="default"] {
190
178
  --tag-bg: var(--border);
191
179
  --tag-tint: var(--muted);
192
180
  --tag-fg: var(--muted-foreground);
193
181
  }
194
- [data-slot="badge"][data-color="default"][data-variant="outlined"] {
182
+ [data-slot="tag"][data-color="default"][data-variant="outlined"] {
195
183
  --tag-fg: var(--foreground);
196
184
  }
197
- [data-slot="badge"][data-color="primary"] {
185
+ [data-slot="tag"][data-color="primary"] {
198
186
  --tag-bg: var(--primary);
199
187
  }
200
- [data-slot="badge"][data-color="danger"] {
188
+ [data-slot="tag"][data-color="danger"] {
201
189
  --tag-bg: var(--destructive);
202
190
  }
203
- [data-slot="badge"][data-color="success"] {
191
+ [data-slot="tag"][data-color="success"] {
204
192
  --tag-bg: var(--color-green-500);
205
193
  --tag-fg: var(--color-green-600);
206
194
  }
207
- [data-slot="badge"][data-color="warning"] {
195
+ [data-slot="tag"][data-color="warning"] {
208
196
  --tag-bg: var(--color-yellow-500);
209
197
  --tag-fg: var(--color-yellow-600);
210
198
  }
211
- :is(.dark, [data-theme="dark"]) [data-slot="badge"][data-color="success"] {
199
+ :is(.dark, [data-theme="dark"]) [data-slot="tag"][data-color="success"] {
212
200
  --tag-fg: var(--color-green-400);
213
201
  }
214
- :is(.dark, [data-theme="dark"]) [data-slot="badge"][data-color="warning"] {
202
+ :is(.dark, [data-theme="dark"]) [data-slot="tag"][data-color="warning"] {
215
203
  --tag-fg: var(--color-yellow-400);
216
204
  }
217
205
  [data-slot="col"] {
@@ -3034,6 +3022,9 @@
3034
3022
  a.\[a\&\]\:hover\:text-accent-foreground:hover {
3035
3023
  color: var(--accent-foreground);
3036
3024
  }
3025
+ a.\[a\&\]\:hover\:underline:hover {
3026
+ text-decoration-line: underline;
3027
+ }
3037
3028
  }
3038
3029
  }
3039
3030
  :root {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/ui-kit",
3
- "version": "8.1.0",
3
+ "version": "9.0.0",
4
4
  "description": "Modern React UI component library built with TypeScript, Tailwind CSS, and Radix UI. Featuring atoms, molecules, organisms and layout templates for building beautiful interfaces.",
5
5
  "keywords": [
6
6
  "react",
@@ -1,23 +0,0 @@
1
- import { cn } from "../utils.mjs";
2
- //#region src/lib/chassis.ts
3
- /**
4
- * Shared a11y/state chassis for interactive single-element atoms (Button, Tag).
5
- *
6
- * Extracted from `core/button` and `core/badge`'s cva base strings during the
7
- * #278 step ③ absorb (see #294 Phase 4). When an atom stops wrapping its core
8
- * primitive it must carry this chassis itself — the focus ring, the
9
- * `aria-invalid` treatment and the flex centering are load-bearing and have no
10
- * type- or lint-level signal, so dropping them regresses silently (the #177
11
- * class of bug the regression net in #300/#301 guards).
12
- *
13
- * Common set only. A Tag is not disableable, does not size icons and (as a
14
- * non-focusable `span`) carries no `outline-none`, so those live on the Button
15
- * extension in `atoms/button`, NOT here — folding them into one shared constant
16
- * would push `disabled:*` onto Tag, which is semantically wrong (#301's
17
- * per-component chassis finding).
18
- */
19
- const INTERACTIVE_CHASSIS = cn("inline-flex items-center justify-center shrink-0", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive");
20
- //#endregion
21
- export { INTERACTIVE_CHASSIS };
22
-
23
- //# sourceMappingURL=chassis.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chassis.mjs","names":[],"sources":["../../src/lib/chassis.ts"],"sourcesContent":["import { cn } from '@repo/ui/utils';\n\n/**\n * Shared a11y/state chassis for interactive single-element atoms (Button, Tag).\n *\n * Extracted from `core/button` and `core/badge`'s cva base strings during the\n * #278 step ③ absorb (see #294 Phase 4). When an atom stops wrapping its core\n * primitive it must carry this chassis itself — the focus ring, the\n * `aria-invalid` treatment and the flex centering are load-bearing and have no\n * type- or lint-level signal, so dropping them regresses silently (the #177\n * class of bug the regression net in #300/#301 guards).\n *\n * Common set only. A Tag is not disableable, does not size icons and (as a\n * non-focusable `span`) carries no `outline-none`, so those live on the Button\n * extension in `atoms/button`, NOT here — folding them into one shared constant\n * would push `disabled:*` onto Tag, which is semantically wrong (#301's\n * per-component chassis finding).\n */\nexport const INTERACTIVE_CHASSIS = cn(\n 'inline-flex items-center justify-center shrink-0',\n 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,MAAa,sBAAsB,GACjC,oDACA,iFACA,wGACF"}