@inglorious/ui 8.4.2 → 8.6.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 +4 -4
- package/src/controls/button/button.stories.js +22 -0
- package/src/controls/button/template.js +35 -2
- package/src/controls/file-upload/style.css +4 -0
- package/src/feedback/snackbar/snackbar.stories.js +25 -1
- package/src/feedback/snackbar/style.css +31 -10
- package/types/controls/button.d.ts +7 -1
- package/types/feedback/snackbar.d.ts +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.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",
|
|
@@ -308,19 +308,19 @@
|
|
|
308
308
|
"peerDependencies": {
|
|
309
309
|
"@inglorious/store": "12.0.0",
|
|
310
310
|
"@inglorious/utils": "5.1.0",
|
|
311
|
-
"@inglorious/web": "6.
|
|
311
|
+
"@inglorious/web": "6.9.0"
|
|
312
312
|
},
|
|
313
313
|
"devDependencies": {
|
|
314
314
|
"@iconify-icons/fa7-brands": "^2.0.0",
|
|
315
315
|
"@iconify-icons/fa7-regular": "^2.0.0",
|
|
316
316
|
"@iconify-icons/fa7-solid": "^2.0.0",
|
|
317
317
|
"@iconify-icons/material-symbols": "^2.0.6",
|
|
318
|
-
"@inglorious/charts": "11.1.
|
|
318
|
+
"@inglorious/charts": "11.1.7",
|
|
319
319
|
"@inglorious/eslint-config": "1.2.0",
|
|
320
320
|
"@inglorious/store": "12.0.0",
|
|
321
321
|
"@inglorious/vite-plugin-jsx": "2.0.5",
|
|
322
322
|
"@inglorious/vite-plugin-vue": "1.0.6",
|
|
323
|
-
"@inglorious/web": "6.
|
|
323
|
+
"@inglorious/web": "6.9.0",
|
|
324
324
|
"@storybook/addon-docs": "^10.6.0",
|
|
325
325
|
"@storybook/web-components-vite": "^10.6.0",
|
|
326
326
|
"happy-dom": "^20.14.0",
|
|
@@ -57,7 +57,19 @@ export default {
|
|
|
57
57
|
options: ["button", "submit", "reset", "menu"],
|
|
58
58
|
description: "Native HTML button type attribute.",
|
|
59
59
|
},
|
|
60
|
+
accept: {
|
|
61
|
+
control: "text",
|
|
62
|
+
description: "File types the picker offers (with `onFileSelect`).",
|
|
63
|
+
},
|
|
64
|
+
isMultiple: {
|
|
65
|
+
control: "boolean",
|
|
66
|
+
description: "Lets the picker select more than one file.",
|
|
67
|
+
},
|
|
60
68
|
onClick: { action: "onClick" },
|
|
69
|
+
onFileSelect: {
|
|
70
|
+
action: "onFileSelect",
|
|
71
|
+
description: "Turns the button into a file picker.",
|
|
72
|
+
},
|
|
61
73
|
},
|
|
62
74
|
parameters: {
|
|
63
75
|
docs: {
|
|
@@ -150,6 +162,16 @@ Round.args = {
|
|
|
150
162
|
children: "+",
|
|
151
163
|
}
|
|
152
164
|
|
|
165
|
+
export const FilePicker = {}
|
|
166
|
+
FilePicker.args = {
|
|
167
|
+
...Default.args,
|
|
168
|
+
children: "Change photo",
|
|
169
|
+
variant: "outline",
|
|
170
|
+
shape: "pill",
|
|
171
|
+
accept: "image/jpeg,image/png,image/webp",
|
|
172
|
+
onFileSelect: () => {},
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
export const CustomContent = {}
|
|
154
176
|
CustomContent.args = {
|
|
155
177
|
...Default.args,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { html } from "@inglorious/web"
|
|
6
|
+
import { html, nothing } from "@inglorious/web"
|
|
7
7
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
8
8
|
import { ref } from "@inglorious/web/directives/ref"
|
|
9
9
|
|
|
@@ -12,10 +12,14 @@ import { applyElementProps } from "../../shared/apply-element-props.js"
|
|
|
12
12
|
/**
|
|
13
13
|
* Renders a themed button for actions and form submission.
|
|
14
14
|
* Supports variants, sizes, shapes, pressed state, and full-width layout.
|
|
15
|
+
* With `onFileSelect`, the button also opens a file picker on click.
|
|
15
16
|
* @example
|
|
16
17
|
* // Stateless usage
|
|
17
18
|
* button.render({ children: 'Click me', variant: 'primary' })
|
|
18
19
|
*
|
|
20
|
+
* // File picker
|
|
21
|
+
* button.render({ children: 'Upload', accept: 'image/*', onFileSelect })
|
|
22
|
+
*
|
|
19
23
|
* @param {ButtonProps} props
|
|
20
24
|
* @returns {TemplateResult}
|
|
21
25
|
*/
|
|
@@ -34,7 +38,10 @@ export function render(props) {
|
|
|
34
38
|
ariaLabel = "",
|
|
35
39
|
isAriaPressed = false,
|
|
36
40
|
className = "",
|
|
41
|
+
accept,
|
|
42
|
+
isMultiple = false,
|
|
37
43
|
onClick,
|
|
44
|
+
onFileSelect,
|
|
38
45
|
...rest
|
|
39
46
|
} = props
|
|
40
47
|
|
|
@@ -57,6 +64,13 @@ export function render(props) {
|
|
|
57
64
|
...extraClasses,
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
const handleClick = onFileSelect
|
|
68
|
+
? (event) => {
|
|
69
|
+
onClick?.(event)
|
|
70
|
+
event.currentTarget.nextElementSibling.click()
|
|
71
|
+
}
|
|
72
|
+
: onClick
|
|
73
|
+
|
|
60
74
|
return html`
|
|
61
75
|
<button
|
|
62
76
|
type=${buttonType}
|
|
@@ -64,10 +78,29 @@ export function render(props) {
|
|
|
64
78
|
aria-pressed=${isAriaPressed}
|
|
65
79
|
class=${classMap(classes)}
|
|
66
80
|
?disabled=${isDisabled}
|
|
67
|
-
@click=${
|
|
81
|
+
@click=${handleClick}
|
|
68
82
|
${ref((element) => applyElementProps(element, rest))}
|
|
69
83
|
>
|
|
70
84
|
${children}
|
|
71
85
|
</button>
|
|
86
|
+
${
|
|
87
|
+
onFileSelect
|
|
88
|
+
? html`<input
|
|
89
|
+
type="file"
|
|
90
|
+
accept=${accept ?? ""}
|
|
91
|
+
?multiple=${isMultiple}
|
|
92
|
+
?disabled=${isDisabled}
|
|
93
|
+
hidden
|
|
94
|
+
@change=${(event) => handleFileChange(event, onFileSelect)}
|
|
95
|
+
/>`
|
|
96
|
+
: nothing
|
|
97
|
+
}
|
|
72
98
|
`
|
|
73
99
|
}
|
|
100
|
+
|
|
101
|
+
function handleFileChange(event, onFileSelect) {
|
|
102
|
+
const { files } = event.target
|
|
103
|
+
if (files?.length) onFileSelect(files)
|
|
104
|
+
// Clear the input so picking the same file again still fires `change`
|
|
105
|
+
event.target.value = ""
|
|
106
|
+
}
|
|
@@ -10,7 +10,14 @@ export default {
|
|
|
10
10
|
message: { control: "text" },
|
|
11
11
|
position: {
|
|
12
12
|
control: "select",
|
|
13
|
-
options: [
|
|
13
|
+
options: [
|
|
14
|
+
"bottom-left",
|
|
15
|
+
"bottom-center",
|
|
16
|
+
"bottom-right",
|
|
17
|
+
"top-left",
|
|
18
|
+
"top-center",
|
|
19
|
+
"top-right",
|
|
20
|
+
],
|
|
14
21
|
},
|
|
15
22
|
onClose: { action: "onClose" },
|
|
16
23
|
},
|
|
@@ -23,3 +30,20 @@ export const Default = {
|
|
|
23
30
|
position: "bottom-left",
|
|
24
31
|
},
|
|
25
32
|
}
|
|
33
|
+
|
|
34
|
+
// A phone app keeps a bar at the bottom: the snackbar rises above it.
|
|
35
|
+
export const AboveBottomNavigation = {
|
|
36
|
+
args: {
|
|
37
|
+
isOpen: true,
|
|
38
|
+
message: "New version available",
|
|
39
|
+
position: "bottom-center",
|
|
40
|
+
},
|
|
41
|
+
decorators: [
|
|
42
|
+
(story) => {
|
|
43
|
+
const container = document.createElement("div")
|
|
44
|
+
container.style.setProperty("--iw-snackbar-offset-bottom", "4.875rem")
|
|
45
|
+
container.append(story())
|
|
46
|
+
return container
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}
|
|
@@ -1,26 +1,47 @@
|
|
|
1
|
+
/* Fixed to the viewport, clear of the system bars. A bottom snackbar also
|
|
2
|
+
clears whatever bar the app keeps at the bottom: the app sets
|
|
3
|
+
--iw-snackbar-offset-bottom to that bar's height while it shows, and the
|
|
4
|
+
offset then stands in for the safe area, which the bar already covers.
|
|
5
|
+
Only the app knows when its bar is on screen, so nothing is guessed here. */
|
|
1
6
|
.iw-snackbar {
|
|
7
|
+
--iw-snackbar-gap: var(--iw-space-4);
|
|
2
8
|
position: fixed;
|
|
3
9
|
z-index: var(--iw-z-popover, 1060);
|
|
10
|
+
max-width: calc(100vw - 2 * var(--iw-snackbar-gap));
|
|
4
11
|
}
|
|
5
12
|
|
|
6
|
-
.iw-snackbar-bottom-left
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
.iw-snackbar-bottom-left,
|
|
14
|
+
.iw-snackbar-bottom-center,
|
|
15
|
+
.iw-snackbar-bottom-right {
|
|
16
|
+
bottom: calc(
|
|
17
|
+
var(--iw-snackbar-gap) +
|
|
18
|
+
var(--iw-snackbar-offset-bottom, var(--safe-area-bottom, 0px))
|
|
19
|
+
);
|
|
9
20
|
}
|
|
10
21
|
|
|
11
|
-
.iw-snackbar-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
.iw-snackbar-top-left,
|
|
23
|
+
.iw-snackbar-top-center,
|
|
24
|
+
.iw-snackbar-top-right {
|
|
25
|
+
top: calc(var(--iw-snackbar-gap) + var(--safe-area-top, 0px));
|
|
14
26
|
}
|
|
15
27
|
|
|
28
|
+
.iw-snackbar-bottom-left,
|
|
16
29
|
.iw-snackbar-top-left {
|
|
17
|
-
left: var(--iw-
|
|
18
|
-
top: var(--iw-space-4);
|
|
30
|
+
left: var(--iw-snackbar-gap);
|
|
19
31
|
}
|
|
20
32
|
|
|
33
|
+
.iw-snackbar-bottom-right,
|
|
21
34
|
.iw-snackbar-top-right {
|
|
22
|
-
right: var(--iw-
|
|
23
|
-
|
|
35
|
+
right: var(--iw-snackbar-gap);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Both insets and auto margins: the snackbar is as wide as its content, up
|
|
39
|
+
to the gutters, and centered within them. */
|
|
40
|
+
.iw-snackbar-bottom-center,
|
|
41
|
+
.iw-snackbar-top-center {
|
|
42
|
+
inset-inline: var(--iw-snackbar-gap);
|
|
43
|
+
width: fit-content;
|
|
44
|
+
margin-inline: auto;
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
.iw-snackbar-content {
|
|
@@ -22,7 +22,13 @@ export interface ButtonProps {
|
|
|
22
22
|
ariaLabel?: string
|
|
23
23
|
isAriaPressed?: boolean
|
|
24
24
|
className?: string
|
|
25
|
-
|
|
25
|
+
/** File types the picker offers, as in the native `accept` attribute */
|
|
26
|
+
accept?: string
|
|
27
|
+
/** Lets the picker select more than one file */
|
|
28
|
+
isMultiple?: boolean
|
|
29
|
+
onClick?: (event: MouseEvent) => void
|
|
30
|
+
/** Turns the button into a file picker; called with the chosen files */
|
|
31
|
+
onFileSelect?: (files: FileList) => void
|
|
26
32
|
[key: string]: unknown
|
|
27
33
|
}
|
|
28
34
|
|
|
@@ -6,7 +6,13 @@ export interface SnackbarProps {
|
|
|
6
6
|
isOpen?: boolean
|
|
7
7
|
message?: TemplateResult | string | number
|
|
8
8
|
action?: TemplateResult | string | number
|
|
9
|
-
position?:
|
|
9
|
+
position?:
|
|
10
|
+
| "bottom-left"
|
|
11
|
+
| "bottom-center"
|
|
12
|
+
| "bottom-right"
|
|
13
|
+
| "top-left"
|
|
14
|
+
| "top-center"
|
|
15
|
+
| "top-right"
|
|
10
16
|
className?: string
|
|
11
17
|
onClose?: () => void
|
|
12
18
|
[key: string]: unknown
|