@simplysm/solid 13.0.85 → 13.0.86

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.
Files changed (116) hide show
  1. package/README.md +143 -28
  2. package/dist/components/data/list/ListItem.d.ts.map +1 -1
  3. package/dist/components/data/list/ListItem.js +11 -4
  4. package/dist/components/data/list/ListItem.js.map +2 -2
  5. package/dist/components/data/list/ListItem.styles.d.ts +2 -0
  6. package/dist/components/data/list/ListItem.styles.d.ts.map +1 -1
  7. package/dist/components/data/list/ListItem.styles.js +11 -1
  8. package/dist/components/data/list/ListItem.styles.js.map +1 -1
  9. package/dist/components/features/crud-sheet/CrudSheet.d.ts.map +1 -1
  10. package/dist/components/features/crud-sheet/CrudSheet.js +7 -0
  11. package/dist/components/features/crud-sheet/CrudSheet.js.map +2 -2
  12. package/dist/components/features/data-select-button/DataSelectButton.d.ts.map +1 -1
  13. package/dist/components/features/data-select-button/DataSelectButton.js +30 -26
  14. package/dist/components/features/data-select-button/DataSelectButton.js.map +2 -2
  15. package/dist/components/features/permission-table/PermissionTable.js +5 -1
  16. package/dist/components/features/permission-table/PermissionTable.js.map +2 -2
  17. package/dist/components/form-control/DropdownTrigger.styles.js +1 -1
  18. package/dist/components/form-control/combobox/Combobox.d.ts +19 -5
  19. package/dist/components/form-control/combobox/Combobox.d.ts.map +1 -1
  20. package/dist/components/form-control/combobox/Combobox.js +2 -4
  21. package/dist/components/form-control/combobox/Combobox.js.map +1 -1
  22. package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts +2 -2
  23. package/dist/components/form-control/date-range-picker/DateRangePicker.d.ts.map +1 -1
  24. package/dist/components/form-control/date-range-picker/DateRangePicker.js +10 -1
  25. package/dist/components/form-control/date-range-picker/DateRangePicker.js.map +2 -2
  26. package/dist/components/form-control/editor/RichTextEditor.d.ts +2 -2
  27. package/dist/components/form-control/editor/RichTextEditor.d.ts.map +1 -1
  28. package/dist/components/form-control/editor/RichTextEditor.js +2 -2
  29. package/dist/components/form-control/editor/RichTextEditor.js.map +1 -1
  30. package/dist/components/form-control/field/DatePicker.d.ts +2 -2
  31. package/dist/components/form-control/field/DatePicker.d.ts.map +1 -1
  32. package/dist/components/form-control/field/DatePicker.js.map +1 -1
  33. package/dist/components/form-control/field/DateTimePicker.d.ts +2 -2
  34. package/dist/components/form-control/field/DateTimePicker.d.ts.map +1 -1
  35. package/dist/components/form-control/field/DateTimePicker.js.map +1 -1
  36. package/dist/components/form-control/field/Field.styles.d.ts +6 -7
  37. package/dist/components/form-control/field/Field.styles.d.ts.map +1 -1
  38. package/dist/components/form-control/field/Field.styles.js.map +1 -1
  39. package/dist/components/form-control/field/NumberInput.d.ts +2 -2
  40. package/dist/components/form-control/field/NumberInput.d.ts.map +1 -1
  41. package/dist/components/form-control/field/NumberInput.js.map +1 -1
  42. package/dist/components/form-control/field/TextInput.d.ts +2 -2
  43. package/dist/components/form-control/field/TextInput.d.ts.map +1 -1
  44. package/dist/components/form-control/field/TextInput.js.map +1 -1
  45. package/dist/components/form-control/field/Textarea.d.ts +2 -2
  46. package/dist/components/form-control/field/Textarea.d.ts.map +1 -1
  47. package/dist/components/form-control/field/Textarea.js.map +1 -1
  48. package/dist/components/form-control/field/TimePicker.d.ts +2 -2
  49. package/dist/components/form-control/field/TimePicker.d.ts.map +1 -1
  50. package/dist/components/form-control/field/TimePicker.js.map +1 -1
  51. package/dist/components/form-control/numpad/Numpad.d.ts.map +1 -1
  52. package/dist/components/form-control/numpad/Numpad.js +4 -17
  53. package/dist/components/form-control/numpad/Numpad.js.map +2 -2
  54. package/dist/components/form-control/select/Select.d.ts.map +1 -1
  55. package/dist/components/form-control/select/Select.js +19 -6
  56. package/dist/components/form-control/select/Select.js.map +2 -2
  57. package/dist/components/form-control/state-preset/StatePreset.d.ts +1 -3
  58. package/dist/components/form-control/state-preset/StatePreset.d.ts.map +1 -1
  59. package/dist/components/form-control/state-preset/StatePreset.js +69 -91
  60. package/dist/components/form-control/state-preset/StatePreset.js.map +2 -2
  61. package/dist/components/layout/FormGroup.js +1 -1
  62. package/dist/components/layout/FormGroup.js.map +1 -1
  63. package/dist/components/layout/FormTable.js +3 -3
  64. package/dist/components/layout/FormTable.js.map +1 -1
  65. package/dist/components/layout/sidebar/Sidebar.d.ts.map +1 -1
  66. package/dist/components/layout/sidebar/Sidebar.js +3 -6
  67. package/dist/components/layout/sidebar/Sidebar.js.map +2 -2
  68. package/dist/providers/i18n/locales/en.d.ts +2 -3
  69. package/dist/providers/i18n/locales/en.d.ts.map +1 -1
  70. package/dist/providers/i18n/locales/en.js +3 -4
  71. package/dist/providers/i18n/locales/en.js.map +1 -1
  72. package/dist/providers/i18n/locales/ko.d.ts +2 -3
  73. package/dist/providers/i18n/locales/ko.d.ts.map +1 -1
  74. package/dist/providers/i18n/locales/ko.js +3 -4
  75. package/dist/providers/i18n/locales/ko.js.map +1 -1
  76. package/docs/display-feedback.md +279 -0
  77. package/docs/features.md +357 -213
  78. package/docs/form-controls.md +261 -403
  79. package/docs/layout-data.md +386 -0
  80. package/docs/providers-hooks.md +411 -0
  81. package/package.json +5 -5
  82. package/src/components/data/list/ListItem.styles.ts +14 -2
  83. package/src/components/data/list/ListItem.tsx +13 -4
  84. package/src/components/features/crud-sheet/CrudSheet.tsx +8 -0
  85. package/src/components/features/data-select-button/DataSelectButton.tsx +39 -32
  86. package/src/components/features/permission-table/PermissionTable.tsx +1 -1
  87. package/src/components/form-control/DropdownTrigger.styles.ts +1 -1
  88. package/src/components/form-control/combobox/Combobox.tsx +42 -16
  89. package/src/components/form-control/date-range-picker/DateRangePicker.tsx +6 -4
  90. package/src/components/form-control/editor/RichTextEditor.tsx +5 -6
  91. package/src/components/form-control/field/DatePicker.tsx +3 -2
  92. package/src/components/form-control/field/DateTimePicker.tsx +3 -2
  93. package/src/components/form-control/field/Field.styles.ts +6 -8
  94. package/src/components/form-control/field/NumberInput.tsx +3 -2
  95. package/src/components/form-control/field/TextInput.tsx +3 -2
  96. package/src/components/form-control/field/Textarea.tsx +3 -2
  97. package/src/components/form-control/field/TimePicker.tsx +3 -2
  98. package/src/components/form-control/numpad/Numpad.tsx +16 -18
  99. package/src/components/form-control/select/Select.tsx +19 -5
  100. package/src/components/form-control/state-preset/StatePreset.tsx +32 -57
  101. package/src/components/layout/FormGroup.tsx +1 -1
  102. package/src/components/layout/FormTable.tsx +3 -3
  103. package/src/components/layout/sidebar/Sidebar.tsx +2 -3
  104. package/src/providers/i18n/locales/en.ts +2 -3
  105. package/src/providers/i18n/locales/ko.ts +2 -3
  106. package/tests/components/features/data-select-button/DataSelectButton.spec.tsx +62 -7
  107. package/tests/components/form-control/combobox/Combobox.spec.tsx +3 -3
  108. package/tests/components/form-control/date-range-picker/DateRangePicker.spec.tsx +56 -0
  109. package/docs/data.md +0 -204
  110. package/docs/disclosure.md +0 -146
  111. package/docs/display.md +0 -125
  112. package/docs/feedback.md +0 -156
  113. package/docs/helpers.md +0 -173
  114. package/docs/hooks.md +0 -146
  115. package/docs/layout.md +0 -94
  116. package/docs/providers.md +0 -180
@@ -1,520 +1,378 @@
1
- # Form Controls
1
+ # 컨트롤
2
2
 
3
3
  ## Button
4
4
 
5
- ```typescript
6
- interface ButtonProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
7
- theme?: "base" | "primary" | "info" | "success" | "warning" | "danger";
8
- variant?: "solid" | "outline" | "ghost";
9
- size?: "xs" | "sm" | "md" | "lg" | "xl";
10
- inset?: boolean;
11
- }
5
+ ```tsx
6
+ import { Button } from "@simplysm/solid";
7
+
8
+ <Button theme="primary" variant="solid" size="md" onClick={handleClick}>
9
+ 저장
10
+ </Button>
11
+ <Button variant="outline" disabled>취소</Button>
12
+ <Button variant="ghost" inset>아이콘 버튼</Button>
12
13
  ```
13
14
 
14
- Standard button with semantic themes and visual variants.
15
+ | Prop | 타입 | 기본값 | 설명 |
16
+ |------|------|--------|------|
17
+ | `theme` | `"base" \| "primary" \| "success" \| "warning" \| "danger"` | `"base"` | 색상 테마 |
18
+ | `variant` | `"solid" \| "outline" \| "ghost"` | `"solid"` | 스타일 변형 |
19
+ | `size` | `ComponentSize` | `"md"` | 크기 |
20
+ | `inset` | `boolean` | `false` | 테두리 없음 |
21
+ | `disabled` | `boolean` | `false` | 비활성화 |
15
22
 
16
23
  ---
17
24
 
18
25
  ## TextInput
19
26
 
20
- ```typescript
21
- interface TextInputProps {
22
- value?: string;
23
- onValueChange?: (value: string) => void;
24
- type?: "text" | "password" | "email";
25
- placeholder?: string;
26
- title?: string;
27
- autocomplete?: JSX.HTMLAutocomplete;
28
- disabled?: boolean;
29
- readOnly?: boolean;
30
- size?: "xs" | "sm" | "md" | "lg" | "xl";
31
- inset?: boolean;
32
- format?: string;
33
- required?: boolean;
34
- minLength?: number;
35
- maxLength?: number;
36
- pattern?: string | RegExp;
37
- validate?: (value: string) => string | undefined;
38
- lazyValidation?: boolean;
39
- class?: string;
40
- style?: JSX.CSSProperties;
41
- children?: JSX.Element;
42
- }
43
- ```
27
+ ```tsx
28
+ import { TextInput } from "@simplysm/solid";
44
29
 
45
- Single-line text input with format masking and validation. Use `format` for input masks (e.g. `"XXX-XXXX-XXXX"`).
30
+ <TextInput value={name()} onValueChange={setName} placeholder="이름 입력" />
46
31
 
47
- **Sub-component:** `TextInput.Prefix` -- renders content before the input.
32
+ // 접두사 슬롯
33
+ <TextInput value={phone()} onValueChange={setPhone} format="XXX-XXXX-XXXX">
34
+ <TextInput.Prefix>+82</TextInput.Prefix>
35
+ </TextInput>
36
+ ```
37
+
38
+ | Prop | 타입 | 설명 |
39
+ |------|------|------|
40
+ | `value` | `string` | 값 |
41
+ | `onValueChange` | `(v: string) => void` | 변경 콜백 |
42
+ | `type` | `"text" \| "password" \| "email"` | 입력 타입 |
43
+ | `format` | `string` | 형식 마스크 (예: `"XXX-XXXX-XXXX"`) |
44
+ | `size`, `inset`, `disabled`, `readOnly`, `required` | | 공통 |
45
+ | `minLength`, `maxLength`, `pattern` | | 유효성 |
46
+ | `validate` | `(v: string) => string \| undefined` | 커스텀 유효성 |
47
+ | `lazyValidation` | `boolean` | blur 시 검증 |
48
48
 
49
49
  ---
50
50
 
51
51
  ## NumberInput
52
52
 
53
- ```typescript
54
- interface NumberInputProps {
55
- value?: number;
56
- onValueChange?: (value: number | undefined) => void;
57
- useGrouping?: boolean;
58
- minimumFractionDigits?: number;
59
- placeholder?: string;
60
- title?: string;
61
- disabled?: boolean;
62
- readOnly?: boolean;
63
- size?: "xs" | "sm" | "md" | "lg" | "xl";
64
- inset?: boolean;
65
- required?: boolean;
66
- min?: number;
67
- max?: number;
68
- validate?: (value: number | undefined) => string | undefined;
69
- lazyValidation?: boolean;
70
- class?: string;
71
- style?: JSX.CSSProperties;
72
- children?: JSX.Element;
73
- }
53
+ ```tsx
54
+ <NumberInput value={amount()} onValueChange={setAmount} min={0} max={100} />
55
+ <NumberInput value={price()} onValueChange={setPrice} useGrouping minimumFractionDigits={2} />
74
56
  ```
75
57
 
76
- Numeric input with locale-aware formatting. `useGrouping` enables thousands separators (default: `true`).
77
-
78
- **Sub-component:** `NumberInput.Prefix` -- renders content before the input.
58
+ | Prop | 타입 | 설명 |
59
+ |------|------|------|
60
+ | `value` | `number` | |
61
+ | `onValueChange` | `(v: number \| undefined) => void` | 변경 콜백 |
62
+ | `useGrouping` | `boolean` | 천단위 구분자 (기본: true) |
63
+ | `minimumFractionDigits` | `number` | 최소 소수점 자릿수 |
64
+ | `min`, `max` | `number` | 범위 |
79
65
 
80
66
  ---
81
67
 
82
- ## DatePicker
68
+ ## Textarea
83
69
 
84
- ```typescript
85
- interface DatePickerProps {
86
- value?: DateOnly;
87
- onValueChange?: (value: DateOnly | undefined) => void;
88
- unit?: "year" | "month" | "date";
89
- min?: DateOnly;
90
- max?: DateOnly;
91
- title?: string;
92
- disabled?: boolean;
93
- readOnly?: boolean;
94
- size?: "xs" | "sm" | "md" | "lg" | "xl";
95
- inset?: boolean;
96
- required?: boolean;
97
- validate?: (value: DateOnly | undefined) => string | undefined;
98
- lazyValidation?: boolean;
99
- class?: string;
100
- style?: JSX.CSSProperties;
101
- }
70
+ ```tsx
71
+ <Textarea value={memo()} onValueChange={setMemo} minRows={3} />
102
72
  ```
103
73
 
104
- Date picker with calendar dropdown. `unit` controls the selection granularity (default: `"date"`).
74
+ Alt+Enter로 줄바꿈. `minRows`로 최소 높이 설정.
105
75
 
106
76
  ---
107
77
 
108
- ## DateTimePicker
78
+ ## DatePicker
109
79
 
110
- ```typescript
111
- interface DateTimePickerProps {
112
- value?: DateTime;
113
- onValueChange?: (value: DateTime | undefined) => void;
114
- unit?: "minute" | "second";
115
- min?: DateTime;
116
- max?: DateTime;
117
- title?: string;
118
- disabled?: boolean;
119
- readOnly?: boolean;
120
- size?: "xs" | "sm" | "md" | "lg" | "xl";
121
- inset?: boolean;
122
- required?: boolean;
123
- validate?: (value: DateTime | undefined) => string | undefined;
124
- lazyValidation?: boolean;
125
- class?: string;
126
- style?: JSX.CSSProperties;
127
- }
80
+ ```tsx
81
+ import { DatePicker } from "@simplysm/solid";
82
+
83
+ <DatePicker value={date()} onValueChange={setDate} />
84
+ <DatePicker value={month()} onValueChange={setMonth} unit="month" />
85
+ <DatePicker value={year()} onValueChange={setYear} unit="year" />
128
86
  ```
129
87
 
130
- Combined date and time picker. `unit` controls time precision (default: `"minute"`).
88
+ | Prop | 타입 | 설명 |
89
+ |------|------|------|
90
+ | `value` | `DateOnly` | 값 |
91
+ | `onValueChange` | `(v: DateOnly \| undefined) => void` | 변경 콜백 |
92
+ | `unit` | `"year" \| "month" \| "date"` | 선택 단위 (기본: `"date"`) |
93
+ | `min`, `max` | `DateOnly` | 범위 제한 |
131
94
 
132
95
  ---
133
96
 
134
- ## TimePicker
97
+ ## DateTimePicker
135
98
 
136
- ```typescript
137
- interface TimePickerProps {
138
- value?: Time;
139
- onValueChange?: (value: Time | undefined) => void;
140
- unit?: "minute" | "second";
141
- title?: string;
142
- disabled?: boolean;
143
- readOnly?: boolean;
144
- size?: "xs" | "sm" | "md" | "lg" | "xl";
145
- inset?: boolean;
146
- min?: Time;
147
- max?: Time;
148
- required?: boolean;
149
- validate?: (value: Time | undefined) => string | undefined;
150
- lazyValidation?: boolean;
151
- class?: string;
152
- style?: JSX.CSSProperties;
153
- }
99
+ ```tsx
100
+ <DateTimePicker value={dt()} onValueChange={setDt} unit="minute" />
154
101
  ```
155
102
 
156
- Time-only picker. `unit` controls precision (default: `"minute"`).
103
+ `unit`: `"minute"` (기본) 또는 `"second"`
157
104
 
158
105
  ---
159
106
 
160
- ## Textarea
107
+ ## TimePicker
161
108
 
162
- ```typescript
163
- interface TextareaProps {
164
- value?: string;
165
- onValueChange?: (value: string) => void;
166
- placeholder?: string;
167
- title?: string;
168
- disabled?: boolean;
169
- readOnly?: boolean;
170
- size?: "xs" | "sm" | "md" | "lg" | "xl";
171
- inset?: boolean;
172
- minRows?: number;
173
- required?: boolean;
174
- minLength?: number;
175
- maxLength?: number;
176
- validate?: (value: string) => string | undefined;
177
- lazyValidation?: boolean;
178
- class?: string;
179
- style?: JSX.CSSProperties;
180
- }
109
+ ```tsx
110
+ <TimePicker value={time()} onValueChange={setTime} />
181
111
  ```
182
112
 
183
- Auto-resizing multi-line text input. `minRows` sets the minimum height (default: `1`).
184
-
185
113
  ---
186
114
 
187
- ## Select
115
+ ## DateRangePicker
188
116
 
189
- ```typescript
190
- // Single select
191
- interface SelectSingleProps<TValue> {
192
- multiple?: false;
193
- value?: TValue;
194
- onValueChange?: (value: TValue | undefined) => void;
195
- }
196
-
197
- // Multiple select
198
- interface SelectMultipleProps<TValue> {
199
- multiple: true;
200
- value?: TValue[];
201
- onValueChange?: (value: TValue[]) => void;
202
- tagDirection?: "horizontal" | "vertical";
203
- hideSelectAll?: boolean;
204
- }
205
-
206
- // Common props
207
- interface SelectCommonProps<TValue> {
208
- items?: TValue[];
209
- itemChildren?: (item: TValue, index: number, depth: number) => TValue[] | undefined;
210
- renderValue?: (value: TValue) => JSX.Element;
211
- itemSearchText?: (item: TValue) => string;
212
- isItemHidden?: (item: TValue) => boolean;
213
- disabled?: boolean;
214
- required?: boolean;
215
- placeholder?: string;
216
- size?: "xs" | "sm" | "md" | "lg" | "xl";
217
- inset?: boolean;
218
- validate?: (value: unknown) => string | undefined;
219
- lazyValidation?: boolean;
220
- class?: string;
221
- style?: JSX.CSSProperties;
222
- children?: JSX.Element;
223
- }
224
- ```
117
+ 기간 유형(일/월/범위) 선택과 시작/종료일 입력을 조합한 날짜 범위 선택기.
225
118
 
226
- Dropdown select with single or multiple selection modes. Supports two usage patterns:
119
+ ```tsx
120
+ import { DateRangePicker, type DateRangePeriodType } from "@simplysm/solid";
227
121
 
228
- 1. **Items mode:** pass `items` array for automatic rendering.
229
- 2. **Children mode:** use `Select.Item` sub-components for declarative composition.
122
+ const [periodType, setPeriodType] = createSignal<DateRangePeriodType>("range");
123
+ const [from, setFrom] = createSignal<DateOnly>();
124
+ const [to, setTo] = createSignal<DateOnly>();
230
125
 
231
- Providing `itemSearchText` enables a built-in search input. `itemChildren` enables tree-structured items.
126
+ <DateRangePicker
127
+ periodType={periodType()}
128
+ onPeriodTypeChange={setPeriodType}
129
+ from={from()}
130
+ onFromChange={setFrom}
131
+ to={to()}
132
+ onToChange={setTo}
133
+ required
134
+ />
135
+ ```
136
+
137
+ | Prop | 타입 | 설명 |
138
+ |------|------|------|
139
+ | `periodType` | `"day" \| "month" \| "range"` | 기간 유형 |
140
+ | `onPeriodTypeChange` | `(v: DateRangePeriodType) => void` | 기간 유형 변경 콜백 |
141
+ | `from` | `DateOnly` | 시작일 |
142
+ | `onFromChange` | `(v: DateOnly \| undefined) => void` | 시작일 변경 콜백 |
143
+ | `to` | `DateOnly` | 종료일 |
144
+ | `onToChange` | `(v: DateOnly \| undefined) => void` | 종료일 변경 콜백 |
232
145
 
233
- **Sub-components:**
234
- - `Select.Item` -- selectable option (`{ value: TValue; disabled?: boolean }`)
235
- - `Select.Header` -- non-selectable header row
236
- - `Select.Action` -- non-selectable action row
237
- - `Select.ItemTemplate` -- custom item renderer
146
+ 기간 유형에 따라 자동으로 from/to를 조정한다:
147
+ - `"day"`: from = to (동일)
148
+ - `"month"`: from = 첫째 날, to = 월 마지막 날
149
+ - `"range"`: 시작일/종료일 독립 선택
238
150
 
239
151
  ---
240
152
 
241
- ## Combobox
153
+ ## Checkbox / Radio
242
154
 
243
- ```typescript
244
- interface ComboboxProps<TValue> {
245
- value?: TValue;
246
- onValueChange?: (value: TValue) => void;
247
- loadItems: (query: string) => TValue[] | Promise<TValue[]>;
248
- renderValue: (value: TValue) => JSX.Element;
249
- debounceMs?: number;
250
- allowsCustomValue?: boolean;
251
- parseCustomValue?: (text: string) => TValue;
252
- disabled?: boolean;
253
- required?: boolean;
254
- validate?: (value: TValue | undefined) => string | undefined;
255
- lazyValidation?: boolean;
256
- placeholder?: string;
257
- size?: "xs" | "sm" | "md" | "lg" | "xl";
258
- inset?: boolean;
259
- class?: string;
260
- style?: JSX.CSSProperties;
261
- children?: JSX.Element;
262
- }
263
- ```
155
+ ```tsx
156
+ <Checkbox checked={active()} onCheckedChange={setActive}>활성</Checkbox>
157
+ <Checkbox checked={agreed()} onCheckedChange={setAgreed} required>약관 동의</Checkbox>
264
158
 
265
- Autocomplete input with async item loading. `loadItems` is called with the current query string. `debounceMs` controls the debounce delay (default: `300`). Set `allowsCustomValue` to accept free-form input not in the results list.
266
-
267
- **Sub-components:** `Combobox.Item`, `Combobox.ItemTemplate`
159
+ <RadioGroup value={role()} onValueChange={setRole}>
160
+ <Radio value="admin">관리자</Radio>
161
+ <Radio value="user">사용자</Radio>
162
+ </RadioGroup>
163
+ ```
268
164
 
269
165
  ---
270
166
 
271
- ## Checkbox
272
-
273
- ```typescript
274
- interface CheckboxProps {
275
- checked?: boolean;
276
- onCheckedChange?: (checked: boolean) => void;
277
- disabled?: boolean;
278
- size?: "xs" | "sm" | "md" | "lg" | "xl";
279
- inset?: boolean;
280
- inline?: boolean;
281
- required?: boolean;
282
- validate?: (checked: boolean) => string | undefined;
283
- lazyValidation?: boolean;
284
- class?: string;
285
- style?: JSX.CSSProperties;
286
- children?: JSX.Element;
287
- }
288
- ```
167
+ ## CheckboxGroup
289
168
 
290
- Standard checkbox with label support via `children`.
169
+ 다중 선택을 위한 체크박스 그룹.
291
170
 
292
- ---
171
+ ```tsx
172
+ import { CheckboxGroup } from "@simplysm/solid";
173
+
174
+ const [selectedTags, setSelectedTags] = createSignal<string[]>([]);
293
175
 
294
- ## Radio
295
-
296
- ```typescript
297
- interface RadioProps {
298
- checked?: boolean;
299
- onCheckedChange?: (checked: boolean) => void;
300
- disabled?: boolean;
301
- size?: "xs" | "sm" | "md" | "lg" | "xl";
302
- inset?: boolean;
303
- inline?: boolean;
304
- required?: boolean;
305
- validate?: (checked: boolean) => string | undefined;
306
- lazyValidation?: boolean;
307
- class?: string;
308
- style?: JSX.CSSProperties;
309
- children?: JSX.Element;
310
- }
176
+ <CheckboxGroup value={selectedTags()} onValueChange={setSelectedTags} required>
177
+ <CheckboxGroup.Item value="frontend">프론트엔드</CheckboxGroup.Item>
178
+ <CheckboxGroup.Item value="backend">백엔드</CheckboxGroup.Item>
179
+ <CheckboxGroup.Item value="devops">DevOps</CheckboxGroup.Item>
180
+ </CheckboxGroup>
311
181
  ```
312
182
 
313
- Standalone radio button. For grouped radio buttons, use `RadioGroup`.
183
+ | Prop | 타입 | 설명 |
184
+ |------|------|------|
185
+ | `value` | `TValue[]` | 선택된 값 배열 |
186
+ | `onValueChange` | `(v: TValue[]) => void` | 변경 콜백 |
187
+ | `disabled` | `boolean` | 비활성화 |
188
+ | `inline` | `boolean` | 가로 배치 |
189
+ | `inset` | `boolean` | 테두리 없음 |
190
+ | `required` | `boolean` | 최소 1개 선택 필수 |
191
+ | `validate` | `(v: TValue[]) => string \| undefined` | 커스텀 유효성 |
314
192
 
315
193
  ---
316
194
 
317
- ## CheckboxGroup
318
-
319
- ```typescript
320
- interface CheckboxGroupProps<TValue> {
321
- value?: TValue[];
322
- onValueChange?: (value: TValue[]) => void;
323
- disabled?: boolean;
324
- size?: "xs" | "sm" | "md" | "lg" | "xl";
325
- inline?: boolean;
326
- inset?: boolean;
327
- required?: boolean;
328
- validate?: (value: TValue[]) => string | undefined;
329
- lazyValidation?: boolean;
330
- class?: string;
331
- style?: JSX.CSSProperties;
332
- children?: JSX.Element;
333
- }
334
- ```
195
+ ## Select
335
196
 
336
- Group of checkboxes with multi-value binding.
197
+ ```tsx
198
+ // 단일 선택 (items 모드)
199
+ <Select
200
+ value={selected()}
201
+ onValueChange={setSelected}
202
+ items={options}
203
+ renderValue={(item) => <span>{item.label}</span>}
204
+ itemSearchText={(item) => item.label}
205
+ />
337
206
 
338
- **Sub-component:** `CheckboxGroup.Item` -- `{ value: TValue; disabled?: boolean; children?: JSX.Element }`
207
+ // 다중 선택
208
+ <Select
209
+ multiple
210
+ value={selectedList()}
211
+ onValueChange={setSelectedList}
212
+ items={options}
213
+ renderValue={(item) => <span>{item.label}</span>}
214
+ tagDirection="horizontal"
215
+ />
339
216
 
340
- ---
217
+ // 트리 구조
218
+ <Select
219
+ value={selected()}
220
+ onValueChange={setSelected}
221
+ items={categories}
222
+ itemChildren={(item) => item.children}
223
+ renderValue={(item) => <span>{item.name}</span>}
224
+ />
341
225
 
342
- ## RadioGroup
343
-
344
- ```typescript
345
- interface RadioGroupProps<TValue> {
346
- value?: TValue;
347
- onValueChange?: (value: TValue) => void;
348
- disabled?: boolean;
349
- size?: "xs" | "sm" | "md" | "lg" | "xl";
350
- inline?: boolean;
351
- inset?: boolean;
352
- required?: boolean;
353
- validate?: (value: TValue | undefined) => string | undefined;
354
- lazyValidation?: boolean;
355
- class?: string;
356
- style?: JSX.CSSProperties;
357
- children?: JSX.Element;
358
- }
226
+ // Children 모드
227
+ <Select value={v()} onValueChange={setV}>
228
+ <Select.Item value="a">옵션 A</Select.Item>
229
+ <Select.Item value="b">옵션 B</Select.Item>
230
+ <Select.Header>그룹 헤더</Select.Header>
231
+ </Select>
232
+
233
+ // ItemTemplate (items 모드에서 드롭다운 렌더링 커스터마이징)
234
+ <Select items={users} renderValue={(u) => <span>{u.name}</span>}>
235
+ <Select.ItemTemplate>
236
+ {(item, _index, _depth) => <span>{item.name} ({item.email})</span>}
237
+ </Select.ItemTemplate>
238
+ </Select>
239
+
240
+ // Action (드롭다운 하단 커스텀 액션)
241
+ <Select items={users} renderValue={(u) => <span>{u.name}</span>}>
242
+ <Select.Action onClick={handleSearch}>
243
+ <Icon icon={IconSearch} />
244
+ </Select.Action>
245
+ </Select>
359
246
  ```
360
247
 
361
- Group of radio buttons with single-value binding.
362
-
363
- **Sub-component:** `RadioGroup.Item` -- `{ value: TValue; disabled?: boolean; children?: JSX.Element }`
364
-
365
248
  ---
366
249
 
367
- ## ColorPicker
250
+ ## Combobox
368
251
 
369
- ```typescript
370
- interface ColorPickerProps {
371
- value?: string;
372
- onValueChange?: (value: string | undefined) => void;
373
- title?: string;
374
- disabled?: boolean;
375
- size?: "xs" | "sm" | "md" | "lg" | "xl";
376
- inset?: boolean;
377
- required?: boolean;
378
- validate?: (value: string | undefined) => string | undefined;
379
- lazyValidation?: boolean;
380
- class?: string;
381
- style?: JSX.CSSProperties;
382
- }
383
- ```
252
+ ```tsx
253
+ <Combobox
254
+ value={user()}
255
+ onValueChange={setUser}
256
+ loadItems={async (query) => await searchUsers(query)}
257
+ renderValue={(u) => <span>{u.name}</span>}
258
+ debounceMs={300}
259
+ />
384
260
 
385
- Color picker with hex `#RRGGBB` value format.
261
+ // 커스텀 허용
262
+ <Combobox
263
+ allowsCustomValue
264
+ parseCustomValue={(text) => ({ id: 0, name: text })}
265
+ loadItems={loadItems}
266
+ renderValue={(v) => <span>{v.name}</span>}
267
+ />
268
+ ```
386
269
 
387
270
  ---
388
271
 
389
- ## DateRangePicker
272
+ ## ColorPicker
390
273
 
391
- ```typescript
392
- type DateRangePeriodType = "day" | "month" | "range";
393
-
394
- interface DateRangePickerProps {
395
- periodType?: DateRangePeriodType;
396
- onPeriodTypeChange?: (value: DateRangePeriodType) => void;
397
- from?: DateOnly;
398
- onFromChange?: (value: DateOnly | undefined) => void;
399
- to?: DateOnly;
400
- onToChange?: (value: DateOnly | undefined) => void;
401
- required?: boolean;
402
- disabled?: boolean;
403
- size?: "xs" | "sm" | "md" | "lg" | "xl";
404
- class?: string;
405
- style?: JSX.CSSProperties;
406
- }
274
+ ```tsx
275
+ <ColorPicker value={color()} onValueChange={setColor} />
276
+ // value: "#RRGGBB" 형식
407
277
  ```
408
278
 
409
- Date range picker with day, month, and custom range modes. Each value (`periodType`, `from`, `to`) is independently controllable.
410
-
411
279
  ---
412
280
 
413
281
  ## RichTextEditor
414
282
 
415
- ```typescript
416
- interface RichTextEditorProps {
417
- value?: string;
418
- onValueChange?: (value: string) => void;
419
- disabled?: boolean;
420
- size?: "xs" | "sm" | "md" | "lg" | "xl";
421
- class?: string;
422
- style?: JSX.CSSProperties;
423
- }
424
- ```
283
+ Tiptap 기반 리치 텍스트 에디터. 서식, 텍스트 스타일, 정렬, 테이블, 이미지, 하이라이트 도구 포함.
425
284
 
426
- Rich text editor backed by Tiptap. `value` is an HTML string.
285
+ ```tsx
286
+ <RichTextEditor value={html()} onValueChange={setHtml} />
287
+ ```
427
288
 
428
289
  ---
429
290
 
430
291
  ## Numpad
431
292
 
432
- ```typescript
433
- interface NumpadProps {
434
- value?: number;
435
- onValueChange?: (value: number | undefined) => void;
436
- placeholder?: string;
437
- required?: boolean;
438
- inputDisabled?: boolean;
439
- withEnterButton?: boolean;
440
- withMinusButton?: boolean;
441
- onEnterButtonClick?: () => void;
442
- size?: "xs" | "sm" | "md" | "lg" | "xl";
443
- class?: string;
444
- style?: JSX.CSSProperties;
445
- }
446
- ```
293
+ 터치 환경용 숫자 키패드 컴포넌트.
447
294
 
448
- On-screen number pad with optional enter and minus buttons.
295
+ ```tsx
296
+ import { Numpad } from "@simplysm/solid";
449
297
 
450
- ---
298
+ <Numpad value={quantity()} onValueChange={setQuantity} />
451
299
 
452
- ## StatePreset
300
+ // Enter 버튼 + 마이너스 버튼 포함
301
+ <Numpad
302
+ value={amount()}
303
+ onValueChange={setAmount}
304
+ withEnterButton
305
+ withMinusButton
306
+ onEnterButtonClick={() => submit()}
307
+ required
308
+ />
453
309
 
454
- ```typescript
455
- interface StatePresetProps<TValue> {
456
- storageKey: string;
457
- value: TValue;
458
- onValueChange: (value: TValue) => void;
459
- size?: "xs" | "sm" | "md" | "lg" | "xl";
460
- class?: string;
461
- style?: JSX.CSSProperties;
462
- }
310
+ // 직접 입력 비활성화 (키패드만 사용)
311
+ <Numpad value={code()} onValueChange={setCode} inputDisabled />
463
312
  ```
464
313
 
465
- Save and restore named presets of component state to sync storage. `storageKey` uniquely identifies the state entry.
314
+ | Prop | 타입 | 설명 |
315
+ |------|------|------|
316
+ | `value` | `number` | 값 |
317
+ | `onValueChange` | `(v: number \| undefined) => void` | 변경 콜백 |
318
+ | `withEnterButton` | `boolean` | Enter 버튼 표시 |
319
+ | `withMinusButton` | `boolean` | 마이너스 토글 버튼 표시 |
320
+ | `onEnterButtonClick` | `() => void` | Enter 버튼 클릭 콜백 |
321
+ | `inputDisabled` | `boolean` | NumberInput 직접 입력 비활성화 |
322
+ | `required` | `boolean` | 필수 입력 |
323
+ | `size` | `ComponentSize` | 크기 |
466
324
 
467
325
  ---
468
326
 
469
- ## ThemeToggle
470
-
471
- ```typescript
472
- interface ThemeToggleProps extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, "children"> {
473
- size?: "xs" | "sm" | "md" | "lg" | "xl";
474
- }
475
- ```
327
+ ## StatePreset
476
328
 
477
- Button that cycles through light, system, and dark theme modes.
329
+ 필터/설정 상태를 프리셋으로 저장하고 복원하는 컴포넌트. localStorage에 자동 저장된다.
478
330
 
479
- ---
331
+ ```tsx
332
+ import { StatePreset } from "@simplysm/solid";
480
333
 
481
- ## Invalid
334
+ const [filterState, setFilterState] = createSignal({ status: "active", keyword: "" });
482
335
 
483
- ```typescript
484
- interface InvalidProps {
485
- message?: string;
486
- variant?: "border" | "dot";
487
- lazyValidation?: boolean;
488
- }
336
+ <StatePreset
337
+ storageKey="user-list-filter"
338
+ value={filterState()}
339
+ onValueChange={setFilterState}
340
+ />
489
341
  ```
490
342
 
491
- Validation error indicator. When `message` is non-empty, the component renders in an invalid state. Used internally by form controls and can be composed for custom validation displays.
343
+ | Prop | 타입 | 설명 |
344
+ |------|------|------|
345
+ | `storageKey` | `string` | localStorage 저장 키 |
346
+ | `value` | `TValue` | 현재 상태 값 |
347
+ | `onValueChange` | `(v: TValue) => void` | 상태 복원 콜백 |
348
+ | `size` | `ComponentSize` | 크기 |
349
+
350
+ 프리셋 칩 UI를 제공하며, 클릭으로 복원, 저장(덮어쓰기), 삭제가 가능하다. 삭제/덮어쓰기 시 실행 취소 알림을 표시한다.
492
351
 
493
352
  ---
494
353
 
495
- ## Usage Examples
354
+ ## ThemeToggle
496
355
 
497
- ```typescript
498
- import { TextInput, Select, Checkbox, DatePicker } from "@simplysm/solid";
499
- import { createSignal } from "solid-js";
356
+ 라이트/다크/시스템 모드 전환 버튼.
500
357
 
501
- const [name, setName] = createSignal("");
502
- const [color, setColor] = createSignal<string>();
503
- const [agreed, setAgreed] = createSignal(false);
504
- const [date, setDate] = createSignal<DateOnly>();
358
+ ```tsx
359
+ <ThemeToggle />
360
+ ```
505
361
 
506
- <TextInput value={name()} onValueChange={setName} placeholder="Name" required />
362
+ ---
507
363
 
508
- <Select
509
- items={["Red", "Green", "Blue"]}
510
- value={color()}
511
- onValueChange={setColor}
512
- placeholder="Pick a color"
513
- />
364
+ ## 유효성 검증
514
365
 
515
- <Checkbox checked={agreed()} onCheckedChange={setAgreed}>
516
- I agree to the terms
517
- </Checkbox>
366
+ 모든 컨트롤은 `validate`, `required`, `lazyValidation` prop을 지원한다.
518
367
 
519
- <DatePicker value={date()} onValueChange={setDate} unit="date" />
368
+ ```tsx
369
+ <TextInput
370
+ value={email()}
371
+ onValueChange={setEmail}
372
+ required
373
+ validate={(v) => v.includes("@") ? undefined : "이메일 형식이 아닙니다"}
374
+ lazyValidation // blur 시 검증
375
+ />
520
376
  ```
377
+
378
+ `Invalid` 컴포넌트로 검증 에러를 감싸서 표시할 수 있다.