@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.
- package/dist/Accordion/README.md +251 -0
- package/dist/Button/README.md +143 -0
- package/dist/Buttons/README.md +108 -0
- package/dist/Carousel/README.md +204 -0
- package/dist/Checkbox/README.md +140 -0
- package/dist/DateTimePicker/README.md +125 -0
- package/dist/DropDown/README.md +237 -0
- package/dist/FileUploader/README.md +224 -0
- package/dist/Icon/README.md +103 -0
- package/dist/Input/README.md +212 -0
- package/dist/Link/README.md +108 -0
- package/dist/Notification/README.md +141 -0
- package/dist/Pagination/README.md +147 -0
- package/dist/Panel/README.md +158 -0
- package/dist/Placeholder/README.md +124 -0
- package/dist/ProgressBar/README.md +137 -0
- package/dist/Radio/README.md +144 -0
- package/dist/RangeSlider/README.md +120 -0
- package/dist/ScrollArea/README.md +105 -0
- package/dist/Select/README.md +243 -0
- package/dist/SheetController/README.md +143 -0
- package/dist/Spinner/README.md +80 -0
- package/dist/SplitLayout/README.md +132 -0
- package/dist/Stepper/README.md +146 -0
- package/dist/Table/README.md +76 -0
- package/dist/Tabs/README.md +352 -0
- package/dist/Tabs/utils/index.d.cts +2 -1
- package/dist/Tag/README.md +90 -0
- package/dist/TaskBar/README.md +105 -0
- package/dist/TextArea/README.md +141 -0
- package/dist/ToastController/README.md +119 -0
- package/dist/Toggle/README.md +109 -0
- package/dist/Tooltip/README.md +128 -0
- package/dist/Walktour/README.md +188 -0
- package/dist/WindowController/README.md +190 -0
- package/package.json +5 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Spinner
|
|
2
|
+
|
|
3
|
+
Spinner displays a rotating visual indicator and exposes a polite loading
|
|
4
|
+
status. Its rendering contract targets WCAG 2.2 Level AA.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use it for short waits when completion cannot be calculated. Use ProgressBar for
|
|
9
|
+
determinate work. Do not rely on animation alone: keep the accessible label
|
|
10
|
+
specific to the operation.
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
|
|
14
|
+
| PropName | Type | Required | Default | Description |
|
|
15
|
+
|---|---|---:|---:|---|
|
|
16
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and part classes. |
|
|
17
|
+
| `ariaLabel` | `string` | no | `"Loading"` | Accessible status name. Pass an empty string to omit the name. |
|
|
18
|
+
| `decorative` | `boolean` | no | - | Removes status semantics and applies `is-decorative`. |
|
|
19
|
+
| `className` | `string` | no | `""` | Additional root classes. |
|
|
20
|
+
|
|
21
|
+
The default slot supplies the visual glyph and is hidden from assistive
|
|
22
|
+
technology.
|
|
23
|
+
|
|
24
|
+
## Mediator state
|
|
25
|
+
|
|
26
|
+
| Property | Type | Description |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `ariaLabel` | `string \| undefined` | Normalized status name. Decorative and empty labels are omitted. |
|
|
29
|
+
| `ariaLive` | `'polite' \| undefined` | Is `polite` unless decorative. |
|
|
30
|
+
| `role` | `'status' \| undefined` | Is `status` unless decorative. |
|
|
31
|
+
|
|
32
|
+
## CSS Variables
|
|
33
|
+
|
|
34
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
35
|
+
|---|---|---|---|---|---|---|
|
|
36
|
+
| `--pw_Spinner_root_default_width` | `root` | `default` | `width` | `1.5em` | Indicator width. | Web, Capacitor, NativeScript |
|
|
37
|
+
| `--pw_Spinner_root_default_height` | `root` | `default` | `height` | `1.5em` | Indicator height. | Web, Capacitor, NativeScript |
|
|
38
|
+
| `--pw_Spinner_root_default_color` | `root` | `default` | `color` | `currentcolor` | Indicator colour. | Web, Capacitor, NativeScript |
|
|
39
|
+
| `--pw_Spinner_content_default_animation-duration` | `content` | `default` | `animation-duration` | `2s` | Rotation duration. | Web, Capacitor; NativeScript adapter |
|
|
40
|
+
| `--pw_Spinner_content_default_animation-timing-function` | `content` | `default` | `animation-timing-function` | `linear` | Rotation timing. | Web, Capacitor; NativeScript adapter |
|
|
41
|
+
|
|
42
|
+
```xts
|
|
43
|
+
import { useMediator, withDefaultProps } from '@pastweb/x';
|
|
44
|
+
import { spinnerMediator } from '@pastweb/components-core/Spinner';
|
|
45
|
+
import { DEFAULT_PROPS } from './constants';
|
|
46
|
+
|
|
47
|
+
export function Spinner(props: SpinnerProps) {
|
|
48
|
+
const p = withDefaultProps<SpinnerDefaultProps>(props, DEFAULT_PROPS);
|
|
49
|
+
const m = useMediator<SpinnerMediator>(spinnerMediator, p);
|
|
50
|
+
const s = m.state;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div class={p.cls('Spinner', { 'is-decorative': p.decorative }, p.className)}
|
|
54
|
+
role={s.role} aria-label={s.ariaLabel} aria-live={s.ariaLive}>
|
|
55
|
+
<span class={p.cls('content')} aria-hidden="true"><slot /></span>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Keyboard interaction
|
|
62
|
+
|
|
63
|
+
- Spinner is not interactive. It does not enter the tab order.
|
|
64
|
+
|
|
65
|
+
## Accessibility notes
|
|
66
|
+
|
|
67
|
+
- The web renderer exposes a named polite status unless decorative.
|
|
68
|
+
- Decorative spinners omit role, name, and live region.
|
|
69
|
+
- Shared web styles disable non-essential rotation under `prefers-reduced-motion`.
|
|
70
|
+
|
|
71
|
+
## Known limitations
|
|
72
|
+
|
|
73
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
74
|
+
conformance.
|
|
75
|
+
|
|
76
|
+
## Standards
|
|
77
|
+
|
|
78
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
79
|
+
- WAI-ARIA status guidance: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
80
|
+
- GOV.UK components: https://design-system.service.gov.uk/components/
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# SplitLayout
|
|
2
|
+
|
|
3
|
+
SplitLayout arranges resizable panels separated by pointer- and keyboard-operable
|
|
4
|
+
controls. Its rendering contract follows the WAI-ARIA Window Splitter pattern
|
|
5
|
+
and targets WCAG 2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use it when users benefit from allocating space between adjacent work areas.
|
|
10
|
+
Avoid it for ordinary responsive page columns, and provide a stacked alternative
|
|
11
|
+
when a small viewport cannot support the minimum panel sizes.
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
| PropName | Type | Required | Default | Description |
|
|
16
|
+
|---|---|---:|---:|---|
|
|
17
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves classes. |
|
|
18
|
+
| `ariaLabel` | `string` | no | `'Resizable panels'` | Names the panel group. Pass an empty string to omit the name. |
|
|
19
|
+
| `className` | `string` | no | `''` | Additional root classes. |
|
|
20
|
+
| `orientation` | `'horizontal' \| 'vertical'` | no | `'horizontal'` | Panel and separator axis. |
|
|
21
|
+
| `sizes` | `number[]` | no | equal | Relative percentages normalized to total 100. |
|
|
22
|
+
| `minSize` | `number` | no | `10` | Minimum percentage for each adjacent panel. |
|
|
23
|
+
| `keyboardStep` | `number` | no | `2` | Percentage moved per arrow-key activation. |
|
|
24
|
+
| `disabled` | `boolean` | no | - | Prevents resizing and applies `is-disabled`. |
|
|
25
|
+
| `onSizesChange` | `(sizes: number[]) => void` | no | noop | Reports normalized percentages after resizing. |
|
|
26
|
+
|
|
27
|
+
`SplitLayoutItem` accepts `id`, `ariaLabel`, and `className`. Only
|
|
28
|
+
`SplitLayoutItem` children are supported.
|
|
29
|
+
|
|
30
|
+
## Mediator integration
|
|
31
|
+
|
|
32
|
+
`SplitLayoutItem` owns pointer resizing through its mediator. Framework
|
|
33
|
+
renderers should forward `pointerdown`, `pointermove`, `pointerup`,
|
|
34
|
+
`pointercancel`, and `lostpointercapture` events to the matching mediator
|
|
35
|
+
methods. The mediator normalizes the framework event internally, manages
|
|
36
|
+
pointer capture, and reports resize deltas to the parent layout context.
|
|
37
|
+
|
|
38
|
+
The `onSizesChange` prop remains the framework-neutral callback for changed
|
|
39
|
+
pane percentages. Renderers may additionally expose that callback through
|
|
40
|
+
their native event convention, such as Vue's `@sizes-change`.
|
|
41
|
+
|
|
42
|
+
## Mediator state
|
|
43
|
+
|
|
44
|
+
| Property | Type | Description |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `activeSeparator` | `number \| null` | Index of the separator currently being resized. |
|
|
47
|
+
| `ariaLabel` | `string \| undefined` | Normalized group name. Empty labels are omitted. |
|
|
48
|
+
| `isDisabled` | `true \| undefined` | Is `true` when resizing is unavailable; otherwise omitted. |
|
|
49
|
+
| `itemCount` | `number` | Registered pane count. |
|
|
50
|
+
| `sizes` | `number[]` | Normalized pane percentages totalling 100. |
|
|
51
|
+
|
|
52
|
+
## CSS Variables
|
|
53
|
+
|
|
54
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
55
|
+
|---|---|---|---|---|---|---|
|
|
56
|
+
| `--pw_SplitLayout_root_default_width` | `root` | `default` | `width` | `100%` | Layout width. | Web, Capacitor, NativeScript |
|
|
57
|
+
| `--pw_SplitLayout_root_default_height` | `root` | `default` | `height` | `100%` | Layout height. | Web, Capacitor, NativeScript |
|
|
58
|
+
| `--pw_SplitLayout_root_disabled_opacity` | `root` | `disabled` | `opacity` | shared disabled opacity | Disabled appearance. | Web, Capacitor, NativeScript |
|
|
59
|
+
| `--pw_SplitLayout_pane_default_background-color` | `pane` | `default` | `background-color` | transparent | Panel background. | Web, Capacitor, NativeScript |
|
|
60
|
+
| `--pw_SplitLayout_splitter_default_width` | `splitter` | `default` | `width` | `0.2em` | Horizontal visual splitter width. | Web, Capacitor, NativeScript |
|
|
61
|
+
| `--pw_SplitLayout_splitter_default_height` | `splitter` | `default` | `height` | `0.2em` | Vertical visual splitter height. | Web, Capacitor, NativeScript |
|
|
62
|
+
| `--pw_SplitLayout_splitter_default_background-color` | `splitter` | `default` | `background-color` | border colour | Splitter colour. | Web, Capacitor, NativeScript |
|
|
63
|
+
| `--pw_SplitLayout_splitter_hover_background-color` | `splitter` | `hover` | `background-color` | strong text | Hover colour. | Web, Capacitor |
|
|
64
|
+
| `--pw_SplitLayout_splitter_default_border-color` | `splitter` | `default` | `border-color` | shared border | Splitter border colour. | Web, Capacitor, NativeScript |
|
|
65
|
+
| `--pw_SplitLayout_splitter_default_border-style` | `splitter` | `default` | `border-style` | shared border style | Splitter border style. | Web, Capacitor, NativeScript |
|
|
66
|
+
| `--pw_SplitLayout_splitter_default_border-width` | `splitter` | `default` | `border-width` | shared border width | Splitter border width. | Web, Capacitor, NativeScript |
|
|
67
|
+
| `--pw_SplitLayout_splitter_default_outline` | `splitter` | `default` | `outline` | shared outline | Keyboard focus indicator. | Web, Capacitor |
|
|
68
|
+
| `--pw_SplitLayout_splitter-target_default_width` | `splitter-target` | `default` | `width` | `2.75em` | Horizontal pointer target width. | Web, Capacitor, NativeScript |
|
|
69
|
+
| `--pw_SplitLayout_splitter-target_default_height` | `splitter-target` | `default` | `height` | `2.75em` | Vertical pointer target height. | Web, Capacitor, NativeScript |
|
|
70
|
+
|
|
71
|
+
```xts
|
|
72
|
+
import { setContext, useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
|
|
73
|
+
import { splitLayoutMediator, SPLIT_LAYOUT_CONTEXT_KEY } from '@pastweb/components-core/SplitLayout';
|
|
74
|
+
import { DEFAULT_PROPS } from './constants';
|
|
75
|
+
|
|
76
|
+
export function SplitLayout(props: SplitLayoutProps) {
|
|
77
|
+
const p = withDefaultProps<SplitLayoutDefaultProps>(props, DEFAULT_PROPS);
|
|
78
|
+
const m = useMediator<SplitLayoutMediator>(splitLayoutMediator, p);
|
|
79
|
+
const s = m.state;
|
|
80
|
+
const c = m.getContext();
|
|
81
|
+
|
|
82
|
+
m.setItemCount(props.children.length);
|
|
83
|
+
setContext(SPLIT_LAYOUT_CONTEXT_KEY, c);
|
|
84
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div class={p.cls('SplitLayout', p.orientation, {
|
|
88
|
+
'is-disabled': s.isDisabled, 'is-resizing': s.activeSeparator !== null,
|
|
89
|
+
}, p.className)} role="group" aria-label={s.ariaLabel}>
|
|
90
|
+
<slot />
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Keyboard interaction
|
|
97
|
+
|
|
98
|
+
- A horizontal panel layout has vertical separators and uses `ArrowLeft` and
|
|
99
|
+
`ArrowRight`.
|
|
100
|
+
- A vertical panel layout has horizontal separators and uses `ArrowUp` and
|
|
101
|
+
`ArrowDown`.
|
|
102
|
+
- `Home` moves the preceding panel to its minimum.
|
|
103
|
+
- `End` moves the following panel to its minimum.
|
|
104
|
+
- Separators expose orientation and current, minimum, and maximum percentage
|
|
105
|
+
values.
|
|
106
|
+
|
|
107
|
+
## Accessibility notes
|
|
108
|
+
|
|
109
|
+
- The layout is a named group. Each separator is a focusable `separator` with
|
|
110
|
+
`aria-controls`, `aria-orientation`, and current/min/max values.
|
|
111
|
+
- Disabled state is reflected through `.is-disabled` and removes separator
|
|
112
|
+
operation.
|
|
113
|
+
- Pointer targets remain at least 44 CSS pixels even when the visible splitter
|
|
114
|
+
is thinner.
|
|
115
|
+
|
|
116
|
+
## Known limitations
|
|
117
|
+
|
|
118
|
+
- Percentages describe the pane proportions within the space left after
|
|
119
|
+
splitters; very small containers can therefore produce a physical pane size
|
|
120
|
+
slightly below the percentage calculated against the full container.
|
|
121
|
+
- The component does not yet observe container resizing or automatically switch
|
|
122
|
+
orientation at responsive breakpoints.
|
|
123
|
+
- Nested layouts require distinct labels so separator announcements remain
|
|
124
|
+
understandable.
|
|
125
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
126
|
+
conformance.
|
|
127
|
+
|
|
128
|
+
## Standards
|
|
129
|
+
|
|
130
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
131
|
+
- WAI-ARIA Window Splitter pattern: https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
|
|
132
|
+
- GOV.UK components: https://design-system.service.gov.uk/components/
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Stepper
|
|
2
|
+
|
|
3
|
+
Stepper communicates progress through an ordered sequence. `active` remains
|
|
4
|
+
one-based for compatibility. A step is interactive only when it supplies
|
|
5
|
+
`onClick`; disabled interactive steps use a native disabled button. Its
|
|
6
|
+
rendering contract targets WCAG 2.2 Level AA.
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use Stepper for a known sequence of stages. Do not use it as a page navigation
|
|
11
|
+
control unless each step is also a destination. Prefer a native continue action
|
|
12
|
+
and back link for linear form journeys that do not need a visible map of every
|
|
13
|
+
stage.
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| PropName | Type | Required | Default | Description |
|
|
18
|
+
|---|---|---:|---:|---|
|
|
19
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
20
|
+
| `steps` | `Step[]` | yes | - | Ordered labels, descriptions, indicators and optional actions. |
|
|
21
|
+
| `active` | `number` | no | `1` | Current one-based step. |
|
|
22
|
+
| `id` | `string` | no | generated | Supplies the host ID. Generated IDs use the `stepper` prefix. Explicit IDs are stored as `_${id}`. |
|
|
23
|
+
| `ariaLabel` | `string` | no | `"Progress"` | Names the ordered sequence. Pass an empty string to omit it. |
|
|
24
|
+
| `orientation` | `'horizontal' \| 'vertical'` | no | `'horizontal'` | Visual direction. |
|
|
25
|
+
| `labelPosition` | `'top' \| 'bottom' \| 'left' \| 'right'` | no | `'top'` | Visual label position. Horizontal uses `top` or `bottom` (`top` when omitted or invalid). Vertical uses `left` or `right` (`right` when omitted or invalid). |
|
|
26
|
+
| `className` | `string` | no | `""` | Additional root classes. |
|
|
27
|
+
|
|
28
|
+
`Step` supports `label`, `description`, `content`, `ariaLabel`, `className`,
|
|
29
|
+
`disabled`, and `onClick`. The current list item exposes `aria-current="step"`.
|
|
30
|
+
|
|
31
|
+
## Mediator state
|
|
32
|
+
|
|
33
|
+
| Property | Type | Description |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `ariaLabel` | `string \| undefined` | Normalized sequence name. Empty labels are omitted. |
|
|
36
|
+
| `id` | `string` | Host ID from an explicit `id` or a generated `stepper` prefix. |
|
|
37
|
+
| `labelPosition` | `'top' \| 'bottom' \| 'left' \| 'right'` | Resolved label side for the current orientation. |
|
|
38
|
+
|
|
39
|
+
`getStepState(index)` returns `'active'`, `'complete'`, or `'pending'`. Title
|
|
40
|
+
and description IDs use `title${id}${index}` and `description${id}${index}`.
|
|
41
|
+
|
|
42
|
+
## CSS classes
|
|
43
|
+
|
|
44
|
+
| Class | Applied when | Notes |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `Stepper` | Always | Root component class. |
|
|
47
|
+
| `is-horizontal` / `is-vertical` | `orientation` | Axis of the ordered list. |
|
|
48
|
+
| `labels-top` / `labels-bottom` / `labels-left` / `labels-right` | resolved `labelPosition` | Horizontal applies `labels-top` or `labels-bottom`. Vertical applies `labels-left` or `labels-right`. |
|
|
49
|
+
| `is-active` | current step | Applied to the list item. |
|
|
50
|
+
| `is-complete` | earlier steps | Applied to the list item. |
|
|
51
|
+
| `is-pending` | later steps | Applied to the list item. |
|
|
52
|
+
| `is-disabled` | `step.disabled` | Applied to the list item. |
|
|
53
|
+
|
|
54
|
+
## CSS Variables
|
|
55
|
+
|
|
56
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
57
|
+
|---|---|---|---|---|---|---|
|
|
58
|
+
| `--pw_Stepper_root_default_background-color` | `root` | `default` | `background-color` | `var(--pw_background-color)` | Component background. | Web, Capacitor, NativeScript |
|
|
59
|
+
| `--pw_Stepper_root_disabled_opacity` | `root` | `disabled` | `opacity` | shared disabled opacity | Disabled step appearance. | Web, Capacitor, NativeScript |
|
|
60
|
+
| `--pw_Stepper_indicator_default_background-color` | `indicator` | `default` | `background-color` | `var(--pw_control_background-color)` | Indicator background. | Web, Capacitor, NativeScript |
|
|
61
|
+
| `--pw_Stepper_indicator_active_border-color` | `indicator` | `active` | `border-color` | strong text | Active indicator border. | Web, Capacitor, NativeScript |
|
|
62
|
+
| `--pw_Stepper_indicator_complete_background-color` | `indicator` | `complete` | `background-color` | shared text | Complete indicator background. | Web, Capacitor, NativeScript |
|
|
63
|
+
| `--pw_Stepper_indicator_default_outline` | `indicator` | `default` | `outline` | shared outline | Keyboard focus indicator. | Web, Capacitor |
|
|
64
|
+
| `--pw_Stepper_indicator-target_default_width` | `indicator-target` | `default` | `width` | `2.75em` | Pointer target width. | Web, Capacitor, NativeScript |
|
|
65
|
+
| `--pw_Stepper_indicator-target_default_height` | `indicator-target` | `default` | `height` | `2.75em` | Pointer target height. | Web, Capacitor, NativeScript |
|
|
66
|
+
| `--pw_Stepper_indicator_slot_size` | `indicator` | `default` | `width` / `height` | calculated | Indicator box used to centre labels. | Web, Capacitor |
|
|
67
|
+
| `--pw_Stepper_label_default_color` | `label` | `default` | `color` | `var(--pw_text_color)` | Label colour. | Web, Capacitor, NativeScript |
|
|
68
|
+
|
|
69
|
+
```xts
|
|
70
|
+
import { useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
|
|
71
|
+
import { stepperMediator } from '@pastweb/components-core/Stepper';
|
|
72
|
+
import { DEFAULT_PROPS, DEFAULT_STEP_PROPS } from './constants';
|
|
73
|
+
|
|
74
|
+
export function Stepper(props: StepperProps) {
|
|
75
|
+
const p = withDefaultProps<StepperDefaultProps>(props, DEFAULT_PROPS);
|
|
76
|
+
const m = useMediator<StepperMediator>(stepperMediator, p);
|
|
77
|
+
const s = m.state;
|
|
78
|
+
const steps = p.steps.map(step => withDefaultProps<StepDefaultProps>(step, DEFAULT_STEP_PROPS));
|
|
79
|
+
|
|
80
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div id={m.id} class={p.cls('Stepper', `is-${p.orientation}`, `labels-${s.labelPosition}`, p.className)}>
|
|
84
|
+
<ol class={p.cls('steps')} aria-label={s.ariaLabel}>
|
|
85
|
+
{steps.map((step, index) => {
|
|
86
|
+
const state = m.getStepState(index);
|
|
87
|
+
const titleId = `title${m.id}${index}`;
|
|
88
|
+
return (
|
|
89
|
+
<li class={p.cls('step', `is-${state}`, { 'is-disabled': step.disabled }, step.className)}
|
|
90
|
+
aria-current={state === 'active' ? 'step' : undefined}>
|
|
91
|
+
<div class={p.cls('step-main')}>
|
|
92
|
+
<button #if={step.onClick !== DEFAULT_STEP_PROPS.onClick} type="button"
|
|
93
|
+
class={p.cls('indicator')} disabled={step.disabled} @click={step.onClick}
|
|
94
|
+
aria-label={step.ariaLabel} aria-labelledby={!step.ariaLabel && step.label ? titleId : undefined}>
|
|
95
|
+
<span class={p.cls('indicator-content')} aria-hidden="true">{step.content ?? index + 1}</span>
|
|
96
|
+
</button>
|
|
97
|
+
<span #else class={p.cls('indicator')}>
|
|
98
|
+
<span class={p.cls('indicator-content')} aria-hidden="true">{step.content ?? index + 1}</span>
|
|
99
|
+
</span>
|
|
100
|
+
<span #if={index < steps.length - 1} class={p.cls('line')} aria-hidden="true" />
|
|
101
|
+
</div>
|
|
102
|
+
<div #if={step.label || step.description} class={p.cls('label')}>
|
|
103
|
+
<span #if={step.label} id={titleId} class={p.cls('title')}>{step.label}</span>
|
|
104
|
+
<small #if={step.description} id={`description${m.id}${index}`}>{step.description}</small>
|
|
105
|
+
</div>
|
|
106
|
+
</li>
|
|
107
|
+
);
|
|
108
|
+
})}
|
|
109
|
+
</ol>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Keyboard interaction
|
|
116
|
+
|
|
117
|
+
- Interactive steps are native buttons operable with `Enter` or `Space`.
|
|
118
|
+
- Disabled interactive steps are skipped in the tab order.
|
|
119
|
+
- Non-interactive steps do not enter the tab order.
|
|
120
|
+
|
|
121
|
+
## Accessibility notes
|
|
122
|
+
|
|
123
|
+
- The sequence is a named ordered list. The current item exposes `aria-current="step"`.
|
|
124
|
+
- Visible labels name interactive steps through `aria-labelledby`. Empty `ariaLabel` values fall back to `Step N of M` when no visible label is present.
|
|
125
|
+
- Indicator glyphs are hidden from assistive technology.
|
|
126
|
+
- Connecting lines are decorative.
|
|
127
|
+
- Pointer targets remain at least 44 CSS pixels even when the visible indicator is smaller.
|
|
128
|
+
- Shared web styles keep a visible `:focus-visible` indicator.
|
|
129
|
+
|
|
130
|
+
## Known limitations
|
|
131
|
+
|
|
132
|
+
- Nested steppers require distinct labels so the sequences remain distinguishable.
|
|
133
|
+
- Automated accessibility checks do not by themselves establish WCAG conformance.
|
|
134
|
+
|
|
135
|
+
## Native renderer mapping
|
|
136
|
+
|
|
137
|
+
Native renderers use an accessible ordered collection. Do not copy ARIA
|
|
138
|
+
attributes into native markup. Interactive steps map to native buttons with
|
|
139
|
+
native disabled state. Focus restoration and current-step identification use
|
|
140
|
+
the platform equivalent of `aria-current="step"`.
|
|
141
|
+
|
|
142
|
+
## Standards
|
|
143
|
+
|
|
144
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
145
|
+
- WAI-ARIA APG patterns: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
146
|
+
- GOV.UK task list: https://design-system.service.gov.uk/components/task-list/
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Table provides data-grid primitives for column visibility, ordering, pinning,
|
|
4
|
+
sorting, filtering, grouping, expansion, and row selection. The mediator owns
|
|
5
|
+
the table state and generated identifiers; framework renderers only connect it
|
|
6
|
+
to their native table elements.
|
|
7
|
+
|
|
8
|
+
## Identifier lifecycle
|
|
9
|
+
|
|
10
|
+
Each mediator instance creates an identifier with `idCache.getId('table')`.
|
|
11
|
+
Renderers use `m.id` as the prefix for header and cell identifiers, keeping ARIA
|
|
12
|
+
relationships unique across multiple tables. `m.useBeforeUnmount()` clears both
|
|
13
|
+
row identity records and the generated identifier.
|
|
14
|
+
|
|
15
|
+
## Mediator API
|
|
16
|
+
|
|
17
|
+
| Property | Type | Description |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `id` | `string` | Unique generated prefix for table accessibility identifiers. |
|
|
20
|
+
| `state` | `TableState` | Reactive columns, rows, controlled values, and drag state. |
|
|
21
|
+
| `table` | `TableInstance` | Public table instance used by consumers and slots. |
|
|
22
|
+
| `onMounted` | `(element: HTMLTableElement) => void` | Attaches the rendered table so header groups can be measured. |
|
|
23
|
+
| `onScroll` | `(event: unknown) => void` | Forwards scrolling from the renderer's scrollable table body. |
|
|
24
|
+
| `useBeforeUnmount` | `() => void` | Releases row IDs and the table identifier. |
|
|
25
|
+
|
|
26
|
+
## Renderer contract
|
|
27
|
+
|
|
28
|
+
```xts
|
|
29
|
+
import { useBeforeUnmount, useMediator, useMounted, withDefaultProps } from '@pastweb/x';
|
|
30
|
+
import { tableMediator } from '@pastweb/components-core/Table';
|
|
31
|
+
import { DEFAULT_PROPS } from './constants';
|
|
32
|
+
|
|
33
|
+
export function Table(props: TableProps) {
|
|
34
|
+
const p = withDefaultProps<TableDefaultProps>(props, DEFAULT_PROPS);
|
|
35
|
+
const m = useMediator<TableMediator>(tableMediator, p);
|
|
36
|
+
const s = m.state;
|
|
37
|
+
|
|
38
|
+
useMounted(element => m.onMounted(element));
|
|
39
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<table aria-label={p.caption ? undefined : p.ariaLabel}>
|
|
43
|
+
<caption #if={p.caption}>{p.caption}</caption>
|
|
44
|
+
<thead>
|
|
45
|
+
<tr #for={group of s.headerGroups}>
|
|
46
|
+
<th
|
|
47
|
+
#for={header of group.headers}
|
|
48
|
+
id={`${m.id}-${header.column.id}`}
|
|
49
|
+
scope="col"
|
|
50
|
+
>
|
|
51
|
+
<render content={header.column.columnDef.header} props={{ column: header.column }} />
|
|
52
|
+
</th>
|
|
53
|
+
</tr>
|
|
54
|
+
</thead>
|
|
55
|
+
<tbody>{/* render s.rows with the same m.id prefix for labelled cells */}</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Always register `m.useBeforeUnmount` in the framework renderer. Do not create
|
|
62
|
+
table identifiers in a framework-specific hook: a mediator-owned ID makes the
|
|
63
|
+
same accessibility contract portable to every supported renderer.
|
|
64
|
+
|
|
65
|
+
## Body scrolling
|
|
66
|
+
|
|
67
|
+
Renderers should attach `m.onScroll` to the scrollable element that contains the
|
|
68
|
+
table body. The core mediator deliberately forwards the event without assuming
|
|
69
|
+
a browser event type, so each renderer can use its native event while consumers
|
|
70
|
+
can implement behaviors such as infinite loading.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
<div onScroll={m.onScroll}>
|
|
74
|
+
<table><!-- rows --></table>
|
|
75
|
+
</div>
|
|
76
|
+
```
|