@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,352 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
Tabs switches between related sections of content in the same view. Its
|
|
4
|
+
rendering contract follows the WAI-ARIA Tabs pattern with automatic activation
|
|
5
|
+
and targets WCAG 2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use Tabs when users need to switch among a small set of related panels without
|
|
10
|
+
leaving the page. Do not use Tabs for site navigation, for a sequence of form
|
|
11
|
+
questions, or when users must compare two sections at once. Prefer separate
|
|
12
|
+
pages, headings with in-page links, or an accordion when those are simpler.
|
|
13
|
+
Keep labels short. Put a heading that repeats the tab label at the start of
|
|
14
|
+
each panel.
|
|
15
|
+
|
|
16
|
+
## Tabs props
|
|
17
|
+
|
|
18
|
+
| PropName | Type | Required | Default | Description |
|
|
19
|
+
|---|---|---:|---:|---|
|
|
20
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component, part, state, and utility classes. |
|
|
21
|
+
| `activeTab` | `number` | no | `0` | Controls the active zero-based index. |
|
|
22
|
+
| `ariaLabel` | `string` | no | `"Tabs"` | Names the tab list. An explicit empty string omits the label. |
|
|
23
|
+
| `className` | `string` | no | `""` | Adds root classes, including utilities that determine layout and inherited descendant styling. |
|
|
24
|
+
| `disabled` | `boolean` | no | - | Disables all Tab controls and applies `is-disabled` to the root. |
|
|
25
|
+
| `onChange` | `(index: number) => void` | no | noop | Reports requested selection changes. |
|
|
26
|
+
|
|
27
|
+
## Tab props
|
|
28
|
+
|
|
29
|
+
| PropName | Type | Required | Default | Description |
|
|
30
|
+
|---|---|---:|---:|---|
|
|
31
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves the Tab part, state, and inherited utility classes. |
|
|
32
|
+
| `ariaLabel` | `string` | no | - | Supplies an accessible name when the visible label is insufficient. |
|
|
33
|
+
| `className` | `string` | no | `""` | Adds classes to this Tab list item. |
|
|
34
|
+
| `disabled` | `boolean` | no | - | Makes this Tab unavailable for activation and keyboard selection. |
|
|
35
|
+
| `onClick` | `() => void` | no | noop | Reports successful activation after the Tabs mediator updates selection. |
|
|
36
|
+
|
|
37
|
+
## Mediator state
|
|
38
|
+
|
|
39
|
+
### Tabs state
|
|
40
|
+
|
|
41
|
+
| Property | Type | Description |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `activeTab` | `number` | Current normalized zero-based active index. |
|
|
44
|
+
| `ariaLabel` | `string \| undefined` | Normalized tab-list accessible label. |
|
|
45
|
+
| `className` | `string` | Utility classes inherited by Tab mediators and applied to generated panels. |
|
|
46
|
+
| `isDisabled` | `true \| undefined` | Normalized root disabled state. |
|
|
47
|
+
| `orientation` | `'horizontal' \| 'vertical'` | Orientation derived from positioning utility classes. |
|
|
48
|
+
|
|
49
|
+
### Tab state
|
|
50
|
+
|
|
51
|
+
| Property | Type | Description |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `ariaLabel` | `string \| undefined` | Optional normalized accessible name. |
|
|
54
|
+
| `className` | `string` | Complete Tab class name, including active, disabled, local, and inherited utility classes. |
|
|
55
|
+
| `controlsId` | `string` | ID of the corresponding tab panel. |
|
|
56
|
+
| `id` | `string` | Stable ID referenced by the corresponding panel. |
|
|
57
|
+
| `index` | `number` | Stable zero-based registration index. |
|
|
58
|
+
| `isActive` | `boolean` | Whether this Tab is selected. |
|
|
59
|
+
| `isDisabled` | `true \| undefined` | Normalized local or inherited disabled state. |
|
|
60
|
+
| `tabIndex` | `0 \| -1` | Roving tab-order value. |
|
|
61
|
+
|
|
62
|
+
## Tabs mediator context
|
|
63
|
+
|
|
64
|
+
`tabsMediator.getContext()` exposes live selection, the registered Tab props
|
|
65
|
+
array, relationship IDs, and navigation methods consumed by Tab mediators, the
|
|
66
|
+
Tabs renderer, and extensions through `useTabsContext`.
|
|
67
|
+
|
|
68
|
+
| Property or method | Type | Description |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `activeTab` | `number` | Live active zero-based index. |
|
|
71
|
+
| `state` | `TabsState` | Live Tabs mediator state, including inherited class names and disabled state. |
|
|
72
|
+
| `tabs` | `TabDefaultProps[]` | Registered Tab props in visual order. |
|
|
73
|
+
| `consumeTabFocus` | `(index: number) => boolean` | Consumes a pending keyboard-focus request for a Tab index. |
|
|
74
|
+
| `getContentId` | `(index: number) => string` | Returns the stable panel relationship ID for an index. |
|
|
75
|
+
| `getTabId` | `(index: number) => string` | Returns the stable Tab relationship ID for an index. |
|
|
76
|
+
| `onTabKeyDown` | `(event: TabsKeyEvent, index: number) => void` | Applies orientation-aware roving keyboard navigation. |
|
|
77
|
+
| `registerTab` | `(props: TabDefaultProps) => number` | Stores Tab props in order and returns the Tab index. The props object identity is the registration key. |
|
|
78
|
+
| `setActiveTab` | `(index: number) => void` | Requests selection of an available Tab index. |
|
|
79
|
+
| `unregisterTab` | `(props: TabDefaultProps) => void` | Removes a Tab registration. Remaining indexes follow array order. |
|
|
80
|
+
|
|
81
|
+
## Slots
|
|
82
|
+
|
|
83
|
+
| Component | SlotName | Description | Default content |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| Tabs | `tabs` | Supplies ordered Tab controls inside the mediator-owned `tablist`. | Empty. |
|
|
86
|
+
| Tabs | `content` | Supplies matching panel children in the same order as the tabs. Tabs wraps each child in a `role="tabpanel"` container with the `tabContent` part class. | Empty. |
|
|
87
|
+
| Tab | default | Supplies the visible Tab label. | Empty. |
|
|
88
|
+
|
|
89
|
+
## Keyboard interaction
|
|
90
|
+
|
|
91
|
+
- `Tab` and `Shift+Tab` move sequential focus. The tab list contains one
|
|
92
|
+
tab stop: the selected Tab. The next stop is the active panel when it has
|
|
93
|
+
`tabindex="0"`, then focusable content inside that panel.
|
|
94
|
+
- Left and Right activate the previous or next enabled Tab in a horizontal
|
|
95
|
+
list. Up and Down do the same in a vertical list. Navigation wraps.
|
|
96
|
+
- `Home` and `End` activate the first and last enabled Tab.
|
|
97
|
+
- `Enter` and `Space` activate the focused Tab through native button behavior.
|
|
98
|
+
- Disabled Tabs are skipped.
|
|
99
|
+
|
|
100
|
+
## Utility classes
|
|
101
|
+
|
|
102
|
+
Positioning classes are applied to the `Tabs` root through `className`; the
|
|
103
|
+
root stylesheet coordinates the generated list, tabs, and panels. Orientation
|
|
104
|
+
is derived from these classes for layout, `aria-orientation`, and arrow-key
|
|
105
|
+
navigation.
|
|
106
|
+
|
|
107
|
+
| Class | Result |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `is-top`, `is-top-left` | List above the content, aligned to the start. |
|
|
110
|
+
| `is-top-right` | List above the content, aligned to the end. |
|
|
111
|
+
| `is-bottom`, `is-bottom-left` | List below the content, aligned to the start. |
|
|
112
|
+
| `is-bottom-right` | List below the content, aligned to the end. |
|
|
113
|
+
| `is-left`, `is-left-top` | List to the left of the content, aligned to the top. |
|
|
114
|
+
| `is-left-bottom` | List to the left of the content, aligned to the bottom. |
|
|
115
|
+
| `is-right`, `is-right-top` | List to the right of the content, aligned to the top. |
|
|
116
|
+
| `is-right-bottom` | List to the right of the content, aligned to the bottom. |
|
|
117
|
+
| `is-full-width` | Makes horizontal Tab labels share the full list width and vertical Tab labels share the full list height. |
|
|
118
|
+
| `has-vertical-labels` | On Tabs, gives every Tab a vertical writing mode when combined with an `is-left*` or `is-right*` utility. |
|
|
119
|
+
| `has-vertical-label` | On one Tab, gives only that label a vertical writing mode when combined with inherited `is-left*` or `is-right*` positioning. |
|
|
120
|
+
| `no-content-padding`, `no-padding` | Removes padding from every generated `tabContent` panel. |
|
|
121
|
+
| `no-content-border`, `no-border` | Removes borders from every generated `tabContent` panel. |
|
|
122
|
+
|
|
123
|
+
Left and right positioning utilities produce vertical Tabs using Up/Down arrow
|
|
124
|
+
keys. Top, bottom, and the default layout produce horizontal Tabs using
|
|
125
|
+
Left/Right arrow keys. Consumers do not set orientation separately. Tab buttons
|
|
126
|
+
remain horizontal unless `has-vertical-labels` is applied to Tabs or
|
|
127
|
+
`has-vertical-label` is applied to an individual Tab.
|
|
128
|
+
|
|
129
|
+
## CSS Variables
|
|
130
|
+
|
|
131
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
132
|
+
|---|---|---|---|---|---|---|
|
|
133
|
+
| `--pw_Tabs_root_disabled_opacity` | root | disabled | `opacity` | `var(--pw_disabled_opacity)` | Opacity applied once when the complete Tabs component is disabled. | Browser, Capacitor |
|
|
134
|
+
| `--pw_Tabs_tab_default_background-color` | tab | default | `background-color` | `var(--pw_control_background-color)` | Background of an inactive tab. | Browser, Capacitor |
|
|
135
|
+
| `--pw_Tabs_tab_default_border-color` | tab | default | `border-color` | `var(--pw_border-color)` | Border colour of a tab. | Browser, Capacitor |
|
|
136
|
+
| `--pw_Tabs_tab_default_border-width` | tab | default | `border-width` | `var(--pw_border-width)` | Border width of a tab. | Browser, Capacitor |
|
|
137
|
+
| `--pw_Tabs_tab_default_border-style` | tab | default | `border-style` | `var(--pw_border-style)` | Border style of a tab. | Browser, Capacitor |
|
|
138
|
+
| `--pw_Tabs_tab_default_border-radius` | tab | default | `border-radius` | `var(--pw_border-radius)` | Corner radius of a tab. | Browser, Capacitor |
|
|
139
|
+
| `--pw_Tabs_tab_default_padding` | tab | default | `padding` | `var(--pw_padding)` | Inner spacing of a tab. | Browser, Capacitor |
|
|
140
|
+
| `--pw_Tabs_tab_default_color` | tab | default | `color` | `var(--pw_text_strong_color)` | Foreground colour of a tab. | Browser, Capacitor |
|
|
141
|
+
| `--pw_Tabs_tab_default_font-size` | tab | default | `font-size` | `var(--pw_font-size)` | Font size of a tab. | Browser, Capacitor |
|
|
142
|
+
| `--pw_Tabs_tab_default_transition-duration` | tab | default | `transition-duration` | `var(--pw_transition-duration)` | Duration of visual tab-state transitions. | Browser, Capacitor |
|
|
143
|
+
| `--pw_Tabs_tab_active_background-color` | tab | active | `background-color` | `var(--pw_background-color)` | Background of the active tab. | Browser, Capacitor |
|
|
144
|
+
| `--pw_Tabs_tab_hover-size_box-shadow` | tab | hover-size | `box-shadow` | `var(--pw_size_box-shadow)` | Size portion of the inactive-tab hover shadow. | Browser, Capacitor |
|
|
145
|
+
| `--pw_Tabs_tab_hover-color_box-shadow` | tab | hover-color | `box-shadow` | `var(--pw_color_box-shadow)` | Colour portion of the inactive-tab hover shadow. | Browser, Capacitor |
|
|
146
|
+
| `--pw_Tabs_content_default_border-color` | content | default | `border-color` | `var(--pw_border-color)` | Border colour of a tab panel. | Browser, Capacitor |
|
|
147
|
+
| `--pw_Tabs_content_default_border-radius` | content | default | `border-radius` | `var(--pw_border-radius)` | Radius applied to the exposed outer corners of a tab panel. | Browser, Capacitor |
|
|
148
|
+
| `--pw_Tabs_content_default_border-style` | content | default | `border-style` | `var(--pw_border-style)` | Border style of a tab panel. | Browser, Capacitor |
|
|
149
|
+
| `--pw_Tabs_content_default_border-width` | content | default | `border-width` | `var(--pw_border-width)` | Border width of a tab panel. | Browser, Capacitor |
|
|
150
|
+
| `--pw_Tabs_content_default_background-color` | content | default | `background-color` | `var(--pw_background-color)` | Background of a tab panel. | Browser, Capacitor |
|
|
151
|
+
| `--pw_Tabs_content_default_padding` | content | default | `padding` | `var(--pw_padding)` | Inner spacing of a tab panel. | Browser, Capacitor |
|
|
152
|
+
|
|
153
|
+
```xts
|
|
154
|
+
import { setContext, useBeforeUnmount, useMediator, withDefaultProps } from '@pastweb/x';
|
|
155
|
+
import { tabsMediator, TABS_CONTEXT_KEY } from '@pastweb/components-core/Tabs';
|
|
156
|
+
import { DEFAULT_PROPS } from './constants';
|
|
157
|
+
|
|
158
|
+
export function Tabs(props) {
|
|
159
|
+
const p = withDefaultProps(props, DEFAULT_PROPS);
|
|
160
|
+
const m = useMediator(tabsMediator, p);
|
|
161
|
+
const s = m.state;
|
|
162
|
+
const c = m.getContext();
|
|
163
|
+
|
|
164
|
+
setContext(TABS_CONTEXT_KEY, c);
|
|
165
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
class={p.cls(
|
|
170
|
+
'Tabs',
|
|
171
|
+
s.isDisabled && 'is-disabled',
|
|
172
|
+
p.className,
|
|
173
|
+
)}
|
|
174
|
+
aria-disabled={s.isDisabled}
|
|
175
|
+
>
|
|
176
|
+
<ul
|
|
177
|
+
class={p.cls(
|
|
178
|
+
'tabsList',
|
|
179
|
+
s.className,
|
|
180
|
+
)}
|
|
181
|
+
role="tablist"
|
|
182
|
+
aria-label={s.ariaLabel}
|
|
183
|
+
aria-orientation={s.orientation}
|
|
184
|
+
>
|
|
185
|
+
<slot name="tabs" props={{ cls: p.cls }} />
|
|
186
|
+
</ul>
|
|
187
|
+
<slot
|
|
188
|
+
name="content"
|
|
189
|
+
map={(content, index) => {
|
|
190
|
+
const isActive = s.activeTab === index;
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div
|
|
194
|
+
id={c.getContentId(index)}
|
|
195
|
+
class={p.cls(
|
|
196
|
+
'tabContent',
|
|
197
|
+
s.className,
|
|
198
|
+
isActive && 'is-active',
|
|
199
|
+
)}
|
|
200
|
+
role="tabpanel"
|
|
201
|
+
aria-labelledby={c.getTabId(index)}
|
|
202
|
+
tabindex={isActive ? 0 : undefined}
|
|
203
|
+
hidden={isActive ? undefined : true}
|
|
204
|
+
>
|
|
205
|
+
{content}
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The standard composition supplies `Tab` controls through the named `tabs`
|
|
216
|
+
template and matching panel children through the named `content` template.
|
|
217
|
+
`Tabs` wraps each `content` child in a `role="tabpanel"` container:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<Tabs>
|
|
221
|
+
<Template name="tabs">
|
|
222
|
+
<Tab>Overview</Tab>
|
|
223
|
+
<Tab>Accessibility</Tab>
|
|
224
|
+
</Template>
|
|
225
|
+
|
|
226
|
+
<Template name="content">
|
|
227
|
+
<div>Overview content.</div>
|
|
228
|
+
<div>Accessibility content.</div>
|
|
229
|
+
</Template>
|
|
230
|
+
</Tabs>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`Tabs` owns the complete tab-list markup and the panel wrappers. Supply one
|
|
234
|
+
`content` child per Tab, in the same order. Generated panels use the
|
|
235
|
+
`tabContent` part class.
|
|
236
|
+
|
|
237
|
+
## useTabsContext
|
|
238
|
+
|
|
239
|
+
React renderers expose `useTabsContext()` for descendants of Tabs. It returns
|
|
240
|
+
the live Tabs context, including `activeTab`, `state`, `tabs` (registered Tab
|
|
241
|
+
props in visual order), and the navigation helpers.
|
|
242
|
+
|
|
243
|
+
Call it inside the Tabs tree. The wrapper that renders `<Tabs>` is outside the
|
|
244
|
+
provider and cannot read the context.
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { useTabsContext } from '@pastweb/react-components/Tabs';
|
|
248
|
+
|
|
249
|
+
function TabScrollArea({ index, children }) {
|
|
250
|
+
const { tabs } = useTabsContext();
|
|
251
|
+
const scrollArea = tabs[index]?.scrollArea;
|
|
252
|
+
|
|
253
|
+
if (!scrollArea) return children;
|
|
254
|
+
|
|
255
|
+
return <ScrollArea {...scrollArea}>{children}</ScrollArea>;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`registerTab` stores each Tab's props object in `context.tabs`. Extra props on
|
|
260
|
+
an extended Tab, such as `scrollArea`, are available at the matching index
|
|
261
|
+
when wrapping `content` slot children.
|
|
262
|
+
|
|
263
|
+
`Tab` delegates registration, relationship IDs, active and disabled state,
|
|
264
|
+
inherited positioning classes, class-name composition, activation, keyboard
|
|
265
|
+
navigation, and mounted-reference focus coordination to `tabMediator`:
|
|
266
|
+
|
|
267
|
+
```xts
|
|
268
|
+
import { useMediator, useMounted, useRef, withDefaultProps } from '@pastweb/x';
|
|
269
|
+
import { tabMediator } from '@pastweb/components-core/Tabs';
|
|
270
|
+
import { DEFAULT_TAB_PROPS } from './constants';
|
|
271
|
+
|
|
272
|
+
export function Tab(props) {
|
|
273
|
+
const p = withDefaultProps(props, DEFAULT_TAB_PROPS);
|
|
274
|
+
const m = useMediator(tabMediator, p);
|
|
275
|
+
const s = m.state;
|
|
276
|
+
const buttonReference = useRef(null);
|
|
277
|
+
|
|
278
|
+
useMounted(() => {
|
|
279
|
+
if (!buttonReference.value) return;
|
|
280
|
+
|
|
281
|
+
m.useMounted(buttonReference.value);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<li
|
|
286
|
+
class={s.className}
|
|
287
|
+
role="presentation"
|
|
288
|
+
>
|
|
289
|
+
<button
|
|
290
|
+
ref={buttonReference}
|
|
291
|
+
id={s.id}
|
|
292
|
+
type="button"
|
|
293
|
+
class={p.cls('tabButton')}
|
|
294
|
+
role="tab"
|
|
295
|
+
aria-label={s.ariaLabel}
|
|
296
|
+
aria-selected={s.isActive}
|
|
297
|
+
aria-controls={s.controlsId}
|
|
298
|
+
aria-disabled={s.isDisabled}
|
|
299
|
+
disabled={s.isDisabled}
|
|
300
|
+
tabindex={s.tabIndex}
|
|
301
|
+
@click={m.onActivate}
|
|
302
|
+
@keydown={m.onKeyDown}
|
|
303
|
+
>
|
|
304
|
+
<span class={p.cls('tabContent')}>
|
|
305
|
+
<slot />
|
|
306
|
+
</span>
|
|
307
|
+
</button>
|
|
308
|
+
</li>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The web template renders a `role="tablist"`, native buttons with `role="tab"`,
|
|
314
|
+
and matching `role="tabpanel"` containers linked by IDs. Native renderers map
|
|
315
|
+
the same state to an accessible selectable collection and content views, using
|
|
316
|
+
platform focus APIs rather than ARIA.
|
|
317
|
+
|
|
318
|
+
## Accessibility notes
|
|
319
|
+
|
|
320
|
+
- The selected Tab has `aria-selected="true"` and `tabindex="0"`. Other Tabs
|
|
321
|
+
have `aria-selected="false"` and `tabindex="-1"`.
|
|
322
|
+
- Each Tab uses `aria-controls` to point at its panel. Each panel uses
|
|
323
|
+
`aria-labelledby` to point at its Tab. Panels stay mounted; inactive panels
|
|
324
|
+
use `hidden`.
|
|
325
|
+
- The tab list is named with `aria-label`. Pass a task-specific label when
|
|
326
|
+
possible; an empty string omits the name.
|
|
327
|
+
- Left and right positioning utilities set `aria-orientation="vertical"`.
|
|
328
|
+
- Disabled Tabs use native `disabled` and `aria-disabled`.
|
|
329
|
+
- Shared web styles keep a 24 by 24 CSS-pixel minimum target, visible
|
|
330
|
+
`:focus-visible` indication, forced-colours treatment, and reduced-motion
|
|
331
|
+
behavior.
|
|
332
|
+
|
|
333
|
+
## Native renderer mapping
|
|
334
|
+
|
|
335
|
+
Native renderers map selection to a platform segmented control or equivalent
|
|
336
|
+
accessible collection. Do not copy ARIA attributes into native markup.
|
|
337
|
+
|
|
338
|
+
## Known limitations
|
|
339
|
+
|
|
340
|
+
- GOV.UK treats Tabs as experimental and prefers a table of contents plus
|
|
341
|
+
stacked sections when JavaScript is unavailable. This package does not
|
|
342
|
+
provide that no-JS fallback.
|
|
343
|
+
- Do not put a second tab list inside a panel. Do not use Tabs as primary
|
|
344
|
+
navigation.
|
|
345
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
346
|
+
conformance.
|
|
347
|
+
|
|
348
|
+
## Standards
|
|
349
|
+
|
|
350
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
351
|
+
- WAI-ARIA Tabs pattern: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
|
|
352
|
+
- GOV.UK Tabs: https://design-system.service.gov.uk/components/tabs/
|
|
@@ -6,8 +6,9 @@ import { getOrientation } from "./getOrientation.cjs";
|
|
|
6
6
|
import { getRelationshipId } from "./getRelationshipId.cjs";
|
|
7
7
|
import { onTabKeyDown } from "./onTabKeyDown.cjs";
|
|
8
8
|
import { registerTab } from "./registerTab.cjs";
|
|
9
|
+
import { unregisterTab } from "./unregisterTab.cjs";
|
|
9
10
|
import { setActiveTab } from "./setActiveTab.cjs";
|
|
10
11
|
import { setActiveTabFromKeyboard } from "./setActiveTabFromKeyboard.cjs";
|
|
11
12
|
import { useBeforeUnmount } from "./useBeforeUnmount.cjs";
|
|
12
13
|
import { consumeTabFocus } from "./consumeTabFocus.cjs";
|
|
13
|
-
export { consumeTabFocus, getActiveTab, getAriaLabel, getContext, getIsDisabled, getOrientation, getRelationshipId, onTabKeyDown, registerTab, setActiveTab, setActiveTabFromKeyboard, useBeforeUnmount };
|
|
14
|
+
export { consumeTabFocus, getActiveTab, getAriaLabel, getContext, getIsDisabled, getOrientation, getRelationshipId, onTabKeyDown, registerTab, setActiveTab, setActiveTabFromKeyboard, unregisterTab, useBeforeUnmount };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Tag
|
|
2
|
+
|
|
3
|
+
Tag displays a short, non-interactive category or status label. Its rendering
|
|
4
|
+
contract targets WCAG 2.2 Level AA.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use Tag for a compact category or status. Visible text must describe the
|
|
9
|
+
status. Colour may reinforce meaning but must not be the only indicator. Use
|
|
10
|
+
`closeButton` only to dismiss the tag itself, not to start another action.
|
|
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 | `""` | Accessible name when visible content is insufficient. Pass an empty string to omit it. |
|
|
18
|
+
| `className` | `string` | no | `""` | Additional classes. |
|
|
19
|
+
| `closeButton` | `boolean` | no | - | Displays the native close button. |
|
|
20
|
+
| `closeLabel` | `string` | no | `"Close tag"` | Names the close button. |
|
|
21
|
+
| `onClose` | `() => void` | no | noop | Reports close intent. |
|
|
22
|
+
| `rounded` | `boolean` | no | - | Applies `is-rounded`. |
|
|
23
|
+
|
|
24
|
+
## CSS Variables
|
|
25
|
+
|
|
26
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
27
|
+
|---|---|---|---|---|---|---|
|
|
28
|
+
| `--pw_Tag_root_default_color` | `root` | `default` | `color` | `var(--pw_text_color)` | Tag foreground. | Web, Capacitor, NativeScript |
|
|
29
|
+
| `--pw_Tag_root_default_background-color` | `root` | `default` | `background-color` | `var(--pw_control_background-color)` | Tag background. | Web, Capacitor, NativeScript |
|
|
30
|
+
| `--pw_Tag_root_default_border-color` | `root` | `default` | `border-color` | `var(--pw_border-color)` | Border colour. | Web, Capacitor, NativeScript |
|
|
31
|
+
| `--pw_Tag_root_default_border-width` | `root` | `default` | `border-width` | `var(--pw_border-width)` | Border width. | Web, Capacitor, NativeScript |
|
|
32
|
+
| `--pw_Tag_root_default_border-style` | `root` | `default` | `border-style` | `var(--pw_border-style)` | Border style. | Web, Capacitor, NativeScript |
|
|
33
|
+
| `--pw_Tag_root_default_border-radius` | `root` | `default` | `border-radius` | `var(--pw_border-radius)` | Corner radius. | Web, Capacitor, NativeScript |
|
|
34
|
+
| `--pw_Tag_root_rounded_border-radius` | `root` | `rounded` | `border-radius` | `var(--pw_rounded_border-radius)` | Rounded variant radius. | Web, Capacitor, NativeScript |
|
|
35
|
+
| `--pw_Tag_root_default_padding` | `root` | `default` | `padding` | `0.125em 0.625em` | Inner spacing. | Web, Capacitor, NativeScript |
|
|
36
|
+
| `--pw_Tag_root_default_font-size` | `root` | `default` | `font-size` | `var(--pw_small_font-size)` | Text size. | Web, Capacitor, NativeScript |
|
|
37
|
+
| `--pw_Tag_root_default_font-weight` | `root` | `default` | `font-weight` | `var(--pw_bold_font-weight)` | Text weight. | Web, Capacitor, NativeScript |
|
|
38
|
+
| `--pw_Tag_root_default_letter-spacing` | `root` | `default` | `letter-spacing` | `0.04em` | Uppercase tracking. | Web, Capacitor, NativeScript |
|
|
39
|
+
| `--pw_Tag_close-button_default_min-width` | `close-button` | `default` | `min-width` | `1.563em` | Close control width, scaled to Tag size. | Web, Capacitor, NativeScript |
|
|
40
|
+
| `--pw_Tag_close-button_default_height` | `close-button` | `default` | `height` | `1.563em` | Close control height, scaled to Tag size. | Web, Capacitor, NativeScript |
|
|
41
|
+
|
|
42
|
+
```xts
|
|
43
|
+
import { withDefaultProps } from '@pastweb/x';
|
|
44
|
+
import { DEFAULT_PROPS } from './constants';
|
|
45
|
+
|
|
46
|
+
export function Tag(props: TagProps) {
|
|
47
|
+
const p = withDefaultProps<TagDefaultProps>(props, DEFAULT_PROPS);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div class={p.cls('Tag', { 'is-rounded': p.rounded, 'has-close': p.closeButton }, p.className)} aria-label={p.ariaLabel || undefined}>
|
|
51
|
+
<span class={p.cls('content')}><slot /></span>
|
|
52
|
+
<button #if={p.closeButton} type="button" class={p.cls('closeButton')} @click={p.onClose} aria-label={p.closeLabel}>
|
|
53
|
+
<span class={p.cls('closeBar')} aria-hidden="true" />
|
|
54
|
+
<span class={p.cls('closeBar')} aria-hidden="true" />
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Keyboard interaction
|
|
62
|
+
|
|
63
|
+
- Without `closeButton`, Tag is not interactive and does not enter the tab order.
|
|
64
|
+
- The close action is a native button operable with `Enter` or `Space`.
|
|
65
|
+
|
|
66
|
+
## Accessibility notes
|
|
67
|
+
|
|
68
|
+
- Visible content is the default accessible name. `ariaLabel` supplements it
|
|
69
|
+
when the visible text is insufficient.
|
|
70
|
+
- The close action is a native button named by `closeLabel`.
|
|
71
|
+
- Status meaning is carried by the text. Colour is decorative reinforcement.
|
|
72
|
+
- Shared web styles use uppercase status text, a Panel-style close control
|
|
73
|
+
scaled to the Tag font size, visible `:focus-visible` indication, and
|
|
74
|
+
forced-colours treatment.
|
|
75
|
+
|
|
76
|
+
## Known limitations
|
|
77
|
+
|
|
78
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
79
|
+
conformance.
|
|
80
|
+
|
|
81
|
+
## Native renderer mapping
|
|
82
|
+
|
|
83
|
+
Native renderers map Tag to a non-interactive accessible text container. Do not
|
|
84
|
+
copy ARIA attributes into native markup.
|
|
85
|
+
|
|
86
|
+
## Standards
|
|
87
|
+
|
|
88
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
89
|
+
- WAI-ARIA APG patterns: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
90
|
+
- GOV.UK Tag: https://design-system.service.gov.uk/components/tag/
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# TaskBar
|
|
2
|
+
|
|
3
|
+
TaskBar renders one control for each window registered with an application
|
|
4
|
+
`WindowManager`. It provides desktop-style selection, restore, and minimization
|
|
5
|
+
through the manager's `manageWindow` command.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use TaskBar with one application-level `WindowManager` when several managed
|
|
10
|
+
windows can remain open at once. Do not use it for unrelated page navigation or
|
|
11
|
+
as a replacement for a toolbar.
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
| PropName | Type | Required | Default | Description |
|
|
16
|
+
|---|---|---:|---:|---|
|
|
17
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
18
|
+
| `manager` | `WindowManager` | yes | - | The application-level manager whose registered windows are displayed. |
|
|
19
|
+
| `className` | `string` | no | `""` | Adds classes to the taskbar root. |
|
|
20
|
+
| `ariaLabel` | `string` | no | `"Window taskbar"` | Names the navigation landmark. |
|
|
21
|
+
| `getLabel` | `(window: ManagedWindowState) => string` | no | `window.meta.label ?? window.meta.title ?? window.id` | Resolves the accessible and visible task button label. |
|
|
22
|
+
|
|
23
|
+
## Mediator state and API
|
|
24
|
+
|
|
25
|
+
| Property | Type | Description |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `state.activeId` | `string \| null` | ID of the manager's active window. |
|
|
28
|
+
| `state.windows` | `readonly ManagedWindowState[]` | Registered windows in stable taskbar order. |
|
|
29
|
+
| `manageWindow` | `(window: ManagedWindowState) => boolean` | Delegates task-button selection to the manager. It activates an inactive window, minimizes an active visible window, and restores a minimized window. |
|
|
30
|
+
|
|
31
|
+
## CSS Variables
|
|
32
|
+
|
|
33
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
34
|
+
|---|---|---|---|---|---|---|
|
|
35
|
+
| `--pw_TaskBar_root_default_gap` | root | default | `gap` | `0.04em` | Gap between task controls. | Web, Capacitor |
|
|
36
|
+
| `--pw_TaskBar_root_default_min-height` | root | default | `min-height` | `3.5em` | Minimum taskbar height. | Web, Capacitor |
|
|
37
|
+
| `--pw_TaskBar_root_default_padding` | root | default | `padding` | `0.125em` | Inner taskbar spacing. | Web, Capacitor |
|
|
38
|
+
| `--pw_TaskBar_root_default_border-color` | root | default | `border-color` | `var(--pw_border-color)` | Taskbar border colour. | Web, Capacitor |
|
|
39
|
+
| `--pw_TaskBar_root_default_border-style` | root | default | `border-style` | `var(--pw_border-style)` | Taskbar border style. | Web, Capacitor |
|
|
40
|
+
| `--pw_TaskBar_root_default_border-width` | root | default | `border-width` | `var(--pw_border-width)` | Taskbar border width. | Web, Capacitor |
|
|
41
|
+
| `--pw_TaskBar_root_default_border-radius` | root | default | `border-radius` | `var(--pw_border-radius)` | Taskbar corner radius. | Web, Capacitor |
|
|
42
|
+
| `--pw_TaskBar_root_default_background-color` | root | default | `background-color` | `var(--pw_control_background-color)` | Taskbar background. | Web, Capacitor |
|
|
43
|
+
| `--pw_TaskBar_taskButton_default_min-width` | taskButton | default | `min-width` | `2.75em` | Minimum task-button width. | Web, Capacitor |
|
|
44
|
+
| `--pw_TaskBar_taskButton_default_min-height` | taskButton | default | `min-height` | `2.75em` | Minimum task-button height. | Web, Capacitor |
|
|
45
|
+
| `--pw_TaskBar_taskButton_default_max-width` | taskButton | default | `max-width` | `16em` | Maximum task-button width before text truncates. | Web, Capacitor |
|
|
46
|
+
| `--pw_TaskBar_taskButton_default_padding` | taskButton | default | `padding` | `var(--pw_padding)` | Task-button inner spacing. | Web, Capacitor |
|
|
47
|
+
| `--pw_TaskBar_taskButton_default_margin` | taskButton | default | `margin` | `0.25em` | Task-button spacing. | Web, Capacitor |
|
|
48
|
+
| `--pw_TaskBar_taskButton_default_border-color` | taskButton | default | `border-color` | `var(--pw_border-color)` | Task-button border colour. | Web, Capacitor |
|
|
49
|
+
| `--pw_TaskBar_taskButton_default_border-style` | taskButton | default | `border-style` | `var(--pw_border-style)` | Task-button border style. | Web, Capacitor |
|
|
50
|
+
| `--pw_TaskBar_taskButton_default_border-width` | taskButton | default | `border-width` | `var(--pw_border-width)` | Task-button border width. | Web, Capacitor |
|
|
51
|
+
| `--pw_TaskBar_taskButton_default_border-radius` | taskButton | default | `border-radius` | `var(--pw_border-radius)` | Task-button corner radius. | Web, Capacitor |
|
|
52
|
+
| `--pw_TaskBar_taskButton_default_background-color` | taskButton | default | `background-color` | `var(--pw_control_background-color)` | Task-button background. | Web, Capacitor |
|
|
53
|
+
| `--pw_TaskBar_taskButton_default_color` | taskButton | default | `color` | `inherit` | Task-button text colour. | Web, Capacitor |
|
|
54
|
+
| `--pw_TaskBar_taskButton_default_font-size` | taskButton | default | `font-size` | `inherit` | Task-button font size. | Web, Capacitor |
|
|
55
|
+
| `--pw_TaskBar_taskButton_default_font-weight` | taskButton | default | `font-weight` | `var(--pw_semibold_font-weight)` | Task-button font weight. | Web, Capacitor |
|
|
56
|
+
| `--pw_TaskBar_taskButton_default_line-height` | taskButton | default | `line-height` | `var(--pw_line-height)` | Task-button line height. | Web, Capacitor |
|
|
57
|
+
| `--pw_TaskBar_taskButton_default_transition-duration` | taskButton | default | `transition-duration` | `var(--pw_transition-duration)` | Task-button transition duration. | Web, Capacitor |
|
|
58
|
+
| `--pw_TaskBar_taskButton_minimised_box-shadow` | taskButton | minimised | `box-shadow` | inset shared shadow | Inset shadow that indicates a minimized window. | Web, Capacitor |
|
|
59
|
+
|
|
60
|
+
```xts
|
|
61
|
+
import { useMediator, withDefaultProps } from '@pastweb/x';
|
|
62
|
+
import { taskBarMediator } from '@pastweb/components-core/TaskBar';
|
|
63
|
+
import { DEFAULT_PROPS } from './constants';
|
|
64
|
+
|
|
65
|
+
export function TaskBar(props: TaskBarProps) {
|
|
66
|
+
const p = withDefaultProps<TaskBarDefaultProps>(props, DEFAULT_PROPS);
|
|
67
|
+
const m = useMediator<TaskBarMediator>(taskBarMediator, p);
|
|
68
|
+
const s = m.state;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<nav class={p.cls('TaskBar', p.className)} aria-label={p.ariaLabel}>
|
|
72
|
+
{s.windows.map(window => (
|
|
73
|
+
<button
|
|
74
|
+
key={window.id}
|
|
75
|
+
type='button'
|
|
76
|
+
class={p.cls('taskButton', {
|
|
77
|
+
'is-active': window.id === s.activeId,
|
|
78
|
+
'is-minimised': window.status === 'minimized',
|
|
79
|
+
})}
|
|
80
|
+
aria-pressed={window.id === s.activeId}
|
|
81
|
+
@click={() => m.manageWindow(window)}
|
|
82
|
+
>
|
|
83
|
+
{p.getLabel(window)}
|
|
84
|
+
</button>
|
|
85
|
+
))}
|
|
86
|
+
</nav>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Keyboard and accessibility behaviour
|
|
92
|
+
|
|
93
|
+
- The root is a named `nav` landmark and each window is a native button.
|
|
94
|
+
- `aria-pressed` identifies the active task. Native button keyboard activation
|
|
95
|
+
runs the same manager command as a pointer click.
|
|
96
|
+
- Horizontal overflow remains available when more windows are open than fit in
|
|
97
|
+
the taskbar.
|
|
98
|
+
- The taskbar uses the `--pw_z-index_window-max` layer so its controls remain
|
|
99
|
+
reachable when floating managed windows overlap the same workspace.
|
|
100
|
+
|
|
101
|
+
## Native renderer mapping
|
|
102
|
+
|
|
103
|
+
Native renderers map the taskbar to an accessible horizontal collection of
|
|
104
|
+
native buttons and use the same `manageWindow` command for activation,
|
|
105
|
+
minimization, and restoration.
|