@nvidia-elements/core 2.15.5 → 2.17.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/CHANGELOG.md +26 -0
- package/dist/accordion/accordion2.js +4 -4
- package/dist/alert/alert-group2.js +1 -1
- package/dist/alert/alert2.js +1 -1
- package/dist/avatar/avatar-group2.js +1 -1
- package/dist/avatar/avatar2.js +1 -1
- package/dist/badge/badge2.js +1 -1
- package/dist/breadcrumb/breadcrumb2.js +1 -1
- package/dist/bundle.d.ts +4 -0
- package/dist/bundles/index.d.ts +63 -0
- package/dist/bundles/index.js +73 -6
- package/dist/button/button2.js +1 -1
- package/dist/button-group/button-group2.js +1 -1
- package/dist/card/card2.js +4 -4
- package/dist/chat-message/chat-message2.js +1 -1
- package/dist/checkbox/checkbox-group2.js +1 -1
- package/dist/checkbox/checkbox2.js +1 -1
- package/dist/color/color2.js +1 -1
- package/dist/combobox/combobox2.js +1 -1
- package/dist/copy-button/copy-button2.js +1 -1
- package/dist/custom-elements-jsx.d.ts +49 -0
- package/dist/custom-elements-vue.d.ts +49 -0
- package/dist/custom-elements.json +370 -55
- package/dist/data.html.json +42 -0
- package/dist/date/date2.js +1 -1
- package/dist/datetime/datetime2.js +1 -1
- package/dist/dialog/dialog-footer2.js +1 -1
- package/dist/dialog/dialog-header2.js +1 -1
- package/dist/dialog/dialog2.js +1 -1
- package/dist/divider/divider2.js +1 -1
- package/dist/dot/dot2.js +1 -1
- package/dist/drag-handle/drag-handle2.js +1 -1
- package/dist/drawer/drawer-content2.js +1 -1
- package/dist/drawer/drawer-footer2.js +1 -1
- package/dist/drawer/drawer-header2.js +1 -1
- package/dist/drawer/drawer2.js +1 -1
- package/dist/dropdown/dropdown-footer2.js +1 -1
- package/dist/dropdown/dropdown-header2.js +1 -1
- package/dist/dropdown/dropdown2.js +1 -1
- package/dist/dropdown-group/dropdown-group.js +1 -1
- package/dist/dropzone/dropzone2.js +1 -1
- package/dist/file/file2.js +1 -1
- package/dist/format-bytes/format-bytes2.js +1 -1
- package/dist/format-datetime/format-datetime2.js +1 -1
- package/dist/format-number/format-number2.js +1 -1
- package/dist/format-relative-time/format-relative-time2.js +1 -1
- package/dist/format-truncate/format-truncate2.js +1 -1
- package/dist/forms/control/control2.js +1 -1
- package/dist/forms/control-group/control-group2.js +1 -1
- package/dist/forms/control-message/control-message2.js +1 -1
- package/dist/gauge/gauge2.js +1 -1
- package/dist/grid/cell/cell2.js +1 -1
- package/dist/grid/column/column2.js +1 -1
- package/dist/grid/footer/footer2.js +1 -1
- package/dist/grid/grid2.js +1 -1
- package/dist/grid/header/header2.js +1 -1
- package/dist/grid/placeholder/placeholder2.js +1 -1
- package/dist/grid/row/row2.js +1 -1
- package/dist/icon/icon2.js +2 -2
- package/dist/icon-button/icon-button.d.ts +5 -0
- package/dist/icon-button/icon-button2.js +3 -3
- package/dist/icon-button/icon-button2.js.map +1 -1
- package/dist/iframe/define.d.ts +6 -0
- package/dist/iframe/define.js +7 -0
- package/dist/iframe/define.js.map +1 -0
- package/dist/iframe/iframe.d.ts +28 -0
- package/dist/iframe/iframe.examples.js +6 -0
- package/dist/iframe/iframe.examples.js.map +1 -0
- package/dist/iframe/iframe.examples.json +59 -0
- package/dist/iframe/iframe.js +6 -0
- package/dist/iframe/iframe.js.map +1 -0
- package/dist/iframe/iframe2.js +183 -0
- package/dist/iframe/iframe2.js.map +1 -0
- package/dist/iframe/index.d.ts +1 -0
- package/dist/iframe/index.js +2 -0
- package/dist/index.js +1 -1
- package/dist/input/input-group2.js +1 -1
- package/dist/input/input2.js +1 -1
- package/dist/internal/controllers/keynav-grid.controller.js +93 -62
- package/dist/internal/controllers/keynav-grid.controller.js.map +1 -1
- package/dist/internal/services/global.service.js +1 -1
- package/dist/internal/utils/events.d.ts +6 -1
- package/dist/internal/utils/events.js +22 -4
- package/dist/internal/utils/events.js.map +1 -1
- package/dist/logo/logo2.js +1 -1
- package/dist/menu/menu-group2.js +1 -1
- package/dist/menu/menu-item2.js +1 -1
- package/dist/menu/menu2.js +1 -1
- package/dist/month/month2.js +1 -1
- package/dist/notification/notification-group2.js +1 -1
- package/dist/notification/notification2.js +1 -1
- package/dist/page/page-panel/page-panel-content2.js +1 -1
- package/dist/page/page-panel/page-panel-footer2.js +1 -1
- package/dist/page/page-panel/page-panel-header2.js +1 -1
- package/dist/page/page-panel/page-panel2.js +1 -1
- package/dist/page/page2.js +1 -1
- package/dist/page-header/page-header2.js +1 -1
- package/dist/page-loader/page-loader2.js +1 -1
- package/dist/pagination/pagination2.js +1 -1
- package/dist/panel/panel2.js +4 -4
- package/dist/password/password2.js +1 -1
- package/dist/preferences-input/preferences-input2.js +1 -1
- package/dist/progress-bar/progress-bar2.js +1 -1
- package/dist/progress-dot/define.d.ts +6 -0
- package/dist/progress-dot/define.js +7 -0
- package/dist/progress-dot/define.js.map +1 -0
- package/dist/progress-dot/index.d.ts +1 -0
- package/dist/progress-dot/index.js +2 -0
- package/dist/progress-dot/progress-dot.d.ts +33 -0
- package/dist/progress-dot/progress-dot.examples.js +6 -0
- package/dist/progress-dot/progress-dot.examples.js.map +1 -0
- package/dist/progress-dot/progress-dot.examples.json +64 -0
- package/dist/progress-dot/progress-dot.js +6 -0
- package/dist/progress-dot/progress-dot.js.map +1 -0
- package/dist/progress-dot/progress-dot2.js +40 -0
- package/dist/progress-dot/progress-dot2.js.map +1 -0
- package/dist/progress-ring/progress-ring2.js +1 -1
- package/dist/progressive-filter-chip/progressive-filter-chip2.js +1 -1
- package/dist/pulse/pulse2.js +1 -1
- package/dist/radio/radio-group2.js +1 -1
- package/dist/radio/radio2.js +1 -1
- package/dist/range/range2.js +1 -1
- package/dist/resize-handle/resize-handle2.js +1 -1
- package/dist/search/search2.js +1 -1
- package/dist/select/select2.js +1 -1
- package/dist/skeleton/skeleton2.js +1 -1
- package/dist/sort-button/sort-button2.js +1 -1
- package/dist/sparkline/sparkline2.js +1 -1
- package/dist/star-rating/star-rating2.js +1 -1
- package/dist/steps/steps2.js +2 -2
- package/dist/switch/switch-group2.js +1 -1
- package/dist/switch/switch2.js +1 -1
- package/dist/tabs/tabs-group2.js +1 -1
- package/dist/tabs/tabs2.js +2 -2
- package/dist/tag/tag2.js +1 -1
- package/dist/textarea/textarea2.js +1 -1
- package/dist/time/time2.js +1 -1
- package/dist/toast/toast2.js +1 -1
- package/dist/toggletip/toggletip-footer2.js +1 -1
- package/dist/toggletip/toggletip-header.js +1 -1
- package/dist/toggletip/toggletip-header.js.map +1 -1
- package/dist/toggletip/toggletip-header2.js +1 -1
- package/dist/toggletip/toggletip.examples.js.map +1 -1
- package/dist/toggletip/toggletip.examples.json +1 -1
- package/dist/toggletip/toggletip2.js +1 -1
- package/dist/toolbar/toolbar2.js +1 -1
- package/dist/tooltip/tooltip2.js +1 -1
- package/dist/tree/tree-node2.js +1 -1
- package/dist/tree/tree2.js +1 -1
- package/dist/week/week2.js +1 -1
- package/package.json +26 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"element": "nve-iframe",
|
|
3
|
+
"entrypoint": "@nvidia-elements/core/iframe/iframe.examples.json",
|
|
4
|
+
"items": [
|
|
5
|
+
{
|
|
6
|
+
"id": "iframe",
|
|
7
|
+
"name": "Default",
|
|
8
|
+
"template": "<nve-iframe aria-label=\"Elements iframe example\">\n <template slot=\"head\">\n <title>Elements iframe example</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/bundles/index.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/fonts/inter.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/styles/dist/bundles/index.css\" />\n <script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/core/dist/bundles/index.min.js\"></script>\n </template>\n <template>\n <nve-alert status=\"success\">isolated iframe content</nve-alert>\n </template>\n</nve-iframe>\n",
|
|
9
|
+
"summary": "Supplies the iframe with Elements themes, utilities, fonts, and component registrations through its head template. Use this structure because iframe documents do not inherit resources from the parent document.",
|
|
10
|
+
"description": "",
|
|
11
|
+
"composition": false,
|
|
12
|
+
"tags": []
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "iframe-dynamic-height",
|
|
16
|
+
"name": "DynamicHeight",
|
|
17
|
+
"template": "<nve-iframe aria-label=\"Dynamic height iframe example\" style=\"--border: 1px solid red\">\n <template slot=\"head\">\n <title>Dynamic iframe height</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/bundles/index.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/fonts/inter.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/styles/dist/bundles/index.css\" />\n <script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/core/dist/bundles/index.min.js\"></script>\n </template>\n <template>\n <nve-accordion behavior-expand>\n <nve-accordion-header>\n <h2 nve-text=\"heading xs medium\" slot=\"prefix\">Dynamic height in iframe</h2>\n </nve-accordion-header>\n <nve-accordion-content>\n The iframe expands when this content opens and contracts when it closes.\n </nve-accordion-content>\n </nve-accordion>\n </template>\n</nve-iframe>\n",
|
|
18
|
+
"summary": "Synchronizes the iframe height with expandable content so the host layout avoids empty space or internal scrollbars. Use for previews whose intrinsic height changes after interaction.",
|
|
19
|
+
"description": "",
|
|
20
|
+
"composition": false,
|
|
21
|
+
"tags": [
|
|
22
|
+
"test-case"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "iframe-fixed-size",
|
|
27
|
+
"name": "FixedSize",
|
|
28
|
+
"template": "<nve-iframe aria-label=\"Fixed-size iframe example\" style=\"--height: 256px; --width: 256px; --border: 1px solid red\">\n <template><p style=\"height: 128px; width: 128px; margin: 1px; outline: 1px solid yellow;\">override iframe size</p></template>\n</nve-iframe>\n",
|
|
29
|
+
"summary": "Overrides intrinsic iframe dimensions with `--width` and `--height` to create a stable preview viewport. Use when you need to inspect embedded content at a fixed size regardless of its rendered bounds.",
|
|
30
|
+
"description": "",
|
|
31
|
+
"composition": false,
|
|
32
|
+
"tags": [
|
|
33
|
+
"test-case"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "iframe-overflow-clip",
|
|
38
|
+
"name": "OverflowClip",
|
|
39
|
+
"template": "<nve-iframe aria-label=\"Overflow clipping iframe example\" style=\"--height: 150px; --border: 1px solid red\">\n <template slot=\"head\">\n <title>Clipped iframe popover</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/bundles/index.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/themes/dist/fonts/inter.css\" />\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/styles/dist/bundles/index.css\" />\n <script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@nvidia-elements/core/dist/bundles/index.min.js\"></script>\n </template>\n <template>\n <div nve-layout=\"pad:md\">\n <nve-dropdown id=\"dropdown\">Popovers <strong>are clipped</strong> by the iframe.</nve-dropdown>\n <nve-button popovertarget=\"dropdown\">Open popover</nve-button>\n </div>\n </template>\n</nve-iframe>\n",
|
|
40
|
+
"summary": "The iframe browsing-context boundary clips popovers at its viewport and prevents them from escaping. Keep overlays inside the frame, or render them outside the iframe when they must overlap surrounding content.",
|
|
41
|
+
"description": "",
|
|
42
|
+
"composition": true,
|
|
43
|
+
"tags": [
|
|
44
|
+
"test-case"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "iframe-dynamically-updated-content",
|
|
49
|
+
"name": "DynamicallyUpdatedContent",
|
|
50
|
+
"template": "<nve-iframe id=\"property-example\" aria-label=\"Dynamically updated iframe example\">\n <template>\n <p nve-text=\"body\">Initial iframe content.</p>\n </template>\n</nve-iframe>\n<script type=\"module\">\n document.querySelector(\"#property-example template\").innerHTML = '<p nve-text=\"body\">This template was dynamically updated.</p>';\n</script>\n",
|
|
51
|
+
"summary": "Regenerates the iframe document when its source template changes. Use for live previews or generated output that must stay synchronized with edits made in the parent document.",
|
|
52
|
+
"description": "",
|
|
53
|
+
"composition": false,
|
|
54
|
+
"tags": [
|
|
55
|
+
"test-case"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/iframe/iframe.css?inline
|
|
2
|
+
var e = ":host{--width: var(--_width, fit-content);--height: var(--_height, fit-content);--border: none;display:block;width:fit-content;height:fit-content;border:var(--border)}iframe{width:var(--width);height:var(--height);border:0}@supports (frame-sizing: content-height){:host{--height: var(--_height, 0px)}iframe{frame-sizing:content-height}}";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { e as default };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=iframe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iframe.js","names":[],"sources":["../../src/iframe/iframe.css?inline"],"sourcesContent":["/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */\n/* SPDX-License-Identifier: Apache-2.0 */\n\n:host {\n --width: var(--_width, fit-content);\n --height: var(--_height, fit-content);\n --border: none;\n display: block;\n width: fit-content;\n height: fit-content;\n border: var(--border);\n}\n\niframe {\n width: var(--width);\n height: var(--height);\n border: 0;\n}\n\n@supports (frame-sizing: content-height) {\n :host {\n /* Native sizing locks the initial viewport height as its minimum. */\n --height: var(--_height, 0px);\n }\n\n iframe {\n frame-sizing: content-height;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { useStyles as e } from "../internal/styles/index.js";
|
|
2
|
+
import t from "./iframe.js";
|
|
3
|
+
import { LitElement as n, html as r } from "lit";
|
|
4
|
+
//#region src/iframe/iframe.ts
|
|
5
|
+
function i(e) {
|
|
6
|
+
return typeof e == "object" && !!e && "resize" in e && e.resize === !0 && "documentId" in e && typeof e.documentId == "number" && "width" in e && typeof e.width == "number" && (!("height" in e) || typeof e.height == "number");
|
|
7
|
+
}
|
|
8
|
+
var a = (e) => `
|
|
9
|
+
<script type="module">
|
|
10
|
+
window.addEventListener('message', event => {
|
|
11
|
+
if (event.source !== window.parent || typeof event.data !== 'string') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let message;
|
|
16
|
+
try {
|
|
17
|
+
message = JSON.parse(event.data);
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof message !== 'object' || message === null || typeof message.theme !== 'string') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
document.documentElement.setAttribute('nve-theme', message.theme);
|
|
27
|
+
document.documentElement.style.colorScheme = message.theme;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
let nativeHeight = CSS.supports('frame-sizing', 'content-height') && typeof window.requestResize === 'function';
|
|
31
|
+
let previousWidth;
|
|
32
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.target === document.body) {
|
|
35
|
+
const width = entry.borderBoxSize[0].inlineSize;
|
|
36
|
+
if (nativeHeight) {
|
|
37
|
+
try {
|
|
38
|
+
window.requestResize();
|
|
39
|
+
} catch {
|
|
40
|
+
// Native sizing can reject the document even when the API exists.
|
|
41
|
+
nativeHeight = false;
|
|
42
|
+
}
|
|
43
|
+
if (nativeHeight && width === previousWidth) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
previousWidth = width;
|
|
48
|
+
const size = nativeHeight
|
|
49
|
+
? { resize: true, documentId: ${e}, width }
|
|
50
|
+
: { resize: true, documentId: ${e}, width, height: entry.borderBoxSize[0].blockSize };
|
|
51
|
+
window.parent.postMessage(JSON.stringify(size), '*');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
resizeObserver.observe(document.body);
|
|
56
|
+
<\/script>
|
|
57
|
+
`, o = class extends n {
|
|
58
|
+
static {
|
|
59
|
+
this.styles = e([t]);
|
|
60
|
+
}
|
|
61
|
+
static {
|
|
62
|
+
this.metadata = {
|
|
63
|
+
tag: "nve-iframe",
|
|
64
|
+
version: "2.17.0"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
static get observedAttributes() {
|
|
68
|
+
return [...super.observedAttributes, "aria-label"];
|
|
69
|
+
}
|
|
70
|
+
get #e() {
|
|
71
|
+
return this.querySelector("template:not([slot])");
|
|
72
|
+
}
|
|
73
|
+
get #t() {
|
|
74
|
+
return this.querySelector("template[slot=\"head\"]");
|
|
75
|
+
}
|
|
76
|
+
get #n() {
|
|
77
|
+
return this.shadowRoot?.querySelector("iframe") ?? null;
|
|
78
|
+
}
|
|
79
|
+
get #r() {
|
|
80
|
+
return this.closest("[nve-theme]");
|
|
81
|
+
}
|
|
82
|
+
#i = 0;
|
|
83
|
+
#a;
|
|
84
|
+
#o;
|
|
85
|
+
#s;
|
|
86
|
+
#c;
|
|
87
|
+
render() {
|
|
88
|
+
return r`<iframe internal-host title="${this.ariaLabel ?? ""}" sandbox="allow-scripts" @load="${this.#m}"></iframe>`;
|
|
89
|
+
}
|
|
90
|
+
connectedCallback() {
|
|
91
|
+
super.connectedCallback(), globalThis.window.addEventListener("message", this.#g), this.hasUpdated && (this.#u(), this.#d(), this.#f(), this.#p(), this.#l());
|
|
92
|
+
}
|
|
93
|
+
attributeChangedCallback(e, t, n) {
|
|
94
|
+
super.attributeChangedCallback(e, t, n), e === "aria-label" && t !== n && this.requestUpdate();
|
|
95
|
+
}
|
|
96
|
+
firstUpdated(e) {
|
|
97
|
+
super.firstUpdated(e), this.#u(), this.#d(), this.#f(), this.#p(), this.#l();
|
|
98
|
+
}
|
|
99
|
+
disconnectedCallback() {
|
|
100
|
+
super.disconnectedCallback(), this.#a?.disconnect(), this.#o?.disconnect(), this.#s?.disconnect(), this.#c?.disconnect(), this.#a = void 0, this.#o = void 0, this.#s = void 0, this.#c = void 0, globalThis.window.removeEventListener("message", this.#g);
|
|
101
|
+
}
|
|
102
|
+
#l() {
|
|
103
|
+
let e = this.#n;
|
|
104
|
+
if (e === null) return;
|
|
105
|
+
this.#i++, this.style.setProperty("--_height", "0px"), CSS.supports("frame-sizing", "content-height") && (e.style.height = getComputedStyle(e).height);
|
|
106
|
+
let t = this.#r?.getAttribute("nve-theme") ?? "";
|
|
107
|
+
e.srcdoc = `
|
|
108
|
+
<!DOCTYPE html>
|
|
109
|
+
<html nve-theme="${t}" style="color-scheme: ${t}">
|
|
110
|
+
<head>
|
|
111
|
+
<meta name="responsive-embedded-sizing" content="allow-origins=*">
|
|
112
|
+
<style>
|
|
113
|
+
html { box-sizing: border-box; }
|
|
114
|
+
*, *:before, *:after { box-sizing: inherit; }
|
|
115
|
+
html, body { margin: 0; overflow: hidden; }
|
|
116
|
+
html { width: 100vw; }
|
|
117
|
+
</style>
|
|
118
|
+
${this.#t?.innerHTML ?? ""}
|
|
119
|
+
</head>
|
|
120
|
+
<body nve-text="body">
|
|
121
|
+
${this.#e?.innerHTML ?? ""}
|
|
122
|
+
${a(this.#i)}
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
#u() {
|
|
128
|
+
this.#a?.disconnect();
|
|
129
|
+
let e = this.#e;
|
|
130
|
+
e ? (this.#a = new MutationObserver(() => this.#l()), this.#a.observe(e.content, {
|
|
131
|
+
childList: !0,
|
|
132
|
+
subtree: !0,
|
|
133
|
+
characterData: !0,
|
|
134
|
+
attributes: !0
|
|
135
|
+
})) : this.#a = void 0;
|
|
136
|
+
}
|
|
137
|
+
#d() {
|
|
138
|
+
this.#o?.disconnect();
|
|
139
|
+
let e = this.#t;
|
|
140
|
+
e ? (this.#o = new MutationObserver(() => this.#l()), this.#o.observe(e.content, {
|
|
141
|
+
childList: !0,
|
|
142
|
+
subtree: !0,
|
|
143
|
+
characterData: !0,
|
|
144
|
+
attributes: !0
|
|
145
|
+
})) : this.#o = void 0;
|
|
146
|
+
}
|
|
147
|
+
#f() {
|
|
148
|
+
this.#s || (this.#s = new MutationObserver(() => {
|
|
149
|
+
this.#u(), this.#d(), this.#l();
|
|
150
|
+
}), this.#s.observe(this, { childList: !0 }));
|
|
151
|
+
}
|
|
152
|
+
#p() {
|
|
153
|
+
let e = this.#r;
|
|
154
|
+
e && !this.#c && (this.#c = new MutationObserver(() => this.#h()), this.#c.observe(e, { attributeFilter: ["nve-theme"] }));
|
|
155
|
+
}
|
|
156
|
+
#m = () => {
|
|
157
|
+
let e = this.#n, t = this.#i;
|
|
158
|
+
requestAnimationFrame(() => {
|
|
159
|
+
this.isConnected && t === this.#i && e?.style.removeProperty("height");
|
|
160
|
+
}), this.#h();
|
|
161
|
+
};
|
|
162
|
+
#h = () => {
|
|
163
|
+
let e = this.#r?.getAttribute("nve-theme"), t = this.#n?.contentWindow;
|
|
164
|
+
if (e == null || t == null) return;
|
|
165
|
+
let n = { theme: e };
|
|
166
|
+
t.postMessage(JSON.stringify(n), "*");
|
|
167
|
+
};
|
|
168
|
+
#g = (e) => {
|
|
169
|
+
let t = this.#n;
|
|
170
|
+
if (t === null || e.source !== t.contentWindow || typeof e.data != "string") return;
|
|
171
|
+
let n;
|
|
172
|
+
try {
|
|
173
|
+
n = JSON.parse(e.data);
|
|
174
|
+
} catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
!i(n) || n.documentId !== this.#i || (this.style.setProperty("--_width", `${n.width}px`), this.style.setProperty("--_height", n.height === void 0 ? "auto" : `${n.height}px`));
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
//#endregion
|
|
181
|
+
export { o as Iframe };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=iframe2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iframe2.js","names":[],"sources":["../../src/iframe/iframe.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { LitElement, html, type PropertyValues } from 'lit';\nimport { useStyles } from '@nvidia-elements/core/internal';\nimport styles from './iframe.css?inline';\n\ntype ResizeMessage = {\n resize: true;\n documentId: number;\n width: number;\n height?: number;\n};\n\ntype ThemeMessage = {\n theme: string;\n};\n\nfunction isResizeMessage(value: unknown): value is ResizeMessage {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'resize' in value &&\n value.resize === true &&\n 'documentId' in value &&\n typeof value.documentId === 'number' &&\n 'width' in value &&\n typeof value.width === 'number' &&\n (!('height' in value) || typeof value.height === 'number')\n );\n}\n\nconst iframeScript = (documentId: number) => /* html */ `\n <script type=\"module\">\n window.addEventListener('message', event => {\n if (event.source !== window.parent || typeof event.data !== 'string') {\n return;\n }\n\n let message;\n try {\n message = JSON.parse(event.data);\n } catch {\n return;\n }\n\n if (typeof message !== 'object' || message === null || typeof message.theme !== 'string') {\n return;\n }\n\n document.documentElement.setAttribute('nve-theme', message.theme);\n document.documentElement.style.colorScheme = message.theme;\n });\n\n let nativeHeight = CSS.supports('frame-sizing', 'content-height') && typeof window.requestResize === 'function';\n let previousWidth;\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === document.body) {\n const width = entry.borderBoxSize[0].inlineSize;\n if (nativeHeight) {\n try {\n window.requestResize();\n } catch {\n // Native sizing can reject the document even when the API exists.\n nativeHeight = false;\n }\n if (nativeHeight && width === previousWidth) {\n continue;\n }\n }\n previousWidth = width;\n const size = nativeHeight\n ? { resize: true, documentId: ${documentId}, width }\n : { resize: true, documentId: ${documentId}, width, height: entry.borderBoxSize[0].blockSize };\n window.parent.postMessage(JSON.stringify(size), '*');\n }\n }\n });\n resizeObserver.observe(document.body);\n </script>\n`;\n\n/**\n * @element nve-iframe\n * @description An iframe component that syncs with the parent document's theme and template content. Use when needing to render dynamically generated or isolated content.\n * @since 0.0.0\n * @entrypoint \\@nvidia-elements/core/iframe\n * @documentation https://nvidia.github.io/elements/docs/elements/iframe/\n * @slot - A template containing the iframe body content.\n * @slot head - A template containing content inserted into the iframe head.\n * @cssprop --border\n * @cssprop --width\n * @cssprop --height\n * @beta\n */\nexport class Iframe extends LitElement {\n static styles = useStyles([styles]);\n\n static readonly metadata = {\n tag: 'nve-iframe',\n version: '0.0.0'\n };\n\n static get observedAttributes() {\n return [...super.observedAttributes, 'aria-label'];\n }\n\n get #template() {\n return this.querySelector<HTMLTemplateElement>('template:not([slot])');\n }\n\n get #headTemplate() {\n return this.querySelector<HTMLTemplateElement>('template[slot=\"head\"]');\n }\n\n get #iframe() {\n return this.shadowRoot?.querySelector<HTMLIFrameElement>('iframe') ?? null;\n }\n\n get #themeHost() {\n return this.closest<HTMLElement>('[nve-theme]');\n }\n\n #documentId = 0;\n #templateObserver?: MutationObserver;\n #headTemplateObserver?: MutationObserver;\n #lightDomObserver?: MutationObserver;\n #themeObserver?: MutationObserver;\n\n render() {\n return html`<iframe\n internal-host\n title=${this.ariaLabel ?? ''}\n sandbox=\"allow-scripts\"\n @load=${this.#handleLoad}\n ></iframe>`;\n }\n\n connectedCallback() {\n super.connectedCallback();\n globalThis.window.addEventListener('message', this.#handleResizeMessage);\n if (this.hasUpdated) {\n this.#setupTemplateUpdates();\n this.#setupTemplateHeadUpdates();\n this.#setupLightDomUpdates();\n this.#setupThemeUpdates();\n this.#updateSource();\n }\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n super.attributeChangedCallback(name, oldValue, newValue);\n if (name === 'aria-label' && oldValue !== newValue) {\n this.requestUpdate();\n }\n }\n\n firstUpdated(props: PropertyValues<this>) {\n super.firstUpdated(props);\n this.#setupTemplateUpdates();\n this.#setupTemplateHeadUpdates();\n this.#setupLightDomUpdates();\n this.#setupThemeUpdates();\n this.#updateSource();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.#templateObserver?.disconnect();\n this.#headTemplateObserver?.disconnect();\n this.#lightDomObserver?.disconnect();\n this.#themeObserver?.disconnect();\n this.#templateObserver = undefined;\n this.#headTemplateObserver = undefined;\n this.#lightDomObserver = undefined;\n this.#themeObserver = undefined;\n globalThis.window.removeEventListener('message', this.#handleResizeMessage);\n }\n\n // <head> and <body> can't be slotted https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template#usage_notes\n #updateSource() {\n const iframe = this.#iframe;\n if (iframe === null) {\n return;\n }\n\n // Start each document with the initial height rather than the previous resize result.\n this.#documentId++;\n this.style.setProperty('--_height', '0px');\n if (CSS.supports('frame-sizing', 'content-height')) {\n // Keep the initial viewport until the new document finishes loading.\n iframe.style.height = getComputedStyle(iframe).height;\n }\n const theme = this.#themeHost?.getAttribute('nve-theme') ?? '';\n iframe.srcdoc = `\n <!DOCTYPE html>\n <html nve-theme=\"${theme}\" style=\"color-scheme: ${theme}\">\n <head>\n <meta name=\"responsive-embedded-sizing\" content=\"allow-origins=*\">\n <style>\n html { box-sizing: border-box; }\n *, *:before, *:after { box-sizing: inherit; }\n html, body { margin: 0; overflow: hidden; }\n html { width: 100vw; }\n </style>\n ${this.#headTemplate?.innerHTML ?? ''}\n </head>\n <body nve-text=\"body\">\n ${this.#template?.innerHTML ?? ''}\n ${iframeScript(this.#documentId)}\n </body>\n </html>\n `;\n }\n\n #setupTemplateUpdates() {\n this.#templateObserver?.disconnect();\n const template = this.#template;\n if (template) {\n this.#templateObserver = new MutationObserver(() => this.#updateSource());\n this.#templateObserver.observe(template.content, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true\n });\n } else {\n this.#templateObserver = undefined;\n }\n }\n\n #setupTemplateHeadUpdates() {\n this.#headTemplateObserver?.disconnect();\n const headTemplate = this.#headTemplate;\n if (headTemplate) {\n this.#headTemplateObserver = new MutationObserver(() => this.#updateSource());\n this.#headTemplateObserver.observe(headTemplate.content, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true\n });\n } else {\n this.#headTemplateObserver = undefined;\n }\n }\n\n #setupLightDomUpdates() {\n if (!this.#lightDomObserver) {\n this.#lightDomObserver = new MutationObserver(() => {\n this.#setupTemplateUpdates();\n this.#setupTemplateHeadUpdates();\n this.#updateSource();\n });\n this.#lightDomObserver.observe(this, { childList: true });\n }\n }\n\n #setupThemeUpdates() {\n const themeHost = this.#themeHost;\n if (themeHost && !this.#themeObserver) {\n this.#themeObserver = new MutationObserver(() => this.#syncTheme());\n this.#themeObserver.observe(themeHost, { attributeFilter: ['nve-theme'] });\n }\n }\n\n #handleLoad = () => {\n const iframe = this.#iframe;\n const documentId = this.#documentId;\n requestAnimationFrame(() => {\n if (this.isConnected && documentId === this.#documentId) {\n iframe?.style.removeProperty('height');\n }\n });\n this.#syncTheme();\n };\n\n #syncTheme = () => {\n const theme = this.#themeHost?.getAttribute('nve-theme');\n const contentWindow = this.#iframe?.contentWindow;\n if (theme === null || theme === undefined || contentWindow === null || contentWindow === undefined) {\n return;\n }\n\n const message: ThemeMessage = { theme };\n // Sandboxed srcdoc documents have opaque origins, so authenticate messages by their window reference.\n contentWindow.postMessage(JSON.stringify(message), '*');\n };\n\n #handleResizeMessage = (event: MessageEvent<unknown>) => {\n const iframe = this.#iframe;\n if (iframe === null || event.source !== iframe.contentWindow || typeof event.data !== 'string') {\n return;\n }\n\n let message: unknown;\n try {\n message = JSON.parse(event.data);\n } catch {\n return;\n }\n\n if (!isResizeMessage(message) || message.documentId !== this.#documentId) {\n return;\n }\n\n this.style.setProperty('--_width', `${message.width}px`);\n this.style.setProperty('--_height', message.height === undefined ? 'auto' : `${message.height}px`);\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './iframe.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { I18nService as e } from "./internal/services/i18n.service.js";
|
|
|
2
2
|
import { getThemeTokens as t } from "./internal/utils/dom.js";
|
|
3
3
|
import { statusIcons as n } from "./internal/types/index.js";
|
|
4
4
|
//#region src/index.ts
|
|
5
|
-
var r = "2.
|
|
5
|
+
var r = "2.17.0", i = "@nvidia-elements";
|
|
6
6
|
//#endregion
|
|
7
7
|
export { e as I18nService, i as SCOPE, r as VERSION, t as getThemeTokens, n as statusIcons };
|
|
8
8
|
|
package/dist/input/input2.js
CHANGED
|
@@ -1,63 +1,95 @@
|
|
|
1
1
|
import { GlobalStateService as e } from "../services/global.service.js";
|
|
2
|
-
import {
|
|
3
|
-
import { LogService as
|
|
4
|
-
import { focusElement as
|
|
5
|
-
import { KeynavCode as
|
|
2
|
+
import { throttle as t } from "../utils/events.js";
|
|
3
|
+
import { LogService as n } from "../services/log.service.js";
|
|
4
|
+
import { focusElement as r, getActiveElement as i, isSimpleFocusable as a, setActiveKeyListItem as o } from "../utils/focus.js";
|
|
5
|
+
import { KeynavCode as s, getFlattenedDOMTree as c, getFlattenedFocusableItems as l, hasInvalidDOMGrid as u, isContextMenuClick as d, validKeyNavigationCode as f } from "../utils/dom.js";
|
|
6
6
|
//#region src/internal/controllers/keynav-grid.controller.ts
|
|
7
|
-
function
|
|
8
|
-
return (e) => e.addInitializer((e) => new
|
|
7
|
+
function p() {
|
|
8
|
+
return (e) => e.addInitializer((e) => new m(e));
|
|
9
9
|
}
|
|
10
|
-
var
|
|
11
|
-
#e
|
|
10
|
+
var m = class {
|
|
11
|
+
#e;
|
|
12
12
|
#t;
|
|
13
|
-
|
|
13
|
+
#n;
|
|
14
|
+
#r = /* @__PURE__ */ new WeakSet();
|
|
15
|
+
#i = t(() => this.#c(), 500, { trailing: !0 });
|
|
16
|
+
get #a() {
|
|
14
17
|
return {
|
|
15
18
|
grid: this.host,
|
|
16
19
|
...this.host.keynavGridConfig
|
|
17
20
|
};
|
|
18
21
|
}
|
|
19
|
-
get #
|
|
20
|
-
let e = Array.from(this.#
|
|
21
|
-
return this.#
|
|
22
|
+
get #o() {
|
|
23
|
+
let e = Array.from(this.#a.rows);
|
|
24
|
+
return this.#a.columnRow && e.unshift(this.#a.columnRow), e;
|
|
22
25
|
}
|
|
23
|
-
get #
|
|
24
|
-
|
|
26
|
+
get #s() {
|
|
27
|
+
let { columns: e, cells: t } = this.#a;
|
|
28
|
+
return [...Array.from(e ?? []), ...Array.from(t)];
|
|
25
29
|
}
|
|
26
30
|
constructor(e) {
|
|
27
31
|
this.host = e, this.host.addController(this);
|
|
28
32
|
}
|
|
29
33
|
async hostConnected() {
|
|
30
|
-
await this.host.updateComplete, this.host.isConnected && (this.#t = this.#
|
|
34
|
+
await this.host.updateComplete, this.host.isConnected && (this.#t = this.#a.grid, this.#c(), this.#t.addEventListener("keyup", this.#m), this.#t.addEventListener("keydown", this.#f), this.#t.addEventListener("mouseup", this.#d), this.#e?.disconnect(), this.#e = new MutationObserver(this.#l), this.#e.observe(this.#t, {
|
|
35
|
+
childList: !0,
|
|
36
|
+
subtree: !0,
|
|
37
|
+
attributes: !0,
|
|
38
|
+
attributeFilter: ["tabindex"]
|
|
39
|
+
}));
|
|
31
40
|
}
|
|
32
41
|
hostDisconnected() {
|
|
33
|
-
this.#e
|
|
42
|
+
this.#e?.disconnect(), this.#e = void 0, this.#i.cancel(), this.#t?.removeEventListener("keyup", this.#m), this.#t?.removeEventListener("keydown", this.#f), this.#t?.removeEventListener("mouseup", this.#d), this.#t = void 0, this.#n = void 0;
|
|
34
43
|
}
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
#c() {
|
|
45
|
+
let e = this.#s, t = e.find((e) => e.tabIndex === 0) ?? (this.#n && e.includes(this.#n) ? this.#n : e[0]);
|
|
46
|
+
this.#n = t, this.#r = new WeakSet(e);
|
|
47
|
+
for (let n of e) {
|
|
48
|
+
let e = n === t ? "0" : "-1";
|
|
49
|
+
n.getAttribute("tabindex") !== e && n.setAttribute("tabindex", e);
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
#l = (e) => {
|
|
53
|
+
e.some((e) => {
|
|
54
|
+
if (e.type === "attributes") {
|
|
55
|
+
let t = e.target;
|
|
56
|
+
return t instanceof HTMLElement && this.#r.has(t) && t.getAttribute("tabindex") !== (t === this.#n ? "0" : "-1");
|
|
57
|
+
}
|
|
58
|
+
return [...e.addedNodes, ...e.removedNodes].some((e) => e.nodeType === Node.ELEMENT_NODE);
|
|
59
|
+
}) && this.#i();
|
|
60
|
+
};
|
|
61
|
+
#u() {
|
|
62
|
+
this.#l(this.#e?.takeRecords() ?? []), this.#i.flush();
|
|
63
|
+
}
|
|
64
|
+
#d = (e) => {
|
|
65
|
+
if (!d(e)) {
|
|
66
|
+
this.#u();
|
|
67
|
+
let t = e.composedPath()[0], n = this.#s.find((e) => e === t) ? t : null;
|
|
68
|
+
n && this.#p(e, n);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
#f = (t) => {
|
|
72
|
+
if (!f(t)) return;
|
|
73
|
+
this.#u();
|
|
74
|
+
let r = Array.from(this.#o), o = Array.from(this.#s);
|
|
75
|
+
if (e.state.env !== "production" && u(r)) {
|
|
76
|
+
n.warn("Invalid grid structure, all rows must have the same number of cells");
|
|
45
77
|
return;
|
|
46
78
|
}
|
|
47
|
-
if (
|
|
48
|
-
let { x: e, y:
|
|
79
|
+
if (a(i())) {
|
|
80
|
+
let { x: e, y: n } = _(o, r, {
|
|
49
81
|
code: t.code,
|
|
50
82
|
ctrlKey: t.ctrlKey,
|
|
51
83
|
dir: this.host.dir
|
|
52
|
-
}),
|
|
53
|
-
this.#
|
|
84
|
+
}), i = Array.from(c(r[n])).filter((e) => !!o.find((t) => t === e))[e];
|
|
85
|
+
this.#p(t, i), t.preventDefault();
|
|
54
86
|
}
|
|
55
|
-
}
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
let n =
|
|
59
|
-
|
|
60
|
-
let
|
|
87
|
+
};
|
|
88
|
+
#p(e, t) {
|
|
89
|
+
this.#n = t, o(this.#s, t);
|
|
90
|
+
let n = l(t), i = n.filter((e) => a(e));
|
|
91
|
+
i.length === 1 && n.length === 1 ? r(i[0]) : r(t);
|
|
92
|
+
let s = {
|
|
61
93
|
code: e instanceof KeyboardEvent ? e.code : null,
|
|
62
94
|
shiftKey: e.shiftKey,
|
|
63
95
|
activeItem: t
|
|
@@ -65,23 +97,22 @@ var g = class {
|
|
|
65
97
|
t.dispatchEvent(new CustomEvent("nve-key-change", {
|
|
66
98
|
bubbles: !0,
|
|
67
99
|
composed: !0,
|
|
68
|
-
detail:
|
|
100
|
+
detail: s
|
|
69
101
|
}));
|
|
70
102
|
}
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#d = (e) => this.#a(e);
|
|
103
|
+
#m = (e) => {
|
|
104
|
+
if (e.code !== "Enter" && e.code !== "Escape") return;
|
|
105
|
+
this.#u();
|
|
106
|
+
let t = Array.from(this.#s).find((e) => e.tabIndex === 0);
|
|
107
|
+
e.code === "Escape" && t?.focus(), e.code === "Enter" && t === e.composedPath()[0] && l(t)[0]?.focus();
|
|
108
|
+
};
|
|
78
109
|
};
|
|
79
|
-
function
|
|
80
|
-
let n = t === "rtl" ?
|
|
81
|
-
return e ===
|
|
110
|
+
function h(e, t) {
|
|
111
|
+
let n = t === "rtl" ? s.ArrowRight : s.ArrowLeft, r = t === "rtl" ? s.ArrowLeft : s.ArrowRight;
|
|
112
|
+
return e === s.ArrowUp ? {
|
|
82
113
|
dx: 0,
|
|
83
114
|
dy: -1
|
|
84
|
-
} : e ===
|
|
115
|
+
} : e === s.ArrowDown ? {
|
|
85
116
|
dx: 0,
|
|
86
117
|
dy: 1
|
|
87
118
|
} : e === n ? {
|
|
@@ -90,42 +121,42 @@ function _(e, t) {
|
|
|
90
121
|
} : e === r ? {
|
|
91
122
|
dx: 1,
|
|
92
123
|
dy: 0
|
|
93
|
-
} : e ===
|
|
124
|
+
} : e === s.PageUp ? {
|
|
94
125
|
dx: 0,
|
|
95
126
|
dy: -4
|
|
96
|
-
} : e ===
|
|
127
|
+
} : e === s.PageDown ? {
|
|
97
128
|
dx: 0,
|
|
98
129
|
dy: 4
|
|
99
130
|
} : null;
|
|
100
131
|
}
|
|
101
|
-
function
|
|
102
|
-
let { x: r, y: i } = t, { columnCount: a, rowCount: o, ctrlKey:
|
|
103
|
-
return e ===
|
|
132
|
+
function g(e, t, n) {
|
|
133
|
+
let { x: r, y: i } = t, { columnCount: a, rowCount: o, ctrlKey: c } = n;
|
|
134
|
+
return e === s.End ? {
|
|
104
135
|
x: a,
|
|
105
|
-
y:
|
|
106
|
-
} : e ===
|
|
136
|
+
y: c ? o : i
|
|
137
|
+
} : e === s.Home ? {
|
|
107
138
|
x: 0,
|
|
108
|
-
y:
|
|
139
|
+
y: c ? 0 : i
|
|
109
140
|
} : {
|
|
110
141
|
x: r,
|
|
111
142
|
y: i
|
|
112
143
|
};
|
|
113
144
|
}
|
|
114
|
-
function
|
|
115
|
-
let r = e.find((e) => e.tabIndex === 0), i = t.find((e) =>
|
|
116
|
-
return d ? (
|
|
117
|
-
x:
|
|
118
|
-
y:
|
|
145
|
+
function _(e, t, n) {
|
|
146
|
+
let r = e.find((e) => e.tabIndex === 0), i = t.find((e) => c(e).find((e) => e === r)), a = Array.from(c(i)).filter((t) => !!e.find((e) => e === t)), o = t.length - 1, s = a.length - 1, l = a.indexOf(r), u = t.indexOf(i), d = h(n.code, n.dir);
|
|
147
|
+
return d ? (l = Math.max(0, Math.min(s, l + d.dx)), u = Math.max(0, Math.min(o, u + d.dy))) : {x: l, y: u} = g(n.code, {
|
|
148
|
+
x: l,
|
|
149
|
+
y: u
|
|
119
150
|
}, {
|
|
120
151
|
columnCount: s,
|
|
121
152
|
rowCount: o,
|
|
122
153
|
ctrlKey: n.ctrlKey
|
|
123
154
|
}), {
|
|
124
|
-
x:
|
|
125
|
-
y:
|
|
155
|
+
x: l,
|
|
156
|
+
y: u
|
|
126
157
|
};
|
|
127
158
|
}
|
|
128
159
|
//#endregion
|
|
129
|
-
export {
|
|
160
|
+
export { m as KeyNavigationGridController, _ as getNextKeyGridItem, p as keyNavigationGrid };
|
|
130
161
|
|
|
131
162
|
//# sourceMappingURL=keynav-grid.controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keynav-grid.controller.js","names":[],"sources":["../../../src/internal/controllers/keynav-grid.controller.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveController, ReactiveElement } from 'lit';\nimport type { LegacyDecoratorTarget } from '../types/index.js';\nimport { onChildListMutation, throttle } from '../utils/events.js';\nimport { GlobalStateService } from '../services/global.service.js';\nimport { LogService } from '../services/log.service.js';\nimport {\n validKeyNavigationCode,\n isContextMenuClick,\n getFlattenedDOMTree,\n getFlattenedFocusableItems,\n KeynavCode,\n hasInvalidDOMGrid\n} from '../utils/dom.js';\nimport {\n focusElement,\n getActiveElement,\n initializeKeyListItems,\n setActiveKeyListItem,\n isSimpleFocusable\n} from '../utils/focus.js';\n\nexport interface KeynavGridConfig {\n grid?: HTMLElement;\n columnRow?: HTMLElement;\n columns?: NodeListOf<HTMLElement> | HTMLElement[];\n rows: NodeListOf<HTMLElement> | HTMLElement[];\n cells: NodeListOf<HTMLElement> | HTMLElement[];\n}\n\nexport interface KeynavGridElement {\n keynavGridConfig: KeynavGridConfig;\n}\n\n/**\n * https://w3c.github.io/aria-practices/#gridNav_focus\n */\nexport function keyNavigationGrid<T extends ReactiveElement & KeynavGridElement>(): ClassDecorator {\n return (target: LegacyDecoratorTarget) =>\n target.addInitializer!((instance: T) => new KeyNavigationGridController(instance));\n}\n\nexport class KeyNavigationGridController<T extends ReactiveElement & KeynavGridElement> implements ReactiveController {\n #observers: MutationObserver[] = [];\n #grid?: HTMLElement;\n\n get #config() {\n return {\n grid: this.host,\n ...this.host.keynavGridConfig\n };\n }\n\n get #hostRows() {\n const rows = Array.from(this.#config.rows);\n\n if (this.#config.columnRow) {\n rows.unshift(this.#config.columnRow);\n }\n\n return rows;\n }\n\n get #hostCells() {\n return [...Array.from(this.#config.columns ?? []), ...Array.from(this.#config.cells)];\n }\n\n constructor(private host: T) {\n this.host.addController(this);\n }\n\n async hostConnected() {\n await this.host.updateComplete;\n if (!this.host.isConnected) return;\n\n this.#grid = this.#config.grid;\n initializeKeyListItems(this.#hostCells);\n this.#grid.addEventListener('keyup', this.#onKeyUp);\n this.#grid.addEventListener('keydown', this.#onKeyDown);\n this.#grid.addEventListener('mouseup', this.#onMouseUp);\n this.#observers.push(\n onChildListMutation(\n this.host,\n throttle(() => initializeKeyListItems(this.#hostCells), 500)\n )\n );\n }\n\n hostDisconnected() {\n this.#observers.forEach(o => o?.disconnect());\n this.#observers.length = 0;\n this.#grid?.removeEventListener('keyup', this.#onKeyUp);\n this.#grid?.removeEventListener('keydown', this.#onKeyDown);\n this.#grid?.removeEventListener('mouseup', this.#onMouseUp);\n this.#grid = undefined;\n }\n\n #clickCell(e: MouseEvent) {\n if (!isContextMenuClick(e)) {\n const focusedElement = e.composedPath()[0] as HTMLElement;\n const activeCell = this.#hostCells.find(i => i === focusedElement) ? focusedElement : null;\n if (activeCell) {\n this.#setActiveCell(e, activeCell as HTMLElement);\n }\n }\n }\n\n #keynavCell(e: KeyboardEvent) {\n const rows = Array.from(this.#hostRows);\n const cells = Array.from(this.#hostCells);\n\n if (GlobalStateService.state.env !== 'production' && hasInvalidDOMGrid(rows)) {\n LogService.warn('Invalid grid structure, all rows must have the same number of cells');\n return;\n }\n\n if (validKeyNavigationCode(e) && isSimpleFocusable(getActiveElement() as Element)) {\n const { x, y } = getNextKeyGridItem(cells, rows, {\n code: e.code,\n ctrlKey: e.ctrlKey,\n dir: this.host.dir\n });\n\n const nextCell = Array.from(getFlattenedDOMTree(rows[y]!)).filter(c => !!cells.find(i => i === c))[\n x\n ] as HTMLElement;\n this.#setActiveCell(e, nextCell);\n e.preventDefault();\n }\n }\n\n #setActiveCell(e: KeyboardEvent | MouseEvent, activeCell: HTMLElement) {\n setActiveKeyListItem(this.#hostCells, activeCell);\n\n const items = getFlattenedFocusableItems(activeCell);\n const simpleItems = items.filter(i => isSimpleFocusable(i));\n\n if (simpleItems.length === 1 && items.length === 1) {\n focusElement(simpleItems[0]!);\n } else {\n focusElement(activeCell);\n }\n\n const detail = { code: e instanceof KeyboardEvent ? e.code : null, shiftKey: e.shiftKey, activeItem: activeCell };\n activeCell.dispatchEvent(new CustomEvent('nve-key-change', { bubbles: true, composed: true, detail }));\n }\n\n #updateCellActivation(e: KeyboardEvent) {\n const activeCell = Array.from(this.#hostCells).find(i => i.tabIndex === 0) as HTMLElement;\n if (e.code === 'Escape') {\n activeCell?.focus();\n }\n\n if (e.code === 'Enter' && activeCell === e.composedPath()[0]) {\n getFlattenedFocusableItems(activeCell as HTMLElement)[0]?.focus();\n }\n }\n\n #onKeyUp = (e: KeyboardEvent) => this.#updateCellActivation(e);\n\n #onKeyDown = (e: KeyboardEvent) => this.#keynavCell(e);\n\n #onMouseUp = (e: MouseEvent) => this.#clickCell(e);\n}\n\nfunction getGridDelta(code: KeynavCode | string, dir: string): { dx: number; dy: number } | null {\n const start = dir === 'rtl' ? KeynavCode.ArrowRight : KeynavCode.ArrowLeft;\n const end = dir === 'rtl' ? KeynavCode.ArrowLeft : KeynavCode.ArrowRight;\n\n if (code === KeynavCode.ArrowUp) return { dx: 0, dy: -1 };\n if (code === KeynavCode.ArrowDown) return { dx: 0, dy: 1 };\n if (code === start) return { dx: -1, dy: 0 };\n if (code === end) return { dx: 1, dy: 0 };\n if (code === KeynavCode.PageUp) return { dx: 0, dy: -4 };\n if (code === KeynavCode.PageDown) return { dx: 0, dy: 4 };\n return null;\n}\n\nfunction applyGridHomeEnd(\n code: KeynavCode | string,\n position: { x: number; y: number },\n bounds: { columnCount: number; rowCount: number; ctrlKey: boolean }\n) {\n const { x, y } = position;\n const { columnCount, rowCount, ctrlKey } = bounds;\n if (code === KeynavCode.End) {\n return { x: columnCount, y: ctrlKey ? rowCount : y };\n }\n\n if (code === KeynavCode.Home) {\n return { x: 0, y: ctrlKey ? 0 : y };\n }\n\n return { x, y };\n}\n\nexport function getNextKeyGridItem(\n cells: HTMLElement[],\n rows: HTMLElement[],\n config: { code: KeynavCode | string; ctrlKey: boolean; dir: string }\n) {\n const currentCell = cells.find(i => i.tabIndex === 0) as HTMLElement;\n const currentRow = rows.find(r => getFlattenedDOMTree(r).find(c => c === currentCell)) as HTMLElement;\n const currentRowCells = Array.from(getFlattenedDOMTree(currentRow)).filter(c => !!cells.find(i => i === c));\n const rowCount = rows.length - 1;\n const columnCount = currentRowCells.length - 1;\n\n let x = currentRowCells.indexOf(currentCell);\n let y = rows.indexOf(currentRow);\n\n const delta = getGridDelta(config.code, config.dir);\n if (delta) {\n x = Math.max(0, Math.min(columnCount, x + delta.dx));\n y = Math.max(0, Math.min(rowCount, y + delta.dy));\n } else {\n ({ x, y } = applyGridHomeEnd(config.code, { x, y }, { columnCount, rowCount, ctrlKey: config.ctrlKey }));\n }\n\n return { x, y };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keynav-grid.controller.js","names":[],"sources":["../../../src/internal/controllers/keynav-grid.controller.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveController, ReactiveElement } from 'lit';\nimport type { LegacyDecoratorTarget } from '../types/index.js';\nimport { throttle } from '../utils/events.js';\nimport { GlobalStateService } from '../services/global.service.js';\nimport { LogService } from '../services/log.service.js';\nimport {\n validKeyNavigationCode,\n isContextMenuClick,\n getFlattenedDOMTree,\n getFlattenedFocusableItems,\n KeynavCode,\n hasInvalidDOMGrid\n} from '../utils/dom.js';\nimport { focusElement, getActiveElement, setActiveKeyListItem, isSimpleFocusable } from '../utils/focus.js';\n\nexport interface KeynavGridConfig {\n grid?: HTMLElement;\n columnRow?: HTMLElement;\n columns?: NodeListOf<HTMLElement> | HTMLElement[];\n rows: NodeListOf<HTMLElement> | HTMLElement[];\n cells: NodeListOf<HTMLElement> | HTMLElement[];\n}\n\nexport interface KeynavGridElement {\n keynavGridConfig: KeynavGridConfig;\n}\n\n/**\n * https://w3c.github.io/aria-practices/#gridNav_focus\n */\nexport function keyNavigationGrid<T extends ReactiveElement & KeynavGridElement>(): ClassDecorator {\n return (target: LegacyDecoratorTarget) =>\n target.addInitializer!((instance: T) => new KeyNavigationGridController(instance));\n}\n\nexport class KeyNavigationGridController<T extends ReactiveElement & KeynavGridElement> implements ReactiveController {\n #observer?: MutationObserver;\n #grid?: HTMLElement;\n #activeCell?: HTMLElement;\n #cells = new WeakSet<HTMLElement>();\n #updateCells = throttle(() => this.#initializeCells(), 500, { trailing: true });\n\n get #config() {\n return {\n grid: this.host,\n ...this.host.keynavGridConfig\n };\n }\n\n get #hostRows() {\n const rows = Array.from(this.#config.rows);\n\n if (this.#config.columnRow) {\n rows.unshift(this.#config.columnRow);\n }\n\n return rows;\n }\n\n get #hostCells() {\n const { columns, cells } = this.#config;\n return [...Array.from(columns ?? []), ...Array.from(cells)];\n }\n\n constructor(private host: T) {\n this.host.addController(this);\n }\n\n async hostConnected() {\n await this.host.updateComplete;\n if (!this.host.isConnected) return;\n\n this.#grid = this.#config.grid;\n this.#initializeCells();\n this.#grid.addEventListener('keyup', this.#onKeyUp);\n this.#grid.addEventListener('keydown', this.#onKeyDown);\n this.#grid.addEventListener('mouseup', this.#onMouseUp);\n this.#observer?.disconnect();\n this.#observer = new MutationObserver(this.#onMutations);\n this.#observer.observe(this.#grid, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['tabindex']\n });\n }\n\n hostDisconnected() {\n this.#observer?.disconnect();\n this.#observer = undefined;\n this.#updateCells.cancel();\n this.#grid?.removeEventListener('keyup', this.#onKeyUp);\n this.#grid?.removeEventListener('keydown', this.#onKeyDown);\n this.#grid?.removeEventListener('mouseup', this.#onMouseUp);\n this.#grid = undefined;\n this.#activeCell = undefined;\n }\n\n #initializeCells() {\n const cells = this.#hostCells;\n const activeCell =\n cells.find(cell => cell.tabIndex === 0) ??\n (this.#activeCell && cells.includes(this.#activeCell) ? this.#activeCell : cells[0]);\n this.#activeCell = activeCell;\n this.#cells = new WeakSet(cells);\n for (const cell of cells) {\n const tabIndex = cell === activeCell ? '0' : '-1';\n // Avoid observing our own unchanged writes after a DOM morph or key change.\n if (cell.getAttribute('tabindex') !== tabIndex) {\n cell.setAttribute('tabindex', tabIndex);\n }\n }\n }\n\n #onMutations = (mutations: MutationRecord[]) => {\n const changed = mutations.some(mutation => {\n if (mutation.type === 'attributes') {\n const cell = mutation.target;\n // Ignore nested controls and our own tabindex writes.\n return (\n cell instanceof HTMLElement &&\n this.#cells.has(cell) &&\n cell.getAttribute('tabindex') !== (cell === this.#activeCell ? '0' : '-1')\n );\n }\n return [...mutation.addedNodes, ...mutation.removedNodes].some(node => node.nodeType === Node.ELEMENT_NODE);\n });\n if (changed) this.#updateCells();\n };\n\n #flushCellUpdates() {\n // A key event can arrive before the observer delivers a synchronous DOM update.\n this.#onMutations(this.#observer?.takeRecords() ?? []);\n this.#updateCells.flush();\n }\n\n #onMouseUp = (e: MouseEvent) => {\n if (!isContextMenuClick(e)) {\n this.#flushCellUpdates();\n const focusedElement = e.composedPath()[0] as HTMLElement;\n const activeCell = this.#hostCells.find(i => i === focusedElement) ? focusedElement : null;\n if (activeCell) {\n this.#setActiveCell(e, activeCell);\n }\n }\n };\n\n #onKeyDown = (e: KeyboardEvent) => {\n if (!validKeyNavigationCode(e)) return;\n this.#flushCellUpdates();\n const rows = Array.from(this.#hostRows);\n const cells = Array.from(this.#hostCells);\n\n if (GlobalStateService.state.env !== 'production' && hasInvalidDOMGrid(rows)) {\n LogService.warn('Invalid grid structure, all rows must have the same number of cells');\n return;\n }\n\n if (isSimpleFocusable(getActiveElement() as Element)) {\n const { x, y } = getNextKeyGridItem(cells, rows, {\n code: e.code,\n ctrlKey: e.ctrlKey,\n dir: this.host.dir\n });\n\n const nextCell = Array.from(getFlattenedDOMTree(rows[y]!)).filter(c => !!cells.find(i => i === c))[\n x\n ] as HTMLElement;\n this.#setActiveCell(e, nextCell);\n e.preventDefault();\n }\n };\n\n #setActiveCell(e: KeyboardEvent | MouseEvent, activeCell: HTMLElement) {\n this.#activeCell = activeCell;\n setActiveKeyListItem(this.#hostCells, activeCell);\n\n const items = getFlattenedFocusableItems(activeCell);\n const simpleItems = items.filter(i => isSimpleFocusable(i));\n\n if (simpleItems.length === 1 && items.length === 1) {\n focusElement(simpleItems[0]!);\n } else {\n focusElement(activeCell);\n }\n\n const detail = { code: e instanceof KeyboardEvent ? e.code : null, shiftKey: e.shiftKey, activeItem: activeCell };\n activeCell.dispatchEvent(new CustomEvent('nve-key-change', { bubbles: true, composed: true, detail }));\n }\n\n #onKeyUp = (e: KeyboardEvent) => {\n if (e.code !== 'Enter' && e.code !== 'Escape') return;\n this.#flushCellUpdates();\n const activeCell = Array.from(this.#hostCells).find(i => i.tabIndex === 0) as HTMLElement;\n if (e.code === 'Escape') {\n activeCell?.focus();\n }\n\n if (e.code === 'Enter' && activeCell === e.composedPath()[0]) {\n getFlattenedFocusableItems(activeCell)[0]?.focus();\n }\n };\n}\n\nfunction getGridDelta(code: KeynavCode | string, dir: string): { dx: number; dy: number } | null {\n const start = dir === 'rtl' ? KeynavCode.ArrowRight : KeynavCode.ArrowLeft;\n const end = dir === 'rtl' ? KeynavCode.ArrowLeft : KeynavCode.ArrowRight;\n\n if (code === KeynavCode.ArrowUp) return { dx: 0, dy: -1 };\n if (code === KeynavCode.ArrowDown) return { dx: 0, dy: 1 };\n if (code === start) return { dx: -1, dy: 0 };\n if (code === end) return { dx: 1, dy: 0 };\n if (code === KeynavCode.PageUp) return { dx: 0, dy: -4 };\n if (code === KeynavCode.PageDown) return { dx: 0, dy: 4 };\n return null;\n}\n\nfunction applyGridHomeEnd(\n code: KeynavCode | string,\n position: { x: number; y: number },\n bounds: { columnCount: number; rowCount: number; ctrlKey: boolean }\n) {\n const { x, y } = position;\n const { columnCount, rowCount, ctrlKey } = bounds;\n if (code === KeynavCode.End) {\n return { x: columnCount, y: ctrlKey ? rowCount : y };\n }\n\n if (code === KeynavCode.Home) {\n return { x: 0, y: ctrlKey ? 0 : y };\n }\n\n return { x, y };\n}\n\nexport function getNextKeyGridItem(\n cells: HTMLElement[],\n rows: HTMLElement[],\n config: { code: KeynavCode | string; ctrlKey: boolean; dir: string }\n) {\n const currentCell = cells.find(i => i.tabIndex === 0) as HTMLElement;\n const currentRow = rows.find(r => getFlattenedDOMTree(r).find(c => c === currentCell)) as HTMLElement;\n const currentRowCells = Array.from(getFlattenedDOMTree(currentRow)).filter(c => !!cells.find(i => i === c));\n const rowCount = rows.length - 1;\n const columnCount = currentRowCells.length - 1;\n\n let x = currentRowCells.indexOf(currentCell);\n let y = rows.indexOf(currentRow);\n\n const delta = getGridDelta(config.code, config.dir);\n if (delta) {\n x = Math.max(0, Math.min(columnCount, x + delta.dx));\n y = Math.max(0, Math.min(rowCount, y + delta.dy));\n } else {\n ({ x, y } = applyGridHomeEnd(config.code, { x, y }, { columnCount, rowCount, ctrlKey: config.ctrlKey }));\n }\n\n return { x, y };\n}\n"],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@ var a = class {
|
|
|
19
19
|
i18nRegistry: {},
|
|
20
20
|
audit: {}
|
|
21
21
|
}
|
|
22
|
-
}, globalThis.NVE_ELEMENTS.state.versions = Array.from(/* @__PURE__ */ new Set([...globalThis.NVE_ELEMENTS.state.versions, "2.
|
|
22
|
+
}, globalThis.NVE_ELEMENTS.state.versions = Array.from(/* @__PURE__ */ new Set([...globalThis.NVE_ELEMENTS.state.versions, "2.17.0"])), globalThis.NVE_ELEMENTS.state.scopedRegistry ??= {}, globalThis.NVE_ELEMENTS.state.scopedRegistry["2.17.0"] = i(), globalThis.NVE_ELEMENTS.state.versions.length > 1 && globalThis.NVE_ELEMENTS.state.env !== "production" && console.warn(e(), `\n\n${JSON.stringify(globalThis.NVE_ELEMENTS.state.versions, null, 2)}`);
|
|
23
23
|
}
|
|
24
24
|
get state() {
|
|
25
25
|
return globalThis.NVE_ELEMENTS.state;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare function stopEvent(event: Event): void;
|
|
2
2
|
export declare function onChildListMutation(element: HTMLElement, fn: (mutation?: MutationRecord) => void, options?: MutationObserverInit): MutationObserver;
|
|
3
|
-
export declare function throttle(func: (...args:
|
|
3
|
+
export declare function throttle<Args extends unknown[]>(func: (...args: Args) => void, limit: number, { trailing }?: {
|
|
4
|
+
trailing?: boolean;
|
|
5
|
+
}): ((...args: Args) => void) & {
|
|
6
|
+
flush(): void;
|
|
7
|
+
cancel(): void;
|
|
8
|
+
};
|
|
4
9
|
export declare function debounce(func: (...args: unknown[]) => void, timeout?: number): (...args: unknown[]) => void;
|