@jbpark/ui-kit 4.1.0 → 5.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;;;KAQK,WAAA,GAAc,OAAA;AAAA,KAEd,YAAA;AAAA,UAeY,KAAA,SAAc,IAAA,CAAK,WAAA;EAClC,IAAA,GAAO,KAAA,CAAM,SAAA;EACb,KAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EAtB6B;AAAA;;;;EA4B7B,IAAA;EACA,OAAA;EAZqB;;;;;EAkBrB,QAAA;EACA,KAAA;EACA,KAAA,GAAQ,YAAA;EACR,OAAA;IAAsB,IAAA,EAAM,KAAA,CAAM,SAAA;EAAA;AAAA;AAAA,cAkD9B,MAAA;EAAU,IAAA;EAAA,SAAA;EAAA,IAAA;EAAA,OAAA;EAAA,QAAA;EAAA,IAAA;EAAA,KAAA;EAAA,KAAA;EAAA,KAAA;EAAA,QAAA;EAAA,OAAA;EAAA,MAAA;EAAA,QAAA;EAAA,WAAA;EAAA,GAAA;AAAA,GAgBb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"Button.d.mts","names":[],"sources":["../src/components/atoms/button.tsx"],"mappings":";;;;;KASK,WAAA,GAAc,OAAA;AAAA,KAEd,YAAA;AAAA,UAeY,KAAA,SAAc,IAAA,CAAK,WAAA;EAClC,IAAA,GAAO,KAAA,CAAM,SAAA;EACb,KAAA;EACA,MAAA;EACA,QAAA;EACA,IAAA;EAtB6B;AAAA;;;;EA4B7B,IAAA;EACA,OAAA;EAZqB;;;;;EAkBrB,QAAA;EACA,KAAA;EACA,KAAA,GAAQ,YAAA;EACR,OAAA;IAAsB,IAAA,EAAM,KAAA,CAAM,SAAA;EAAA;AAAA;AAAA,cAkD9B,MAAA;EAAU,IAAA;EAAA,SAAA;EAAA,IAAA;EAAA,OAAA;EAAA,QAAA;EAAA,IAAA;EAAA,KAAA;EAAA,KAAA;EAAA,KAAA;EAAA,QAAA;EAAA,OAAA;EAAA,MAAA;EAAA,QAAA;EAAA,WAAA;EAAA,GAAA;AAAA,GAgBb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
package/dist/Button.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { cn } from "./utils.mjs";
4
4
  import { Button as Button$1 } from "./core/button.mjs";
5
+ import { useConfig } from "./providers/config/context.mjs";
5
6
  import { LoaderCircle } from "lucide-react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
 
@@ -37,7 +38,11 @@ const shapesClasses = {
37
38
  circle: "rounded-full",
38
39
  round: "rounded-2xl"
39
40
  };
40
- const Button = ({ icon, className, type = "default", variant, htmlType = "button", size = "middle", color = "default", shape = "default", block = false, disabled, loading, danger, children, onMouseDown, ...props }) => {
41
+ const Button = ({ icon, className, type = "default", variant, htmlType = "button", size, color = "default", shape, block = false, disabled, loading, danger, children, onMouseDown, ...props }) => {
42
+ const { componentSize, defaultProps } = useConfig();
43
+ const buttonDefaults = defaultProps?.button;
44
+ const resolvedSize = size ?? componentSize ?? "middle";
45
+ const resolvedShape = shape ?? buttonDefaults?.shape ?? "default";
41
46
  const iconOnly = icon && !children;
42
47
  const computedColor = danger ? "danger" : color;
43
48
  const colored = computedColor && computedColor !== "default";
@@ -50,7 +55,7 @@ const Button = ({ icon, className, type = "default", variant, htmlType = "button
50
55
  "aria-busy": isLoading,
51
56
  variant: "default",
52
57
  "data-color": computedColor,
53
- className: cn("inline-flex items-center justify-center gap-x-2", "rounded-lg", "cursor-pointer", "h-auto py-0", "transition-all", variantClasses[resolvedVariant], sizesClasses[size], iconOnly && ["p-0", iconClasses[size]], shapesClasses[shape || "default"], block && "w-full", colored && (resolvedVariant === "solid" ? [
58
+ className: cn("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" ? [
54
59
  "bg-(--btn-bg)",
55
60
  "hover:bg-(--btn-bg-hover)",
56
61
  "active:bg-(--btn-bg-active)",
@@ -1 +1 @@
1
- {"version":3,"file":"Button.mjs","names":["button.Button"],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport { LoaderCircle } from 'lucide-react';\n\nimport { button } from '@repo/ui/core';\nimport { cn } from '@repo/ui/utils';\n\nconst Core = button.Button;\ntype ButtonProps = button.Props;\n\ntype PresetColors =\n | 'blue'\n | 'purple'\n | 'cyan'\n | 'green'\n | 'magenta'\n | 'pink'\n | 'red'\n | 'orange'\n | 'yellow'\n | 'volcano'\n | 'geekblue'\n | 'lime'\n | 'gold';\n\nexport interface Props extends Omit<ButtonProps, 'size' | 'variant' | 'type'> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n /**\n * antd-style visual preset. Maps to a `variant` internally — passing\n * both `type` and `variant` lets `variant` win, so it can override the\n * preset for one-off cases without needing every value duplicated here.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\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 color?: PresetColors | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\nconst variantClasses: Record<string, string> = {\n solid: '',\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\nconst typeToVariant: Record<string, string> = {\n primary: 'solid',\n default: 'outlined',\n dashed: 'dashed',\n text: 'text',\n link: '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 = 'default',\n variant,\n htmlType = 'button',\n size = 'middle',\n color = 'default',\n shape = 'default',\n block = false,\n disabled,\n loading,\n danger,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const iconOnly = icon && !children;\n const computedColor = danger ? 'danger' : color;\n const colored = computedColor && computedColor !== 'default';\n const resolvedVariant = variant ?? typeToVariant[type] ?? 'solid';\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 type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n variant=\"default\"\n data-color={computedColor}\n className={cn(\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[size],\n iconOnly && ['p-0', iconClasses[size]],\n shapesClasses[shape || 'default'],\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 onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {children}\n </Core>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;AAOA,MAAM,OAAOA;AA0Cb,MAAM,iBAAyC;CAC7C,OAAO;CACP,UAAU,GACR,yEACA,iCACA,kBACD;CACD,QAAQ,GACN,uFACA,iCACA,kBACD;CACD,QAAQ,GAAG,4BAA4B,oBAAoB;CAC3D,MAAM,GAAG,kCAAkC,kBAAkB;CAC7D,MAAM,GACJ,+BACA,sCACA,sBACD;CACF;AAED,MAAM,gBAAwC;CAC5C,SAAS;CACT,SAAS;CACT,QAAQ;CACR,MAAM;CACN,MAAM;CACP;AAED,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;CACR;AAED,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;CACR;AAED,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;CACR;AAED,MAAM,UAAU,EACd,MACA,WACA,OAAO,WACP,SACA,WAAW,UACX,OAAO,UACP,QAAQ,WACR,QAAQ,WACR,QAAQ,OACR,UACA,SACA,QACA,UACA,aACA,GAAG,YACQ;CACX,MAAM,WAAW,QAAQ,CAAC;CAC1B,MAAM,gBAAgB,SAAS,WAAW;CAC1C,MAAM,UAAU,iBAAiB,kBAAkB;CACnD,MAAM,kBAAkB,WAAW,cAAc,SAAS;CAC1D,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,gBAAa,WAAU,iBAAiB,GAG3C;AAGF,QACE,qBAAC;EACC,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,SAAQ;EACR,cAAY;EACZ,WAAW,GACT,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,OACb,YAAY,CAAC,OAAO,YAAY,MAAM,EACtC,cAAc,SAAS,YACvB,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;GACD,GACD,CACE,mBACA,iEAED,GACP,UAED;EACD,cAAa,MAAK;AAChB,iBAAc,EAAE;;EAElB,GAAI;aAEH,aACA;GACI"}
1
+ {"version":3,"file":"Button.mjs","names":["button.Button"],"sources":["../src/components/atoms/button.tsx"],"sourcesContent":["'use client';\n\nimport { LoaderCircle } from 'lucide-react';\n\nimport { button } from '@repo/ui/core';\nimport { useConfig } from '@repo/ui/providers';\nimport { cn } from '@repo/ui/utils';\n\nconst Core = button.Button;\ntype ButtonProps = button.Props;\n\ntype PresetColors =\n | 'blue'\n | 'purple'\n | 'cyan'\n | 'green'\n | 'magenta'\n | 'pink'\n | 'red'\n | 'orange'\n | 'yellow'\n | 'volcano'\n | 'geekblue'\n | 'lime'\n | 'gold';\n\nexport interface Props extends Omit<ButtonProps, 'size' | 'variant' | 'type'> {\n icon?: React.ReactNode;\n block?: boolean;\n danger?: boolean;\n disabled?: boolean;\n size?: 'small' | 'middle' | 'large';\n /**\n * antd-style visual preset. Maps to a `variant` internally — passing\n * both `type` and `variant` lets `variant` win, so it can override the\n * preset for one-off cases without needing every value duplicated here.\n */\n type?: 'primary' | 'default' | 'dashed' | 'text' | 'link';\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 color?: PresetColors | 'default' | 'primary' | 'danger';\n loading?: boolean | { icon: React.ReactNode };\n}\n\nconst variantClasses: Record<string, string> = {\n solid: '',\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\nconst typeToVariant: Record<string, string> = {\n primary: 'solid',\n default: 'outlined',\n dashed: 'dashed',\n text: 'text',\n link: '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 = 'default',\n variant,\n htmlType = 'button',\n size,\n color = 'default',\n shape,\n block = false,\n disabled,\n loading,\n danger,\n children,\n onMouseDown,\n ...props\n}: Props) => {\n const { componentSize, defaultProps } = useConfig();\n const buttonDefaults = defaultProps?.button as\n | Partial<Pick<Props, 'shape'>>\n | undefined;\n const resolvedSize = size ?? componentSize ?? 'middle';\n const resolvedShape = shape ?? buttonDefaults?.shape ?? 'default';\n const iconOnly = icon && !children;\n const computedColor = danger ? 'danger' : color;\n const colored = computedColor && computedColor !== 'default';\n const resolvedVariant = variant ?? typeToVariant[type] ?? 'solid';\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 type={htmlType}\n disabled={disabled || isLoading}\n aria-busy={isLoading}\n variant=\"default\"\n data-color={computedColor}\n className={cn(\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 onMouseDown={e => {\n onMouseDown?.(e);\n }}\n {...props}\n >\n {displayIcon}\n {children}\n </Core>\n );\n};\n\nexport default Button;\n"],"mappings":";;;;;;;;;AAQA,MAAM,OAAOA;AA0Cb,MAAM,iBAAyC;CAC7C,OAAO;CACP,UAAU,GACR,yEACA,iCACA,kBACD;CACD,QAAQ,GACN,uFACA,iCACA,kBACD;CACD,QAAQ,GAAG,4BAA4B,oBAAoB;CAC3D,MAAM,GAAG,kCAAkC,kBAAkB;CAC7D,MAAM,GACJ,+BACA,sCACA,sBACD;CACF;AAED,MAAM,gBAAwC;CAC5C,SAAS;CACT,SAAS;CACT,QAAQ;CACR,MAAM;CACN,MAAM;CACP;AAED,MAAM,eAAuC;CAC3C,OAAO;CACP,QAAQ;CACR,OAAO;CACR;AAED,MAAM,cAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,OAAO;CACR;AAED,MAAM,gBAAgB;CACpB,SAAS;CACT,QAAQ;CACR,OAAO;CACR;AAED,MAAM,UAAU,EACd,MACA,WACA,OAAO,WACP,SACA,WAAW,UACX,MACA,QAAQ,WACR,OACA,QAAQ,OACR,UACA,SACA,QACA,UACA,aACA,GAAG,YACQ;CACX,MAAM,EAAE,eAAe,iBAAiB,WAAW;CACnD,MAAM,iBAAiB,cAAc;CAGrC,MAAM,eAAe,QAAQ,iBAAiB;CAC9C,MAAM,gBAAgB,SAAS,gBAAgB,SAAS;CACxD,MAAM,WAAW,QAAQ,CAAC;CAC1B,MAAM,gBAAgB,SAAS,WAAW;CAC1C,MAAM,UAAU,iBAAiB,kBAAkB;CACnD,MAAM,kBAAkB,WAAW,cAAc,SAAS;CAC1D,MAAM,YAAY,CAAC,CAAC;CAEpB,MAAM,cAAc,UAClB,OAAO,YAAY,WACjB,QAAQ,OAER,oBAAC,gBAAa,WAAU,iBAAiB,GAG3C;AAGF,QACE,qBAAC;EACC,MAAM;EACN,UAAU,YAAY;EACtB,aAAW;EACX,SAAQ;EACR,cAAY;EACZ,WAAW,GACT,mDACA,cACA,kBACA,eACA,kBACA,eAAe,kBACf,aAAa,eACb,YAAY,CAAC,OAAO,YAAY,cAAc,EAC9C,cAAc,gBACd,SAAS,UACT,YACG,oBAAoB,UACjB;GACE;GACA;GACA;GACA;GACA;GACD,GACD,CACE,mBACA,iEAED,GACP,UAED;EACD,cAAa,MAAK;AAChB,iBAAc,EAAE;;EAElB,GAAI;aAEH,aACA;GACI"}
@@ -8,7 +8,7 @@ declare namespace badge_d_exports {
8
8
  export { Badge, Props, badgeVariants };
9
9
  }
10
10
  declare const badgeVariants: (props?: ({
11
- variant?: "outline" | "default" | "destructive" | "secondary" | null | undefined;
11
+ variant?: "default" | "secondary" | "destructive" | "outline" | null | undefined;
12
12
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
13
13
  type Props = React$1.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
14
14
  asChild?: boolean;
@@ -8,7 +8,7 @@ declare namespace button_d_exports {
8
8
  export { Button, Props, buttonVariants };
9
9
  }
10
10
  declare const buttonVariants: (props?: ({
11
- variant?: "link" | "outline" | "default" | "destructive" | "secondary" | "ghost" | null | undefined;
11
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
12
12
  size?: "default" | "icon" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
13
13
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
14
14
  type Props = React$1.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
package/dist/index.d.mts CHANGED
@@ -48,8 +48,8 @@ import { PageHeader, Props as Props$16 } from "./components/templates/page-heade
48
48
  import { Props as Props$19, Row } from "./components/templates/grid/row.mjs";
49
49
  import { Col, Props as Props$1 } from "./components/templates/grid/col.mjs";
50
50
  import "./components/templates/index.mjs";
51
- import { Locale, ThemeConfig, ThemeToken } from "./providers/config/types.mjs";
51
+ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./providers/config/types.mjs";
52
52
  import { DEFAULT_LOCALE, useConfig } from "./providers/config/context.mjs";
53
53
  import { Config, Props as Props$3 } from "./providers/config/config.mjs";
54
54
  import "./providers.mjs";
55
- export { Button, Card, Props as CardProps, Checkbox, Col, Props$1 as ColProps, Collapse, Props$2 as CollapseProps, ColorPicker, Config, Props$3 as ConfigProps, Container, Props$4 as ContainerProps, Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, Drawer, Props$6 as DrawerProps, Dropdown, Props$7 as DropdownProps, Empty, Props$8 as EmptyProps, FloatButton, Props$9 as FooterProps, Props$10 as HeaderProps, Input, Layout, Props$11 as LayoutProps, List, Props$12 as ListProps, Locale, Marquees, Props$13 as MarqueesProps, Menu, Props$14 as MenuProps, Modal, Props$15 as ModalProps, PageHeader, Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, Props$17 as ResultProps, Reveals, Props$18 as RevealsProps, Row, Props$19 as RowProps, Select, Props$20 as SiderProps, Skeleton, Space, Props$21 as SpaceProps, Spin, Splitter, Props$22 as SplitterProps, Swiper, Props$23 as SwiperProps, Switch, Tag, ThemeConfig, ThemeToken, Toast, StaticProps as ToastOptions, Typography, Upload, UploadFile, Props$24 as UploadProps, useConfig };
55
+ export { Button, Card, Props as CardProps, Checkbox, Col, Props$1 as ColProps, Collapse, Props$2 as CollapseProps, ColorPicker, ComponentSize, Config, Props$3 as ConfigProps, Container, Props$4 as ContainerProps, Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, DefaultProps, Drawer, Props$6 as DrawerProps, Dropdown, Props$7 as DropdownProps, Empty, Props$8 as EmptyProps, FloatButton, Props$9 as FooterProps, Props$10 as HeaderProps, Input, Layout, Props$11 as LayoutProps, List, Props$12 as ListProps, Locale, Marquees, Props$13 as MarqueesProps, Menu, Props$14 as MenuProps, Modal, Props$15 as ModalProps, PageHeader, Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, Props$17 as ResultProps, Reveals, Props$18 as RevealsProps, Row, Props$19 as RowProps, Select, Props$20 as SiderProps, Skeleton, Space, Props$21 as SpaceProps, Spin, Splitter, Props$22 as SplitterProps, Swiper, Props$23 as SwiperProps, Switch, Tag, ThemeConfig, ThemeToken, Toast, StaticProps as ToastOptions, Typography, Upload, UploadFile, Props$24 as UploadProps, useConfig };
@@ -1,4 +1,4 @@
1
- import { Locale, ThemeConfig, ThemeToken } from "./types.mjs";
1
+ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./types.mjs";
2
2
  import { DEFAULT_LOCALE, useConfig } from "./context.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
@@ -7,14 +7,22 @@ interface Props {
7
7
  theme?: ThemeConfig;
8
8
  locale?: Locale;
9
9
  getContainer?: () => HTMLElement;
10
+ componentSize?: ComponentSize;
11
+ direction?: 'ltr' | 'rtl';
12
+ defaultProps?: DefaultProps;
10
13
  className?: string;
14
+ style?: React.CSSProperties;
11
15
  children: React.ReactNode;
12
16
  }
13
17
  declare const Config: ({
14
18
  theme,
15
19
  locale,
16
20
  getContainer,
21
+ componentSize,
22
+ direction,
23
+ defaultProps,
17
24
  className,
25
+ style,
18
26
  children
19
27
  }: Props) => react_jsx_runtime0.JSX.Element;
20
28
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../../../src/providers/config/config.tsx"],"mappings":";;;;;UAmDU,KAAA;EACR,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,MAAA;EACT,YAAA,SAAqB,WAAA;EACrB,SAAA;EACA,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA;AAAA,cAGZ,MAAA;EAAU,KAAA;EAAA,MAAA;EAAA,YAAA;EAAA,SAAA;EAAA;AAAA,GAMb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"config.d.mts","names":[],"sources":["../../../src/providers/config/config.tsx"],"mappings":";;;;;UAgHU,KAAA;EACR,KAAA,GAAQ,WAAA;EACR,MAAA,GAAS,MAAA;EACT,YAAA,SAAqB,WAAA;EACrB,aAAA,GAAgB,aAAA;EAChB,SAAA;EACA,YAAA,GAAe,YAAA;EACf,SAAA;EACA,KAAA,GAAQ,KAAA,CAAM,aAAA;EACd,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA;AAAA,cAGZ,MAAA;EAAU,KAAA;EAAA,MAAA;EAAA,YAAA;EAAA,aAAA;EAAA,SAAA;EAAA,YAAA;EAAA,SAAA;EAAA,KAAA;EAAA;AAAA,GAUb,KAAA,KAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -2,10 +2,21 @@
2
2
 
3
3
  import { cn } from "../../utils.mjs";
4
4
  import { Context, DEFAULT_LOCALE, useConfig } from "./context.mjs";
5
- import { useCallback, useMemo, useState } from "react";
5
+ import { useCallback, useMemo, useState, useSyncExternalStore } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
+ import { DirectionProvider } from "@radix-ui/react-direction";
7
8
 
8
9
  //#region src/providers/config/config.tsx
10
+ const DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
11
+ const subscribeToSystemColorScheme = (callback) => {
12
+ if (typeof window === "undefined") return () => {};
13
+ const mql = window.matchMedia(DARK_MEDIA_QUERY);
14
+ mql.addEventListener("change", callback);
15
+ return () => mql.removeEventListener("change", callback);
16
+ };
17
+ const getSystemPrefersDark = () => typeof window !== "undefined" && window.matchMedia(DARK_MEDIA_QUERY).matches;
18
+ const getServerSnapshot = () => false;
19
+ const useSystemPrefersDark = () => useSyncExternalStore(subscribeToSystemColorScheme, getSystemPrefersDark, getServerSnapshot);
9
20
  const tokenToCssVar = {
10
21
  colorPrimary: "--primary",
11
22
  colorPrimaryForeground: "--primary-foreground",
@@ -25,7 +36,27 @@ const tokenToCssVar = {
25
36
  colorBorder: "--border",
26
37
  colorInput: "--input",
27
38
  colorRing: "--ring",
28
- borderRadius: "--radius"
39
+ borderRadius: "--radius",
40
+ btnBackground: "--btn-bg",
41
+ btnBackgroundHover: "--btn-bg-hover",
42
+ btnBackgroundActive: "--btn-bg-active",
43
+ btnBorder: "--btn-border",
44
+ btnForeground: "--btn-fg",
45
+ sidebar: "--sidebar",
46
+ sidebarForeground: "--sidebar-foreground",
47
+ sidebarPrimary: "--sidebar-primary",
48
+ sidebarPrimaryForeground: "--sidebar-primary-foreground",
49
+ sidebarAccent: "--sidebar-accent",
50
+ sidebarAccentForeground: "--sidebar-accent-foreground",
51
+ sidebarBorder: "--sidebar-border",
52
+ sidebarRing: "--sidebar-ring",
53
+ chart1: "--chart-1",
54
+ chart2: "--chart-2",
55
+ chart3: "--chart-3",
56
+ chart4: "--chart-4",
57
+ chart5: "--chart-5",
58
+ fontSans: "--font-sans",
59
+ fontMono: "--font-mono"
29
60
  };
30
61
  const buildCssVars = (token) => {
31
62
  if (!token) return {};
@@ -37,7 +68,7 @@ const buildCssVars = (token) => {
37
68
  }
38
69
  return vars;
39
70
  };
40
- const Config = ({ theme = {}, locale = {}, getContainer, className, children }) => {
71
+ const Config = ({ theme = {}, locale = {}, getContainer, componentSize, direction, defaultProps, className, style, children }) => {
41
72
  const parent = useConfig();
42
73
  const [wrapperEl, setWrapperEl] = useState(null);
43
74
  const themeKey = JSON.stringify(theme);
@@ -50,16 +81,29 @@ const Config = ({ theme = {}, locale = {}, getContainer, className, children })
50
81
  token: {
51
82
  ...parent.theme.token,
52
83
  ...theme.token
84
+ },
85
+ darkToken: {
86
+ ...parent.theme.darkToken,
87
+ ...theme.darkToken
53
88
  }
54
89
  }), [parentThemeKey, themeKey]);
55
90
  const mergedLocale = useMemo(() => ({
56
91
  ...parent.locale,
57
92
  ...locale
58
93
  }), [parentLocaleKey, localeKey]);
59
- const cssVars = useMemo(() => buildCssVars(mergedTheme.token), [mergedTheme.token]);
94
+ const systemPrefersDark = useSystemPrefersDark();
95
+ const isDarkActive = mergedTheme.dark === "system" ? systemPrefersDark : mergedTheme.dark === "dark";
96
+ const cssVars = useMemo(() => buildCssVars(isDarkActive ? {
97
+ ...mergedTheme.token,
98
+ ...mergedTheme.darkToken
99
+ } : mergedTheme.token), [
100
+ mergedTheme.token,
101
+ mergedTheme.darkToken,
102
+ isDarkActive
103
+ ]);
60
104
  const hasCssVars = Object.keys(cssVars).length > 0;
61
105
  const hasDarkMode = mergedTheme.dark !== void 0;
62
- const needsWrapper = hasCssVars || hasDarkMode || className;
106
+ const needsWrapper = hasCssVars || hasDarkMode || !!className || !!style || !!direction;
63
107
  const resolvedGetContainer = useCallback(() => {
64
108
  if (getContainer) return getContainer();
65
109
  if (needsWrapper && wrapperEl) return wrapperEl;
@@ -70,27 +114,47 @@ const Config = ({ theme = {}, locale = {}, getContainer, className, children })
70
114
  wrapperEl,
71
115
  parent
72
116
  ]);
117
+ const resolvedComponentSize = componentSize ?? parent.componentSize;
118
+ const resolvedDirection = direction ?? parent.direction;
119
+ const defaultPropsKey = JSON.stringify(defaultProps);
120
+ const mergedDefaultProps = useMemo(() => parent.defaultProps || defaultProps ? {
121
+ ...parent.defaultProps,
122
+ ...defaultProps
123
+ } : void 0, [JSON.stringify(parent.defaultProps), defaultPropsKey]);
73
124
  const contextValue = useMemo(() => ({
74
125
  theme: mergedTheme,
75
126
  locale: mergedLocale,
76
- getContainer: resolvedGetContainer
127
+ getContainer: resolvedGetContainer,
128
+ componentSize: resolvedComponentSize,
129
+ direction: resolvedDirection,
130
+ defaultProps: mergedDefaultProps,
131
+ isConfigured: true
77
132
  }), [
78
133
  mergedTheme,
79
134
  mergedLocale,
80
- resolvedGetContainer
135
+ resolvedGetContainer,
136
+ resolvedComponentSize,
137
+ resolvedDirection,
138
+ mergedDefaultProps
81
139
  ]);
82
- return /* @__PURE__ */ jsx(Context.Provider, {
140
+ const content = /* @__PURE__ */ jsx(Context.Provider, {
83
141
  value: contextValue,
84
142
  children: needsWrapper ? /* @__PURE__ */ jsx("div", {
85
143
  ref: setWrapperEl,
86
- className: cn(mergedTheme.dark && "dark", className),
144
+ dir: direction,
145
+ className: cn(isDarkActive && "dark", className),
87
146
  style: {
88
147
  ...hasCssVars ? cssVars : void 0,
148
+ ...style,
89
149
  display: "contents"
90
150
  },
91
151
  children
92
152
  }) : children
93
153
  });
154
+ return direction ? /* @__PURE__ */ jsx(DirectionProvider, {
155
+ dir: direction,
156
+ children: content
157
+ }) : content;
94
158
  };
95
159
 
96
160
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState } from 'react';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport type { Locale, ThemeConfig, ThemeToken } from './types';\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n className?: string;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n className,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const cssVars = useMemo(\n () => buildCssVars(mergedTheme.token),\n [mergedTheme.token],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper = hasCssVars || hasDarkMode || className;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n }),\n [mergedTheme, mergedLocale, resolvedGetContainer],\n );\n\n return (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n className={cn(mergedTheme.dark && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className shouldn't be\n // used here for anything that needs a real box (padding,\n // background, borders); Config's className is meant for\n // theming hooks (the `dark` class, arbitrary selector hooks),\n // not layout.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type { Props, Locale, ThemeConfig, ThemeToken };\n"],"mappings":";;;;;;;;AASA,MAAM,gBAAkD;CACtD,cAAc;CACd,wBAAwB;CACxB,iBAAiB;CACjB,iBAAiB;CACjB,WAAW;CACX,qBAAqB;CACrB,cAAc;CACd,wBAAwB;CACxB,gBAAgB;CAChB,0BAA0B;CAC1B,YAAY;CACZ,sBAAsB;CACtB,aAAa;CACb,uBAAuB;CACvB,kBAAkB;CAClB,aAAa;CACb,YAAY;CACZ,WAAW;CACX,cAAc;CACf;AAED,MAAM,gBAAgB,UAA4C;AAChE,KAAI,CAAC,MACH,QAAO,EAAE;CAGX,MAAM,OAA+B,EAAE;AAEvC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE;AAChD,MAAI,UAAU,OACZ;EAEF,MAAM,SAAS,cAAc;AAC7B,MAAI,OACF,MAAK,UAAU;;AAInB,QAAO;;AAWT,MAAM,UAAU,EACd,QAAQ,EAAE,EACV,SAAS,EAAE,EACX,cACA,WACA,eACW;CACX,MAAM,SAAS,WAAW;CAC1B,MAAM,CAAC,WAAW,gBAAgB,SAAgC,KAAK;CAWvE,MAAM,WAAW,KAAK,UAAU,MAAM;CACtC,MAAM,iBAAiB,KAAK,UAAU,OAAO,MAAM;CACnD,MAAM,YAAY,KAAK,UAAU,OAAO;CACxC,MAAM,kBAAkB,KAAK,UAAU,OAAO,OAAO;CAErD,MAAM,cAAc,eACX;EACL,GAAG,OAAO;EACV,GAAG;EACH,OAAO;GACL,GAAG,OAAO,MAAM;GAChB,GAAG,MAAM;GACV;EACF,GAED,CAAC,gBAAgB,SAAS,CAC3B;CAED,MAAM,eAAe,eACZ;EACL,GAAG,OAAO;EACV,GAAG;EACJ,GAED,CAAC,iBAAiB,UAAU,CAC7B;CAED,MAAM,UAAU,cACR,aAAa,YAAY,MAAM,EACrC,CAAC,YAAY,MAAM,CACpB;CAED,MAAM,aAAa,OAAO,KAAK,QAAQ,CAAC,SAAS;CACjD,MAAM,cAAc,YAAY,SAAS;CACzC,MAAM,eAAe,cAAc,eAAe;CAUlD,MAAM,uBAAuB,kBAA2C;AACtE,MAAI,aACF,QAAO,cAAc;AAEvB,MAAI,gBAAgB,UAClB,QAAO;AAET,SAAO,OAAO,cAAc;IAC3B;EAAC;EAAc;EAAc;EAAW;EAAO,CAAC;CAEnD,MAAM,eAAe,eACZ;EACL,OAAO;EACP,QAAQ;EACR,cAAc;EACf,GACD;EAAC;EAAa;EAAc;EAAqB,CAClD;AAED,QACE,oBAAC,QAAQ;EAAS,OAAO;YACtB,eACC,oBAAC;GACC,KAAK;GACL,WAAW,GAAG,YAAY,QAAQ,QAAQ,UAAU;GACpD,OAAO;IACL,GAAI,aAAa,UAAU;IAW3B,SAAS;IACV;GAEA;IACG,GAEN;GAEe"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport { DirectionProvider } from '@radix-ui/react-direction';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport type {\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n} from './types';\n\nconst DARK_MEDIA_QUERY = '(prefers-color-scheme: dark)';\n\nconst subscribeToSystemColorScheme = (callback: () => void) => {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const mql = window.matchMedia(DARK_MEDIA_QUERY);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n};\n\nconst getSystemPrefersDark = () =>\n typeof window !== 'undefined' && window.matchMedia(DARK_MEDIA_QUERY).matches;\n\n// No `window` during SSR, so there's no way to know the visitor's actual\n// preference before hydration — assume light (the conservative default)\n// and let useSyncExternalStore correct it client-side once matchMedia is\n// available, same flash-of-incorrect-guess tradeoff already accepted for\n// Sider's breakpoint prop elsewhere in this library.\nconst getServerSnapshot = () => false;\n\nconst useSystemPrefersDark = () =>\n useSyncExternalStore(\n subscribeToSystemColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n\n btnBackground: '--btn-bg',\n btnBackgroundHover: '--btn-bg-hover',\n btnBackgroundActive: '--btn-bg-active',\n btnBorder: '--btn-border',\n btnForeground: '--btn-fg',\n\n sidebar: '--sidebar',\n sidebarForeground: '--sidebar-foreground',\n sidebarPrimary: '--sidebar-primary',\n sidebarPrimaryForeground: '--sidebar-primary-foreground',\n sidebarAccent: '--sidebar-accent',\n sidebarAccentForeground: '--sidebar-accent-foreground',\n sidebarBorder: '--sidebar-border',\n sidebarRing: '--sidebar-ring',\n\n chart1: '--chart-1',\n chart2: '--chart-2',\n chart3: '--chart-3',\n chart4: '--chart-4',\n chart5: '--chart-5',\n\n fontSans: '--font-sans',\n fontMono: '--font-mono',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n componentSize?: ComponentSize;\n direction?: 'ltr' | 'rtl';\n defaultProps?: DefaultProps;\n className?: string;\n style?: React.CSSProperties;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n componentSize,\n direction,\n defaultProps,\n className,\n style,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n darkToken: {\n ...parent.theme.darkToken,\n ...theme.darkToken,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const systemPrefersDark = useSystemPrefersDark();\n const isDarkActive =\n mergedTheme.dark === 'system'\n ? systemPrefersDark\n : mergedTheme.dark === 'dark';\n\n // `darkToken` overrides individual keys of `token` (not a full swap)\n // only while dark mode is actually active.\n const cssVars = useMemo(\n () =>\n buildCssVars(\n isDarkActive\n ? { ...mergedTheme.token, ...mergedTheme.darkToken }\n : mergedTheme.token,\n ),\n [mergedTheme.token, mergedTheme.darkToken, isDarkActive],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper =\n hasCssVars || hasDarkMode || !!className || !!style || !!direction;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const resolvedComponentSize = componentSize ?? parent.componentSize;\n const resolvedDirection = direction ?? parent.direction;\n\n const defaultPropsKey = JSON.stringify(defaultProps);\n const parentDefaultPropsKey = JSON.stringify(parent.defaultProps);\n\n const mergedDefaultProps = useMemo<DefaultProps | undefined>(\n () =>\n parent.defaultProps || defaultProps\n ? { ...parent.defaultProps, ...defaultProps }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentDefaultPropsKey, defaultPropsKey],\n );\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n componentSize: resolvedComponentSize,\n direction: resolvedDirection,\n defaultProps: mergedDefaultProps,\n isConfigured: true,\n }),\n [\n mergedTheme,\n mergedLocale,\n resolvedGetContainer,\n resolvedComponentSize,\n resolvedDirection,\n mergedDefaultProps,\n ],\n );\n\n const content = (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n dir={direction}\n className={cn(isDarkActive && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n ...style,\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className/style\n // shouldn't be used here for anything that needs a real box\n // (padding, background, borders); Config's className/style are\n // meant for theming hooks (the `dark` class, arbitrary\n // selector hooks, custom properties), not layout — spread\n // after the caller's `style` so it can't be overridden into\n // something that reintroduces the layout-breaking wrapper this\n // was added to fix in the first place.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n\n // DirectionProvider is Radix's own mechanism (@radix-ui/react-direction)\n // for propagating `dir` to every primitive that calls its useDirection()\n // internally — Select, Menu (Dropdown/Menu), Accordion (Collapse),\n // RadioGroup, ScrollArea all pick up RTL automatically this way, no\n // per-component wiring needed. Only wraps when this Config's own\n // `direction` prop is set (not the merely-inherited resolved value) —\n // context already cascades on its own, so re-wrapping at every nested\n // Config that doesn't override direction would just be redundant.\n // Components with hardcoded LTR-only layout logic that Radix's Direction\n // context doesn't reach (Popover placement, Splitter, Sider, Drawer)\n // aren't covered by this and would need dedicated follow-up work.\n return direction ? (\n <DirectionProvider dir={direction}>{content}</DirectionProvider>\n ) : (\n content\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type {\n Props,\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.mts","names":[],"sources":["../../../src/providers/config/context.ts"],"mappings":";;;;cAMa,cAAA,EAAgB,QAAA,CAAS,MAAA;AAAA,cAezB,SAAA,QAAS,YAAA"}
1
+ {"version":3,"file":"context.d.mts","names":[],"sources":["../../../src/providers/config/context.ts"],"mappings":";;;;cAMa,cAAA,EAAgB,QAAA,CAAS,MAAA;AAAA,cAgBzB,SAAA,QAAS,YAAA"}
@@ -14,7 +14,8 @@ const DEFAULT_LOCALE = {
14
14
  const Context = createContext({
15
15
  theme: {},
16
16
  locale: DEFAULT_LOCALE,
17
- getContainer: () => void 0
17
+ getContainer: () => void 0,
18
+ isConfigured: false
18
19
  });
19
20
  const useConfig = () => useContext(Context);
20
21
 
@@ -1 +1 @@
1
- {"version":3,"file":"context.mjs","names":[],"sources":["../../../src/providers/config/context.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\n\nimport type { ContextValue, Locale } from './types';\n\nexport const DEFAULT_LOCALE: Required<Locale> = {\n close: '닫기',\n clear: '지우기',\n backToTop: '맨 위로',\n loading: '로딩 중',\n expand: '펼치기',\n collapse: '접기',\n};\n\nexport const Context = createContext<ContextValue>({\n theme: {},\n locale: DEFAULT_LOCALE,\n getContainer: () => undefined,\n});\n\nexport const useConfig = () => useContext(Context);\n"],"mappings":";;;;;AAMA,MAAa,iBAAmC;CAC9C,OAAO;CACP,OAAO;CACP,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACX;AAED,MAAa,UAAU,cAA4B;CACjD,OAAO,EAAE;CACT,QAAQ;CACR,oBAAoB;CACrB,CAAC;AAEF,MAAa,kBAAkB,WAAW,QAAQ"}
1
+ {"version":3,"file":"context.mjs","names":[],"sources":["../../../src/providers/config/context.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\n\nimport type { ContextValue, Locale } from './types';\n\nexport const DEFAULT_LOCALE: Required<Locale> = {\n close: '닫기',\n clear: '지우기',\n backToTop: '맨 위로',\n loading: '로딩 중',\n expand: '펼치기',\n collapse: '접기',\n};\n\nexport const Context = createContext<ContextValue>({\n theme: {},\n locale: DEFAULT_LOCALE,\n getContainer: () => undefined,\n isConfigured: false,\n});\n\nexport const useConfig = () => useContext(Context);\n"],"mappings":";;;;;AAMA,MAAa,iBAAmC;CAC9C,OAAO;CACP,OAAO;CACP,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACX;AAED,MAAa,UAAU,cAA4B;CACjD,OAAO,EAAE;CACT,QAAQ;CACR,oBAAoB;CACpB,cAAc;CACf,CAAC;AAEF,MAAa,kBAAkB,WAAW,QAAQ"}
@@ -1,3 +1,3 @@
1
- import { Locale, ThemeConfig, ThemeToken } from "./types.mjs";
1
+ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./types.mjs";
2
2
  import { DEFAULT_LOCALE, useConfig } from "./context.mjs";
3
3
  import { Config, Props } from "./config.mjs";
@@ -19,10 +19,31 @@ interface ThemeToken {
19
19
  colorInput?: string;
20
20
  colorRing?: string;
21
21
  borderRadius?: string;
22
+ btnBackground?: string;
23
+ btnBackgroundHover?: string;
24
+ btnBackgroundActive?: string;
25
+ btnBorder?: string;
26
+ btnForeground?: string;
27
+ sidebar?: string;
28
+ sidebarForeground?: string;
29
+ sidebarPrimary?: string;
30
+ sidebarPrimaryForeground?: string;
31
+ sidebarAccent?: string;
32
+ sidebarAccentForeground?: string;
33
+ sidebarBorder?: string;
34
+ sidebarRing?: string;
35
+ chart1?: string;
36
+ chart2?: string;
37
+ chart3?: string;
38
+ chart4?: string;
39
+ chart5?: string;
40
+ fontSans?: string;
41
+ fontMono?: string;
22
42
  }
23
43
  interface ThemeConfig {
24
44
  token?: ThemeToken;
25
- dark?: boolean;
45
+ darkToken?: ThemeToken;
46
+ dark?: 'light' | 'dark' | 'system';
26
47
  }
27
48
  interface Locale {
28
49
  close?: string;
@@ -32,11 +53,17 @@ interface Locale {
32
53
  expand?: string;
33
54
  collapse?: string;
34
55
  }
56
+ type ComponentSize = 'small' | 'middle' | 'large';
57
+ type DefaultProps = Record<string, Record<string, unknown>>;
35
58
  interface ContextValue {
36
59
  theme: ThemeConfig;
37
60
  locale: Locale;
61
+ direction?: 'ltr' | 'rtl';
38
62
  getContainer: () => HTMLElement | undefined;
63
+ componentSize?: ComponentSize;
64
+ defaultProps?: DefaultProps;
65
+ isConfigured: boolean;
39
66
  }
40
67
  //#endregion
41
- export { ContextValue, Locale, ThemeConfig, ThemeToken };
68
+ export { ComponentSize, ContextValue, DefaultProps, Locale, ThemeConfig, ThemeToken };
42
69
  //# sourceMappingURL=types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";UAAiB,UAAA;EAEf,YAAA;EACA,sBAAA;EACA,eAAA;EACA,eAAA;EACA,SAAA;EACA,mBAAA;EACA,YAAA;EACA,sBAAA;EACA,cAAA;EACA,wBAAA;EACA,UAAA;EACA,oBAAA;EACA,WAAA;EACA,qBAAA;EACA,gBAAA;EACA,WAAA;EACA,UAAA;EACA,SAAA;EAGA,YAAA;AAAA;AAAA,UAGe,WAAA;EACf,KAAA,GAAQ,UAAA;EACR,IAAA;AAAA;AAAA,UAQe,MAAA;EACf,KAAA;EACA,KAAA;EACA,SAAA;EACA,OAAA;EACA,MAAA;EACA,QAAA;AAAA;AAAA,UAGe,YAAA;EACf,KAAA,EAAO,WAAA;EACP,MAAA,EAAQ,MAAA;EAUR,YAAA,QAAoB,WAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";UAAiB,UAAA;EAEf,YAAA;EACA,sBAAA;EACA,eAAA;EACA,eAAA;EACA,SAAA;EACA,mBAAA;EACA,YAAA;EACA,sBAAA;EACA,cAAA;EACA,wBAAA;EACA,UAAA;EACA,oBAAA;EACA,WAAA;EACA,qBAAA;EACA,gBAAA;EACA,WAAA;EACA,UAAA;EACA,SAAA;EASA,YAAA;EAIA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,SAAA;EACA,aAAA;EAGA,OAAA;EACA,iBAAA;EACA,cAAA;EACA,wBAAA;EACA,aAAA;EACA,uBAAA;EACA,aAAA;EACA,WAAA;EAGA,MAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA;EACA,MAAA;EAGA,QAAA;EACA,QAAA;AAAA;AAAA,UAGe,WAAA;EACf,KAAA,GAAQ,UAAA;EAKR,SAAA,GAAY,UAAA;EASZ,IAAA;AAAA;AAAA,UAQe,MAAA;EACf,KAAA;EACA,KAAA;EACA,SAAA;EACA,OAAA;EACA,MAAA;EACA,QAAA;AAAA;AAAA,KAMU,aAAA;AAAA,KAYA,YAAA,GAAe,MAAA,SAAe,MAAA;AAAA,UAEzB,YAAA;EACf,KAAA,EAAO,WAAA;EACP,MAAA,EAAQ,MAAA;EACR,SAAA;EAUA,YAAA,QAAoB,WAAA;EACpB,aAAA,GAAgB,aAAA;EAChB,YAAA,GAAe,YAAA;EAKf,YAAA;AAAA"}
@@ -1,5 +1,5 @@
1
- import { Locale, ThemeConfig, ThemeToken } from "./providers/config/types.mjs";
1
+ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./providers/config/types.mjs";
2
2
  import { DEFAULT_LOCALE, useConfig } from "./providers/config/context.mjs";
3
3
  import { Config, Props } from "./providers/config/config.mjs";
4
4
  import "./providers/config/index.mjs";
5
- export { Config, type Props as ConfigProps, DEFAULT_LOCALE, type Locale, type ThemeConfig, type ThemeToken, useConfig };
5
+ export { type ComponentSize, Config, type Props as ConfigProps, DEFAULT_LOCALE, type DefaultProps, type Locale, type ThemeConfig, type ThemeToken, useConfig };
package/dist/style.css CHANGED
@@ -3,6 +3,8 @@
3
3
  @layer theme, base, components, utilities;
4
4
  @layer theme {
5
5
  :root, :host {
6
+ --font-sans: var(--font-geist-sans);
7
+ --font-mono: var(--font-geist-mono);
6
8
  --color-red-400: oklch(70.4% 0.191 22.216);
7
9
  --color-yellow-400: oklch(85.2% 0.199 91.936);
8
10
  --color-yellow-500: oklch(79.5% 0.184 86.047);
@@ -54,6 +56,8 @@
54
56
  --leading-snug: 1.375;
55
57
  --leading-normal: 1.5;
56
58
  --radius-xs: 0.125rem;
59
+ --radius-sm: calc(var(--radius) - 4px);
60
+ --radius-lg: var(--radius);
57
61
  --animate-spin: spin 1s linear infinite;
58
62
  --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
59
63
  --default-transition-duration: 150ms;
@@ -1426,6 +1430,9 @@
1426
1430
  .text-yellow-600 {
1427
1431
  color: var(--color-yellow-600);
1428
1432
  }
1433
+ .lowercase {
1434
+ text-transform: lowercase;
1435
+ }
1429
1436
  .underline {
1430
1437
  text-decoration-line: underline;
1431
1438
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/ui-kit",
3
- "version": "4.1.0",
3
+ "version": "5.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",
@@ -131,6 +131,7 @@
131
131
  "@radix-ui/react-accordion": "^1.2.12",
132
132
  "@radix-ui/react-checkbox": "^1.3.3",
133
133
  "@radix-ui/react-dialog": "^1.1.15",
134
+ "@radix-ui/react-direction": "^1.1.1",
134
135
  "@radix-ui/react-label": "^2.1.8",
135
136
  "@radix-ui/react-popover": "^1.1.15",
136
137
  "@radix-ui/react-progress": "^1.1.7",