@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,237 @@
|
|
|
1
|
+
# DropDown
|
|
2
|
+
|
|
3
|
+
DropDown reveals contextual content from a trigger. Its rendering contract
|
|
4
|
+
follows the WAI-ARIA Disclosure pattern and targets WCAG 2.2 Level AA.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use DropDown for short contextual content or a collection of actions associated
|
|
9
|
+
with a trigger. Do not use hover-only activation for essential content. Use a
|
|
10
|
+
dialog when the revealed content requires a modal task or a complex form.
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
|
|
14
|
+
| PropName | Type | Required | Default | Description |
|
|
15
|
+
|---|---|---:|---:|---|
|
|
16
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
17
|
+
| `id` | `string` | no | generated | Supplies the stable base for trigger and content IDs. Generated IDs use the `dropDown` prefix. |
|
|
18
|
+
| `ariaLabel` | `string` | no | `"DropDown"` | Names the trigger when its visible content does not provide a name. Pass an empty string to omit the accessible name. |
|
|
19
|
+
| `arrow` | `boolean` | no | `true` | Displays the visual arrow. |
|
|
20
|
+
| `className` | `string` | no | `""` | Adds classes to the root. |
|
|
21
|
+
| `contentClassName` | `string` | no | `""` | Adds classes to the revealed content. |
|
|
22
|
+
| `offset` | `number` | no | `2` | Sets the renderer positioning offset. |
|
|
23
|
+
| `disabled` | `boolean` | no | `false` | Prevents the content from opening. |
|
|
24
|
+
| `isVisible` | `boolean` | no | `false` | Controls the requested visibility. |
|
|
25
|
+
| `visibleOn` | `'click' \| 'over' \| 'context'` | no | `'click'` | Selects the activation model. Essential content must remain available without hover or a context-menu gesture. |
|
|
26
|
+
| `visibleTarget` | `'element' \| 'pointer'` | no | `'element'` | Selects the positioning reference. `element` uses the trigger. `pointer` uses the pointer position from the trigger event. Keyboard and programmatic opens fall back to the trigger. |
|
|
27
|
+
| `placement` | `Placement` | no | `'bottom'` | Requests placement relative to the trigger. |
|
|
28
|
+
| `hideOnClick` | `boolean` | no | `true` | Closes after activation inside the revealed content. |
|
|
29
|
+
| `hideOnClickOut` | `boolean` | no | `true` | Closes after a click or context-menu activation outside the component. |
|
|
30
|
+
| `hideOnBlur` | `boolean` | no | `true` | Closes when focus leaves the component. |
|
|
31
|
+
| `position` | `'absolute' \| 'fixed'` | no | `'absolute'` | Selects the web positioning strategy. |
|
|
32
|
+
| `clickTimeGap` | `number` | no | `16` | Delays click/context activation in milliseconds. |
|
|
33
|
+
| `onChange` | `(isVisible: boolean) => void` | no | noop | Reports visibility changes. |
|
|
34
|
+
| `onClick` | `() => void` | no | noop | Reports click activation. |
|
|
35
|
+
| `onContext` | `() => void` | no | noop | Reports context-menu activation. |
|
|
36
|
+
| `onClickOut` | `() => void` | no | noop | Reports an outside activation that closed the content. |
|
|
37
|
+
| `onMouseEnter` | `() => void` | no | noop | Reports hover open. |
|
|
38
|
+
| `onMouseLeave` | `() => void` | no | noop | Reports hover close. |
|
|
39
|
+
|
|
40
|
+
## Mediator API
|
|
41
|
+
|
|
42
|
+
| Property | Type | Description |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `id` | `string` | Resolved host ID. Generated IDs use the `dropDown` prefix; an explicit `id` is exposed as `_${id}`. |
|
|
45
|
+
|
|
46
|
+
## Mediator state
|
|
47
|
+
|
|
48
|
+
| Property | Type | Description |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `ariaLabel` | `string \| undefined` | Normalized trigger accessible name. Empty labels are omitted. |
|
|
51
|
+
| `contentId` | `string` | Content relationship ID in the form `content${id}`. |
|
|
52
|
+
| `isDisabled` | `true \| undefined` | Is `true` when the DropDown is disabled; otherwise omitted. Renderers use this value directly for `.is-disabled`, `aria-disabled`, and unavailable keyboard behavior. |
|
|
53
|
+
| `isVisible` | `boolean` | Whether the content is requested open. Renderers use this for `.is-open`, `.is-visible`, `aria-expanded`, and `hidden`. |
|
|
54
|
+
| `triggerId` | `string` | Trigger relationship ID in the form `trigger${id}`. |
|
|
55
|
+
|
|
56
|
+
## Mediator context
|
|
57
|
+
|
|
58
|
+
Descendant or replacement-slot content reads this API through the renderer context hook. Live getters stay synchronized with mediator state.
|
|
59
|
+
|
|
60
|
+
| Context property | Type | Description |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `ariaLabel` | `string \| undefined` | Normalized trigger accessible name. |
|
|
63
|
+
| `arrow` | `boolean` | Whether the decorative arrow is requested. |
|
|
64
|
+
| `cls` | `(...args: ClassValue[]) => string` | Resolves component classes. |
|
|
65
|
+
| `contentClassName` | `string` | Classes added to the revealed content. |
|
|
66
|
+
| `contentId` | `string` | Content relationship ID. |
|
|
67
|
+
| `hide` | `() => void` | Closes the DropDown. Nested menus should call this from item activation. |
|
|
68
|
+
| `id` | `string` | Resolved host ID. |
|
|
69
|
+
| `isDisabled` | `true \| undefined` | Normalized disabled state. |
|
|
70
|
+
| `isVisible` | `boolean` | Whether the content is requested open. |
|
|
71
|
+
| `onClick` | `(event?) => void` | Toggles click-activated content. |
|
|
72
|
+
| `onKeyDown` | `(event) => void` | Handles Enter, Space, and Escape. |
|
|
73
|
+
| `position` | `'absolute' \| 'fixed'` | Web positioning strategy. |
|
|
74
|
+
| `setArrowRef` | `(arrow?) => void` | Stores the mounted arrow reference. |
|
|
75
|
+
| `triggerId` | `string` | Trigger relationship ID. |
|
|
76
|
+
|
|
77
|
+
## Slots
|
|
78
|
+
|
|
79
|
+
| SlotName | Description | Default content |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| default | Supplies the trigger content. | Empty. |
|
|
82
|
+
| `header` | Supplies optional leading content inside the revealed panel. | Empty. |
|
|
83
|
+
| `content` | Supplies the main revealed content. | Empty. |
|
|
84
|
+
| `footer` | Supplies optional trailing content inside the revealed panel. | Empty. |
|
|
85
|
+
|
|
86
|
+
## CSS Variables
|
|
87
|
+
|
|
88
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
89
|
+
|---|---|---|---|---|---|---|
|
|
90
|
+
| `--pw_DropDown_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled opacity. | Web, Capacitor, NativeScript |
|
|
91
|
+
| `--pw_DropDown_content_default_background-color` | `content` | `default` | `background-color` | `var(--pw_control_background-color)` | Revealed-content background. | Web, Capacitor, NativeScript |
|
|
92
|
+
| `--pw_DropDown_content_default_color` | `content` | `default` | `color` | `var(--pw_text_color)` | Content foreground. | Web, Capacitor, NativeScript |
|
|
93
|
+
| `--pw_DropDown_content_default_border-color` | `content` | `default` | `border-color` | `var(--pw_border-color)` | Content border colour. | Web, Capacitor, NativeScript |
|
|
94
|
+
| `--pw_DropDown_content_default_border-width` | `content` | `default` | `border-width` | `var(--pw_border-width)` | Content border width. | Web, Capacitor, NativeScript |
|
|
95
|
+
| `--pw_DropDown_content_default_border-style` | `content` | `default` | `border-style` | `var(--pw_border-style)` | Content border style. | Web, Capacitor, NativeScript |
|
|
96
|
+
| `--pw_DropDown_content_default_border-radius` | `content` | `default` | `border-radius` | `var(--pw_small_border-radius)` | Content corner radius. | Web, Capacitor, NativeScript |
|
|
97
|
+
| `--pw_DropDown_content_default_padding` | `content` | `default` | `padding` | `var(--pw_padding) 0` | Content spacing. | Web, Capacitor, NativeScript |
|
|
98
|
+
| `--pw_DropDown_content_default_box-shadow` | `content` | `default` | `box-shadow` | shared shadow | Floating-content elevation. | Web, Capacitor |
|
|
99
|
+
| `--pw_DropDown_arrow_default_border-width` | `arrow` | `default` | `border-width` | `1em` | Arrow triangle size. | Web, Capacitor |
|
|
100
|
+
| `--pw_DropDown_target_default_min-width` | `target` | `default` | `min-width` | `2.75em` | Minimum target width. | Web, Capacitor, NativeScript |
|
|
101
|
+
| `--pw_DropDown_target_default_min-height` | `target` | `default` | `min-height` | `2.75em` | Minimum target height. | Web, Capacitor, NativeScript |
|
|
102
|
+
| `--pw_DropDown_target_focus-visible_box-shadow` | `target` | `focus-visible` | `box-shadow` | shared focus shadow | Keyboard focus indicator. | Web, Capacitor |
|
|
103
|
+
|
|
104
|
+
```xts
|
|
105
|
+
import {
|
|
106
|
+
setContext,
|
|
107
|
+
useBeforeUnmount,
|
|
108
|
+
useMediator,
|
|
109
|
+
useMounted,
|
|
110
|
+
useRef,
|
|
111
|
+
withDefaultProps,
|
|
112
|
+
} from '@pastweb/x';
|
|
113
|
+
import { dropdownMediator } from '@pastweb/components-core/DropDown';
|
|
114
|
+
import { DEFAULT_PROPS, DROPDOWN_CONTEXT_KEY } from './constants';
|
|
115
|
+
|
|
116
|
+
export function DropDown(props: DropDownProps) {
|
|
117
|
+
const p = withDefaultProps<DropDownDefaultProps>(props, DEFAULT_PROPS);
|
|
118
|
+
const m = useMediator<DropDownMediator>(dropdownMediator, p);
|
|
119
|
+
const s = m.state;
|
|
120
|
+
const c = m.getContext();
|
|
121
|
+
const rootRef = useRef(null);
|
|
122
|
+
const targetRef = useRef(null);
|
|
123
|
+
const containerRef = useRef(null);
|
|
124
|
+
const contentRef = useRef(null);
|
|
125
|
+
const arrowRef = useRef(null);
|
|
126
|
+
|
|
127
|
+
setContext(DROPDOWN_CONTEXT_KEY, c);
|
|
128
|
+
useMounted(() => {
|
|
129
|
+
if (
|
|
130
|
+
!rootRef.value
|
|
131
|
+
|| !targetRef.value
|
|
132
|
+
|| !containerRef.value
|
|
133
|
+
|| !contentRef.value
|
|
134
|
+
) return;
|
|
135
|
+
|
|
136
|
+
m.useMounted(
|
|
137
|
+
rootRef.value,
|
|
138
|
+
targetRef.value,
|
|
139
|
+
containerRef.value,
|
|
140
|
+
contentRef.value,
|
|
141
|
+
);
|
|
142
|
+
if (arrowRef.value) m.setArrowRef(arrowRef.value);
|
|
143
|
+
});
|
|
144
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div
|
|
148
|
+
#ref={rootRef}
|
|
149
|
+
id={m.id}
|
|
150
|
+
class={p.cls('DropDown', p.className, {
|
|
151
|
+
'is-disabled': s.isDisabled,
|
|
152
|
+
'is-open': s.isVisible,
|
|
153
|
+
})}
|
|
154
|
+
@click={m.onClick}
|
|
155
|
+
@contextmenu={m.onContext}
|
|
156
|
+
@mouseenter={m.onMouseEnter}
|
|
157
|
+
@mouseleave={m.onMouseLeave}
|
|
158
|
+
@blur={m.onBlur}
|
|
159
|
+
>
|
|
160
|
+
<span
|
|
161
|
+
#ref={targetRef}
|
|
162
|
+
id={s.triggerId}
|
|
163
|
+
class={p.cls('target')}
|
|
164
|
+
role="button"
|
|
165
|
+
tabIndex={s.isDisabled ? -1 : 0}
|
|
166
|
+
aria-label={s.ariaLabel}
|
|
167
|
+
aria-haspopup="true"
|
|
168
|
+
aria-expanded={s.isVisible}
|
|
169
|
+
aria-controls={s.contentId}
|
|
170
|
+
aria-disabled={s.isDisabled}
|
|
171
|
+
@keydown={m.onKeyDown}
|
|
172
|
+
>
|
|
173
|
+
<slot />
|
|
174
|
+
<div
|
|
175
|
+
#ref={containerRef}
|
|
176
|
+
class={p.cls('container', { 'is-visible': s.isVisible })}
|
|
177
|
+
style={{ position: p.position }}
|
|
178
|
+
aria-hidden={!s.isVisible || undefined}
|
|
179
|
+
inert={!s.isVisible || undefined}
|
|
180
|
+
>
|
|
181
|
+
<div
|
|
182
|
+
#ref={contentRef}
|
|
183
|
+
id={s.contentId}
|
|
184
|
+
class={p.cls('content', p.contentClassName)}
|
|
185
|
+
aria-labelledby={s.triggerId}
|
|
186
|
+
@click={m.onClick}
|
|
187
|
+
>
|
|
188
|
+
<slot name="header" />
|
|
189
|
+
<slot name="content" />
|
|
190
|
+
<slot name="footer" />
|
|
191
|
+
</div>
|
|
192
|
+
<div #if={p.arrow} #ref={arrowRef} class={p.cls('arrow')} aria-hidden="true">
|
|
193
|
+
<div class={p.cls('triangle')} />
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Keyboard interaction
|
|
203
|
+
|
|
204
|
+
- `Tab` and `Shift+Tab` move focus to and from the trigger in document order.
|
|
205
|
+
- `Enter` or `Space` on the trigger toggles click-activated content.
|
|
206
|
+
- A click on the trigger hides context-activated content when it is already open.
|
|
207
|
+
- `Escape` closes open content.
|
|
208
|
+
|
|
209
|
+
## Accessibility notes
|
|
210
|
+
|
|
211
|
+
- The trigger exposes expanded, disabled, and ID relationship state.
|
|
212
|
+
- Closed content is `inert` and `aria-hidden` so it is removed from keyboard and
|
|
213
|
+
accessibility navigation. Do not rely on opacity alone.
|
|
214
|
+
- Hover-only activation is not sufficient for essential content.
|
|
215
|
+
- The decorative arrow is hidden from assistive technology.
|
|
216
|
+
- Shared web styles provide at least a 44 by 44 CSS-pixel target and a visible
|
|
217
|
+
focus indicator.
|
|
218
|
+
|
|
219
|
+
## Native renderer mapping
|
|
220
|
+
|
|
221
|
+
NativeScript renderers map the trigger to a focusable native control, visibility
|
|
222
|
+
to the native hidden/collapsed state, and expanded/disabled intent to native
|
|
223
|
+
accessibility state. Positioning, outside activation, and animation completion
|
|
224
|
+
remain renderer responsibilities and require iOS and Android device testing.
|
|
225
|
+
|
|
226
|
+
## Known limitations
|
|
227
|
+
|
|
228
|
+
- Positioning uses web layout measurement. Native renderers must supply an
|
|
229
|
+
equivalent placement adapter.
|
|
230
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
231
|
+
conformance.
|
|
232
|
+
|
|
233
|
+
## Standards
|
|
234
|
+
|
|
235
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
236
|
+
- WAI-ARIA Disclosure pattern: https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
|
|
237
|
+
- GOV.UK Details guidance: https://design-system.service.gov.uk/components/details/
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# FileUploader
|
|
2
|
+
|
|
3
|
+
FileUploader lets users choose files through a native file picker, with an
|
|
4
|
+
optional dropzone. Its rendering contract uses a native `input type="file"`
|
|
5
|
+
and a visible trigger, and targets WCAG 2.2 Level AA.
|
|
6
|
+
|
|
7
|
+
## When to use
|
|
8
|
+
|
|
9
|
+
Use a file uploader only when a file is required to complete the task. Provide
|
|
10
|
+
a visible label that describes the file being requested. Do not rely on
|
|
11
|
+
drag-and-drop as the only way to add files; keyboard and assistive-technology
|
|
12
|
+
users must be able to open the native picker.
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
| PropName | Type | Required | Default | Description |
|
|
17
|
+
|---|---|---:|---:|---|
|
|
18
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component and state classes. |
|
|
19
|
+
| `accept` | `string` | no | - | Restricts the native picker to matching file types. |
|
|
20
|
+
| `ariaLabel` | `string` | no | `"Upload a file"` | Names the trigger when no visible `label` is supplied. Pass an empty string to omit the accessible name. |
|
|
21
|
+
| `className` | `string` | no | `""` | Adds classes to the wrapper. |
|
|
22
|
+
| `disabled` | `boolean` | no | - | Makes choosing and dropping files unavailable. |
|
|
23
|
+
| `dropzone` | `boolean` | no | - | Enables drop-to-add in addition to the native picker. |
|
|
24
|
+
| `formData` | `FormData` | no | `new FormData()` | Supplies the store used for selected files. |
|
|
25
|
+
| `id` | `string` | no | generated | Supplies the root ID. Generated IDs use the `fileUploader` prefix. |
|
|
26
|
+
| `label` | `string` | no | `""` | Supplies the visible accessible label. |
|
|
27
|
+
| `maxNumber` | `number` | no | `1` | Limits how many files can be stored. |
|
|
28
|
+
| `maxSize` | `number` | no | - | Maximum file size in bytes. Oversized files are reported through `onAddFile` and are not stored. |
|
|
29
|
+
| `multiple` | `boolean` | no | - | Allows the native picker to select more than one file at a time. |
|
|
30
|
+
| `name` | `string` | no | - | Supplies the native file input name. |
|
|
31
|
+
| `tabIndex` | `number` | no | `0` | Controls sequential focus order of the trigger. Prefer the default document order. |
|
|
32
|
+
| `onAddFile` | `(file: File, data: FormData) => void` | no | noop | Reports each file after a successful store, and oversized files that were rejected. |
|
|
33
|
+
|
|
34
|
+
## Mediator API
|
|
35
|
+
|
|
36
|
+
| Property | Type | Description |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `id` | `string` | Resolved host ID. Generated IDs use the `fileUploader` prefix; an explicit `id` is exposed as `_${id}`. |
|
|
39
|
+
| `inputId` | `string` | File input ID derived as `input${id}`. |
|
|
40
|
+
| `labelId` | `string` | Visible label ID derived as `label${id}`. |
|
|
41
|
+
|
|
42
|
+
## Mediator state
|
|
43
|
+
|
|
44
|
+
| Property | Type | Description |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `ariaLabel` | `string \| undefined` | Normalized accessible name when no visible `label` is present. Empty labels are omitted. |
|
|
47
|
+
| `canAddMore` | `boolean` | Is `true` while stored files are under `maxNumber`. |
|
|
48
|
+
| `fileCount` | `number` | Number of stored files. |
|
|
49
|
+
| `files` | `File[]` | File values currently stored in FormData. |
|
|
50
|
+
| `isDisabled` | `true \| undefined` | Is `true` when the uploader is disabled; otherwise omitted. Renderers use this value directly for `.is-disabled`, `disabled`, and `aria-disabled`. |
|
|
51
|
+
| `isDragging` | `true \| undefined` | Is `true` while files are dragged over an enabled dropzone; otherwise omitted. Renderers use this value for `.is-dragging`. |
|
|
52
|
+
|
|
53
|
+
## Mediator context
|
|
54
|
+
|
|
55
|
+
`fileUploaderMediator.getContext()` exposes live getters consumed by custom
|
|
56
|
+
descendant content. Values that also exist on mediator state are read from that
|
|
57
|
+
normalized state rather than recomputed from raw props.
|
|
58
|
+
|
|
59
|
+
| Property | Type | Description |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `accept` | `string \| undefined` | Native picker accept filter. |
|
|
62
|
+
| `canAddMore` | `boolean` | Whether another file can be stored. |
|
|
63
|
+
| `dropzone` | `boolean` | Whether drop-to-add is enabled. |
|
|
64
|
+
| `fileCount` | `number` | Number of stored files. |
|
|
65
|
+
| `files` | `File[]` | Stored File values. |
|
|
66
|
+
| `formData` | `FormData` | The file store. |
|
|
67
|
+
| `id` | `string` | Resolved host ID. |
|
|
68
|
+
| `inputId` | `string` | File input ID. |
|
|
69
|
+
| `isDisabled` | `true \| undefined` | Normalized disabled state. |
|
|
70
|
+
| `labelId` | `string` | Visible label ID. |
|
|
71
|
+
| `maxNumber` | `number` | Stored-file limit. |
|
|
72
|
+
| `maxSize` | `number \| undefined` | Maximum file size in bytes. |
|
|
73
|
+
| `multiple` | `boolean` | Native picker multiple-selection flag. |
|
|
74
|
+
| `removeFile` | `(fileName: string) => void` | Removes a stored file by the FormData key used when it was added. |
|
|
75
|
+
|
|
76
|
+
## Slots
|
|
77
|
+
|
|
78
|
+
| SlotName | Description | Default content |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| default | Supplies trigger content such as an icon or “Choose file” text. Must provide an accessible name when `label` and `ariaLabel` are both empty. | Empty. |
|
|
81
|
+
|
|
82
|
+
## CSS Variables
|
|
83
|
+
|
|
84
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
85
|
+
|---|---|---|---|---|---|---|
|
|
86
|
+
| `--pw_FileUploader_root_default_background-color` | `root` | `default` | `background-color` | `var(--pw_control_background-color)` | Trigger background. | Web, Capacitor, NativeScript |
|
|
87
|
+
| `--pw_FileUploader_root_default_color` | `root` | `default` | `color` | `var(--pw_text_color)` | Trigger text colour. | Web, Capacitor, NativeScript |
|
|
88
|
+
| `--pw_FileUploader_root_default_padding` | `root` | `default` | `padding` | `var(--pw_padding)` | Trigger inner spacing. | Web, Capacitor, NativeScript |
|
|
89
|
+
| `--pw_FileUploader_root_default_margin` | `root` | `default` | `margin` | shared margins | Component spacing. | Web, Capacitor, NativeScript |
|
|
90
|
+
| `--pw_FileUploader_root_default_border-color` | `root` | `default` | `border-color` | `var(--pw_border-color)` | Trigger border colour. | Web, Capacitor, NativeScript |
|
|
91
|
+
| `--pw_FileUploader_root_default_border-width` | `root` | `default` | `border-width` | `var(--pw_border-width)` | Trigger border width. | Web, Capacitor, NativeScript |
|
|
92
|
+
| `--pw_FileUploader_root_default_border-style` | `root` | `default` | `border-style` | `var(--pw_border-style)` | Trigger border style. | Web, Capacitor, NativeScript |
|
|
93
|
+
| `--pw_FileUploader_root_default_border-radius` | `root` | `default` | `border-radius` | `var(--pw_border-radius)` | Trigger corner radius. | Web, Capacitor, NativeScript |
|
|
94
|
+
| `--pw_FileUploader_root_rounded_border-radius` | `root` | `rounded` | `border-radius` | `var(--pw_rounded_border-radius)` | Trigger corner radius for `is-rounded`. | Web, Capacitor, NativeScript |
|
|
95
|
+
| `--pw_FileUploader_root_default_min-height` | `root` | `default` | `min-height` | `2.75em` | Minimum trigger size, 44px at the default font size. | Web, Capacitor, NativeScript |
|
|
96
|
+
| `--pw_FileUploader_root_default_transition-duration` | `root` | `default` | `transition-duration` | `var(--pw_transition-duration)` | Visual state transition duration. | Web, Capacitor; NativeScript adapter |
|
|
97
|
+
| `--pw_FileUploader_root_disabled_opacity` | `root` | `disabled` | `opacity` | `var(--pw_disabled_opacity)` | Disabled opacity. | Web, Capacitor, NativeScript |
|
|
98
|
+
| `--pw_FileUploader_root_highlighted_border-color` | `root` | `highlighted` | `border-color` | `var(--pw_outline_color)` | Drop-target border colour. | Web, Capacitor |
|
|
99
|
+
| `--pw_FileUploader_trigger_default_gap` | `trigger` | `default` | `gap` | `var(--pw_horizontal_padding)` | Space between trigger contents. | Web, Capacitor, NativeScript |
|
|
100
|
+
| `--pw_FileUploader_label_default_color` | `label` | `default` | `color` | `var(--pw_label_text_color)` | Visible label colour. | Web, Capacitor, NativeScript |
|
|
101
|
+
| `--pw_FileUploader_label_default_font-weight` | `label` | `default` | `font-weight` | `var(--pw_semibold_font-weight)` | Visible label weight. | Web, Capacitor, NativeScript |
|
|
102
|
+
| `--pw_FileUploader_color_default_box-shadow` | `color` | `default` | `box-shadow` | `var(--pw_color_box-shadow)` | Focus and drag shadow colour. | Web, Capacitor |
|
|
103
|
+
| `--pw_FileUploader_size_default_box-shadow` | `size` | `default` | `box-shadow` | `var(--pw_size_box-shadow)` | Shadow size. | Web, Capacitor |
|
|
104
|
+
|
|
105
|
+
```xts
|
|
106
|
+
import {
|
|
107
|
+
setContext,
|
|
108
|
+
useBeforeUnmount,
|
|
109
|
+
useMediator,
|
|
110
|
+
useMounted,
|
|
111
|
+
useRef,
|
|
112
|
+
withDefaultProps,
|
|
113
|
+
} from '@pastweb/x';
|
|
114
|
+
import { fileUploaderMediator } from '@pastweb/components-core/FileUploader';
|
|
115
|
+
import { DEFAULT_PROPS, FILE_UPLOADER_CONTEXT_KEY } from './constants';
|
|
116
|
+
|
|
117
|
+
export function FileUploader(props: FileUploaderProps) {
|
|
118
|
+
const p = withDefaultProps<FileUploaderDefaultProps>(props, DEFAULT_PROPS);
|
|
119
|
+
const m = useMediator<FileUploaderMediator>(fileUploaderMediator, p);
|
|
120
|
+
const s = m.state;
|
|
121
|
+
const inputRef = useRef(null);
|
|
122
|
+
const c = m.getContext();
|
|
123
|
+
|
|
124
|
+
setContext(FILE_UPLOADER_CONTEXT_KEY, c);
|
|
125
|
+
useMounted(() => m.useMounted(inputRef.value));
|
|
126
|
+
useBeforeUnmount(m.useBeforeUnmount);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
id={m.id}
|
|
131
|
+
class={p.cls('FileUploader', p.className, {
|
|
132
|
+
'is-disabled': s.isDisabled,
|
|
133
|
+
'is-dragging': s.isDragging,
|
|
134
|
+
'is-dropzone': p.dropzone,
|
|
135
|
+
'is-full': !s.canAddMore,
|
|
136
|
+
})}
|
|
137
|
+
aria-disabled={s.isDisabled}
|
|
138
|
+
@dragenter={m.onDragEnter}
|
|
139
|
+
@dragover={m.onDragOver}
|
|
140
|
+
@dragleave={m.onDragLeave}
|
|
141
|
+
@drop={m.onDrop}
|
|
142
|
+
>
|
|
143
|
+
<label #if={p.label} id={m.labelId} class={p.cls('label')} for={m.inputId}>
|
|
144
|
+
{p.label}
|
|
145
|
+
</label>
|
|
146
|
+
<button
|
|
147
|
+
type="button"
|
|
148
|
+
class={p.cls('trigger')}
|
|
149
|
+
disabled={s.isDisabled}
|
|
150
|
+
aria-disabled={s.isDisabled}
|
|
151
|
+
aria-label={s.ariaLabel}
|
|
152
|
+
aria-labelledby={p.label ? m.labelId : undefined}
|
|
153
|
+
tabIndex={p.tabIndex}
|
|
154
|
+
@click={m.onActivate}
|
|
155
|
+
@keydown={m.onKeyDown}
|
|
156
|
+
>
|
|
157
|
+
<slot />
|
|
158
|
+
</button>
|
|
159
|
+
<input
|
|
160
|
+
#ref={inputRef}
|
|
161
|
+
id={m.inputId}
|
|
162
|
+
class={p.cls('inputElement')}
|
|
163
|
+
type="file"
|
|
164
|
+
name={p.name}
|
|
165
|
+
accept={p.accept}
|
|
166
|
+
multiple={p.multiple}
|
|
167
|
+
disabled={s.isDisabled}
|
|
168
|
+
tabIndex={-1}
|
|
169
|
+
@change={m.onChange}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Renderers pass native change and drag events through to the mediator. For a
|
|
177
|
+
native change event, the mediator reads the file input from `event.target` and
|
|
178
|
+
falls back to `event.currentTarget`. This supports both native DOM events and
|
|
179
|
+
renderer event wrappers without renderer-specific adapters. The mediator stops
|
|
180
|
+
drag propagation, prevents the browser from opening dropped files, and converts
|
|
181
|
+
`FileList` values to `File[]`.
|
|
182
|
+
|
|
183
|
+
## Keyboard interaction
|
|
184
|
+
|
|
185
|
+
- `Tab` and `Shift+Tab` move focus to and from the trigger in document order.
|
|
186
|
+
- `Enter` and `Space` open the native file picker when another file can be
|
|
187
|
+
stored.
|
|
188
|
+
- The hidden file input is not in the tab order. Keyboard users operate the
|
|
189
|
+
visible trigger.
|
|
190
|
+
- Drag-and-drop is pointer-only additional behaviour. It must not replace the
|
|
191
|
+
picker.
|
|
192
|
+
|
|
193
|
+
## Accessibility behaviour
|
|
194
|
+
|
|
195
|
+
- The native file input remains the mechanism that chooses files. The trigger
|
|
196
|
+
is a `type="button"` control that opens that input.
|
|
197
|
+
- A visible `label` names the control through `aria-labelledby`. When no
|
|
198
|
+
visible label is supplied, `ariaLabel` names the trigger. Pass an empty
|
|
199
|
+
`ariaLabel` only when slot content already provides the accessible name.
|
|
200
|
+
- Disabled state is exposed as `.is-disabled`, native `disabled`, and
|
|
201
|
+
`aria-disabled`.
|
|
202
|
+
- Dropzone entry is a visual `is-dragging` state. Do not make dropping the
|
|
203
|
+
only way to add files.
|
|
204
|
+
- IDs used for the host, label, and input are mediator-owned and stable.
|
|
205
|
+
|
|
206
|
+
## Native renderer mapping
|
|
207
|
+
|
|
208
|
+
NativeScript renderers map the contract to the platform file-picker API, a
|
|
209
|
+
native button or equivalent trigger, disabled state, and native accessibility
|
|
210
|
+
name. Web DOM refs, `FileList`, and drag events are renderer details. Do not
|
|
211
|
+
copy ARIA attributes into native markup.
|
|
212
|
+
|
|
213
|
+
## Known limitations
|
|
214
|
+
|
|
215
|
+
- `accept` is enforced by the native picker. Dropped files are not filtered by
|
|
216
|
+
`accept` in the mediator.
|
|
217
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
218
|
+
conformance.
|
|
219
|
+
|
|
220
|
+
## Standards
|
|
221
|
+
|
|
222
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
223
|
+
- WAI-ARIA Button pattern: https://www.w3.org/WAI/ARIA/apg/patterns/button/
|
|
224
|
+
- GOV.UK file upload guidance: https://design-system.service.gov.uk/components/file-upload/
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Icon
|
|
2
|
+
|
|
3
|
+
Icon renders an inline SVG or image URL from a direct source, local collection,
|
|
4
|
+
or Iconify-compatible API. Its rendering contract targets WCAG 2.2 Level AA.
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use Icon for a compact visual symbol. Icons are decorative by default. Supply
|
|
9
|
+
`ariaLabel` only when the icon communicates meaning that is not already present
|
|
10
|
+
in nearby text. Do not use an icon as an unlabeled interactive control. Prefer
|
|
11
|
+
a visible text label alongside an icon when the symbol is part of a control.
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
| PropName | Type | Required | Default | Description |
|
|
16
|
+
|---|---|---:|---:|---|
|
|
17
|
+
| `cls` | `(...args: ClassValue[]) => string` | yes | - | Resolves component classes. |
|
|
18
|
+
| `ariaLabel` | `string` | no | `""` | Names a meaningful icon and gives it the image role. Pass an empty string to keep the icon decorative. |
|
|
19
|
+
| `className` | `string` | no | `""` | Adds classes to the root. |
|
|
20
|
+
| `src` | `string` | no | `""` | Supplies an SVG string, image URL, or data URL. |
|
|
21
|
+
| `name` | `string` | no | `""` | Selects an icon using `collection:icon` syntax. |
|
|
22
|
+
| `collection` | `Collection` | no | `{}` | Supplies trusted local icon sources by collection and name. |
|
|
23
|
+
| `apiUrl` | `string` | no | `https://api.iconify.design` | Selects the Iconify-compatible API endpoint. |
|
|
24
|
+
|
|
25
|
+
SVG strings are injected as markup and therefore must come from trusted sources.
|
|
26
|
+
|
|
27
|
+
## Mediator state
|
|
28
|
+
|
|
29
|
+
| Property | Type | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `ariaHidden` | `true \| undefined` | Is `true` when the icon is decorative; otherwise omitted. Renderers use this value directly for `aria-hidden`. |
|
|
32
|
+
| `ariaLabel` | `string \| undefined` | Normalized accessible name. Empty labels are omitted. |
|
|
33
|
+
| `collectionName` | `string` | Collection segment parsed from `name`. |
|
|
34
|
+
| `iconData` | `IconData \| string` | Inline SVG markup when the source is an SVG string. |
|
|
35
|
+
| `iconName` | `string` | Icon segment parsed from `name`. |
|
|
36
|
+
| `role` | `'img' \| undefined` | Is `img` when `ariaLabel` supplies a name; otherwise omitted. |
|
|
37
|
+
| `src` | `string` | Image URL when the source is not inline SVG. |
|
|
38
|
+
|
|
39
|
+
## CSS Variables
|
|
40
|
+
|
|
41
|
+
| Variable | Part | State | CSS property | Default | Description | Platforms |
|
|
42
|
+
|---|---|---|---|---|---|---|
|
|
43
|
+
| `--pw_Icon_root_default_color` | `root` | `default` | `color` | `var(--pw_text_strong_color)` | Icon foreground. | Web, Capacitor, NativeScript |
|
|
44
|
+
| `--pw_Icon_graphic_default_width` | `graphic` | `default` | `width` | `1em` | Graphic width. | Web, Capacitor, NativeScript |
|
|
45
|
+
| `--pw_Icon_graphic_default_height` | `graphic` | `default` | `height` | `1em` | Graphic height. | Web, Capacitor, NativeScript |
|
|
46
|
+
|
|
47
|
+
```xts
|
|
48
|
+
import { useMediator, withDefaultProps } from '@pastweb/x';
|
|
49
|
+
import { iconMediator } from '@pastweb/components-core/Icon';
|
|
50
|
+
import { DEFAULT_PROPS } from './constants';
|
|
51
|
+
|
|
52
|
+
export function Icon(props: IconProps) {
|
|
53
|
+
const p = withDefaultProps<IconDefaultProps>(props, DEFAULT_PROPS);
|
|
54
|
+
const m = useMediator<IconMediator>(iconMediator, p);
|
|
55
|
+
const s = m.state;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<i
|
|
59
|
+
#if={s.iconData || s.src}
|
|
60
|
+
class={p.cls('Icon', p.className)}
|
|
61
|
+
role={s.role}
|
|
62
|
+
aria-label={s.ariaLabel}
|
|
63
|
+
aria-hidden={s.ariaHidden}
|
|
64
|
+
#html={s.iconData || undefined}
|
|
65
|
+
>
|
|
66
|
+
<img #if={s.src} src={s.src} alt="" />
|
|
67
|
+
</i>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Keyboard interaction
|
|
73
|
+
|
|
74
|
+
- Icon is not interactive. It does not enter the tab order.
|
|
75
|
+
|
|
76
|
+
## Accessibility notes
|
|
77
|
+
|
|
78
|
+
- Decorative icons are removed from the accessibility tree with `aria-hidden`.
|
|
79
|
+
- Meaningful icons expose `role="img"` and one accessible name on the root.
|
|
80
|
+
Nested images use empty alternative text to prevent duplicate announcements.
|
|
81
|
+
- The default `ariaLabel` is an empty string, which keeps the icon decorative.
|
|
82
|
+
Pass a task-specific name when the icon is meaningful.
|
|
83
|
+
- Icons used inside controls do not replace the control's visible or accessible
|
|
84
|
+
label.
|
|
85
|
+
|
|
86
|
+
## Native renderer mapping
|
|
87
|
+
|
|
88
|
+
NativeScript renderers map trusted vector/image sources to supported native image
|
|
89
|
+
views. Decorative icons disable native accessibility exposure; meaningful icons
|
|
90
|
+
map `ariaLabel` intent to `accessibilityLabel` and image-role state. Do not copy
|
|
91
|
+
ARIA attributes into native markup.
|
|
92
|
+
|
|
93
|
+
## Known limitations
|
|
94
|
+
|
|
95
|
+
- Inline SVG markup must come from trusted sources.
|
|
96
|
+
- Automated accessibility checks do not by themselves establish WCAG
|
|
97
|
+
conformance.
|
|
98
|
+
|
|
99
|
+
## Standards
|
|
100
|
+
|
|
101
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
102
|
+
- WAI-ARIA Image pattern: https://www.w3.org/WAI/ARIA/apg/patterns/
|
|
103
|
+
- GOV.UK images guidance: https://design-system.service.gov.uk/styles/images/
|