@incursa/ui-kit 1.7.0 → 1.8.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/NOTICE +8 -0
- package/README.md +16 -0
- package/dist/icons/index.js +371 -0
- package/dist/icons/package.json +3 -0
- package/dist/inc-design-language.css +34 -1
- package/dist/inc-design-language.css.map +1 -1
- package/dist/inc-design-language.js +1558 -1462
- package/dist/inc-design-language.min.css +1 -1
- package/dist/inc-design-language.min.css.map +1 -1
- package/dist/mcp/components/cards.json +3 -3
- package/dist/mcp/components/metrics.json +3 -3
- package/dist/mcp/components/states.json +3 -3
- package/dist/mcp/examples/data-grid-advanced.json +2 -2
- package/dist/mcp/examples/demo.json +2 -2
- package/dist/mcp/examples/overlay-workflows.json +2 -2
- package/dist/mcp/examples/reference.json +2 -2
- package/dist/mcp/examples/states.json +2 -2
- package/dist/mcp/examples/web-components.json +2 -2
- package/dist/mcp/guides/latest.json +2 -2
- package/dist/mcp/guides/package-metadata.json +2 -2
- package/dist/mcp/guides/update.json +2 -2
- package/dist/mcp/install.json +1 -1
- package/dist/mcp/patterns/data-grid-advanced.json +2 -2
- package/dist/mcp/patterns/demo.json +2 -2
- package/dist/mcp/patterns/overlay-workflows.json +2 -2
- package/dist/mcp/patterns/reference.json +2 -2
- package/dist/mcp/patterns/states.json +2 -2
- package/dist/mcp/patterns/web-components.json +2 -2
- package/dist/mcp/resources.json +77 -74
- package/dist/mcp/search-index.json +21 -21
- package/dist/mcp/update.json +2 -2
- package/dist/mcp/worker.mjs +164 -281
- package/dist/mcp/worker.mjs.map +2 -2
- package/dist/web-components/README.md +4 -0
- package/dist/web-components/components/actions.js +149 -2
- package/dist/web-components/components/feedback.js +63 -12
- package/dist/web-components/index.js +501 -14
- package/package.json +10 -3
- package/src/icons/index.js +229 -0
- package/src/icons/package.json +3 -0
- package/src/inc-design-language.js +12 -11
- package/src/inc-design-language.scss +38 -1
- package/src/web-components/README.md +4 -0
- package/src/web-components/components/actions.js +149 -2
- package/src/web-components/components/feedback.js +63 -12
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import createLucideElement from "lucide/dist/esm/createElement.mjs";
|
|
2
|
+
import CircleCheck from "lucide/dist/esm/icons/circle-check.mjs";
|
|
3
|
+
import CircleHelp from "lucide/dist/esm/icons/circle-question-mark.mjs";
|
|
4
|
+
import CircleX from "lucide/dist/esm/icons/circle-x.mjs";
|
|
5
|
+
import Download from "lucide/dist/esm/icons/download.mjs";
|
|
6
|
+
import ExternalLink from "lucide/dist/esm/icons/external-link.mjs";
|
|
7
|
+
import FileText from "lucide/dist/esm/icons/file-text.mjs";
|
|
8
|
+
import FolderPlus from "lucide/dist/esm/icons/folder-plus.mjs";
|
|
9
|
+
import Info from "lucide/dist/esm/icons/info.mjs";
|
|
10
|
+
import Lock from "lucide/dist/esm/icons/lock.mjs";
|
|
11
|
+
import Pause from "lucide/dist/esm/icons/pause.mjs";
|
|
12
|
+
import Play from "lucide/dist/esm/icons/play.mjs";
|
|
13
|
+
import RefreshCw from "lucide/dist/esm/icons/refresh-cw.mjs";
|
|
14
|
+
import SearchX from "lucide/dist/esm/icons/search-x.mjs";
|
|
15
|
+
import Settings from "lucide/dist/esm/icons/settings.mjs";
|
|
16
|
+
import ShieldCheck from "lucide/dist/esm/icons/shield-check.mjs";
|
|
17
|
+
import TriangleAlert from "lucide/dist/esm/icons/triangle-alert.mjs";
|
|
18
|
+
import Upload from "lucide/dist/esm/icons/upload.mjs";
|
|
19
|
+
|
|
20
|
+
const ICON_NODES = Object.freeze({
|
|
21
|
+
info: Info,
|
|
22
|
+
help: CircleHelp,
|
|
23
|
+
success: CircleCheck,
|
|
24
|
+
warning: TriangleAlert,
|
|
25
|
+
error: CircleX,
|
|
26
|
+
upload: Upload,
|
|
27
|
+
document: FileText,
|
|
28
|
+
download: Download,
|
|
29
|
+
settings: Settings,
|
|
30
|
+
"external-link": ExternalLink,
|
|
31
|
+
empty: FolderPlus,
|
|
32
|
+
"no-results": SearchX,
|
|
33
|
+
loading: RefreshCw,
|
|
34
|
+
lock: Lock,
|
|
35
|
+
pause: Pause,
|
|
36
|
+
play: Play,
|
|
37
|
+
permission: ShieldCheck,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const ICON_NAMES = Object.freeze(Object.keys(ICON_NODES));
|
|
41
|
+
const DEFAULT_SIZE = 16;
|
|
42
|
+
|
|
43
|
+
function getNamespace() {
|
|
44
|
+
if (typeof globalThis === "undefined") {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const root = globalThis.IncWebComponents || (globalThis.IncWebComponents = {});
|
|
49
|
+
const icons = root.icons || (root.icons = {});
|
|
50
|
+
|
|
51
|
+
if (!icons.names) {
|
|
52
|
+
icons.names = ICON_NAMES;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!icons.defaultRenderer) {
|
|
56
|
+
icons.defaultRenderer = renderDefaultIcon;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!icons.render) {
|
|
60
|
+
icons.render = renderIncIcon;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!icons.setRenderer) {
|
|
64
|
+
icons.setRenderer = setIconRenderer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return icons;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeIconName(name) {
|
|
71
|
+
return String(name || "")
|
|
72
|
+
.trim()
|
|
73
|
+
.toLowerCase()
|
|
74
|
+
.replace(/[_\s]+/g, "-");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeSize(value) {
|
|
78
|
+
const parsed = Number.parseFloat(value);
|
|
79
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_SIZE;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildIconAttributes(name, options = {}) {
|
|
83
|
+
const size = normalizeSize(options.size);
|
|
84
|
+
const className = options.className || "inc-icon";
|
|
85
|
+
const attrs = {
|
|
86
|
+
width: size,
|
|
87
|
+
height: size,
|
|
88
|
+
"data-inc-icon": name,
|
|
89
|
+
class: className,
|
|
90
|
+
focusable: "false",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (options.decorative !== false) {
|
|
94
|
+
attrs["aria-hidden"] = "true";
|
|
95
|
+
} else {
|
|
96
|
+
attrs.role = "img";
|
|
97
|
+
attrs["aria-label"] = options.label || name;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return attrs;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function renderDefaultIcon(name, options = {}) {
|
|
104
|
+
const normalizedName = normalizeIconName(name);
|
|
105
|
+
const iconNode = ICON_NODES[normalizedName] || ICON_NODES.info;
|
|
106
|
+
|
|
107
|
+
if (typeof document === "undefined") {
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return createLucideElement(iconNode, buildIconAttributes(normalizedName, options));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function coerceIconResult(result) {
|
|
115
|
+
if (!result || typeof document === "undefined") {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (result instanceof Node) {
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof result === "string") {
|
|
124
|
+
const template = document.createElement("template");
|
|
125
|
+
template.innerHTML = result.trim();
|
|
126
|
+
return template.content.firstElementChild || null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function getIconRenderer() {
|
|
133
|
+
const namespace = getNamespace();
|
|
134
|
+
return typeof namespace?.renderer === "function"
|
|
135
|
+
? namespace.renderer
|
|
136
|
+
: renderDefaultIcon;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function setIconRenderer(renderer) {
|
|
140
|
+
const namespace = getNamespace();
|
|
141
|
+
if (!namespace) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (renderer == null) {
|
|
146
|
+
delete namespace.renderer;
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (typeof renderer !== "function") {
|
|
151
|
+
throw new TypeError("Inc icon renderer must be a function.");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
namespace.renderer = renderer;
|
|
155
|
+
return renderer;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderIncIcon(name, options = {}) {
|
|
159
|
+
const normalizedName = normalizeIconName(name) || "info";
|
|
160
|
+
const renderer = getIconRenderer();
|
|
161
|
+
const rendered = renderer(normalizedName, options);
|
|
162
|
+
const icon = coerceIconResult(rendered) || coerceIconResult(renderDefaultIcon(normalizedName, options));
|
|
163
|
+
|
|
164
|
+
if (icon instanceof Element && options.decorative !== false) {
|
|
165
|
+
icon.setAttribute("aria-hidden", "true");
|
|
166
|
+
icon.removeAttribute("aria-label");
|
|
167
|
+
icon.removeAttribute("role");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return icon;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function replaceIconContents(container, name, options = {}) {
|
|
174
|
+
if (!(container instanceof Element)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
container.replaceChildren();
|
|
179
|
+
const icon = renderIncIcon(name, options);
|
|
180
|
+
if (icon) {
|
|
181
|
+
icon.setAttribute("data-inc-generated-icon", "true");
|
|
182
|
+
icon.setAttribute("data-inc-icon-upgraded", "true");
|
|
183
|
+
container.append(icon);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return icon;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function upgradeIconPlaceholders(root = typeof document !== "undefined" ? document : null) {
|
|
190
|
+
if (!root || typeof root.querySelectorAll !== "function") {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const upgraded = [];
|
|
195
|
+
root.querySelectorAll("[data-inc-icon]").forEach((node) => {
|
|
196
|
+
if (!(node instanceof Element)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const name = node.getAttribute("data-inc-icon");
|
|
201
|
+
if (!name || node.hasAttribute("data-inc-icon-upgraded") || node.hasAttribute("data-inc-generated-icon")) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
replaceIconContents(node, name, {
|
|
206
|
+
className: node.getAttribute("data-inc-icon-class") || "inc-icon",
|
|
207
|
+
decorative: node.getAttribute("aria-hidden") !== "false",
|
|
208
|
+
label: node.getAttribute("aria-label") || undefined,
|
|
209
|
+
size: node.getAttribute("data-inc-icon-size") || undefined,
|
|
210
|
+
});
|
|
211
|
+
node.setAttribute("data-inc-icon-upgraded", "true");
|
|
212
|
+
upgraded.push(node);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return upgraded;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
getNamespace();
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
ICON_NAMES as incIconNames,
|
|
222
|
+
getIconRenderer,
|
|
223
|
+
normalizeIconName,
|
|
224
|
+
renderDefaultIcon,
|
|
225
|
+
renderIncIcon,
|
|
226
|
+
replaceIconContents,
|
|
227
|
+
setIconRenderer,
|
|
228
|
+
upgradeIconPlaceholders,
|
|
229
|
+
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
replaceIconContents,
|
|
3
|
+
upgradeIconPlaceholders,
|
|
4
|
+
} from "./icons/index.js";
|
|
5
|
+
|
|
1
6
|
(function () {
|
|
2
7
|
"use strict";
|
|
3
8
|
|
|
@@ -12,6 +17,7 @@
|
|
|
12
17
|
themeLabel: "[data-inc-theme-label]",
|
|
13
18
|
themeSwitcher: "[data-inc-theme-switcher], details.inc-theme-switcher",
|
|
14
19
|
nativeDialogOpen: "[data-inc-native-dialog-open]",
|
|
20
|
+
icon: "[data-inc-icon]",
|
|
15
21
|
autoRefresh: "[data-inc-auto-refresh]",
|
|
16
22
|
autoRefreshToggle: '[data-inc-action="auto-refresh-toggle"]',
|
|
17
23
|
fileExample: "[data-inc-file-example]",
|
|
@@ -1132,16 +1138,6 @@
|
|
|
1132
1138
|
return `${minutes}m ${seconds}s`;
|
|
1133
1139
|
}
|
|
1134
1140
|
|
|
1135
|
-
const AUTO_REFRESH_PAUSE_ICON = `
|
|
1136
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
1137
|
-
<path d="M4 3h3v10H4zM9 3h3v10H9z"></path>
|
|
1138
|
-
</svg>`.trim();
|
|
1139
|
-
|
|
1140
|
-
const AUTO_REFRESH_PLAY_ICON = `
|
|
1141
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
1142
|
-
<path d="M4 3.5v9l8-4.5-8-4.5z"></path>
|
|
1143
|
-
</svg>`.trim();
|
|
1144
|
-
|
|
1145
1141
|
function getAutoRefreshParts(root) {
|
|
1146
1142
|
const toggle = root.querySelector(".inc-auto-refresh__toggle");
|
|
1147
1143
|
|
|
@@ -1183,7 +1179,11 @@
|
|
|
1183
1179
|
}
|
|
1184
1180
|
|
|
1185
1181
|
if (parts.toggleIcon instanceof HTMLElement) {
|
|
1186
|
-
parts.toggleIcon
|
|
1182
|
+
replaceIconContents(parts.toggleIcon, isPaused ? "play" : "pause", {
|
|
1183
|
+
className: "inc-icon",
|
|
1184
|
+
decorative: true,
|
|
1185
|
+
size: 16,
|
|
1186
|
+
});
|
|
1187
1187
|
}
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
@@ -1848,6 +1848,7 @@
|
|
|
1848
1848
|
initializeMenus();
|
|
1849
1849
|
initializeCollapses();
|
|
1850
1850
|
initializeTabs();
|
|
1851
|
+
upgradeIconPlaceholders(document);
|
|
1851
1852
|
initializeFileExamples();
|
|
1852
1853
|
initializeAutoRefresh();
|
|
1853
1854
|
attachEventHandlers();
|
|
@@ -448,6 +448,12 @@
|
|
|
448
448
|
// Buttons, badges, alerts, and form primitives
|
|
449
449
|
// -----------------------------------------------------------------------------
|
|
450
450
|
|
|
451
|
+
.inc-icon {
|
|
452
|
+
display: block;
|
|
453
|
+
flex: 0 0 auto;
|
|
454
|
+
stroke: currentColor;
|
|
455
|
+
}
|
|
456
|
+
|
|
451
457
|
.inc-btn {
|
|
452
458
|
@extend .btn;
|
|
453
459
|
display: inline-flex;
|
|
@@ -457,6 +463,15 @@
|
|
|
457
463
|
vertical-align: middle;
|
|
458
464
|
transition: color 0.18s ease, background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
|
459
465
|
|
|
466
|
+
&__icon {
|
|
467
|
+
display: inline-flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: center;
|
|
470
|
+
width: 1rem;
|
|
471
|
+
height: 1rem;
|
|
472
|
+
flex: 0 0 auto;
|
|
473
|
+
}
|
|
474
|
+
|
|
460
475
|
&--primary {
|
|
461
476
|
@extend .btn-primary;
|
|
462
477
|
}
|
|
@@ -736,6 +751,17 @@
|
|
|
736
751
|
@extend .alert-dismissible;
|
|
737
752
|
}
|
|
738
753
|
|
|
754
|
+
&__icon {
|
|
755
|
+
display: inline-flex;
|
|
756
|
+
align-items: center;
|
|
757
|
+
justify-content: center;
|
|
758
|
+
width: 1.125rem;
|
|
759
|
+
height: 1.125rem;
|
|
760
|
+
margin-inline-end: 0.5rem;
|
|
761
|
+
vertical-align: -0.2em;
|
|
762
|
+
color: currentColor;
|
|
763
|
+
}
|
|
764
|
+
|
|
739
765
|
&__heading {
|
|
740
766
|
@extend .alert-heading;
|
|
741
767
|
}
|
|
@@ -1175,6 +1201,12 @@
|
|
|
1175
1201
|
display: inline-flex;
|
|
1176
1202
|
align-items: center;
|
|
1177
1203
|
justify-content: center;
|
|
1204
|
+
|
|
1205
|
+
svg {
|
|
1206
|
+
display: block;
|
|
1207
|
+
width: 2.125rem;
|
|
1208
|
+
height: 2.125rem;
|
|
1209
|
+
}
|
|
1178
1210
|
}
|
|
1179
1211
|
|
|
1180
1212
|
&__form {
|
|
@@ -3238,7 +3270,6 @@ dialog.inc-native-dialog.inc-native-dialog--drawer .inc-native-dialog__body {
|
|
|
3238
3270
|
display: block;
|
|
3239
3271
|
width: 1rem;
|
|
3240
3272
|
height: 1rem;
|
|
3241
|
-
fill: currentColor;
|
|
3242
3273
|
}
|
|
3243
3274
|
}
|
|
3244
3275
|
|
|
@@ -4644,6 +4675,12 @@ body.inc-offcanvas-open {
|
|
|
4644
4675
|
font-size: 1.25rem;
|
|
4645
4676
|
font-weight: 700;
|
|
4646
4677
|
flex: 0 0 auto;
|
|
4678
|
+
|
|
4679
|
+
svg {
|
|
4680
|
+
display: block;
|
|
4681
|
+
width: 1.375rem;
|
|
4682
|
+
height: 1.375rem;
|
|
4683
|
+
}
|
|
4647
4684
|
}
|
|
4648
4685
|
|
|
4649
4686
|
&__title {
|
|
@@ -8,9 +8,11 @@ The CSS-first [`inc-*`](../../reference.html) class surface remains the canonica
|
|
|
8
8
|
|
|
9
9
|
- Package entrypoint: `@incursa/ui-kit/web-components`
|
|
10
10
|
- Style entrypoint: `@incursa/ui-kit/web-components/style.css`
|
|
11
|
+
- Icon entrypoint: `@incursa/ui-kit/icons`
|
|
11
12
|
- Built output: `dist/web-components/`
|
|
12
13
|
- Package export: `./web-components` resolves to `dist/web-components/index.js`
|
|
13
14
|
- Package export: `./web-components/style.css` resolves to `dist/web-components/style.css`
|
|
15
|
+
- Package export: `./icons` resolves to `dist/icons/index.js`
|
|
14
16
|
- Module boundary: `src/web-components/package.json` sets this subtree to `type: module`
|
|
15
17
|
|
|
16
18
|
Load these entrypoints only when the consuming app wants the custom elements and their default look. CSS-only consumers should not pay for the runtime.
|
|
@@ -45,6 +47,8 @@ The runtime auto-defines the shipped elements on load. If a consumer needs expli
|
|
|
45
47
|
The `IncElement` base class, including reflected attribute/property wiring and slot helpers.
|
|
46
48
|
- [`registry.js`](registry.js)
|
|
47
49
|
Idempotent registration helpers and the `IncWebComponents.registry` namespace.
|
|
50
|
+
- [`../icons/index.js`](../icons/index.js)
|
|
51
|
+
Semantic Incursa icon names, default Lucide-backed rendering, and the global renderer override used by component fallbacks.
|
|
48
52
|
- [`components/dom-helpers.js`](components/dom-helpers.js)
|
|
49
53
|
Shared DOM helpers used by the action and collection modules.
|
|
50
54
|
- [`controllers/focus.js`](controllers/focus.js)
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeIconName,
|
|
3
|
+
replaceIconContents,
|
|
4
|
+
} from "../../icons/index.js";
|
|
1
5
|
import {
|
|
2
6
|
addClass,
|
|
3
7
|
ensureNode,
|
|
@@ -28,6 +32,14 @@ const ALERT_DEFAULT_ROLE_BY_TONE = new Map([
|
|
|
28
32
|
["info", "status"],
|
|
29
33
|
["secondary", "status"],
|
|
30
34
|
]);
|
|
35
|
+
const ALERT_ICON_BY_TONE = new Map([
|
|
36
|
+
["success", "success"],
|
|
37
|
+
["danger", "error"],
|
|
38
|
+
["warning", "warning"],
|
|
39
|
+
["info", "info"],
|
|
40
|
+
["secondary", "info"],
|
|
41
|
+
["primary", "info"],
|
|
42
|
+
]);
|
|
31
43
|
|
|
32
44
|
const HostElement = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
|
|
33
45
|
|
|
@@ -53,6 +65,29 @@ function emit(host, type, detail = {}, options = {}) {
|
|
|
53
65
|
}));
|
|
54
66
|
}
|
|
55
67
|
|
|
68
|
+
function getDirectIconSlot(host) {
|
|
69
|
+
return Array.from(host.children || []).find((node) => (
|
|
70
|
+
node instanceof HTMLElement
|
|
71
|
+
&& node.getAttribute("slot") === "icon"
|
|
72
|
+
)) || null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hasConsumerIcon(container) {
|
|
76
|
+
return Array.from(container.children || []).some((node) => (
|
|
77
|
+
node instanceof HTMLElement
|
|
78
|
+
&& !node.hasAttribute("data-inc-generated-icon")
|
|
79
|
+
));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderDecorativeIcon(container, name, options = {}) {
|
|
83
|
+
replaceIconContents(container, name, {
|
|
84
|
+
className: "inc-icon",
|
|
85
|
+
decorative: true,
|
|
86
|
+
size: options.size || 16,
|
|
87
|
+
});
|
|
88
|
+
container.hidden = false;
|
|
89
|
+
}
|
|
90
|
+
|
|
56
91
|
class IncElement extends HostElement {
|
|
57
92
|
emit(type, detail = {}, options = {}) {
|
|
58
93
|
return emit(this, type, detail, options);
|
|
@@ -61,7 +96,7 @@ class IncElement extends HostElement {
|
|
|
61
96
|
|
|
62
97
|
export class IncButtonElement extends IncElement {
|
|
63
98
|
static get observedAttributes() {
|
|
64
|
-
return ["tone", "variant", "size", "loading", "href", "type", "disabled", "label", "target", "rel", "download"];
|
|
99
|
+
return ["tone", "variant", "size", "loading", "href", "type", "disabled", "label", "target", "rel", "download", "icon"];
|
|
65
100
|
}
|
|
66
101
|
|
|
67
102
|
connectedCallback() {
|
|
@@ -166,6 +201,8 @@ export class IncButtonElement extends IncElement {
|
|
|
166
201
|
this.removeLoadingSpinner(control);
|
|
167
202
|
}
|
|
168
203
|
|
|
204
|
+
this.syncIcon(control);
|
|
205
|
+
|
|
169
206
|
const label = this.getAttribute("label");
|
|
170
207
|
if (label) {
|
|
171
208
|
control.setAttribute("aria-label", label);
|
|
@@ -220,6 +257,53 @@ export class IncButtonElement extends IncElement {
|
|
|
220
257
|
|
|
221
258
|
control.querySelectorAll(":scope > [data-inc-button-spinner]").forEach((node) => node.remove());
|
|
222
259
|
}
|
|
260
|
+
|
|
261
|
+
syncIcon(control) {
|
|
262
|
+
if (!(control instanceof HTMLElement)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const explicitIcon = normalizeIconName(this.getAttribute("icon"));
|
|
267
|
+
const inferredIcon = this.getAttribute("download") != null
|
|
268
|
+
? "download"
|
|
269
|
+
: this.getAttribute("target") === "_blank"
|
|
270
|
+
? "external-link"
|
|
271
|
+
: "";
|
|
272
|
+
const iconName = explicitIcon || inferredIcon;
|
|
273
|
+
let icon = control.querySelector(":scope > [data-inc-button-icon]");
|
|
274
|
+
const slotted = getDirectIconSlot(control);
|
|
275
|
+
|
|
276
|
+
if (!icon && (iconName || slotted)) {
|
|
277
|
+
icon = document.createElement("span");
|
|
278
|
+
icon.className = "inc-btn__icon";
|
|
279
|
+
icon.setAttribute("data-inc-button-icon", "true");
|
|
280
|
+
icon.setAttribute("aria-hidden", "true");
|
|
281
|
+
control.prepend(icon);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!(icon instanceof HTMLElement)) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (slotted) {
|
|
289
|
+
slotted.removeAttribute("slot");
|
|
290
|
+
icon.replaceChildren(slotted);
|
|
291
|
+
icon.hidden = false;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (hasConsumerIcon(icon)) {
|
|
296
|
+
icon.hidden = false;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (iconName && iconName !== "none") {
|
|
301
|
+
renderDecorativeIcon(icon, iconName, { size: 16 });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
icon.remove();
|
|
306
|
+
}
|
|
223
307
|
}
|
|
224
308
|
|
|
225
309
|
export class IncButtonGroupElement extends IncElement {
|
|
@@ -350,7 +434,7 @@ export class IncCloseButtonElement extends IncElement {
|
|
|
350
434
|
|
|
351
435
|
export class IncAlertElement extends IncElement {
|
|
352
436
|
static get observedAttributes() {
|
|
353
|
-
return ["tone", "variant", "dismissible", "dismiss-label", "timeout"];
|
|
437
|
+
return ["tone", "variant", "dismissible", "dismiss-label", "timeout", "icon"];
|
|
354
438
|
}
|
|
355
439
|
|
|
356
440
|
connectedCallback() {
|
|
@@ -398,6 +482,7 @@ export class IncAlertElement extends IncElement {
|
|
|
398
482
|
const tone = normalizeToken(this.getAttribute("tone") || this.getAttribute("variant")) || "info";
|
|
399
483
|
const resolvedTone = BADGE_TONES.has(tone) ? tone : "info";
|
|
400
484
|
this.classList.add(`inc-alert--${resolvedTone}`);
|
|
485
|
+
this.syncIcon(resolvedTone);
|
|
401
486
|
|
|
402
487
|
if (toBoolean(this.getAttribute("dismissible"))) {
|
|
403
488
|
this.classList.add("inc-alert--dismissible");
|
|
@@ -465,6 +550,44 @@ export class IncAlertElement extends IncElement {
|
|
|
465
550
|
this.querySelectorAll(":scope > .inc-alert__progress").forEach((node) => node.remove());
|
|
466
551
|
}
|
|
467
552
|
|
|
553
|
+
syncIcon(tone) {
|
|
554
|
+
const explicitIcon = normalizeIconName(this.getAttribute("icon"));
|
|
555
|
+
const iconName = explicitIcon || ALERT_ICON_BY_TONE.get(tone) || "info";
|
|
556
|
+
let icon = this.querySelector(":scope > .inc-alert__icon");
|
|
557
|
+
const slotted = getDirectIconSlot(this);
|
|
558
|
+
|
|
559
|
+
if (!icon && (iconName !== "none" || slotted)) {
|
|
560
|
+
icon = document.createElement("span");
|
|
561
|
+
icon.className = "inc-alert__icon";
|
|
562
|
+
icon.setAttribute("part", "icon");
|
|
563
|
+
icon.setAttribute("aria-hidden", "true");
|
|
564
|
+
this.prepend(icon);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (!(icon instanceof HTMLElement)) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (slotted) {
|
|
572
|
+
slotted.removeAttribute("slot");
|
|
573
|
+
icon.replaceChildren(slotted);
|
|
574
|
+
icon.hidden = false;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (hasConsumerIcon(icon)) {
|
|
579
|
+
icon.hidden = false;
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (iconName === "none") {
|
|
584
|
+
icon.remove();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
renderDecorativeIcon(icon, iconName, { size: 18 });
|
|
589
|
+
}
|
|
590
|
+
|
|
468
591
|
startDismissTimer(timeoutMs) {
|
|
469
592
|
const progress = this.ensureProgressBar();
|
|
470
593
|
this.stopDismissTimer();
|
|
@@ -514,11 +637,21 @@ export class IncAlertElement extends IncElement {
|
|
|
514
637
|
}
|
|
515
638
|
|
|
516
639
|
export class IncEmptyStateElement extends IncElement {
|
|
640
|
+
static get observedAttributes() {
|
|
641
|
+
return ["icon"];
|
|
642
|
+
}
|
|
643
|
+
|
|
517
644
|
connectedCallback() {
|
|
518
645
|
addClass(this, "inc-empty-state");
|
|
519
646
|
this.sync();
|
|
520
647
|
}
|
|
521
648
|
|
|
649
|
+
attributeChangedCallback() {
|
|
650
|
+
if (this.isConnected) {
|
|
651
|
+
this.sync();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
522
655
|
sync() {
|
|
523
656
|
addClass(this, "inc-empty-state");
|
|
524
657
|
this.setAttribute("part", "empty-state content icon body actions");
|
|
@@ -572,6 +705,20 @@ export class IncEmptyStateElement extends IncElement {
|
|
|
572
705
|
|
|
573
706
|
body.append(node);
|
|
574
707
|
});
|
|
708
|
+
|
|
709
|
+
if (hasConsumerIcon(icon)) {
|
|
710
|
+
icon.hidden = false;
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const iconName = normalizeIconName(this.getAttribute("icon")) || "empty";
|
|
715
|
+
if (iconName === "none") {
|
|
716
|
+
icon.replaceChildren();
|
|
717
|
+
icon.hidden = true;
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
renderDecorativeIcon(icon, iconName, { size: 34 });
|
|
575
722
|
}
|
|
576
723
|
}
|
|
577
724
|
|