@incursa/ui-kit 1.6.1 → 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 +144 -51
- package/dist/inc-design-language.css.map +1 -1
- package/dist/inc-design-language.js +1627 -1206
- package/dist/inc-design-language.min.css +1 -1
- package/dist/inc-design-language.min.css.map +1 -1
- package/dist/mcp/components/buttons.json +3 -3
- 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/components/status.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 +83 -80
- package/dist/mcp/search-index.json +25 -25
- package/dist/mcp/update.json +2 -2
- package/dist/mcp/worker.mjs +394 -391
- package/dist/mcp/worker.mjs.map +2 -2
- package/dist/web-components/README.md +4 -0
- package/dist/web-components/components/actions.js +237 -14
- package/dist/web-components/components/feedback.js +71 -7
- package/dist/web-components/index.js +583 -21
- 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 +327 -1
- package/src/inc-design-language.scss +178 -55
- package/src/web-components/README.md +4 -0
- package/src/web-components/components/actions.js +237 -14
- package/src/web-components/components/feedback.js +71 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incursa/ui-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Reusable UI kit for data-heavy business applications.",
|
|
6
6
|
"keywords": [
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"style": "./dist/web-components/style.css",
|
|
34
34
|
"default": "./dist/web-components/index.js"
|
|
35
35
|
},
|
|
36
|
+
"./icons": {
|
|
37
|
+
"default": "./dist/icons/index.js"
|
|
38
|
+
},
|
|
36
39
|
"./web-components/style.css": "./dist/web-components/style.css",
|
|
37
40
|
"./dist/inc-design-language.css": "./dist/inc-design-language.css",
|
|
38
41
|
"./dist/inc-design-language.min.css": "./dist/inc-design-language.min.css",
|
|
@@ -52,11 +55,12 @@
|
|
|
52
55
|
"scripts": {
|
|
53
56
|
"build:css": "sass --load-path=node_modules --quiet-deps --silence-deprecation=import src/inc-design-language.scss dist/inc-design-language.css --style=expanded",
|
|
54
57
|
"build:css:min": "sass --load-path=node_modules --quiet-deps --silence-deprecation=import src/inc-design-language.scss dist/inc-design-language.min.css --style=compressed",
|
|
55
|
-
"build:
|
|
58
|
+
"build:icons": "node scripts/build-icons.mjs",
|
|
59
|
+
"build:js": "esbuild src/inc-design-language.js --bundle --format=iife --platform=browser --outfile=dist/inc-design-language.js",
|
|
56
60
|
"build:wc": "node scripts/build-web-components.mjs",
|
|
57
61
|
"build:mcp:manifests": "node scripts/generate-mcp.mjs",
|
|
58
62
|
"build:mcp": "node scripts/build-mcp.mjs",
|
|
59
|
-
"build": "npm run build:css && npm run build:css:min && npm run build:js && npm run build:wc && npm run build:mcp",
|
|
63
|
+
"build": "npm run build:icons && npm run build:css && npm run build:css:min && npm run build:js && npm run build:wc && npm run build:mcp",
|
|
60
64
|
"test:browser:install": "playwright install chromium",
|
|
61
65
|
"test:browser": "playwright test",
|
|
62
66
|
"test:mcp": "node --test tests/mcp/transport.test.mjs tests/mcp/search.test.mjs tests/mcp/installation.test.mjs tests/mcp/markup.test.mjs tests/mcp/freshness.test.mjs",
|
|
@@ -70,6 +74,9 @@
|
|
|
70
74
|
"pack:tarball": "npm pack",
|
|
71
75
|
"package": "npm run build && npm run smoke && npm run pack:tarball"
|
|
72
76
|
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"lucide": "^1.17.0"
|
|
79
|
+
},
|
|
73
80
|
"devDependencies": {
|
|
74
81
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
75
82
|
"@playwright/test": "^1.58.2",
|
|
@@ -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,8 +17,15 @@
|
|
|
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"]',
|
|
23
|
+
fileExample: "[data-inc-file-example]",
|
|
24
|
+
fileDropzone: "[data-inc-file-dropzone]",
|
|
25
|
+
fileInput: "[data-inc-file-input]",
|
|
26
|
+
fileBrowse: "[data-inc-file-browse]",
|
|
27
|
+
fileList: "[data-inc-file-list]",
|
|
28
|
+
fileRemove: '[data-inc-action="file-remove"]',
|
|
17
29
|
modalToggle: '[data-inc-toggle="modal"]',
|
|
18
30
|
modalDismiss: '[data-inc-dismiss="modal"]',
|
|
19
31
|
offcanvasToggle: '[data-inc-toggle="offcanvas"]',
|
|
@@ -824,6 +836,298 @@
|
|
|
824
836
|
return parsed;
|
|
825
837
|
}
|
|
826
838
|
|
|
839
|
+
function formatFileExampleSize(bytes) {
|
|
840
|
+
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
841
|
+
return "0 B";
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (bytes < 1024) {
|
|
845
|
+
return `${bytes} B`;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
849
|
+
let value = bytes / 1024;
|
|
850
|
+
let unitIndex = 0;
|
|
851
|
+
|
|
852
|
+
while (value >= 1024 && unitIndex < (units.length - 1)) {
|
|
853
|
+
value /= 1024;
|
|
854
|
+
unitIndex += 1;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const precision = value >= 10 ? 0 : 1;
|
|
858
|
+
return `${value.toFixed(precision)} ${units[unitIndex]}`;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function getFileExampleTypeLabel(file) {
|
|
862
|
+
if (!(file instanceof File)) {
|
|
863
|
+
return "File";
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const extensionMatch = /\.([a-z0-9]+)$/i.exec(file.name || "");
|
|
867
|
+
if (extensionMatch?.[1]) {
|
|
868
|
+
return extensionMatch[1].toUpperCase();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (typeof file.type === "string" && file.type.includes("/")) {
|
|
872
|
+
return file.type.split("/").at(-1)?.toUpperCase() || "File";
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return "File";
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function createFileExampleRow(file) {
|
|
879
|
+
const row = document.createElement("div");
|
|
880
|
+
const meta = document.createElement("div");
|
|
881
|
+
const name = document.createElement("p");
|
|
882
|
+
const detail = document.createElement("p");
|
|
883
|
+
const badge = document.createElement("span");
|
|
884
|
+
const actions = document.createElement("div");
|
|
885
|
+
const preview = document.createElement("a");
|
|
886
|
+
const remove = document.createElement("button");
|
|
887
|
+
const objectUrl = URL.createObjectURL(file);
|
|
888
|
+
|
|
889
|
+
row.className = "inc-file-row";
|
|
890
|
+
row._incFileExampleObjectUrl = objectUrl;
|
|
891
|
+
|
|
892
|
+
meta.className = "inc-file-row__meta";
|
|
893
|
+
name.className = "inc-file-row__name";
|
|
894
|
+
name.textContent = file.name || "untitled-file";
|
|
895
|
+
detail.className = "inc-file-row__detail";
|
|
896
|
+
detail.textContent = `${getFileExampleTypeLabel(file)} • ${formatFileExampleSize(file.size)} • selected just now`;
|
|
897
|
+
meta.append(name, detail);
|
|
898
|
+
|
|
899
|
+
badge.className = "inc-badge inc-badge--secondary inc-badge--pill";
|
|
900
|
+
badge.textContent = "Ready";
|
|
901
|
+
|
|
902
|
+
actions.className = "inc-file-row__actions";
|
|
903
|
+
|
|
904
|
+
preview.className = "inc-btn inc-btn--outline-secondary inc-btn--sm";
|
|
905
|
+
preview.href = objectUrl;
|
|
906
|
+
preview.target = "_blank";
|
|
907
|
+
preview.rel = "noreferrer";
|
|
908
|
+
preview.textContent = "Preview";
|
|
909
|
+
|
|
910
|
+
remove.type = "button";
|
|
911
|
+
remove.className = "inc-btn inc-btn--secondary inc-btn--sm";
|
|
912
|
+
remove.textContent = "Remove";
|
|
913
|
+
remove.setAttribute("data-inc-action", "file-remove");
|
|
914
|
+
remove.setAttribute("aria-label", `Remove ${file.name}`);
|
|
915
|
+
|
|
916
|
+
actions.append(preview, remove);
|
|
917
|
+
row.append(meta, badge, actions);
|
|
918
|
+
return row;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function revokeFileExampleRow(row) {
|
|
922
|
+
if (!(row instanceof HTMLElement)) {
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (typeof row._incFileExampleObjectUrl === "string" && row._incFileExampleObjectUrl) {
|
|
927
|
+
URL.revokeObjectURL(row._incFileExampleObjectUrl);
|
|
928
|
+
row._incFileExampleObjectUrl = "";
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function updateFileExampleEmptyState(controller) {
|
|
933
|
+
const { list } = controller.parts;
|
|
934
|
+
if (!(list instanceof HTMLElement)) {
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const rows = list.querySelectorAll(".inc-file-row");
|
|
939
|
+
let empty = list.querySelector("[data-inc-file-empty]");
|
|
940
|
+
|
|
941
|
+
if (rows.length > 0) {
|
|
942
|
+
if (empty instanceof HTMLElement) {
|
|
943
|
+
empty.hidden = true;
|
|
944
|
+
}
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
if (!(empty instanceof HTMLElement)) {
|
|
949
|
+
empty = document.createElement("p");
|
|
950
|
+
empty.className = "inc-text inc-text--small inc-text--muted";
|
|
951
|
+
empty.setAttribute("data-inc-file-empty", "");
|
|
952
|
+
empty.textContent = controller.emptyText;
|
|
953
|
+
list.append(empty);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
empty.hidden = false;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
function appendFilesToExample(controller, files) {
|
|
960
|
+
const { list } = controller.parts;
|
|
961
|
+
if (!(list instanceof HTMLElement)) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const fileItems = Array.from(files || []).filter((file) => file instanceof File);
|
|
966
|
+
if (!fileItems.length) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
fileItems.forEach((file) => {
|
|
971
|
+
list.append(createFileExampleRow(file));
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
updateFileExampleEmptyState(controller);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function dataTransferIncludesFiles(dataTransfer) {
|
|
978
|
+
if (!dataTransfer) {
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (dataTransfer.files?.length) {
|
|
983
|
+
return true;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return Array.from(dataTransfer.types || []).includes("Files");
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function setFileDropzoneActiveState(controller, isActive) {
|
|
990
|
+
const { dropzone } = controller.parts;
|
|
991
|
+
if (!(dropzone instanceof HTMLElement)) {
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
dropzone.classList.toggle("is-drag-over", Boolean(isActive));
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function openFileExamplePicker(controller) {
|
|
999
|
+
const { input } = controller.parts;
|
|
1000
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
input.click();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function initializeFileExamples() {
|
|
1008
|
+
document.querySelectorAll(selectors.fileExample).forEach((root) => {
|
|
1009
|
+
if (!(root instanceof HTMLElement) || root._incFileExampleInitialized) {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const controller = {
|
|
1014
|
+
root,
|
|
1015
|
+
parts: {
|
|
1016
|
+
dropzone: root.querySelector(selectors.fileDropzone),
|
|
1017
|
+
input: root.querySelector(selectors.fileInput),
|
|
1018
|
+
browse: root.querySelector(selectors.fileBrowse),
|
|
1019
|
+
list: root.querySelector(selectors.fileList),
|
|
1020
|
+
},
|
|
1021
|
+
emptyText: root.getAttribute("data-inc-file-empty-text") || "No files selected yet.",
|
|
1022
|
+
dragDepth: 0,
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
const { dropzone, input, browse, list } = controller.parts;
|
|
1026
|
+
if (!(dropzone instanceof HTMLElement) || !(input instanceof HTMLInputElement) || !(list instanceof HTMLElement)) {
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
root._incFileExampleInitialized = true;
|
|
1031
|
+
root._incFileExampleController = controller;
|
|
1032
|
+
|
|
1033
|
+
dropzone.setAttribute("tabindex", dropzone.getAttribute("tabindex") || "0");
|
|
1034
|
+
dropzone.setAttribute("role", dropzone.getAttribute("role") || "button");
|
|
1035
|
+
dropzone.setAttribute("aria-label", dropzone.getAttribute("aria-label") || "Drop files here or browse for files");
|
|
1036
|
+
|
|
1037
|
+
const browseAction = (event) => {
|
|
1038
|
+
event.preventDefault();
|
|
1039
|
+
openFileExamplePicker(controller);
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
if (browse instanceof HTMLElement) {
|
|
1043
|
+
browse.addEventListener("click", browseAction);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
dropzone.addEventListener("click", (event) => {
|
|
1047
|
+
if (event.target instanceof Element && event.target.closest("a, button, input, label")) {
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
openFileExamplePicker(controller);
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
dropzone.addEventListener("keydown", (event) => {
|
|
1055
|
+
if (event.key !== "Enter" && event.key !== " ") {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
event.preventDefault();
|
|
1060
|
+
openFileExamplePicker(controller);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
dropzone.addEventListener("dragenter", (event) => {
|
|
1064
|
+
if (!dataTransferIncludesFiles(event.dataTransfer)) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
event.preventDefault();
|
|
1069
|
+
controller.dragDepth += 1;
|
|
1070
|
+
setFileDropzoneActiveState(controller, true);
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
dropzone.addEventListener("dragover", (event) => {
|
|
1074
|
+
if (!dataTransferIncludesFiles(event.dataTransfer)) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
event.preventDefault();
|
|
1079
|
+
if (event.dataTransfer) {
|
|
1080
|
+
event.dataTransfer.dropEffect = "copy";
|
|
1081
|
+
}
|
|
1082
|
+
setFileDropzoneActiveState(controller, true);
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
dropzone.addEventListener("dragleave", (event) => {
|
|
1086
|
+
if (!dataTransferIncludesFiles(event.dataTransfer)) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
event.preventDefault();
|
|
1091
|
+
controller.dragDepth = Math.max(0, controller.dragDepth - 1);
|
|
1092
|
+
|
|
1093
|
+
if (controller.dragDepth === 0 || !dropzone.contains(event.relatedTarget)) {
|
|
1094
|
+
setFileDropzoneActiveState(controller, false);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
dropzone.addEventListener("drop", (event) => {
|
|
1099
|
+
if (!dataTransferIncludesFiles(event.dataTransfer)) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
event.preventDefault();
|
|
1104
|
+
controller.dragDepth = 0;
|
|
1105
|
+
setFileDropzoneActiveState(controller, false);
|
|
1106
|
+
appendFilesToExample(controller, event.dataTransfer?.files);
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
input.addEventListener("change", () => {
|
|
1110
|
+
appendFilesToExample(controller, input.files);
|
|
1111
|
+
input.value = "";
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
list.addEventListener("click", (event) => {
|
|
1115
|
+
const removeButton = event.target.closest(selectors.fileRemove);
|
|
1116
|
+
if (!removeButton) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
event.preventDefault();
|
|
1121
|
+
const row = removeButton.closest(".inc-file-row");
|
|
1122
|
+
revokeFileExampleRow(row);
|
|
1123
|
+
row?.remove();
|
|
1124
|
+
updateFileExampleEmptyState(controller);
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
updateFileExampleEmptyState(controller);
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
827
1131
|
function formatAutoRefreshRemaining(totalSeconds) {
|
|
828
1132
|
if (totalSeconds < 60) {
|
|
829
1133
|
return `${totalSeconds}s`;
|
|
@@ -835,13 +1139,25 @@
|
|
|
835
1139
|
}
|
|
836
1140
|
|
|
837
1141
|
function getAutoRefreshParts(root) {
|
|
1142
|
+
const toggle = root.querySelector(".inc-auto-refresh__toggle");
|
|
1143
|
+
|
|
1144
|
+
if (toggle instanceof HTMLElement) {
|
|
1145
|
+
if (!toggle.querySelector(".inc-auto-refresh__toggle-icon")) {
|
|
1146
|
+
const icon = document.createElement("span");
|
|
1147
|
+
icon.className = "inc-auto-refresh__toggle-icon";
|
|
1148
|
+
icon.setAttribute("aria-hidden", "true");
|
|
1149
|
+
toggle.prepend(icon);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
838
1153
|
return {
|
|
839
1154
|
countdown: root.querySelector(".inc-auto-refresh__countdown"),
|
|
840
1155
|
label: root.querySelector(".inc-auto-refresh__label"),
|
|
841
1156
|
value: root.querySelector(".inc-auto-refresh__value"),
|
|
842
1157
|
status: root.querySelector(".inc-auto-refresh__status"),
|
|
843
1158
|
statusText: root.querySelector(".inc-auto-refresh__status-text"),
|
|
844
|
-
toggle
|
|
1159
|
+
toggle,
|
|
1160
|
+
toggleIcon: root.querySelector(".inc-auto-refresh__toggle-icon"),
|
|
845
1161
|
toggleText: root.querySelector(".inc-auto-refresh__toggle-text"),
|
|
846
1162
|
};
|
|
847
1163
|
}
|
|
@@ -861,6 +1177,14 @@
|
|
|
861
1177
|
if (parts.toggleText) {
|
|
862
1178
|
parts.toggleText.textContent = actionLabel;
|
|
863
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
if (parts.toggleIcon instanceof HTMLElement) {
|
|
1182
|
+
replaceIconContents(parts.toggleIcon, isPaused ? "play" : "pause", {
|
|
1183
|
+
className: "inc-icon",
|
|
1184
|
+
decorative: true,
|
|
1185
|
+
size: 16,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
864
1188
|
}
|
|
865
1189
|
|
|
866
1190
|
function renderAutoRefreshCountdown(controller, remainingSeconds) {
|
|
@@ -1524,6 +1848,8 @@
|
|
|
1524
1848
|
initializeMenus();
|
|
1525
1849
|
initializeCollapses();
|
|
1526
1850
|
initializeTabs();
|
|
1851
|
+
upgradeIconPlaceholders(document);
|
|
1852
|
+
initializeFileExamples();
|
|
1527
1853
|
initializeAutoRefresh();
|
|
1528
1854
|
attachEventHandlers();
|
|
1529
1855
|
}
|