@xonic0506/sonic-ui 0.0.0-stage → 0.2.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.
- package/AI-RULES.md +18 -0
- package/DESIGN.md +652 -0
- package/LICENSE +21 -0
- package/README.md +124 -2
- package/dist/components/alert-dialog.d.ts +16 -0
- package/dist/components/alert-dialog.js +46 -0
- package/dist/components/avatar.d.ts +10 -0
- package/dist/components/avatar.js +27 -0
- package/dist/components/badge.d.ts +8 -0
- package/dist/components/badge.js +77 -0
- package/dist/components/button.d.ts +18 -0
- package/dist/components/button.js +49 -0
- package/dist/components/card.d.ts +24 -0
- package/dist/components/card.js +15 -0
- package/dist/components/checkbox.d.ts +30 -0
- package/dist/components/checkbox.js +48 -0
- package/dist/components/dialog.d.ts +39 -0
- package/dist/components/dialog.js +52 -0
- package/dist/components/empty-state.d.ts +11 -0
- package/dist/components/empty-state.js +17 -0
- package/dist/components/error-state.d.ts +12 -0
- package/dist/components/error-state.js +24 -0
- package/dist/components/field.d.ts +13 -0
- package/dist/components/field.js +33 -0
- package/dist/components/input.d.ts +8 -0
- package/dist/components/input.js +16 -0
- package/dist/components/menu.d.ts +33 -0
- package/dist/components/menu.js +44 -0
- package/dist/components/popover.d.ts +26 -0
- package/dist/components/popover.js +50 -0
- package/dist/components/popup-styles.d.ts +3 -0
- package/dist/components/popup-styles.js +5 -0
- package/dist/components/progress.d.ts +16 -0
- package/dist/components/progress.js +27 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +40 -0
- package/dist/components/select.d.ts +30 -0
- package/dist/components/select.js +66 -0
- package/dist/components/separator.d.ts +7 -0
- package/dist/components/separator.js +14 -0
- package/dist/components/skeleton.d.ts +8 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/state-layout.d.ts +11 -0
- package/dist/components/state-layout.js +31 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.js +24 -0
- package/dist/components/tabs.d.ts +31 -0
- package/dist/components/tabs.js +41 -0
- package/dist/components/text.d.ts +10 -0
- package/dist/components/text.js +39 -0
- package/dist/components/textarea.d.ts +6 -0
- package/dist/components/textarea.js +20 -0
- package/dist/components/toast.d.ts +24 -0
- package/dist/components/toast.js +62 -0
- package/dist/components/tooltip.d.ts +11 -0
- package/dist/components/tooltip.js +19 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +26 -0
- package/dist/lib/cn.d.ts +1 -0
- package/dist/lib/cn.js +19 -0
- package/dist/styles.css +3 -0
- package/dist/tailwind.css +72 -0
- package/lint/design-rules.js +25 -0
- package/package.json +62 -4
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface ErrorStateProps {
|
|
3
|
+
/** 기본값: triangle-alert */
|
|
4
|
+
icon?: React.ReactNode;
|
|
5
|
+
title: React.ReactNode;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
/** 있으면 "다시 시도" 버튼을 보여 줘요 */
|
|
8
|
+
onRetry?: () => void;
|
|
9
|
+
retryLabel?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function ErrorState({ icon, title, description, onRetry, retryLabel, className }: ErrorStateProps): React.JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button as e } from "./button.js";
|
|
3
|
+
import { StateLayout as t } from "./state-layout.js";
|
|
4
|
+
import "react";
|
|
5
|
+
import { RefreshCw as n, TriangleAlert as r } from "lucide-react";
|
|
6
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/error-state.tsx
|
|
8
|
+
function o({ icon: o = /* @__PURE__ */ i(r, {}), title: s, description: c, onRetry: l, retryLabel: u = "다시 시도", className: d }) {
|
|
9
|
+
return /* @__PURE__ */ i(t, {
|
|
10
|
+
role: "alert",
|
|
11
|
+
icon: o,
|
|
12
|
+
iconClassName: "sn:text-danger",
|
|
13
|
+
title: s,
|
|
14
|
+
description: c,
|
|
15
|
+
className: d,
|
|
16
|
+
children: l && /* @__PURE__ */ a(e, {
|
|
17
|
+
variant: "secondary",
|
|
18
|
+
onClick: l,
|
|
19
|
+
children: [/* @__PURE__ */ i(n, {}), u]
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { o as ErrorState };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface FieldProps {
|
|
3
|
+
label: React.ReactNode;
|
|
4
|
+
description?: React.ReactNode;
|
|
5
|
+
/** invalid일 때만 보이고, 그때는 description 자리를 대신해요 */
|
|
6
|
+
error?: React.ReactNode;
|
|
7
|
+
/** 안의 Input에 자동으로 전달돼요. Input에는 invalid를 따로 주지 않아요 */
|
|
8
|
+
invalid?: boolean;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare const Field: React.ForwardRefExoticComponent<FieldProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import * as t from "react";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
import { Field as i } from "@base-ui/react/field";
|
|
6
|
+
//#region src/components/field.tsx
|
|
7
|
+
var a = t.forwardRef(function({ label: t, description: a, error: o, invalid: s, disabled: c, className: l, children: u }, d) {
|
|
8
|
+
let f = s && !!o;
|
|
9
|
+
return /* @__PURE__ */ r(i.Root, {
|
|
10
|
+
ref: d,
|
|
11
|
+
invalid: s,
|
|
12
|
+
disabled: c,
|
|
13
|
+
className: e("sn:flex sn:flex-col sn:gap-2 sn:font-sans", l),
|
|
14
|
+
children: [
|
|
15
|
+
/* @__PURE__ */ n(i.Label, {
|
|
16
|
+
className: "sn:text-label sn:text-default",
|
|
17
|
+
children: t
|
|
18
|
+
}),
|
|
19
|
+
u,
|
|
20
|
+
a && !f && /* @__PURE__ */ n(i.Description, {
|
|
21
|
+
className: "sn:m-0 sn:text-caption sn:text-muted",
|
|
22
|
+
children: a
|
|
23
|
+
}),
|
|
24
|
+
f && /* @__PURE__ */ n(i.Error, {
|
|
25
|
+
match: !0,
|
|
26
|
+
className: "sn:m-0 sn:text-caption sn:text-danger",
|
|
27
|
+
children: o
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { a as Field };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export declare const inputClass = "sn:box-border sn:m-0 sn:w-full sn:min-w-0 sn:rounded-md sn:border sn:border-default sn:bg-surface sn:px-3 sn:font-sans sn:text-body sn:text-default sn:placeholder:text-muted sn:transition-colors sn:duration-(--sn-duration-fast) sn:ease-standard sn:hover:not-data-disabled:border-strong sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus sn:aria-invalid:focus-visible:outline-danger sn:data-invalid:focus-visible:outline-danger sn:aria-invalid:border-danger sn:hover:aria-invalid:border-danger sn:data-invalid:border-danger sn:data-disabled:cursor-not-allowed sn:data-disabled:opacity-(--sn-opacity-disabled)";
|
|
3
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'size'> {
|
|
4
|
+
/** 에러 상태. aria-invalid로 전달돼요 */
|
|
5
|
+
invalid?: boolean;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import * as t from "react";
|
|
4
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
5
|
+
import { Input as r } from "@base-ui/react/input";
|
|
6
|
+
//#region src/components/input.tsx
|
|
7
|
+
var i = "sn:box-border sn:m-0 sn:w-full sn:min-w-0 sn:rounded-md sn:border sn:border-default sn:bg-surface sn:px-3 sn:font-sans sn:text-body sn:text-default sn:placeholder:text-muted sn:transition-colors sn:duration-(--sn-duration-fast) sn:ease-standard sn:hover:not-data-disabled:border-strong sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus sn:aria-invalid:focus-visible:outline-danger sn:data-invalid:focus-visible:outline-danger sn:aria-invalid:border-danger sn:hover:aria-invalid:border-danger sn:data-invalid:border-danger sn:data-disabled:cursor-not-allowed sn:data-disabled:opacity-(--sn-opacity-disabled)", a = t.forwardRef(function({ invalid: t, className: a, ...o }, s) {
|
|
8
|
+
return /* @__PURE__ */ n(r, {
|
|
9
|
+
ref: s,
|
|
10
|
+
"aria-invalid": t || void 0,
|
|
11
|
+
className: e("sn:h-8", i, a),
|
|
12
|
+
...o
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { a as Input, i as inputClass };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface MenuProps {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
onOpenChange?: (open: boolean) => void;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function Menu({ onOpenChange, ...props }: MenuProps): React.JSX.Element;
|
|
9
|
+
export interface MenuTriggerProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
|
|
10
|
+
className?: string;
|
|
11
|
+
/** 보통 sonic-ui Button: render={<Button size="icon-md" variant="ghost" aria-label="더 보기" />} */
|
|
12
|
+
render?: React.ReactElement;
|
|
13
|
+
}
|
|
14
|
+
export declare const MenuTrigger: React.ForwardRefExoticComponent<MenuTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
15
|
+
export interface MenuPopupProps {
|
|
16
|
+
className?: string;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare const MenuPopup: React.ForwardRefExoticComponent<MenuPopupProps & React.RefAttributes<HTMLDivElement>>;
|
|
20
|
+
export interface MenuItemProps {
|
|
21
|
+
/** danger: 삭제처럼 위험한 동작. 맨 아래, 구분선 뒤에 둬요 */
|
|
22
|
+
tone?: 'default' | 'danger';
|
|
23
|
+
onClick?: () => void;
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** 고른 뒤 메뉴를 닫을지 (기본 true) */
|
|
26
|
+
closeOnClick?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
children?: React.ReactNode;
|
|
29
|
+
}
|
|
30
|
+
export declare const MenuItem: React.ForwardRefExoticComponent<MenuItemProps & React.RefAttributes<HTMLDivElement>>;
|
|
31
|
+
export declare function MenuSeparator({ className }: {
|
|
32
|
+
className?: string;
|
|
33
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { itemClass as t, popupClass as n } from "./popup-styles.js";
|
|
4
|
+
import * as r from "react";
|
|
5
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
6
|
+
import { Menu as a } from "@base-ui/react/menu";
|
|
7
|
+
//#region src/components/menu.tsx
|
|
8
|
+
function o({ onOpenChange: e, ...t }) {
|
|
9
|
+
return /* @__PURE__ */ i(a.Root, {
|
|
10
|
+
...t,
|
|
11
|
+
onOpenChange: e && ((t) => e(t))
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
var s = r.forwardRef(function(e, t) {
|
|
15
|
+
return /* @__PURE__ */ i(a.Trigger, {
|
|
16
|
+
ref: t,
|
|
17
|
+
...e
|
|
18
|
+
});
|
|
19
|
+
}), c = r.forwardRef(function({ className: t, children: r }, o) {
|
|
20
|
+
return /* @__PURE__ */ i(a.Portal, { children: /* @__PURE__ */ i(a.Positioner, {
|
|
21
|
+
sideOffset: 4,
|
|
22
|
+
align: "start",
|
|
23
|
+
className: "sn:z-50 sn:outline-none",
|
|
24
|
+
children: /* @__PURE__ */ i(a.Popup, {
|
|
25
|
+
ref: o,
|
|
26
|
+
className: e(n, "sn:min-w-48", t),
|
|
27
|
+
children: r
|
|
28
|
+
})
|
|
29
|
+
}) });
|
|
30
|
+
}), l = r.forwardRef(function({ tone: n = "default", onClick: r, disabled: o, closeOnClick: s, className: c, children: l }, u) {
|
|
31
|
+
return /* @__PURE__ */ i(a.Item, {
|
|
32
|
+
ref: u,
|
|
33
|
+
onClick: r && (() => r()),
|
|
34
|
+
disabled: o,
|
|
35
|
+
closeOnClick: s,
|
|
36
|
+
className: e(t, n === "danger" ? "sn:text-danger" : "sn:text-default", c),
|
|
37
|
+
children: l
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
function u({ className: t }) {
|
|
41
|
+
return /* @__PURE__ */ i(a.Separator, { className: e("sn:my-1 sn:h-px sn:border-0 sn:bg-(--sn-color-border-default)", t) });
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { o as Menu, l as MenuItem, c as MenuPopup, u as MenuSeparator, s as MenuTrigger };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface PopoverProps {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
onOpenChange?: (open: boolean) => void;
|
|
6
|
+
children?: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function Popover({ onOpenChange, ...props }: PopoverProps): React.JSX.Element;
|
|
9
|
+
export interface PopoverButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'className'> {
|
|
10
|
+
className?: string;
|
|
11
|
+
render?: React.ReactElement;
|
|
12
|
+
}
|
|
13
|
+
export declare const PopoverTrigger: React.ForwardRefExoticComponent<PopoverButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
14
|
+
export declare const PopoverClose: React.ForwardRefExoticComponent<PopoverButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
15
|
+
export interface PopoverPopupProps {
|
|
16
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
17
|
+
className?: string;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
export declare const PopoverPopup: React.ForwardRefExoticComponent<PopoverPopupProps & React.RefAttributes<HTMLDivElement>>;
|
|
21
|
+
export declare const PopoverTitle: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLHeadingElement>, "className"> & {
|
|
22
|
+
className?: string;
|
|
23
|
+
} & React.RefAttributes<HTMLHeadingElement>>;
|
|
24
|
+
export declare const PopoverDescription: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLParagraphElement>, "className"> & {
|
|
25
|
+
className?: string;
|
|
26
|
+
} & React.RefAttributes<HTMLParagraphElement>>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { popupClass as t } from "./popup-styles.js";
|
|
4
|
+
import * as n from "react";
|
|
5
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
6
|
+
import { Popover as i } from "@base-ui/react/popover";
|
|
7
|
+
//#region src/components/popover.tsx
|
|
8
|
+
function a({ onOpenChange: e, ...t }) {
|
|
9
|
+
return /* @__PURE__ */ r(i.Root, {
|
|
10
|
+
...t,
|
|
11
|
+
onOpenChange: e && ((t) => e(t))
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
var o = n.forwardRef(function(e, t) {
|
|
15
|
+
return /* @__PURE__ */ r(i.Trigger, {
|
|
16
|
+
ref: t,
|
|
17
|
+
...e
|
|
18
|
+
});
|
|
19
|
+
}), s = n.forwardRef(function(e, t) {
|
|
20
|
+
return /* @__PURE__ */ r(i.Close, {
|
|
21
|
+
ref: t,
|
|
22
|
+
...e
|
|
23
|
+
});
|
|
24
|
+
}), c = n.forwardRef(function({ side: n = "bottom", className: a, children: o }, s) {
|
|
25
|
+
return /* @__PURE__ */ r(i.Portal, { children: /* @__PURE__ */ r(i.Positioner, {
|
|
26
|
+
side: n,
|
|
27
|
+
sideOffset: 8,
|
|
28
|
+
align: "start",
|
|
29
|
+
className: "sn:z-50 sn:outline-none",
|
|
30
|
+
children: /* @__PURE__ */ r(i.Popup, {
|
|
31
|
+
ref: s,
|
|
32
|
+
className: e(t, "sn:flex sn:w-80 sn:max-w-[calc(100vw-2*var(--sn-space-4))] sn:min-w-0 sn:flex-col sn:gap-3 sn:p-4", a),
|
|
33
|
+
children: o
|
|
34
|
+
})
|
|
35
|
+
}) });
|
|
36
|
+
}), l = n.forwardRef(function({ className: t, ...n }, a) {
|
|
37
|
+
return /* @__PURE__ */ r(i.Title, {
|
|
38
|
+
ref: a,
|
|
39
|
+
className: e("sn:m-0 sn:text-body-strong sn:text-default", t),
|
|
40
|
+
...n
|
|
41
|
+
});
|
|
42
|
+
}), u = n.forwardRef(function({ className: t, ...n }, a) {
|
|
43
|
+
return /* @__PURE__ */ r(i.Description, {
|
|
44
|
+
ref: a,
|
|
45
|
+
className: e("sn:-mt-2 sn:mb-0 sn:text-caption sn:text-muted", t),
|
|
46
|
+
...n
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//#endregion
|
|
50
|
+
export { a as Popover, s as PopoverClose, u as PopoverDescription, c as PopoverPopup, l as PopoverTitle, o as PopoverTrigger };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const popupClass = "sn:box-border sn:min-w-(--anchor-width) sn:max-h-(--available-height) sn:overflow-y-auto sn:origin-(--transform-origin) sn:rounded-lg sn:border sn:border-default sn:bg-surface sn:p-1 sn:font-sans sn:text-body sn:text-default sn:outline-none sn:transition-[opacity,scale] sn:duration-(--sn-duration-normal) sn:ease-standard sn:data-starting-style:scale-98 sn:data-starting-style:opacity-0 sn:data-ending-style:scale-98 sn:data-ending-style:opacity-0 sn:motion-reduce:transition-none";
|
|
2
|
+
export declare const itemClass = "sn:group sn:flex sn:h-8 sn:cursor-default sn:items-center sn:gap-2 sn:rounded-md sn:px-2 sn:outline-none sn:select-none sn:data-highlighted:bg-subtle sn:data-disabled:opacity-(--sn-opacity-disabled) sn:[&_svg]:size-4 sn:[&_svg]:shrink-0";
|
|
3
|
+
export declare const boxClass = "sn:box-border sn:inline-flex sn:size-4 sn:shrink-0 sn:items-center sn:justify-center sn:rounded-sm sn:border sn:border-strong sn:bg-surface sn:text-on-primary sn:[&_svg]:size-3";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region src/components/popup-styles.ts
|
|
3
|
+
var e = "sn:box-border sn:min-w-(--anchor-width) sn:max-h-(--available-height) sn:overflow-y-auto sn:origin-(--transform-origin) sn:rounded-lg sn:border sn:border-default sn:bg-surface sn:p-1 sn:font-sans sn:text-body sn:text-default sn:outline-none sn:transition-[opacity,scale] sn:duration-(--sn-duration-normal) sn:ease-standard sn:data-starting-style:scale-98 sn:data-starting-style:opacity-0 sn:data-ending-style:scale-98 sn:data-ending-style:opacity-0 sn:motion-reduce:transition-none", t = "sn:group sn:flex sn:h-8 sn:cursor-default sn:items-center sn:gap-2 sn:rounded-md sn:px-2 sn:outline-none sn:select-none sn:data-highlighted:bg-subtle sn:data-disabled:opacity-(--sn-opacity-disabled) sn:[&_svg]:size-4 sn:[&_svg]:shrink-0", n = "sn:box-border sn:inline-flex sn:size-4 sn:shrink-0 sn:items-center sn:justify-center sn:rounded-sm sn:border sn:border-strong sn:bg-surface sn:text-on-primary sn:[&_svg]:size-3";
|
|
4
|
+
//#endregion
|
|
5
|
+
export { n as boxClass, t as itemClass, e as popupClass };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
interface ProgressBaseProps {
|
|
3
|
+
value: number;
|
|
4
|
+
/** 기본 100 */
|
|
5
|
+
max?: number;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
/** 라벨이 없으면 읽을 이름이 없어서 aria-label이 필수예요 */
|
|
9
|
+
export type ProgressProps = ProgressBaseProps & ({
|
|
10
|
+
label: React.ReactNode;
|
|
11
|
+
} | {
|
|
12
|
+
label?: undefined;
|
|
13
|
+
'aria-label': string;
|
|
14
|
+
});
|
|
15
|
+
export declare function Progress({ value, max, className, ...props }: ProgressProps): React.JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
5
|
+
import { Progress as r } from "@base-ui/react/progress";
|
|
6
|
+
//#region src/components/progress.tsx
|
|
7
|
+
function i({ value: i, max: a = 100, className: o, ...s }) {
|
|
8
|
+
let c = s.label;
|
|
9
|
+
return /* @__PURE__ */ n(r.Root, {
|
|
10
|
+
value: i,
|
|
11
|
+
max: a,
|
|
12
|
+
"aria-label": "aria-label" in s ? s["aria-label"] : void 0,
|
|
13
|
+
className: e("sn:flex sn:flex-col sn:gap-2 sn:font-sans", o),
|
|
14
|
+
children: [c != null && /* @__PURE__ */ n("div", {
|
|
15
|
+
className: "sn:flex sn:items-center sn:gap-2",
|
|
16
|
+
children: [/* @__PURE__ */ t(r.Label, {
|
|
17
|
+
className: "sn:min-w-0 sn:flex-1 sn:text-label sn:text-default",
|
|
18
|
+
children: c
|
|
19
|
+
}), /* @__PURE__ */ t(r.Value, { className: "sn:font-mono sn:text-caption sn:text-muted" })]
|
|
20
|
+
}), /* @__PURE__ */ t(r.Track, {
|
|
21
|
+
className: "sn:h-1 sn:overflow-hidden sn:rounded-full sn:bg-subtle",
|
|
22
|
+
children: /* @__PURE__ */ t(r.Indicator, { className: "sn:h-full sn:rounded-full sn:bg-primary sn:transition-[width] sn:duration-(--sn-duration-normal) sn:ease-standard sn:motion-reduce:transition-none" })
|
|
23
|
+
})]
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { i as Progress };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface RadioGroupProps {
|
|
3
|
+
/** 그룹 이름 (fieldset legend) */
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
value?: string;
|
|
6
|
+
defaultValue?: string;
|
|
7
|
+
onValueChange?: (value: string) => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
name?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare function RadioGroup({ label, value, defaultValue, onValueChange, disabled, name, className, children }: RadioGroupProps): React.JSX.Element;
|
|
14
|
+
export interface RadioProps {
|
|
15
|
+
value: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
className?: string;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
export declare const Radio: React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { choiceFocusClass as t, choiceLabelClass as n } from "./checkbox.js";
|
|
4
|
+
import * as r from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
import { Fieldset as o } from "@base-ui/react/fieldset";
|
|
7
|
+
import { Radio as s } from "@base-ui/react/radio";
|
|
8
|
+
import { RadioGroup as c } from "@base-ui/react/radio-group";
|
|
9
|
+
//#region src/components/radio-group.tsx
|
|
10
|
+
function l({ label: t, value: n, defaultValue: r, onValueChange: s, disabled: l, name: u, className: d, children: f }) {
|
|
11
|
+
return /* @__PURE__ */ a(o.Root, {
|
|
12
|
+
className: e("sn:m-0 sn:flex sn:min-w-0 sn:flex-col sn:gap-3 sn:border-0 sn:p-0 sn:font-sans", d),
|
|
13
|
+
children: [/* @__PURE__ */ i(o.Legend, {
|
|
14
|
+
className: "sn:p-0 sn:text-label sn:text-default",
|
|
15
|
+
children: t
|
|
16
|
+
}), /* @__PURE__ */ i(c, {
|
|
17
|
+
value: n,
|
|
18
|
+
defaultValue: r,
|
|
19
|
+
onValueChange: s && ((e) => s(e)),
|
|
20
|
+
disabled: l,
|
|
21
|
+
name: u,
|
|
22
|
+
className: "sn:flex sn:flex-col sn:gap-2",
|
|
23
|
+
children: f
|
|
24
|
+
})]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
var u = r.forwardRef(function({ value: r, disabled: o, className: c, children: l }, u) {
|
|
28
|
+
return /* @__PURE__ */ a("label", {
|
|
29
|
+
className: e(n, o && "sn:cursor-not-allowed sn:opacity-(--sn-opacity-disabled)", c),
|
|
30
|
+
children: [/* @__PURE__ */ i(s.Root, {
|
|
31
|
+
ref: u,
|
|
32
|
+
value: r,
|
|
33
|
+
disabled: o,
|
|
34
|
+
className: e("sn:box-border sn:inline-flex sn:size-4 sn:shrink-0 sn:items-center sn:justify-center sn:rounded-full sn:border sn:border-strong sn:bg-surface sn:data-checked:border-transparent sn:data-checked:bg-primary", t),
|
|
35
|
+
children: /* @__PURE__ */ i(s.Indicator, { className: "sn:size-2 sn:rounded-full sn:bg-(--sn-color-text-on-primary)" })
|
|
36
|
+
}), l]
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
//#endregion
|
|
40
|
+
export { u as Radio, l as RadioGroup };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SelectOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
interface SelectBaseProps {
|
|
8
|
+
options: SelectOption[];
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
invalid?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
name?: string;
|
|
13
|
+
id?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
'aria-label'?: string;
|
|
16
|
+
}
|
|
17
|
+
/** multiple이면 값이 배열이에요 */
|
|
18
|
+
export type SelectProps = SelectBaseProps & ({
|
|
19
|
+
multiple?: false;
|
|
20
|
+
value?: string | null;
|
|
21
|
+
defaultValue?: string | null;
|
|
22
|
+
onValueChange?: (value: string | null) => void;
|
|
23
|
+
} | {
|
|
24
|
+
multiple: true;
|
|
25
|
+
value?: string[];
|
|
26
|
+
defaultValue?: string[];
|
|
27
|
+
onValueChange?: (value: string[]) => void;
|
|
28
|
+
});
|
|
29
|
+
export declare function Select(props: SelectProps): React.JSX.Element;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { inputClass as t } from "./input.js";
|
|
4
|
+
import { itemClass as n, popupClass as r } from "./popup-styles.js";
|
|
5
|
+
import "react";
|
|
6
|
+
import { Check as i, ChevronDown as a } from "lucide-react";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { Select as c } from "@base-ui/react/select";
|
|
9
|
+
//#region src/components/select.tsx
|
|
10
|
+
function l(l) {
|
|
11
|
+
let { options: u, placeholder: d, invalid: f, disabled: p, name: m, id: h, className: g, multiple: _ } = l, v = (e) => u.find((t) => t.value === e)?.label ?? e;
|
|
12
|
+
return /* @__PURE__ */ s(c.Root, {
|
|
13
|
+
items: u,
|
|
14
|
+
multiple: _,
|
|
15
|
+
value: l.value,
|
|
16
|
+
defaultValue: l.defaultValue,
|
|
17
|
+
onValueChange: l.onValueChange && ((e) => l.onValueChange(e)),
|
|
18
|
+
disabled: p,
|
|
19
|
+
name: m,
|
|
20
|
+
children: [/* @__PURE__ */ s(c.Trigger, {
|
|
21
|
+
id: h,
|
|
22
|
+
"aria-label": l["aria-label"],
|
|
23
|
+
"aria-invalid": f || void 0,
|
|
24
|
+
className: e(t, "sn:group sn:flex sn:h-8 sn:items-center sn:gap-2 sn:text-left sn:cursor-default sn:data-popup-open:border-focus", g),
|
|
25
|
+
children: [/* @__PURE__ */ o(c.Value, {
|
|
26
|
+
placeholder: d,
|
|
27
|
+
className: "sn:min-w-0 sn:flex-1 sn:truncate sn:data-placeholder:text-muted",
|
|
28
|
+
children: _ ? (e) => e.length ? e.map(v).join(", ") : d : void 0
|
|
29
|
+
}), /* @__PURE__ */ o(c.Icon, {
|
|
30
|
+
className: "sn:flex sn:text-muted sn:transition-transform sn:duration-(--sn-duration-fast) sn:group-data-popup-open:rotate-180 sn:motion-reduce:transition-none sn:[&_svg]:size-4",
|
|
31
|
+
children: /* @__PURE__ */ o(a, {})
|
|
32
|
+
})]
|
|
33
|
+
}), /* @__PURE__ */ o(c.Portal, { children: /* @__PURE__ */ o(c.Positioner, {
|
|
34
|
+
sideOffset: 4,
|
|
35
|
+
alignItemWithTrigger: !1,
|
|
36
|
+
className: "sn:z-50 sn:outline-none",
|
|
37
|
+
children: /* @__PURE__ */ o(c.Popup, {
|
|
38
|
+
className: r,
|
|
39
|
+
children: /* @__PURE__ */ o(c.List, { children: u.map((t) => /* @__PURE__ */ s(c.Item, {
|
|
40
|
+
value: t.value,
|
|
41
|
+
disabled: t.disabled,
|
|
42
|
+
className: n,
|
|
43
|
+
children: [
|
|
44
|
+
_ && /* @__PURE__ */ o("span", {
|
|
45
|
+
className: e("sn:box-border sn:inline-flex sn:size-4 sn:shrink-0 sn:items-center sn:justify-center sn:rounded-sm sn:border sn:border-strong sn:bg-surface sn:text-on-primary sn:[&_svg]:size-3", "sn:group-data-selected:border-transparent sn:group-data-selected:bg-primary"),
|
|
46
|
+
children: /* @__PURE__ */ o(c.ItemIndicator, {
|
|
47
|
+
className: "sn:flex",
|
|
48
|
+
children: /* @__PURE__ */ o(i, {})
|
|
49
|
+
})
|
|
50
|
+
}),
|
|
51
|
+
/* @__PURE__ */ o(c.ItemText, {
|
|
52
|
+
className: "sn:min-w-0 sn:flex-1 sn:truncate",
|
|
53
|
+
children: t.label
|
|
54
|
+
}),
|
|
55
|
+
!_ && /* @__PURE__ */ o(c.ItemIndicator, {
|
|
56
|
+
className: "sn:flex",
|
|
57
|
+
children: /* @__PURE__ */ o(i, {})
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
}, t.value)) })
|
|
61
|
+
})
|
|
62
|
+
}) })]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { l as Select };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SeparatorProps {
|
|
3
|
+
orientation?: 'horizontal' | 'vertical';
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
/** 관련 없는 묶음 사이를 나눌 때만 써요. 1px border/default */
|
|
7
|
+
export declare function Separator({ orientation, className }: SeparatorProps): React.JSX.Element;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
5
|
+
import { Separator as n } from "@base-ui/react/separator";
|
|
6
|
+
//#region src/components/separator.tsx
|
|
7
|
+
function r({ orientation: r = "horizontal", className: i }) {
|
|
8
|
+
return /* @__PURE__ */ t(n, {
|
|
9
|
+
orientation: r,
|
|
10
|
+
className: e("sn:shrink-0 sn:border-0 sn:bg-(--sn-color-border-default)", r === "horizontal" ? "sn:h-px sn:w-full" : "sn:w-px sn:self-stretch", i)
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { r as Separator };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SkeletonProps {
|
|
3
|
+
shape?: 'text' | 'rect' | 'circle';
|
|
4
|
+
/** 크기는 쓰는 곳에서 정해요 (예: className="w-40") */
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
/** 로딩 자리 표시. 스크린 리더에는 숨겨요 — 로딩 영역 전체에 aria-busy를 두세요 */
|
|
8
|
+
export declare function Skeleton({ shape, className }: SkeletonProps): React.JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { cva as t } from "class-variance-authority";
|
|
5
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/skeleton.tsx
|
|
7
|
+
var r = t("sn:block sn:shrink-0 sn:bg-subtle sn:animate-[sn-pulse_var(--sn-duration-loop)_ease-in-out_infinite] sn:motion-reduce:animate-none", {
|
|
8
|
+
variants: { shape: {
|
|
9
|
+
text: "sn:h-3 sn:w-full sn:rounded-sm",
|
|
10
|
+
rect: "sn:h-16 sn:w-full sn:rounded-md",
|
|
11
|
+
circle: "sn:size-8 sn:rounded-full"
|
|
12
|
+
} },
|
|
13
|
+
defaultVariants: { shape: "text" }
|
|
14
|
+
});
|
|
15
|
+
function i({ shape: t, className: i }) {
|
|
16
|
+
return /* @__PURE__ */ n("span", {
|
|
17
|
+
"aria-hidden": !0,
|
|
18
|
+
className: e(r({ shape: t }), i)
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { i as Skeleton };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface StateLayoutProps {
|
|
3
|
+
icon?: React.ReactNode;
|
|
4
|
+
iconClassName: string;
|
|
5
|
+
title: React.ReactNode;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
role?: 'alert';
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare function StateLayout({ icon, iconClassName, title, description, className, role, children }: StateLayoutProps): React.JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/state-layout.tsx
|
|
6
|
+
function r({ icon: r, iconClassName: i, title: a, description: o, className: s, role: c, children: l }) {
|
|
7
|
+
return /* @__PURE__ */ n("div", {
|
|
8
|
+
role: c,
|
|
9
|
+
className: e("sn:box-border sn:flex sn:flex-col sn:items-center sn:gap-4 sn:px-6 sn:py-12 sn:text-center sn:font-sans", s),
|
|
10
|
+
children: [
|
|
11
|
+
r && /* @__PURE__ */ t("span", {
|
|
12
|
+
"aria-hidden": !0,
|
|
13
|
+
className: e("sn:inline-flex sn:size-12 sn:items-center sn:justify-center sn:rounded-full sn:bg-subtle sn:[&_svg]:size-6", i),
|
|
14
|
+
children: r
|
|
15
|
+
}),
|
|
16
|
+
/* @__PURE__ */ n("div", {
|
|
17
|
+
className: "sn:flex sn:flex-col sn:gap-1",
|
|
18
|
+
children: [/* @__PURE__ */ t("p", {
|
|
19
|
+
className: "sn:m-0 sn:text-body-strong sn:text-default",
|
|
20
|
+
children: a
|
|
21
|
+
}), o && /* @__PURE__ */ t("p", {
|
|
22
|
+
className: "sn:m-0 sn:text-body sn:text-muted",
|
|
23
|
+
children: o
|
|
24
|
+
})]
|
|
25
|
+
}),
|
|
26
|
+
l
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { r as StateLayout };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SwitchProps {
|
|
3
|
+
checked?: boolean;
|
|
4
|
+
defaultChecked?: boolean;
|
|
5
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
name?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/** 누르는 즉시 적용되는 설정에만 써요 */
|
|
12
|
+
export declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { choiceFocusClass as t, choiceLabelClass as n } from "./checkbox.js";
|
|
4
|
+
import * as r from "react";
|
|
5
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
|
+
import { Switch as o } from "@base-ui/react/switch";
|
|
7
|
+
//#region src/components/switch.tsx
|
|
8
|
+
var s = r.forwardRef(function({ checked: r, defaultChecked: s, onCheckedChange: c, disabled: l, name: u, className: d, children: f }, p) {
|
|
9
|
+
return /* @__PURE__ */ a("label", {
|
|
10
|
+
className: e(n, l && "sn:cursor-not-allowed sn:opacity-(--sn-opacity-disabled)", d),
|
|
11
|
+
children: [/* @__PURE__ */ i(o.Root, {
|
|
12
|
+
ref: p,
|
|
13
|
+
checked: r,
|
|
14
|
+
defaultChecked: s,
|
|
15
|
+
onCheckedChange: c && ((e) => c(e)),
|
|
16
|
+
disabled: l,
|
|
17
|
+
name: u,
|
|
18
|
+
className: e("sn:box-border sn:inline-flex sn:h-4 sn:w-7 sn:shrink-0 sn:items-center sn:rounded-full sn:border sn:border-strong sn:bg-subtle sn:p-px sn:transition-colors sn:duration-(--sn-duration-fast) sn:ease-standard sn:data-checked:border-transparent sn:data-checked:bg-primary sn:motion-reduce:transition-none", t),
|
|
19
|
+
children: /* @__PURE__ */ i(o.Thumb, { className: "sn:block sn:size-3 sn:rounded-full sn:bg-surface sn:transition-transform sn:duration-(--sn-duration-fast) sn:ease-standard sn:data-checked:translate-x-3 sn:data-checked:bg-(--sn-color-text-on-primary) sn:motion-reduce:transition-none" })
|
|
20
|
+
}), f]
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { s as Switch };
|