@inglorious/ui 9.11.1 → 9.13.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/package.json +5 -5
- package/src/controls/button/button.stories.js +12 -0
- package/src/controls/button/template.js +17 -0
- package/src/controls/button-group/template.js +5 -2
- package/src/controls/file-upload/file-upload.stories.js +11 -0
- package/src/controls/file-upload/template.js +2 -1
- package/src/controls/image-cropper/image-cropper.stories.js +9 -0
- package/src/controls/image-cropper/template.js +1 -1
- package/src/data-display/carousel/carousel.stories.js +14 -0
- package/src/data-display/carousel/template.js +20 -5
- package/src/data-display/data-grid/data-grid.stories.js +73 -0
- package/src/data-display/data-grid/template.js +129 -23
- package/src/data-display/data-grid/theme.css +10 -2
- package/src/data-display/list/list.stories.js +8 -0
- package/src/data-display/list/template.js +1 -1
- package/src/feedback/alert/alert.stories.js +11 -0
- package/src/feedback/alert/template.js +2 -1
- package/src/feedback/snackbar/snackbar.stories.js +11 -0
- package/src/feedback/snackbar/template.js +2 -1
- package/src/navigation/breadcrumbs/breadcrumbs.stories.js +12 -0
- package/src/navigation/breadcrumbs/template.js +2 -1
- package/src/navigation/menu/menu.stories.js +15 -0
- package/src/navigation/pagination/pagination.stories.js +9 -0
- package/src/navigation/pagination/template.js +7 -2
- package/src/navigation/speed-dial/speed-dial.stories.js +13 -0
- package/src/navigation/speed-dial/template.js +10 -1
- package/types/controls/button-group.d.ts +1 -0
- package/types/controls/button.d.ts +2 -0
- package/types/controls/file-upload.d.ts +2 -0
- package/types/controls/image-cropper.d.ts +2 -0
- package/types/data-display/carousel.d.ts +12 -0
- package/types/data-display/data-grid.d.ts +14 -0
- package/types/data-display/list.d.ts +2 -0
- package/types/feedback/alert.d.ts +2 -0
- package/types/feedback/snackbar.d.ts +2 -0
- package/types/navigation/breadcrumbs.d.ts +2 -0
- package/types/navigation/pagination.d.ts +4 -0
- package/types/navigation/speed-dial.d.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.13.0",
|
|
4
4
|
"description": "A design system for Inglorious Web with render-function components and customizable themes",
|
|
5
5
|
"author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -339,9 +339,9 @@
|
|
|
339
339
|
"**/*.css"
|
|
340
340
|
],
|
|
341
341
|
"peerDependencies": {
|
|
342
|
-
"@inglorious/store": "^12.
|
|
342
|
+
"@inglorious/store": "^12.2.0",
|
|
343
343
|
"@inglorious/utils": "^5.1.0",
|
|
344
|
-
"@inglorious/web": "^6.
|
|
344
|
+
"@inglorious/web": "^6.16.0"
|
|
345
345
|
},
|
|
346
346
|
"devDependencies": {
|
|
347
347
|
"@iconify-icons/fa7-brands": "^2.0.0",
|
|
@@ -350,10 +350,10 @@
|
|
|
350
350
|
"@iconify-icons/material-symbols": "^2.0.6",
|
|
351
351
|
"@inglorious/charts": "12.2.6",
|
|
352
352
|
"@inglorious/eslint-config": "1.2.0",
|
|
353
|
-
"@inglorious/store": "12.
|
|
353
|
+
"@inglorious/store": "12.2.0",
|
|
354
354
|
"@inglorious/vite-plugin-jsx": "2.0.6",
|
|
355
355
|
"@inglorious/vite-plugin-vue": "1.0.7",
|
|
356
|
-
"@inglorious/web": "6.
|
|
356
|
+
"@inglorious/web": "6.16.0",
|
|
357
357
|
"@lit-labs/ssr": "^4.1.0",
|
|
358
358
|
"@storybook/addon-docs": "^10.6.5",
|
|
359
359
|
"@storybook/web-components-vite": "^10.6.5",
|
|
@@ -57,6 +57,10 @@ export default {
|
|
|
57
57
|
options: ["button", "submit", "reset", "menu"],
|
|
58
58
|
description: "Native HTML button type attribute.",
|
|
59
59
|
},
|
|
60
|
+
href: {
|
|
61
|
+
control: "text",
|
|
62
|
+
description: "Renders a link styled as a button.",
|
|
63
|
+
},
|
|
60
64
|
accept: {
|
|
61
65
|
control: "text",
|
|
62
66
|
description: "File types the picker offers (with `onFileSelect`).",
|
|
@@ -178,6 +182,14 @@ FilePicker.args = {
|
|
|
178
182
|
onFileSelect: () => {},
|
|
179
183
|
}
|
|
180
184
|
|
|
185
|
+
export const Link = {}
|
|
186
|
+
Link.args = {
|
|
187
|
+
...Default.args,
|
|
188
|
+
children: "Open events",
|
|
189
|
+
color: "primary",
|
|
190
|
+
href: "#events",
|
|
191
|
+
}
|
|
192
|
+
|
|
181
193
|
export const CustomContent = {}
|
|
182
194
|
CustomContent.args = {
|
|
183
195
|
...Default.args,
|
|
@@ -14,6 +14,7 @@ import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
|
14
14
|
* Renders a themed button for actions and form submission.
|
|
15
15
|
* Supports variants, sizes, shapes, pressed state, and full-width layout.
|
|
16
16
|
* With `onFileSelect`, the button also opens a file picker on click.
|
|
17
|
+
* With `href`, it is a link that looks like a button.
|
|
17
18
|
* @example
|
|
18
19
|
* // Stateless usage
|
|
19
20
|
* button.render({ children: 'Click me', color: 'primary' })
|
|
@@ -21,6 +22,9 @@ import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
|
21
22
|
* // File picker
|
|
22
23
|
* button.render({ children: 'Upload', accept: 'image/*', onFileSelect })
|
|
23
24
|
*
|
|
25
|
+
* // Link
|
|
26
|
+
* button.render({ children: 'Open', href: '/events' })
|
|
27
|
+
*
|
|
24
28
|
* @param {ButtonProps} props
|
|
25
29
|
* @returns {TemplateResult}
|
|
26
30
|
*/
|
|
@@ -36,6 +40,7 @@ export function render(props) {
|
|
|
36
40
|
isPressed = false,
|
|
37
41
|
isFullWidth = false,
|
|
38
42
|
buttonType = "button",
|
|
43
|
+
href,
|
|
39
44
|
ariaLabel,
|
|
40
45
|
isAriaPressed,
|
|
41
46
|
className = "",
|
|
@@ -72,6 +77,18 @@ export function render(props) {
|
|
|
72
77
|
}
|
|
73
78
|
: onClick
|
|
74
79
|
|
|
80
|
+
if (href != null) {
|
|
81
|
+
return html`<a
|
|
82
|
+
href=${isDisabled ? nothing : href}
|
|
83
|
+
aria-label=${ariaLabel || nothing}
|
|
84
|
+
aria-disabled=${isDisabled ? "true" : nothing}
|
|
85
|
+
class=${classMap(classes)}
|
|
86
|
+
@click=${isDisabled ? nothing : onClick}
|
|
87
|
+
${ref((element) => applyElementProps(element, rest))}
|
|
88
|
+
>${children}</a
|
|
89
|
+
>`
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
return html`
|
|
76
93
|
<button
|
|
77
94
|
type=${buttonType}
|
|
@@ -33,9 +33,11 @@ export function render(props) {
|
|
|
33
33
|
...rest
|
|
34
34
|
} = props
|
|
35
35
|
|
|
36
|
+
const isSelection = value != null || isMultiple
|
|
37
|
+
|
|
36
38
|
const groupClasses = {
|
|
37
39
|
"iw-button-group": true,
|
|
38
|
-
"iw-button-group-selection":
|
|
40
|
+
"iw-button-group-selection": isSelection,
|
|
39
41
|
[`iw-button-group-${direction}`]: true,
|
|
40
42
|
"iw-button-group-attached": isAttached,
|
|
41
43
|
}
|
|
@@ -86,7 +88,8 @@ export function render(props) {
|
|
|
86
88
|
size: itemSize ?? size,
|
|
87
89
|
isDisabled: isDisabled || !!itemDisabled,
|
|
88
90
|
isPressed,
|
|
89
|
-
isAriaPressed:
|
|
91
|
+
isAriaPressed: isSelection ? isPressed : undefined,
|
|
92
|
+
ariaLabel: children != null ? label : undefined,
|
|
90
93
|
onClick: () => {
|
|
91
94
|
if (!onChange) return
|
|
92
95
|
|
|
@@ -91,3 +91,14 @@ Error.args = {
|
|
|
91
91
|
error: "Only PDF files under 5MB are allowed.",
|
|
92
92
|
selectedFiles: ["invoice.png"],
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/** Every text the field shows is a prop: `title`, `description` and `emptyLabel`. */
|
|
96
|
+
export const Localized = {}
|
|
97
|
+
Localized.args = {
|
|
98
|
+
...Default.args,
|
|
99
|
+
label: "Curriculum",
|
|
100
|
+
hint: "Carica il curriculum in PDF o DOC.",
|
|
101
|
+
title: "Scegli un file",
|
|
102
|
+
description: "o trascinalo qui",
|
|
103
|
+
emptyLabel: "Nessun file selezionato",
|
|
104
|
+
}
|
|
@@ -32,6 +32,7 @@ export function render(props) {
|
|
|
32
32
|
selectedFiles = [],
|
|
33
33
|
title = "Choose files",
|
|
34
34
|
description = "or drag and drop here",
|
|
35
|
+
emptyLabel = "No file selected",
|
|
35
36
|
onChange,
|
|
36
37
|
onBlur,
|
|
37
38
|
onFocus,
|
|
@@ -54,7 +55,7 @@ export function render(props) {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
const summaryText =
|
|
57
|
-
selectedFiles.length > 0 ? selectedFiles.join(", ") :
|
|
58
|
+
selectedFiles.length > 0 ? selectedFiles.join(", ") : emptyLabel
|
|
58
59
|
|
|
59
60
|
return html`
|
|
60
61
|
<div class=${classMap(wrapperClasses)}>
|
|
@@ -83,3 +83,12 @@ Disabled.args = {
|
|
|
83
83
|
rect: { x: 0.1, y: 0.1, w: 0.6, h: 0.6 },
|
|
84
84
|
isDisabled: true,
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/** Screen readers announce the crop area by `label` and `roleDescription`. */
|
|
88
|
+
export const Localized = {}
|
|
89
|
+
Localized.args = {
|
|
90
|
+
...Default.args,
|
|
91
|
+
id: "image-cropper-localized",
|
|
92
|
+
label: "Ritaglia la foto",
|
|
93
|
+
roleDescription: "area di ritaglio",
|
|
94
|
+
}
|
|
@@ -72,7 +72,7 @@ export const ImageCropper = {
|
|
|
72
72
|
class=${classMap(classes)}
|
|
73
73
|
style=${style}
|
|
74
74
|
role="group"
|
|
75
|
-
aria-roledescription
|
|
75
|
+
aria-roledescription=${props.roleDescription ?? "crop area"}
|
|
76
76
|
aria-label=${label || "Crop area"}
|
|
77
77
|
tabindex=${isDisabled ? -1 : 0}
|
|
78
78
|
aria-disabled=${isDisabled}
|
|
@@ -163,3 +163,17 @@ function slide(index) {
|
|
|
163
163
|
${index + 1}
|
|
164
164
|
</div>`
|
|
165
165
|
}
|
|
166
|
+
|
|
167
|
+
/** The carousel, its slides, arrows and dots take their accessible names from props. */
|
|
168
|
+
export const Localized = {}
|
|
169
|
+
Localized.args = {
|
|
170
|
+
...Default.args,
|
|
171
|
+
id: "carousel-localized",
|
|
172
|
+
label: "Carosello di esempio",
|
|
173
|
+
roleDescription: "carosello",
|
|
174
|
+
slideRoleDescription: "diapositiva",
|
|
175
|
+
formatSlideLabel: (index, total) => `${index} di ${total}`,
|
|
176
|
+
formatIndicatorLabel: (index) => `Vai alla diapositiva ${index}`,
|
|
177
|
+
previousLabel: "Diapositiva precedente",
|
|
178
|
+
nextLabel: "Diapositiva successiva",
|
|
179
|
+
}
|
|
@@ -92,7 +92,7 @@ export const Carousel = {
|
|
|
92
92
|
return html`<div
|
|
93
93
|
class="iw-carousel-viewport"
|
|
94
94
|
role="group"
|
|
95
|
-
aria-roledescription
|
|
95
|
+
aria-roledescription=${props.roleDescription ?? "carousel"}
|
|
96
96
|
aria-label=${label || "Carousel"}
|
|
97
97
|
tabindex="0"
|
|
98
98
|
@scroll=${handleScroll(props)}
|
|
@@ -126,8 +126,11 @@ export const Carousel = {
|
|
|
126
126
|
return html`<div
|
|
127
127
|
class="iw-carousel-item"
|
|
128
128
|
role="group"
|
|
129
|
-
aria-roledescription
|
|
130
|
-
aria-label=${
|
|
129
|
+
aria-roledescription=${props.slideRoleDescription ?? "slide"}
|
|
130
|
+
aria-label=${(props.formatSlideLabel ?? formatSlideLabel)(
|
|
131
|
+
index + HUMAN_OFFSET,
|
|
132
|
+
total,
|
|
133
|
+
)}
|
|
131
134
|
>
|
|
132
135
|
${item}
|
|
133
136
|
</div>`
|
|
@@ -147,6 +150,8 @@ export const Carousel = {
|
|
|
147
150
|
isInfinite = false,
|
|
148
151
|
arrowVariant = DEFAULT_ARROW_VARIANT,
|
|
149
152
|
arrowColor,
|
|
153
|
+
previousLabel = "Previous slide",
|
|
154
|
+
nextLabel = "Next slide",
|
|
150
155
|
} = props
|
|
151
156
|
const isPrevious = direction === PREVIOUS
|
|
152
157
|
|
|
@@ -168,7 +173,7 @@ export const Carousel = {
|
|
|
168
173
|
shape: "round",
|
|
169
174
|
isDisabled,
|
|
170
175
|
children: html`<span class="iw-carousel-chevron"></span>`,
|
|
171
|
-
"aria-label": isPrevious ?
|
|
176
|
+
"aria-label": isPrevious ? previousLabel : nextLabel,
|
|
172
177
|
})}
|
|
173
178
|
</span>`
|
|
174
179
|
},
|
|
@@ -217,7 +222,9 @@ export const Carousel = {
|
|
|
217
222
|
"iw-carousel-indicator-current": isCurrent,
|
|
218
223
|
})}
|
|
219
224
|
type="button"
|
|
220
|
-
aria-label=${
|
|
225
|
+
aria-label=${(props.formatIndicatorLabel ?? formatIndicatorLabel)(
|
|
226
|
+
index + HUMAN_OFFSET,
|
|
227
|
+
)}
|
|
221
228
|
aria-current=${isCurrent ? "page" : "false"}
|
|
222
229
|
@click=${(event) =>
|
|
223
230
|
this.indicatorSelect?.(props, index, viewportFrom(event.currentTarget))}
|
|
@@ -638,3 +645,11 @@ function viewportFrom(element) {
|
|
|
638
645
|
element.closest(".iw-carousel")?.querySelector(".iw-carousel-viewport")
|
|
639
646
|
)
|
|
640
647
|
}
|
|
648
|
+
|
|
649
|
+
function formatSlideLabel(index, total) {
|
|
650
|
+
return `${index} of ${total}`
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function formatIndicatorLabel(index) {
|
|
654
|
+
return `Go to slide ${index}`
|
|
655
|
+
}
|
|
@@ -195,3 +195,76 @@ function createRows(count) {
|
|
|
195
195
|
role: index % 4 === 0 ? "Admin" : "User",
|
|
196
196
|
}))
|
|
197
197
|
}
|
|
198
|
+
|
|
199
|
+
/** Rows are selected from the keyboard: Tab to the grid, then use the arrows, Home and End to move, Enter to select a row, Space to toggle it, and Shift to extend the selection. */
|
|
200
|
+
export const KeyboardSelection = {
|
|
201
|
+
args: {
|
|
202
|
+
...Default.args,
|
|
203
|
+
id: "dataGridKeyboard",
|
|
204
|
+
ariaLabel: "Users",
|
|
205
|
+
search: null,
|
|
206
|
+
selection: [],
|
|
207
|
+
isMultiSelect: true,
|
|
208
|
+
},
|
|
209
|
+
parameters: {
|
|
210
|
+
docs: {
|
|
211
|
+
description: {
|
|
212
|
+
story:
|
|
213
|
+
"An ARIA grid named by `ariaLabel`. One row is in the tab order: the selection anchor, or else the first row. Keys pressed in a cell's own field stay with the field.",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** With no rows to show, the footer counts no entries. */
|
|
220
|
+
export const Empty = {
|
|
221
|
+
args: {
|
|
222
|
+
...Default.args,
|
|
223
|
+
id: "dataGridEmpty",
|
|
224
|
+
rows: [],
|
|
225
|
+
selection: [],
|
|
226
|
+
pagination: { page: 0, pageSize: 10, pageSizes: [10, 20, 50] },
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const TITLES_IT = {
|
|
231
|
+
id: "Id",
|
|
232
|
+
name: "Nome",
|
|
233
|
+
age: "Età",
|
|
234
|
+
active: "Attivo",
|
|
235
|
+
role: "Ruolo",
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Every text the grid shows or announces is a prop, with English as the default. */
|
|
239
|
+
export const Localized = {
|
|
240
|
+
args: {
|
|
241
|
+
...Default.args,
|
|
242
|
+
id: "dataGridLocalized",
|
|
243
|
+
ariaLabel: "Utenti",
|
|
244
|
+
rows: createRows(24),
|
|
245
|
+
selection: [],
|
|
246
|
+
columns: baseColumns.map((column) => ({
|
|
247
|
+
...column,
|
|
248
|
+
title: TITLES_IT[column.id],
|
|
249
|
+
isEditable: column.id === "name",
|
|
250
|
+
filter: {
|
|
251
|
+
...column.filter,
|
|
252
|
+
ariaLabel: `Filtra ${TITLES_IT[column.id]}`,
|
|
253
|
+
minLabel: `Filtra ${TITLES_IT[column.id]}: minimo`,
|
|
254
|
+
maxLabel: `Filtra ${TITLES_IT[column.id]}: massimo`,
|
|
255
|
+
},
|
|
256
|
+
})),
|
|
257
|
+
search: { value: "", ariaLabel: "Cerca", placeholder: "Cerca…" },
|
|
258
|
+
edits: { 1: { name: "Carlo" } },
|
|
259
|
+
saveLabel: "Salva le modifiche",
|
|
260
|
+
resetLabel: "Annulla",
|
|
261
|
+
formatEditCount: (count) =>
|
|
262
|
+
count === 1 ? "1 modifica" : `${count} modifiche`,
|
|
263
|
+
formatRange: ({ start, end, total }) =>
|
|
264
|
+
total ? `${start}–${end} di ${total} righe` : "Nessuna riga",
|
|
265
|
+
pageLabel: "Pagina",
|
|
266
|
+
pageSizeLabel: "Righe per pagina",
|
|
267
|
+
formatPageLabel: (page) => `Vai a pagina ${page}`,
|
|
268
|
+
pagination: { page: 0, pageSize: 10, pageSizes: [10, 20, 50] },
|
|
269
|
+
},
|
|
270
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { html } from "@inglorious/web"
|
|
8
|
+
import { html, nothing } from "@inglorious/web"
|
|
9
9
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
10
10
|
import { repeat } from "@inglorious/web/directives/repeat"
|
|
11
11
|
import { when } from "@inglorious/web/directives/when"
|
|
@@ -40,6 +40,10 @@ const DATE_PART_LENGTH = 2
|
|
|
40
40
|
/** Date's months count from zero, people's from one. */
|
|
41
41
|
const MONTH_OFFSET = 1
|
|
42
42
|
const INPUT_TYPES = { number: "number", date: "date", url: "url" }
|
|
43
|
+
const FIRST_ROW = 0
|
|
44
|
+
const LAST_ROW = -1
|
|
45
|
+
const ROW_STEPS = { ArrowDown: 1, ArrowUp: -1 }
|
|
46
|
+
const ARIA_SORT = { asc: "ascending", desc: "descending" }
|
|
43
47
|
|
|
44
48
|
export const DataGrid = {
|
|
45
49
|
/**
|
|
@@ -67,21 +71,28 @@ export const DataGrid = {
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
return html`<div class=${classMap(classes)}>
|
|
70
|
-
${
|
|
74
|
+
${when(props.search, () => this.renderToolbar?.(props))}
|
|
75
|
+
<div
|
|
76
|
+
class="iw-data-grid-table"
|
|
77
|
+
role="grid"
|
|
78
|
+
aria-label=${props.ariaLabel ?? nothing}
|
|
79
|
+
aria-multiselectable=${props.isMultiSelect ? "true" : nothing}
|
|
80
|
+
>
|
|
81
|
+
${this.renderHeader?.(props)} ${this.renderBody?.(props)}
|
|
82
|
+
</div>
|
|
71
83
|
${this.renderFooter?.(props)}
|
|
72
84
|
</div> `
|
|
73
85
|
},
|
|
74
86
|
|
|
75
87
|
/**
|
|
76
|
-
* Renders the grid header region
|
|
88
|
+
* Renders the grid header region with the column headers.
|
|
77
89
|
* Column rendering is delegated to `renderHeaderColumn`.
|
|
78
90
|
* @param {DataGridProps} props The data grid props.
|
|
79
91
|
* @returns {TemplateResult} The rendered header.
|
|
80
92
|
*/
|
|
81
93
|
renderHeader(props) {
|
|
82
94
|
return html`<div class="iw-data-grid-header">
|
|
83
|
-
|
|
84
|
-
<div class="iw-data-grid-header-row">
|
|
95
|
+
<div class="iw-data-grid-header-row" role="row">
|
|
85
96
|
${repeat(
|
|
86
97
|
props.columns,
|
|
87
98
|
(column) => column.id,
|
|
@@ -113,8 +124,12 @@ export const DataGrid = {
|
|
|
113
124
|
* @returns {TemplateResult} The rendered header column.
|
|
114
125
|
*/
|
|
115
126
|
renderHeaderColumn(props, { column }) {
|
|
127
|
+
const direction = getSortDirection(props, column.id)
|
|
128
|
+
|
|
116
129
|
return html`<div
|
|
117
130
|
class="iw-data-grid-header-column"
|
|
131
|
+
role="columnheader"
|
|
132
|
+
aria-sort=${column.isSortable ? (ARIA_SORT[direction] ?? "none") : nothing}
|
|
118
133
|
style=${getColumnStyle(column)}
|
|
119
134
|
>
|
|
120
135
|
${when(
|
|
@@ -126,9 +141,7 @@ export const DataGrid = {
|
|
|
126
141
|
@click=${() => props.onSortChange?.(column.id)}
|
|
127
142
|
>
|
|
128
143
|
${column.title}
|
|
129
|
-
<span aria-hidden="true"
|
|
130
|
-
>${getSortIcon(getSortDirection(props, column.id))}</span
|
|
131
|
-
>
|
|
144
|
+
<span aria-hidden="true">${getSortIcon(direction)}</span>
|
|
132
145
|
</button>`,
|
|
133
146
|
() =>
|
|
134
147
|
html`<div class="iw-data-grid-header-title">${column.title}</div>`,
|
|
@@ -176,14 +189,20 @@ export const DataGrid = {
|
|
|
176
189
|
*/
|
|
177
190
|
renderBody(props) {
|
|
178
191
|
const rows = getRows(props)
|
|
192
|
+
const rowIds = rows.map((row) => getRowKeyValue(props, row))
|
|
193
|
+
const tabStopId = rowIds.includes(props.selectionAnchor)
|
|
194
|
+
? props.selectionAnchor
|
|
195
|
+
: rowIds[FIRST_ROW]
|
|
196
|
+
const renderRow = (row, index) =>
|
|
197
|
+
this.renderRow?.(props, {
|
|
198
|
+
row,
|
|
199
|
+
index,
|
|
200
|
+
isTabStop: getRowKeyValue(props, row) === tabStopId,
|
|
201
|
+
})
|
|
179
202
|
|
|
180
203
|
if (!props.isVirtualized) {
|
|
181
204
|
return html`<div class="iw-data-grid-body">
|
|
182
|
-
${repeat(
|
|
183
|
-
rows,
|
|
184
|
-
(row) => getRowKeyValue(props, row),
|
|
185
|
-
(row, index) => this.renderRow?.(props, { row, index }),
|
|
186
|
-
)}
|
|
205
|
+
${repeat(rows, (row) => getRowKeyValue(props, row), renderRow)}
|
|
187
206
|
</div>`
|
|
188
207
|
}
|
|
189
208
|
|
|
@@ -211,8 +230,7 @@ export const DataGrid = {
|
|
|
211
230
|
}
|
|
212
231
|
},
|
|
213
232
|
getType: () => ({
|
|
214
|
-
renderItem: (_entity, { item, index }) =>
|
|
215
|
-
this.renderRow?.(props, { row: item, index }),
|
|
233
|
+
renderItem: (_entity, { item, index }) => renderRow(item, index),
|
|
216
234
|
}),
|
|
217
235
|
}
|
|
218
236
|
|
|
@@ -221,17 +239,25 @@ export const DataGrid = {
|
|
|
221
239
|
|
|
222
240
|
/**
|
|
223
241
|
* Renders a single data row and wires row click/selection handlers.
|
|
224
|
-
*
|
|
242
|
+
* The row is focusable: arrows, Home and End move between rows, Space
|
|
243
|
+
* toggles the row as Ctrl+click does, Enter selects it alone, and Shift
|
|
244
|
+
* with either extends the selection. Only the tab stop row is in the tab
|
|
245
|
+
* order. Cells are delegated to `renderCell` for customization.
|
|
225
246
|
* @param {DataGridProps} props The data grid props.
|
|
226
247
|
* @param {object} payload The payload.
|
|
227
248
|
* @param {Row} payload.row The row data.
|
|
228
249
|
* @param {number} payload.index The row index.
|
|
250
|
+
* @param {boolean} [payload.isTabStop] Whether Tab reaches this row.
|
|
229
251
|
* @returns {TemplateResult} The rendered row.
|
|
230
252
|
*/
|
|
231
|
-
renderRow(props, { row, index }) {
|
|
253
|
+
renderRow(props, { row, index, isTabStop = false }) {
|
|
232
254
|
const rowId = getRowKeyValue(props, row)
|
|
255
|
+
const isSelected = Boolean(props.selection?.includes(rowId))
|
|
233
256
|
|
|
234
257
|
return html`<div
|
|
258
|
+
role="row"
|
|
259
|
+
aria-selected=${String(isSelected)}
|
|
260
|
+
tabindex=${isTabStop ? "0" : "-1"}
|
|
235
261
|
@click=${(event) =>
|
|
236
262
|
props.onRowClick?.({
|
|
237
263
|
rowId,
|
|
@@ -239,9 +265,10 @@ export const DataGrid = {
|
|
|
239
265
|
metaKey: event.metaKey,
|
|
240
266
|
ctrlKey: event.ctrlKey,
|
|
241
267
|
})}
|
|
268
|
+
@keydown=${(event) => handleRowKeydown(event, props, rowId)}
|
|
242
269
|
class="iw-data-grid-row ${classMap({
|
|
243
270
|
"iw-data-grid-row-even": index % DIVISOR,
|
|
244
|
-
"iw-data-grid-row-selected":
|
|
271
|
+
"iw-data-grid-row-selected": isSelected,
|
|
245
272
|
})}"
|
|
246
273
|
>
|
|
247
274
|
${repeat(
|
|
@@ -272,6 +299,7 @@ export const DataGrid = {
|
|
|
272
299
|
const isEditable = Boolean(column.isEditable && row)
|
|
273
300
|
|
|
274
301
|
return html`<div
|
|
302
|
+
role="gridcell"
|
|
275
303
|
class="iw-data-grid-cell ${classMap({
|
|
276
304
|
"iw-data-grid-cell-number": column.type === "number",
|
|
277
305
|
"iw-data-grid-cell-date": column.type === "date",
|
|
@@ -382,8 +410,11 @@ export const DataGrid = {
|
|
|
382
410
|
() =>
|
|
383
411
|
html`<div class="iw-data-grid-footer-row">
|
|
384
412
|
<div>
|
|
385
|
-
${
|
|
386
|
-
|
|
413
|
+
${(props.formatRange ?? formatRange)({
|
|
414
|
+
start: pagination.start + PRETTY_PAGE,
|
|
415
|
+
end: pagination.end,
|
|
416
|
+
total: pagination.totalRows,
|
|
417
|
+
})}
|
|
387
418
|
</div>
|
|
388
419
|
|
|
389
420
|
${this.renderPagination?.(props, pagination)}
|
|
@@ -405,11 +436,11 @@ export const DataGrid = {
|
|
|
405
436
|
renderEdits(props, editCount) {
|
|
406
437
|
return html`<div class="iw-data-grid-footer-row iw-data-grid-edits">
|
|
407
438
|
<div role="status">
|
|
408
|
-
${
|
|
439
|
+
${(props.formatEditCount ?? formatEditCount)(editCount)}
|
|
409
440
|
</div>
|
|
410
441
|
<div class="iw-data-grid-edits-actions">
|
|
411
442
|
${Button.render({
|
|
412
|
-
children: "Reset",
|
|
443
|
+
children: props.resetLabel ?? "Reset",
|
|
413
444
|
variant: "ghost",
|
|
414
445
|
size: "sm",
|
|
415
446
|
onClick: () => props.onEditsReset?.(),
|
|
@@ -438,6 +469,7 @@ export const DataGrid = {
|
|
|
438
469
|
buttonSize: "sm",
|
|
439
470
|
buttonVariant: "outline",
|
|
440
471
|
itemClassName: "iw-data-grid-pagination-button",
|
|
472
|
+
formatPageLabel: props.formatPageLabel,
|
|
441
473
|
},
|
|
442
474
|
{
|
|
443
475
|
label: "«",
|
|
@@ -451,6 +483,7 @@ export const DataGrid = {
|
|
|
451
483
|
buttonSize: "sm",
|
|
452
484
|
buttonVariant: "outline",
|
|
453
485
|
itemClassName: "iw-data-grid-pagination-button",
|
|
486
|
+
formatPageLabel: props.formatPageLabel,
|
|
454
487
|
},
|
|
455
488
|
{
|
|
456
489
|
label: "‹",
|
|
@@ -461,6 +494,7 @@ export const DataGrid = {
|
|
|
461
494
|
)}
|
|
462
495
|
${Input.render({
|
|
463
496
|
name: "page",
|
|
497
|
+
ariaLabel: props.pageLabel ?? "Page",
|
|
464
498
|
size: "sm",
|
|
465
499
|
inputType: "number",
|
|
466
500
|
min: 1,
|
|
@@ -475,6 +509,7 @@ export const DataGrid = {
|
|
|
475
509
|
buttonSize: "sm",
|
|
476
510
|
buttonVariant: "outline",
|
|
477
511
|
itemClassName: "iw-data-grid-pagination-button",
|
|
512
|
+
formatPageLabel: props.formatPageLabel,
|
|
478
513
|
},
|
|
479
514
|
{
|
|
480
515
|
label: "›",
|
|
@@ -488,6 +523,7 @@ export const DataGrid = {
|
|
|
488
523
|
buttonSize: "sm",
|
|
489
524
|
buttonVariant: "outline",
|
|
490
525
|
itemClassName: "iw-data-grid-pagination-button",
|
|
526
|
+
formatPageLabel: props.formatPageLabel,
|
|
491
527
|
},
|
|
492
528
|
{
|
|
493
529
|
label: "»",
|
|
@@ -502,8 +538,9 @@ export const DataGrid = {
|
|
|
502
538
|
|
|
503
539
|
renderPageSize(props, pagination) {
|
|
504
540
|
return html`<div class="iw-data-grid-footer-row">
|
|
505
|
-
<div
|
|
541
|
+
<div>${props.pageSizeLabel ?? "Page size"}</div>
|
|
506
542
|
${Select.render({
|
|
543
|
+
ariaLabel: props.pageSizeLabel ?? "Page size",
|
|
507
544
|
size: "sm",
|
|
508
545
|
value: pagination.pageSize,
|
|
509
546
|
options: pagination.pageSizes,
|
|
@@ -636,3 +673,72 @@ function fromField(text, type, saved) {
|
|
|
636
673
|
date.setFullYear(year, month - MONTH_OFFSET, day)
|
|
637
674
|
return saved instanceof Date ? date : date.getTime()
|
|
638
675
|
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Selects or moves between rows from the keyboard. Keys pressed in a cell's
|
|
679
|
+
* field stay the field's.
|
|
680
|
+
* @param {KeyboardEvent} event
|
|
681
|
+
* @param {DataGridProps} props
|
|
682
|
+
* @param {string|number} rowId
|
|
683
|
+
*/
|
|
684
|
+
function handleRowKeydown(event, props, rowId) {
|
|
685
|
+
if (event.target !== event.currentTarget) return
|
|
686
|
+
|
|
687
|
+
if (event.key === " " || event.key === "Enter") {
|
|
688
|
+
event.preventDefault()
|
|
689
|
+
props.onRowClick?.({
|
|
690
|
+
rowId,
|
|
691
|
+
shiftKey: event.shiftKey,
|
|
692
|
+
metaKey: event.metaKey,
|
|
693
|
+
ctrlKey: event.ctrlKey || event.key === " ",
|
|
694
|
+
})
|
|
695
|
+
return
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const rows = [
|
|
699
|
+
...event.currentTarget
|
|
700
|
+
.closest(".iw-data-grid-body")
|
|
701
|
+
.querySelectorAll(".iw-data-grid-row"),
|
|
702
|
+
]
|
|
703
|
+
const target = getRowToFocus(
|
|
704
|
+
rows,
|
|
705
|
+
rows.indexOf(event.currentTarget),
|
|
706
|
+
event.key,
|
|
707
|
+
)
|
|
708
|
+
if (!target) return
|
|
709
|
+
|
|
710
|
+
event.preventDefault()
|
|
711
|
+
target.focus()
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* The row a navigation key moves focus to, if any.
|
|
716
|
+
* @param {HTMLElement[]} rows
|
|
717
|
+
* @param {number} current
|
|
718
|
+
* @param {string} key
|
|
719
|
+
* @returns {HTMLElement|undefined}
|
|
720
|
+
*/
|
|
721
|
+
function getRowToFocus(rows, current, key) {
|
|
722
|
+
if (key === "Home") return rows[FIRST_ROW]
|
|
723
|
+
if (key === "End") return rows.at(LAST_ROW)
|
|
724
|
+
if (key in ROW_STEPS) return rows[current + ROW_STEPS[key]]
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* The footer's default count of the rows shown.
|
|
729
|
+
* @param {{ start: number, end: number, total: number }} range
|
|
730
|
+
* @returns {string}
|
|
731
|
+
*/
|
|
732
|
+
function formatRange({ start, end, total }) {
|
|
733
|
+
if (!total) return "0 entries"
|
|
734
|
+
return `${start} to ${end} of ${total} entries`
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* The edits bar's default count of the unsaved edits.
|
|
739
|
+
* @param {number} count
|
|
740
|
+
* @returns {string}
|
|
741
|
+
*/
|
|
742
|
+
function formatEditCount(count) {
|
|
743
|
+
return `${count} ${count === ONE_EDIT ? "change" : "changes"}`
|
|
744
|
+
}
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.iw-data-grid-toolbar {
|
|
13
|
-
padding:
|
|
14
|
-
|
|
13
|
+
padding: var(--iw-space-4) var(--iw-space-4) var(--iw-space-3);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.iw-data-grid-toolbar + .iw-data-grid-table .iw-data-grid-header {
|
|
17
|
+
padding-top: var(--iw-space-3);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
.iw-data-grid-header-row {
|
|
@@ -71,6 +74,11 @@
|
|
|
71
74
|
.iw-data-grid-row {
|
|
72
75
|
align-items: center;
|
|
73
76
|
column-gap: var(--iw-space-4);
|
|
77
|
+
|
|
78
|
+
&:focus-visible {
|
|
79
|
+
outline: 2px solid var(--iw-color-primary);
|
|
80
|
+
outline-offset: -2px;
|
|
81
|
+
}
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
&.iw-data-grid-striped {
|
|
@@ -225,7 +225,7 @@ export const List = {
|
|
|
225
225
|
shape: "square",
|
|
226
226
|
className: "iw-list-item-toggle",
|
|
227
227
|
isDisabled,
|
|
228
|
-
ariaLabel: "Toggle section",
|
|
228
|
+
ariaLabel: props.toggleLabel ?? "Toggle section",
|
|
229
229
|
"aria-expanded": isExpanded,
|
|
230
230
|
onClick: (event) => {
|
|
231
231
|
event.stopPropagation()
|
|
@@ -76,3 +76,14 @@ export const Actionable = {
|
|
|
76
76
|
onClose: () => {},
|
|
77
77
|
},
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
/** The dismiss button's accessible name comes from `dismissLabel`. */
|
|
81
|
+
export const Localized = {
|
|
82
|
+
args: {
|
|
83
|
+
title: "Salvato",
|
|
84
|
+
description: "Le modifiche sono state salvate.",
|
|
85
|
+
severity: "success",
|
|
86
|
+
dismissLabel: "Chiudi",
|
|
87
|
+
onClose: () => {},
|
|
88
|
+
},
|
|
89
|
+
}
|
|
@@ -28,6 +28,7 @@ export function render(props) {
|
|
|
28
28
|
icon,
|
|
29
29
|
action,
|
|
30
30
|
onClose,
|
|
31
|
+
dismissLabel = "Dismiss",
|
|
31
32
|
className = "",
|
|
32
33
|
...rest
|
|
33
34
|
} = props
|
|
@@ -73,7 +74,7 @@ export function render(props) {
|
|
|
73
74
|
html`<button
|
|
74
75
|
type="button"
|
|
75
76
|
class="iw-alert-close"
|
|
76
|
-
aria-label
|
|
77
|
+
aria-label=${dismissLabel}
|
|
77
78
|
@click=${onClose}
|
|
78
79
|
>
|
|
79
80
|
×
|
|
@@ -46,3 +46,14 @@ export const AboveBottomNavigation = {
|
|
|
46
46
|
},
|
|
47
47
|
],
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
/** The dismiss button's accessible name comes from `dismissLabel`. */
|
|
51
|
+
export const Localized = {
|
|
52
|
+
args: {
|
|
53
|
+
isOpen: true,
|
|
54
|
+
message: "Salvato",
|
|
55
|
+
position: "bottom-left",
|
|
56
|
+
dismissLabel: "Chiudi",
|
|
57
|
+
onClose: () => {},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
@@ -26,6 +26,7 @@ export function render(props) {
|
|
|
26
26
|
className = "",
|
|
27
27
|
position = "bottom-left",
|
|
28
28
|
onClose,
|
|
29
|
+
dismissLabel = "Dismiss",
|
|
29
30
|
} = props
|
|
30
31
|
|
|
31
32
|
if (!isOpen) return null
|
|
@@ -57,7 +58,7 @@ export function render(props) {
|
|
|
57
58
|
html`<button
|
|
58
59
|
type="button"
|
|
59
60
|
class="iw-snackbar-close"
|
|
60
|
-
aria-label
|
|
61
|
+
aria-label=${dismissLabel}
|
|
61
62
|
@click=${onClose}
|
|
62
63
|
>
|
|
63
64
|
×
|
|
@@ -31,3 +31,15 @@ export const Default = {
|
|
|
31
31
|
],
|
|
32
32
|
},
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
/** The navigation's accessible name follows the app's language through `ariaLabel`. */
|
|
36
|
+
export const Localized = {
|
|
37
|
+
args: {
|
|
38
|
+
ariaLabel: "Percorso",
|
|
39
|
+
items: [
|
|
40
|
+
{ label: "Home", href: "#" },
|
|
41
|
+
{ label: "Libreria", href: "#" },
|
|
42
|
+
{ label: "Dati" },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
}
|
|
@@ -33,6 +33,7 @@ export const Breadcrumbs = {
|
|
|
33
33
|
type, // eslint-disable-line no-unused-vars
|
|
34
34
|
items = [],
|
|
35
35
|
separator = "/",
|
|
36
|
+
ariaLabel = "Breadcrumb",
|
|
36
37
|
className = "",
|
|
37
38
|
...rest
|
|
38
39
|
} = props
|
|
@@ -45,7 +46,7 @@ export const Breadcrumbs = {
|
|
|
45
46
|
)
|
|
46
47
|
|
|
47
48
|
return html`<nav
|
|
48
|
-
aria-label
|
|
49
|
+
aria-label=${ariaLabel}
|
|
49
50
|
class=${classMap({ "iw-breadcrumbs": true, ...extraClasses })}
|
|
50
51
|
${ref((el) => applyElementProps(el, rest))}
|
|
51
52
|
>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Form } from "@inglorious/web/form"
|
|
2
|
+
|
|
1
3
|
import { createRender } from "../../stories/notify-story.js"
|
|
2
4
|
import { Menu } from "."
|
|
3
5
|
|
|
@@ -47,3 +49,16 @@ export const WithTrigger = {
|
|
|
47
49
|
items: Default.args.items,
|
|
48
50
|
},
|
|
49
51
|
}
|
|
52
|
+
|
|
53
|
+
/** A menu with a trigger inside a form opens as anywhere else: the Form type cancels only buttons that would submit or reset the form. */
|
|
54
|
+
export const InsideAForm = {
|
|
55
|
+
args: WithTrigger.args,
|
|
56
|
+
decorators: [
|
|
57
|
+
(story) => {
|
|
58
|
+
Form.init()
|
|
59
|
+
const form = document.createElement("form")
|
|
60
|
+
form.append(story())
|
|
61
|
+
return form
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}
|
|
@@ -59,3 +59,12 @@ export const Default = {
|
|
|
59
59
|
buttonSize: "sm",
|
|
60
60
|
},
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
/** `ariaLabel` names the navigation and `formatPageLabel` each control, in the app's language. */
|
|
64
|
+
export const Localized = {
|
|
65
|
+
args: {
|
|
66
|
+
...Default.args,
|
|
67
|
+
ariaLabel: "Pagine",
|
|
68
|
+
formatPageLabel: (page) => `Vai a pagina ${page}`,
|
|
69
|
+
},
|
|
70
|
+
}
|
|
@@ -38,6 +38,7 @@ export const Pagination = {
|
|
|
38
38
|
isFirstButtonVisible = false,
|
|
39
39
|
isLastButtonVisible = false,
|
|
40
40
|
isDisabled = false,
|
|
41
|
+
ariaLabel = "Pagination",
|
|
41
42
|
className = "",
|
|
42
43
|
} = props
|
|
43
44
|
|
|
@@ -51,7 +52,7 @@ export const Pagination = {
|
|
|
51
52
|
|
|
52
53
|
return html`<nav
|
|
53
54
|
class=${classMap({ "iw-pagination": true, ...extraClasses })}
|
|
54
|
-
aria-label
|
|
55
|
+
aria-label=${ariaLabel}
|
|
55
56
|
>
|
|
56
57
|
${when(isFirstButtonVisible, () =>
|
|
57
58
|
this.renderControl(props, {
|
|
@@ -130,7 +131,7 @@ export const Pagination = {
|
|
|
130
131
|
className: ["iw-pagination-item", props.itemClassName ?? ""]
|
|
131
132
|
.filter(Boolean)
|
|
132
133
|
.join(" "),
|
|
133
|
-
ariaLabel:
|
|
134
|
+
ariaLabel: (props.formatPageLabel ?? formatPageLabel)(target),
|
|
134
135
|
isDisabled,
|
|
135
136
|
onClick: () => onChange?.(target),
|
|
136
137
|
children: label,
|
|
@@ -138,6 +139,10 @@ export const Pagination = {
|
|
|
138
139
|
},
|
|
139
140
|
}
|
|
140
141
|
|
|
142
|
+
function formatPageLabel(page) {
|
|
143
|
+
return `Go to page ${page}`
|
|
144
|
+
}
|
|
145
|
+
|
|
141
146
|
function getPageItems(page, count, siblingCount) {
|
|
142
147
|
if (count <= 7) {
|
|
143
148
|
return Array.from({ length: count }, (_, index) => index + PRETTY_PAGE)
|
|
@@ -38,3 +38,16 @@ export const Default = {
|
|
|
38
38
|
],
|
|
39
39
|
},
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/** Actions with only an icon are named by `formatActionLabel`, counted from 1. */
|
|
43
|
+
export const UnlabelledActions = {
|
|
44
|
+
args: {
|
|
45
|
+
isOpen: true,
|
|
46
|
+
ariaLabel: "Azioni rapide",
|
|
47
|
+
formatActionLabel: (index) => `Azione ${index}`,
|
|
48
|
+
actions: [
|
|
49
|
+
{ value: "edit", icon: "✎" },
|
|
50
|
+
{ value: "share", icon: "↗" },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
}
|
|
@@ -10,6 +10,8 @@ import { ref } from "@inglorious/web/directives/ref"
|
|
|
10
10
|
|
|
11
11
|
import { syncPopover } from "../../shared/top-layer.js"
|
|
12
12
|
|
|
13
|
+
const HUMAN_OFFSET = 1
|
|
14
|
+
|
|
13
15
|
export const SpeedDial = {
|
|
14
16
|
/**
|
|
15
17
|
* Main entrypoint for the speed dial. Delegates to the base renderer for overrides.
|
|
@@ -96,7 +98,10 @@ export const SpeedDial = {
|
|
|
96
98
|
})
|
|
97
99
|
}
|
|
98
100
|
})}
|
|
99
|
-
aria-label=${
|
|
101
|
+
aria-label=${
|
|
102
|
+
action.label ??
|
|
103
|
+
(props.formatActionLabel ?? formatActionLabel)(index + HUMAN_OFFSET)
|
|
104
|
+
}
|
|
100
105
|
?disabled=${action.isDisabled}
|
|
101
106
|
@click=${() => {
|
|
102
107
|
action.onClick?.(action.value ?? index)
|
|
@@ -107,3 +112,7 @@ export const SpeedDial = {
|
|
|
107
112
|
</button>`
|
|
108
113
|
},
|
|
109
114
|
}
|
|
115
|
+
|
|
116
|
+
function formatActionLabel(index) {
|
|
117
|
+
return `Speed dial action ${index}`
|
|
118
|
+
}
|
|
@@ -21,6 +21,8 @@ export interface ButtonProps {
|
|
|
21
21
|
isPressed?: boolean
|
|
22
22
|
isFullWidth?: boolean
|
|
23
23
|
buttonType?: "button" | "submit" | "reset" | "menu"
|
|
24
|
+
/** Renders a link styled as a button instead of a `<button>` */
|
|
25
|
+
href?: string
|
|
24
26
|
ariaLabel?: string
|
|
25
27
|
isAriaPressed?: boolean
|
|
26
28
|
className?: string
|
|
@@ -14,6 +14,8 @@ export interface FileUploadProps {
|
|
|
14
14
|
selectedFiles?: string[]
|
|
15
15
|
title?: string
|
|
16
16
|
description?: string
|
|
17
|
+
/** Shown while no file is selected. Defaults to "No file selected". */
|
|
18
|
+
emptyLabel?: string
|
|
17
19
|
onChange?: (files: FileList | null) => void
|
|
18
20
|
onBlur?: () => void
|
|
19
21
|
onFocus?: () => void
|
|
@@ -13,6 +13,18 @@ export interface CarouselProps {
|
|
|
13
13
|
id?: string
|
|
14
14
|
type?: string
|
|
15
15
|
label?: string
|
|
16
|
+
/** What screen readers call the carousel. Defaults to "carousel". */
|
|
17
|
+
roleDescription?: string
|
|
18
|
+
/** What screen readers call a slide. Defaults to "slide". */
|
|
19
|
+
slideRoleDescription?: string
|
|
20
|
+
/** A slide's accessible name. Defaults to "{index} of {total}", counted from 1. */
|
|
21
|
+
formatSlideLabel?: (index: number, total: number) => string
|
|
22
|
+
/** A dot's accessible name. Defaults to "Go to slide {index}", counted from 1. */
|
|
23
|
+
formatIndicatorLabel?: (index: number) => string
|
|
24
|
+
/** The previous arrow's accessible name. Defaults to "Previous slide". */
|
|
25
|
+
previousLabel?: string
|
|
26
|
+
/** The next arrow's accessible name. Defaults to "Next slide". */
|
|
27
|
+
nextLabel?: string
|
|
16
28
|
|
|
17
29
|
items?: unknown[]
|
|
18
30
|
/** Index of the item the viewport has settled on. */
|
|
@@ -79,6 +79,8 @@ export interface DataGridProps<T extends Row = Row> {
|
|
|
79
79
|
/** The search field's accessible name. Defaults to "Search". */
|
|
80
80
|
ariaLabel?: string
|
|
81
81
|
} | null
|
|
82
|
+
/** The grid's accessible name. */
|
|
83
|
+
ariaLabel?: string
|
|
82
84
|
isStriped?: boolean
|
|
83
85
|
isVirtualized?: boolean
|
|
84
86
|
virtualization?: DataGridVirtualization
|
|
@@ -101,6 +103,18 @@ export interface DataGridProps<T extends Row = Row> {
|
|
|
101
103
|
edits?: Record<string | number, Record<string, any>>
|
|
102
104
|
/** The label of the button that saves the edits. Defaults to "Save changes". */
|
|
103
105
|
saveLabel?: string
|
|
106
|
+
/** The label of the button that resets the edits. Defaults to "Reset". */
|
|
107
|
+
resetLabel?: string
|
|
108
|
+
/** The edits bar's count. Defaults to "{count} change(s)". */
|
|
109
|
+
formatEditCount?: (count: number) => string
|
|
110
|
+
/** The footer's count of the rows shown. Defaults to "{start} to {end} of {total} entries", or "0 entries". */
|
|
111
|
+
formatRange?: (range: { start: number; end: number; total: number }) => string
|
|
112
|
+
/** The page field's accessible name. Defaults to "Page". */
|
|
113
|
+
pageLabel?: string
|
|
114
|
+
/** The page size selector's label. Defaults to "Page size". */
|
|
115
|
+
pageSizeLabel?: string
|
|
116
|
+
/** A page control's accessible name. Defaults to "Go to page {page}". */
|
|
117
|
+
formatPageLabel?: (page: number) => string
|
|
104
118
|
onCellChange?: (payload: {
|
|
105
119
|
rowId: string | number
|
|
106
120
|
columnId: string
|
|
@@ -32,6 +32,8 @@ export interface ListProps {
|
|
|
32
32
|
padding?: "none" | "sm" | "md" | "lg" | "xl"
|
|
33
33
|
path?: number[]
|
|
34
34
|
className?: string
|
|
35
|
+
/** The expand toggle's accessible name. Defaults to "Toggle section". */
|
|
36
|
+
toggleLabel?: string
|
|
35
37
|
onItemClick?: (item: ListItem, path: number[]) => void
|
|
36
38
|
onItemToggle?: (item: ListItem, path: number[]) => void
|
|
37
39
|
[key: string]: unknown
|
|
@@ -13,6 +13,8 @@ export interface AlertProps {
|
|
|
13
13
|
icon?: TemplateResult | string | number
|
|
14
14
|
action?: TemplateResult | string | number
|
|
15
15
|
onClose?: () => void
|
|
16
|
+
/** The dismiss button's accessible name. Defaults to "Dismiss". */
|
|
17
|
+
dismissLabel?: string
|
|
16
18
|
className?: string
|
|
17
19
|
[key: string]: unknown
|
|
18
20
|
}
|
|
@@ -13,6 +13,8 @@ export interface BreadcrumbsProps {
|
|
|
13
13
|
type?: string
|
|
14
14
|
items: BreadcrumbItem[]
|
|
15
15
|
separator?: TemplateResult | string | number
|
|
16
|
+
/** The navigation's accessible name. Defaults to "Breadcrumb". */
|
|
17
|
+
ariaLabel?: string
|
|
16
18
|
className?: string
|
|
17
19
|
[key: string]: unknown
|
|
18
20
|
}
|
|
@@ -15,6 +15,10 @@ export interface PaginationProps {
|
|
|
15
15
|
buttonSize?: "sm" | "md" | "lg"
|
|
16
16
|
itemClassName?: string
|
|
17
17
|
className?: string
|
|
18
|
+
/** The navigation's accessible name. Defaults to "Pagination". */
|
|
19
|
+
ariaLabel?: string
|
|
20
|
+
/** A control's accessible name. Defaults to "Go to page {page}". */
|
|
21
|
+
formatPageLabel?: (page: number) => string
|
|
18
22
|
onChange?: (page: number) => void
|
|
19
23
|
[key: string]: unknown
|
|
20
24
|
}
|
|
@@ -18,6 +18,8 @@ export interface SpeedDialProps {
|
|
|
18
18
|
ariaLabel?: string
|
|
19
19
|
direction?: "up" | "down" | "left" | "right"
|
|
20
20
|
actions?: SpeedDialAction[]
|
|
21
|
+
/** The name of an action with no label. Defaults to "Speed dial action {index}", counted from 1. */
|
|
22
|
+
formatActionLabel?: (index: number) => string
|
|
21
23
|
/**
|
|
22
24
|
* Called with the new state when the browser opens or closes the actions.
|
|
23
25
|
* Without it, the browser alone keeps the open state and `isOpen` is not
|