@selkit/dom 0.2.0 → 0.3.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/dist/index.cjs +33 -0
- package/dist/index.js +38 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -170,6 +170,8 @@ var SelkitDom = class {
|
|
|
170
170
|
#hiddenContainer = null;
|
|
171
171
|
#selectPrevDisplay = "";
|
|
172
172
|
#dragFrom = -1;
|
|
173
|
+
/** 上次已捲入可視的 activeIndex 僅在變動時才捲 避免跟使用者手動捲動打架 */
|
|
174
|
+
#lastActive = -1;
|
|
173
175
|
#positioner = null;
|
|
174
176
|
#unsubscribe;
|
|
175
177
|
#offClose;
|
|
@@ -442,6 +444,37 @@ var SelkitDom = class {
|
|
|
442
444
|
this.#syncForm();
|
|
443
445
|
this.element.classList.toggle(this.#cls("", "open"), s.isOpen);
|
|
444
446
|
this.element.classList.toggle(this.#cls("", "disabled"), s.disabled);
|
|
447
|
+
this.#scrollActiveIntoView(s);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* 鍵盤導航/開啟時讓作用中選項保持可見(aria-activedescendant 完整度)
|
|
451
|
+
* 僅在 activeIndex 變動時動作 手動捲動觸發的重繪不會跟著捲
|
|
452
|
+
*/
|
|
453
|
+
#scrollActiveIntoView(s) {
|
|
454
|
+
if (!s.isOpen) {
|
|
455
|
+
this.#lastActive = -1;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (s.activeIndex === this.#lastActive || s.activeIndex < 0) return;
|
|
459
|
+
this.#lastActive = s.activeIndex;
|
|
460
|
+
const hasGroups = this.controller.getGroupedView().rows.some((r) => r.type === "group");
|
|
461
|
+
if (this.#virtual && !hasGroups) {
|
|
462
|
+
const next = (0, import_core.computeScrollIntoView)({
|
|
463
|
+
index: s.activeIndex,
|
|
464
|
+
scrollTop: this.#dropdown.scrollTop,
|
|
465
|
+
viewportHeight: this.#dropdown.clientHeight,
|
|
466
|
+
itemHeight: this.#itemHeight
|
|
467
|
+
});
|
|
468
|
+
if (next !== null) {
|
|
469
|
+
this.#dropdown.scrollTop = next;
|
|
470
|
+
this.#renderOptions(s);
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const active = this.#dropdown.querySelector(
|
|
475
|
+
`.${this.#cls("option", "active")}`
|
|
476
|
+
);
|
|
477
|
+
active?.scrollIntoView?.({ block: "nearest" });
|
|
445
478
|
}
|
|
446
479
|
/** 套用模板輸出:字串走 textContent(防 XSS)Node 直接掛入 */
|
|
447
480
|
#applyTemplate(host, out) {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// src/dom.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
computeScrollIntoView,
|
|
4
|
+
computeVirtualRange,
|
|
5
|
+
createSelkit
|
|
6
|
+
} from "@selkit/core";
|
|
3
7
|
|
|
4
8
|
// src/positioner.ts
|
|
5
9
|
function computePosition(triggerRect, dropdownHeight, viewportHeight, gap = 4) {
|
|
@@ -139,6 +143,8 @@ var SelkitDom = class {
|
|
|
139
143
|
#hiddenContainer = null;
|
|
140
144
|
#selectPrevDisplay = "";
|
|
141
145
|
#dragFrom = -1;
|
|
146
|
+
/** 上次已捲入可視的 activeIndex 僅在變動時才捲 避免跟使用者手動捲動打架 */
|
|
147
|
+
#lastActive = -1;
|
|
142
148
|
#positioner = null;
|
|
143
149
|
#unsubscribe;
|
|
144
150
|
#offClose;
|
|
@@ -411,6 +417,37 @@ var SelkitDom = class {
|
|
|
411
417
|
this.#syncForm();
|
|
412
418
|
this.element.classList.toggle(this.#cls("", "open"), s.isOpen);
|
|
413
419
|
this.element.classList.toggle(this.#cls("", "disabled"), s.disabled);
|
|
420
|
+
this.#scrollActiveIntoView(s);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* 鍵盤導航/開啟時讓作用中選項保持可見(aria-activedescendant 完整度)
|
|
424
|
+
* 僅在 activeIndex 變動時動作 手動捲動觸發的重繪不會跟著捲
|
|
425
|
+
*/
|
|
426
|
+
#scrollActiveIntoView(s) {
|
|
427
|
+
if (!s.isOpen) {
|
|
428
|
+
this.#lastActive = -1;
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (s.activeIndex === this.#lastActive || s.activeIndex < 0) return;
|
|
432
|
+
this.#lastActive = s.activeIndex;
|
|
433
|
+
const hasGroups = this.controller.getGroupedView().rows.some((r) => r.type === "group");
|
|
434
|
+
if (this.#virtual && !hasGroups) {
|
|
435
|
+
const next = computeScrollIntoView({
|
|
436
|
+
index: s.activeIndex,
|
|
437
|
+
scrollTop: this.#dropdown.scrollTop,
|
|
438
|
+
viewportHeight: this.#dropdown.clientHeight,
|
|
439
|
+
itemHeight: this.#itemHeight
|
|
440
|
+
});
|
|
441
|
+
if (next !== null) {
|
|
442
|
+
this.#dropdown.scrollTop = next;
|
|
443
|
+
this.#renderOptions(s);
|
|
444
|
+
}
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const active = this.#dropdown.querySelector(
|
|
448
|
+
`.${this.#cls("option", "active")}`
|
|
449
|
+
);
|
|
450
|
+
active?.scrollIntoView?.({ block: "nearest" });
|
|
414
451
|
}
|
|
415
452
|
/** 套用模板輸出:字串走 textContent(防 XSS)Node 直接掛入 */
|
|
416
453
|
#applyTemplate(host, out) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selkit/dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Vanilla JS renderer for Selkit — DOM, events, a11y and default positioner.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@selkit/core": "0.
|
|
22
|
+
"@selkit/core": "0.3.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"axe-core": "^4.12.1",
|