본문으로 건너뛰기

PlTimePicker

열에서 시각을 고릅니다. 열인 이유는, 시간 picker가 실제로 받는 질문에 답하는 모양이 열이기 때문입니다.

React
tsx
import { PlTimePicker } from 'plass-ui';

<PlTimePicker label="Doors" placeholder="Pick a time" minuteStep={15} />;
dart
import 'package:plass_ui/plass_ui.dart';

PlTimePicker(
  label: const Text('Doors'),
  minuteStep: 15,
  value: doors,
  onChanged: (DateTime? next) => setState(() => doors = next),
);

열들은 트리 밖으로 자기를 들어 올리므로 picker 위에 Overlay가 필요합니다.

Props

Prop타입기본값설명
valueDate | null선택된 시각. Date이므로 날짜도 함께 지닙니다 — referenceDate를 보세요
defaultValueDate | nulluncontrolled일 때 시작하는 시각
onValueChange(value: Date | null) => void값이 바뀔 때 호출됩니다
referenceDateDatetoday아직 값이 없을 때 고른 시각이 얹히는 날. picker가 마운트되어 있는 동안 고정입니다 — 자정을 넘겨 열어 둔 팝업이 값을 다른 날로 옮기면 안 되니까요
minTimeDate | null고를 수 있는 가장 이른 시각. 시계만 읽습니다
maxTimeDate | null고를 수 있는 가장 늦은 시각
hour12booleanAM/PM 열이 붙은 12시간 다이얼. 기본은 locale이 하는 대로입니다
showSecondsbooleanfalse초 열을 더합니다
hourStepnumber1각 열의 행 간격
minuteStepnumber1hourStep를 보세요
secondStepnumber1hourStep를 보세요
shouldDisableTime(value: Date, unit: TimeUnit) => boolean개별 행을 막습니다. 열마다 행마다, 그 행이 만들어 낼 시각과 그 행이 속한 열을 받아 한 번씩 호출됩니다 — "오후는 안 됨"만큼 성길 수도, 1분만큼 촘촘할 수도 있습니다
showNowButtonbooleantrue푸터에 지금으로 가는 지름길을 둡니다
variant공통'solid' | 'glass' | 'ghost''glass'trigger의 재질. PlTextField와 같은 껍데기를 씁니다. solid는 시트에 파인 우물
size공통'xs' | 'sm' | 'md' | 'lg' | 'xl''md'높이와 타입 스케일
color공통'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'의미론적 색 역할. 임의 색상값은 받지 않습니다
density공통'default' | 'compact''default'여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통0 | 1 | 2 | 30trigger의 그림자 깊이. 팝업은 3으로 고정입니다 — 팝업은 정말로 페이지 위에 떠 있습니다
openboolean팝업이 열려 있는지. onOpenChange와 함께 controlled로 씁니다
defaultOpenbooleanfalse팝업이 열린 채로 시작할지
onOpenChange(open: boolean) => void팝업이 열리고 닫힐 때 호출됩니다
localestringBCP 47 태그. 월과 요일 이름, 헤더 두 버튼의 순서, trigger가 날짜를 쓰는 방식을 정합니다. 기본은 브라우저의 것
formatIntl.DateTimeFormatOptions{ hour: 'numeric', minute: '2-digit' }trigger가 시각을 쓰는 방식. Intl로 그대로 넘어갑니다. showSeconds면 초가 붙습니다
placeholderReactNode아무것도 고르지 않았을 때 trigger에 보이는 내용
clearablebooleanfalse값을 비우는 ×를 보여 줍니다
closeOnSelectbooleanfalse어느 열이든 건드리는 즉시 팝업을 닫습니다. PlDatePicker와 달리 기본이 false인 건 시각이 답 두 개이고, 첫 답에 닫으면 9:30을 고르는 데 팝업을 두 번 열어야 하기 때문입니다
labelsPartial<PlPickerLabels>picker가 스스로 말하는 문자열들. 전부 영어 기본값이 있습니다. 날짜 이름은 여기 없습니다 — 그건 Intl이 압니다
labelReactNodetrigger 위 라벨
descriptionReactNodetrigger 아래 보조 설명
errorReactNode오류 메시지. 존재 자체가 invalid 상태를 만듭니다
invalidboolean메시지 없이 invalid로 만듭니다
startIconReactNode값 앞의 글리프. 기본은 달력(또는 시계)입니다
fullWidthbooleanfalse컨테이너 너비만큼 확장
readOnlybooleanfalse값은 보이지만 바꿀 수 없고, 팝업도 열리지 않습니다
disabledbooleanfalse사용 불가
requiredbooleanfalse폼 제출 전에 값이 있어야 하는지
namestring폼 제출 시 필드를 식별합니다. HH:MM으로, showSeconds면 HH:MM:SS로 보냅니다
classNames{ label?, control?, description?, error?: string }className이 닿지 않는 부분에 붙는 class. control은 실제로 조작하는 부분입니다
Prop타입기본값설명
value * DateTime?선택된 시각. DateTime이므로 날짜도 함께 지닙니다
onChangedValueChanged<DateTime?>?고른 시각과 함께 호출됩니다. 비우면 null입니다
openbool?팝업이 열려 있는지. onOpenChange와 함께 controlled로 씁니다
onOpenChangedValueChanged<bool>?열들이 열리거나 닫혀야 할 때 호출됩니다
referenceDateDateTime?now아직 값이 없을 때 고른 시각이 얹히는 날. picker가 마운트되어 있는 동안 고정입니다 — 자정을 넘겨 열어 둔 팝업이 값을 다른 날로 옮기면 안 되니까요
minTimeDateTime?고를 수 있는 가장 이른 시각. 시계만 읽습니다
maxTimeDateTime?고를 수 있는 가장 늦은 시각
hour12boolfalseAM/PM 열이 붙은 12시간 다이얼. React가 locale에서 가져오는 자리에서 여기서는 그냥 false입니다 — 물어볼 Intl이 없고, 켰을 때 쓰는 말은 PlDateNames의 am/pm입니다
showSecondsboolfalse초 열을 더합니다
hourStepint1각 열의 행 간격
minuteStepint1hourStep를 보세요
secondStepint1hourStep를 보세요
shouldDisableTimebool Function(DateTime value, PlassTimeUnit unit)?개별 행을 막습니다. 열마다 행마다, 그 행이 만들어 낼 시각과 그 행이 속한 열을 받아 한 번씩 호출됩니다 — "오후는 안 됨"만큼 성길 수도, 1분만큼 촘촘할 수도 있습니다
namesPlDateNamesPlDateNames.englishAM과 PM이 나오는 곳
labelsPlPickerLabelsPlPickerLabels.englishpicker가 스스로 말하는 문자열들. 전부 영어 기본값이 있습니다. 날짜 이름은 여기 없습니다 — 그건 Intl이 압니다
formatValueString Function(DateTime value)?trigger가 시각을 쓰는 방식. 빼면 H:MM이고, 초와 오전/오후가 켜져 있으면 함께 붙습니다
placeholderWidget?아무것도 고르지 않았을 때 trigger에 보이는 내용
clearableboolfalse값을 비우는 ×를 보여 줍니다
showNowButtonbooltrue푸터에 지금으로 가는 지름길을 둡니다
closeOnSelectboolfalse어느 열이든 건드리는 즉시 팝업을 닫습니다. PlDatePicker와 달리 기본이 false인 건 시각이 답 두 개이고, 첫 답에 닫으면 9:30을 고르는 데 팝업을 두 번 열어야 하기 때문입니다
variant공통PlassVariantPlassVariant.glasstrigger의 재질. PlTextField와 같은 껍데기를 씁니다. solid는 시트에 파인 우물
size공통PlassSizePlassSize.md높이와 타입 스케일
color공통PlassColorPlassColor.primary의미론적 색 역할. 임의 색상값은 받지 않습니다
density공통PlassDensityPlassDensity.standard여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통int0trigger의 그림자 깊이. 팝업은 3으로 고정입니다 — 팝업은 정말로 페이지 위에 떠 있습니다
labelWidget?trigger 위 라벨
descriptionWidget?trigger 아래 보조 설명
errorWidget?오류 메시지. 존재 자체가 invalid 상태를 만듭니다
invalidbool?메시지 없이 invalid로 만듭니다
startIconWidget?값 앞의 글리프. 기본은 달력(또는 시계)입니다
fullWidthboolfalse컨테이너 너비만큼 확장
readOnlyboolfalse값은 보이지만 바꿀 수 없고, 팝업도 열리지 않습니다
disabledboolfalse사용 불가
semanticLabelString?보이는 label이 없는 trigger를 스크린 리더가 부를 이름
focusNodeFocusNode?포커스를 밖에서 제어할 때 넘깁니다
autofocusboolfalse트리에 들어가면서 포커스를 가져갑니다

나머지 <div> 속성은 field 래퍼로 그대로 통과합니다. color는 위 표의 color와 겹쳐서, defaultValue는 DOM 속성이 아니라 값으로 쓰기 때문에, children은 열들이 곧 컴포넌트이기 때문에 제외했습니다.

className은 label과 control, 그 아래 두 줄을 함께 담는 stack에 붙습니다. 그 안쪽 네 부분에 닿는 것이 classNames입니다: label, control(트리거), description, error.

picker는 controlled입니다. valueonChanged를 함께 주고, null은 아무것도 고르지 않은 picker입니다.

PlDatePickerlocale과 날짜 라이브러리의 부재에 대해 말한 것은 여기서도 성립합니다. 시계가 12시간 다이얼인지, 오전/오후를 뭐라 부르는지는 Intl이 정합니다.

PlDatePicker가 날짜 라이브러리의 부재에 대해 말한 것은 여기서도 성립합니다. 다른 것은 다이얼을 대신 정해 주는 것이 없다는 점입니다. hour12는 기본이 그냥 false이고, 켰을 때 쓰는 말은 PlDateNames.am.pm입니다.

다이얼이 아니라 열입니다

"9시 반"은 두 열에 대한 두 번의 눈길입니다. "정각이면 아무 때나"는 아예 건드리지 않는 열입니다. 시계 문자판은 더 예쁘고, 읽으려면 transform이 필요하며, 어느 질문에도 더 빨리 답하지 못합니다. 그리고 이 라이브러리는 컨트롤에 transform을 걸지 않습니다.

각 열에서 고른 행은 열릴 때 한 번 화면 안으로 스크롤됩니다. 장식이 아닙니다. 값이 45인데 00에서 열리는 60분짜리 열은 자기 답을 숨긴 것입니다.

경계

작동하는 시간 picker와 짜증나는 시간 picker를 가르는 지점입니다. 경계는 한 행이 대표하는 구간 에 대고 검사하지, 그 안의 한 순간에 대고 검사하지 않습니다.

minTime이 09:30이면 시각 9는 09:00:00–09:59:59를 덮고 그것은 허용 범위와 겹치므로 그대로 남습니다. 그리고 00부터 25가 흐려지는 곳은 분 열입니다. 후보 전체를 비교하면 9가 통째로 사라지고 9시 반은 닿을 수 없게 됩니다.

문자열도, 분의 개수도 아닙니다. 이 라이브러리에서 순간을 지니는 다른 모든 것이 Date이고, 맨 시각에는 서머타임 경계를 넘었다는 사실을 기록할 자리가 없습니다. referenceDate는 맨 시각이 얹히는 날이고, picker가 마운트되어 있는 동안 고정입니다. 자정을 넘겨 열어 둔 팝업이 값을 조용히 다른 날로 옮기면 안 됩니다.

Examples

hour12

말하지 않으면 locale에서 가져옵니다.

말하지 않으면 false입니다. 이 지역이 무엇을 쓰는지 물어볼 Intl이 없습니다.

12시간 다이얼은 0, 1, 2가 아니라 12, 1, 2 … 11로 읽히고 AM/PM 열이 붙습니다. 24시간 다이얼은 00부터 23까지이고 그 열이 없습니다.

React

step 간격

hourStep, minuteStep, secondStep이 행 간격을 정합니다. 15분 단위로만 받는 예약이라면 09:07을 나중에 거절하는 대신 minuteStep={15}로 미리 말해야 합니다.

React

minTime · maxTime · shouldDisableTime

minTimemaxTime은 시계만 읽습니다. 거기 붙은 날짜는 무시됩니다. shouldDisableTime은 열마다 행마다, 그 행이 만들어 낼 시각과 그 행이 속한 열을 받아 한 번씩 호출됩니다. 규칙은 "오후는 안 됨"만큼 성길 수도, 1분만큼 촘촘할 수도 있습니다.

React

closeOnSelect

여기서는 false이고 PlDatePicker에서는 true입니다. 날은 답이 하나이고 시각은 둘입니다. 첫 답에 닫아 버리면 9:30을 고르는 데 팝업을 두 번 열어야 합니다.

열들을 읽는 동안 팝업이 떠 있으므로, 그게 그거다 라는 뜻으로 누를 것이 있어야 합니다. 그래서 푸터에 Done 이 있습니다. closeOnSelect를 켜면 할 일이 없어지므로 사라집니다.

readOnly · disabled · error

React

Accessibility

  • 각 열은 자기가 담은 단위의 이름을 달고, 각 행은 자기가 골라진 행인지를 말합니다.
  • 막힌 행은 사라지지 않고 자기 열에 남아 사용할 수 없다고 읽힙니다.
  • 각 열은 role="listbox"이고 각 행은 aria-selected를 지닌 option입니다. 막힌 행은 속성이 아니라 aria-disabled를 답니다.
  • 이름 없는 숫자 목록 셋은 보지 않는 독자에게 아무 말도 하지 않습니다. 그래서 열들 옆의 polite live region이 값이 바뀔 때마다 전체 시각을 한 문장으로 읽어 줍니다.
  • 각 열에서 고른 행은 자기 열 안에서만 화면 안으로 들어옵니다. scrollIntoView가 아니라 scrollTop을 씁니다. 전자는 문서까지 올라가며 스크롤 가능한 모든 조상을 훑고, 팝업이 열리는 그 프레임에는 곧 움직일 행을 보여 주겠다고 페이지를 맨 위로 끌어올립니다.
  • trigger는 담을 수 있는 가장 긴 시각의 너비로 붙잡혀 있습니다. 그 샘플들은 aria-hidden이고 generated content로 그려집니다.
  • name이 있으면 hidden input이 값을 로컬 HH:MM으로 담습니다. <input type="time">이 제출하는 모양이라, 그것을 이미 파싱하는 서버는 새 코드가 필요 없습니다.
  • 각 열은 자기 단위의 이름을 단 semantics container이고, 각 행은 자기가 뜻하는 것 전체로 읽힙니다: 14가 아니라 14 Hour.
  • trigger는 시각을 label에 접어 넣는 대신 semantics value 로 지닙니다.
  • 열들 옆의 live region이 값이 바뀔 때마다 전체 시각을 읽어 줍니다.

React 빌드와 다른 점

ReactFlutter이유
hour12가 locale의 다이얼을 따름기본이 false물어볼 Intl이 없습니다. 말은 PlDateNames.am / .pm이 댑니다.
format: Intl.DateTimeFormatOptionsformatValue: String Function(DateTime)PlDatePicker가 설명하는 그 거래입니다.
role="listbox"option이름 붙은 semantics container와 뜻을 말하는 행Flutter는 상태를 노드 자체에 적습니다.
hidden input, name참여할 네이티브 form 제출이 없습니다.
className, style, 네이티브 속성통과시킬 class 목록도 style 속성도 없습니다.

Released under the MIT License