ComponentsText & numbersTime
On this page
Text & numbers

Time released

A time of day typed into a field or picked from columns the field opens.

  • Import@pacit/components/time
  • Selectorpct-time

Implements the textbox and dialog of the W3C ARIA APG Date Picker Dialog pattern ↗

Type it, or pick it from the columns

time.demo.ts — the same file runs here and is highlighted under Code

Start here

Usage

One import per component, and the shortest possible use — the package itself is installed once, on the start page.

Import this component
import { PctTime } from '@pacit/components/time';
ExportedWhat it is
PctHourCycletypeunreleased'h11' | 'h12' | 'h23' | 'h24'The four clocks Intl names. h11 and h12 are twelve-hour clocks that write the first hour 0 and 12; h23 and h24 are twenty-four-hour clocks that write it 0 and 24. The value is 00:05 under all four — a cycle is how a time is written, never which time it is.
PctTimeFieldtypeunreleased'hour' | 'minute' | 'second' | 'dayPeriod'Which part of the written time a position holds.
PctTimeLettersinterfaceunreleasedThe letters a format hint is written with — one per field, from PCT_TEXTS, because they are words of the application's language and not facts about the locale (0086 §3).
PctTimeFormatinterfaceunreleasedEverything about writing and reading a time in one language, built once per locale.
pctTimeFormatfunctionunreleasedHow a time is written and read in one language, built once and shared.
PctTimeOfDaytypeunreleasedstringA time of day, written the way the platform already writes one: HH:mm, or HH:mm:ss where the field's step has seconds in it.
PctTimeOfDayPartsinterfaceunreleasedThe three fields of a time, as numbers on a 24-hour clock. second is 0 for a time written without seconds — the two shapes are one time.
isPctTimeOfDayfunctionunreleasedWhether a string is a time this library will work with: 00:00 to 23:59:59, two digits a field. A value read from a server, a query string or localStorage is a string somebody else wrote, and three of the strings they write are refused on purpose:
pctTimeOfDayfunctionunreleasedBuilds a time from its fields: HH:mm, or HH:mm:ss when second is given — 0 included, because the shape is the caller's to choose.
pctTimeOfDayPartsfunctionunreleasedThe three fields of a time. The caller has already established that it is one.
pctNowfunctionunreleasedThe time on the LOCAL clock — the one read of local time in this file, and the only one that belongs: "what time is it" is a question about where the user is standing, and every other question here is arithmetic on a time that already has an answer.
pctAddSecondsfunctionunreleasedn seconds on (or back), and the clock comes round: 23:59:30 plus 45 seconds is 00:00:15. The result keeps the shape it was given unless it has seconds that shape cannot carry — 13:05 plus an hour is 14:05, plus 30 seconds is 13:05:30.
pctAddMinutesfunctionunreleasedn minutes on (or back), and the clock comes round: 23:30 plus 45 minutes is 00:15. The shape is kept — a whole minute never moves the seconds.
pctCompareTimesfunctionunreleased-1, 0 or 1, read as seconds since midnight — so 13:05 and 13:05:00, one time in two shapes, compare equal.
pctClampTimefunctionunreleasedHeld inside the bounds, either of which may be absent — and undefined is what absent is, because that is what the FormUiControl contract's min / max are.
isPctTimeStepfunctionunreleasedWhether step — in seconds, like the native attribute — is one the columns can list: a whole number of seconds that divides a minute, a whole number of minutes that divides an hour, or a whole number of hours that divides a day. Any other — 420 seconds, seven-minute slots — makes the valid minutes depend on the hour, which columns that are fields cannot show (0086 §5).
pctTimeOnStepfunctionunreleasedWhether a time stands on the step, counted from base — the field's min when it has one, as the native element counts (0086, A11).
pctSnapToStepfunctionunreleasedThe time on the step nearest to this one, counted from base, with the clock coming round: at an hour step 23:40 is 00:00. Exactly halfway goes up, as Math.round does. The shape is kept unless the step puts seconds where it had none.
PctTimeRowinterfaceunreleasedOne row of a column, with everything the template and the walk need already decided.
PctTimeColumninterfaceunreleasedOne column — one field of the time, one role="listbox", one tab stop.
PctTimePredicatetypeunreleased(time: PctTimeOfDay) => booleanA predicate on a time of day: true refuses it. The columns ask it of every time on the step, once per change of the step, the bounds or the predicate itself.
PctTimeColumnscomponentunreleasedpct-time-columnsTime columns — one role="listbox" per field of a time, side by side: hours and minutes, seconds where the step has them, and the two halves of the day where the clock has twelve hours (0086 §4).
PctTimePanelAligntypeunreleased'start' | 'end'Which edge of the field the panel lines its own up with.
PctTimecomponentunreleasedpct-timeTime field — a text control the library formats and parses per locale, with columns of hours and minutes in a panel beside it.
PctTimeBoundtypeunreleased| PctTimeOfDay | undefined | LogicFn<TValue, PctTimeOfDay | undefined, TPathKind>A bound pctTimeBounds takes: a time, or a function of the field's context that returns one — the two shapes Angular's own min() takes. One that is not a time, undefined among them, is no bound, as it is on the field's input.
PctTimeBoundErrortypeunreleased| { readonly kind: 'minTime'What pctTimeBounds reports: minTime for a time before min, maxTime for one after max — and both at once for a time in the gap of a window across midnight, which is the native element's reading, "under and over" (0086, A12). Each carries the bound it broke, as the bound was written.
PctTimeBoundsConfiginterfaceunreleasedThe bounds of pctTimeBounds, and the sentence its errors carry — one for both, or one written from the error, which holds the bound.
pctTimeBoundsfunctionunreleasedBounds a time field in a signal form: what Angular's min() and max() do for a number, for a PctTimeOfDay — which they cannot take, and which the compiler refuses as an attribute beside [formField] (NG8022). The bounds reach the field's min / max, so the panel's walk stops at them, and the form grades the value: a time outside is minTime or maxTime, and a time in the gap of a window across midnight — min later than max, 22:00 to 06:00 — is both, as the native element reads it (0086, A12).
<pct-time label="Starts at" [(value)]="startsAt" />
Examples

Every state, running

Each example is the component itself, not a screenshot. Open the code under any of them.

The clock is the language's

One value on three clocks: twelve hours in American English, twenty-four in British, and the day period before the hour in Korean — all of it from Intl for the field's locale, and a clock forced on a reader is a locale too (en-US-u-hc-h23).

The step, and seconds

step is in seconds, as the native attribute's: 900 lists the quarters in the panel, and a step under a minute adds a column of seconds and puts them in the value. A time typed off the step is kept — it is the form's to refuse, with pctTimeOnStep.

Values: 09:15 · 09:15:30

Within bounds

min and max are times, and the panel disables the rows outside them; a min later than max is a window across midnight. A time typed outside them is kept and left to the form.

API

What it reads, and what it writes back

Read from the source at build time — the types, the defaults and the lines of documentation are the library's own.

unreleased marks 49 of them: on main, and not in @pacit/components@0.2.0 — the next release names the version.

Time field — a text control the library formats and parses per locale, with columns of hours and minutes in a panel beside it.

Inputs

NameTypeWhat it does
valuetwo-wayunreleaseddefault nullPctTimeOfDay | nullThe chosen time, or null when the field is empty.
disabledunreleaseddefault falsebooleanBlocks the control and greys it — the native disabled, so it leaves the tab order as well. With [formField] the directive writes it, as it writes every input of the FormUiControl contract.
readonlyunreleaseddefault falsebooleanKeeps the value: the input takes the native readonly and the clock button is disabled with it, while the field stays focusable.
invalidunreleaseddefault falsebooleanThe form's verdict; shown only once touched, so an empty form does not open red.
touchedunreleaseddefault falsebooleanWhether the user has left the field once; with invalid it gates the error face.
requiredunreleaseddefault falsebooleanMarks the label with the required sign; with [formField] it follows the schema's required().
errorsunreleaseddefault []readonly ValidationError.WithOptionalFieldTree[]The form's validation errors; the first one's message takes the hint's place once the field is touched.
warningsunreleaseddefault undefinedreadonly ValidationError.WithOptionalFieldTree[] | undefinedA verdict without a veto (0087): shown after the error and before the hint once the field is touched, in the warning tone, with aria-invalid untouched. Left unbound, the control reads what pctWarn() wrote for its own [formField]; bound — [] included — the list given stands.
nameunreleaseddefault ''stringThe native name — what a form submission calls the value.
minunreleaseddefault undefinedunknownThe earliest time. It belongs to the FormUiControl contract, which spells an absent bound undefined, and a signal form gives it through pctTimeBounds — the compiler refuses the attribute beside [formField].
maxunreleaseddefault undefinedunknownThe latest time — as min, a bound on the panel's walk and not a rewrite of what was typed.
stepunreleaseddefault 60numberThe step in seconds, counted from min — 60 by default, like the native attribute. It decides what the panel's columns offer (900 is the minutes 00 15 30 45), whether a seconds column exists and the value carries :ss — and nothing about typed text: a time off the step is a time, and the form's to refuse (0086 §5). A step the columns cannot list is refused with a dev-mode warning and read as 60.
labelunreleaseddefault ''stringThe visible label, rendered by the control itself when it stands outside a pct-field; inside one, the field's label is the name.
hintunreleaseddefault ''stringA line of help under the control, outside a pct-field; the first error message takes its place while the field is invalid and touched.
ariaLabelunreleaseddefault ''stringThe accessible name of a field with no visible label — an INPUT rather than an aria-label on the tag, because the textbox sits inside this template and an ARIA name on the roleless host is ignored.
ariaLabelledbyunreleaseddefault ''stringAs ariaLabel, for a name that already stands somewhere on the page.
localeunreleaseddefault ''stringOverrides the application's LOCALE_ID for this field — and with it the clock: en-US counts twelve hours and en-GB twenty-four, and en-US-u-hc-h23 forces the second on the first.
showFormatunreleaseddefault truebooleanWhether the format hint stands in the field while it is empty (hh:mm).
sizeunreleaseddefault config.defaultSizePctSizeHeight 28 / 36 / 44 px — the axis every field shares; from providePctConfig by default (req-api-config).
panelAlignunreleaseddefault 'start'PctTimePanelAlignWhich edge of the field the panel lines up with.

Outputs

NamePayloadWhen it fires
touchunreleasedvoidEmitted on blur — lets the form mark the field as touched.

What it writes on the element

The hooks a stylesheet or a test can hold. Stable by contract — the parts inventory gate fails the build when one disappears.

AttributeBound toNote
classpct-time
data-pct-sizesize()
data-pct-openopen() ? "" : null
data-pct-invalidshowInvalid() ? "" : null
data-pct-warningshowWarning() ? "" : null
data-pct-disableddisabled() ? "" : null
data-pct-malformedmalformed() ? "" : nullWhat the control knows and the form cannot: there is text in the field and it is not a time. See malformed below.
data-pct-in-fieldinField ? "" : null
Styling

Parts to select, tokens to override

Two ways in, both stable. No class names to depend on.

Parts

In a test, PctTimeHarness from @pacit/components/testing reads a part by the name below and a state by its attribute — the harness gate holds the names to the package.

PartSelectorWhat it is
control[data-pct-part="control"]the typed input
error[data-pct-part="error"]the message when the value is invalid
hint[data-pct-part="hint"]the hint under the input
label[data-pct-part="label"]the label of the typed input
panel[data-pct-part="panel"]the dialog the columns stand in
toggle[data-pct-part="toggle"]the button that opens the columns
warning[data-pct-part="warning"]the message when the value is allowed and suspect — after the error, before the hint (0087)
column[data-pct-part="column"]one column of the panel — a listbox of one field of the time
option[data-pct-part="option"]one row of a column

Tokens

Every value the component reads, with the light and the dark default. Override a token at any scope and it cascades like any custom property.

TokenStyles
--pct-time-bg{pct.surface}#ffffff · #0f172aBackground of the time field.
--pct-time-bg-disabled{pct.surface-disabled}#f1f5f9 · #1e293bBackground of the time field while disabled.
--pct-time-border{pct.border-strong}#64748bEdge colour of the time field.
--pct-time-border-disabled{pct.border}#e2e8f0 · #334155Edge colour of the time field while disabled.
--pct-time-border-focus{pct.primary}#2563eb · #60a5faEdge colour of the time field when focused.
--pct-time-border-hover{pct.text-muted}#334155 · #e2e8f0Edge colour of the time field on hover.
--pct-time-border-invalid{pct.danger}#b91c1c · #f87171Edge colour of the time field when invalid.
--pct-time-border-warning{pct.warning}#b45309 · #fbbf24Edge colour of the time field under a warning (0087).
--pct-time-column-height224pxHeight of a column.
--pct-time-column-min-width56pxMinimum width of a column — a day-period word wider than two digits widens its own column.
--pct-time-fg{pct.text}#0f172a · #ffffffText colour of the time field.
--pct-time-fg-disabled{pct.text-disabled}#64748bText colour of the time field while disabled.
--pct-time-fg-invalid{pct.danger}#b91c1c · #f87171Text colour of the time field when invalid.
--pct-time-fg-warning{pct.warning}#b45309 · #fbbf24Text colour of the warning line (0087).
--pct-time-font-size{pct.font.size.md}14pxFont size of the time field.
--pct-time-font-size-lg{pct.font.size.lg}16pxFont size of the time field at size lg.
--pct-time-font-size-sm{pct.font.size.sm}13pxFont size of the time field at size sm.
--pct-time-hint-fg{pct.text-muted}#334155 · #e2e8f0Text colour of the hint.
--pct-time-icon-size16pxSize of the icon.
--pct-time-label-fg{pct.text}#0f172a · #ffffffText colour of the label.
--pct-time-option-bg-hover{pct.surface-100}#f1f5f9 · #1e293bBackground of a row on hover.
--pct-time-option-bg-selected{pct.primary}#2563eb · #60a5faBackground of a row when selected.
--pct-time-option-fg-disabled{pct.text-disabled}#64748bText colour of a row while disabled.
--pct-time-option-fg-selected{pct.on-primary}#ffffff · #0f172aText colour of a row when selected.
--pct-time-option-font-size{pct.font.size.md}14pxFont size of a row.
--pct-time-option-font-size-lg{pct.font.size.lg}16pxFont size of a row at size lg.
--pct-time-option-font-size-sm{pct.font.size.sm}13pxFont size of a row at size sm.
--pct-time-option-font-weight-selected{pct.font.weight.medium}500Font weight of a row when selected.
--pct-time-option-height32pxHeight of a row.
--pct-time-option-height-lg36pxHeight of a row at size lg.
--pct-time-option-height-sm28pxHeight of a row at size sm.
--pct-time-option-padding-x{pct.space.3}8pxHorizontal padding of a row.
--pct-time-option-radius{pct.radius.md}8pxCorner radius of a row.
--pct-time-padding-x{pct.space.4}12pxHorizontal padding of the time field.
--pct-time-padding-x-lg{pct.space.4}12pxHorizontal padding of the time field at size lg.
--pct-time-padding-x-sm{pct.space.3}8pxHorizontal padding of the time field at size sm.
--pct-time-padding-y{pct.space.3}8pxVertical padding of the time field.
--pct-time-padding-y-lg{pct.space.4}12pxVertical padding of the time field at size lg.
--pct-time-padding-y-sm{pct.space.2}4pxVertical padding of the time field at size sm.
--pct-time-panel-bg{pct.surface}#ffffff · #0f172aBackground of the panel.
--pct-time-panel-border{pct.border-strong}#64748bEdge colour of the panel.
--pct-time-panel-gap{pct.space.2}4pxGap between the columns of the panel.
--pct-time-panel-padding-x{pct.space.3}8pxHorizontal padding of the panel.
--pct-time-panel-padding-y{pct.space.3}8pxVertical padding of the panel.
--pct-time-panel-radius{pct.radius.md}8pxCorner radius of the panel.
--pct-time-panel-shadow{pct.shadow-panel}0 8px 24px rgba(15, 23, 42, 0.14) · 0 8px 24px rgba(0, 0, 0, 0.55)Shadow of the panel.
--pct-time-placeholder-fg{pct.text-muted}#334155 · #e2e8f0Text colour of the placeholder.
--pct-time-radius{pct.radius.md}8pxCorner radius of the time field.
--pct-time-target-min{pct.target.min}24pxTouch-target floor of the time field.
--pct-time-toggle-bg-hover{pct.surface-100}#f1f5f9 · #1e293bBackground of the clock toggle on hover.
--pct-time-toggle-fg{pct.text-muted}#334155 · #e2e8f0Text colour of the clock toggle.
--pct-time-toggle-radius{pct.radius.md}8pxCorner radius of the clock toggle.
--pct-time-toggle-size20pxSize of the clock toggle.

Theming

A brand override in a few declarations. Scope it to a theme attribute, a page or one container — the component never notices.

[data-theme='brand'] {
  --pct-time-border-focus: #0f766e;
  --pct-time-option-bg-selected: #0f766e;
  --pct-time-option-fg-selected: #ffffff;
}
Type it, or pick it from the columns
Accessibility

Measured, not assumed

Pattern: the textbox and dialog of Date Picker Dialog — a plain textbox with a button beside it that opens a role="dialog" holding one named role="listbox" per field of the time. The APG has no time picker, and the example it borrows from is built around a grid this field does not have, so the claim is the part implemented and no more (0086 §7). The textbox is deliberately not a combobox: a column of hours narrows nothing — it is a second road to the same value (0035 read backwards)

Keyboard

keyeffecttest
any texttyped into the field, parsed on commitapps/sandbox-e2e/src/time.spec.ts
Tabfield → clock button → outnative
Enter on the buttonopens the panel, focus lands on the hour column at the held timeapps/sandbox-e2e/src/time.spec.ts
Tab in the panelthe next column — one stop each — and off the last, outapps/sandbox-e2e/src/time.spec.ts
Enter in a columntakes the time and closes the panel, focus back to the fieldapps/sandbox-e2e/src/time.spec.ts
Escape in the panelcloses it, focus back to the field, the value as the columns leftapps/sandbox-e2e/src/time.spec.ts, libs/components/time/src/time-field.spec.ts
Tab out of the panelcloses it, focus spliced back onto the fieldapps/sandbox-e2e/src/time.spec.ts

The columns' own map is on PctTimeColumns. The field itself adds no key of its own: what a user types is text, and the platform's own editing keys are the whole of it — the arrows do not step the segment under the caret, because the engines step it three ways (0086, A13 and A14) and a reader is never told a textbox has a value to step.

What is measured, and what is not yet

Every line is a test in three engines or a gate on every commit. The gaps are listed the same way, each with the requirement that will close it.

MeasuredARIA APG pattern in the JSDoclibs/components/time/src/time-field.ts — the field's reasons and the decision behind them in the class comment; the columns name their listbox in theirs
MeasuredKeyboard mapapps/sandbox-e2e/src/time.spec.ts — the panel's map in three engines; libs/components/time/src/time-field.spec.ts — Escape, Tab off either end and Tab inside, key by key
Measuredaxe auditapps/sandbox-e2e/src/a11y.spec.ts — the /time view and /all, in both directions, and separately the open panel with the field it belongs to, whole-page: the toggle's aria-controls into the overlay container and the three named listboxes pointing at their rows
MeasuredVisual screenshotapps/sandbox-e2e/src/visual.spec.ts — time-field, time-columns and time-columns-rtl
Measuredforced-colors: activeapps/sandbox-e2e/src/forced-colors.spec.ts — the chosen row takes the mode's SelectedItem pair, the keyboard's row keeps a ring in Highlight (in SelectedItemText on the chosen row, where Highlight may be the surface's own colour), a refused row says GrayText; the field's invalid edge is drawn twice as thick, a shape the substitution keeps
Measuredprefers-reduced-motiontools/check-styles.mjs point 9 — the row's and the field's transitions take --pct-motion-transition-duration, which the token build switches off
MeasuredTouch target ≥ 24×24 pxapps/sandbox-e2e/src/target-min.spec.ts — the clock button is max(--pct-time-toggle-size, --pct-time-target-min), measured with the drawing disarmed; a row is 28 px at sm and taller above
MeasuredSize axisapps/sandbox/src/app/views/size/size-view.html — the /size view puts the field on the shared sm/md/lg axis beside the date field, and the rows follow the field into the panel
GapDensity axisnone — gap (req-token-density has not one token in the sources; the row height is the number a density axis would move)
MeasuredRTLapps/sandbox-e2e/src/rtl.spec.ts — the button changes sides and the hour column is drawn on the right; apps/sandbox-e2e/src/time.spec.ts — the same in the panel, and the keys, which are about the row above and below, unchanged
MeasuredSSR + hydrationapps/sandbox-e2e/src/hydration.spec.ts — the /time view is in SBX_ROUTES
MeasuredFormslibs/components/time/src/time-field.spec.ts › the forms that bind it — [formField], [formControl] and [(ngModel)], each from a non-empty value (lesson-26); apps/sandbox-e2e/src/forms.spec.ts — the field in a signal form whose validity waits for it; a form's reset — reset() keeping the value, reset(value) showing it, the form pristine: libs/components/time/src/time-field.spec.ts › the forms that bind it
MeasuredBounds from a signal formlibs/components/time/src/time-field.spec.ts › bounds from a signal form (pctTimeBounds) — the bounds reach the field and the panel walks inside them, a time outside is minTime / maxTime and a bound is inside in either shape, a window's gap is both, the message, a bound read off another field, several rules walked as the shortest stretch holding what all of them accept, in either order, rules that accept no time, and a bound read off another field in a rule of its own; apps/sandbox-e2e/src/time.spec.ts › a signal form hands its bounds to the panel — the bounds reaching the panel and a time typed outside reported, in three engines
MeasuredMessage announcedtools/check-aria.mjs (point 7, target check-aria) — the error part carries role="alert", the warning part role="status"
MeasuredTime arithmetic as lawslibs/components/time/src/time.property.spec.ts — the value module's laws; libs/components/time/src/lattice.spec.ts — the rows a step lists and where a walk lands, under bounds, a window and a base off the hour
MeasuredParts in the inventorylibs/components/parts.snapshot.md, tools/check-parts.mjs (target check-parts)
MeasuredTokens + contrast.policy.jsonlibs/tokens/src/contrast.policy.json — 22 pairs for the field and its columns together, the keyboard's ring on the chosen row among them
MeasuredStrings through PCT_TEXTStools/check-texts.mjs — nine keys; the three letters are texts because they are words, and the clock's own words — AM, 오후 — are the formatter's, because they are what the field shows and reads back
MeasuredSize budgetlibs/components/size.snapshot.md, tools/check-bundle.mjs (target check-bundle)
MeasuredNot a time, saidlibs/components/time/src/time-field.spec.ts › junk in the field — the sentence in the message line ahead of the form's, in the default wording and in the application's; apps/sandbox-e2e/src/time.spec.ts › keeps junk in the field and reports it — the same in three engines, in French, with aria-describedby naming it (0070)
MeasuredScreen-reader logRead 2026-10-08 from docs/acr/at/ — three readers, whole sandbox, and the act that opens the columns. The field is its label, its time and its hint: Orca Time · entry · 13:05 · selected. · The sandbox runs under fr-FR, so the clock counts to 24., NVDA Time, edit, The sandbox runs under fr-FR, so the clock counts to 24, selected 13:05; VoiceOver left the view after one stop, as on most views of this pass. Each field says its own clock — American English · entry · 1:05 PM · selected. (Orca), Korean, edit, selected 오후 1:05 (NVDA) — and the button says what it opens: Choisir une heure, button, collapsed, opens dialog (NVDA), Choisir une heure · collapsed button. · opens dialog (Orca). Opening lands on the hour column: Orca Choisir une heure · dialog · Heures · List with 24 items · 13, VoiceOver Choisir une heure web dialog 13 selected (14 of 24), NVDA Choisir une heure, dialog and nothing of the column. Escape did not close the panel under NVDA, which closed the date's on the same key: on a listbox NVDA is in focus mode and keeps the first Escape to leave it — and VoiceOver's Escape closed no panel in this pass, the dialog's included
Measureddocs page/components/time on the published site — prerendered, the demo's own source is the code tab
Evidence

What the repository holds for this component

Read at build time from the tracked snapshots, not typed here.

86.41%mutation score318 of 368 mutants killed
23 × 3e2e cases × enginesplus the shared audits: axe, forced colors, preferences, size, hydration
23colour pairs measuredlight and dark, at every token build
3visual baselinesChromium, the sandbox's own pictures
10elements in the preview4 listeners · 1 render to settle · 3978 µs in jsdom, held exactly but for the clock

Decisions behind it

  • 0086A time of day is a wall clock, and the field that takes one is text the application formats
  • 0093A bound of the field's own type reaches it through the platform's limit key, and the module ships the rule
  • 0070What the control knows and the form cannot is a second channel
  • 0035A filter is a question, not a value
  • 0031A panel's tab order belongs to its trigger
  • 0025A panel says whether it takes focus, and the rest of the focus layer waits for a consumer
  • 0024The overlay layer carries what an overlay severs, and borrows the closing stack
  • 0022One message line, and it is the error that takes it
  • 0005Signal forms without ControlValueAccessor
  • 0003Wrapper and control
  • lesson-26FormField supplies an NgControl itself

Known limitations

  • No zone, and no day. The value is what a clock on the wall shows. Turning it into an instant needs a day and a zone the field does not have, and belongs to the application — or to the datetime field, which has the day and still leaves the zone to whoever knows where the event is.
  • Several rules walk a span, not a set. Two pctTimeBounds on one path hand the field the shortest stretch of the clock holding every time both accept: where that is several runs, the panel offers the gaps between them but the widest, and the form refuses them; where it is none, the panel has no bounds and the form refuses every time (0093).
  • One time, not a range. A shift's two ends are two fields with min / max bound to each other — in a signal form, pctTimeBounds with a bound read off the other field, in a rule of its own — by 0034's reading of multiplicity. A shift that runs to midnight ends at 23:59, because 24:00 is the end of a day and not a time of it.
  • The step is not snapped. A typed 13:05 in a quarter-hour field stands until the form says otherwise, and an application with no validator for it keeps a value off its own step. A step the columns cannot list — seven-minute slots — is refused: that is a PctSelect of times, not a free one.
  • No predicate on a time. dateDisabled has no counterpart here: an hour row would be sixty questions, and the cases split between the bounds, a slot taken on a day — the datetime field's question — and a break a form validator already answers (0086 §5).
  • The clock is the engine's. In chromium a Burmese or Icelandic field is written in the browser's default locale where the engine does not carry the language, and in webkit a Korean one says AM and PM — what the same engine's own Intl writes on the same page (0086, C5 and D2).
  • An empty value written from outside does not take junk back. Junk leaves the value null; a form that then writes null again — ctrl.reset(), setValue(null) — writes what the model already holds, the model does not move, and the field keeps the text and the report. A time written from outside does take it back.
  • The sentence comes first. A control's own error stands ahead of the form's, always — there is no knob to let "required" win over "not a time" (0070).