@uniai-fe/uds-primitives 0.11.0 → 0.12.1

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 (30) hide show
  1. package/README.md +38 -453
  2. package/dist/styles.css +370 -327
  3. package/package.json +2 -2
  4. package/src/components/input/index.tsx +1 -1
  5. package/src/components/input/markup/time/Template.tsx +412 -0
  6. package/src/components/input/markup/time/Trigger.tsx +233 -0
  7. package/src/components/input/markup/time/index.tsx +9 -2
  8. package/src/components/input/styles/time.scss +160 -317
  9. package/src/components/input/styles/variables.scss +9 -47
  10. package/src/components/input/types/time.ts +263 -120
  11. package/src/components/time-picker/img/minus.svg +3 -0
  12. package/src/components/time-picker/img/plus.svg +3 -0
  13. package/src/components/time-picker/index.scss +1 -0
  14. package/src/components/time-picker/index.tsx +11 -0
  15. package/src/components/time-picker/markup/Footer.tsx +53 -0
  16. package/src/components/time-picker/markup/HourSection.tsx +72 -0
  17. package/src/components/time-picker/markup/MinuteSection.tsx +68 -0
  18. package/src/components/time-picker/markup/Summary.tsx +19 -0
  19. package/src/components/time-picker/markup/Template.tsx +86 -0
  20. package/src/components/time-picker/markup/index.tsx +10 -0
  21. package/src/components/time-picker/styles/index.scss +2 -0
  22. package/src/components/time-picker/styles/template.scss +174 -0
  23. package/src/components/time-picker/styles/variables.scss +64 -0
  24. package/src/components/time-picker/types/index.ts +1 -0
  25. package/src/components/time-picker/types/time-picker.ts +193 -0
  26. package/src/components/time-picker/utils/index.ts +1 -0
  27. package/src/components/time-picker/utils/time.ts +110 -0
  28. package/src/index.scss +1 -0
  29. package/src/index.tsx +1 -0
  30. package/src/components/input/markup/time/Picker.tsx +0 -378
@@ -1,22 +1,26 @@
1
- import type { PopoverProps, ScrollAreaProps } from "@mantine/core";
2
1
  import type {
3
- TimePickerAmPmLabels,
4
- TimePickerFormat,
5
- TimePickerPasteSplit,
6
- TimePickerPresets,
7
- } from "@mantine/dates";
8
- import type { ComponentPropsWithoutRef, FocusEvent, ReactNode } from "react";
2
+ ComponentPropsWithoutRef,
3
+ FocusEvent,
4
+ KeyboardEvent,
5
+ ReactNode,
6
+ } from "react";
9
7
  import type { UseFormRegisterReturn } from "react-hook-form";
10
8
  import type { FormFieldWidth } from "../../form/types/props";
9
+ import type {
10
+ TimePickerFormat,
11
+ TimePickerPeriod,
12
+ TimePickerUnit,
13
+ } from "../../time-picker/types";
11
14
  import type { InputPriority, InputSize, InputState } from "./foundation";
12
15
 
13
16
  /**
14
- * Input Time icon position.
17
+ * Input Time icon 위치.
18
+ * @typedef {"left" | "right"} InputTimeIconPosition
15
19
  */
16
20
  export type InputTimeIconPosition = "left" | "right";
17
21
 
18
22
  /**
19
- * Input TimePicker hidden input props.
23
+ * Input Time hidden input props.
20
24
  */
21
25
  export type InputTimeHiddenInputProps = Omit<
22
26
  ComponentPropsWithoutRef<"input">,
@@ -24,7 +28,201 @@ export type InputTimeHiddenInputProps = Omit<
24
28
  >;
25
29
 
26
30
  /**
27
- * Input TimePicker props.
31
+ * Input Time draft field 값.
32
+ * @property {string} hours 시 입력값
33
+ * @property {string} minutes 분 입력값
34
+ * @property {string} seconds 초 입력값
35
+ */
36
+ export interface InputTimeDraftFields {
37
+ /**
38
+ * 시 입력값.
39
+ */
40
+ hours: string;
41
+ /**
42
+ * 분 입력값.
43
+ */
44
+ minutes: string;
45
+ /**
46
+ * 초 입력값.
47
+ */
48
+ seconds: string;
49
+ }
50
+
51
+ /**
52
+ * Input Time 편집 중인 시간 값.
53
+ * @property {InputTimeDraftFields} fields 시·분·초 입력값
54
+ * @property {"am" | "pm"} period 오전·오후 구분
55
+ */
56
+ export interface InputTimeDraft {
57
+ /**
58
+ * 시·분·초 입력값.
59
+ */
60
+ fields: InputTimeDraftFields;
61
+ /**
62
+ * "am" | "pm".
63
+ */
64
+ period: TimePickerPeriod;
65
+ }
66
+
67
+ /**
68
+ * Input Time trigger field 값.
69
+ * @see InputTimeDraftFields
70
+ */
71
+ export type InputTimeTriggerFields = InputTimeDraftFields;
72
+
73
+ /**
74
+ * Input Time Trigger props.
75
+ * @property {InputTimeTriggerFields} fields 시·분·초 입력값
76
+ * @property {"am" | "pm"} period 오전·오후 구분
77
+ * @property {"hours" | "minutes" | "seconds" | null} activeUnit 현재 활성 단위
78
+ * @property {"primary" | "secondary" | "tertiary" | "table"} priority input priority
79
+ * @property {"small" | "medium" | "large"} size input size
80
+ * @property {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} state input state
81
+ * @property {"12h" | "24h"} format 표시 형식
82
+ * @property {boolean} withSeconds 초 field 노출 여부
83
+ * @property {boolean} disabled 비활성화 여부
84
+ * @property {boolean} readOnly 읽기 전용 여부
85
+ * @property {ReactNode} icon clock icon
86
+ * @property {"left" | "right"} iconPosition clock icon 위치
87
+ * @property {string} iconLabel clock icon 접근성 라벨
88
+ * @property {string} hoursInputLabel 시 input 접근성 라벨
89
+ * @property {string} minutesInputLabel 분 input 접근성 라벨
90
+ * @property {string} secondsInputLabel 초 input 접근성 라벨
91
+ * @property {string} amPmInputLabel 오전·오후 input 접근성 라벨
92
+ * @property {string} hoursPlaceholder 시 placeholder
93
+ * @property {string} minutesPlaceholder 분 placeholder
94
+ * @property {string} secondsPlaceholder 초 placeholder
95
+ * @property {(unit: "hours" | "minutes" | "seconds", value: string) => void} onFieldChange field 변경 핸들러
96
+ * @property {(unit: "hours" | "minutes" | "seconds", event: FocusEvent<HTMLInputElement>) => void} onFieldFocus field focus 핸들러
97
+ * @property {(unit: "hours" | "minutes" | "seconds") => void} onFieldBlur field blur 핸들러
98
+ * @property {(unit: "hours" | "minutes" | "seconds", event: KeyboardEvent<HTMLInputElement>) => void} onFieldKeyDown field keyboard 핸들러
99
+ * @property {(period: "am" | "pm") => void} onPeriodChange 오전·오후 변경 핸들러
100
+ * @property {(amount: 1 | -1) => void} onStep 활성 단위 증감 핸들러
101
+ * @property {(event: FocusEvent<HTMLDivElement>) => void} onRootBlur root blur 핸들러
102
+ */
103
+ export interface InputTimeTriggerProps extends Omit<
104
+ ComponentPropsWithoutRef<"div">,
105
+ "onBlur" | "onFocus"
106
+ > {
107
+ /**
108
+ * 시·분·초 입력값.
109
+ */
110
+ fields: InputTimeTriggerFields;
111
+ /**
112
+ * "am" | "pm".
113
+ */
114
+ period: TimePickerPeriod;
115
+ /**
116
+ * "hours" | "minutes" | "seconds" | null.
117
+ */
118
+ activeUnit: TimePickerUnit | null;
119
+ /**
120
+ * "primary" | "secondary" | "tertiary" | "table".
121
+ */
122
+ priority: InputPriority;
123
+ /**
124
+ * "small" | "medium" | "large".
125
+ */
126
+ size: InputSize;
127
+ /**
128
+ * "default" | "active" | "focused" | "success" | "error" | "disabled" | "loading".
129
+ */
130
+ state: InputState;
131
+ /**
132
+ * "12h" | "24h".
133
+ */
134
+ format: TimePickerFormat;
135
+ /**
136
+ * 초 field 노출 여부.
137
+ */
138
+ withSeconds: boolean;
139
+ /**
140
+ * 비활성화 여부.
141
+ */
142
+ disabled: boolean;
143
+ /**
144
+ * 읽기 전용 여부.
145
+ */
146
+ readOnly: boolean;
147
+ /**
148
+ * clock icon.
149
+ */
150
+ icon: ReactNode;
151
+ /**
152
+ * "left" | "right".
153
+ */
154
+ iconPosition: InputTimeIconPosition;
155
+ /**
156
+ * clock icon 접근성 라벨.
157
+ */
158
+ iconLabel: string;
159
+ /**
160
+ * 시 input 접근성 라벨.
161
+ */
162
+ hoursInputLabel: string;
163
+ /**
164
+ * 분 input 접근성 라벨.
165
+ */
166
+ minutesInputLabel: string;
167
+ /**
168
+ * 초 input 접근성 라벨.
169
+ */
170
+ secondsInputLabel: string;
171
+ /**
172
+ * 오전·오후 input 접근성 라벨.
173
+ */
174
+ amPmInputLabel: string;
175
+ /**
176
+ * 시 placeholder.
177
+ */
178
+ hoursPlaceholder: string;
179
+ /**
180
+ * 분 placeholder.
181
+ */
182
+ minutesPlaceholder: string;
183
+ /**
184
+ * 초 placeholder.
185
+ */
186
+ secondsPlaceholder: string;
187
+ /**
188
+ * field 변경 핸들러.
189
+ */
190
+ onFieldChange: (unit: TimePickerUnit, value: string) => void;
191
+ /**
192
+ * field focus 핸들러.
193
+ */
194
+ onFieldFocus: (
195
+ unit: TimePickerUnit,
196
+ event: FocusEvent<HTMLInputElement>,
197
+ ) => void;
198
+ /**
199
+ * field blur 핸들러.
200
+ */
201
+ onFieldBlur: (unit: TimePickerUnit) => void;
202
+ /**
203
+ * field keyboard 핸들러.
204
+ */
205
+ onFieldKeyDown: (
206
+ unit: TimePickerUnit,
207
+ event: KeyboardEvent<HTMLInputElement>,
208
+ ) => void;
209
+ /**
210
+ * 오전·오후 변경 핸들러.
211
+ */
212
+ onPeriodChange: (period: TimePickerPeriod) => void;
213
+ /**
214
+ * 활성 단위 증감 핸들러.
215
+ */
216
+ onStep: (amount: 1 | -1) => void;
217
+ /**
218
+ * root blur 핸들러.
219
+ */
220
+ onRootBlur: (event: FocusEvent<HTMLDivElement>) => void;
221
+ }
222
+
223
+ /**
224
+ * Input Time Template props.
225
+ * 저장값은 24시간 `HH:mm` 또는 `HH:mm:ss` 문자열을 사용한다.
28
226
  * @property {string} [value] 제어형 시간 값
29
227
  * @property {string} [defaultValue] 비제어 초기 시간 값
30
228
  * @property {(value:string)=>void} [onChange] 값 변경 핸들러
@@ -32,234 +230,179 @@ export type InputTimeHiddenInputProps = Omit<
32
230
  * @property {string} [name] form name
33
231
  * @property {string} [form] form id
34
232
  * @property {UseFormRegisterReturn} [register] react-hook-form register 결과
35
- * @property {InputPriority} [priority] input priority
36
- * @property {InputSize} [size] input size
37
- * @property {InputState} [state] input state
233
+ * @property {"primary" | "secondary" | "tertiary" | "table"} [priority] input priority
234
+ * @property {"small" | "medium" | "large"} [size] input size
235
+ * @property {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [state] input state
38
236
  * @property {boolean} [block] width 100% 여부
39
- * @property {"full" | "fit" | "fill" | "auto" | number | string} [width] width preset 옵션
237
+ * @property {"full" | "fit" | "fill" | "auto" | number | string} [width] width preset
40
238
  * @property {string} [className] root className
41
239
  * @property {boolean} [disabled] disabled 여부
42
240
  * @property {boolean} [readOnly] readOnly 여부
43
241
  * @property {boolean} [required] required 여부
44
242
  * @property {string} [id] root id
45
- * @property {"12h" | "24h"} [format] 표시/입력 시간 포맷
46
- * @property {boolean} [withSeconds] seconds 입력 노출 여부
47
- * @property {boolean} [withDropdown] dropdown 노출 여부
48
- * @property {boolean} [clearable] clear 버튼 노출 여부
49
- * @property {string} [min] 최소 시간
50
- * @property {string} [max] 최대 시간
51
- * @property {number} [hoursStep] hour step
52
- * @property {number} [minutesStep] minute step
53
- * @property {number} [secondsStep] second step
243
+ * @property {"12h" | "24h"} [format] 표시 형식
244
+ * @property {boolean} [withSeconds] field 노출 여부
245
+ * @property {boolean} [clearable] 삭제 액션 노출 여부
246
+ * @property {number} [hoursStep] 증감 단위
247
+ * @property {number} [minutesStep] 증감 단위
248
+ * @property {number} [secondsStep] 증감 단위
54
249
  * @property {ReactNode} [icon] clock icon override
55
250
  * @property {"left" | "right"} [iconPosition] clock icon 위치
56
251
  * @property {string} [iconLabel] clock icon 접근성 라벨
57
- * @property {string} [clearButtonLabel] clear 버튼 접근성 라벨
58
- * @property {PopoverProps} [popoverProps] Mantine Popover 옵션
59
- * @property {InputTimeHiddenInputProps} [hiddenInputProps] hidden input 옵션
60
- * @property {string} [hoursInputLabel] hour input aria-label
61
- * @property {string} [minutesInputLabel] minute input aria-label
62
- * @property {string} [secondsInputLabel] second input aria-label
63
- * @property {string} [amPmInputLabel] am/pm input aria-label
64
- * @property {TimePickerAmPmLabels} [amPmLabels] am/pm 라벨
65
- * @property {string} [hoursPlaceholder] hour placeholder
66
- * @property {string} [minutesPlaceholder] minute placeholder
67
- * @property {string} [secondsPlaceholder] second placeholder
68
- * @property {TimePickerPasteSplit} [pasteSplit] paste parser
69
- * @property {TimePickerPresets} [presets] dropdown preset
70
- * @property {number} [maxDropdownContentHeight] dropdown 최대 높이
71
- * @property {ScrollAreaProps} [scrollAreaProps] dropdown scroll area 옵션
72
- * @property {boolean} [reverseTimeControlsList] dropdown control 역순 여부
252
+ * @property {InputTimeHiddenInputProps} [hiddenInputProps] hidden input props
253
+ * @property {string} [hoursInputLabel] input 접근성 라벨
254
+ * @property {string} [minutesInputLabel] input 접근성 라벨
255
+ * @property {string} [secondsInputLabel] input 접근성 라벨
256
+ * @property {string} [amPmInputLabel] 오전/오후 접근성 라벨
257
+ * @property {string} [hoursPlaceholder] placeholder
258
+ * @property {string} [minutesPlaceholder] placeholder
259
+ * @property {string} [secondsPlaceholder] placeholder
73
260
  * @property {(event:FocusEvent<HTMLInputElement>)=>void} [onFocus] focus 핸들러
74
261
  * @property {(event:FocusEvent<HTMLDivElement>)=>void} [onBlur] blur 핸들러
75
262
  */
76
- export interface InputTimePickerProps {
263
+ export interface InputTimeTemplateProps {
77
264
  /**
78
- * 제어형 시간
265
+ * 제어형 시간 값.
79
266
  */
80
267
  value?: string;
81
268
  /**
82
- * 비제어 초기 시간
269
+ * 비제어 초기 시간 값.
83
270
  */
84
271
  defaultValue?: string;
85
272
  /**
86
- * 값 변경 핸들러
273
+ * 값 변경 핸들러.
87
274
  */
88
275
  onChange?: (value: string) => void;
89
276
  /**
90
- * 값 변경 별칭
277
+ * 값 변경 별칭.
91
278
  */
92
279
  onValueChange?: (value: string) => void;
93
280
  /**
94
- * form name
281
+ * form name.
95
282
  */
96
283
  name?: string;
97
284
  /**
98
- * form id
285
+ * form id.
99
286
  */
100
287
  form?: string;
101
288
  /**
102
- * react-hook-form register 결과
289
+ * react-hook-form register 결과.
103
290
  */
104
291
  register?: UseFormRegisterReturn;
105
292
  /**
106
- * input priority
293
+ * "primary" | "secondary" | "tertiary" | "table".
107
294
  */
108
295
  priority?: InputPriority;
109
296
  /**
110
- * input size
297
+ * "small" | "medium" | "large".
111
298
  */
112
299
  size?: InputSize;
113
300
  /**
114
- * input state
301
+ * "default" | "active" | "focused" | "success" | "error" | "disabled" | "loading".
115
302
  */
116
303
  state?: InputState;
117
304
  /**
118
- * width 100% 여부
305
+ * width 100% 여부.
119
306
  */
120
307
  block?: boolean;
121
308
  /**
122
- * width preset 옵션
309
+ * "full" | "fit" | "fill" | "auto" | number | string.
123
310
  */
124
311
  width?: FormFieldWidth;
125
312
  /**
126
- * root className
313
+ * root className.
127
314
  */
128
315
  className?: string;
129
316
  /**
130
- * disabled 여부
317
+ * disabled 여부.
131
318
  */
132
319
  disabled?: boolean;
133
320
  /**
134
- * readOnly 여부
321
+ * readOnly 여부.
135
322
  */
136
323
  readOnly?: boolean;
137
324
  /**
138
- * required 여부
325
+ * required 여부.
139
326
  */
140
327
  required?: boolean;
141
328
  /**
142
- * root id
329
+ * root id.
143
330
  */
144
331
  id?: string;
145
332
  /**
146
- * 표시/입력 시간 포맷
333
+ * "12h" | "24h".
147
334
  */
148
335
  format?: TimePickerFormat;
149
336
  /**
150
- * seconds 입력 노출 여부
337
+ * field 노출 여부.
151
338
  */
152
339
  withSeconds?: boolean;
153
340
  /**
154
- * dropdown 노출 여부
155
- */
156
- withDropdown?: boolean;
157
- /**
158
- * clear 버튼 노출 여부
341
+ * 삭제 액션 노출 여부.
159
342
  */
160
343
  clearable?: boolean;
161
344
  /**
162
- * 최소 시간
163
- */
164
- min?: string;
165
- /**
166
- * 최대 시간 값
167
- */
168
- max?: string;
169
- /**
170
- * hour step
345
+ * 증감 단위.
171
346
  */
172
347
  hoursStep?: number;
173
348
  /**
174
- * minute step
349
+ * 증감 단위.
175
350
  */
176
351
  minutesStep?: number;
177
352
  /**
178
- * second step
353
+ * 증감 단위.
179
354
  */
180
355
  secondsStep?: number;
181
356
  /**
182
- * clock icon override
357
+ * clock icon override.
183
358
  */
184
359
  icon?: ReactNode;
185
360
  /**
186
- * clock icon 위치
361
+ * "left" | "right".
187
362
  */
188
363
  iconPosition?: InputTimeIconPosition;
189
364
  /**
190
- * clock icon 접근성 라벨
365
+ * clock icon 접근성 라벨.
191
366
  */
192
367
  iconLabel?: string;
193
368
  /**
194
- * clear 버튼 접근성 라벨
195
- */
196
- clearButtonLabel?: string;
197
- /**
198
- * Mantine Popover 옵션
199
- */
200
- popoverProps?: PopoverProps;
201
- /**
202
- * hidden input 옵션
369
+ * hidden input props.
203
370
  */
204
371
  hiddenInputProps?: InputTimeHiddenInputProps;
205
372
  /**
206
- * hour input aria-label
373
+ * input 접근성 라벨.
207
374
  */
208
375
  hoursInputLabel?: string;
209
376
  /**
210
- * minute input aria-label
377
+ * input 접근성 라벨.
211
378
  */
212
379
  minutesInputLabel?: string;
213
380
  /**
214
- * second input aria-label
381
+ * input 접근성 라벨.
215
382
  */
216
383
  secondsInputLabel?: string;
217
384
  /**
218
- * am/pm input aria-label
385
+ * 오전/오후 접근성 라벨.
219
386
  */
220
387
  amPmInputLabel?: string;
221
388
  /**
222
- * am/pm 라벨
223
- */
224
- amPmLabels?: TimePickerAmPmLabels;
225
- /**
226
- * hour placeholder
389
+ * placeholder.
227
390
  */
228
391
  hoursPlaceholder?: string;
229
392
  /**
230
- * minute placeholder
393
+ * placeholder.
231
394
  */
232
395
  minutesPlaceholder?: string;
233
396
  /**
234
- * second placeholder
397
+ * placeholder.
235
398
  */
236
399
  secondsPlaceholder?: string;
237
400
  /**
238
- * paste parser
239
- */
240
- pasteSplit?: TimePickerPasteSplit;
241
- /**
242
- * dropdown preset
243
- */
244
- presets?: TimePickerPresets;
245
- /**
246
- * dropdown 최대 높이
247
- */
248
- maxDropdownContentHeight?: number;
249
- /**
250
- * dropdown scroll area 옵션
251
- */
252
- scrollAreaProps?: ScrollAreaProps;
253
- /**
254
- * dropdown control 역순 여부
255
- */
256
- reverseTimeControlsList?: boolean;
257
- /**
258
- * focus 핸들러
401
+ * focus 핸들러.
259
402
  */
260
403
  onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
261
404
  /**
262
- * blur 핸들러
405
+ * blur 핸들러.
263
406
  */
264
407
  onBlur?: (event: FocusEvent<HTMLDivElement>) => void;
265
408
  }
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM8 11.2002C7.55817 11.2002 7.2002 11.5582 7.2002 12C7.2002 12.4418 7.55817 12.7998 8 12.7998H16C16.4418 12.7998 16.7998 12.4418 16.7998 12C16.7998 11.5582 16.4418 11.2002 16 11.2002H8Z" fill="#3A3C46"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM12 7.20508C11.5582 7.20508 11.2002 7.56306 11.2002 8.00488V11.2051H8C7.55817 11.2051 7.2002 11.5631 7.2002 12.0049C7.2002 12.4467 7.55817 12.8047 8 12.8047H11.2002V16.0049C11.2002 16.4467 11.5582 16.8047 12 16.8047C12.4418 16.8047 12.7998 16.4467 12.7998 16.0049V12.8047H16C16.4418 12.8047 16.7998 12.4467 16.7998 12.0049C16.7998 11.5631 16.4418 11.2051 16 11.2051H12.7998V8.00488C12.7998 7.56306 12.4418 7.20508 12 7.20508Z" fill="#3A3C46"/>
3
+ </svg>
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * TimePicker; 시간 선택 패널 카테고리 배럴.
3
+ * @desc
4
+ * - `TimePicker.Template`: 시간·분 선택과 footer action을 제공한다.
5
+ * - `TimePickerUtils`: 시간 파싱·직렬화·증감 유틸리티다.
6
+ */
7
+ import "./index.scss";
8
+
9
+ export * from "./markup";
10
+ export * as TimePickerUtils from "./utils";
11
+ export type * from "./types";
@@ -0,0 +1,53 @@
1
+ import { Button } from "../../button";
2
+ import type { TimePickerFooterProps } from "../types";
3
+
4
+ /**
5
+ * TimePicker Footer; 삭제와 적용 action을 렌더한다.
6
+ * @component
7
+ * @param {TimePickerFooterProps} props
8
+ * @param {boolean} props.clearable 삭제 action 노출 여부
9
+ * @param {boolean} props.disabled 비활성화 여부
10
+ * @param {() => void} props.onClear 삭제 핸들러
11
+ * @param {() => void} props.onApply 적용 핸들러
12
+ * @returns {ReactNode} TimePicker footer
13
+ * @example
14
+ * <TimePickerFooter
15
+ * clearable
16
+ * disabled={false}
17
+ * onClear={onClear}
18
+ * onApply={onApply}
19
+ * />
20
+ */
21
+ export default function TimePickerFooter({
22
+ clearable,
23
+ disabled,
24
+ onClear,
25
+ onApply,
26
+ }: TimePickerFooterProps) {
27
+ return (
28
+ <footer className="time-picker-template-footer">
29
+ {clearable ? (
30
+ <button
31
+ type="button"
32
+ className="time-picker-clear-action"
33
+ disabled={disabled}
34
+ onClick={onClear}
35
+ >
36
+ 삭제하기
37
+ </button>
38
+ ) : (
39
+ <span />
40
+ )}
41
+ <Button.Default
42
+ type="button"
43
+ priority="primary"
44
+ fill="solid"
45
+ size="large"
46
+ disabled={disabled}
47
+ onClick={onApply}
48
+ >
49
+ 적용하기
50
+ </Button.Default>
51
+ </footer>
52
+ );
53
+ }
@@ -0,0 +1,72 @@
1
+ import { SegmentedControl } from "../../segmented-control";
2
+ import type { TimePickerHourSectionProps } from "../types";
3
+
4
+ const HOURS_12 = Array.from({ length: 12 }, (_, index) => index + 1);
5
+ const HOURS_24 = Array.from({ length: 24 }, (_, index) => index);
6
+ const PERIOD_OPTIONS = [
7
+ { value: "am", label: "오전" },
8
+ { value: "pm", label: "오후" },
9
+ ];
10
+
11
+ /**
12
+ * TimePicker Hour Section; 오전·오후와 시 선택 grid를 렌더한다.
13
+ * @component
14
+ * @param {TimePickerHourSectionProps} props
15
+ * @param {"12h" | "24h"} props.format 표시 형식
16
+ * @param {number | null} props.selectedHour 선택한 표시 시
17
+ * @param {"am" | "pm"} props.period 오전·오후
18
+ * @param {boolean} props.disabled 비활성화 여부
19
+ * @param {(hours: number) => void} props.onHourSelect 시 선택 핸들러
20
+ * @param {(period: "am" | "pm") => void} props.onPeriodChange 오전·오후 변경 핸들러
21
+ * @returns {ReactNode} 시간 선택 section
22
+ * @example
23
+ * <TimePickerHourSection
24
+ * format="12h"
25
+ * selectedHour={9}
26
+ * period="am"
27
+ * disabled={false}
28
+ * onHourSelect={onHourSelect}
29
+ * onPeriodChange={onPeriodChange}
30
+ * />
31
+ */
32
+ export default function TimePickerHourSection({
33
+ format,
34
+ selectedHour,
35
+ period,
36
+ disabled,
37
+ onHourSelect,
38
+ onPeriodChange,
39
+ }: TimePickerHourSectionProps) {
40
+ const hours = format === "12h" ? HOURS_12 : HOURS_24;
41
+
42
+ return (
43
+ <section className="time-picker-template-section">
44
+ <h3>시간 선택</h3>
45
+ {format === "12h" ? (
46
+ <SegmentedControl
47
+ className="time-picker-period-control"
48
+ options={PERIOD_OPTIONS}
49
+ ariaLabel="오전 또는 오후 선택"
50
+ value={period}
51
+ onValueChange={nextPeriod =>
52
+ onPeriodChange(nextPeriod === "pm" ? "pm" : "am")
53
+ }
54
+ />
55
+ ) : null}
56
+ <div className="time-picker-option-grid">
57
+ {hours.map(hour => (
58
+ <button
59
+ key={`time-picker/hour/${hour}`}
60
+ type="button"
61
+ className="time-picker-option"
62
+ aria-pressed={selectedHour === hour}
63
+ disabled={disabled}
64
+ onClick={() => onHourSelect(hour)}
65
+ >
66
+ {hour}:00
67
+ </button>
68
+ ))}
69
+ </div>
70
+ </section>
71
+ );
72
+ }