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,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
5
6
|
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix"> {
|
|
6
7
|
/** input 우측에 부착할 보조 노드(아이콘·단위·버튼 등). 더 많은 슬롯이 필요하면 InputGroup 사용. */
|
|
7
8
|
suffix?: React.ReactNode;
|
|
@@ -9,9 +10,6 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
9
10
|
prefix?: React.ReactNode;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
function cx(...args: (string | undefined | null | false)[]) {
|
|
13
|
-
return args.filter(Boolean).join(" ");
|
|
14
|
-
}
|
|
15
13
|
|
|
16
14
|
/* ───────── Base utility 묶음 (반복 줄이기) ───────── */
|
|
17
15
|
|
|
@@ -69,7 +67,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
69
67
|
<InputGroupContext.Provider value={{ inGroup: true }}>
|
|
70
68
|
<div
|
|
71
69
|
ref={mergedRef}
|
|
72
|
-
className={
|
|
70
|
+
className={cn(baseGroupClasses, className)}
|
|
73
71
|
data-disabled={disabled || undefined}
|
|
74
72
|
aria-invalid={ariaInvalid}
|
|
75
73
|
onClick={handleClick}
|
|
@@ -91,7 +89,7 @@ export const InputAdornment = React.forwardRef<HTMLSpanElement, InputAdornmentPr
|
|
|
91
89
|
({ className, interactive, ...props }, ref) => (
|
|
92
90
|
<span
|
|
93
91
|
ref={ref}
|
|
94
|
-
className={
|
|
92
|
+
className={cn(
|
|
95
93
|
"inline-flex items-center justify-center flex-none text-foreground-muted px-[var(--space-1)] data-[interactive]:p-0",
|
|
96
94
|
className,
|
|
97
95
|
)}
|
|
@@ -112,7 +110,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
112
110
|
<input
|
|
113
111
|
ref={ref}
|
|
114
112
|
type={type}
|
|
115
|
-
className={
|
|
113
|
+
className={cn(
|
|
116
114
|
baseInputClasses,
|
|
117
115
|
inGroupOverrides,
|
|
118
116
|
!!prefix && "pl-[var(--space-10)]",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import "./styles.css";
|
|
5
5
|
|
|
6
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
6
7
|
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix"> {
|
|
7
8
|
/** input 우측에 부착할 보조 노드(아이콘·단위·버튼 등). 더 많은 슬롯이 필요하면 InputGroup 사용. */
|
|
8
9
|
suffix?: React.ReactNode;
|
|
@@ -10,9 +11,6 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
10
11
|
prefix?: React.ReactNode;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function cx(...args: (string | undefined | null | false)[]) {
|
|
14
|
-
return args.filter(Boolean).join(" ");
|
|
15
|
-
}
|
|
16
14
|
|
|
17
15
|
/* ───────── InputGroup + InputAdornment (compound) ─────────
|
|
18
16
|
* <InputGroup>
|
|
@@ -87,7 +85,7 @@ export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
|
|
87
85
|
<InputGroupContext.Provider value={{ inGroup: true }}>
|
|
88
86
|
<div
|
|
89
87
|
ref={mergedRef}
|
|
90
|
-
className={
|
|
88
|
+
className={cn("sh-ui-input-group", className)}
|
|
91
89
|
data-disabled={disabled || undefined}
|
|
92
90
|
aria-invalid={ariaInvalid}
|
|
93
91
|
onClick={handleClick}
|
|
@@ -123,7 +121,7 @@ export const InputAdornment = React.forwardRef<
|
|
|
123
121
|
return (
|
|
124
122
|
<span
|
|
125
123
|
ref={ref}
|
|
126
|
-
className={
|
|
124
|
+
className={cn("sh-ui-input-group__adornment", className)}
|
|
127
125
|
data-interactive={interactive || undefined}
|
|
128
126
|
{...props}
|
|
129
127
|
/>
|
|
@@ -143,7 +141,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
143
141
|
<input
|
|
144
142
|
ref={ref}
|
|
145
143
|
type={type}
|
|
146
|
-
className={
|
|
144
|
+
className={cn(
|
|
147
145
|
"sh-ui-input",
|
|
148
146
|
!!prefix && "sh-ui-input--with-prefix",
|
|
149
147
|
!!suffix && "sh-ui-input--with-suffix",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
3
4
|
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
|
4
5
|
/**
|
|
5
6
|
* 필수 필드 표시. `true`면 LabelTitle 뒤에 `*` 표시.
|
|
@@ -10,15 +11,12 @@ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>
|
|
|
10
11
|
isRequired?: boolean;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
14
|
-
return args.filter(Boolean).join(" ");
|
|
15
|
-
}
|
|
16
14
|
|
|
17
15
|
export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
|
|
18
16
|
({ className, children, isRequired, ...props }, ref) => (
|
|
19
17
|
<label
|
|
20
18
|
ref={ref}
|
|
21
|
-
className={
|
|
19
|
+
className={cn(
|
|
22
20
|
"flex flex-col gap-0.5 text-[length:var(--text-sm)] font-medium leading-snug text-foreground cursor-pointer select-none not-has-[[data-sh-ui-label-part]]:block",
|
|
23
21
|
// 필수 표시 — title 이 있으면 title 뒤, 없으면 label 뒤에 * 부착
|
|
24
22
|
isRequired &&
|
|
@@ -38,7 +36,7 @@ export function LabelTitle({ className, ...props }: React.HTMLAttributes<HTMLSpa
|
|
|
38
36
|
return (
|
|
39
37
|
<span
|
|
40
38
|
data-sh-ui-label-part="title"
|
|
41
|
-
className={
|
|
39
|
+
className={cn("font-semibold text-[length:var(--text-sm)] text-foreground", className)}
|
|
42
40
|
{...props}
|
|
43
41
|
/>
|
|
44
42
|
);
|
|
@@ -48,7 +46,7 @@ export function LabelSubtitle({ className, ...props }: React.HTMLAttributes<HTML
|
|
|
48
46
|
return (
|
|
49
47
|
<span
|
|
50
48
|
data-sh-ui-label-part="subtitle"
|
|
51
|
-
className={
|
|
49
|
+
className={cn("font-normal text-[0.8125rem] text-foreground", className)}
|
|
52
50
|
{...props}
|
|
53
51
|
/>
|
|
54
52
|
);
|
|
@@ -58,7 +56,7 @@ export function LabelDescription({ className, ...props }: React.HTMLAttributes<H
|
|
|
58
56
|
return (
|
|
59
57
|
<p
|
|
60
58
|
data-sh-ui-label-part="description"
|
|
61
|
-
className={
|
|
59
|
+
className={cn("m-0 font-normal text-[0.8125rem] leading-snug text-foreground-muted", className)}
|
|
62
60
|
{...props}
|
|
63
61
|
/>
|
|
64
62
|
);
|
|
@@ -68,7 +66,7 @@ export function LabelCaption({ className, ...props }: React.HTMLAttributes<HTMLP
|
|
|
68
66
|
return (
|
|
69
67
|
<p
|
|
70
68
|
data-sh-ui-label-part="caption"
|
|
71
|
-
className={
|
|
69
|
+
className={cn(
|
|
72
70
|
"m-0 font-normal text-[length:var(--text-xs)] leading-tight text-[var(--foreground-subtle,var(--foreground-muted))] opacity-75",
|
|
73
71
|
className,
|
|
74
72
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import "./styles.css";
|
|
3
3
|
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
4
5
|
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
|
5
6
|
/**
|
|
6
7
|
* 필수 필드 표시. `true`면 `::after`로 `*` 표시가 붙는다.
|
|
@@ -11,9 +12,6 @@ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>
|
|
|
11
12
|
isRequired?: boolean;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
15
|
-
return args.filter(Boolean).join(" ");
|
|
16
|
-
}
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
17
|
* 폼 컨트롤과 1:1로 연결되는 레이블. `htmlFor`로 컨트롤의 `id`와 매칭하거나
|
|
@@ -23,7 +21,7 @@ export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
|
|
|
23
21
|
({ className, children, isRequired, ...props }, ref) => (
|
|
24
22
|
<label
|
|
25
23
|
ref={ref}
|
|
26
|
-
className={
|
|
24
|
+
className={cn("sh-ui-label", className)}
|
|
27
25
|
data-required={isRequired || undefined}
|
|
28
26
|
{...props}
|
|
29
27
|
>
|
|
@@ -35,20 +33,20 @@ Label.displayName = "Label";
|
|
|
35
33
|
|
|
36
34
|
/** Label 안의 주 라벨 텍스트. 구조적 그룹핑이 필요할 때 Label과 함께 사용. */
|
|
37
35
|
export function LabelTitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
38
|
-
return <span className={
|
|
36
|
+
return <span className={cn("sh-ui-label__title", className)} {...props} />;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
/** 라벨 옆에 약하게 표시되는 보조 텍스트(예: "선택 사항"). */
|
|
42
40
|
export function LabelSubtitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
|
|
43
|
-
return <span className={
|
|
41
|
+
return <span className={cn("sh-ui-label__subtitle", className)} {...props} />;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
/** 라벨 아래에 붙는 안내 문구. 컨트롤과 `aria-describedby`로 연결할 것. */
|
|
47
45
|
export function LabelDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
48
|
-
return <p className={
|
|
46
|
+
return <p className={cn("sh-ui-label__description", className)} {...props} />;
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
/** 라벨 아래의 보조 캡션(예: 입력 형식 예시, 글자 수 제한). */
|
|
52
50
|
export function LabelCaption({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
53
|
-
return <p className={
|
|
51
|
+
return <p className={cn("sh-ui-label__caption", className)} {...props} />;
|
|
54
52
|
}
|
|
@@ -5,6 +5,7 @@ import ReactMarkdown from "react-markdown";
|
|
|
5
5
|
import remarkGfm from "remark-gfm";
|
|
6
6
|
import { CodeEditor } from "../code-editor";
|
|
7
7
|
|
|
8
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
8
9
|
export interface MarkdownEditorProps {
|
|
9
10
|
value?: string;
|
|
10
11
|
defaultValue?: string;
|
|
@@ -19,9 +20,6 @@ export interface MarkdownEditorProps {
|
|
|
19
20
|
"aria-label"?: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
23
|
-
return args.filter(Boolean).join(" ");
|
|
24
|
-
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
27
25
|
* 마크다운 에디터 (Tailwind 변종) — react-markdown 의 출력 HTML 트리에 대한
|
|
@@ -50,7 +48,7 @@ export function MarkdownEditor({
|
|
|
50
48
|
|
|
51
49
|
return (
|
|
52
50
|
<div
|
|
53
|
-
className={
|
|
51
|
+
className={cn("grid gap-[var(--space-3)]", layoutClass, className)}
|
|
54
52
|
data-readonly={readOnly || undefined}
|
|
55
53
|
>
|
|
56
54
|
<div className="min-w-0">
|
|
@@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm";
|
|
|
6
6
|
import { CodeEditor } from "../code-editor";
|
|
7
7
|
import "./styles.css";
|
|
8
8
|
|
|
9
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
9
10
|
export interface MarkdownEditorProps {
|
|
10
11
|
/**
|
|
11
12
|
* Controlled — 현재 마크다운. 명시 시 외부 상태가 진실원천.
|
|
@@ -42,9 +43,6 @@ export interface MarkdownEditorProps {
|
|
|
42
43
|
"aria-label"?: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
46
|
-
return args.filter(Boolean).join(" ");
|
|
47
|
-
}
|
|
48
46
|
|
|
49
47
|
/**
|
|
50
48
|
* 마크다운 에디터 — CodeEditor(소스) + react-markdown(라이브 프리뷰)의 합성.
|
|
@@ -79,7 +77,7 @@ export function MarkdownEditor({
|
|
|
79
77
|
|
|
80
78
|
return (
|
|
81
79
|
<div
|
|
82
|
-
className={
|
|
80
|
+
className={cn(
|
|
83
81
|
"sh-ui-md-editor",
|
|
84
82
|
preview && `sh-ui-md-editor--${previewPosition}`,
|
|
85
83
|
!preview && "sh-ui-md-editor--no-preview",
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Menubar as BaseMenubar } from "@base-ui/react/menubar";
|
|
3
3
|
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
4
5
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
5
6
|
|
|
6
|
-
function cx(...args: (string | undefined | false | null)[]) {
|
|
7
|
-
return args.filter(Boolean).join(" ");
|
|
8
|
-
}
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* 상단 앱 메뉴바 (Tailwind 변종). DropdownMenu 와 함께 사용 — DropdownMenu 의
|
|
@@ -22,7 +20,7 @@ export const Menubar = React.forwardRef<
|
|
|
22
20
|
return (
|
|
23
21
|
<BaseMenubar
|
|
24
22
|
ref={ref}
|
|
25
|
-
className={
|
|
23
|
+
className={cn(
|
|
26
24
|
"inline-flex items-center gap-[var(--space-1)] p-[var(--space-1)] bg-background border border-border rounded-[var(--radius)] shadow-[0_1px_2px_rgba(0,0,0,0.04)]",
|
|
27
25
|
className,
|
|
28
26
|
)}
|
|
@@ -2,11 +2,9 @@ import * as React from "react";
|
|
|
2
2
|
import { Menubar as BaseMenubar } from "@base-ui/react/menubar";
|
|
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
|
/**
|
|
12
10
|
* 상단 앱 메뉴바(파일/편집/보기 등). 내부에 DropdownMenu를 나란히 배치하여
|
|
@@ -27,7 +25,7 @@ export const Menubar = React.forwardRef<
|
|
|
27
25
|
return (
|
|
28
26
|
<BaseMenubar
|
|
29
27
|
ref={ref}
|
|
30
|
-
className={
|
|
28
|
+
className={cn("sh-ui-menubar", className)}
|
|
31
29
|
{...props}
|
|
32
30
|
/>
|
|
33
31
|
);
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
function cx(...args: (string | undefined | null | false)[]) {
|
|
6
|
-
return args.filter(Boolean).join(" ");
|
|
7
|
-
}
|
|
8
5
|
|
|
6
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
9
7
|
export interface NumericInputProps
|
|
10
8
|
extends Omit<
|
|
11
9
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
@@ -59,7 +57,7 @@ export const NumericInput = React.forwardRef<HTMLInputElement, NumericInputProps
|
|
|
59
57
|
ref={ref}
|
|
60
58
|
type="text"
|
|
61
59
|
inputMode="decimal"
|
|
62
|
-
className={
|
|
60
|
+
className={cn(inputClasses, className)}
|
|
63
61
|
value={buffer}
|
|
64
62
|
onChange={(e) => {
|
|
65
63
|
const raw = e.target.value;
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import "./styles.css";
|
|
5
5
|
|
|
6
|
-
function cx(...args: (string | undefined | null | false)[]) {
|
|
7
|
-
return args.filter(Boolean).join(" ");
|
|
8
|
-
}
|
|
9
6
|
|
|
7
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
10
8
|
export interface NumericInputProps
|
|
11
9
|
extends Omit<
|
|
12
10
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
@@ -93,7 +91,7 @@ export const NumericInput = React.forwardRef<HTMLInputElement, NumericInputProps
|
|
|
93
91
|
ref={ref}
|
|
94
92
|
type="text"
|
|
95
93
|
inputMode="decimal"
|
|
96
|
-
className={
|
|
94
|
+
className={cn("sh-ui-numeric-input__input", className)}
|
|
97
95
|
value={buffer}
|
|
98
96
|
onChange={(e) => {
|
|
99
97
|
const raw = e.target.value;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
5
6
|
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
6
7
|
|
|
7
8
|
export interface PageTOCProps {
|
|
@@ -120,7 +121,7 @@ export function PageTOC({
|
|
|
120
121
|
|
|
121
122
|
return (
|
|
122
123
|
<nav
|
|
123
|
-
className={
|
|
124
|
+
className={cn(
|
|
124
125
|
"fixed top-20 right-6 w-56 max-h-[calc(100vh-7rem)] overflow-y-auto pl-4 pr-2 py-3 border-l border-border text-[0.8125rem] z-[5] max-[80rem]:hidden",
|
|
125
126
|
className,
|
|
126
127
|
)}
|
|
@@ -135,7 +136,7 @@ export function PageTOC({
|
|
|
135
136
|
<a
|
|
136
137
|
href={`#${item.id}`}
|
|
137
138
|
onClick={(e) => handleClick(e, item.id)}
|
|
138
|
-
className={
|
|
139
|
+
className={cn(linkBase, linkClassesForLevel(item.level))}
|
|
139
140
|
data-active={activeId === item.id ? "true" : undefined}
|
|
140
141
|
aria-current={activeId === item.id ? "true" : undefined}
|
|
141
142
|
>
|
|
@@ -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 "./styles.css";
|
|
5
6
|
|
|
6
7
|
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
|
@@ -52,8 +53,6 @@ interface TocItem {
|
|
|
52
53
|
level: HeadingLevel;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
const cx = (...args: (string | undefined | false | null)[]) =>
|
|
56
|
-
args.filter(Boolean).join(" ");
|
|
57
56
|
|
|
58
57
|
/**
|
|
59
58
|
* 페이지 내 자동 목차 (On this page).
|
|
@@ -151,7 +150,7 @@ export function PageTOC({
|
|
|
151
150
|
|
|
152
151
|
return (
|
|
153
152
|
<nav
|
|
154
|
-
className={
|
|
153
|
+
className={cn("sh-ui-page-toc", className)}
|
|
155
154
|
aria-label={typeof label === "string" ? label : "목차"}
|
|
156
155
|
>
|
|
157
156
|
<div className="sh-ui-page-toc__label">{label}</div>
|
|
@@ -1,16 +1,14 @@
|
|
|
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 Pagination = React.forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement>>(
|
|
8
6
|
function Pagination({ className, ...props }, ref) {
|
|
9
7
|
return (
|
|
10
8
|
<nav
|
|
11
9
|
ref={ref}
|
|
12
10
|
aria-label="Pagination"
|
|
13
|
-
className={
|
|
11
|
+
className={cn("flex justify-center text-[length:var(--text-sm)] text-foreground", className)}
|
|
14
12
|
{...props}
|
|
15
13
|
/>
|
|
16
14
|
);
|
|
@@ -22,7 +20,7 @@ export const PaginationContent = React.forwardRef<HTMLUListElement, React.HTMLAt
|
|
|
22
20
|
return (
|
|
23
21
|
<ul
|
|
24
22
|
ref={ref}
|
|
25
|
-
className={
|
|
23
|
+
className={cn("flex flex-wrap items-center gap-1 m-0 p-0 list-none", className)}
|
|
26
24
|
{...props}
|
|
27
25
|
/>
|
|
28
26
|
);
|
|
@@ -31,7 +29,7 @@ export const PaginationContent = React.forwardRef<HTMLUListElement, React.HTMLAt
|
|
|
31
29
|
|
|
32
30
|
export const PaginationItem = React.forwardRef<HTMLLIElement, React.LiHTMLAttributes<HTMLLIElement>>(
|
|
33
31
|
function PaginationItem({ className, ...props }, ref) {
|
|
34
|
-
return <li ref={ref} className={
|
|
32
|
+
return <li ref={ref} className={cn("inline-flex items-center", className)} {...props} />;
|
|
35
33
|
},
|
|
36
34
|
);
|
|
37
35
|
|
|
@@ -51,7 +49,7 @@ export const PaginationLink = React.forwardRef<HTMLAnchorElement, PaginationLink
|
|
|
51
49
|
aria-current={isActive ? "page" : undefined}
|
|
52
50
|
data-active={isActive ? "" : undefined}
|
|
53
51
|
data-size={size}
|
|
54
|
-
className={
|
|
52
|
+
className={cn(linkBase, className)}
|
|
55
53
|
{...props}
|
|
56
54
|
/>
|
|
57
55
|
);
|
|
@@ -61,7 +59,7 @@ export const PaginationLink = React.forwardRef<HTMLAnchorElement, PaginationLink
|
|
|
61
59
|
export const PaginationPrevious = React.forwardRef<HTMLAnchorElement, PaginationLinkProps>(
|
|
62
60
|
function PaginationPrevious({ className, children, ...props }, ref) {
|
|
63
61
|
return (
|
|
64
|
-
<PaginationLink ref={ref} aria-label="이전 페이지" className={
|
|
62
|
+
<PaginationLink ref={ref} aria-label="이전 페이지" className={cn("px-2.5", className)} {...props}>
|
|
65
63
|
<ChevronLeftIcon />
|
|
66
64
|
{children ?? <span>이전</span>}
|
|
67
65
|
</PaginationLink>
|
|
@@ -72,7 +70,7 @@ export const PaginationPrevious = React.forwardRef<HTMLAnchorElement, Pagination
|
|
|
72
70
|
export const PaginationNext = React.forwardRef<HTMLAnchorElement, PaginationLinkProps>(
|
|
73
71
|
function PaginationNext({ className, children, ...props }, ref) {
|
|
74
72
|
return (
|
|
75
|
-
<PaginationLink ref={ref} aria-label="다음 페이지" className={
|
|
73
|
+
<PaginationLink ref={ref} aria-label="다음 페이지" className={cn("px-2.5", className)} {...props}>
|
|
76
74
|
{children ?? <span>다음</span>}
|
|
77
75
|
<ChevronRightIcon />
|
|
78
76
|
</PaginationLink>
|
|
@@ -87,7 +85,7 @@ export const PaginationEllipsis = React.forwardRef<HTMLSpanElement, React.HTMLAt
|
|
|
87
85
|
ref={ref}
|
|
88
86
|
role="presentation"
|
|
89
87
|
aria-hidden="true"
|
|
90
|
-
className={
|
|
88
|
+
className={cn("inline-flex items-center justify-center w-9 h-9 text-foreground-muted", className)}
|
|
91
89
|
{...props}
|
|
92
90
|
>
|
|
93
91
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden>
|
|
@@ -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
|
/* ───────── Pagination (nav) ─────────
|
|
9
7
|
* 시맨틱: <nav aria-label="Pagination"><ul>...</ul></nav>.
|
|
10
8
|
* 현재 페이지 링크에 aria-current="page"를 부여해 스크린리더가 위치를 읽게 한다.
|
|
@@ -22,7 +20,7 @@ export const Pagination = React.forwardRef<
|
|
|
22
20
|
<nav
|
|
23
21
|
ref={ref}
|
|
24
22
|
aria-label="Pagination"
|
|
25
|
-
className={
|
|
23
|
+
className={cn("sh-ui-pagination", className)}
|
|
26
24
|
{...props}
|
|
27
25
|
/>
|
|
28
26
|
);
|
|
@@ -38,7 +36,7 @@ export const PaginationContent = React.forwardRef<
|
|
|
38
36
|
return (
|
|
39
37
|
<ul
|
|
40
38
|
ref={ref}
|
|
41
|
-
className={
|
|
39
|
+
className={cn("sh-ui-pagination__content", className)}
|
|
42
40
|
{...props}
|
|
43
41
|
/>
|
|
44
42
|
);
|
|
@@ -54,7 +52,7 @@ export const PaginationItem = React.forwardRef<
|
|
|
54
52
|
return (
|
|
55
53
|
<li
|
|
56
54
|
ref={ref}
|
|
57
|
-
className={
|
|
55
|
+
className={cn("sh-ui-pagination__item", className)}
|
|
58
56
|
{...props}
|
|
59
57
|
/>
|
|
60
58
|
);
|
|
@@ -98,7 +96,7 @@ export const PaginationLink = React.forwardRef<
|
|
|
98
96
|
aria-current={isActive ? "page" : undefined}
|
|
99
97
|
data-active={isActive ? "" : undefined}
|
|
100
98
|
data-size={size}
|
|
101
|
-
className={
|
|
99
|
+
className={cn("sh-ui-pagination__link", className)}
|
|
102
100
|
{...props}
|
|
103
101
|
/>
|
|
104
102
|
);
|
|
@@ -117,7 +115,7 @@ export const PaginationPrevious = React.forwardRef<
|
|
|
117
115
|
<PaginationLink
|
|
118
116
|
ref={ref}
|
|
119
117
|
aria-label="이전 페이지"
|
|
120
|
-
className={
|
|
118
|
+
className={cn("sh-ui-pagination__nav", className)}
|
|
121
119
|
{...props}
|
|
122
120
|
>
|
|
123
121
|
<ChevronLeftIcon />
|
|
@@ -135,7 +133,7 @@ export const PaginationNext = React.forwardRef<
|
|
|
135
133
|
<PaginationLink
|
|
136
134
|
ref={ref}
|
|
137
135
|
aria-label="다음 페이지"
|
|
138
|
-
className={
|
|
136
|
+
className={cn("sh-ui-pagination__nav", className)}
|
|
139
137
|
{...props}
|
|
140
138
|
>
|
|
141
139
|
{children ?? <span>다음</span>}
|
|
@@ -156,7 +154,7 @@ export const PaginationEllipsis = React.forwardRef<
|
|
|
156
154
|
ref={ref}
|
|
157
155
|
role="presentation"
|
|
158
156
|
aria-hidden="true"
|
|
159
|
-
className={
|
|
157
|
+
className={cn("sh-ui-pagination__ellipsis", className)}
|
|
160
158
|
{...props}
|
|
161
159
|
>
|
|
162
160
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor" aria-hidden>
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
3
3
|
|
|
4
|
+
import { cn } from "@SH_UI_UTILS@";
|
|
4
5
|
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
5
6
|
|
|
6
|
-
function cx(...args: (string | undefined | false)[]) {
|
|
7
|
-
return args.filter(Boolean).join(" ");
|
|
8
|
-
}
|
|
9
7
|
|
|
10
8
|
export const Popover = BasePopover.Root;
|
|
11
9
|
export const PopoverTrigger = BasePopover.Trigger;
|
|
@@ -35,7 +33,7 @@ export const PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentPro
|
|
|
35
33
|
>
|
|
36
34
|
<BasePopover.Popup
|
|
37
35
|
ref={ref}
|
|
38
|
-
className={
|
|
36
|
+
className={cn(
|
|
39
37
|
"min-w-48 p-[var(--space-2)] bg-background text-foreground border border-border rounded-[var(--radius)] shadow-[0_8px_24px_rgba(0,0,0,0.12)] outline-none text-[length:var(--text-sm)] leading-snug origin-[var(--transform-origin)] transition-[opacity,transform] duration-[140ms] ease-out motion-reduce:transition-none data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 motion-reduce:data-[starting-style]:scale-100 motion-reduce:data-[ending-style]:scale-100 focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2",
|
|
40
38
|
className,
|
|
41
39
|
)}
|
|
@@ -57,7 +55,7 @@ export const PopoverTitle = React.forwardRef<
|
|
|
57
55
|
return (
|
|
58
56
|
<BasePopover.Title
|
|
59
57
|
ref={ref}
|
|
60
|
-
className={
|
|
58
|
+
className={cn("m-0 mb-[var(--space-1)] font-semibold text-[0.9375rem]", className)}
|
|
61
59
|
{...props}
|
|
62
60
|
/>
|
|
63
61
|
);
|
|
@@ -70,7 +68,7 @@ export const PopoverDescription = React.forwardRef<
|
|
|
70
68
|
return (
|
|
71
69
|
<BasePopover.Description
|
|
72
70
|
ref={ref}
|
|
73
|
-
className={
|
|
71
|
+
className={cn("m-0 text-foreground-muted text-[0.8125rem]", className)}
|
|
74
72
|
{...props}
|
|
75
73
|
/>
|
|
76
74
|
);
|
|
@@ -2,11 +2,9 @@ import * as React from "react";
|
|
|
2
2
|
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
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)[]) {
|
|
8
|
-
return args.filter(Boolean).join(" ");
|
|
9
|
-
}
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* 트리거 요소에 떠다니는 가벼운 패널을 띄우는 비모달 컨테이너. 포커스를 가두지 않으므로
|
|
@@ -72,7 +70,7 @@ export const PopoverContent = React.forwardRef<HTMLDivElement, PopoverContentPro
|
|
|
72
70
|
>
|
|
73
71
|
<BasePopover.Popup
|
|
74
72
|
ref={ref}
|
|
75
|
-
className={
|
|
73
|
+
className={cn("sh-ui-popover__content", className)}
|
|
76
74
|
{...props}
|
|
77
75
|
>
|
|
78
76
|
{showArrow && (
|
|
@@ -94,7 +92,7 @@ export const PopoverTitle = React.forwardRef<
|
|
|
94
92
|
return (
|
|
95
93
|
<BasePopover.Title
|
|
96
94
|
ref={ref}
|
|
97
|
-
className={
|
|
95
|
+
className={cn("sh-ui-popover__title", className)}
|
|
98
96
|
{...props}
|
|
99
97
|
/>
|
|
100
98
|
);
|
|
@@ -108,7 +106,7 @@ export const PopoverDescription = React.forwardRef<
|
|
|
108
106
|
return (
|
|
109
107
|
<BasePopover.Description
|
|
110
108
|
ref={ref}
|
|
111
|
-
className={
|
|
109
|
+
className={cn("sh-ui-popover__description", className)}
|
|
112
110
|
{...props}
|
|
113
111
|
/>
|
|
114
112
|
);
|
|
@@ -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
|
function clamp(n: number, min: number, max: number) {
|
|
8
6
|
return Math.min(max, Math.max(min, n));
|
|
9
7
|
}
|
|
@@ -32,14 +30,14 @@ export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
|
32
30
|
aria-valuemax={isDeterminate ? max : undefined}
|
|
33
31
|
aria-valuenow={isDeterminate ? value : undefined}
|
|
34
32
|
data-state={isDeterminate ? "determinate" : "indeterminate"}
|
|
35
|
-
className={
|
|
33
|
+
className={cn(
|
|
36
34
|
"relative w-full h-2 overflow-hidden bg-background-muted rounded-full",
|
|
37
35
|
className,
|
|
38
36
|
)}
|
|
39
37
|
{...props}
|
|
40
38
|
>
|
|
41
39
|
<div
|
|
42
|
-
className={
|
|
40
|
+
className={cn(
|
|
43
41
|
"h-full bg-primary rounded-full transition-[width] duration-[var(--duration-base)] ease-out motion-reduce:transition-none",
|
|
44
42
|
!isDeterminate &&
|
|
45
43
|
"w-2/5 animate-[sh-ui-progress-slide_1.2s_ease-in-out_infinite] motion-reduce:animate-none motion-reduce:translate-x-3/4",
|
|
@@ -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
|
function clamp(n: number, min: number, max: number) {
|
|
9
7
|
return Math.min(max, Math.max(min, n));
|
|
10
8
|
}
|
|
@@ -42,7 +40,7 @@ export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
|
42
40
|
aria-valuemax={isDeterminate ? max : undefined}
|
|
43
41
|
aria-valuenow={isDeterminate ? value : undefined}
|
|
44
42
|
data-state={isDeterminate ? "determinate" : "indeterminate"}
|
|
45
|
-
className={
|
|
43
|
+
className={cn("sh-ui-progress", className)}
|
|
46
44
|
{...props}
|
|
47
45
|
>
|
|
48
46
|
<div
|