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

autoComplete

string

지정 안 함

autoFocus

boolean

지정 안 함

className

string

지정 안 함

description

ReactNode

지정 안 함

editable

boolean

지정 안 함

endError

boolean

지정 안 함

endHelperText

ReactNode

지정 안 함

endLabel

ReactNode

지정 안 함

endName

string

지정 안 함

error

boolean

지정 안 함

formatDisplay

(value: string) => string

지정 안 함

fullTimeEndValue

string

지정 안 함

fullTimeLabel

string

지정 안 함

fullTimeStartValue

string

지정 안 함

fullWidth

boolean

지정 안 함

ghost

boolean

false

helperText

ReactNode

지정 안 함

inputContainerClassName

string

지정 안 함

inputElementClassName

string

지정 안 함

label

ReactNode

지정 안 함

minuteStep

1 | 5 | 10 | 15 | 30

지정 안 함

multiple

boolean

지정 안 함

onBlur

FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>

지정 안 함

onChange

(value: TimeRangeValue) => void

지정 안 함

onFocus

FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>

지정 안 함

onInvalid

FormEventHandler<HTMLInputElement | HTMLTextAreaElement>

지정 안 함

onKeyDown

KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>

지정 안 함

onKeyUp

KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>

지정 안 함

outEndAdornment

ReactNode

지정 안 함

required

boolean

지정 안 함

separator

ReactNode

~

sideContent

ReactNode

지정 안 함

size

"xs" | "sm" | "md" | "lg" | "xl"

md

startAdornment

ReactNode

지정 안 함

startError

boolean

지정 안 함

startHelperText

ReactNode

지정 안 함

startLabel

ReactNode

지정 안 함

startName

string

지정 안 함

success

boolean

지정 안 함

timeMode

"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"

지정 안 함

triggerIcon

ReactNode

지정 안 함

value

TimeRangeValue

지정 안 함

warning

boolean

지정 안 함

같은 패밀리

@mildang/design-system/TimeRangeField 에서 같이 내보내는 컴포넌트다.

표시 형식

formatDisplay 를 넘기지 않으면 닫힌 입력에는 12시간제 + 오전/오후 표기가 자동으로 뜬다 — TimeField 와 동일한 useFormatDateTime({ type: 'time' }) 경로다(자세한 내용은 TimeField 문서의 "표시 형식" 참고). 시작 · 종료 두 입력에 동일하게 적용된다.

Full-time 잠금

Full-time 은 range 전체 상태다 (start 만 종일 · end 만 종일은 없음). caller 가 onFullTimeChange 를 넘기면 두 picker 의 footer 좌측에 체크박스가 렌더되고, 체크 시:

  • startfullTimeStartValue (기본 "00:00")
  • endfullTimeEndValue (기본 "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 별로 문제 · 안내를 세분화할 때 쓴다.
  • 둘은 공존 가능하다. 각자 필요한 것만 지정하면 된다.

구성요소

컴포넌트역할
TimeRangeFieldTanStack Form 필드로 쓰는 form-integrated wrapper (field.TimeRangeField)
TimeRangeFieldBaseTimeFieldBase' ~ ' 로 결합. label / helperText / error / picker / Full-time 통합 상태 관리 (순서 검증은 하지 않음)
TimeRangePickerrange 전용 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);
}