sh-ui-cli 0.45.1 → 0.45.3
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.
- package/data/changelog/versions.json +25 -0
- package/data/registry/react/components/accordion/index.tailwind.tsx +5 -7
- package/data/registry/react/components/accordion/index.tsx +5 -7
- package/data/registry/react/components/avatar/index.tailwind.tsx +4 -6
- package/data/registry/react/components/avatar/index.tsx +4 -6
- package/data/registry/react/components/badge/index.tailwind.tsx +2 -4
- package/data/registry/react/components/badge/index.tsx +2 -4
- package/data/registry/react/components/breadcrumb/index.tailwind.tsx +8 -10
- package/data/registry/react/components/breadcrumb/index.tsx +8 -10
- package/data/registry/react/components/button/index.tailwind.tsx +2 -1
- package/data/registry/react/components/button/index.tsx +3 -4
- package/data/registry/react/components/calendar/index.tailwind.tsx +10 -12
- package/data/registry/react/components/calendar/index.tsx +9 -11
- package/data/registry/react/components/card/index.tailwind.tsx +8 -10
- package/data/registry/react/components/card/index.tsx +8 -10
- package/data/registry/react/components/carousel/index.tailwind.tsx +7 -9
- package/data/registry/react/components/carousel/index.tsx +7 -9
- package/data/registry/react/components/checkbox/index.tailwind.tsx +3 -5
- package/data/registry/react/components/checkbox/index.tsx +3 -5
- package/data/registry/react/components/code-editor/index.tailwind.tsx +2 -4
- package/data/registry/react/components/code-editor/index.tsx +2 -4
- package/data/registry/react/components/code-panel/index.tailwind.tsx +5 -7
- package/data/registry/react/components/code-panel/index.tsx +5 -7
- package/data/registry/react/components/color-picker/index.tailwind.tsx +7 -6
- package/data/registry/react/components/color-picker/index.tsx +7 -6
- package/data/registry/react/components/combobox/index.tailwind.tsx +8 -10
- package/data/registry/react/components/combobox/index.tsx +8 -10
- package/data/registry/react/components/context-menu/index.tailwind.tsx +10 -12
- package/data/registry/react/components/context-menu/index.tsx +10 -12
- package/data/registry/react/components/date-picker/index.tailwind.tsx +7 -9
- package/data/registry/react/components/date-picker/index.tsx +7 -9
- package/data/registry/react/components/dialog/index.tailwind.tsx +6 -8
- package/data/registry/react/components/dialog/index.tsx +6 -8
- package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +10 -12
- package/data/registry/react/components/dropdown-menu/index.tsx +10 -12
- package/data/registry/react/components/file-upload/index.tailwind.tsx +6 -8
- package/data/registry/react/components/file-upload/index.tsx +6 -8
- package/data/registry/react/components/form/field.tailwind.tsx +2 -1
- package/data/registry/react/components/form/field.tsx +2 -3
- package/data/registry/react/components/header/index.tailwind.tsx +17 -19
- package/data/registry/react/components/header/index.tsx +17 -19
- package/data/registry/react/components/input/index.tailwind.tsx +4 -6
- package/data/registry/react/components/input/index.tsx +4 -6
- package/data/registry/react/components/label/index.tailwind.tsx +6 -8
- package/data/registry/react/components/label/index.tsx +6 -8
- package/data/registry/react/components/markdown-editor/index.tailwind.tsx +2 -4
- package/data/registry/react/components/markdown-editor/index.tsx +2 -4
- package/data/registry/react/components/menubar/index.tailwind.tsx +2 -4
- package/data/registry/react/components/menubar/index.tsx +2 -4
- package/data/registry/react/components/numeric-input/index.tailwind.tsx +2 -4
- package/data/registry/react/components/numeric-input/index.tsx +2 -4
- package/data/registry/react/components/page-toc/index.tailwind.tsx +3 -2
- package/data/registry/react/components/page-toc/index.tsx +2 -3
- package/data/registry/react/components/pagination/index.tailwind.tsx +8 -10
- package/data/registry/react/components/pagination/index.tsx +8 -10
- package/data/registry/react/components/popover/index.tailwind.tsx +4 -6
- package/data/registry/react/components/popover/index.tsx +4 -6
- package/data/registry/react/components/progress/index.tailwind.tsx +3 -5
- package/data/registry/react/components/progress/index.tsx +2 -4
- package/data/registry/react/components/radio/index.tailwind.tsx +3 -5
- package/data/registry/react/components/radio/index.tsx +3 -5
- package/data/registry/react/components/rich-text-editor/index.tailwind.tsx +3 -5
- package/data/registry/react/components/rich-text-editor/index.tsx +3 -5
- package/data/registry/react/components/select/index.tailwind.tsx +8 -10
- package/data/registry/react/components/select/index.tsx +8 -10
- package/data/registry/react/components/separator/index.tailwind.tsx +2 -4
- package/data/registry/react/components/separator/index.tsx +2 -4
- package/data/registry/react/components/sidebar/index.tailwind.tsx +32 -43
- package/data/registry/react/components/sidebar/index.tsx +29 -46
- package/data/registry/react/components/skeleton/index.tailwind.tsx +2 -4
- package/data/registry/react/components/skeleton/index.tsx +2 -4
- package/data/registry/react/components/slider/index.tailwind.tsx +5 -7
- package/data/registry/react/components/slider/index.tsx +5 -7
- package/data/registry/react/components/spinner/index.tailwind.tsx +3 -5
- package/data/registry/react/components/spinner/index.tsx +2 -4
- package/data/registry/react/components/switch/index.tailwind.tsx +3 -5
- package/data/registry/react/components/switch/index.tsx +2 -4
- package/data/registry/react/components/tabs/index.tailwind.tsx +6 -8
- package/data/registry/react/components/tabs/index.tsx +6 -8
- package/data/registry/react/components/textarea/index.tailwind.tsx +2 -4
- package/data/registry/react/components/textarea/index.tsx +2 -4
- package/data/registry/react/components/toggle/index.tailwind.tsx +4 -6
- package/data/registry/react/components/toggle/index.tsx +4 -6
- package/data/registry/react/components/tooltip/index.tailwind.tsx +2 -4
- package/data/registry/react/components/tooltip/index.tsx +2 -4
- package/data/registry/react/lib/cn.tailwind.ts +17 -0
- package/data/registry/react/peer-versions.json +3 -1
- package/data/registry/react/registry.json +159 -43
- package/package.json +1 -1
- package/src/add.mjs +25 -1
- package/src/mcp-init.mjs +13 -5
- package/src/mcp.mjs +20 -15
- package/templates/ui-app-template/sh-ui.config.json +5 -0
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
|
|
4
4
|
"versions": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.45.3",
|
|
7
|
+
"date": "2026-04-30",
|
|
8
|
+
"title": "공유 `cn` 유틸 + CSS 프레임워크 가이드 docs",
|
|
9
|
+
"type": "patch",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**공유 `cn` 유틸** — 컴포넌트 74 곳에 흩어져 있던 로컬 `cx`/`mergeClass` 함수 제거. 모두 `lib/utils.ts` 의 `cn` 으로 통일. plain 변종은 zero-dep custom 구현, tailwind 변종은 `clsx + tailwind-merge` 로 utility 충돌 머지(예: `cn(\"bg-primary\", \"bg-red-500\")` → `\"bg-red-500\"`). registry.json 에 `utils` 엔트리 신설, styled 컴포넌트 43 개가 `registryDependencies: [\"utils\"]` 로 자동 의존 — `add button` 시 `utils` 도 함께 카피.",
|
|
12
|
+
"**`@SH_UI_UTILS@` placeholder 치환** — 컴포넌트가 `import { cn } from \"@SH_UI_UTILS@\"` 로 import 하면 CLI 가 add 시점에 `aliases.utils` 값(예: `@/lib/utils`)으로 치환. cn 유틸 import 경로가 사용자 프로젝트 alias 와 자동 맞아떨어짐. `aliases.utils` 미설정 시 친절 에러로 안내.",
|
|
13
|
+
"**CSS 프레임워크 docs 페이지 추가** — `apps/docs/(docs)/css-framework/page.tsx` 신설. 변종 시스템 큰 그림 (현재 plain/tailwind + 계획 중 css-modules/vanilla-extract) + 모드별 차이·전환·fallback 설명. 사이드바 nav 에 \"CSS 프레임워크\" 링크 추가, cli 페이지에서 cross-link.",
|
|
14
|
+
"**ui-app-template config 보강** — 누락됐던 `aliases.utils` 추가. 신규 init 도 동일하게 채워짐."
|
|
15
|
+
],
|
|
16
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.45.3"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"version": "0.45.2",
|
|
20
|
+
"date": "2026-04-30",
|
|
21
|
+
"title": "추가 하드코딩 제거 — release URL · MCP 패키지명 동적 파생",
|
|
22
|
+
"type": "patch",
|
|
23
|
+
"highlights": [
|
|
24
|
+
"**`scripts/release.mjs` 의 GitHub URL 동적화** — `cli/package.json` 의 `repository.url` 에서 base URL 추출 (`.git` suffix + `git+` prefix 정리) 후 `${base}/releases/tag/v${ver}` 빌드. owner/repo 이름이 바뀌면 cli/package.json 한 곳만 갱신하면 따라옴.",
|
|
25
|
+
"**MCP 서버의 cli 패키지명 동적 읽기** — `mcp-init.mjs` 의 `SH_UI_ENTRY.args` 와 `mcp.mjs` 의 `SERVER_INSTRUCTIONS`/tool description 안 `npx <cli> create ...` 예시가 모두 `package.json` 의 `name` 에서 동적 보간. cli 가 rename 되어도 자동 동기화.",
|
|
26
|
+
"기능 변경 0 — drift 위험만 추가 제거. v0.45.1 의 mcp.mjs version 동적 읽기 + `pnpm release` 에 이은 후속 정리."
|
|
27
|
+
],
|
|
28
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.45.2"
|
|
29
|
+
},
|
|
5
30
|
{
|
|
6
31
|
"version": "0.45.1",
|
|
7
32
|
"date": "2026-04-30",
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Accordion as BaseAccordion } from "@base-ui/react/accordion";
|
|
3
3
|
|
|
4
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
5
|
-
return args.filter(Boolean).join(" ");
|
|
6
|
-
}
|
|
7
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
8
6
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
9
7
|
|
|
10
8
|
export type AccordionSize = "sm" | "md";
|
|
@@ -19,7 +17,7 @@ export const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
19
17
|
({ className, size = "md", ...props }, ref) => (
|
|
20
18
|
<BaseAccordion.Root
|
|
21
19
|
ref={ref}
|
|
22
|
-
className={
|
|
20
|
+
className={cn("flex flex-col w-full", className)}
|
|
23
21
|
data-size={size}
|
|
24
22
|
{...props}
|
|
25
23
|
/>
|
|
@@ -33,7 +31,7 @@ export const AccordionItem = React.forwardRef<
|
|
|
33
31
|
>(({ className, ...props }, ref) => (
|
|
34
32
|
<BaseAccordion.Item
|
|
35
33
|
ref={ref}
|
|
36
|
-
className={
|
|
34
|
+
className={cn("border-b border-border first:border-t", className)}
|
|
37
35
|
{...props}
|
|
38
36
|
/>
|
|
39
37
|
));
|
|
@@ -46,7 +44,7 @@ export const AccordionTrigger = React.forwardRef<
|
|
|
46
44
|
<BaseAccordion.Header className="m-0 font-[inherit]">
|
|
47
45
|
<BaseAccordion.Trigger
|
|
48
46
|
ref={ref}
|
|
49
|
-
className={
|
|
47
|
+
className={cn(
|
|
50
48
|
"flex items-center justify-between gap-[var(--space-4)] w-full px-[var(--space-1)] py-[var(--space-4)] bg-transparent border-none text-foreground text-[0.9375rem] font-medium leading-snug text-left cursor-pointer transition-[background-color] duration-[var(--duration-fast)] hover:not-disabled:not-data-[disabled]:bg-background-muted focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 focus-visible:rounded-[calc(var(--radius)-2px)] disabled:cursor-not-allowed disabled:text-foreground-muted data-[disabled]:cursor-not-allowed data-[disabled]:text-foreground-muted [[data-size=sm]_&]:py-[var(--space-2)] [[data-size=sm]_&]:text-[length:var(--text-xs)] [[data-size=sm]_&]:leading-[1.2] motion-reduce:transition-none",
|
|
51
49
|
className,
|
|
52
50
|
)}
|
|
@@ -74,7 +72,7 @@ export const AccordionContent = React.forwardRef<
|
|
|
74
72
|
>(({ className, children, ...props }, ref) => (
|
|
75
73
|
<BaseAccordion.Panel
|
|
76
74
|
ref={ref}
|
|
77
|
-
className={
|
|
75
|
+
className={cn(
|
|
78
76
|
"overflow-hidden h-[var(--accordion-panel-height)] transition-[height] duration-[var(--duration-slow)] data-[starting-style]:h-0 data-[ending-style]:h-0 motion-reduce:transition-none",
|
|
79
77
|
className,
|
|
80
78
|
)}
|
|
@@ -2,10 +2,8 @@ import * as React from "react";
|
|
|
2
2
|
import { Accordion as BaseAccordion } from "@base-ui/react/accordion";
|
|
3
3
|
import "./styles.css";
|
|
4
4
|
|
|
5
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
6
|
-
return args.filter(Boolean).join(" ");
|
|
7
|
-
}
|
|
8
5
|
|
|
6
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
9
7
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
10
8
|
|
|
11
9
|
export type AccordionSize = "sm" | "md";
|
|
@@ -26,7 +24,7 @@ export const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
26
24
|
({ className, size = "md", ...props }, ref) => (
|
|
27
25
|
<BaseAccordion.Root
|
|
28
26
|
ref={ref}
|
|
29
|
-
className={
|
|
27
|
+
className={cn("sh-ui-accordion", className)}
|
|
30
28
|
data-size={size}
|
|
31
29
|
{...props}
|
|
32
30
|
/>
|
|
@@ -40,7 +38,7 @@ export const AccordionItem = React.forwardRef<
|
|
|
40
38
|
>(({ className, ...props }, ref) => (
|
|
41
39
|
<BaseAccordion.Item
|
|
42
40
|
ref={ref}
|
|
43
|
-
className={
|
|
41
|
+
className={cn("sh-ui-accordion__item", className)}
|
|
44
42
|
{...props}
|
|
45
43
|
/>
|
|
46
44
|
));
|
|
@@ -59,7 +57,7 @@ export const AccordionTrigger = React.forwardRef<
|
|
|
59
57
|
<BaseAccordion.Header className="sh-ui-accordion__header">
|
|
60
58
|
<BaseAccordion.Trigger
|
|
61
59
|
ref={ref}
|
|
62
|
-
className={
|
|
60
|
+
className={cn("sh-ui-accordion__trigger", className)}
|
|
63
61
|
{...props}
|
|
64
62
|
>
|
|
65
63
|
<span className="sh-ui-accordion__trigger-label">{children}</span>
|
|
@@ -90,7 +88,7 @@ export const AccordionContent = React.forwardRef<
|
|
|
90
88
|
>(({ className, children, ...props }, ref) => (
|
|
91
89
|
<BaseAccordion.Panel
|
|
92
90
|
ref={ref}
|
|
93
|
-
className={
|
|
91
|
+
className={cn("sh-ui-accordion__panel", className)}
|
|
94
92
|
{...props}
|
|
95
93
|
>
|
|
96
94
|
<div className="sh-ui-accordion__content">{children}</div>
|
|
@@ -2,11 +2,9 @@ import * as React from "react";
|
|
|
2
2
|
import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
|
|
3
3
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
5
6
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
6
7
|
|
|
7
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
8
|
-
return args.filter(Boolean).join(" ");
|
|
9
|
-
}
|
|
10
8
|
|
|
11
9
|
const avatarVariants = cva(
|
|
12
10
|
"relative inline-flex items-center justify-center shrink-0 align-middle overflow-hidden rounded-full bg-background-muted text-foreground-muted font-medium select-none",
|
|
@@ -37,7 +35,7 @@ export const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(
|
|
|
37
35
|
return (
|
|
38
36
|
<BaseAvatar.Root
|
|
39
37
|
ref={ref}
|
|
40
|
-
className={
|
|
38
|
+
className={cn(avatarVariants({ size }), className)}
|
|
41
39
|
{...props}
|
|
42
40
|
/>
|
|
43
41
|
);
|
|
@@ -51,7 +49,7 @@ export const AvatarImage = React.forwardRef<
|
|
|
51
49
|
return (
|
|
52
50
|
<BaseAvatar.Image
|
|
53
51
|
ref={ref}
|
|
54
|
-
className={
|
|
52
|
+
className={cn("w-full h-full object-cover block", className)}
|
|
55
53
|
{...props}
|
|
56
54
|
/>
|
|
57
55
|
);
|
|
@@ -64,7 +62,7 @@ export const AvatarFallback = React.forwardRef<
|
|
|
64
62
|
return (
|
|
65
63
|
<BaseAvatar.Fallback
|
|
66
64
|
ref={ref}
|
|
67
|
-
className={
|
|
65
|
+
className={cn(
|
|
68
66
|
"inline-flex items-center justify-center w-full h-full uppercase tracking-[0.02em]",
|
|
69
67
|
className,
|
|
70
68
|
)}
|
|
@@ -2,11 +2,9 @@ import * as React from "react";
|
|
|
2
2
|
import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
|
|
3
3
|
import "./styles.css";
|
|
4
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
5
6
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
6
7
|
|
|
7
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
8
|
-
return args.filter(Boolean).join(" ");
|
|
9
|
-
}
|
|
10
8
|
|
|
11
9
|
export type AvatarSize = "sm" | "md" | "lg" | "xl";
|
|
12
10
|
|
|
@@ -35,7 +33,7 @@ export const Avatar = React.forwardRef<HTMLSpanElement, AvatarProps>(
|
|
|
35
33
|
return (
|
|
36
34
|
<BaseAvatar.Root
|
|
37
35
|
ref={ref}
|
|
38
|
-
className={
|
|
36
|
+
className={cn("sh-ui-avatar", `sh-ui-avatar--${size}`, className)}
|
|
39
37
|
{...props}
|
|
40
38
|
/>
|
|
41
39
|
);
|
|
@@ -52,7 +50,7 @@ export const AvatarImage = React.forwardRef<
|
|
|
52
50
|
return (
|
|
53
51
|
<BaseAvatar.Image
|
|
54
52
|
ref={ref}
|
|
55
|
-
className={
|
|
53
|
+
className={cn("sh-ui-avatar__image", className)}
|
|
56
54
|
{...props}
|
|
57
55
|
/>
|
|
58
56
|
);
|
|
@@ -68,7 +66,7 @@ export const AvatarFallback = React.forwardRef<
|
|
|
68
66
|
return (
|
|
69
67
|
<BaseAvatar.Fallback
|
|
70
68
|
ref={ref}
|
|
71
|
-
className={
|
|
69
|
+
className={cn("sh-ui-avatar__fallback", className)}
|
|
72
70
|
{...props}
|
|
73
71
|
/>
|
|
74
72
|
);
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
|
|
4
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
5
|
-
return args.filter(Boolean).join(" ");
|
|
6
|
-
}
|
|
7
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
8
6
|
const badgeVariants = cva(
|
|
9
7
|
"inline-flex items-center gap-1 px-2 border border-transparent rounded-full font-medium leading-none whitespace-nowrap align-middle select-none",
|
|
10
8
|
{
|
|
@@ -39,7 +37,7 @@ export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
|
39
37
|
return (
|
|
40
38
|
<span
|
|
41
39
|
ref={ref}
|
|
42
|
-
className={
|
|
40
|
+
className={cn(badgeVariants({ variant, size }), className)}
|
|
43
41
|
{...props}
|
|
44
42
|
/>
|
|
45
43
|
);
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import "./styles.css";
|
|
3
3
|
|
|
4
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
5
|
-
return args.filter(Boolean).join(" ");
|
|
6
|
-
}
|
|
7
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
8
6
|
export type BadgeVariant =
|
|
9
7
|
| "primary"
|
|
10
8
|
| "secondary"
|
|
@@ -29,7 +27,7 @@ export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
|
29
27
|
return (
|
|
30
28
|
<span
|
|
31
29
|
ref={ref}
|
|
32
|
-
className={
|
|
30
|
+
className={cn(
|
|
33
31
|
"sh-ui-badge",
|
|
34
32
|
`sh-ui-badge--${variant}`,
|
|
35
33
|
`sh-ui-badge--${size}`,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
4
|
-
return args.filter(Boolean).join(" ");
|
|
5
|
-
}
|
|
6
3
|
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
7
5
|
export const Breadcrumb = React.forwardRef<
|
|
8
6
|
HTMLElement,
|
|
9
7
|
React.HTMLAttributes<HTMLElement>
|
|
@@ -12,7 +10,7 @@ export const Breadcrumb = React.forwardRef<
|
|
|
12
10
|
<nav
|
|
13
11
|
ref={ref}
|
|
14
12
|
aria-label="Breadcrumb"
|
|
15
|
-
className={
|
|
13
|
+
className={cn("text-[length:var(--text-sm)] text-foreground-muted", className)}
|
|
16
14
|
{...props}
|
|
17
15
|
/>
|
|
18
16
|
);
|
|
@@ -25,7 +23,7 @@ export const BreadcrumbList = React.forwardRef<
|
|
|
25
23
|
return (
|
|
26
24
|
<ol
|
|
27
25
|
ref={ref}
|
|
28
|
-
className={
|
|
26
|
+
className={cn(
|
|
29
27
|
"flex items-center flex-wrap gap-1.5 m-0 p-0 list-none",
|
|
30
28
|
className,
|
|
31
29
|
)}
|
|
@@ -41,7 +39,7 @@ export const BreadcrumbItem = React.forwardRef<
|
|
|
41
39
|
return (
|
|
42
40
|
<li
|
|
43
41
|
ref={ref}
|
|
44
|
-
className={
|
|
42
|
+
className={cn("inline-flex items-center gap-1.5 min-w-0", className)}
|
|
45
43
|
{...props}
|
|
46
44
|
/>
|
|
47
45
|
);
|
|
@@ -54,7 +52,7 @@ export const BreadcrumbLink = React.forwardRef<
|
|
|
54
52
|
return (
|
|
55
53
|
<a
|
|
56
54
|
ref={ref}
|
|
57
|
-
className={
|
|
55
|
+
className={cn(
|
|
58
56
|
"text-foreground-muted no-underline rounded-[calc(var(--radius)-2px)] px-0.5 transition-colors duration-[var(--duration-fast)] hover:text-foreground hover:underline hover:underline-offset-[3px] focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 motion-reduce:transition-none",
|
|
59
57
|
className,
|
|
60
58
|
)}
|
|
@@ -73,7 +71,7 @@ export const BreadcrumbPage = React.forwardRef<
|
|
|
73
71
|
role="link"
|
|
74
72
|
aria-current="page"
|
|
75
73
|
aria-disabled="true"
|
|
76
|
-
className={
|
|
74
|
+
className={cn(
|
|
77
75
|
"text-foreground font-medium overflow-hidden text-ellipsis whitespace-nowrap",
|
|
78
76
|
className,
|
|
79
77
|
)}
|
|
@@ -91,7 +89,7 @@ export const BreadcrumbSeparator = React.forwardRef<
|
|
|
91
89
|
ref={ref}
|
|
92
90
|
role="presentation"
|
|
93
91
|
aria-hidden="true"
|
|
94
|
-
className={
|
|
92
|
+
className={cn(
|
|
95
93
|
"inline-flex items-center text-foreground-muted opacity-60",
|
|
96
94
|
className,
|
|
97
95
|
)}
|
|
@@ -111,7 +109,7 @@ export const BreadcrumbEllipsis = React.forwardRef<
|
|
|
111
109
|
ref={ref}
|
|
112
110
|
role="presentation"
|
|
113
111
|
aria-hidden="true"
|
|
114
|
-
className={
|
|
112
|
+
className={cn(
|
|
115
113
|
"inline-flex items-center w-6 h-6 justify-center text-foreground-muted",
|
|
116
114
|
className,
|
|
117
115
|
)}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import "./styles.css";
|
|
3
3
|
|
|
4
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
5
|
-
return args.filter(Boolean).join(" ");
|
|
6
|
-
}
|
|
7
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
8
6
|
/* ───────── Breadcrumb (nav) ─────────
|
|
9
7
|
* 시맨틱: <nav aria-label="Breadcrumb"><ol>...</ol></nav>.
|
|
10
8
|
*/
|
|
@@ -21,7 +19,7 @@ export const Breadcrumb = React.forwardRef<
|
|
|
21
19
|
<nav
|
|
22
20
|
ref={ref}
|
|
23
21
|
aria-label="Breadcrumb"
|
|
24
|
-
className={
|
|
22
|
+
className={cn("sh-ui-breadcrumb", className)}
|
|
25
23
|
{...props}
|
|
26
24
|
/>
|
|
27
25
|
);
|
|
@@ -37,7 +35,7 @@ export const BreadcrumbList = React.forwardRef<
|
|
|
37
35
|
return (
|
|
38
36
|
<ol
|
|
39
37
|
ref={ref}
|
|
40
|
-
className={
|
|
38
|
+
className={cn("sh-ui-breadcrumb__list", className)}
|
|
41
39
|
{...props}
|
|
42
40
|
/>
|
|
43
41
|
);
|
|
@@ -53,7 +51,7 @@ export const BreadcrumbItem = React.forwardRef<
|
|
|
53
51
|
return (
|
|
54
52
|
<li
|
|
55
53
|
ref={ref}
|
|
56
|
-
className={
|
|
54
|
+
className={cn("sh-ui-breadcrumb__item", className)}
|
|
57
55
|
{...props}
|
|
58
56
|
/>
|
|
59
57
|
);
|
|
@@ -69,7 +67,7 @@ export const BreadcrumbLink = React.forwardRef<
|
|
|
69
67
|
return (
|
|
70
68
|
<a
|
|
71
69
|
ref={ref}
|
|
72
|
-
className={
|
|
70
|
+
className={cn("sh-ui-breadcrumb__link", className)}
|
|
73
71
|
{...props}
|
|
74
72
|
/>
|
|
75
73
|
);
|
|
@@ -88,7 +86,7 @@ export const BreadcrumbPage = React.forwardRef<
|
|
|
88
86
|
role="link"
|
|
89
87
|
aria-current="page"
|
|
90
88
|
aria-disabled="true"
|
|
91
|
-
className={
|
|
89
|
+
className={cn("sh-ui-breadcrumb__page", className)}
|
|
92
90
|
{...props}
|
|
93
91
|
/>
|
|
94
92
|
);
|
|
@@ -106,7 +104,7 @@ export const BreadcrumbSeparator = React.forwardRef<
|
|
|
106
104
|
ref={ref}
|
|
107
105
|
role="presentation"
|
|
108
106
|
aria-hidden="true"
|
|
109
|
-
className={
|
|
107
|
+
className={cn("sh-ui-breadcrumb__separator", className)}
|
|
110
108
|
{...props}
|
|
111
109
|
>
|
|
112
110
|
{children ?? <ChevronRightIcon />}
|
|
@@ -126,7 +124,7 @@ export const BreadcrumbEllipsis = React.forwardRef<
|
|
|
126
124
|
ref={ref}
|
|
127
125
|
role="presentation"
|
|
128
126
|
aria-hidden="true"
|
|
129
|
-
className={
|
|
127
|
+
className={cn("sh-ui-breadcrumb__ellipsis", className)}
|
|
130
128
|
{...props}
|
|
131
129
|
>
|
|
132
130
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
2
3
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
4
|
|
|
4
5
|
const buttonVariants = cva(
|
|
@@ -62,7 +63,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
62
63
|
({ variant = "primary", size = "md", className, ...props }, ref) => (
|
|
63
64
|
<button
|
|
64
65
|
ref={ref}
|
|
65
|
-
className={
|
|
66
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
66
67
|
{...props}
|
|
67
68
|
/>
|
|
68
69
|
),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
2
3
|
import "./styles.css";
|
|
3
4
|
|
|
4
5
|
type Variant = "primary" | "secondary" | "ghost" | "danger" | "link";
|
|
@@ -33,14 +34,12 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
33
34
|
*/
|
|
34
35
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
35
36
|
({ variant = "primary", size = "md", className, ...props }, ref) => {
|
|
36
|
-
const classes =
|
|
37
|
+
const classes = cn(
|
|
37
38
|
"sh-ui-button",
|
|
38
39
|
`sh-ui-button--${variant}`,
|
|
39
40
|
`sh-ui-button--${size}`,
|
|
40
41
|
className,
|
|
41
|
-
|
|
42
|
-
.filter(Boolean)
|
|
43
|
-
.join(" ");
|
|
42
|
+
);
|
|
44
43
|
return <button ref={ref} className={classes} {...props} />;
|
|
45
44
|
},
|
|
46
45
|
);
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Select, SelectContent, SelectItem, SelectTrigger } from "../select";
|
|
5
5
|
|
|
6
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
7
|
-
return args.filter(Boolean).join(" ");
|
|
8
|
-
}
|
|
9
6
|
|
|
7
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
10
8
|
const DEFAULT_WEEKDAYS_KO = ["일", "월", "화", "수", "목", "금", "토"] as const;
|
|
11
9
|
|
|
12
10
|
const isSameDay = (a: Date, b: Date) =>
|
|
@@ -287,7 +285,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
287
285
|
|
|
288
286
|
return (
|
|
289
287
|
<div
|
|
290
|
-
className={
|
|
288
|
+
className={cn("inline-flex gap-[var(--space-4)] select-none", numberOfMonths > 1 && "flex-wrap", className)}
|
|
291
289
|
aria-label={ariaLabel}
|
|
292
290
|
>
|
|
293
291
|
{children
|
|
@@ -316,7 +314,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
316
314
|
export interface CalendarHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
317
315
|
export const CalendarHeader = React.forwardRef<HTMLDivElement, CalendarHeaderProps>(
|
|
318
316
|
function CalendarHeader({ className, ...props }, ref) {
|
|
319
|
-
return <div ref={ref} className={
|
|
317
|
+
return <div ref={ref} className={cn("flex items-center justify-between gap-[var(--space-1)] mb-[var(--space-2)]", className)} {...props} />;
|
|
320
318
|
},
|
|
321
319
|
);
|
|
322
320
|
|
|
@@ -324,7 +322,7 @@ const navButtonClasses =
|
|
|
324
322
|
"inline-flex items-center justify-center w-7 h-7 p-0 border-none rounded-[calc(var(--radius)-2px)] bg-transparent text-foreground-muted cursor-pointer shrink-0 transition-[background-color,color] duration-[var(--duration-fast)] hover:not-disabled:bg-background-muted hover:not-disabled:text-foreground focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 motion-reduce:transition-none";
|
|
325
323
|
|
|
326
324
|
function CalendarNavPlaceholder() {
|
|
327
|
-
return <span className={
|
|
325
|
+
return <span className={cn(navButtonClasses, "invisible pointer-events-none")} aria-hidden />;
|
|
328
326
|
}
|
|
329
327
|
|
|
330
328
|
export interface CalendarNavButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
|
|
@@ -344,7 +342,7 @@ function makeNavButton(
|
|
|
344
342
|
<button
|
|
345
343
|
ref={ref}
|
|
346
344
|
type="button"
|
|
347
|
-
className={
|
|
345
|
+
className={cn(navButtonClasses, className)}
|
|
348
346
|
aria-label={ariaLabel ?? defaultLabel}
|
|
349
347
|
onClick={(e) => { resolveHandler(ctx)(); onClick?.(e); }}
|
|
350
348
|
{...props}
|
|
@@ -377,7 +375,7 @@ export function CalendarYearSelect({ className, formatYear = (y) => `${y}년` }:
|
|
|
377
375
|
const items = ctx.yearOptions.includes(year) ? ctx.yearOptions : [...ctx.yearOptions, year].sort((a, b) => a - b);
|
|
378
376
|
return (
|
|
379
377
|
<Select value={String(year)} onValueChange={(v) => ctx.setYearForVisible(Number(v))}>
|
|
380
|
-
<SelectTrigger className={
|
|
378
|
+
<SelectTrigger className={cn(calendarSelectTriggerClasses, className)} aria-label="연도">
|
|
381
379
|
<span>{formatYear(year)}</span>
|
|
382
380
|
</SelectTrigger>
|
|
383
381
|
<SelectContent>
|
|
@@ -397,7 +395,7 @@ export function CalendarMonthSelect({ className, formatMonth = (m) => `${m + 1}
|
|
|
397
395
|
const month = ctx.visibleMonth.getMonth();
|
|
398
396
|
return (
|
|
399
397
|
<Select value={String(month)} onValueChange={(v) => ctx.setMonthForVisible(Number(v))}>
|
|
400
|
-
<SelectTrigger className={
|
|
398
|
+
<SelectTrigger className={cn(calendarSelectTriggerClasses, className)} aria-label="월">
|
|
401
399
|
<span>{formatMonth(month)}</span>
|
|
402
400
|
</SelectTrigger>
|
|
403
401
|
<SelectContent>
|
|
@@ -420,7 +418,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
420
418
|
const ariaLabel = ctx.ariaLabel ?? monthLabel;
|
|
421
419
|
|
|
422
420
|
return (
|
|
423
|
-
<div ref={ref} className={
|
|
421
|
+
<div ref={ref} className={cn("", className)} {...rest}>
|
|
424
422
|
<div className="grid grid-cols-7 mb-[var(--space-1)]" role="row">
|
|
425
423
|
{ctx.weekdayLabels.map((label) => (
|
|
426
424
|
<span key={label} className="flex items-center justify-center h-8 text-[length:var(--text-xs)] font-medium text-foreground-muted" role="columnheader" aria-label={label}>
|
|
@@ -450,10 +448,10 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
450
448
|
isStart && isEnd ? "rounded-[calc(var(--radius)-2px)]" : "";
|
|
451
449
|
|
|
452
450
|
return (
|
|
453
|
-
<div key={i} className={
|
|
451
|
+
<div key={i} className={cn("flex items-center justify-center w-full h-[2.375rem] min-w-0", cellRangeBg, cellRangeRadius)}>
|
|
454
452
|
<button
|
|
455
453
|
type="button"
|
|
456
|
-
className={
|
|
454
|
+
className={cn(
|
|
457
455
|
"flex items-center justify-center w-9 h-9 p-0 border-none rounded-[calc(var(--radius)-2px)] bg-transparent text-foreground text-[0.8125rem] font-[inherit] cursor-pointer transition-[background-color,color] duration-[var(--duration-fast)] hover:not-disabled:bg-background-muted focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 disabled:opacity-30 disabled:cursor-not-allowed motion-reduce:transition-none",
|
|
458
456
|
!current && "text-[var(--foreground-subtle,var(--foreground-muted))] opacity-40",
|
|
459
457
|
isToday && "font-bold underline underline-offset-[0.125rem]",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
4
5
|
import {
|
|
5
6
|
Select,
|
|
6
7
|
SelectContent,
|
|
@@ -11,9 +12,6 @@ import "./styles.css";
|
|
|
11
12
|
|
|
12
13
|
/* ───────── Helpers ───────── */
|
|
13
14
|
|
|
14
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
15
|
-
return args.filter(Boolean).join(" ");
|
|
16
|
-
}
|
|
17
15
|
|
|
18
16
|
const DEFAULT_WEEKDAYS_KO = ["일", "월", "화", "수", "목", "금", "토"] as const;
|
|
19
17
|
|
|
@@ -505,7 +503,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
505
503
|
|
|
506
504
|
return (
|
|
507
505
|
<div
|
|
508
|
-
className={
|
|
506
|
+
className={cn("sh-ui-calendar", numberOfMonths > 1 && "sh-ui-calendar--multi", className)}
|
|
509
507
|
aria-label={ariaLabel}
|
|
510
508
|
>
|
|
511
509
|
{children
|
|
@@ -545,7 +543,7 @@ export interface CalendarHeaderProps extends React.HTMLAttributes<HTMLDivElement
|
|
|
545
543
|
/** 헤더 컨테이너. 화살표/dropdown 등을 children 으로 자유롭게 배치. */
|
|
546
544
|
export const CalendarHeader = React.forwardRef<HTMLDivElement, CalendarHeaderProps>(
|
|
547
545
|
function CalendarHeader({ className, ...props }, ref) {
|
|
548
|
-
return <div ref={ref} className={
|
|
546
|
+
return <div ref={ref} className={cn("sh-ui-calendar__header", className)} {...props} />;
|
|
549
547
|
},
|
|
550
548
|
);
|
|
551
549
|
|
|
@@ -574,7 +572,7 @@ function makeNavButton(
|
|
|
574
572
|
<button
|
|
575
573
|
ref={ref}
|
|
576
574
|
type="button"
|
|
577
|
-
className={
|
|
575
|
+
className={cn("sh-ui-calendar__nav", className)}
|
|
578
576
|
aria-label={ariaLabel ?? defaultLabel}
|
|
579
577
|
onClick={(e) => {
|
|
580
578
|
resolveHandler(ctx)();
|
|
@@ -649,7 +647,7 @@ export function CalendarYearSelect({
|
|
|
649
647
|
onValueChange={(v) => ctx.setYearForVisible(Number(v))}
|
|
650
648
|
>
|
|
651
649
|
<SelectTrigger
|
|
652
|
-
className={
|
|
650
|
+
className={cn("sh-ui-calendar__select-trigger", className)}
|
|
653
651
|
aria-label="연도"
|
|
654
652
|
>
|
|
655
653
|
<span className="sh-ui-calendar__select-value">{formatYear(year)}</span>
|
|
@@ -684,7 +682,7 @@ export function CalendarMonthSelect({
|
|
|
684
682
|
onValueChange={(v) => ctx.setMonthForVisible(Number(v))}
|
|
685
683
|
>
|
|
686
684
|
<SelectTrigger
|
|
687
|
-
className={
|
|
685
|
+
className={cn("sh-ui-calendar__select-trigger", className)}
|
|
688
686
|
aria-label="월"
|
|
689
687
|
>
|
|
690
688
|
<span className="sh-ui-calendar__select-value">{formatMonth(month)}</span>
|
|
@@ -716,7 +714,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
716
714
|
const ariaLabel = ctx.ariaLabel ?? monthLabel;
|
|
717
715
|
|
|
718
716
|
return (
|
|
719
|
-
<div ref={ref} className={
|
|
717
|
+
<div ref={ref} className={cn("sh-ui-calendar__grid-wrap", className)} {...rest}>
|
|
720
718
|
<div className="sh-ui-calendar__weekdays" role="row">
|
|
721
719
|
{ctx.weekdayLabels.map((label) => (
|
|
722
720
|
<span
|
|
@@ -751,7 +749,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
751
749
|
return (
|
|
752
750
|
<div
|
|
753
751
|
key={i}
|
|
754
|
-
className={
|
|
752
|
+
className={cn(
|
|
755
753
|
"sh-ui-calendar__cell",
|
|
756
754
|
inRange && "sh-ui-calendar__cell--in-range",
|
|
757
755
|
isStart && "sh-ui-calendar__cell--range-start",
|
|
@@ -760,7 +758,7 @@ export const CalendarGrid = React.forwardRef<HTMLDivElement, CalendarGridProps>(
|
|
|
760
758
|
>
|
|
761
759
|
<button
|
|
762
760
|
type="button"
|
|
763
|
-
className={
|
|
761
|
+
className={cn(
|
|
764
762
|
"sh-ui-calendar__day",
|
|
765
763
|
!current && "sh-ui-calendar__day--outside",
|
|
766
764
|
selected && "sh-ui-calendar__day--selected",
|