@inglorious/ui 7.8.0 → 8.0.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/README.md +21 -4
- package/package.json +5 -9
- package/src/data-display/avatar/style.css +8 -3
- package/src/data-display/avatar/template.js +1 -1
- package/src/data-display/data-display.css +0 -1
- package/src/data-display/icon/icon-implementations.mdx +52 -149
- package/src/data-display/icon/icon.stories.js +48 -3
- package/src/data-display/icon/style.css +10 -3
- package/src/data-display/icon/template.js +50 -4
- package/src/examples/dashboard/app-drawer.js +16 -14
- package/src/examples/dashboard/app-header.js +26 -17
- package/src/examples/dashboard/data.js +36 -19
- package/src/examples/dashboard/primitive-content.js +35 -38
- package/src/examples/dashboard/social-card.js +2 -2
- package/src/examples/dashboard/stat-card.js +5 -4
- package/src/examples/dashboard/style.css +2 -2
- package/src/examples/dashboard/traffic-card.js +3 -2
- package/src/examples/dashboard/traffic-sales-card.js +5 -11
- package/src/index.js +0 -1
- package/src/navigation/bottom-navigation/style.css +6 -2
- package/src/navigation/drawer/template.js +4 -9
- package/src/shared/icons.js +15 -0
- package/types/data-display/icon.d.ts +24 -0
- package/types/index.d.ts +1 -5
- package/src/data-display/material-icon/index.js +0 -3
- package/src/data-display/material-icon/material-icon.stories.js +0 -100
- package/src/data-display/material-icon/style.css +0 -33
- package/src/data-display/material-icon/template.js +0 -39
- package/types/data-display/material-icon.d.ts +0 -21
package/README.md
CHANGED
|
@@ -44,6 +44,26 @@ The public CSS exports are:
|
|
|
44
44
|
- `@inglorious/ui/themes/bootstrap.css`
|
|
45
45
|
- `@inglorious/ui/all.css`
|
|
46
46
|
|
|
47
|
+
## Icons
|
|
48
|
+
|
|
49
|
+
`Icon` draws icons from any [Iconify](https://icon-sets.iconify.design/) set as inline SVG: no icon font to load, and only the icons you import end up in the bundle. Install a set and import each icon as its own module:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm add @iconify-icons/material-symbols
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
import { Icon } from "@inglorious/ui/icon"
|
|
57
|
+
import arrowBack from "@iconify-icons/material-symbols/arrow-back"
|
|
58
|
+
import settingsOutline from "@iconify-icons/material-symbols/settings-outline"
|
|
59
|
+
|
|
60
|
+
Icon.render({ icon: settingsOutline, size: "lg" })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The variant is part of the name: in Material Symbols the plain name is filled and `-outline` outlined, with `-rounded` and `-sharp` styles too. Font Awesome is `@iconify-icons/fa7-solid`, `fa7-regular` and `fa7-brands`; Lucide is `@iconify-icons/lucide`. Without `icon`, `Icon` shows its `children`: an emoji, a glyph, your own SVG.
|
|
64
|
+
|
|
65
|
+
An icon is decorative unless it has a `label`, which makes it an image with that name for assistive technology.
|
|
66
|
+
|
|
47
67
|
## Right-to-left
|
|
48
68
|
|
|
49
69
|
The components mirror in a right-to-left page (`<html dir="rtl">`, which `@inglorious/i18n` sets for Arabic, Hebrew and the like): their spacing, borders and corners use logical properties, and a `left` drawer sits at the start of the line.
|
|
@@ -59,10 +79,7 @@ For your own styles, use logical properties too (`margin-inline-start`, `inset-i
|
|
|
59
79
|
Icons that point along the line, such as a back arrow or a chevron, take the `direction` of the text they sit in and are mirrored with `"rtl"`. With `@inglorious/i18n`, that is the i18n entity's `dir`:
|
|
60
80
|
|
|
61
81
|
```javascript
|
|
62
|
-
|
|
63
|
-
name: "arrow_back",
|
|
64
|
-
direction: api.getEntity("i18n").dir,
|
|
65
|
-
})
|
|
82
|
+
Icon.render({ icon: arrowBack, direction: api.getEntity("i18n").dir })
|
|
66
83
|
```
|
|
67
84
|
|
|
68
85
|
## Safe-area utilities
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.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",
|
|
@@ -169,11 +169,6 @@
|
|
|
169
169
|
"import": "./src/data-display/list/index.js"
|
|
170
170
|
},
|
|
171
171
|
"./list.css": "./src/data-display/list/style.css",
|
|
172
|
-
"./material-icon": {
|
|
173
|
-
"types": "./types/data-display/material-icon.d.ts",
|
|
174
|
-
"import": "./src/data-display/material-icon/index.js"
|
|
175
|
-
},
|
|
176
|
-
"./material-icon.css": "./src/data-display/material-icon/style.css",
|
|
177
172
|
"./table": {
|
|
178
173
|
"types": "./types/data-display/table.d.ts",
|
|
179
174
|
"import": "./src/data-display/table/index.js"
|
|
@@ -312,8 +307,10 @@
|
|
|
312
307
|
"@inglorious/web": "6.6.0"
|
|
313
308
|
},
|
|
314
309
|
"devDependencies": {
|
|
315
|
-
"@
|
|
316
|
-
"@
|
|
310
|
+
"@iconify-icons/fa7-brands": "^2.0.0",
|
|
311
|
+
"@iconify-icons/fa7-regular": "^2.0.0",
|
|
312
|
+
"@iconify-icons/fa7-solid": "^2.0.0",
|
|
313
|
+
"@iconify-icons/material-symbols": "^2.0.6",
|
|
317
314
|
"@inglorious/eslint-config": "1.1.2",
|
|
318
315
|
"@inglorious/store": "11.0.3",
|
|
319
316
|
"@inglorious/vite-plugin-jsx": "2.0.3",
|
|
@@ -323,7 +320,6 @@
|
|
|
323
320
|
"@storybook/web-components-vite": "^10.6.0",
|
|
324
321
|
"happy-dom": "^20.14.0",
|
|
325
322
|
"jsdom": "^30.0.1",
|
|
326
|
-
"lucide": "^1.41.0",
|
|
327
323
|
"prettier": "^3.9.6",
|
|
328
324
|
"storybook": "^10.6.0",
|
|
329
325
|
"vite": "^8.2.2",
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
.iw-avatar {
|
|
2
2
|
display: inline-grid;
|
|
3
3
|
place-items: center;
|
|
4
|
-
width: 2rem;
|
|
5
|
-
height: 2rem;
|
|
6
4
|
flex: 0 0 auto;
|
|
7
5
|
border-radius: 999px;
|
|
8
6
|
background: var(--iw-color-bg-secondary);
|
|
9
7
|
color: var(--iw-color-text);
|
|
10
|
-
font-size: var(--iw-font-size-sm);
|
|
11
8
|
font-weight: var(--iw-font-weight-medium);
|
|
12
9
|
line-height: 1;
|
|
13
10
|
overflow: hidden;
|
|
@@ -21,11 +18,19 @@
|
|
|
21
18
|
&.iw-avatar-sm {
|
|
22
19
|
width: 1.5rem;
|
|
23
20
|
height: 1.5rem;
|
|
21
|
+
font-size: var(--iw-font-size-xs);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&.iw-avatar-md {
|
|
25
|
+
width: 2rem;
|
|
26
|
+
height: 2rem;
|
|
27
|
+
font-size: var(--iw-font-size-sm);
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
&.iw-avatar-lg {
|
|
27
31
|
width: 3rem;
|
|
28
32
|
height: 3rem;
|
|
33
|
+
font-size: var(--iw-font-size-base);
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
&.iw-avatar-square {
|
|
@@ -32,7 +32,7 @@ export function render(props) {
|
|
|
32
32
|
color === "auto" && !hasCustomBackground ? "default" : color
|
|
33
33
|
const classes = {
|
|
34
34
|
"iw-avatar": true,
|
|
35
|
-
[`iw-avatar-${size}`]:
|
|
35
|
+
[`iw-avatar-${size}`]: true,
|
|
36
36
|
[`iw-avatar-${shape}`]: shape !== "circle",
|
|
37
37
|
[`iw-avatar-color-${resolvedColor}`]:
|
|
38
38
|
resolvedColor !== "default" && !hasCustomBackground,
|
|
@@ -4,160 +4,63 @@ import { Meta } from "@storybook/addon-docs/blocks"
|
|
|
4
4
|
|
|
5
5
|
<div className="iw-theme-content">
|
|
6
6
|
|
|
7
|
-
# Icon Implementations
|
|
8
|
-
|
|
9
|
-
Use this page as a quick reference for supported icon strategies in `@inglorious/ui`.
|
|
10
|
-
|
|
11
7
|
<style>{`
|
|
12
|
-
.iw-icon-tabs { display: grid; gap: 0.75rem; }
|
|
13
|
-
.iw-icon-tabs > input { position: absolute; opacity: 0; pointer-events: none; }
|
|
14
|
-
.iw-icon-tab-labels { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
|
15
|
-
.iw-icon-tab-labels label {
|
|
16
|
-
border: 1px solid var(--iw-color-border);
|
|
17
|
-
border-radius: var(--iw-radius-md);
|
|
18
|
-
background: var(--iw-color-bg-secondary);
|
|
19
|
-
color: var(--iw-color-text);
|
|
20
|
-
padding: 0.35rem 0.65rem;
|
|
21
|
-
cursor: pointer;
|
|
22
|
-
font-size: 0.875rem;
|
|
23
|
-
}
|
|
24
|
-
.iw-icon-tab-panels {
|
|
25
|
-
border: 1px solid var(--iw-color-border);
|
|
26
|
-
border-radius: var(--iw-radius-md);
|
|
27
|
-
background: var(--iw-color-surface);
|
|
28
|
-
padding: 0.85rem;
|
|
29
|
-
}
|
|
30
|
-
.iw-icon-tab-panel { display: none; line-height: 1.45; }
|
|
31
|
-
#iw-icon-tab-generic:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-generic,
|
|
32
|
-
#iw-icon-tab-svg:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-svg,
|
|
33
|
-
#iw-icon-tab-material:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-material,
|
|
34
|
-
#iw-icon-tab-fa:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-fa,
|
|
35
|
-
#iw-icon-tab-lucide:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-lucide,
|
|
36
|
-
#iw-icon-tab-a11y:checked ~ .iw-icon-tab-panels .iw-icon-tab-panel-a11y { display: block; }
|
|
37
|
-
#iw-icon-tab-generic:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-generic"],
|
|
38
|
-
#iw-icon-tab-svg:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-svg"],
|
|
39
|
-
#iw-icon-tab-material:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-material"],
|
|
40
|
-
#iw-icon-tab-fa:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-fa"],
|
|
41
|
-
#iw-icon-tab-lucide:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-lucide"],
|
|
42
|
-
#iw-icon-tab-a11y:checked ~ .iw-icon-tab-labels label[for="iw-icon-tab-a11y"] {
|
|
43
|
-
border-color: var(--iw-color-primary);
|
|
44
|
-
box-shadow: inset 0 0 0 1px var(--iw-color-primary);
|
|
45
|
-
}
|
|
46
|
-
.iw-icon-example { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 1.5rem; margin-top: 0.35rem; }
|
|
47
|
-
.iw-material-symbol {
|
|
48
|
-
font-family: "Material Symbols Outlined";
|
|
49
|
-
font-weight: 400;
|
|
50
|
-
font-style: normal;
|
|
51
|
-
font-size: 1.5rem;
|
|
52
|
-
line-height: 1;
|
|
53
|
-
letter-spacing: normal;
|
|
54
|
-
text-transform: none;
|
|
55
|
-
display: inline-block;
|
|
56
|
-
white-space: nowrap;
|
|
57
|
-
word-wrap: normal;
|
|
58
|
-
direction: ltr;
|
|
59
|
-
font-feature-settings: "liga";
|
|
60
|
-
-webkit-font-smoothing: antialiased;
|
|
61
|
-
}
|
|
62
8
|
.iw-theme-content { background: var(--iw-color-bg, #ffffff); color: var(--iw-color-text, #000000); padding: 1rem; border-radius: var(--iw-radius-md); }
|
|
63
|
-
.iw-theme-content h1, .iw-theme-content p, .iw-theme-content li, .iw-theme-content pre, .iw-theme-content code {
|
|
9
|
+
.iw-theme-content h1, .iw-theme-content h2, .iw-theme-content p, .iw-theme-content li, .iw-theme-content pre, .iw-theme-content code { color: var(--iw-color-text, #000000); }
|
|
64
10
|
`}</style>
|
|
65
11
|
|
|
66
|
-
|
|
67
|
-
<input id="iw-icon-tab-generic" type="radio" name="iw-icon-tab" defaultChecked />
|
|
68
|
-
<input id="iw-icon-tab-svg" type="radio" name="iw-icon-tab" />
|
|
69
|
-
<input id="iw-icon-tab-material" type="radio" name="iw-icon-tab" />
|
|
70
|
-
<input id="iw-icon-tab-fa" type="radio" name="iw-icon-tab" />
|
|
71
|
-
<input id="iw-icon-tab-lucide" type="radio" name="iw-icon-tab" />
|
|
72
|
-
<input id="iw-icon-tab-a11y" type="radio" name="iw-icon-tab" />
|
|
73
|
-
|
|
74
|
-
<div className="iw-icon-tab-labels">
|
|
75
|
-
<label htmlFor="iw-icon-tab-generic">Generic</label>
|
|
76
|
-
<label htmlFor="iw-icon-tab-svg">Inline SVG</label>
|
|
77
|
-
<label htmlFor="iw-icon-tab-material">Material</label>
|
|
78
|
-
<label htmlFor="iw-icon-tab-fa">Font Awesome</label>
|
|
79
|
-
<label htmlFor="iw-icon-tab-lucide">Lucide</label>
|
|
80
|
-
<label htmlFor="iw-icon-tab-a11y">Accessibility</label>
|
|
81
|
-
</div>
|
|
12
|
+
# Icon Implementations
|
|
82
13
|
|
|
83
|
-
|
|
84
|
-
<section className="iw-icon-tab-panel iw-icon-tab-panel-generic">
|
|
85
|
-
<p>Use <code>@inglorious/ui/icon</code> for glyphs, emoji, and simple symbols. Reference: <a href="/?path=/story/data-display-icon--unicode">UTF-8 Icons</a></p>
|
|
86
|
-
<pre><code>{`Icon.render({ children: "★", size: "md", color: "primary" })`}</code></pre>
|
|
87
|
-
<div className="iw-icon-example">★ ⚑ ♫</div>
|
|
88
|
-
</section>
|
|
89
|
-
|
|
90
|
-
<section className="iw-icon-tab-panel iw-icon-tab-panel-svg">
|
|
91
|
-
<p>Use inline SVG when you want a custom icon pack without font dependencies.</p>
|
|
92
|
-
<pre><code>{`Icon.render({
|
|
93
|
-
children: html\`<svg viewBox="0 0 24 24" ...>...</svg>\`,
|
|
94
|
-
size: "md",
|
|
95
|
-
|
|
96
|
-
})`}</code></pre>
|
|
97
|
-
|
|
98
|
-
<div className="iw-icon-example">
|
|
99
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
100
|
-
<path d="M12 2v20M2 12h20" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
|
|
101
|
-
</svg>
|
|
102
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
103
|
-
<path d="M3 12l6 6 12-12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
104
|
-
</svg>
|
|
105
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
106
|
-
<path d="M12 21s-7-4.35-9-8.5C1.25 8.6 3.8 5 7.6 5c2.1 0 3.4 1.1 4.4 2.5C13 6.1 14.3 5 16.4 5 20.2 5 22.75 8.6 21 12.5 19 16.65 12 21 12 21Z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
107
|
-
</svg>
|
|
108
|
-
</div>
|
|
109
|
-
</section>
|
|
110
|
-
|
|
111
|
-
<section className="iw-icon-tab-panel iw-icon-tab-panel-material">
|
|
112
|
-
<p>Use <code>@inglorious/ui/material-icon</code> when Material Symbols fonts are loaded. Reference: <a href="https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Outlined" target="_blank">Material Symbols</a></p>
|
|
113
|
-
<pre><code>{`MaterialIcon.render({ name: "home", isFilled: true, size: "md" })`}</code></pre>
|
|
114
|
-
<div className="iw-icon-example">
|
|
115
|
-
<span className="iw-material-symbol" aria-hidden="true">home</span>
|
|
116
|
-
<span className="iw-material-symbol" aria-hidden="true">settings</span>
|
|
117
|
-
<span className="iw-material-symbol" aria-hidden="true">favorite</span>
|
|
118
|
-
</div>
|
|
119
|
-
</section>
|
|
120
|
-
|
|
121
|
-
<section className="iw-icon-tab-panel iw-icon-tab-panel-fa">
|
|
122
|
-
<p>Load Font Awesome CSS globally, then pass an icon element as <code>children</code>. Reference: <a href="https://fontawesome.com/search" target="_blank">Font Awesome Icons</a></p>
|
|
123
|
-
<pre><code>{`Icon.render({
|
|
124
|
-
children: html\`<i class="fa-solid fa-user" aria-hidden="true"></i>\`,
|
|
125
|
-
|
|
126
|
-
})`}</code></pre>
|
|
127
|
-
|
|
128
|
-
<div className="iw-icon-example">
|
|
129
|
-
<i className="fa-solid fa-user" aria-hidden="true"></i>
|
|
130
|
-
<i className="fa-solid fa-gear" aria-hidden="true"></i>
|
|
131
|
-
<i className="fa-solid fa-heart" aria-hidden="true"></i>
|
|
132
|
-
</div>
|
|
133
|
-
</section>
|
|
134
|
-
|
|
135
|
-
<section className="iw-icon-tab-panel iw-icon-tab-panel-lucide">
|
|
136
|
-
<p>Lucide requires runtime bootstrapping before icons render. Reference: <a href="https://lucide.dev/icons/?focus" target="_blank">Lucide Icons</a></p>
|
|
137
|
-
<pre><code>{`import { createIcons, icons } from "lucide"
|
|
138
|
-
|
|
139
|
-
createIcons({
|
|
140
|
-
icons,
|
|
141
|
-
attrs: { "stroke-width": 1.75 },
|
|
142
|
-
})`}</code></pre>
|
|
143
|
-
<p>Then render nodes such as <code>{`<i data-lucide="user"></i>`}</code>.</p>
|
|
144
|
-
|
|
145
|
-
<div className="iw-icon-example">
|
|
146
|
-
<i data-lucide="user" aria-hidden="true"></i>
|
|
147
|
-
<i data-lucide="settings" aria-hidden="true"></i>
|
|
148
|
-
<i data-lucide="heart" aria-hidden="true"></i>
|
|
149
|
-
</div>
|
|
150
|
-
</section>
|
|
14
|
+
`Icon` draws icons as inline SVG. No icon font is loaded, so nothing waits on a download and only the icons an app imports end up in its bundle.
|
|
151
15
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
16
|
+
## Icon sets
|
|
17
|
+
|
|
18
|
+
Any set published by [Iconify](https://icon-sets.iconify.design/) works in the same way: install the set, import each icon as its own module, pass it as `icon`.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @iconify-icons/material-symbols
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import { Icon } from "@inglorious/ui/icon"
|
|
26
|
+
import arrowBack from "@iconify-icons/material-symbols/arrow-back"
|
|
27
|
+
import settingsOutline from "@iconify-icons/material-symbols/settings-outline"
|
|
28
|
+
|
|
29
|
+
Icon.render({ icon: arrowBack, size: "lg" })
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The icon is one em square, so `size` sets it, and it takes the color of the text unless `color` says otherwise.
|
|
33
|
+
|
|
34
|
+
| Set | Package | Example imports |
|
|
35
|
+
| ------------------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
36
|
+
| [Material Symbols](https://fonts.google.com/icons) | `@iconify-icons/material-symbols` | `settings` (filled), `settings-outline`, `settings-rounded`, `settings-sharp` |
|
|
37
|
+
| [Font Awesome 7 Free](https://fontawesome.com/search?ic=free) | `@iconify-icons/fa7-solid`, `fa7-regular`, `fa7-brands` | `user`, `heart`, `instagram` |
|
|
38
|
+
| [Lucide](https://lucide.dev/icons/) | `@iconify-icons/lucide` | `user`, `settings`, `heart` |
|
|
39
|
+
|
|
40
|
+
The variant is part of the name, not a prop: in Material Symbols the plain name is the filled icon and `-outline` the outlined one. Font Awesome's icons are under CC BY 4.0, which asks for attribution.
|
|
41
|
+
|
|
42
|
+
## Glyphs and custom markup
|
|
43
|
+
|
|
44
|
+
Without `icon`, `children` is shown instead: an emoji, a Unicode glyph, or SVG of your own.
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
Icon.render({ children: "★", size: "lg" })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The Unicode glyph explorer is in the **Data Display/Icon/Unicode** story.
|
|
51
|
+
|
|
52
|
+
## Direction
|
|
53
|
+
|
|
54
|
+
An icon that points along the line, such as a back arrow or a chevron, should point the other way in right-to-left text. Pass the direction of the text it sits in; with `@inglorious/i18n`, that is the i18n entity's `dir`:
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
Icon.render({ icon: arrowBack, direction: api.getEntity("i18n").dir })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Accessibility
|
|
61
|
+
|
|
62
|
+
- An icon is decorative by default: it is hidden from assistive technology.
|
|
63
|
+
- An icon that means something on its own, with no text next to it, takes a `label`: it is then announced as an image with that name. On an icon-only button, label the button instead.
|
|
64
|
+
- Do not rely on an icon's shape alone for state; pair it with text or color contrast.
|
|
160
65
|
|
|
161
|
-
</div>
|
|
162
|
-
</div>
|
|
163
66
|
</div>
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import addressBookRegular from "@iconify-icons/fa7-regular/address-book"
|
|
2
|
+
import addressBookSolid from "@iconify-icons/fa7-solid/address-book"
|
|
3
|
+
import arrowBack from "@iconify-icons/material-symbols/arrow-back"
|
|
4
|
+
import settingsFilled from "@iconify-icons/material-symbols/settings"
|
|
5
|
+
import settingsOutline from "@iconify-icons/material-symbols/settings-outline"
|
|
6
|
+
|
|
1
7
|
import { createRender } from "../../stories/notifyStory.js"
|
|
2
8
|
import { Icon } from "."
|
|
3
9
|
|
|
@@ -6,7 +12,26 @@ export default {
|
|
|
6
12
|
tags: ["autodocs"],
|
|
7
13
|
render: createRender(Icon),
|
|
8
14
|
argTypes: {
|
|
9
|
-
|
|
15
|
+
icon: {
|
|
16
|
+
control: false,
|
|
17
|
+
description:
|
|
18
|
+
"An Iconify icon, imported from a set such as `@iconify-icons/material-symbols`.",
|
|
19
|
+
},
|
|
20
|
+
children: {
|
|
21
|
+
control: "text",
|
|
22
|
+
description: "Shown when there is no `icon`: a glyph or inline content.",
|
|
23
|
+
},
|
|
24
|
+
direction: {
|
|
25
|
+
control: "inline-radio",
|
|
26
|
+
options: ["ltr", "rtl"],
|
|
27
|
+
description:
|
|
28
|
+
"The direction of the surrounding text. An icon that points along the line is mirrored with `rtl`.",
|
|
29
|
+
},
|
|
30
|
+
label: {
|
|
31
|
+
control: "text",
|
|
32
|
+
description:
|
|
33
|
+
"Names an icon that means something on its own. Without it the icon is decorative.",
|
|
34
|
+
},
|
|
10
35
|
size: {
|
|
11
36
|
control: "select",
|
|
12
37
|
options: ["sm", "md", "lg"],
|
|
@@ -23,13 +48,33 @@ export default {
|
|
|
23
48
|
docs: {
|
|
24
49
|
description: {
|
|
25
50
|
component:
|
|
26
|
-
"
|
|
51
|
+
"Draws an icon from any Iconify set as inline SVG, or a glyph. See `Data Display/Icon/Implementations` for the icon sets.",
|
|
27
52
|
},
|
|
28
53
|
},
|
|
29
54
|
},
|
|
30
55
|
}
|
|
31
56
|
|
|
32
|
-
export const
|
|
57
|
+
export const FontAwesomeRegular = {
|
|
58
|
+
args: { icon: addressBookRegular, size: "lg", color: "current" },
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const FontAwesomeSolid = {
|
|
62
|
+
args: { icon: addressBookSolid, size: "lg", color: "current" },
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const MaterialOutline = {
|
|
66
|
+
args: { icon: settingsOutline, size: "lg", color: "current" },
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const MaterialFilled = {
|
|
70
|
+
args: { icon: settingsFilled, size: "lg", color: "current" },
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const RightToLeft = {
|
|
74
|
+
args: { icon: arrowBack, size: "lg", direction: "rtl" },
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const CustomIcon = {
|
|
33
78
|
args: {
|
|
34
79
|
children: "★",
|
|
35
80
|
size: "md",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* The size is a floor, not a fixed box. A
|
|
2
|
-
font-size and fits it exactly; content that sizes
|
|
3
|
-
|
|
1
|
+
/* The size is a floor, not a fixed box. A glyph or an `icon` takes the size's
|
|
2
|
+
font-size and fits it exactly, an SVG being drawn at 1em; content that sizes
|
|
3
|
+
itself (an SVG with its own width, a button) grows the box instead of
|
|
4
4
|
overflowing it or being squeezed into it. */
|
|
5
5
|
.iw-icon {
|
|
6
6
|
display: inline-flex;
|
|
@@ -25,4 +25,11 @@
|
|
|
25
25
|
min-height: var(--iw-space-6);
|
|
26
26
|
font-size: var(--iw-font-size-2xl);
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/* An icon that points along the line, in right-to-left text. `scale`
|
|
30
|
+
rather than `transform`, so it adds to a rotation set elsewhere instead
|
|
31
|
+
of replacing it. */
|
|
32
|
+
&.iw-icon-rtl {
|
|
33
|
+
scale: -1 1;
|
|
34
|
+
}
|
|
28
35
|
}
|
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('../../../types/data-display/icon').IconProps} IconProps
|
|
3
|
+
* @typedef {import('../../../types/data-display/icon').IconData} IconData
|
|
3
4
|
* @typedef {import('@inglorious/web').TemplateResult} TemplateResult
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { html } from "@inglorious/web"
|
|
7
8
|
import { classMap } from "@inglorious/web/directives/class-map"
|
|
9
|
+
import { ref } from "@inglorious/web/directives/ref"
|
|
10
|
+
import { unsafeSVG } from "@inglorious/web/directives/unsafe-svg"
|
|
11
|
+
|
|
12
|
+
import { applyElementProps } from "../../shared/applyElementProps.js"
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
|
-
* Renders an inline icon
|
|
11
|
-
*
|
|
15
|
+
* Renders an inline icon with size and color control.
|
|
16
|
+
*
|
|
17
|
+
* `icon` takes an icon in the Iconify format, the one every set on
|
|
18
|
+
* `@iconify-icons/*` exports: `import arrowBack from
|
|
19
|
+
* "@iconify-icons/material-symbols/arrow-back"`. It is drawn as inline SVG at
|
|
20
|
+
* 1em, so the size follows `size` and the color follows the text. Without
|
|
21
|
+
* `icon`, `children` is shown instead: an emoji, a glyph, any markup.
|
|
22
|
+
*
|
|
23
|
+
* `direction` is the direction of the text the icon sits in: an icon that
|
|
24
|
+
* points along the line, such as a back arrow, is mirrored with `"rtl"`.
|
|
25
|
+
* `label` names an icon that means something on its own; without one the
|
|
26
|
+
* icon is decorative and hidden from assistive technology.
|
|
27
|
+
*
|
|
12
28
|
* @param {IconProps} props
|
|
13
29
|
* @returns {TemplateResult}
|
|
14
30
|
*/
|
|
15
31
|
export function render({
|
|
32
|
+
icon,
|
|
16
33
|
children,
|
|
17
34
|
size = "md",
|
|
18
35
|
color = "current",
|
|
36
|
+
direction = "ltr",
|
|
37
|
+
label,
|
|
19
38
|
className = "",
|
|
20
39
|
onClick,
|
|
21
40
|
}) {
|
|
@@ -31,11 +50,38 @@ export function render({
|
|
|
31
50
|
"iw-icon": true,
|
|
32
51
|
[`iw-icon-${size}`]: true,
|
|
33
52
|
"iw-icon-current": color === "current",
|
|
53
|
+
"iw-icon-rtl": direction === "rtl",
|
|
34
54
|
...extraClasses,
|
|
35
55
|
})}
|
|
36
56
|
style=${color !== "current" ? `color: ${color};` : ""}
|
|
37
|
-
aria-hidden="true"
|
|
38
57
|
@click=${onClick}
|
|
39
|
-
|
|
58
|
+
${ref((el) =>
|
|
59
|
+
applyElementProps(el, {
|
|
60
|
+
role: label ? "img" : null,
|
|
61
|
+
"aria-label": label ?? null,
|
|
62
|
+
"aria-hidden": label ? null : "true",
|
|
63
|
+
}),
|
|
64
|
+
)}
|
|
65
|
+
>${icon ? renderSvg(icon) : children}</span
|
|
40
66
|
>`
|
|
41
67
|
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The icon as an SVG one em square. The body comes from the icon set, which is
|
|
71
|
+
* code the app chose to import, so it is inserted as markup. Iconify leaves out
|
|
72
|
+
* a 16 by 16 box at the origin, the format's default.
|
|
73
|
+
* @param {IconData} icon
|
|
74
|
+
* @returns {TemplateResult}
|
|
75
|
+
*/
|
|
76
|
+
function renderSvg({ body, width = 16, height = 16, left = 0, top = 0 }) {
|
|
77
|
+
return html`<svg
|
|
78
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
79
|
+
viewBox="${left} ${top} ${width} ${height}"
|
|
80
|
+
width="1em"
|
|
81
|
+
height="1em"
|
|
82
|
+
fill="currentColor"
|
|
83
|
+
focusable="false"
|
|
84
|
+
>
|
|
85
|
+
${unsafeSVG(body)}
|
|
86
|
+
</svg>`
|
|
87
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import buttonsAltOutline from "@iconify-icons/material-symbols/buttons-alt-outline"
|
|
2
|
+
import databaseOutline from "@iconify-icons/material-symbols/database-outline"
|
|
3
|
+
import exploreOutline from "@iconify-icons/material-symbols/explore-outline"
|
|
4
|
+
import gridViewOutline from "@iconify-icons/material-symbols/grid-view-outline"
|
|
5
|
+
import layersOutline from "@iconify-icons/material-symbols/layers-outline"
|
|
6
|
+
import notificationsOutline from "@iconify-icons/material-symbols/notifications-outline"
|
|
7
|
+
import speedOutline from "@iconify-icons/material-symbols/speed-outline"
|
|
1
8
|
import { html } from "@inglorious/web"
|
|
2
9
|
|
|
3
10
|
import { Avatar } from "../../data-display/avatar"
|
|
4
11
|
import { Badge } from "../../data-display/badge"
|
|
12
|
+
import { Icon } from "../../data-display/icon"
|
|
5
13
|
import { List } from "../../data-display/list"
|
|
6
|
-
import { MaterialIcon } from "../../data-display/material-icon"
|
|
7
14
|
import { Flex } from "../../layout/flex"
|
|
8
15
|
import { Drawer } from "../../navigation/drawer"
|
|
9
16
|
|
|
@@ -72,7 +79,7 @@ function mapItemsForRender(entity, items, api) {
|
|
|
72
79
|
const mappedItem = { ...item }
|
|
73
80
|
|
|
74
81
|
if (item.icon) {
|
|
75
|
-
mappedItem.icon =
|
|
82
|
+
mappedItem.icon = Icon.render({ icon: item.icon, size: "lg" })
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
if (item.badge) {
|
|
@@ -107,7 +114,7 @@ function getDefaultItems() {
|
|
|
107
114
|
{
|
|
108
115
|
id: "dashboard",
|
|
109
116
|
primary: "Dashboard",
|
|
110
|
-
icon:
|
|
117
|
+
icon: speedOutline,
|
|
111
118
|
href: "/",
|
|
112
119
|
badge: {
|
|
113
120
|
color: "info",
|
|
@@ -119,7 +126,7 @@ function getDefaultItems() {
|
|
|
119
126
|
{
|
|
120
127
|
id: "layout",
|
|
121
128
|
primary: "Layout",
|
|
122
|
-
icon:
|
|
129
|
+
icon: gridViewOutline,
|
|
123
130
|
children: [
|
|
124
131
|
{ id: "container", primary: "Container", href: "/layout/container" },
|
|
125
132
|
{ id: "flex", primary: "Flex", href: "/layout/flex" },
|
|
@@ -129,7 +136,7 @@ function getDefaultItems() {
|
|
|
129
136
|
{
|
|
130
137
|
id: "controls",
|
|
131
138
|
primary: "Controls",
|
|
132
|
-
icon:
|
|
139
|
+
icon: buttonsAltOutline,
|
|
133
140
|
children: [
|
|
134
141
|
{ id: "button", primary: "Button", href: "/controls/button" },
|
|
135
142
|
{
|
|
@@ -166,7 +173,7 @@ function getDefaultItems() {
|
|
|
166
173
|
{
|
|
167
174
|
id: "data-display",
|
|
168
175
|
primary: "Data Display",
|
|
169
|
-
icon:
|
|
176
|
+
icon: databaseOutline,
|
|
170
177
|
children: [
|
|
171
178
|
{ id: "avatar", primary: "Avatar", href: "/data-display/avatar" },
|
|
172
179
|
{ id: "badge", primary: "Badge", href: "/data-display/badge" },
|
|
@@ -185,11 +192,6 @@ function getDefaultItems() {
|
|
|
185
192
|
{ id: "divider", primary: "Divider", href: "/data-display/divider" },
|
|
186
193
|
{ id: "icon", primary: "Icon", href: "/data-display/icon" },
|
|
187
194
|
{ id: "list", primary: "List", href: "/data-display/list" },
|
|
188
|
-
{
|
|
189
|
-
id: "material-icon",
|
|
190
|
-
primary: "Material Icon",
|
|
191
|
-
href: "/data-display/material-icon",
|
|
192
|
-
},
|
|
193
195
|
{ id: "table", primary: "Table", href: "/data-display/table" },
|
|
194
196
|
{ id: "tooltip", primary: "Tooltip", href: "/data-display/tooltip" },
|
|
195
197
|
{
|
|
@@ -202,7 +204,7 @@ function getDefaultItems() {
|
|
|
202
204
|
{
|
|
203
205
|
id: "feedback",
|
|
204
206
|
primary: "Feedback",
|
|
205
|
-
icon:
|
|
207
|
+
icon: notificationsOutline,
|
|
206
208
|
children: [
|
|
207
209
|
{ id: "alert", primary: "Alert", href: "/feedback/alert" },
|
|
208
210
|
{ id: "backdrop", primary: "Backdrop", href: "/feedback/backdrop" },
|
|
@@ -215,7 +217,7 @@ function getDefaultItems() {
|
|
|
215
217
|
{
|
|
216
218
|
id: "navigation",
|
|
217
219
|
primary: "Navigation",
|
|
218
|
-
icon:
|
|
220
|
+
icon: exploreOutline,
|
|
219
221
|
children: [
|
|
220
222
|
{
|
|
221
223
|
id: "bottom-navigation",
|
|
@@ -247,7 +249,7 @@ function getDefaultItems() {
|
|
|
247
249
|
{
|
|
248
250
|
id: "surfaces",
|
|
249
251
|
primary: "Surfaces",
|
|
250
|
-
icon:
|
|
252
|
+
icon: layersOutline,
|
|
251
253
|
children: [
|
|
252
254
|
{ id: "accordion", primary: "Accordion", href: "/surfaces/accordion" },
|
|
253
255
|
{ id: "app-bar", primary: "App Bar", href: "/surfaces/app-bar" },
|