@pastweb/components-core 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ # Carousel
2
+
3
+ Carousel presents one item from an ordered collection and provides controls for
4
+ moving through the collection. Its web contract follows the WAI-ARIA carousel
5
+ pattern.
6
+
7
+ ## When to use
8
+
9
+ Use a carousel only when the content has a meaningful sequence and users can
10
+ understand the page without seeing every slide.
11
+
12
+ ## Carousel props
13
+
14
+ | PropName | Type | Required | Default | Description |
15
+ |---|---|---:|---:|---|
16
+ | `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
17
+ | `ariaLabel` | `string` | no | `'Carousel'` | Names the carousel region. Supply a content-specific label. |
18
+ | `className` | `string` | no | `""` | Adds classes to the carousel. |
19
+ | `current` | `number` | no | `0` | Sets the current slide index. |
20
+ | `disabled` | `boolean` | no | - | Prevents navigation and disables controls. |
21
+ | `id` | `string` | no | generated | Supplies the carousel ID. Generated IDs use the `carousel` prefix. |
22
+ | `showCommands` | `boolean` | no | `true` | Renders navigation controls. |
23
+ | `prevAriaLabel` | `string` | no | `'Previous slide'` | Names the previous control. |
24
+ | `nextAriaLabel` | `string` | no | `'Next slide'` | Names the next control. |
25
+ | `onCurrentChange` | `(current: number) => void` | no | - | Reports navigation changes. |
26
+
27
+ ## CarouselItem props
28
+
29
+ | PropName | Type | Required | Default | Description |
30
+ |---|---|---:|---:|---|
31
+ | `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves item classes. |
32
+ | `className` | `string` | no | `""` | Adds classes to the slide. |
33
+ | `disabled` | `boolean` | no | - | Disables the slide and exposes its disabled state. |
34
+
35
+ ## Mediator state
36
+
37
+ `carouselMediator` exposes renderer-ready state for the root semantics and
38
+ track presentation:
39
+
40
+ | State | Type | Description |
41
+ |---|---|---|
42
+ | `ariaLabel` | `string \| undefined` | Normalized carousel name. |
43
+ | `ariaLive` | `'off' \| 'polite'` | Live-region mode for slide changes. |
44
+ | `ariaRoleDescription` | `'carousel' \| undefined` | Carousel role description when the root is named. |
45
+ | `className` | `string` | Complete root class name. |
46
+ | `current` | `number` | Current slide index. |
47
+ | `isDisabled` | `true \| undefined` | Renderer-ready disabled and ARIA value. |
48
+ | `itemsCount` | `number` | Number of registered CarouselItem instances. |
49
+ | `prevAriaLabel` / `nextAriaLabel` | `string` | Normalized navigation-control names. |
50
+ | `role` | `'region' \| undefined` | Region role when the root is named. |
51
+ | `transform` | `string` | Renderer-ready track transform. |
52
+
53
+ `carouselItemMediator` reads the Carousel context internally and exposes the
54
+ following renderer-ready state:
55
+
56
+ | State | Type | Description |
57
+ |---|---|---|
58
+ | `ariaHidden` | `true \| undefined` | Hides an inactive slide from the accessibility tree. |
59
+ | `ariaLabel` | `string` | One-based position label in the form `n of total`. |
60
+ | `ariaRoleDescription` | `'slide'` | Renderer-ready slide role description. |
61
+ | `className` | `string` | Complete item class name, including active and disabled state classes. |
62
+ | `index` | `number` | Stable zero-based registration index. |
63
+ | `inert` | `true \| undefined` | Prevents interaction with descendants of an inactive slide. |
64
+ | `isActive` | `boolean` | Whether this item matches the current Carousel index. |
65
+ | `isDisabled` | `true \| undefined` | Normalized local or inherited disabled state. |
66
+ | `role` | `'group'` | Renderer-ready slide group role. |
67
+
68
+ ## Carousel mediator context
69
+
70
+ `carouselMediator.getContext()` exposes live state and stable commands for
71
+ `CarouselItem` and custom descendant controls. Framework renderers publish this
72
+ contract through `CAROUSEL_CONTEXT_KEY`; command slots do not receive mediator
73
+ state or methods as slot props.
74
+
75
+ | Property or method | Type | Description |
76
+ |---|---|---|
77
+ | `current` | `number` | Live current slide index. |
78
+ | `isDisabled` | `true \| undefined` | Live normalized Carousel disabled state. |
79
+ | `itemsCount` | `number` | Live number of registered CarouselItem descendants. |
80
+ | `prev` | `() => void` | Selects the previous enabled position when navigation is available. |
81
+ | `next` | `() => void` | Selects the next enabled position when navigation is available. |
82
+ | `registerItem` | `() => number` | Registers a CarouselItem and returns its stable zero-based index. |
83
+
84
+ ## Slots
85
+
86
+ | SlotName | Description | Access |
87
+ |---|---|---|
88
+ | default | Supplies `CarouselItem` children. | - |
89
+ | `commands` | Replaces the navigation controls while preserving behavior. | Carousel context |
90
+
91
+ ## Utility classes
92
+
93
+ The Carousel does not currently define component-specific utility classes.
94
+
95
+ ## CSS Variables
96
+
97
+ | Variable | Part | State | CSS property | Default | Description | Platforms |
98
+ |---|---|---|---|---|---|---|
99
+ | `--pw_Carousel_root_default_max-width` | `root` | `default` | `max-width` | `800px` | Maximum carousel width. | Web, Capacitor |
100
+ | `--pw_Carousel_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled carousel opacity. | Web, Capacitor, NativeScript |
101
+ | `--pw_Carousel_track_default_transition-duration` | `track` | `default` | `transition-duration` | `0.5s` | Slide movement duration. | Web, Capacitor; NativeScript adapter |
102
+ | `--pw_Carousel_control_default_background-color` | `control` | `default` | `background-color` | `rgba(0,0,0,.6)` | Control background. | Web, Capacitor, NativeScript |
103
+ | `--pw_Carousel_control_hover_background-color` | `control` | `hover` | `background-color` | `rgba(0,0,0,.8)` | Hover background. | Web, Capacitor |
104
+ | `--pw_Carousel_control_default_color` | `control` | `default` | `color` | `white` | Control foreground. | Web, Capacitor, NativeScript |
105
+ | `--pw_Carousel_control_default_padding` | `control` | `default` | `padding` | `0.75em 1em` | Control spacing. | Web, Capacitor, NativeScript |
106
+ | `--pw_Carousel_control_default_font-size` | `control` | `default` | `font-size` | `1.25em` | Control icon size. | Web, Capacitor, NativeScript |
107
+ | `--pw_Carousel_control_default_border-radius` | `control` | `default` | `border-radius` | `var(--pw_border-radius)` | Control corner radius. | Web, Capacitor, NativeScript |
108
+ | `--pw_Carousel_control_default_transition-duration` | `control` | `default` | `transition-duration` | `0.3s` | Control state duration. | Web, Capacitor |
109
+ | `--pw_CarouselItem_root_default_min-height` | `root` | `default` | `min-height` | `300px` | Minimum slide height. | Web, Capacitor, NativeScript |
110
+ | `--pw_CarouselItem_root_default_transition-duration` | `root` | `default` | `transition-duration` | `0.5s` | Slide visual transition duration. | Web, Capacitor; NativeScript adapter |
111
+ | `--pw_CarouselItem_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled slide opacity. | Web, Capacitor, NativeScript |
112
+
113
+ ```xts
114
+ import { setContext, useBeforeMount, useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
115
+ import {
116
+ carouselItemMediator,
117
+ carouselMediator,
118
+ CAROUSEL_CONTEXT_KEY,
119
+ DEFAULT_CAROUSEL_ITEM_PROPS,
120
+ DEFAULT_PROPS,
121
+ } from '@pastweb/components-core/Carousel';
122
+
123
+ export function Carousel(props: CarouselProps) {
124
+ const p = withDefaultProps<CarouselDefaultProps>(props, DEFAULT_PROPS);
125
+ const m = useMediator<CarouselMediator>(carouselMediator, p);
126
+ const s = m.state;
127
+ const c = m.getContext();
128
+
129
+ setContext(CAROUSEL_CONTEXT_KEY, c);
130
+
131
+ useBeforeUnmount(m.useBeforeUnmount);
132
+
133
+ return (
134
+ <div
135
+ id={m.id}
136
+ class={s.className}
137
+ role={s.role}
138
+ aria-roledescription={s.ariaRoleDescription}
139
+ aria-label={s.ariaLabel}
140
+ aria-disabled={s.isDisabled}
141
+ @keydown={m.onKeyDown}
142
+ >
143
+ <div class={p.cls('carouselInner')} #style={{ transform: s.transform }} aria-live={s.ariaLive}>
144
+ {p.children}
145
+ </div>
146
+ <slot #if={p.showCommands} name="commands">
147
+ <button class={p.cls('prev')} type="button" @click={m.prev} disabled={s.isDisabled} aria-disabled={s.isDisabled} aria-label={s.prevAriaLabel}>
148
+ <span aria-hidden="true">◄</span>
149
+ </button>
150
+ <button class={p.cls('next')} type="button" @click={m.next} disabled={s.isDisabled} aria-disabled={s.isDisabled} aria-label={s.nextAriaLabel}>
151
+ <span aria-hidden="true">►</span>
152
+ </button>
153
+ </slot>
154
+ </div>
155
+ );
156
+ }
157
+
158
+ export function CarouselItem(props: CarouselItemProps) {
159
+ const p = withDefaultProps<CarouselItemDefaultProps>(props, DEFAULT_CAROUSEL_ITEM_PROPS);
160
+ const m = useMediator<CarouselItemMediator>(carouselItemMediator, p);
161
+ const s = m.state;
162
+
163
+ useBeforeMount(m.useBeforeMount);
164
+
165
+ return (
166
+ <div
167
+ class={s.className}
168
+ role={s.role}
169
+ aria-roledescription={s.ariaRoleDescription}
170
+ aria-label={s.ariaLabel}
171
+ aria-hidden={s.ariaHidden}
172
+ aria-disabled={s.isDisabled}
173
+ inert={s.inert}
174
+ >
175
+ <slot />
176
+ </div>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ## Keyboard interaction
182
+
183
+ - `Tab` and `Shift+Tab` move through the carousel and its native controls in document order.
184
+ - `ArrowLeft` and `ArrowRight` move to the previous and next slides when the carousel is focused.
185
+ - Collapsed or inactive slide content is inert and removed from sequential focus.
186
+
187
+ ## Accessibility notes
188
+
189
+ - The carousel is a labelled region with `aria-roledescription="carousel"`.
190
+ - Slides use `role="group"`, expose their position, and inactive slides are
191
+ `aria-hidden` and inert.
192
+ - The default `ariaLabel` is `"Carousel"`. An explicit empty string omits the
193
+ root label, region role, and carousel role description.
194
+ - A disabled Carousel passes the same normalized `true` value to its
195
+ `is-disabled` class, control `disabled`/`aria-disabled` attributes, root
196
+ `aria-disabled`, and inherited CarouselItem state.
197
+ - Left and Right Arrow navigate; native buttons remain in normal tab order.
198
+ - Shared styles honor `prefers-reduced-motion` and `forced-colors`.
199
+
200
+ ## Standards
201
+
202
+ - WCAG 2.2: https://www.w3.org/TR/WCAG22/
203
+ - WAI-ARIA Carousel pattern: https://www.w3.org/WAI/ARIA/apg/patterns/carousel/
204
+ - GOV.UK guidance: https://design-system.service.gov.uk/components/
@@ -0,0 +1,140 @@
1
+ # Checkbox
2
+
3
+ Checkbox renders a native binary choice with a visible label or explicit
4
+ accessible name. Its rendering contract follows the WAI-ARIA Checkbox pattern
5
+ and targets WCAG 2.2 Level AA.
6
+
7
+ ## When to use
8
+
9
+ Use a checkbox when users may independently select or clear one option. Provide
10
+ a concise visible label that describes the choice. Do not use a checkbox when
11
+ exactly one option must be selected from a group; use radio buttons then.
12
+
13
+ ## Props
14
+
15
+ | PropName | Type | Required | Default | Description |
16
+ |---|---|---:|---:|---|
17
+ | `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
18
+ | `ariaLabel` | `string` | no | `"Checkbox"` | Names the checkbox when no visible `label` is supplied. Pass an empty string to omit the accessible name. |
19
+ | `checkChar` | `string` | no | `✔` | Supplies the default checked mark. |
20
+ | `className` | `string` | no | `""` | Adds classes to the wrapper. |
21
+ | `id` | `string` | no | generated | Supplies the native input ID. Generated IDs use the `checkbox` prefix. |
22
+ | `disabled` | `boolean` | no | - | Makes the choice unavailable. |
23
+ | `label` | `string` | no | `""` | Supplies the visible accessible label. |
24
+ | `labelPlacement` | `Placement` | no | `'top-start'` | Places the visible label around the box. |
25
+ | `name` | `string` | no | - | Supplies the form field name. |
26
+ | `value` | `boolean` | no | - | Controls the checked state. |
27
+ | `tabIndex` | `number` | no | `0` | Controls sequential focus order. Prefer the default document order. |
28
+ | `onChange` | `(checked: boolean) => void` | no | noop | Reports checked-state changes. |
29
+
30
+ ## Mediator API
31
+
32
+ | Property | Type | Description |
33
+ |---|---|---|
34
+ | `id` | `string` | Resolved input ID. Generated IDs use the `checkbox` prefix; an explicit `id` is exposed as `_${id}`. |
35
+
36
+ ## Mediator state
37
+
38
+ | Property | Type | Description |
39
+ |---|---|---|
40
+ | `ariaLabel` | `string \| undefined` | Normalized accessible name when no visible `label` is present. Empty labels are omitted. |
41
+ | `isDisabled` | `true \| undefined` | Is `true` when the checkbox is disabled; otherwise omitted. Renderers use this value directly for `.is-disabled`, `disabled`, and `aria-disabled`. |
42
+ | `value` | `boolean` | Checked state consumed as `.is-checked` and the native `checked` property. |
43
+
44
+ ## Slots
45
+
46
+ | SlotName | Description | Default content | Props |
47
+ |---|---|---|---|
48
+ | `check` | Replaces the decorative checked mark. | `<span>{checkChar}</span>` | `{ checkChar }` |
49
+
50
+ ## CSS Variables
51
+
52
+ | Variable | Part | State | CSS property | Default | Description | Platforms |
53
+ |---|---|---|---|---|---|---|
54
+ | `--pw_Checkbox_root_default_margin` | `root` | `default` | `margin` | shared margins | Component spacing. | Web, Capacitor, NativeScript |
55
+ | `--pw_Checkbox_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled opacity. | Web, Capacitor, NativeScript |
56
+ | `--pw_Checkbox_root_default_size` | `root` | `default` | `size` | line-height plus padding and border | Box width and height, with a 44px web minimum. | Web, Capacitor, NativeScript |
57
+ | `--pw_Checkbox_root_default_background-color` | `root` | `default` | `background-color` | `var(--pw_control_background-color)` | Box background. | Web, Capacitor, NativeScript |
58
+ | `--pw_Checkbox_root_default_border-color` | `root` | `default` | `border-color` | `var(--pw_border-color)` | Box border colour. | Web, Capacitor, NativeScript |
59
+ | `--pw_Checkbox_root_default_border-width` | `root` | `default` | `border-width` | `var(--pw_border-width)` | Box border width. | Web, Capacitor, NativeScript |
60
+ | `--pw_Checkbox_root_default_border-style` | `root` | `default` | `border-style` | `var(--pw_border-style)` | Box border style. | Web, Capacitor, NativeScript |
61
+ | `--pw_Checkbox_root_default_border-radius` | `root` | `default` | `border-radius` | `var(--pw_border-radius)` | Box corner radius. | Web, Capacitor, NativeScript |
62
+ | `--pw_Checkbox_check_default_size` | `check` | `default` | `font-size` | `1.5em` | Checked mark size. | Web, Capacitor, NativeScript |
63
+ | `--pw_Checkbox_label_default_color` | `label` | `default` | `color` | `var(--pw_label_text_color)` | Label colour. | Web, Capacitor, NativeScript |
64
+
65
+ ```xts
66
+ import { useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
67
+ import { checkboxMediator } from '@pastweb/components-core/Checkbox';
68
+ import { DEFAULT_PROPS } from './constants';
69
+
70
+ export function Checkbox(props: CheckboxProps) {
71
+ const p = withDefaultProps<CheckboxDefaultProps>(props, DEFAULT_PROPS);
72
+ const m = useMediator<CheckboxMediator>(checkboxMediator, p);
73
+ const s = m.state;
74
+
75
+ useBeforeUnmount(m.useBeforeUnmount);
76
+
77
+ return (
78
+ <div
79
+ class={p.cls('Checkbox', `label-${p.labelPlacement}`, p.className, {
80
+ 'is-disabled': s.isDisabled,
81
+ 'is-checked': s.value,
82
+ })}
83
+ >
84
+ <label class={p.cls('control')}>
85
+ <span class={p.cls('box')}>
86
+ <input
87
+ type="checkbox"
88
+ class={p.cls('input')}
89
+ id={m.id}
90
+ name={p.name}
91
+ checked={s.value}
92
+ disabled={s.isDisabled}
93
+ aria-disabled={s.isDisabled}
94
+ aria-label={s.ariaLabel}
95
+ tabIndex={p.tabIndex}
96
+ @change={event => m.onChange(event.target.checked)}
97
+ />
98
+ <slot name="check" props={{ checkChar: p.checkChar }}>
99
+ <span class={p.cls('check')} aria-hidden="true">{p.checkChar}</span>
100
+ </slot>
101
+ </span>
102
+ <span #if={p.label} class={p.cls('label', p.labelPlacement)}>{p.label}</span>
103
+ </label>
104
+ </div>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## Keyboard interaction
110
+
111
+ - `Tab` and `Shift+Tab` move focus to and from the native checkbox in document order.
112
+ - `Space` toggles the checked state through native checkbox behavior.
113
+
114
+ ## Accessibility notes
115
+
116
+ - The input remains a native checkbox. One wrapping label supplies the visible
117
+ accessible name, avoiding multiple labels for the same control.
118
+ - When visible label text is absent, the mediator exposes `ariaLabel`. The
119
+ default name is `Checkbox`. An explicit empty string omits the name.
120
+ - Disabled checkboxes use the native `disabled` property, `aria-disabled="true"`,
121
+ and the `is-disabled` class.
122
+ - Checked state is exposed through the native `checked` property and the
123
+ `is-checked` class.
124
+ - The decorative check is hidden from assistive technology. Replacement `check`
125
+ slot content must remain decorative.
126
+ - Shared web styles provide at least a 44 by 44 CSS-pixel target, visible focus,
127
+ forced-colours treatment, and reduced-motion behavior.
128
+
129
+ ## Known limitations
130
+
131
+ - The custom checked mark is visual only. Do not place interactive content in
132
+ the `check` slot.
133
+ - Automated accessibility checks do not by themselves establish WCAG
134
+ conformance.
135
+
136
+ ## Standards
137
+
138
+ - WCAG 2.2: https://www.w3.org/TR/WCAG22/
139
+ - WAI-ARIA Checkbox pattern: https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/
140
+ - GOV.UK Checkboxes guidance: https://design-system.service.gov.uk/components/checkboxes/
@@ -0,0 +1,125 @@
1
+ # DateTimePicker
2
+
3
+ `DateTimePicker` is a framework-neutral calendar and time-selection mediator. Renderers provide the target and popup integration; the mediator owns value normalization, single/multiple/range selection, calendar navigation, date rules, and keyboard intent.
4
+
5
+ ## Features
6
+
7
+ - Single, multiple, and range date selection.
8
+ - Optional time selection, calendar-only, and time-only modes.
9
+ - Controlled or uncontrolled popup visibility and selected values.
10
+ - Keyboard calendar navigation, date rules, localized labels, and accessible IDs.
11
+ - A renderer-neutral target-props context for custom trigger implementations.
12
+
13
+ ```xts
14
+ <DateTimePicker
15
+ mode="range"
16
+ enableTime
17
+ minDate={today}
18
+ disable={[weekends]}
19
+ locale={{ months, weekdays, clear: 'Clear' }}
20
+ onChange={value => booking.dates = value}
21
+ />
22
+ ```
23
+
24
+ ## Values
25
+
26
+ Use a `Date`, an array of `Date` values, or `null`. `single` returns one date in an array internally and `multiple`/`range` return arrays. The public change callback uses `Date | Date[] | null` so consumers can preserve the same value shape they provide.
27
+
28
+ ```xts
29
+ <DateTimePicker value={departure} onChange={value => departure = value} />
30
+ <DateTimePicker mode="multiple" defaultValue={[firstDate, secondDate]} />
31
+ <DateTimePicker mode="range" value={stay} />
32
+ ```
33
+
34
+ ## Calendar rules and language
35
+
36
+ `minDate`, `maxDate`, `disable`, and `enable` constrain selection. Rules accept a date, an inclusive `{ from, to }` range, or a predicate returning `true` when it matches.
37
+
38
+ The component does not read an application i18n service. Supply the calendar words directly with `locale` when a language is required.
39
+
40
+ ```xts
41
+ <DateTimePicker
42
+ minDate={today}
43
+ disable={[date => date.getDay() === 0 || date.getDay() === 6]}
44
+ locale={{
45
+ months: italianMonths,
46
+ weekdays: italianWeekdays,
47
+ previousMonth: 'Mese precedente',
48
+ nextMonth: 'Mese successivo',
49
+ }}
50
+ />
51
+ ```
52
+
53
+ ## Rendering modes
54
+
55
+ The default picker is positioned locally, like `Select`. Set `inline` for an always-visible calendar that does not open and close.
56
+
57
+ ```xts
58
+ <DateTimePicker inline weekNumbers showMonths={1} />
59
+ <DateTimePicker enableTime noCalendar minuteIncrement={15} />
60
+ ```
61
+
62
+ ## Calendar presentation
63
+
64
+ Set `yearSelect` to render a compact year selector beside the month name. Its range follows `minDate` and `maxDate`, or otherwise surrounds the viewed year.
65
+
66
+ `workingWeekDays` counts working days from Monday (default `5` for Monday–Friday). Remaining days receive the `is-free-day` state. Saturdays and Sundays always receive `is-sat` and `is-sun`; dates listed in `holidaysDates` receive `is-holiday`. Use `getDayClassName` for application-specific states.
67
+
68
+ ```xts
69
+ <DateTimePicker
70
+ yearSelect
71
+ workingWeekDays={4}
72
+ holidaysDates={[companyHoliday]}
73
+ getDayClassName={date => date.toDateString() === releaseDay.toDateString() ? 'is-release-day' : undefined}
74
+ />
75
+ ```
76
+
77
+ ## Target and accessibility labels
78
+
79
+ Renderers use their default target when no `target` slot is supplied. The default target displays date and time values as `<date> - <time>`, caps its width at `20rem`, and horizontally scrolls long multiple selections while keeping the arrow visible. Override the width through `--pw_DateTimePicker_target_default_max-width`.
80
+
81
+ Set `label` and `labelPlacement` to render a visible label using the same placement utilities as `Select`. `labelPlacement` defaults to `'top-start'` and accepts the standard placement values such as `'bottom'`, `'left-end'`, and `'right-start'`. A visible label is linked to the default target through `aria-labelledby`.
82
+
83
+ ```xts
84
+ <DateTimePicker label="Appointment" labelPlacement="left" />
85
+ ```
86
+
87
+ Provide `ariaLabel`, `previousMonthAriaLabel`, `nextMonthAriaLabel`, `yearSelectAriaLabel`, `selectTimeAriaLabel`, and `backToCalendarAriaLabel` when the default English labels do not fit the product vocabulary.
88
+
89
+ ```xts
90
+ <DateTimePicker
91
+ ariaLabel="Appointment date and time"
92
+ previousMonthAriaLabel="Show previous appointment month"
93
+ selectTimeAriaLabel="Choose appointment time"
94
+ />
95
+ ```
96
+
97
+ ## Renderer integration
98
+
99
+ Renderers own their target, calendar, and time-toggle references, then provide
100
+ them together after mounting. This keeps the mediator independent of framework
101
+ ref objects and slot mechanisms.
102
+
103
+ ```xts
104
+ const triggerReference = getTriggerElement();
105
+ const calendarReference = getCalendarElement();
106
+ const timeToggleReference = getTimeToggleElement();
107
+
108
+ mediator.useMounted(
109
+ triggerReference,
110
+ calendarReference,
111
+ timeToggleReference,
112
+ );
113
+ ```
114
+
115
+ Custom targets consume renderer-provided bindings from the shared
116
+ `DATETIME_PICKER_TRIGGER_PROPS_CONTEXT_KEY`. The core
117
+ `DateTimePickerTriggerProps` contract provides the ID, role, tab index, ARIA
118
+ attributes, and click/keyboard handlers. Each renderer adapts only its own ref
119
+ and event types.
120
+
121
+ ## Summary
122
+
123
+ Use DateTimePicker when users need to choose a date, date range, or time with
124
+ calendar guidance. Use a native date input when the browser-provided control is
125
+ sufficient and a richer calendar interface is unnecessary.