TimeRangeFieldBase
Data Input
시작·종료 시간 입력과 선택 패널을 묶은 시간 범위 필드.
Usage
운영 시간이나 수업 시간처럼 시작과 종료 시간을 함께 입력할 때 사용한다.
import
import
import { TimeRangeFieldBase } from '@mildang/design-system/TimeRangeField';예제를 복사해 쓸 때 필요한 준비
• @mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.
API Reference
TimeRangeFieldBase Props
Prop
Type
Default
string
지정 안 함
boolean
지정 안 함
string
지정 안 함
ReactNode
지정 안 함
boolean
지정 안 함
boolean
지정 안 함
ReactNode
지정 안 함
ReactNode
지정 안 함
string
지정 안 함
boolean
지정 안 함
(value: string) => string
지정 안 함
string
지정 안 함
string
지정 안 함
string
지정 안 함
boolean
지정 안 함
boolean
false
ReactNode
지정 안 함
string
지정 안 함
string
지정 안 함
ReactNode
지정 안 함
1 | 5 | 10 | 15 | 30
지정 안 함
boolean
지정 안 함
FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>
지정 안 함
(value: TimeRangeValue) => void
지정 안 함
FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>
지정 안 함
FormEventHandler<HTMLInputElement | HTMLTextAreaElement>
지정 안 함
KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>
지정 안 함
KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>
지정 안 함
ReactNode
지정 안 함
boolean
지정 안 함
ReactNode
~
ReactNode
지정 안 함
"xs" | "sm" | "md" | "lg" | "xl"
md
ReactNode
지정 안 함
boolean
지정 안 함
ReactNode
지정 안 함
ReactNode
지정 안 함
string
지정 안 함
boolean
지정 안 함
"HH AA" | "HH:MM AA" | "HH:MM:SS AA" | "HH:MM:SS.MSS AA" | "HH:MM:SS.MSS" | "HH:MM:SS" | "HH:MM" | "HH" | "MM:SS.MSS" | "MM:SS" | "SS.MSS"
지정 안 함
ReactNode
지정 안 함
TimeRangeValue
지정 안 함
boolean
지정 안 함
같은 패밀리
@mildang/design-system/TimeRangeField 에서 같이 내보내는 컴포넌트다.
표시 형식
formatDisplay 를 넘기지 않으면 닫힌 입력에는 12시간제 + 오전/오후 표기가 자동으로 뜬다 —
TimeField 와 동일한 useFormatDateTime({ type: 'time' }) 경로다(자세한 내용은 TimeField
문서의 "표시 형식" 참고). 시작 · 종료 두 입력에 동일하게 적용된다.
Full-time 잠금
Full-time 은 range 전체 상태다 (start 만 종일 · end 만 종일은 없음). caller 가
onFullTimeChange 를 넘기면 두 picker 의 footer 좌측에 체크박스가 렌더되고, 체크 시:
start→fullTimeStartValue(기본"00:00")end→fullTimeEndValue(기본"23:59")- 두 picker 의 wheel 은 조작이 막히고 (색상은 평소와 동일)
- 잠금값이
minuteStep배수가 아니어도(23:59등) 잠금 중에는 wheel 이 자동으로 1분 단위로 전환돼 정확히 표시된다
해제 시 사용자가 잠금 전 지정했던 값(snapshot)이 복원된다.
| 도메인 | 설정 | 체크 시 잠금 결과 |
|---|---|---|
| 광고 스케줄 (24시간) | 기본값 | start=00:00, end=23:59 |
| 학원 운영시간 | fullTimeStartValue="09:00" fullTimeEndValue="22:00" | start=09:00, end=22:00 |
| 오전 근무 | fullTimeStartValue="08:00" fullTimeEndValue="12:30" | start=08:00, end=12:30 |
라벨/헬퍼/에러 — 통합 vs per-side
tsx
// 통합 (range 전체에 하나씩)
const unified = {
label: '운영 시간',
helperText: '영업 개시 ~ 마감',
required: true,
sideContent: '(ReactNode) 선택사항 아님',
error: hasError,
};
// per-side (각 입력에 개별)
const perSide = {
startLabel: '개점',
endLabel: '마감',
startHelperText: '10:00 이후',
endHelperText: '24:00 이전',
startError: startMissing,
endError: endMissing,
};- 통합 props 는 두 입력 위/아래에 한 번만 렌더된다. range 전체를 한 덩어리로 취급할 때 쓴다.
- per-side props 는 각
TimeFieldBase안에서 개별 렌더된다. start / end 별로 문제 · 안내를 세분화할 때 쓴다. - 둘은 공존 가능하다. 각자 필요한 것만 지정하면 된다.
구성요소
| 컴포넌트 | 역할 |
|---|---|
TimeRangeField | TanStack Form 필드로 쓰는 form-integrated wrapper (field.TimeRangeField) |
TimeRangeFieldBase | 두 TimeFieldBase 를 ' ~ ' 로 결합. label / helperText / error / picker / Full-time 통합 상태 관리 (순서 검증은 하지 않음) |
TimeRangePicker | range 전용 wheel picker. footer 좌측 Full-time 체크박스 슬롯(핸들러 유무로 노출 판단). 잠금 중 wheel 비활성화 + 1분 단위 자동 전환 |
예제
범위 입력과 선택 패널
평상시(닫힌) 상태의 시작·종료 두 칸이 본체입니다. 선택 패널은 TimeRangePicker 카드에서 봅니다.
import { TimeRangeFieldBase } from '@mildang/design-system/TimeRangeField';
/**
* 평상시(닫힌) 상태의 완성형 시간 범위 필드. 시작·종료 입력 두 칸이 본체다.
*
* 선택 패널을 함께 열어 두지 않는 이유는 `TimeField/examples/TimeFieldOverview.example.tsx`
* 주석과 같다 — 패널은 `TimeRangePicker` 카드가 따로 보여준다.
*/
export default function TimeRangeFieldOverviewExample() {
return <TimeRangeFieldBase value={{ start: '09:00', end: '18:00' }} />;
}
크기와 비활성 상태
xs부터 xl까지의 높이와 오류·비활성을 함께 비교합니다.
import { TimeRangeFieldBase } from '@mildang/design-system/TimeRangeField';
import { VStack } from '@mildang/styled-system/jsx';
export default function TimeRangeFieldSizesExample() {
return (
<VStack alignItems="flex-start" gap="12">
<TimeRangeFieldBase size="xs" value={{ start: '09:00', end: '18:00' }} />
<TimeRangeFieldBase size="sm" value={{ start: '09:00', end: '18:00' }} />
<TimeRangeFieldBase size="md" value={{ start: '09:00', end: '18:00' }} />
<TimeRangeFieldBase size="md" value={{ start: '09:00', end: '18:00' }} error />
<TimeRangeFieldBase size="lg" value={{ start: '09:00', end: '18:00' }} disabled />
<TimeRangeFieldBase size="xl" value={{ start: '09:00', end: '18:00' }} />
</VStack>
);
}
editable + timeMode 비교
import { Stack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const TimeRangeFieldBaseEditableWithTimeModeExample = () => (
<Stack gap="20">
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
{'timeMode="HH:MM" (기본)'}
</Text>
<TimeRangeDemo editable timeMode="HH:MM" value={{ start: '09:00', end: '18:00' }} />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
{'timeMode="HH:MM:SS"'}
</Text>
<TimeRangeDemo editable timeMode="HH:MM:SS" value={{ start: '09:00', end: '18:00' }} />
</Stack>
</Stack>
);
export default TimeRangeFieldBaseEditableWithTimeModeExample;
커스터마이즈 · minuteStep
import { Stack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const TimeRangeFieldBaseCustomMinuteStepExample = () => (
<Stack gap="20">
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
minuteStep=5 (기본) · 분 = 00, 05, 10, ..., 55
</Text>
<TimeRangeDemo minuteStep={5} value={{ start: '09:00', end: '18:00' }} />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
minuteStep=15 · 분 = 00, 15, 30, 45
</Text>
<TimeRangeDemo minuteStep={15} value={{ start: '09:00', end: '18:00' }} />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
minuteStep=30 · 분 = 00, 30
</Text>
<TimeRangeDemo minuteStep={30} value={{ start: '09:00', end: '18:00' }} />
</Stack>
</Stack>
);
export default TimeRangeFieldBaseCustomMinuteStepExample;
Demo
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseDemoExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => <TimeRangeDemo {...args} />;
export default function TimeRangeFieldBaseDemoExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseDemoExampleRender(mergedProps);
}
WithValue
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseWithValueExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => <TimeRangeDemo {...args} value={{ start: '09:00', end: '18:00' }} />;
export default function TimeRangeFieldBaseWithValueExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseWithValueExampleRender(mergedProps);
}
Full-time 초기값
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseFullTimePresetExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => <TimeRangeDemo {...args} value={{ start: '00:00', end: '23:59' }} />;
export default function TimeRangeFieldBaseFullTimePresetExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseFullTimePresetExampleRender(mergedProps);
}
label · helperText
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseWithLabelExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="운영 시간"
helperText="영업 개시 ~ 마감 시각"
value={{ start: '10:30', end: '14:00' }}
/>
);
export default function TimeRangeFieldBaseWithLabelExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseWithLabelExampleRender(mergedProps);
}
required + sideContent
import type { ComponentProps } from 'react';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseRequiredWithLabelSuffixExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="근무 시간"
required
sideContent={
<Text variant="caption-lg-medium" color="neutral.text.lowest">
선택사항 아님
</Text>
}
value={{ start: '09:00', end: '18:00' }}
/>
);
export default function TimeRangeFieldBaseRequiredWithLabelSuffixExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseRequiredWithLabelSuffixExampleRender(mergedProps);
}
error 상태
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseErrorStateExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => <TimeRangeDemo {...args} label="상영 시간" error helperText="시간을 선택하세요." />;
export default function TimeRangeFieldBaseErrorStateExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseErrorStateExampleRender(mergedProps);
}
editable (maskito 타이핑 모드)
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseEditableModeExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo {...args} editable label="근무 시간" value={{ start: '09:00', end: '18:00' }} />
);
export default function TimeRangeFieldBaseEditableModeExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseEditableModeExampleRender(mergedProps);
}
per-side · 개별 라벨
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBasePerSideLabelsExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo {...args} startLabel="개점" endLabel="마감" value={{ start: '10:00', end: '22:00' }} />
);
export default function TimeRangeFieldBasePerSideLabelsExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBasePerSideLabelsExampleRender(mergedProps);
}
per-side · 개별 helperText
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBasePerSideHelperTextExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="영업 시간"
startHelperText="개점 시각"
endHelperText="마감 시각"
value={{ start: '10:00', end: '22:00' }}
/>
);
export default function TimeRangeFieldBasePerSideHelperTextExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBasePerSideHelperTextExampleRender(mergedProps);
}
per-side · 개별 error
import type { ComponentProps } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBasePerSideErrorExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<Stack gap="20">
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
end 쪽만 error + 개별 helperText
</Text>
<TimeRangeDemo
{...args}
label="회의 시간"
endError
endHelperText="종료 시각을 선택하세요."
value={{ start: '14:00' }}
/>
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
start 쪽만 error
</Text>
<TimeRangeDemo
{...args}
label="회의 시간"
startError
startHelperText="시작 시각을 선택하세요."
value={{ end: '16:00' }}
/>
</Stack>
</Stack>
);
export default function TimeRangeFieldBasePerSideErrorExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBasePerSideErrorExampleRender(mergedProps);
}
통합 + per-side 조합
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseCombinedUnifiedAndPerSideExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="영업 시간"
helperText="정기 운영일 기준"
startLabel="개점"
endLabel="마감"
startHelperText="10:00 이후"
endHelperText="24:00 이전"
value={{ start: '10:00', end: '22:00' }}
/>
);
export default function TimeRangeFieldBaseCombinedUnifiedAndPerSideExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseCombinedUnifiedAndPerSideExampleRender(mergedProps);
}
자정 걸침 range
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseOvernightRangeExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="야간 근무"
helperText="예: 22:00 ~ 06:00 (다음날 06시)"
value={{ start: '22:00', end: '06:00' }}
/>
);
export default function TimeRangeFieldBaseOvernightRangeExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseOvernightRangeExampleRender(mergedProps);
}
커스터마이즈 · 라벨
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseCustomLabelsExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => <TimeRangeDemo {...args} label="노출 시간" fullTimeLabel="24시간 노출" />;
export default function TimeRangeFieldBaseCustomLabelsExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseCustomLabelsExampleRender(mergedProps);
}
커스터마이즈 · separator
import type { ComponentProps } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseCustomSeparatorExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<Stack gap="20">
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
{'기본 · "~"'}
</Text>
<TimeRangeDemo {...args} value={{ start: '09:00', end: '18:00' }} />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
{'separator="부터"'}
</Text>
<TimeRangeDemo {...args} value={{ start: '09:00', end: '18:00' }} separator="부터" />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
{'separator="→"'}
</Text>
<TimeRangeDemo {...args} value={{ start: '09:00', end: '18:00' }} separator="→" />
</Stack>
</Stack>
);
export default function TimeRangeFieldBaseCustomSeparatorExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseCustomSeparatorExampleRender(mergedProps);
}
커스터마이즈 · Full-time 잠금 값
import type { ComponentProps } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { Text } from '@mildang/design-system/Text';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { css } from '@mildang/styled-system/css';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseCustomFullTimeRangeExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<Stack gap="20">
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
기본 · 하루 종일 (00:00 ~ 23:59)
</Text>
<TimeRangeDemo {...args} fullTimeLabel="하루 종일" />
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
학원 운영시간 · 09:00 ~ 22:00
</Text>
<TimeRangeDemo
{...args}
label="운영 시간"
fullTimeLabel="정규 운영시간"
fullTimeStartValue="09:00"
fullTimeEndValue="22:00"
/>
</Stack>
<Stack gap="4">
<Text variant="caption-lg-medium" color="neutral.text.lowest">
minuteStep=15 + 잠금값이 15 배수 아님 (08:07 ~ 22:53) — 잠금 중에만 wheel 이 1분 단위로 전환되어
정확히 표시
</Text>
<TimeRangeDemo
{...args}
minuteStep={15}
fullTimeLabel="특수 시간대"
fullTimeStartValue="08:07"
fullTimeEndValue="22:53"
/>
</Stack>
</Stack>
);
export default function TimeRangeFieldBaseCustomFullTimeRangeExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseCustomFullTimeRangeExampleRender(mergedProps);
}
커스터마이즈 · 모두 조합
import type { ComponentProps } from 'react';
import { TimeRangeFieldBase, type TimeRangeValue } from '@mildang/design-system/TimeRangeField';
import { useState } from 'react';
import { Stack } from '@mildang/styled-system/jsx';
import { css } from '@mildang/styled-system/css';
import { Text } from '@mildang/design-system/Text';
/* ─── Helper ─── */
function TimeRangeDemo(props: React.ComponentProps<typeof TimeRangeFieldBase>) {
const [value, setValue] = useState<TimeRangeValue | undefined>(props.value);
return (
<Stack gap="8" className={css({ width: '520px' })}>
<TimeRangeFieldBase {...props} value={value} onChange={setValue} />
<Text variant="caption-lg-medium" color="neutral.text.lowest">
value: {JSON.stringify(value ?? {})}
</Text>
</Stack>
);
}
const STORY_DEFAULT_ARGS = { ...({}), ...({}) } as ComponentProps<typeof TimeRangeDemo>;
const TimeRangeFieldBaseAllCustomizedExampleRender = (args: ComponentProps<typeof TimeRangeDemo>) => (
<TimeRangeDemo
{...args}
label="노출 시간"
helperText="캠페인이 노출될 시간대"
separator="→"
fullTimeLabel="24시간 노출"
fullTimeStartValue="00:00"
fullTimeEndValue="23:59"
minuteStep={30}
placeholder="선택"
/>
);
export default function TimeRangeFieldBaseAllCustomizedExample(props: Partial<ComponentProps<typeof TimeRangeDemo>>) {
const mergedProps = { ...STORY_DEFAULT_ARGS, ...props } as ComponentProps<typeof TimeRangeDemo>;
return TimeRangeFieldBaseAllCustomizedExampleRender(mergedProps);
}