cashdoc-cms-design-system 1.0.0 → 1.0.2
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/README.md +106 -0
- package/dist/components/Button/Button.d.ts +116 -0
- package/dist/components/Checkbox/Checkbox.d.ts +79 -0
- package/dist/components/DatePicker/DatePicker.d.ts +82 -0
- package/dist/components/DatePicker/index.d.ts +0 -2
- package/dist/components/DateRangePicker/DateRangePicker.d.ts +93 -0
- package/dist/components/DateRangePicker/index.d.ts +2 -0
- package/dist/components/Dropdown/Combobox.d.ts +78 -0
- package/dist/components/Dropdown/Dropdown.d.ts +81 -0
- package/dist/components/Dropdown/Select.d.ts +78 -0
- package/dist/components/LoadingCircle/LoadingCircle.d.ts +70 -0
- package/dist/components/Modal/ConfirmModal.d.ts +24 -0
- package/dist/components/Modal/DeleteModal.d.ts +19 -0
- package/dist/components/Modal/ErrorModal.d.ts +20 -0
- package/dist/components/Modal/Modal.d.ts +171 -0
- package/dist/components/Modal/SuccessModal.d.ts +19 -0
- package/dist/components/Modal/WarningModal.d.ts +21 -0
- package/dist/components/Pagination/Pagination.d.ts +106 -0
- package/dist/components/Pagination/index.d.ts +2 -0
- package/dist/components/Popover/Popover.d.ts +70 -0
- package/dist/components/Popover/PopoverMenuItem.d.ts +25 -0
- package/dist/components/RadioButton/RadioButton.d.ts +71 -0
- package/dist/components/SideNavigation/SideNavigation.d.ts +77 -0
- package/dist/components/Switch/Switch.d.ts +60 -0
- package/dist/components/TagInput/TagInput.d.ts +96 -0
- package/dist/components/Text/Text.d.ts +57 -0
- package/dist/components/TextInput/TextInput.d.ts +78 -0
- package/dist/components/TimePicker/TimePicker.d.ts +110 -0
- package/dist/components/TimePicker/index.d.ts +2 -0
- package/dist/components/Toast/Toaster.d.ts +78 -0
- package/dist/components/icons/ChevronRightIcon.d.ts +11 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/index.es.js +3770 -3292
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +37 -22
- package/dist/index.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/dist/components/DatePicker/DateRangePicker.d.ts +0 -14
|
@@ -7,5 +7,30 @@ declare const popoverMenuItemVariants: (props?: ({
|
|
|
7
7
|
export interface PopoverMenuItemProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof popoverMenuItemVariants> {
|
|
8
8
|
icon?: React.ReactNode;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Popover 내부에 배치되는 개별 액션 항목 컴포넌트입니다.
|
|
12
|
+
*
|
|
13
|
+
* {@link PopoverMenuItem}은 아이콘과 텍스트가 결합된 형태의 메뉴 버튼으로,
|
|
14
|
+
* 일관된 패딩, 폰트, 호버 효과를 제공합니다.
|
|
15
|
+
*
|
|
16
|
+
* ## Example
|
|
17
|
+
*
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <PopoverContent>
|
|
20
|
+
* <div className="flex flex-col gap-1">
|
|
21
|
+
* <PopoverMenuItem icon={<EditIcon />} onClick={handleEdit}>
|
|
22
|
+
* 수정하기
|
|
23
|
+
* </PopoverMenuItem>
|
|
24
|
+
* <PopoverMenuItem
|
|
25
|
+
* variant="destructive"
|
|
26
|
+
* icon={<TrashIcon />}
|
|
27
|
+
* onClick={handleDelete}
|
|
28
|
+
* >
|
|
29
|
+
* 삭제하기
|
|
30
|
+
* </PopoverMenuItem>
|
|
31
|
+
* </div>
|
|
32
|
+
* </PopoverContent>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
10
35
|
declare const PopoverMenuItem: import('react').ForwardRefExoticComponent<PopoverMenuItemProps & import('react').RefAttributes<HTMLButtonElement>>;
|
|
11
36
|
export { PopoverMenuItem, popoverMenuItemVariants };
|
|
@@ -2,6 +2,77 @@ import { default as React } from 'react';
|
|
|
2
2
|
import { VariantProps } from 'class-variance-authority';
|
|
3
3
|
|
|
4
4
|
import * as RadioGroupPrimitives from "@radix-ui/react-radio-group";
|
|
5
|
+
/**
|
|
6
|
+
* 상호 배타적인 옵션 목록 중 단 하나만을 선택해야 할 때 사용하는 컴포넌트입니다.
|
|
7
|
+
*
|
|
8
|
+
* {@link RadioGroup}은 여러 개의 {@link RadioGroupItem}을 포함하는 컨테이너이며,
|
|
9
|
+
* 한 번에 하나의 아이템만 선택될 수 있도록 상태를 관리합니다.
|
|
10
|
+
*
|
|
11
|
+
* Radix UI의 Radio Group 컴포넌트를 기반으로 구현되어 방향키를 이용한 내비게이션 등
|
|
12
|
+
* 표준 라디오 그룹 동작을 지원합니다.
|
|
13
|
+
*
|
|
14
|
+
* ## When (언제 사용해야 하는가)
|
|
15
|
+
*
|
|
16
|
+
* **사용해야 하는 경우:**
|
|
17
|
+
* - **단일 선택**: 성별 선택, 배송 방법 선택 등 여러 옵션 중 하나만 골라야 할 때
|
|
18
|
+
* - **옵션 노출**: 모든 선택지가 사용자에게 한눈에 보여야 할 때 (옵션이 5개 이하인 경우 권장)
|
|
19
|
+
*
|
|
20
|
+
* **사용하지 말아야 하는 경우:**
|
|
21
|
+
* - **다중 선택**: 여러 개를 동시에 선택할 수 있어야 하는 경우 `Checkbox`를 사용하세요.
|
|
22
|
+
* - **옵션이 많은 경우**: 선택지가 5-6개를 넘어간다면 화면 공간을 위해 `Dropdown`을 사용하는 것이 좋습니다.
|
|
23
|
+
* - **독립적 On/Off**: 단순히 하나의 항목을 켜고 끄는 것이라면 `Checkbox`나 `Switch`가 더 적절합니다.
|
|
24
|
+
*
|
|
25
|
+
* ## Layout behavior
|
|
26
|
+
*
|
|
27
|
+
* - **asChild**: `asChild` 속성을 사용하면 기본 렌더링 요소 대신 자식 요소를 렌더링하고 속성을 병합합니다.
|
|
28
|
+
* 커스텀 컴포넌트나 다른 라이브러리와 연동할 때 유용합니다.
|
|
29
|
+
*
|
|
30
|
+
* ## Accessibility
|
|
31
|
+
*
|
|
32
|
+
* - **Keyboard Navigation**: `Tab` 키로 그룹에 진입한 후, 화살표 키(`Up`, `Down`, `Left`, `Right`)를 사용하여 옵션 간 이동 및 선택이 가능합니다.
|
|
33
|
+
* - **Roles**: `role="radiogroup"` 및 `role="radio"` 속성이 자동으로 부여됩니다.
|
|
34
|
+
*
|
|
35
|
+
* ## Example
|
|
36
|
+
*
|
|
37
|
+
* {@tool snippet}
|
|
38
|
+
* 기본적인 라디오 그룹 사용 예시:
|
|
39
|
+
*
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <RadioGroup defaultValue="apple" onValueChange={(val) => console.log(val)}>
|
|
42
|
+
* <div className="flex items-center gap-2">
|
|
43
|
+
* <RadioGroupItem value="apple" id="apple" />
|
|
44
|
+
* <label htmlFor="apple">사과</label>
|
|
45
|
+
* </div>
|
|
46
|
+
* <div className="flex items-center gap-2">
|
|
47
|
+
* <RadioGroupItem value="banana" id="banana" />
|
|
48
|
+
* <label htmlFor="banana">바나나</label>
|
|
49
|
+
* </div>
|
|
50
|
+
* </RadioGroup>
|
|
51
|
+
* ```
|
|
52
|
+
* {@end-tool}
|
|
53
|
+
*
|
|
54
|
+
* {@tool snippet}
|
|
55
|
+
* `asChild`를 사용한 커스텀 렌더링 예시:
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <RadioGroup asChild>
|
|
59
|
+
* <section className="my-custom-group">
|
|
60
|
+
* <RadioGroupItem asChild value="custom">
|
|
61
|
+
* <button type="button" className="custom-radio-trigger">
|
|
62
|
+
* 커스텀 라디오
|
|
63
|
+
* </button>
|
|
64
|
+
* </RadioGroupItem>
|
|
65
|
+
* </section>
|
|
66
|
+
* </RadioGroup>
|
|
67
|
+
* ```
|
|
68
|
+
* {@end-tool}
|
|
69
|
+
*
|
|
70
|
+
* See also:
|
|
71
|
+
*
|
|
72
|
+
* - {@link Checkbox}, 다중 선택이 필요한 경우
|
|
73
|
+
* - {@link Dropdown}, 옵션이 많아 리스트로 숨겨야 하는 경우
|
|
74
|
+
* - {@link Switch}, 단순 활성화/비활성화를 토글할 때
|
|
75
|
+
*/
|
|
5
76
|
declare const RadioGroup: React.ForwardRefExoticComponent<Omit<RadioGroupPrimitives.RadioGroupProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
6
77
|
declare const radioGroupItemVariants: (props?: ({
|
|
7
78
|
variant?: "default" | "black" | "green" | "blue" | "red" | null | undefined;
|
|
@@ -18,4 +18,81 @@ export interface SideNavigationProps {
|
|
|
18
18
|
headerSlot?: React.ReactNode;
|
|
19
19
|
className?: string;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* 어플리케이션의 주요 섹션 간 이동을 담당하는 왼쪽 사이드바 메뉴 컴포넌트입니다.
|
|
23
|
+
*
|
|
24
|
+
* {@link SideNavigation}은 계층형 메뉴 구조(1단계 및 서브메뉴)를 지원하며,
|
|
25
|
+
* 현재 사용자의 위치를 시각적으로 강조하여 내비게이션 맥락을 제공합니다.
|
|
26
|
+
* 다크 테마(Dark Theme) 스타일로 고정되어 대시보드 및 CMS 관리 도구에 적합합니다.
|
|
27
|
+
*
|
|
28
|
+
* Radix UI의 Accordion 컴포넌트를 기반으로 구현되어 서브메뉴의 펼침/접힘 동작이
|
|
29
|
+
* 부드럽게 처리됩니다.
|
|
30
|
+
*
|
|
31
|
+
* ## When (언제 사용해야 하는가)
|
|
32
|
+
*
|
|
33
|
+
* **사용해야 하는 경우:**
|
|
34
|
+
* - **메인 내비게이션**: 서비스의 핵심 기능을 상시 노출하고 접근해야 할 때
|
|
35
|
+
* - **계층 구조 관리**: 대메뉴와 그에 속한 소메뉴를 구조적으로 보여줘야 할 때
|
|
36
|
+
* - **관리자 페이지**: 다양한 관리 도구와 설정 항목을 분류하여 제공할 때
|
|
37
|
+
*
|
|
38
|
+
* **사용하지 말아야 하는 경우:**
|
|
39
|
+
* - **단순 링크 목록**: 3-4개의 단순한 링크라면 상단 내비게이션(GNB)이 더 적절할 수 있습니다.
|
|
40
|
+
* - **모바일 전용 탭**: 모바일 앱 스타일의 내비게이션이 필요하다면 하단 탭 바를 고려하세요.
|
|
41
|
+
*
|
|
42
|
+
* ## Layout behavior
|
|
43
|
+
*
|
|
44
|
+
* - **Fixed Width**: 기본적으로 `w-70` (280px)의 고정 너비를 가지며, 세로 전체(h-full)를 차지합니다.
|
|
45
|
+
* - **Scrollable**: 메뉴 항목이 많아 화면 높이를 넘어가면 메뉴 영역 내부에 스크롤이 발생합니다.
|
|
46
|
+
* - **Accordion**: 서브메뉴가 있는 항목 클릭 시 아래로 펼쳐지는 애니메이션이 적용됩니다.
|
|
47
|
+
*
|
|
48
|
+
* ## Usage guidelines
|
|
49
|
+
*
|
|
50
|
+
* ### ✅ Do (권장 사항)
|
|
51
|
+
*
|
|
52
|
+
* - **아이콘 활용**: 각 대메뉴에 적절한 아이콘을 배치하여 사용자가 메뉴의 성격을 빠르게 파악하게 하세요.
|
|
53
|
+
* - **상태 연동**: 현재 활성화된 페이지의 URL과 `selectedUrl`을 정확히 일치시켜 하이라이트가 올바르게 표시되도록 하세요.
|
|
54
|
+
* - **일관된 그룹화**: 연관된 기능들은 하나의 대메뉴 아래 서브메뉴로 묶어 복잡도를 낮추세요.
|
|
55
|
+
*
|
|
56
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
57
|
+
*
|
|
58
|
+
* - **과도한 계층**: 서브메뉴의 서브메뉴(3단계 이상)는 가급적 피하세요. UI가 복잡해지고 사용성이 떨어집니다.
|
|
59
|
+
* - **긴 메뉴 명칭**: 사이드바 너비가 제한적이므로 메뉴 이름은 짧고 명확하게(가급적 10자 이내) 작성하세요.
|
|
60
|
+
*
|
|
61
|
+
* ## Accessibility
|
|
62
|
+
*
|
|
63
|
+
* - **Accordion Support**: 서브메뉴 상태를 스크린 리더에서 '펼쳐짐/접힘'으로 인식합니다.
|
|
64
|
+
* - **Keyboard Interaction**: `Tab` 키로 메뉴를 이동하고, `Enter`나 `Space`로 메뉴를 열거나 이동할 수 있습니다.
|
|
65
|
+
*
|
|
66
|
+
* ## Example
|
|
67
|
+
*
|
|
68
|
+
* {@tool snippet}
|
|
69
|
+
* 서브메뉴를 포함한 사이드 네비게이션:
|
|
70
|
+
*
|
|
71
|
+
* ```tsx
|
|
72
|
+
* const menus = [
|
|
73
|
+
* { url: '/dashboard', title: '홈', icon: <HomeIcon /> },
|
|
74
|
+
* {
|
|
75
|
+
* url: '/contents',
|
|
76
|
+
* title: '콘텐츠 관리',
|
|
77
|
+
* icon: <FileTextIcon />,
|
|
78
|
+
* subMenu: [
|
|
79
|
+
* { url: '/contents/posts', title: '게시글 목록' },
|
|
80
|
+
* { url: '/contents/comments', title: '댓글 관리' },
|
|
81
|
+
* ]
|
|
82
|
+
* },
|
|
83
|
+
* ];
|
|
84
|
+
*
|
|
85
|
+
* <SideNavigation
|
|
86
|
+
* menus={menus}
|
|
87
|
+
* selectedUrl="/contents/posts"
|
|
88
|
+
* onMenuClick={(url) => navigate(url)}
|
|
89
|
+
* />
|
|
90
|
+
* ```
|
|
91
|
+
* {@end-tool}
|
|
92
|
+
*
|
|
93
|
+
* See also:
|
|
94
|
+
*
|
|
95
|
+
* - {@link Button}, 단순 액션 실행을 위한 요소
|
|
96
|
+
* - {@link Popover}, 클릭 시 일시적으로 나타나는 추가 메뉴가 필요한 경우
|
|
97
|
+
*/
|
|
21
98
|
export declare const SideNavigation: React.ForwardRefExoticComponent<SideNavigationProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -7,5 +7,65 @@ declare const switchVariants: (props?: ({
|
|
|
7
7
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
8
8
|
export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>, VariantProps<typeof switchVariants> {
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* 두 가지 상반된 상태(On/Off, 활성/비활성)를 즉각적으로 전환할 때 사용하는 컴포넌트입니다.
|
|
12
|
+
*
|
|
13
|
+
* {@link Switch}는 실제 전등 스위치와 같은 직관적인 시각적 모델을 제공하며,
|
|
14
|
+
* 체크박스보다 더 '즉각적인 반영'의 의미를 가집니다.
|
|
15
|
+
*
|
|
16
|
+
* Radix UI의 Switch 컴포넌트를 기반으로 구현되어 접근성과 애니메이션이
|
|
17
|
+
* 자동으로 처리됩니다.
|
|
18
|
+
*
|
|
19
|
+
* ## When (언제 사용해야 하는가)
|
|
20
|
+
*
|
|
21
|
+
* **사용해야 하는 경우:**
|
|
22
|
+
* - **즉각적 설정 반영**: 클릭 즉시 시스템 설정이 변경되거나 저장되어야 할 때 (예: 다크모드 켜기, 알림 수신 동의)
|
|
23
|
+
* - **상태 전환**: 특정 기능의 사용 여부를 결정할 때
|
|
24
|
+
* - **단순 토글**: 복잡한 입력 없이 켜고 끄는 행위만 필요할 때
|
|
25
|
+
*
|
|
26
|
+
* **사용하지 말아야 하는 경우:**
|
|
27
|
+
* - **제출 버튼 필요**: 여러 정보를 입력하고 '확인' 버튼을 눌러야 결과가 반영되는 폼 내에서는 `Checkbox`를 사용하세요.
|
|
28
|
+
* - **다중 선택**: 여러 항목 중 일부를 골라야 할 때도 `Checkbox`가 더 적합합니다.
|
|
29
|
+
*
|
|
30
|
+
* ## Layout behavior
|
|
31
|
+
*
|
|
32
|
+
* - **Inline Component**: 주변 텍스트나 다른 요소와 자연스럽게 어우러지는 인라인 블록 형태입니다.
|
|
33
|
+
* - **Thumb Animation**: 클릭 시 스위치의 '손잡이(Thumb)'가 부드럽게 좌우로 이동하며 상태 변화를 시각화합니다.
|
|
34
|
+
*
|
|
35
|
+
* ## Usage guidelines
|
|
36
|
+
*
|
|
37
|
+
* ### ✅ Do (권장 사항)
|
|
38
|
+
*
|
|
39
|
+
* - **명확한 현재 상태 표시**: 색상 변화(회색 vs 색상)를 통해 켜져 있는지 꺼져 있는지 한눈에 알 수 있게 하세요.
|
|
40
|
+
* - **레이블과 함께 사용**: 스위치 옆에 무엇을 제어하는지 설명하는 텍스트를 반드시 배치하세요.
|
|
41
|
+
*
|
|
42
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
43
|
+
*
|
|
44
|
+
* - **모호한 의미**: 상태 전환 후에 어떤 변화가 생길지 명확하지 않은 경우에는 사용을 지양하세요.
|
|
45
|
+
* - **긴 대기 시간**: 서버 통신 등으로 인해 상태 반영에 시간이 걸리는 경우, 로딩 인디케이터를 함께 보여주거나 즉시 반응을 우선 처리하세요.
|
|
46
|
+
*
|
|
47
|
+
* ## Accessibility
|
|
48
|
+
*
|
|
49
|
+
* - **Keyboard Support**: `Space` 키를 사용하여 상태를 전환할 수 있습니다.
|
|
50
|
+
* - **Roles**: `role="switch"` 속성을 사용하여 스크린 리더에서 토글 상태를 읽어줍니다.
|
|
51
|
+
*
|
|
52
|
+
* ## Example
|
|
53
|
+
*
|
|
54
|
+
* {@tool snippet}
|
|
55
|
+
* 레이블과 함께 사용하는 기본적인 스위치:
|
|
56
|
+
*
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <div className="flex items-center gap-2">
|
|
59
|
+
* <Switch id="airplane-mode" />
|
|
60
|
+
* <label htmlFor="airplane-mode">비행기 모드</label>
|
|
61
|
+
* </div>
|
|
62
|
+
* ```
|
|
63
|
+
* {@end-tool}
|
|
64
|
+
*
|
|
65
|
+
* See also:
|
|
66
|
+
*
|
|
67
|
+
* - {@link Checkbox}, 제출 전까지 상태를 유지해야 하는 경우
|
|
68
|
+
* - {@link RadioButton}, 여러 선택지 중 하나를 고르는 경우
|
|
69
|
+
*/
|
|
10
70
|
declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLButtonElement>>;
|
|
11
71
|
export { Switch };
|
|
@@ -17,6 +17,102 @@ export interface TagInputProps extends Omit<VariantProps<typeof tagInputContaine
|
|
|
17
17
|
validateTag?: (tag: string, currentTags: string[]) => boolean | string | undefined;
|
|
18
18
|
className?: string;
|
|
19
19
|
id?: string;
|
|
20
|
+
labelLayout?: "vertical" | "horizontal";
|
|
21
|
+
labelWidth?: string;
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* 사용자가 텍스트를 입력하여 여러 개의 태그(키워드) 목록을 생성하고 관리할 수 있는 컴포넌트입니다.
|
|
25
|
+
*
|
|
26
|
+
* {@link TagInput}은 입력창 내부에 시각적인 태그를 표시하며,
|
|
27
|
+
* 엔터(Enter) 키를 통해 새로운 태그를 추가하고 'x' 버튼을 눌러 기존 태그를 제거할 수 있습니다.
|
|
28
|
+
* 주로 게시글의 태그, 검색 필터 조건, 수신자 목록 등을 입력받을 때 사용됩니다.
|
|
29
|
+
*
|
|
30
|
+
* ## When (언제 사용해야 하는가)
|
|
31
|
+
*
|
|
32
|
+
* **사용해야 하는 경우:**
|
|
33
|
+
* - **키워드 입력**: 게시글의 해시태그나 카테고리를 입력받을 때
|
|
34
|
+
* - **다중 항목 수집**: 여러 개의 이메일 주소나 사용자 아이디를 한꺼번에 입력받아야 할 때
|
|
35
|
+
* - **제한된 개수의 항목 선택**: 최대 N개까지의 조건을 입력받아야 할 때 (`maxTags` 활용)
|
|
36
|
+
*
|
|
37
|
+
* **사용하지 말아야 하는 경우:**
|
|
38
|
+
* - **단일 텍스트 입력**: 하나의 값만 필요한 경우 일반 `TextInput`을 사용하세요.
|
|
39
|
+
* - **미리 정의된 옵션**: 고정된 목록에서만 선택해야 한다면 `Dropdown`이나 `Combobox`가 더 적합합니다.
|
|
40
|
+
*
|
|
41
|
+
* ## Layout behavior
|
|
42
|
+
*
|
|
43
|
+
* - **Flow Layout**: 태그들은 가로 방향으로 나열되며, 공간이 부족하면 자동으로 다음 줄로 넘어갑니다 (`layout="row"`).
|
|
44
|
+
* - **Column Layout**: 태그들을 수직으로 쌓고 싶을 경우 `layout="column"` 설정을 사용할 수 있습니다.
|
|
45
|
+
* - **Constraint**: `maxTags`에 도달하면 추가 입력이 차단되며 시각적으로 안내됩니다.
|
|
46
|
+
*
|
|
47
|
+
* 레이블 배치는 `labelLayout` prop으로 제어됩니다:
|
|
48
|
+
* - **vertical** (기본값): Label이 태그 입력 필드 위에 세로로 배치됩니다.
|
|
49
|
+
* - **horizontal**: Label과 태그 입력 필드가 가로로 나란히 배치됩니다. `labelWidth`로 Label 너비를 조정할 수 있습니다 (기본값: 120px).
|
|
50
|
+
*
|
|
51
|
+
* ## Usage guidelines
|
|
52
|
+
*
|
|
53
|
+
* ### ✅ Do (권장 사항)
|
|
54
|
+
*
|
|
55
|
+
* - **유효성 검사 활용**: `validateTag`를 통해 이메일 형식 확인이나 글자 수 제한 등 비즈니스 로직을 적용하세요.
|
|
56
|
+
* - **중복 방지**: 동일한 태그가 입력되지 않도록 자동으로 처리되지만, 필요에 따라 사용자에게 알림을 줄 수 있습니다.
|
|
57
|
+
* - **가로 배치 활용**: 폼에서 여러 입력 필드를 정렬할 때는 `labelLayout="horizontal"`을 사용하여 일관된 레이아웃을 유지하세요.
|
|
58
|
+
*
|
|
59
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
60
|
+
*
|
|
61
|
+
* - **너무 긴 태그**: 태그 하나에 너무 긴 문장이 들어가는 것은 피하세요. 가급적 단어나 짧은 구문으로 제한하는 것이 좋습니다.
|
|
62
|
+
* - **복잡한 인터페이스**: 태그 내부에 너무 많은 기능을 넣지 마세요. 태그는 가볍고 빠르게 훑어볼 수 있어야 합니다.
|
|
63
|
+
*
|
|
64
|
+
* ## Accessibility
|
|
65
|
+
*
|
|
66
|
+
* - **Keyboard Management**: `Enter`로 추가, `Backspace`(구현 예정) 또는 'x' 버튼 클릭으로 삭제가 가능합니다.
|
|
67
|
+
* - **Screen Reader**: 추가된 각 태그의 내용과 삭제 버튼의 역할을 음성으로 안내합니다.
|
|
68
|
+
*
|
|
69
|
+
* ## Example
|
|
70
|
+
*
|
|
71
|
+
* {@tool snippet}
|
|
72
|
+
* 기본적인 태그 입력 예시:
|
|
73
|
+
*
|
|
74
|
+
* ```tsx
|
|
75
|
+
* <TagInput
|
|
76
|
+
* label="게시글 태그"
|
|
77
|
+
* maxTags={5}
|
|
78
|
+
* placeholder="태그 입력 후 Enter"
|
|
79
|
+
* value={['React', 'Next.js']}
|
|
80
|
+
* onChange={(tags) => console.log(tags)}
|
|
81
|
+
* />
|
|
82
|
+
* ```
|
|
83
|
+
* {@end-tool}
|
|
84
|
+
*
|
|
85
|
+
* {@tool snippet}
|
|
86
|
+
* 커스텀 유효성 검사가 포함된 태그 입력:
|
|
87
|
+
*
|
|
88
|
+
* ```tsx
|
|
89
|
+
* <TagInput
|
|
90
|
+
* validateTag={(tag) => {
|
|
91
|
+
* if (tag.length < 2) return "태그는 2글자 이상이어야 합니다.";
|
|
92
|
+
* return true;
|
|
93
|
+
* }}
|
|
94
|
+
* />
|
|
95
|
+
* ```
|
|
96
|
+
* {@end-tool}
|
|
97
|
+
*
|
|
98
|
+
* {@tool snippet}
|
|
99
|
+
* 가로 배치 레이아웃:
|
|
100
|
+
*
|
|
101
|
+
* ```tsx
|
|
102
|
+
* <TagInput
|
|
103
|
+
* label="키워드"
|
|
104
|
+
* required={true}
|
|
105
|
+
* labelLayout="horizontal"
|
|
106
|
+
* labelWidth="150px"
|
|
107
|
+
* placeholder="태그 입력 후 Enter"
|
|
108
|
+
* />
|
|
109
|
+
* ```
|
|
110
|
+
* {@end-tool}
|
|
111
|
+
*
|
|
112
|
+
* See also:
|
|
113
|
+
*
|
|
114
|
+
* - {@link TextInput}, 단순 텍스트 입력이 필요한 경우
|
|
115
|
+
* - {@link Combobox}, 목록에서 검색하여 태그를 추가하고 싶은 경우
|
|
116
|
+
*/
|
|
21
117
|
export declare const TagInput: React.ForwardRefExoticComponent<TagInputProps & React.RefAttributes<HTMLDivElement>>;
|
|
22
118
|
export {};
|
|
@@ -10,5 +10,62 @@ export interface TextProps extends React.HTMLAttributes<HTMLElement>, VariantPro
|
|
|
10
10
|
as?: "p" | "span" | "div" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "label";
|
|
11
11
|
children: React.ReactNode;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* 일관된 타이포그래피 시스템을 적용하기 위한 텍스트 컴포넌트입니다.
|
|
15
|
+
*
|
|
16
|
+
* {@link Text}는 제목(Heading), 본문(Body), 캡션(Caption) 등 미리 정의된 스타일을 제공하여
|
|
17
|
+
* 디자인 일관성을 유지하고 텍스트의 의미적 구조(Semantic Structure)를 쉽게 정의할 수 있게 합니다.
|
|
18
|
+
*
|
|
19
|
+
* ## When (언제 사용해야 하는가)
|
|
20
|
+
*
|
|
21
|
+
* **사용해야 하는 경우:**
|
|
22
|
+
* - **페이지 제목 및 부제목**: 화면의 위계를 나누는 타이틀을 작성할 때
|
|
23
|
+
* - **본문 콘텐츠**: 일반적인 설명글이나 데이터를 표시할 때
|
|
24
|
+
* - **캡션 및 힌트**: 부가적인 설명이나 작은 크기의 정보가 필요할 때
|
|
25
|
+
* - **정형화된 스타일**: 특정 폰트 두께나 크기를 시스템 규칙에 맞춰 적용해야 할 때
|
|
26
|
+
*
|
|
27
|
+
* **사용하지 말아야 하는 경우:**
|
|
28
|
+
* - **복잡한 스타일링**: 시스템 정의 범위를 크게 벗어나는 특수 스타일은 별도 CSS 클래스를 활용하세요.
|
|
29
|
+
*
|
|
30
|
+
* ## Layout behavior
|
|
31
|
+
*
|
|
32
|
+
* - **Semantic Tag**: `as` prop을 통해 실제 HTML 태그(`h1`, `p`, `span` 등)를 결정할 수 있어 SEO와 접근성에 유리합니다.
|
|
33
|
+
* - **Alignment**: `align` 속성을 통해 왼쪽, 중앙, 오른쪽 정렬을 손쉽게 조절할 수 있습니다.
|
|
34
|
+
*
|
|
35
|
+
* ## Usage guidelines
|
|
36
|
+
*
|
|
37
|
+
* ### ✅ Do (권장 사항)
|
|
38
|
+
*
|
|
39
|
+
* - **의미론적 태그 사용**: 제목에는 `as="h1"`, 본문에는 `as="p"`를 사용하는 등 맥락에 맞는 태그를 선택하세요.
|
|
40
|
+
* - **계층 구조 준수**: 큰 제목(h1) 아래에 작은 제목(h2, h3)이 오도록 논리적인 흐름을 유지하세요.
|
|
41
|
+
* - **변형(Variant) 활용**: 폰트 크기와 두께를 직접 조절하기보다 제공되는 `variant`를 우선적으로 사용하세요.
|
|
42
|
+
*
|
|
43
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
44
|
+
*
|
|
45
|
+
* - **과도한 폰트 크기 사용**: 가급적 시스템에서 정의한 크기를 벗어나지 않도록 주의하세요.
|
|
46
|
+
* - **의미와 맞지 않는 태그**: 시각적으로만 크게 보이기 위해 제목 태그를 남용하지 마세요.
|
|
47
|
+
*
|
|
48
|
+
* ## Example
|
|
49
|
+
*
|
|
50
|
+
* {@tool snippet}
|
|
51
|
+
* 다양한 위계의 텍스트 구성:
|
|
52
|
+
*
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <div className="space-y-4">
|
|
55
|
+
* <Text variant="h1" as="h1">대시보드</Text>
|
|
56
|
+
* <Text variant="subtitle">오늘의 요약 정보입니다.</Text>
|
|
57
|
+
* <Text variant="body">
|
|
58
|
+
* 현재 활성화된 사용자는 총 1,234명이며, 어제 대비 5% 증가했습니다.
|
|
59
|
+
* </Text>
|
|
60
|
+
* <Text variant="caption" align="right">최근 업데이트: 2024-01-24</Text>
|
|
61
|
+
* </div>
|
|
62
|
+
* ```
|
|
63
|
+
* {@end-tool}
|
|
64
|
+
*
|
|
65
|
+
* See also:
|
|
66
|
+
*
|
|
67
|
+
* - {@link TextInput}, 사용자의 입력을 받는 텍스트 필드
|
|
68
|
+
* - {@link Button}, 텍스트를 포함하는 액션 요소
|
|
69
|
+
*/
|
|
13
70
|
export declare const Text: React.ForwardRefExoticComponent<TextProps & React.RefAttributes<HTMLElement>>;
|
|
14
71
|
export {};
|
|
@@ -7,10 +7,88 @@ declare const textInputVariants: (props?: ({
|
|
|
7
7
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
8
8
|
export interface TextInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof textInputVariants> {
|
|
9
9
|
label?: string;
|
|
10
|
+
required?: boolean;
|
|
10
11
|
error?: boolean;
|
|
11
12
|
errorMessage?: string;
|
|
12
13
|
helperText?: string;
|
|
13
14
|
showCharCount?: boolean;
|
|
15
|
+
labelLayout?: "vertical" | "horizontal";
|
|
16
|
+
labelWidth?: string;
|
|
14
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* 사용자로부터 텍스트, 이메일, 숫자 등의 단일 라인 데이터를 입력받는 필드입니다.
|
|
20
|
+
*
|
|
21
|
+
* {@link TextInput}은 가장 기본적인 폼 입력 요소로, label, placeholder, 에러 메시지,
|
|
22
|
+
* helper 텍스트, 글자 수 카운터 등을 통합적으로 제공합니다. 일관된 스타일과 동작으로
|
|
23
|
+
* 사용자 경험을 향상시킵니다.
|
|
24
|
+
*
|
|
25
|
+
* ## When (언제 사용해야 하는가)
|
|
26
|
+
*
|
|
27
|
+
* **사용해야 하는 경우:**
|
|
28
|
+
* - **단일 라인 텍스트 입력**: 이름, 이메일, 전화번호 등 한 줄로 입력 가능한 정보
|
|
29
|
+
* - **특정 타입 입력**: email, password, number, date 등 HTML input type을 활용
|
|
30
|
+
* - **글자 수 제한**: 최대 글자 수를 지정하고 실시간으로 카운터를 표시
|
|
31
|
+
* - **유효성 검증**: 에러 상태와 메시지를 통해 사용자에게 피드백 제공
|
|
32
|
+
* - **필수 입력**: `required` 속성을 사용하여 반드시 입력해야 함을 시각적으로 안내
|
|
33
|
+
*
|
|
34
|
+
* ## Layout behavior
|
|
35
|
+
*
|
|
36
|
+
* TextInput은 기본적으로 부모 요소의 전체 너비(`fullWidth={true}`)를 차지합니다.
|
|
37
|
+
* `fullWidth={false}`로 설정하면 내용에 맞춰 자동으로 조절됩니다.
|
|
38
|
+
*
|
|
39
|
+
* 레이블 배치는 `labelLayout` prop으로 제어됩니다:
|
|
40
|
+
* - **vertical** (기본값): Label이 입력 필드 위에 세로로 배치됩니다.
|
|
41
|
+
* - **horizontal**: Label과 입력 필드가 가로로 나란히 배치됩니다. `labelWidth`로 Label 너비를 조정할 수 있습니다 (기본값: 120px).
|
|
42
|
+
*
|
|
43
|
+
* 구조는 다음 순서로 배치됩니다:
|
|
44
|
+
* 1. **헤더 영역** (있는 경우): label (좌측, 필수 시 * 표시) + 글자 수 카운터 (우측)
|
|
45
|
+
* 2. **입력 필드**: 텍스트 입력 영역
|
|
46
|
+
* 3. **메시지 영역** (있는 경우): errorMessage 또는 helperText
|
|
47
|
+
*
|
|
48
|
+
* 높이는 `h-10` (2.5rem / 40px)로 고정되어 일관된 버튼 높이와 정렬됩니다.
|
|
49
|
+
*
|
|
50
|
+
* ## Usage guidelines
|
|
51
|
+
*
|
|
52
|
+
* ### ✅ Do (권장 사항)
|
|
53
|
+
*
|
|
54
|
+
* - **명확한 label 제공**: 무엇을 입력해야 하는지 명확하게 표시하세요
|
|
55
|
+
* - **필수 여부 명시**: 반드시 입력해야 하는 필드라면 `required` 속성을 활성화하세요.
|
|
56
|
+
* - **가로 배치 활용**: 폼에서 여러 입력 필드를 정렬할 때는 `labelLayout="horizontal"`을 사용하여 일관된 레이아웃을 유지하세요.
|
|
57
|
+
*
|
|
58
|
+
* ## Example
|
|
59
|
+
*
|
|
60
|
+
* {@tool snippet}
|
|
61
|
+
* 레이블과 필수 표시가 포함된 입력 필드:
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <TextInput
|
|
65
|
+
* label="사용자 아이디"
|
|
66
|
+
* required={true}
|
|
67
|
+
* placeholder="아이디를 입력하세요"
|
|
68
|
+
* />
|
|
69
|
+
* ```
|
|
70
|
+
* {@end-tool}
|
|
71
|
+
*
|
|
72
|
+
* {@tool snippet}
|
|
73
|
+
* 가로 배치 레이아웃:
|
|
74
|
+
*
|
|
75
|
+
* ```tsx
|
|
76
|
+
* <TextInput
|
|
77
|
+
* label="상호(법인명)"
|
|
78
|
+
* required={true}
|
|
79
|
+
* labelLayout="horizontal"
|
|
80
|
+
* labelWidth="150px"
|
|
81
|
+
* placeholder="회사명을 입력하세요"
|
|
82
|
+
* />
|
|
83
|
+
* ```
|
|
84
|
+
* {@end-tool}
|
|
85
|
+
*
|
|
86
|
+
* See also:
|
|
87
|
+
*
|
|
88
|
+
* - {@link Textarea}, 여러 줄 텍스트 입력을 위한 컴포넌트
|
|
89
|
+
* - {@link TagInput}, 여러 태그를 입력받는 컴포넌트
|
|
90
|
+
* - {@link DatePicker}, 날짜 선택을 위한 컴포넌트
|
|
91
|
+
* - {@link Dropdown}, 옵션 선택을 위한 컴포넌트
|
|
92
|
+
*/
|
|
15
93
|
export declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
16
94
|
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface TimePickerProps {
|
|
4
|
+
value?: string;
|
|
5
|
+
onChange?: (time: string) => void;
|
|
6
|
+
label?: string;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
format?: "24h" | "12h";
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
error?: boolean;
|
|
11
|
+
errorMessage?: string;
|
|
12
|
+
helperText?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
minuteStep?: number;
|
|
15
|
+
showIcon?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 사용자가 시간을 선택할 수 있게 하는 컴포넌트입니다.
|
|
19
|
+
*
|
|
20
|
+
* {@link TimePicker}는 직관적인 스크롤 인터페이스를 통해 시간과 분을 선택할 수 있으며,
|
|
21
|
+
* 24시간 형식과 12시간(AM/PM) 형식을 모두 지원합니다.
|
|
22
|
+
*
|
|
23
|
+
* `@radix-ui/react-popover`를 기반으로 구현되었으며, 사용자가 잘못된 시간을 입력하는 것을 방지합니다.
|
|
24
|
+
*
|
|
25
|
+
* ## When (언제 사용해야 하는가)
|
|
26
|
+
*
|
|
27
|
+
* **사용해야 하는 경우:**
|
|
28
|
+
* - **특정 시간 입력**: 예약 시간, 알림 시간, 업무 시작/종료 시간 등을 선택해야 할 때
|
|
29
|
+
* - **정확한 시간 선택**: 사용자가 직접 타이핑하여 발생할 수 있는 형식 오류를 방지하고 싶을 때
|
|
30
|
+
* - **제한된 간격 선택**: 5분, 10분, 15분 단위로만 선택이 필요한 경우 (minuteStep 사용)
|
|
31
|
+
*
|
|
32
|
+
* **사용하지 말아야 하는 경우:**
|
|
33
|
+
* - **대략적인 시간**: '오전', '오후', '저녁' 등 상대적인 시간 선택이 필요한 경우 `Dropdown`이나 `RadioButton`이 더 적절할 수 있습니다.
|
|
34
|
+
* - **초 단위 정밀도**: 현재는 시간과 분까지만 지원하므로, 초 단위가 필요한 경우 다른 컴포넌트를 고려하세요.
|
|
35
|
+
*
|
|
36
|
+
* ## Layout behavior
|
|
37
|
+
*
|
|
38
|
+
* - **Popover 기반**: 클릭 시 입력창 아래에 시간 선택 팝오버가 나타나며, 화면 공간을 효율적으로 사용합니다.
|
|
39
|
+
* - **스크롤 선택**: 시간과 분을 각각 스크롤하여 선택할 수 있어 직관적입니다.
|
|
40
|
+
* - **Full Width**: `className`을 통해 부모 요소의 너비에 맞게 조절할 수 있습니다.
|
|
41
|
+
*
|
|
42
|
+
* ## Usage guidelines
|
|
43
|
+
*
|
|
44
|
+
* ### ✅ Do (권장 사항)
|
|
45
|
+
*
|
|
46
|
+
* - **적절한 Format**: 사용자가 익숙한 형식(한국: 24시간, 미국: 12시간)을 선택하세요.
|
|
47
|
+
* - **Minute Step 활용**: 일정 간격으로만 선택이 필요한 경우 `minuteStep`을 활용하여 UX를 개선하세요.
|
|
48
|
+
* - **레이블 및 헬퍼 텍스트**: 입력 항목의 용도를 명확히 하고, 필요한 경우 보충 설명을 제공하세요.
|
|
49
|
+
*
|
|
50
|
+
* ### 🚫 Don't (주의/금지 사항)
|
|
51
|
+
*
|
|
52
|
+
* - **Read-only 입력**: 사용자가 직접 타이핑하여 입력하는 기능은 지원하지 않으므로, 반드시 UI를 통해 선택하도록 안내하세요.
|
|
53
|
+
* - **과도한 Step**: `minuteStep`이 너무 크면 정확한 시간을 선택하기 어려울 수 있습니다.
|
|
54
|
+
*
|
|
55
|
+
* ## Accessibility
|
|
56
|
+
*
|
|
57
|
+
* - **Keyboard Navigation**: 팝오버는 키보드로 열고 닫을 수 있으며, `Esc` 키로 취소할 수 있습니다.
|
|
58
|
+
* - **Focus Management**: 시간 선택기가 열려 있는 동안 포커스가 적절히 관리됩니다.
|
|
59
|
+
* - **Aria Labels**: 각 버튼에는 스크린 리더를 위한 적절한 레이블이 포함되어 있습니다.
|
|
60
|
+
*
|
|
61
|
+
* ## Example
|
|
62
|
+
*
|
|
63
|
+
* {@tool snippet}
|
|
64
|
+
* 기본적인 시간 선택 예시 (24시간 형식):
|
|
65
|
+
*
|
|
66
|
+
* ```tsx
|
|
67
|
+
* const [time, setTime] = useState("");
|
|
68
|
+
*
|
|
69
|
+
* <TimePicker
|
|
70
|
+
* label="출근 시간"
|
|
71
|
+
* value={time}
|
|
72
|
+
* onChange={setTime}
|
|
73
|
+
* placeholder="시간을 선택하세요"
|
|
74
|
+
* />
|
|
75
|
+
* ```
|
|
76
|
+
* {@end-tool}
|
|
77
|
+
*
|
|
78
|
+
* {@tool snippet}
|
|
79
|
+
* 12시간 형식 및 15분 단위 선택:
|
|
80
|
+
*
|
|
81
|
+
* ```tsx
|
|
82
|
+
* <TimePicker
|
|
83
|
+
* label="회의 시간"
|
|
84
|
+
* format="12h"
|
|
85
|
+
* minuteStep={15}
|
|
86
|
+
* value="2:30 PM"
|
|
87
|
+
* onChange={setTime}
|
|
88
|
+
* />
|
|
89
|
+
* ```
|
|
90
|
+
* {@end-tool}
|
|
91
|
+
*
|
|
92
|
+
* {@tool snippet}
|
|
93
|
+
* 에러 상태:
|
|
94
|
+
*
|
|
95
|
+
* ```tsx
|
|
96
|
+
* <TimePicker
|
|
97
|
+
* label="마감 시간"
|
|
98
|
+
* error={true}
|
|
99
|
+
* errorMessage="업무 시간(9:00-18:00) 내에서 선택해주세요"
|
|
100
|
+
* />
|
|
101
|
+
* ```
|
|
102
|
+
* {@end-tool}
|
|
103
|
+
*
|
|
104
|
+
* See also:
|
|
105
|
+
*
|
|
106
|
+
* - {@link DatePicker}, 날짜를 선택해야 하는 경우
|
|
107
|
+
* - {@link DateRangePicker}, 날짜 범위를 선택해야 하는 경우
|
|
108
|
+
* - {@link TextInput}, 단순한 텍스트 입력이 필요한 경우
|
|
109
|
+
*/
|
|
110
|
+
export declare const TimePicker: React.ForwardRefExoticComponent<TimePickerProps & React.RefAttributes<HTMLDivElement>>;
|