@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,141 @@
|
|
|
1
|
+
# TextArea
|
|
2
|
+
|
|
3
|
+
TextArea is a labelled multiline text control with explicit disabled, read-only,
|
|
4
|
+
required, resizable, and loading states. Its rendering contract targets WCAG
|
|
5
|
+
2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use TextArea for multi-line values. Use Input for a single line. Prefer a
|
|
10
|
+
visible label; placeholders do not replace labels. If there is a good reason to
|
|
11
|
+
limit length, associate a character-count message through `id` rather than
|
|
12
|
+
relying on `maxLength` alone.
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| PropName | Type | Required | Default | Description |
|
|
17
|
+
|---|---|---:|---:|---|
|
|
18
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component, part, and state classes. |
|
|
19
|
+
| `ariaLabel` | `string` | no | `"TextArea"` | Names the field when no visible `label` is supplied. Pass an empty string to omit it. |
|
|
20
|
+
| `className` | `string` | no | `""` | Additional root classes. The `is-loading` class is also treated as a loading signal. |
|
|
21
|
+
| `cols` | `number` | no | - | Native column count. |
|
|
22
|
+
| `disabled` | `boolean` | no | - | Makes the field unavailable. |
|
|
23
|
+
| `id` | `string` | no | generated | Native textarea ID. Generated IDs use the `textArea` prefix. |
|
|
24
|
+
| `label` | `string` | no | `""` | Visible accessible label. |
|
|
25
|
+
| `labelPlacement` | `Placement` | no | `'top-start'` | Visual label placement. |
|
|
26
|
+
| `loading` | `boolean` | no | - | Marks the field busy and temporarily unavailable. |
|
|
27
|
+
| `maxLength` | `number` | no | - | Native maximum character length. |
|
|
28
|
+
| `name` | `string` | no | - | Native form name. |
|
|
29
|
+
| `onChange` | `(value: string) => void` | no | noop | Reports edited text. |
|
|
30
|
+
| `placeholder` | `string` | no | `""` | Short input hint. Do not use it as the only label. |
|
|
31
|
+
| `readOnly` | `boolean` | no | - | Prevents editing and applies `is-readonly`. |
|
|
32
|
+
| `required` | `boolean` | no | - | Marks the value as required. |
|
|
33
|
+
| `resizeable` | `boolean` | no | - | Enables two-axis web resizing and `is-resizeable`. |
|
|
34
|
+
| `rows` | `number` | no | - | Native row count. |
|
|
35
|
+
| `tabIndex` | `number` | no | `0` | Sequential focus order. Prefer document order. |
|
|
36
|
+
| `value` | `string` | no | `""` | Controlled value. |
|
|
37
|
+
|
|
38
|
+
The named `loading` slot defaults to “Loading...”.
|
|
39
|
+
|
|
40
|
+
## Utility classes
|
|
41
|
+
|
|
42
|
+
Apply these classes through `className`.
|
|
43
|
+
|
|
44
|
+
| Class | Description |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `is-rounded` | Uses the rounded control radius token. |
|
|
47
|
+
| `is-full-width` | Expands the field to the available width. |
|
|
48
|
+
| `is-loading` | Compatibility signal for the loading state. Prefer the `loading` prop. |
|
|
49
|
+
|
|
50
|
+
## Mediator state
|
|
51
|
+
|
|
52
|
+
| Property | Type | Description |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `ariaLabel` | `string \| undefined` | Normalized accessible name when no visible `label` is present. Empty labels are omitted. |
|
|
55
|
+
| `inputId` | `string` | Native textarea ID from an explicit `id` or a generated `textArea` prefix. |
|
|
56
|
+
| `isDisabled` | `true \| undefined` | Is `true` when disabled; otherwise omitted. |
|
|
57
|
+
| `isLoading` | `true \| undefined` | Is `true` when busy; otherwise omitted. |
|
|
58
|
+
| `isReadOnly` | `true \| undefined` | Is `true` when read-only; otherwise omitted. |
|
|
59
|
+
| `isResizeable` | `true \| undefined` | Is `true` when resizing is enabled; otherwise omitted. |
|
|
60
|
+
|
|
61
|
+
## CSS Variables
|
|
62
|
+
|
|
63
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
64
|
+
|---|---|---|---|---|---|---|
|
|
65
|
+
| `--pw_TextArea_root_default_margin` | `root` | `default` | `margin` | shared margins | Component spacing. | Web, Capacitor, NativeScript |
|
|
66
|
+
| `--pw_TextArea_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled appearance. | Web, Capacitor, NativeScript |
|
|
67
|
+
| `--pw_TextArea_control_default_min-height` | `control` | `default` | `min-height` | `2.75em` | Minimum control height, with a 44px web target. | Web, Capacitor, NativeScript |
|
|
68
|
+
| `--pw_TextArea_root_default_background-color` | `control` | `default` | `background-color` | `var(--pw_control_background-color)` | Control background. | Web, Capacitor, NativeScript |
|
|
69
|
+
| `--pw_TextArea_root_default_color` | `control` | `default` | `color` | `var(--pw_text_color)` | Control foreground. | Web, Capacitor, NativeScript |
|
|
70
|
+
| `--pw_TextArea_root_default_border-color` | `control` | `default` | `border-color` | `var(--pw_border-color)` | Border colour. | Web, Capacitor, NativeScript |
|
|
71
|
+
| `--pw_TextArea_root_default_border-radius` | `control` | `default` | `border-radius` | `var(--pw_border-radius)` | Corner radius. | Web, Capacitor, NativeScript |
|
|
72
|
+
| `--pw_TextArea_label_default_color` | `label` | `default` | `color` | `var(--pw_label_text_color)` | Label colour. | Web, Capacitor, NativeScript |
|
|
73
|
+
| `--pw_TextArea_loading_default_background-color` | `loading` | `default` | `background-color` | control background | Loading overlay. | Web, Capacitor, NativeScript |
|
|
74
|
+
|
|
75
|
+
```xts
|
|
76
|
+
import { useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
|
|
77
|
+
import { textAreaMediator } from '@pastweb/components-core/TextArea';
|
|
78
|
+
import { DEFAULT_PROPS } from './constants';
|
|
79
|
+
|
|
80
|
+
export function TextArea(props: TextAreaProps) {
|
|
81
|
+
const p = withDefaultProps<TextAreaDefaultProps>(props, DEFAULT_PROPS);
|
|
82
|
+
const m = useMediator<TextAreaMediator>(textAreaMediator, p);
|
|
83
|
+
const s = m.state;
|
|
84
|
+
|
|
85
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div class={p.cls('TextArea', `label-${p.labelPlacement}`, {
|
|
89
|
+
'is-disabled': s.isDisabled, 'is-loading': s.isLoading, 'is-readonly': s.isReadOnly, 'is-resizeable': s.isResizeable,
|
|
90
|
+
}, p.className)} aria-busy={s.isLoading}>
|
|
91
|
+
<div class={p.cls('textarea')}>
|
|
92
|
+
<textarea id={s.inputId} class={p.cls('input')} disabled={s.isDisabled || s.isLoading} readonly={s.isReadOnly}
|
|
93
|
+
required={p.required} value={p.value} aria-label={s.ariaLabel} aria-busy={s.isLoading} aria-disabled={s.isDisabled}
|
|
94
|
+
@change={event => m.onChange(event.target.value)} />
|
|
95
|
+
<div #if={s.isLoading} class={p.cls('loading')} role="status" aria-live="polite">
|
|
96
|
+
<slot name="loading">Loading...</slot>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<label #if={p.label} class={p.cls('label', p.labelPlacement)} for={s.inputId}>{p.label}</label>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Keyboard interaction
|
|
106
|
+
|
|
107
|
+
- Printable keys, `Backspace`, and `Delete` edit the value through native
|
|
108
|
+
textarea behavior.
|
|
109
|
+
- `Tab` and `Shift+Tab` move sequential focus.
|
|
110
|
+
|
|
111
|
+
## Accessibility notes
|
|
112
|
+
|
|
113
|
+
- The native textarea supplies keyboard, form, and validation behavior.
|
|
114
|
+
- Visible `label` text is connected through `for`/`id`. When it is absent,
|
|
115
|
+
`ariaLabel` names the field. The default name is `TextArea`.
|
|
116
|
+
- Disabled fields use `.is-disabled`, native `disabled`, and `aria-disabled`.
|
|
117
|
+
- Loading disables the field, applies `aria-busy`, and displays a polite status
|
|
118
|
+
region.
|
|
119
|
+
- Shared web styles provide at least a 44 by 44 CSS-pixel target, visible
|
|
120
|
+
`:focus-visible` indication, forced-colours treatment, and reduced-motion
|
|
121
|
+
behavior.
|
|
122
|
+
|
|
123
|
+
## Native renderer mapping
|
|
124
|
+
|
|
125
|
+
Native renderers use a multiline editable text view. Do not copy ARIA attributes
|
|
126
|
+
into native markup.
|
|
127
|
+
|
|
128
|
+
## Known limitations
|
|
129
|
+
|
|
130
|
+
- Placeholders do not replace a visible or accessible name.
|
|
131
|
+
- Hint, error, and character-count text are not built in. Associate them with
|
|
132
|
+
the native textarea through `id` when the surrounding form provides that
|
|
133
|
+
content.
|
|
134
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
135
|
+
conformance.
|
|
136
|
+
|
|
137
|
+
## Standards
|
|
138
|
+
|
|
139
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
140
|
+
- WAI-ARIA Textbox pattern: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
141
|
+
- GOV.UK textarea: https://design-system.service.gov.uk/components/textarea/
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# ToastController
|
|
2
|
+
|
|
3
|
+
ToastController presents a temporary, politely announced status message. Its
|
|
4
|
+
rendering contract targets WCAG 2.2 Level AA.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use ToastController for non-urgent confirmation or status that can disappear.
|
|
9
|
+
Use Notification for a persistent in-page message, an alert pattern for urgent
|
|
10
|
+
information, and a dialog when the user must decide before continuing.
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
|
|
14
|
+
| PropName | Type | Required | Default | Description |
|
|
15
|
+
|---|---|---:|---:|---|
|
|
16
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
17
|
+
| `ariaLabel` | `string` | no | `""` | Names the status region. Pass an empty string to omit it. |
|
|
18
|
+
| `portal` | `Portal` | no | - | Portal configuration. |
|
|
19
|
+
| `portalElement` | `HTMLElement` | no | - | Portal container. |
|
|
20
|
+
| `entryId` | `string` | no | - | Portal entry ID. |
|
|
21
|
+
| `placement` | `PLACEMENT` | no | `top-right` | Viewport placement. |
|
|
22
|
+
| `padding` | `number` | no | `15` | Inner padding in pixels. |
|
|
23
|
+
| `delay` | `number` | no | `5` | Auto-dismiss delay in seconds. |
|
|
24
|
+
| `timeUpdate` | `number` | no | `10` | Timer tick interval. |
|
|
25
|
+
| `animation` | `Animation` | no | `slide` | Entry and exit animation. |
|
|
26
|
+
| `closeOnEscKey` | `boolean` | no | - | Dismisses on `Escape`. |
|
|
27
|
+
| `pauseTimerOnOver` | `boolean` | no | - | Pauses the timer on pointer or keyboard focus. |
|
|
28
|
+
| `resetTimerOnPause` | `boolean` | no | - | Resets the timer when pausing. |
|
|
29
|
+
| `onClose` | `() => void` | no | noop | Reports close intent. |
|
|
30
|
+
| `onDestroy` | `() => void` | no | noop | Reports portal removal. |
|
|
31
|
+
| `onKeyDown` | `(event) => void` | no | noop | Observes keyboard input. |
|
|
32
|
+
|
|
33
|
+
## XTS rendering contract
|
|
34
|
+
|
|
35
|
+
```xts
|
|
36
|
+
import { setContext, useMediator, useRef, withDefaultProps } from '@pastweb/x';
|
|
37
|
+
import {
|
|
38
|
+
JUSTIFY,
|
|
39
|
+
TOAST_CONTEXT_KEY,
|
|
40
|
+
toastControllerMediator,
|
|
41
|
+
} from '@pastweb/components-core/ToastController';
|
|
42
|
+
import { DEFAULT_PROPS } from './constants';
|
|
43
|
+
|
|
44
|
+
export function ToastController(props: ToastControllerProps) {
|
|
45
|
+
const p = withDefaultProps<ToastControllerDefaultProps>(props, DEFAULT_PROPS);
|
|
46
|
+
const m = useMediator<ToastControllerMediator>(toastControllerMediator, p);
|
|
47
|
+
const s = m.state;
|
|
48
|
+
const c = m.getContext();
|
|
49
|
+
const toastReference = useRef(null);
|
|
50
|
+
|
|
51
|
+
setContext(TOAST_CONTEXT_KEY, c);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
#ref={(element) => {
|
|
56
|
+
toastReference.value = element;
|
|
57
|
+
if (element) m.onMounted(element);
|
|
58
|
+
}}
|
|
59
|
+
class={p.cls('ToastController', p.placement)}
|
|
60
|
+
style={{ [s.y]: s.top || s.bottom, justifyContent: JUSTIFY[s.x] }}
|
|
61
|
+
>
|
|
62
|
+
<div
|
|
63
|
+
class={p.cls('toast', {
|
|
64
|
+
[`${p.animation.name}-in-${s.x}-${s.y}`]: s.isOpen,
|
|
65
|
+
[`${p.animation.name}-out-${s.x}-${s.y}`]: !s.isOpen,
|
|
66
|
+
})}
|
|
67
|
+
role='status'
|
|
68
|
+
aria-live='polite'
|
|
69
|
+
aria-atomic='true'
|
|
70
|
+
aria-label={p.ariaLabel || undefined}
|
|
71
|
+
@keydown={m.onKeyDown}
|
|
72
|
+
@mouseenter={m.onMouseOver}
|
|
73
|
+
@mouseleave={m.onMouseLeave}
|
|
74
|
+
@focus={m.onMouseOver}
|
|
75
|
+
@blur={m.onMouseLeave}
|
|
76
|
+
>
|
|
77
|
+
<slot />
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
ToastController is a browser-only portal controller. Create it only while its
|
|
85
|
+
portal element is available; it is not part of initial server rendering.
|
|
86
|
+
|
|
87
|
+
## Keyboard interaction
|
|
88
|
+
|
|
89
|
+
- `Escape` dismisses the toast when `closeOnEscKey` is enabled.
|
|
90
|
+
- The toast does not enter sequential tab order. Interactive content inside it
|
|
91
|
+
remains operable.
|
|
92
|
+
|
|
93
|
+
## Accessibility notes
|
|
94
|
+
|
|
95
|
+
- The toast is a polite `status` live region. It does not move focus on open.
|
|
96
|
+
- The auto-dismiss timer pauses while the pointer is over the toast, while it
|
|
97
|
+
has keyboard focus, and while it is not fully visible.
|
|
98
|
+
- Time limits can be paused (`pauseTimerOnOver`) and dismissed early (`Escape`
|
|
99
|
+
or a consumer close control).
|
|
100
|
+
- Shared web styles keep a visible `:focus-visible` indicator and disable
|
|
101
|
+
non-essential motion under `prefers-reduced-motion`.
|
|
102
|
+
|
|
103
|
+
## Known limitations
|
|
104
|
+
|
|
105
|
+
- A visible close control is not built in. Supply one in the toast content when
|
|
106
|
+
users must dismiss without waiting.
|
|
107
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
108
|
+
conformance.
|
|
109
|
+
|
|
110
|
+
## Native renderer mapping
|
|
111
|
+
|
|
112
|
+
Native renderers map the toast to a native polite announcement and timed overlay.
|
|
113
|
+
Do not copy ARIA attributes into native markup.
|
|
114
|
+
|
|
115
|
+
## Standards
|
|
116
|
+
|
|
117
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
118
|
+
- WAI-ARIA Status and alert patterns: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
119
|
+
- GOV.UK notification banner: https://design-system.service.gov.uk/components/notification-banner/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Toggle
|
|
2
|
+
|
|
3
|
+
Toggle represents an immediate binary setting. The web renderer uses a native
|
|
4
|
+
checkbox with `role="switch"`. Its rendering contract follows the WAI-ARIA
|
|
5
|
+
Switch pattern and targets WCAG 2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use Toggle for an immediate on/off setting. Use Checkbox for a form choice that
|
|
10
|
+
is submitted later. Prefer a pair of radios when both options need visible
|
|
11
|
+
labels, such as “On” and “Off”.
|
|
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 | `"Toggle"` | Names the switch when no visible `label` is supplied. Pass an empty string to omit it. |
|
|
19
|
+
| `className` | `string` | no | `""` | Additional root classes. |
|
|
20
|
+
| `disabled` | `boolean` | no | - | Disables the control and applies `is-disabled`. |
|
|
21
|
+
| `id` | `string \| number` | no | generated | Native control ID. Generated IDs use the `toggle` prefix. |
|
|
22
|
+
| `label` | `string` | no | `""` | Visible accessible label. |
|
|
23
|
+
| `labelPlacement` | `Placement` | no | `'top-start'` | Visual label position. |
|
|
24
|
+
| `name` | `string` | no | - | Native form name. |
|
|
25
|
+
| `onChange` | `(checked, event) => void` | no | noop | Reports state changes. |
|
|
26
|
+
| `tabIndex` | `number` | no | `0` | Sequential focus order. |
|
|
27
|
+
| `value` | `boolean` | no | - | Controlled checked state. |
|
|
28
|
+
|
|
29
|
+
## Mediator state
|
|
30
|
+
|
|
31
|
+
| Property | Type | Description |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `ariaLabel` | `string \| undefined` | Normalized accessible name when no visible `label` is present. |
|
|
34
|
+
| `inputId` | `string` | Native input ID from an explicit `id` or a generated `toggle` prefix. |
|
|
35
|
+
| `isDisabled` | `true \| undefined` | Is `true` when the Toggle is disabled; otherwise omitted. |
|
|
36
|
+
| `value` | `boolean` | Checked state used for `.is-checked`, native `checked`, and `aria-checked`. |
|
|
37
|
+
|
|
38
|
+
## CSS Variables
|
|
39
|
+
|
|
40
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
41
|
+
|---|---|---|---|---|---|---|
|
|
42
|
+
| `--pw_Toggle_root_default_margin` | `root` | `default` | `margin` | shared margins | Component spacing. | Web, Capacitor, NativeScript |
|
|
43
|
+
| `--pw_Toggle_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled appearance. | Web, Capacitor, NativeScript |
|
|
44
|
+
| `--pw_Toggle_background_default_width` | `track` | `default` | `width` | `4em` | Track width. | Web, Capacitor, NativeScript |
|
|
45
|
+
| `--pw_Toggle_background_default_height` | `track` | `default` | `height` | `2.75em` | Track height, with a 44px web target. | Web, Capacitor, NativeScript |
|
|
46
|
+
| `--pw_Toggle_root_default_background-color` | `track` | `default` | `background-color` | `var(--pw_control_background-color)` | Unchecked track. | Web, Capacitor, NativeScript |
|
|
47
|
+
| `--pw_Toggle_label_default_color` | `label` | `default` | `color` | `var(--pw_label_text_color)` | Label colour. | Web, Capacitor, NativeScript |
|
|
48
|
+
|
|
49
|
+
```xts
|
|
50
|
+
import { useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
|
|
51
|
+
import { toggleMediator } from '@pastweb/components-core/Toggle';
|
|
52
|
+
import { DEFAULT_PROPS } from './constants';
|
|
53
|
+
|
|
54
|
+
export function Toggle(props: ToggleProps) {
|
|
55
|
+
const p = withDefaultProps<ToggleDefaultProps>(props, DEFAULT_PROPS);
|
|
56
|
+
const m = useMediator<ToggleMediator>(toggleMediator, p);
|
|
57
|
+
const s = m.state;
|
|
58
|
+
|
|
59
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div class={p.cls('Toggle', `label-${p.labelPlacement}`, { 'is-checked': s.value, 'is-disabled': s.isDisabled }, p.className)}>
|
|
63
|
+
<label class={p.cls('control')}>
|
|
64
|
+
<span class={p.cls('button')}>
|
|
65
|
+
<input type="checkbox" role="switch" id={s.inputId} checked={s.value} disabled={s.isDisabled}
|
|
66
|
+
aria-checked={s.value} aria-disabled={s.isDisabled} aria-label={s.ariaLabel}
|
|
67
|
+
@change={event => m.onChange(event.target.checked, event)} />
|
|
68
|
+
</span>
|
|
69
|
+
<span #if={p.label} class={p.cls('label', p.labelPlacement)}>{p.label}</span>
|
|
70
|
+
</label>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Keyboard interaction
|
|
77
|
+
|
|
78
|
+
- `Space` toggles the native switch.
|
|
79
|
+
- `Tab` and `Shift+Tab` move sequential focus.
|
|
80
|
+
|
|
81
|
+
## Accessibility notes
|
|
82
|
+
|
|
83
|
+
- The web renderer uses a native checkbox with `role="switch"` and
|
|
84
|
+
`aria-checked`.
|
|
85
|
+
- One wrapping label supplies the visible accessible name. When visible label
|
|
86
|
+
text is absent, the mediator exposes `ariaLabel`.
|
|
87
|
+
- Disabled switches use native `disabled`, `aria-disabled="true"`, and
|
|
88
|
+
`is-disabled`.
|
|
89
|
+
- Shared web styles provide at least a 44 by 44 CSS-pixel target, visible
|
|
90
|
+
`:focus-visible` indication, forced-colours treatment, and reduced-motion
|
|
91
|
+
behavior.
|
|
92
|
+
|
|
93
|
+
## Native renderer mapping
|
|
94
|
+
|
|
95
|
+
Native renderers map Toggle to the platform switch control. Do not copy ARIA
|
|
96
|
+
attributes into native markup.
|
|
97
|
+
|
|
98
|
+
## Known limitations
|
|
99
|
+
|
|
100
|
+
- GOV.UK prefers radios or checkboxes when both options need equal visible
|
|
101
|
+
labels. Use Toggle only for an immediate setting.
|
|
102
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
103
|
+
conformance.
|
|
104
|
+
|
|
105
|
+
## Standards
|
|
106
|
+
|
|
107
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
108
|
+
- WAI-ARIA Switch pattern: https://www.w3.org/WAI/ARIA/apg/patterns/switch/
|
|
109
|
+
- GOV.UK radios: https://design-system.service.gov.uk/components/radios/
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
Tooltip provides a short, non-interactive description for a focused or hovered
|
|
4
|
+
control. Its rendering contract follows the WAI-ARIA Tooltip pattern and
|
|
5
|
+
targets WCAG 2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use Tooltip to supplement a control’s visible name with extra description. Do
|
|
10
|
+
not put essential instructions, errors, or interactive content in a tooltip.
|
|
11
|
+
Prefer visible hint text, especially in forms. Hover-only UI is unreliable on
|
|
12
|
+
touch devices.
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| PropName | Type | Required | Default | Description |
|
|
17
|
+
|---|---|---:|---:|---|
|
|
18
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
19
|
+
| `content` | `string` | no | `""` | Tooltip description. Empty content never opens. |
|
|
20
|
+
| `id` | `string` | no | generated | Stable ID used by the trigger description relationship. Generated IDs use the `tooltip` prefix. |
|
|
21
|
+
| `placement` | `TooltipPlacement` | no | `'top'` | Preferred side and optional start/end alignment. |
|
|
22
|
+
| `offset` | `number` | no | `8` | Renderer positioning gap. |
|
|
23
|
+
| `showDelay` | `number` | no | `300` | Pointer-hover opening delay in milliseconds. Focus opens immediately. |
|
|
24
|
+
| `hideDelay` | `number` | no | `100` | Pointer-leave closing delay in milliseconds. Blur and Escape close immediately. |
|
|
25
|
+
| `disabled` | `boolean` | no | - | Prevents opening and applies `is-disabled`. |
|
|
26
|
+
| `className` | `string` | no | `""` | Additional root classes. |
|
|
27
|
+
| `tooltipClassName` | `string` | no | `""` | Additional tooltip-surface classes. |
|
|
28
|
+
|
|
29
|
+
`TooltipPlacement` supports `top`, `right`, `bottom`, and `left`, each
|
|
30
|
+
optionally followed by `-start` or `-end`. `Placement` remains a deprecated
|
|
31
|
+
alias.
|
|
32
|
+
|
|
33
|
+
## Mediator state
|
|
34
|
+
|
|
35
|
+
| Property | Type | Description |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `isDisabled` | `true \| undefined` | Is `true` when the Tooltip is disabled; otherwise omitted. |
|
|
38
|
+
| `isVisible` | `boolean` | Whether the tooltip is requested open. |
|
|
39
|
+
| `tooltipId` | `string` | Tooltip relationship ID from an explicit `id` or a generated `tooltip` prefix. |
|
|
40
|
+
|
|
41
|
+
## CSS Variables
|
|
42
|
+
|
|
43
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
44
|
+
|---|---|---|---|---|---|---|
|
|
45
|
+
| `--pw_Tooltip_content_default_background-color` | `content` | `default` | `background-color` | `var(--pw_background-color)` | Tooltip background. | Web, Capacitor |
|
|
46
|
+
| `--pw_Tooltip_content_default_color` | `content` | `default` | `color` | `var(--pw_text_color)` | Tooltip foreground. | Web, Capacitor |
|
|
47
|
+
| `--pw_Tooltip_content_default_border-color` | `content` | `default` | `border-color` | `var(--pw_border-color)` | Border colour. | Web, Capacitor |
|
|
48
|
+
| `--pw_Tooltip_content_default_max-width` | `content` | `default` | `max-width` | `20rem` | Maximum tooltip width. | Web, Capacitor |
|
|
49
|
+
| `--pw_Tooltip_content_default_font-size` | `content` | `default` | `font-size` | shared font size | Tooltip type size. | Web, Capacitor |
|
|
50
|
+
| `--pw_Tooltip_content_default_line-height` | `content` | `default` | `line-height` | `1.5em` | Tooltip line box. With vertical padding this matches the arrow box. | Web, Capacitor |
|
|
51
|
+
| `--pw_Tooltip_content_default_padding` | `content` | `default` | `padding` | `0.25em` plus shared horizontal padding | Inner spacing, in `em` so it tracks font size. | Web, Capacitor |
|
|
52
|
+
| `--pw_Tooltip_content_default_drop-shadow` | `content` | `default` | `filter` | `0 0 0.2em` shared shadow colour | Shadow that follows the tooltip and arrow. | Web, Capacitor |
|
|
53
|
+
| `--pw_Tooltip_arrow_default_size` | `arrow` | `default` | `size` | `1em` | Decorative arrow size, in `em` so it tracks the tooltip font. | Web, Capacitor |
|
|
54
|
+
|
|
55
|
+
```xts
|
|
56
|
+
import { useBeforeUnmount, useMediator, useMounted, useRef, withDefaultProps } from '@pastweb/x';
|
|
57
|
+
import { tooltipMediator } from '@pastweb/components-core/Tooltip';
|
|
58
|
+
import { DEFAULT_PROPS } from './constants';
|
|
59
|
+
|
|
60
|
+
export function Tooltip(props: TooltipProps) {
|
|
61
|
+
const p = withDefaultProps<TooltipDefaultProps>(props, DEFAULT_PROPS);
|
|
62
|
+
const m = useMediator<TooltipMediator>(tooltipMediator, p);
|
|
63
|
+
const s = m.state;
|
|
64
|
+
const trigger = useRef(null);
|
|
65
|
+
const tooltip = useRef(null);
|
|
66
|
+
const arrow = useRef(null);
|
|
67
|
+
|
|
68
|
+
useMounted(() => m.useMounted(trigger.value, tooltip.value, arrow.value));
|
|
69
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<span class={p.cls('Tooltip', { 'is-disabled': s.isDisabled }, p.className)}
|
|
73
|
+
@pointerenter={event => event.pointerType !== 'touch' && m.show()}
|
|
74
|
+
@pointerleave={() => m.hide()} @focusin={() => m.show(true)}
|
|
75
|
+
@focusout={() => m.hide(true)} @keydown={m.onKeyDown}>
|
|
76
|
+
<span #ref={trigger} class={p.cls('trigger')} aria-describedby={s.isDisabled ? undefined : s.tooltipId}>
|
|
77
|
+
<slot />
|
|
78
|
+
</span>
|
|
79
|
+
<div #ref={tooltip} id={s.tooltipId} role="tooltip"
|
|
80
|
+
class={p.cls('tooltipContent', { 'is-visible': s.isVisible }, p.tooltipClassName)}>
|
|
81
|
+
<Render content={p.content} />
|
|
82
|
+
<div #ref={arrow} class={p.cls('arrow')} aria-hidden="true" />
|
|
83
|
+
</div>
|
|
84
|
+
</span>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Keyboard interaction
|
|
90
|
+
|
|
91
|
+
- Focus on the trigger opens the tooltip immediately.
|
|
92
|
+
- Blur closes the tooltip immediately.
|
|
93
|
+
- `Escape` dismisses an open tooltip without moving focus.
|
|
94
|
+
- The tooltip itself does not enter the tab order.
|
|
95
|
+
|
|
96
|
+
## Accessibility notes
|
|
97
|
+
|
|
98
|
+
- The popup uses `role="tooltip"`. The trigger references it with
|
|
99
|
+
`aria-describedby`.
|
|
100
|
+
- A single element trigger receives `aria-describedby` on that element. Plain
|
|
101
|
+
text triggers are wrapped in a focusable span.
|
|
102
|
+
- Pointer hover opens after `showDelay`. Pointer leave closes after
|
|
103
|
+
`hideDelay` so the pointer can move onto the tooltip (WCAG 1.4.13).
|
|
104
|
+
- Touch pointer entry is ignored because hover-only UI is unreliable on touch
|
|
105
|
+
devices.
|
|
106
|
+
- Empty `content` never opens.
|
|
107
|
+
- The decorative arrow is hidden from assistive technology.
|
|
108
|
+
- Shared web styles wrap long text, keep a visible trigger focus indicator,
|
|
109
|
+
support forced colours, and disable non-essential motion under
|
|
110
|
+
`prefers-reduced-motion`.
|
|
111
|
+
|
|
112
|
+
## Native renderer mapping
|
|
113
|
+
|
|
114
|
+
NativeScript renderers map show/hide intent to a native accessible hint or
|
|
115
|
+
non-interactive popup. Do not copy ARIA attributes into native markup.
|
|
116
|
+
|
|
117
|
+
## Known limitations
|
|
118
|
+
|
|
119
|
+
- Tooltip content must not be interactive. Use a dialog or disclosure for
|
|
120
|
+
actions or forms.
|
|
121
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
122
|
+
conformance.
|
|
123
|
+
|
|
124
|
+
## Standards
|
|
125
|
+
|
|
126
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
127
|
+
- WAI-ARIA Tooltip pattern: https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/
|
|
128
|
+
- GOV.UK components: https://design-system.service.gov.uk/components/
|