@uniai-fe/uds-primitives 0.6.4 → 0.6.6
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/dist/styles.css +190 -4
- package/package.json +3 -3
- package/src/components/calendar/index.tsx +1 -0
- package/src/components/calendar/markup/Core.tsx +37 -11
- package/src/components/calendar/markup/index.tsx +5 -0
- package/src/components/calendar/markup/range/Core.tsx +109 -0
- package/src/components/calendar/markup/range/Root.tsx +133 -0
- package/src/components/calendar/markup/range/index.tsx +15 -0
- package/src/components/calendar/styles/mantine-calendar.scss +175 -4
- package/src/components/calendar/types/calendar.ts +150 -0
- package/src/components/calendar/utils/value-mapper.ts +37 -4
- package/src/components/input/markup/date/Template.tsx +1 -4
- package/src/components/input/markup/date/index.tsx +11 -0
- package/src/components/input/markup/date/range/Template.tsx +261 -0
- package/src/components/input/styles/date.scss +44 -0
- package/src/components/input/types/date.ts +114 -3
- package/src/components/input/utils/date.ts +41 -1
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ChangeEvent, MouseEvent as ReactMouseEvent } from "react";
|
|
4
|
+
import { forwardRef, useCallback, useMemo, useState } from "react";
|
|
5
|
+
import { useUncontrolled } from "@mantine/hooks";
|
|
6
|
+
import { Calendar } from "../../../../calendar";
|
|
7
|
+
import type {
|
|
8
|
+
InputCalendarRangeProps,
|
|
9
|
+
InputCalendarTriggerRenderProps,
|
|
10
|
+
} from "../../../types";
|
|
11
|
+
import type { CalendarRangeValue, CalendarValue } from "../../../../calendar";
|
|
12
|
+
import {
|
|
13
|
+
createEmptyRangeValue,
|
|
14
|
+
formatRangeTriggerValue,
|
|
15
|
+
getTodayValue,
|
|
16
|
+
serializeCalendarValue,
|
|
17
|
+
} from "../../../utils/date";
|
|
18
|
+
import InputDateApplyButton from "../button/ApplyButton";
|
|
19
|
+
import InputDateClearButton from "../button/ClearButton";
|
|
20
|
+
import InputDateTodayButton from "../button/TodayButton";
|
|
21
|
+
import InputDateFooterTemplateContainer from "../footer/Container";
|
|
22
|
+
import InputDateFooterUtilContainer from "../footer/UtilContainer";
|
|
23
|
+
import InputDateTrigger from "../Trigger";
|
|
24
|
+
|
|
25
|
+
const INPUT_DATE_RANGE_TABLE_FORMAT = "YY-MM-DD";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Input Date Range Template; trigger + Calendar.Range 조합.
|
|
29
|
+
* priority가 table이면 trigger 표시 포맷을 `YY-MM-DD ~ YY-MM-DD`로 맞춘다.
|
|
30
|
+
* form 저장은 start/end hidden input 2개로 분리한다.
|
|
31
|
+
* @component
|
|
32
|
+
* @param {InputCalendarRangeProps} props range date props
|
|
33
|
+
* @param {CalendarRangeValue} [props.value] 제어형 range 값
|
|
34
|
+
* @param {CalendarRangeValue} [props.defaultValue] 비제어 range 초기값
|
|
35
|
+
* @param {CalendarRangeOnChange} [props.onChange] range 값 변경 이벤트
|
|
36
|
+
* @param {CalendarRangeOnChange} [props.onValueChange] onChange alias
|
|
37
|
+
* @param {boolean} [props.readOnly] 읽기 전용 여부
|
|
38
|
+
* @param {boolean} [props.disabled] 비활성화 여부
|
|
39
|
+
* @param {CalendarRangeDatePickerProps} [props.datePickerProps] Mantine range DatePicker 옵션
|
|
40
|
+
* @param {string} [props.startName] 시작일 hidden input name
|
|
41
|
+
* @param {string} [props.endName] 종료일 hidden input name
|
|
42
|
+
* @param {UseFormRegisterReturn} [props.startRegister] 시작일 RHF register
|
|
43
|
+
* @param {UseFormRegisterReturn} [props.endRegister] 종료일 RHF register
|
|
44
|
+
* @param {string} [props.placeholder="YYYY-MM-DD ~ YYYY-MM-DD"] placeholder
|
|
45
|
+
* @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] trigger input priority
|
|
46
|
+
* @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] trigger input state
|
|
47
|
+
* @param {ReactNode} [props.header] 패널 header 콘텐츠
|
|
48
|
+
* @param {ReactNode} [props.footer] 패널 footer 콘텐츠
|
|
49
|
+
* @param {boolean} [props.calendarOpened] calendar 열림 제어 상태
|
|
50
|
+
* @param {string} [props.id] trigger id
|
|
51
|
+
* @param {ReactNode} [props.trigger] 커스텀 trigger 슬롯
|
|
52
|
+
* @param {(props: InputCalendarTriggerRenderProps) => ReactNode} [props.renderTrigger] 커스텀 trigger 렌더 함수
|
|
53
|
+
* @param {(event: MouseEvent<Element>) => void} [props.onClick] trigger 클릭 핸들러
|
|
54
|
+
* @example
|
|
55
|
+
* <Input.Date.Range.Template
|
|
56
|
+
* startName="start_date"
|
|
57
|
+
* endName="end_date"
|
|
58
|
+
* value={range}
|
|
59
|
+
* onChange={setRange}
|
|
60
|
+
* />
|
|
61
|
+
*/
|
|
62
|
+
const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
|
|
63
|
+
(
|
|
64
|
+
{
|
|
65
|
+
value,
|
|
66
|
+
defaultValue,
|
|
67
|
+
onChange,
|
|
68
|
+
onValueChange,
|
|
69
|
+
readOnly,
|
|
70
|
+
disabled,
|
|
71
|
+
datePickerProps,
|
|
72
|
+
startName,
|
|
73
|
+
endName,
|
|
74
|
+
startRegister,
|
|
75
|
+
endRegister,
|
|
76
|
+
placeholder = "YYYY-MM-DD ~ YYYY-MM-DD",
|
|
77
|
+
priority = "primary",
|
|
78
|
+
state = "default",
|
|
79
|
+
header,
|
|
80
|
+
footer,
|
|
81
|
+
calendarOpened,
|
|
82
|
+
onClick: triggerOnClick,
|
|
83
|
+
id,
|
|
84
|
+
trigger,
|
|
85
|
+
renderTrigger,
|
|
86
|
+
},
|
|
87
|
+
ref,
|
|
88
|
+
) => {
|
|
89
|
+
// 기본(비제어) range calendar open 상태를 관리한다.
|
|
90
|
+
const [internalCalendarOpen, setInternalCalendarOpen] = useState(false);
|
|
91
|
+
const resolvedCalendarOpen = calendarOpened ?? internalCalendarOpen;
|
|
92
|
+
|
|
93
|
+
// Calendar.Range tuple 계약을 그대로 유지하며 제어형/비제어 값을 모두 허용한다.
|
|
94
|
+
const [calendarValue, setCalendarValue] =
|
|
95
|
+
useUncontrolled<CalendarRangeValue>({
|
|
96
|
+
value,
|
|
97
|
+
defaultValue,
|
|
98
|
+
finalValue: createEmptyRangeValue(),
|
|
99
|
+
onChange: next => {
|
|
100
|
+
onChange?.(next as CalendarRangeValue);
|
|
101
|
+
onValueChange?.(next as CalendarRangeValue);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// react-hook-form register onChange를 start/end 각각 호출해 hidden input 저장값을 맞춘다.
|
|
106
|
+
const emitRegisterChange = useCallback(
|
|
107
|
+
(nextValue: CalendarRangeValue) => {
|
|
108
|
+
const emit = (
|
|
109
|
+
register: typeof startRegister | typeof endRegister | undefined,
|
|
110
|
+
nextCalendarValue: CalendarValue,
|
|
111
|
+
) => {
|
|
112
|
+
if (!register?.onChange) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const serialized = serializeCalendarValue(nextCalendarValue);
|
|
117
|
+
const syntheticEvent = {
|
|
118
|
+
target: {
|
|
119
|
+
name: register.name,
|
|
120
|
+
value: serialized,
|
|
121
|
+
},
|
|
122
|
+
currentTarget: {
|
|
123
|
+
name: register.name,
|
|
124
|
+
value: serialized,
|
|
125
|
+
},
|
|
126
|
+
} as unknown as ChangeEvent<HTMLInputElement>;
|
|
127
|
+
|
|
128
|
+
register.onChange(syntheticEvent);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
emit(startRegister, nextValue[0]);
|
|
132
|
+
emit(endRegister, nextValue[1]);
|
|
133
|
+
},
|
|
134
|
+
[endRegister, startRegister],
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const updateValue = useCallback(
|
|
138
|
+
(nextValue: CalendarRangeValue) => {
|
|
139
|
+
setCalendarValue(nextValue);
|
|
140
|
+
emitRegisterChange(nextValue);
|
|
141
|
+
},
|
|
142
|
+
[emitRegisterChange, setCalendarValue],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const triggerValue = useMemo(
|
|
146
|
+
() =>
|
|
147
|
+
formatRangeTriggerValue(
|
|
148
|
+
calendarValue,
|
|
149
|
+
priority === "table" ? INPUT_DATE_RANGE_TABLE_FORMAT : undefined,
|
|
150
|
+
),
|
|
151
|
+
[calendarValue, priority],
|
|
152
|
+
);
|
|
153
|
+
const serializedStartValue = serializeCalendarValue(calendarValue[0]);
|
|
154
|
+
const serializedEndValue = serializeCalendarValue(calendarValue[1]);
|
|
155
|
+
|
|
156
|
+
const handleTriggerClick = (event: ReactMouseEvent<Element>) => {
|
|
157
|
+
triggerOnClick?.(event);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const handleCalendarChange = (nextValue: CalendarRangeValue) => {
|
|
161
|
+
updateValue(nextValue);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// range 기본 footer는 Today row와 Clear/Apply row를 분리해 Figma range footer 구조를 유지한다.
|
|
165
|
+
const footerContent = footer ?? (
|
|
166
|
+
<InputDateFooterTemplateContainer className="input-date-range-footer-template">
|
|
167
|
+
<InputDateFooterUtilContainer className="input-date-range-today-row">
|
|
168
|
+
<InputDateTodayButton
|
|
169
|
+
disabled={disabled}
|
|
170
|
+
onClick={() => {
|
|
171
|
+
const today = getTodayValue();
|
|
172
|
+
updateValue([today, today]);
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
</InputDateFooterUtilContainer>
|
|
176
|
+
<InputDateFooterUtilContainer className="input-date-range-footer-row">
|
|
177
|
+
<InputDateClearButton
|
|
178
|
+
className="input-date-range-clear-button"
|
|
179
|
+
disabled={disabled}
|
|
180
|
+
onClick={() => updateValue(createEmptyRangeValue())}
|
|
181
|
+
>
|
|
182
|
+
삭제하기
|
|
183
|
+
</InputDateClearButton>
|
|
184
|
+
{/* 변경: range footer는 삭제/적용을 같은 row에 두고 Apply 폭을 content 기준으로 줄인다. */}
|
|
185
|
+
<InputDateApplyButton
|
|
186
|
+
className="input-date-range-apply-button"
|
|
187
|
+
disabled={disabled}
|
|
188
|
+
onClick={() => setInternalCalendarOpen(false)}
|
|
189
|
+
/>
|
|
190
|
+
</InputDateFooterUtilContainer>
|
|
191
|
+
</InputDateFooterTemplateContainer>
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const triggerRenderProps: InputCalendarTriggerRenderProps = {
|
|
195
|
+
id,
|
|
196
|
+
name: startRegister?.name ?? startName,
|
|
197
|
+
placeholder,
|
|
198
|
+
displayValue: triggerValue,
|
|
199
|
+
disabled,
|
|
200
|
+
readOnly,
|
|
201
|
+
onClick: handleTriggerClick,
|
|
202
|
+
priority,
|
|
203
|
+
state,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const triggerNode = trigger ?? renderTrigger?.(triggerRenderProps) ?? (
|
|
207
|
+
<InputDateTrigger
|
|
208
|
+
id={id}
|
|
209
|
+
placeholder={placeholder}
|
|
210
|
+
displayValue={triggerValue}
|
|
211
|
+
disabled={disabled}
|
|
212
|
+
state={state}
|
|
213
|
+
readOnly={readOnly}
|
|
214
|
+
onClick={handleTriggerClick}
|
|
215
|
+
priority={priority}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<>
|
|
221
|
+
<Calendar.Range.Root
|
|
222
|
+
ref={ref}
|
|
223
|
+
className="input-date-field"
|
|
224
|
+
disabled={disabled}
|
|
225
|
+
readOnly={readOnly}
|
|
226
|
+
value={calendarValue}
|
|
227
|
+
onChange={handleCalendarChange}
|
|
228
|
+
datePickerProps={datePickerProps}
|
|
229
|
+
header={header}
|
|
230
|
+
footer={footerContent}
|
|
231
|
+
open={resolvedCalendarOpen}
|
|
232
|
+
onOpenChange={setInternalCalendarOpen}
|
|
233
|
+
>
|
|
234
|
+
{triggerNode}
|
|
235
|
+
</Calendar.Range.Root>
|
|
236
|
+
{(startRegister?.name || startName) && (
|
|
237
|
+
<input
|
|
238
|
+
// 변경: range 시작일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
|
|
239
|
+
type="hidden"
|
|
240
|
+
value={serializedStartValue}
|
|
241
|
+
name={startRegister?.name ?? startName}
|
|
242
|
+
{...startRegister}
|
|
243
|
+
/>
|
|
244
|
+
)}
|
|
245
|
+
{(endRegister?.name || endName) && (
|
|
246
|
+
<input
|
|
247
|
+
// 변경: range 종료일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
|
|
248
|
+
type="hidden"
|
|
249
|
+
value={serializedEndValue}
|
|
250
|
+
name={endRegister?.name ?? endName}
|
|
251
|
+
{...endRegister}
|
|
252
|
+
/>
|
|
253
|
+
)}
|
|
254
|
+
</>
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
InputDateRangeTemplate.displayName = "InputDateRangeTemplate";
|
|
260
|
+
|
|
261
|
+
export default InputDateRangeTemplate;
|
|
@@ -64,3 +64,47 @@
|
|
|
64
64
|
.input-date-apply-button {
|
|
65
65
|
width: 100%;
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
.input-date-range-footer-template {
|
|
69
|
+
gap: var(--spacing-gap-6);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.input-date-range-today-row {
|
|
73
|
+
justify-content: flex-end;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.input-date-range-footer-row {
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.input-date-range-clear-button {
|
|
81
|
+
// range footer의 삭제 액션은 Figma Button/Text node처럼 border 없는 error text로 표현한다.
|
|
82
|
+
min-height: 20px;
|
|
83
|
+
padding-inline: var(--spacing-padding-5);
|
|
84
|
+
border-color: transparent;
|
|
85
|
+
background-color: transparent;
|
|
86
|
+
color: var(--color-feedback-error);
|
|
87
|
+
|
|
88
|
+
&:hover:not(:disabled):not([aria-disabled="true"]),
|
|
89
|
+
&:active:not(:disabled):not([aria-disabled="true"]) {
|
|
90
|
+
border-color: transparent;
|
|
91
|
+
background-color: transparent;
|
|
92
|
+
color: var(--color-feedback-error);
|
|
93
|
+
box-shadow: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.button-label {
|
|
97
|
+
font-size: var(--font-body-xxsmall-size);
|
|
98
|
+
line-height: 1.4;
|
|
99
|
+
font-weight: var(--font-body-medium-weight);
|
|
100
|
+
letter-spacing: 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.input-date-range-apply-button {
|
|
105
|
+
// range footer의 적용 버튼은 full width가 아니라 Figma CTA 크기처럼 content 폭만 사용한다.
|
|
106
|
+
width: fit-content;
|
|
107
|
+
flex: 0 0 auto;
|
|
108
|
+
--button-width: fit-content;
|
|
109
|
+
--button-flex: 0 0 auto;
|
|
110
|
+
}
|
|
@@ -5,6 +5,9 @@ import type {
|
|
|
5
5
|
CalendarDatePickerProps,
|
|
6
6
|
CalendarMode,
|
|
7
7
|
CalendarOnChange,
|
|
8
|
+
CalendarRangeDatePickerProps,
|
|
9
|
+
CalendarRangeOnChange,
|
|
10
|
+
CalendarRangeValue,
|
|
8
11
|
CalendarValue,
|
|
9
12
|
} from "../../calendar";
|
|
10
13
|
import type { InputPriority, InputState } from "./foundation";
|
|
@@ -147,7 +150,6 @@ export interface InputCalendarTriggerRenderProps {
|
|
|
147
150
|
* @property {ReactNode} [footer] 커스텀 footer 콘텐츠
|
|
148
151
|
* @property {unknown} [timePicker] TimePicker 확장용 예약 슬롯(현재 미구현)
|
|
149
152
|
* @property {boolean} [calendarOpened] calendar 열림 제어 여부
|
|
150
|
-
* @property {string} [className] root className
|
|
151
153
|
* @property {ReactNode} [trigger] 커스텀 trigger 슬롯
|
|
152
154
|
* @property {(props: InputCalendarTriggerRenderProps) => ReactNode} [renderTrigger] 커스텀 trigger 렌더 함수
|
|
153
155
|
*/
|
|
@@ -207,9 +209,114 @@ export interface InputCalendarProps extends InputCalendarTriggerProps {
|
|
|
207
209
|
*/
|
|
208
210
|
calendarOpened?: boolean;
|
|
209
211
|
/**
|
|
210
|
-
*
|
|
212
|
+
* 커스텀 trigger 슬롯
|
|
211
213
|
*/
|
|
212
|
-
|
|
214
|
+
trigger?: ReactNode;
|
|
215
|
+
/**
|
|
216
|
+
* 커스텀 trigger 렌더 함수
|
|
217
|
+
*/
|
|
218
|
+
renderTrigger?: (props: InputCalendarTriggerRenderProps) => ReactNode;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Input Calendar Range Template props.
|
|
223
|
+
* @property {CalendarRangeValue} [value] 제어형 range 값
|
|
224
|
+
* @property {CalendarRangeValue} [defaultValue] 비제어 range 초기값
|
|
225
|
+
* @property {CalendarRangeOnChange} [onChange] range 값 변경 핸들러
|
|
226
|
+
* @property {CalendarRangeOnChange} [onValueChange] onChange 별칭
|
|
227
|
+
* @property {boolean} [readOnly] 읽기 전용 여부
|
|
228
|
+
* @property {boolean} [disabled] disabled 여부
|
|
229
|
+
* @property {CalendarRangeDatePickerProps} [datePickerProps] Mantine range DatePicker 옵션
|
|
230
|
+
* @property {string} [startName] 시작일 hidden input name
|
|
231
|
+
* @property {string} [endName] 종료일 hidden input name
|
|
232
|
+
* @property {UseFormRegisterReturn} [startRegister] 시작일 react-hook-form register 결과
|
|
233
|
+
* @property {UseFormRegisterReturn} [endRegister] 종료일 react-hook-form register 결과
|
|
234
|
+
* @property {string} [placeholder] trigger 내부 placeholder
|
|
235
|
+
* @property {InputPriority} [priority] trigger input priority
|
|
236
|
+
* @property {InputState} [state] trigger input state
|
|
237
|
+
* @property {ReactNode} [header] 커스텀 header 콘텐츠
|
|
238
|
+
* @property {ReactNode} [footer] 커스텀 footer 콘텐츠
|
|
239
|
+
* @property {boolean} [calendarOpened] calendar 열림 제어 여부
|
|
240
|
+
* @property {string} [id] trigger id
|
|
241
|
+
* @property {ReactNode} [trigger] 커스텀 trigger 슬롯
|
|
242
|
+
* @property {(props: InputCalendarTriggerRenderProps) => ReactNode} [renderTrigger] 커스텀 trigger 렌더 함수
|
|
243
|
+
* @property {(event: MouseEvent<Element>) => void} [onClick] trigger 클릭 핸들러
|
|
244
|
+
*/
|
|
245
|
+
export interface InputCalendarRangeProps {
|
|
246
|
+
/**
|
|
247
|
+
* 제어형 range 값
|
|
248
|
+
*/
|
|
249
|
+
value?: CalendarRangeValue;
|
|
250
|
+
/**
|
|
251
|
+
* 비제어 range 초기값
|
|
252
|
+
*/
|
|
253
|
+
defaultValue?: CalendarRangeValue;
|
|
254
|
+
/**
|
|
255
|
+
* range 값 변경 핸들러
|
|
256
|
+
*/
|
|
257
|
+
onChange?: CalendarRangeOnChange;
|
|
258
|
+
/**
|
|
259
|
+
* onChange 별칭; 외부 파이프라인 시 사용
|
|
260
|
+
*/
|
|
261
|
+
onValueChange?: CalendarRangeOnChange;
|
|
262
|
+
/**
|
|
263
|
+
* 읽기 전용 여부
|
|
264
|
+
*/
|
|
265
|
+
readOnly?: boolean;
|
|
266
|
+
/**
|
|
267
|
+
* disabled 여부
|
|
268
|
+
*/
|
|
269
|
+
disabled?: boolean;
|
|
270
|
+
/**
|
|
271
|
+
* Mantine range DatePicker 옵션
|
|
272
|
+
*/
|
|
273
|
+
datePickerProps?: CalendarRangeDatePickerProps;
|
|
274
|
+
/**
|
|
275
|
+
* 시작일 hidden input name
|
|
276
|
+
*/
|
|
277
|
+
startName?: string;
|
|
278
|
+
/**
|
|
279
|
+
* 종료일 hidden input name
|
|
280
|
+
*/
|
|
281
|
+
endName?: string;
|
|
282
|
+
/**
|
|
283
|
+
* 시작일 react-hook-form register 결과
|
|
284
|
+
*/
|
|
285
|
+
startRegister?: UseFormRegisterReturn;
|
|
286
|
+
/**
|
|
287
|
+
* 종료일 react-hook-form register 결과
|
|
288
|
+
*/
|
|
289
|
+
endRegister?: UseFormRegisterReturn;
|
|
290
|
+
/**
|
|
291
|
+
* trigger 내부 placeholder
|
|
292
|
+
*/
|
|
293
|
+
placeholder?: string;
|
|
294
|
+
/**
|
|
295
|
+
* trigger input priority
|
|
296
|
+
* - table일 때 Trigger는 left icon 배치를 사용한다.
|
|
297
|
+
*/
|
|
298
|
+
priority?: InputPriority;
|
|
299
|
+
/**
|
|
300
|
+
* trigger input state
|
|
301
|
+
*/
|
|
302
|
+
state?: InputState;
|
|
303
|
+
/**
|
|
304
|
+
* 커스텀 header 콘텐츠
|
|
305
|
+
*/
|
|
306
|
+
header?: ReactNode;
|
|
307
|
+
/**
|
|
308
|
+
* 커스텀 footer 콘텐츠
|
|
309
|
+
*/
|
|
310
|
+
footer?: ReactNode;
|
|
311
|
+
/**
|
|
312
|
+
* calendar 열림 제어 여부
|
|
313
|
+
* - 지정되면 제어형(open state controlled)으로 동작한다.
|
|
314
|
+
*/
|
|
315
|
+
calendarOpened?: boolean;
|
|
316
|
+
/**
|
|
317
|
+
* trigger id
|
|
318
|
+
*/
|
|
319
|
+
id?: string;
|
|
213
320
|
/**
|
|
214
321
|
* 커스텀 trigger 슬롯
|
|
215
322
|
*/
|
|
@@ -218,6 +325,10 @@ export interface InputCalendarProps extends InputCalendarTriggerProps {
|
|
|
218
325
|
* 커스텀 trigger 렌더 함수
|
|
219
326
|
*/
|
|
220
327
|
renderTrigger?: (props: InputCalendarTriggerRenderProps) => ReactNode;
|
|
328
|
+
/**
|
|
329
|
+
* trigger 클릭 핸들러
|
|
330
|
+
*/
|
|
331
|
+
onClick?: (event: MouseEvent<Element>) => void;
|
|
221
332
|
}
|
|
222
333
|
|
|
223
334
|
/**
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { dayjs } from "../../../init/dayjs";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CalendarColumns,
|
|
4
|
+
CalendarRangeValue,
|
|
5
|
+
CalendarValue,
|
|
6
|
+
} from "../../calendar";
|
|
3
7
|
|
|
4
8
|
const DATE_FORMAT = "YYYY-MM-DD";
|
|
5
9
|
|
|
@@ -9,6 +13,12 @@ const DATE_FORMAT = "YYYY-MM-DD";
|
|
|
9
13
|
*/
|
|
10
14
|
export const createEmptyValue = (): CalendarValue => null;
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Calendar range에 빈 값을 선언한다.
|
|
18
|
+
* @returns {CalendarRangeValue} 시작/종료가 모두 비어 있는 range tuple
|
|
19
|
+
*/
|
|
20
|
+
export const createEmptyRangeValue = (): CalendarRangeValue => [null, null];
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* Date 객체를 YYYY-MM-DD 문자열로 포맷한다.
|
|
14
24
|
* @param {Date | string | null} date 포맷 대상
|
|
@@ -67,6 +77,36 @@ export const formatTriggerValue = (
|
|
|
67
77
|
return parsed.format(format);
|
|
68
78
|
};
|
|
69
79
|
|
|
80
|
+
/**
|
|
81
|
+
* range trigger에 표시할 문자열을 계산한다.
|
|
82
|
+
* @param {CalendarRangeValue} value 현재 range tuple
|
|
83
|
+
* @param {string} [format="YYYY-MM-DD"] dayjs 표시 포맷
|
|
84
|
+
* @returns {string} range trigger 표시 문자열
|
|
85
|
+
* @example
|
|
86
|
+
* formatRangeTriggerValue(["2026-02-23", "2026-02-27"]);
|
|
87
|
+
*/
|
|
88
|
+
export const formatRangeTriggerValue = (
|
|
89
|
+
value: CalendarRangeValue,
|
|
90
|
+
format = DATE_FORMAT,
|
|
91
|
+
) => {
|
|
92
|
+
const start = formatTriggerValue(value[0], format);
|
|
93
|
+
const end = formatTriggerValue(value[1], format);
|
|
94
|
+
|
|
95
|
+
if (!start && !end) {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!end) {
|
|
100
|
+
return `${start} ~`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!start) {
|
|
104
|
+
return `~ ${end}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return `${start} ~ ${end}`;
|
|
108
|
+
};
|
|
109
|
+
|
|
70
110
|
/**
|
|
71
111
|
* columns 값을 Mantine numberOfColumns와 맞춘다.
|
|
72
112
|
* @param {CalendarColumns} columns 열 개수
|