agentcss 0.13.2 → 0.15.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/01-tokens/behavior/dismissable.js +31 -15
- package/02-primitives/combobox/combobox.js +6 -14
- package/02-primitives/popover/popover.js +6 -14
- package/02-primitives/select/README.md +5 -0
- package/02-primitives/select/select.js +6 -14
- package/03-patterns/drawer/drawer.js +86 -8
- package/03-patterns/dropdown/README.md +20 -4
- package/03-patterns/dropdown/dropdown.js +106 -15
- package/03-patterns/nav/nav.js +6 -14
- package/03-patterns/overlay/overlay.js +85 -16
- package/03-patterns/tooltip/tooltip.js +6 -14
- package/04-recipes/table/table.css +8 -4
- package/CHANGELOG.md +166 -2
- package/CONTRIBUTING.md +1 -1
- package/README.zh-CN.md +1 -1
- package/START-HERE.md +10 -10
- package/SUPPORT.md +7 -1
- package/ai/START-HERE.md +1 -1
- package/ai/behaviors.json +10 -10
- package/ai/components.json +12 -21
- package/dist/01-tokens/behavior/dismissable.js +3 -1
- package/dist/02-primitives/combobox/combobox.js +3 -1
- package/dist/02-primitives/popover/popover.js +3 -1
- package/dist/02-primitives/select/select.js +3 -1
- package/dist/03-patterns/drawer/drawer.js +73 -7
- package/dist/03-patterns/dropdown/dropdown.js +89 -2
- package/dist/03-patterns/nav/nav.js +3 -1
- package/dist/03-patterns/overlay/overlay.js +72 -7
- package/dist/03-patterns/tooltip/tooltip.js +3 -1
- package/dist/COSTS.md +10 -10
- package/docs/A11Y-MANUAL-PROTOCOL.md +84 -23
- package/docs/ACCESSIBILITY.md +11 -6
- package/docs/CONVENTIONS.md +9 -0
- package/index.html +1 -1
- package/package.json +1 -1
|
@@ -357,6 +357,73 @@
|
|
|
357
357
|
};
|
|
358
358
|
};
|
|
359
359
|
/* ==== BEHAVIOR INJECT END: focus-return ==== */
|
|
360
|
+
|
|
361
|
+
/* ==== BEHAVIOR INJECT BEGIN: dismissable ==== */
|
|
362
|
+
var flDismissable = function (opts) {
|
|
363
|
+
var opt = opts || {};
|
|
364
|
+
var escOn = opt.escOn || [];
|
|
365
|
+
var inside = opt.inside || [];
|
|
366
|
+
var outside = opt.outside !== false;
|
|
367
|
+
var when = opt.when || function () { return true; };
|
|
368
|
+
var onDismiss = opt.onDismiss || function () {};
|
|
369
|
+
|
|
370
|
+
var shared = window.__flDismiss || (window.__flDismiss = { stack: [], bound: false });
|
|
371
|
+
|
|
372
|
+
function isInside(node) {
|
|
373
|
+
if (!node) return false;
|
|
374
|
+
for (var i = 0; i < inside.length; i++) {
|
|
375
|
+
var el = inside[i];
|
|
376
|
+
if (!el || !el.contains) continue;
|
|
377
|
+
if (el === node || el.contains(node)) return true;
|
|
378
|
+
}
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function onKey(e) {
|
|
383
|
+
if (!when()) return;
|
|
384
|
+
var k = e.key;
|
|
385
|
+
if (k !== 'Escape' && k !== 'Esc' && e.keyCode !== 27) return;
|
|
386
|
+
/* 先问 onDismiss,它返回 false 就不干预(分级 Esc,见文件头 ⑤) */
|
|
387
|
+
var closed = onDismiss(e, 'esc');
|
|
388
|
+
if (closed === false) return;
|
|
389
|
+
if (e.preventDefault) e.preventDefault();
|
|
390
|
+
if (e.stopPropagation) e.stopPropagation();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function onDocClick(e) {
|
|
394
|
+
var top = null;
|
|
395
|
+
for (var i = shared.stack.length - 1; i >= 0; i--) {
|
|
396
|
+
if (shared.stack[i].when()) { top = shared.stack[i]; break; }
|
|
397
|
+
}
|
|
398
|
+
if (!top || top.isInside(e.target)) return;
|
|
399
|
+
top.onDismiss(e, 'outside');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
var rec = { when: when, isInside: isInside, onDismiss: onDismiss };
|
|
403
|
+
|
|
404
|
+
for (var j = 0; j < escOn.length; j++) {
|
|
405
|
+
if (escOn[j]) escOn[j].addEventListener('keydown', onKey);
|
|
406
|
+
}
|
|
407
|
+
/* 只有"点外部要关"的层才进栈:栈是给 onDocClick 挑最内层用的,
|
|
408
|
+
模态层不该被这条规则选中(见文件头 ④)。
|
|
409
|
+
⚠️ Esc 不依赖栈(它挂在元素上),所以不进栈不影响 Esc。 */
|
|
410
|
+
if (outside) shared.stack.push(rec);
|
|
411
|
+
if (!shared.bound) {
|
|
412
|
+
shared.bound = true;
|
|
413
|
+
document.addEventListener('click', onDocClick, true);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
destroy: function () {
|
|
418
|
+
for (var j = 0; j < escOn.length; j++) {
|
|
419
|
+
if (escOn[j]) escOn[j].removeEventListener('keydown', onKey);
|
|
420
|
+
}
|
|
421
|
+
var k = shared.stack.indexOf(rec);
|
|
422
|
+
if (k >= 0) shared.stack.splice(k, 1);
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
/* ==== BEHAVIOR INJECT END: dismissable ==== */
|
|
360
427
|
function dialog(opts) {
|
|
361
428
|
opts = opts || {};
|
|
362
429
|
|
|
@@ -432,26 +499,25 @@
|
|
|
432
499
|
openStack.push(stackEntry);
|
|
433
500
|
if (!useInert) uninstallIsolation = installIsolation(stackEntry);
|
|
434
501
|
|
|
502
|
+
/* ① Esc 走 dismissable 核(0.14.0 收编)。
|
|
503
|
+
`outside: false` 是模态的定义:点外部**不该关**。核里这个开关专门
|
|
504
|
+
为它加的(不进栈 ⇒ 不参与"点外部关最内层"的仲裁)。
|
|
505
|
+
⚠️ 点遮罩关闭是**另一件事**,不归核管:它带 danger / closeOnBackdrop
|
|
506
|
+
两个前置条件,是组件自己的语义(见下方 mousedown)。
|
|
507
|
+
关于"为什么不再手写 stopPropagation":核在 onDismiss 之后统一
|
|
508
|
+
preventDefault + stopPropagation,一次 Esc 只关最内层的语义与
|
|
509
|
+
以前实测一致(两个 backdrop 是兄弟节点,本来也冒泡不到外层)。 */
|
|
510
|
+
var dm = flDismissable({
|
|
511
|
+
escOn: [box],
|
|
512
|
+
outside: false,
|
|
513
|
+
when: function () { return !closed; },
|
|
514
|
+
onDismiss: function () { close(); },
|
|
515
|
+
});
|
|
516
|
+
|
|
435
517
|
/* ------------------------------------------------------------ 焦点 */
|
|
436
518
|
function focusables() { return focusableIn(box); }
|
|
437
519
|
|
|
438
520
|
box.addEventListener('keydown', function (e) {
|
|
439
|
-
if (e.key === 'Escape') {
|
|
440
|
-
e.preventDefault();
|
|
441
|
-
/* ⚠️ 防御性 stopPropagation —— 保留,但**当前结构下它不改变任何行为**。
|
|
442
|
-
* 等价突变体实测:删掉这一行,一次 Esc 仍然只关最内层。
|
|
443
|
-
* 原因:两个 `.dialog-backdrop` 是 `body` 的**兄弟**节点,外层 box 不是
|
|
444
|
-
* 内层的祖先,所以内层按钮的 keydown 冒泡路径是
|
|
445
|
-
* button → .dialog → .dialog-backdrop → body
|
|
446
|
-
* —— 根本到不了外层的 `.dialog`(实测 `outer.contains(inner) === false`)。
|
|
447
|
-
* 原注释写「不拦住会一次关两层」,那是**假设了 DOM 嵌套**,与实现不符。
|
|
448
|
-
* 保留理由:若将来改成「内层放进外层 box」的真嵌套,这行就必需。
|
|
449
|
-
* 判据 `pattern-behavior-check.js ⑬` 验证的是**行为**(只关最内层),
|
|
450
|
-
* 它抓不到删除本行的突变 —— 那是等价突变体,不是判据失效。 */
|
|
451
|
-
e.stopPropagation();
|
|
452
|
-
close();
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
521
|
if (e.key !== 'Tab') return;
|
|
456
522
|
|
|
457
523
|
/* focus trap:Tab 在弹层内循环,绝不跑到背后的页面去 */
|
|
@@ -527,6 +593,9 @@
|
|
|
527
593
|
function close() {
|
|
528
594
|
if (closed) return;
|
|
529
595
|
closed = true;
|
|
596
|
+
/* 核的 Esc 监听器要摘(outside:false ⇒ 它本来就没进栈,
|
|
597
|
+
但 destroy 仍然必须调用:摘监听器与摘栈是同一件事的两半)。 */
|
|
598
|
+
dm.destroy();
|
|
530
599
|
|
|
531
600
|
/* 🔴 焦点归还前先看清**焦点现在在谁手里**(必须在移除 backdrop 之前读,
|
|
532
601
|
移除之后 activeElement 会变成 body)。
|
|
@@ -60,6 +60,7 @@ var flDismissable = function (opts) {
|
|
|
60
60
|
var opt = opts || {};
|
|
61
61
|
var escOn = opt.escOn || [];
|
|
62
62
|
var inside = opt.inside || [];
|
|
63
|
+
var outside = opt.outside !== false;
|
|
63
64
|
var when = opt.when || function () { return true; };
|
|
64
65
|
var onDismiss = opt.onDismiss || function () {};
|
|
65
66
|
|
|
@@ -79,19 +80,7 @@ var flDismissable = function (opts) {
|
|
|
79
80
|
if (!when()) return;
|
|
80
81
|
var k = e.key;
|
|
81
82
|
if (k !== 'Escape' && k !== 'Esc' && e.keyCode !== 27) return;
|
|
82
|
-
/*
|
|
83
|
-
(0.8.8 改):**分级 Esc** 需要核让组件有机会说"这次我没关"。
|
|
84
|
-
combobox 的 Esc 是三级的:
|
|
85
|
-
① 列表开着 → 关列表
|
|
86
|
-
② 列表关了、输入有字 → 清空输入
|
|
87
|
-
③ 都没了 → 什么都不做(此时**不该**阻止默认行为,
|
|
88
|
-
让 Esc 能继续传给外层浮层)
|
|
89
|
-
而原实现是「无条件 preventDefault + stopPropagation,再调 onDismiss」
|
|
90
|
-
⇒ 组件根本无法表达"这次我没关"。
|
|
91
|
-
⇒ 改成:先问onDismiss,它返回 false 就**不干预**默认行为。
|
|
92
|
-
⚠️ 顺序安全性:stopPropagation 只需在**本次监听器执行期间**被调用即可
|
|
93
|
-
阻止冒泡到外层,放在 onDismiss 之后仍然有效。
|
|
94
|
-
*/
|
|
83
|
+
/* 先问 onDismiss,它返回 false 就不干预(分级 Esc,见文件头 ⑤) */
|
|
95
84
|
var closed = onDismiss(e, 'esc');
|
|
96
85
|
if (closed === false) return;
|
|
97
86
|
if (e.preventDefault) e.preventDefault();
|
|
@@ -112,7 +101,10 @@ var flDismissable = function (opts) {
|
|
|
112
101
|
for (var j = 0; j < escOn.length; j++) {
|
|
113
102
|
if (escOn[j]) escOn[j].addEventListener('keydown', onKey);
|
|
114
103
|
}
|
|
115
|
-
|
|
104
|
+
/* 只有"点外部要关"的层才进栈:栈是给 onDocClick 挑最内层用的,
|
|
105
|
+
模态层不该被这条规则选中(见文件头 ④)。
|
|
106
|
+
⚠️ Esc 不依赖栈(它挂在元素上),所以不进栈不影响 Esc。 */
|
|
107
|
+
if (outside) shared.stack.push(rec);
|
|
116
108
|
if (!shared.bound) {
|
|
117
109
|
shared.bound = true;
|
|
118
110
|
document.addEventListener('click', onDocClick, true);
|
|
@@ -73,10 +73,14 @@
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/* ⭐ 排序指示符改用 mask 画,不再用文字字形。
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
⚠️ 不要改回 `content: "↕"
|
|
76
|
+
原因:浏览器会把 ::after 的 `content` 文本算进**可访问名**(name-from-contents,
|
|
77
|
+
`<button>` / `<a>` 这类),读屏于是念出「公司 · ↕ · 按钮」—— 这个符号对用户毫无意义,
|
|
78
|
+
而且自动检查抓不到(名字非空即过)。换成图像后字形不进无障碍树。
|
|
79
|
+
⚠️ 不要改回 `content: "↕"` 的字形写法,那会把符号念出来。
|
|
80
|
+
⚠️ 也先别改成 `content: "↕" / ""` 的替代文本语法:三引擎实测**都支持**、也都能清掉 ↕,
|
|
81
|
+
但它要 Chromium 108 / Firefox 128 / Safari 17.4 起才认,更老的引擎会丢掉整条声明
|
|
82
|
+
⇒ 箭头彻底消失(支持矩阵里有 Chromium 97,正好在门槛外)。
|
|
83
|
+
实测矩阵见 docs/A11Y-MANUAL-PROTOCOL.md。 */
|
|
80
84
|
.table__sort::after {
|
|
81
85
|
content: ""; /* 空内容 ⇒ 不进可访问名 */
|
|
82
86
|
inline-size: .85em;
|
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,163 @@
|
|
|
16
16
|
|
|
17
17
|
(无)
|
|
18
18
|
|
|
19
|
+
## [0.15.0] - 2026-10-31
|
|
20
|
+
|
|
21
|
+
### 行为 · dropdown 的「打字跳项」收编 typeahead 核(**最后一个 beta 升 stable**)
|
|
22
|
+
|
|
23
|
+
`ai/behaviors.json` 里最后一条真缺口是 `dropdown` 的 `typeahead:gap`:
|
|
24
|
+
APG Menu 把"按字符跳到名字以它开头的项"列为 `(Optional)`,本库确实没做。
|
|
25
|
+
这一轮补上,走 `typeahead` 核(不新写一份)。
|
|
26
|
+
|
|
27
|
+
接线时补了两件核不管的事:
|
|
28
|
+
|
|
29
|
+
- **打开/关闭都清缓冲**:上次关菜单前打了一半的前缀不该留到下次;
|
|
30
|
+
- **匹配前剥掉开头的装饰符号**:带勾选的项文本是「✓ 按日期」,
|
|
31
|
+
不剥的话打「按」永远匹配不到。这和 table 列头把 `↕` 念出来是同一类问题 ——
|
|
32
|
+
装饰符号混进了"名字"里。
|
|
33
|
+
|
|
34
|
+
⚠️ 一个必须写明的限制:**中文标签打不出来**。IME 组字时 `keydown` 给的是
|
|
35
|
+
`Process` / `Unidentified`,拿不到字符。所以这条能力天然只对拉丁字母标签有效 ——
|
|
36
|
+
这也是 APG 把它标成 Optional 的现实原因。判据因此用一张**英文夹具**测
|
|
37
|
+
(只换标签、跑的是同一份 `Dropdown.create`),demo 的中文菜单不受影响。
|
|
38
|
+
|
|
39
|
+
判据 6 条,每条各有独立突变体(删分支 / 去掉同字母特判 / 不累积前缀 /
|
|
40
|
+
不剥装饰符号 / 没找到就跳首项 —— 每次只有对应的那条红):
|
|
41
|
+
|
|
42
|
+
| 判据 | 断言 | 只被哪个突变体打红 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| ⑦ 首字母 | 打 `p` ⇒ Paste | 删掉打字分支 |
|
|
45
|
+
| ⑦b 同字母循环 | `c` `c` ⇒ Copy → Cut | 去掉 `repeated()` 特判 |
|
|
46
|
+
| ⑦c 前缀累积 | `s` `e` ⇒ Select all | 把 `buf +=` 改成 `buf =` |
|
|
47
|
+
| ⑦d 不匹配不动 | 打 `z` 焦点不变 | `return -1` 改成 `return 0` |
|
|
48
|
+
| ⑦e 关闭态 | 关着打字不开菜单 | 删掉打字分支 |
|
|
49
|
+
| ⑦f 装饰符号 | 打 `c` 命中「✓ Copy」 | 去掉剥符号那一步 |
|
|
50
|
+
|
|
51
|
+
### 判据 · 全库 stable 之后,ladder 怎么才算没瞎
|
|
52
|
+
|
|
53
|
+
37 个组件全 stable,`maturity-gate` ③("不得全落同一档")按时报红。
|
|
54
|
+
门禁自己写了两条路:加严判据,或显式声明台阶退役。这轮走了**第三条**,
|
|
55
|
+
因为前两条都不诚实:为了"留几个 beta"而加严是拿数据凑判据,直接删掉 ③ 又是放走量具。
|
|
56
|
+
|
|
57
|
+
⇒ ③ 改成:全库 stable 时先做**正向控制** —— 给一个真实组件临时塞一条合成债务,
|
|
58
|
+
重算必须降成 beta;撤掉必须回到 stable。通过了才放行,并在输出里说明
|
|
59
|
+
"阶梯退而不休"。**分支里唯一的证据就是"判据仍然会红"**,不是一个允许全绿的开关。
|
|
60
|
+
|
|
61
|
+
反向控制也跟着换了:原来的突变体是"把矩阵洗白"(全改成 core)——
|
|
62
|
+
全库达标后这已经是**合法状态**,突变消失了。改成"**把判据改松**"
|
|
63
|
+
(让 `recompute` 忽略债务、一律返回 stable)⇒ 正向控制抓得到 ⇒ ③ 红,且只红这一条。
|
|
64
|
+
|
|
65
|
+
### 体积
|
|
66
|
+
|
|
67
|
+
`dropdown.js` 多背一份 typeahead 核(gzip **6.4 → 7.6 KB / +18.9%**)。
|
|
68
|
+
全库 gzip 258.5 → 259.7 KB(**+0.46%**)。基线按显式重录记下(`size-baseline.py --record`)。
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### 行为 · drawer / overlay 的「关得掉」收编进核(beta → stable)
|
|
72
|
+
|
|
73
|
+
`ai/behaviors.json` 里这两格写着 `manual` 已经很久了,理由也很诚实:0.8.6 评估过,
|
|
74
|
+
但核当时只有"点外部一律关",而这两个都是**模态** —— 收编等于给它们引入错误行为。
|
|
75
|
+
于是 Esc 各留一份手写实现,I-14 的病留在原地。
|
|
76
|
+
|
|
77
|
+
这次补的是核缺的那一半:`outside: false`(模态层**不进栈**)。
|
|
78
|
+
不进栈的后果写进了核文件头:模态层不再参与"点外部关最内层"的仲裁 ——
|
|
79
|
+
它压根不该被这条规则选中;Esc 走元素监听器,与栈无关。
|
|
80
|
+
|
|
81
|
+
- `drawer`:删掉手写的 Esc 分支与 `if (e.target === host) close()`;
|
|
82
|
+
`inside` 只放抽屉本体,遮罩**不在** inside 里(放进去就等于声明"点遮罩算内部")。
|
|
83
|
+
- `overlay`:删掉手写的 Esc 分支;点遮罩关闭仍是它自己的语义
|
|
84
|
+
(带 `danger` / `closeOnBackdrop` 两个前置条件),没并进核。
|
|
85
|
+
|
|
86
|
+
判据跟着补,且都不是"改完再看一眼":
|
|
87
|
+
|
|
88
|
+
- `dismissable-check` ⑧ 模态点外部不关、⑧b Esc 仍关得掉(两半分开断 ——
|
|
89
|
+
只断"不关"的话,把关闭路径整条删掉也能过)、⑨ 模态不进栈(模态里开菜单,
|
|
90
|
+
点外面只关菜单)。自检突变从 3 个加到 **5 个**,新增的两个专打 `outside`。
|
|
91
|
+
- `drawer-check` ⑧ 点抽屉内部不关、⑨ 点遮罩关闭。
|
|
92
|
+
- `overlay-check` ⑤c `body` 上的外部点击不关模态。
|
|
93
|
+
- 反向实验:把遮罩写进 drawer 的 `inside` ⇒ ⑨ 红;把 overlay 的 `outside` 改回
|
|
94
|
+
`true` ⇒ ⑤c 红。
|
|
95
|
+
|
|
96
|
+
成熟度:**stable 36 / beta 1**(原 33 / 4),`strict` 档位下不可用的组件从 4 个降到 1 个
|
|
97
|
+
(只剩 `dropdown` 的 `typeahead:gap`)。`budget.manual` 棘轮从 2 收到 **0**。
|
|
98
|
+
|
|
99
|
+
### 判据 · accordion 那格 `roving:gap` 是**写错了**,不是没做
|
|
100
|
+
|
|
101
|
+
同一轮里核对 APG 原文才发现:`ai/behaviors.json` 里 accordion 的 roving 写着
|
|
102
|
+
"APG 把方向键列为**可选**增强,本库未实现" —— 这句话不成立。APG Accordion 的
|
|
103
|
+
Keyboard Interaction 只有 `Enter`/`Space`、`Tab`、`Shift+Tab` 三项,
|
|
104
|
+
**没有**方向键 / Home / End,也没有"可选"字样(w3.org 原文,2026-10-30 取)。
|
|
105
|
+
|
|
106
|
+
而组件 README 第 68 行早就写了同一个结论,还多给了一条更硬的理由:
|
|
107
|
+
移动端的方向键就是**页面滚动键**,抢过来会直接和滚动打架。
|
|
108
|
+
⇒ 矩阵没跟上组件自己的文档,让 accordion 白背了一个 beta。
|
|
109
|
+
|
|
110
|
+
改成 `n/a`(附核对日期与链接),不是"为了升 stable 而改判据"——同一轮里
|
|
111
|
+
`dropdown` 的 `typeahead:gap` **保留**:APG Menu 明确把"按字符跳项"列为
|
|
112
|
+
`(Optional)`,本库确实没做。两格按同一条标准判:矩阵记的是**规范要求什么**,
|
|
113
|
+
不是我们想不想做。
|
|
114
|
+
|
|
115
|
+
**体积代价**:两个组件各多背一份核 ⇒ gzip 总体积 **+0.49%**(257.2 → 258.5 KB),
|
|
116
|
+
`drawer.js` 单文件 gzip 3.4 → 4.5 KB(+32.7%)。这是「注入而非运行时依赖」的既定代价,
|
|
117
|
+
`size-baseline` 已按显式重录记下。顺带把注入块里 429 字节的长注释挪回核文件头
|
|
118
|
+
(8 个使用点都背一份),把原本 +1.53% 压回 +0.49%。
|
|
119
|
+
|
|
120
|
+
### 判据 · 「打开」的三条输入路径现在有一张矩阵
|
|
121
|
+
|
|
122
|
+
起因不是新 bug,是**判据的覆盖面**:`dropdown` 的菜单开了、焦点留在按钮上,
|
|
123
|
+
而当时 212 道门禁全绿 —— 因为那条判据只按了 `↓`。读屏在浏览模式按 Enter,
|
|
124
|
+
浏览器把它变成**一次点击**,走的是另一条分支。
|
|
125
|
+
|
|
126
|
+
新增 `05-audit/opener-path-check.js`:`dropdown` / `select` / `popover` / `overlay` /
|
|
127
|
+
`drawer` 五个"点一下就开"的组件 × 三条路径(键盘 / 鼠标点击 / 读屏合成点击),
|
|
128
|
+
每格断言「打开 + 焦点到位」,并额外判**三条路径结果必须一致**——不一致本身就是缺陷,
|
|
129
|
+
哪怕每种结果单独看都能自圆其说。
|
|
130
|
+
|
|
131
|
+
「焦点到位」按组件各自的合法形态断:菜单 / 对话框 / 气泡是**焦点移进弹层**;
|
|
132
|
+
select-only combobox 是**焦点留在按钮 + `aria-activedescendant` 指向选中项**。
|
|
133
|
+
后者是这一轮实测出来的:拿"DOM 焦点必须进列表"去要求它,会把自己判成缺陷。
|
|
134
|
+
|
|
135
|
+
`--selftest` 用一张故意写坏的夹具证明它分得清三条路:只监听 `keydown` 的实现在
|
|
136
|
+
键盘上绿、在另两条上红;只监听 `pointerdown` 的只在鼠标上绿;开得了但不动焦点的
|
|
137
|
+
三条全红,且**"没打开"与"打开了没移焦"分开判**。
|
|
138
|
+
|
|
139
|
+
结论:五个组件 15 格全绿 —— 这一类没有新缺陷,变的是判据,不是实现。
|
|
140
|
+
|
|
141
|
+
### 文档 · select 的「焦点落在选中项」说清了是哪一种焦点
|
|
142
|
+
|
|
143
|
+
原文只写"落在选中项(不是第一项,也不是按钮)"。实际实现是
|
|
144
|
+
`aria-activedescendant` 指过去、DOM 焦点留在按钮上 —— 这是 APG 对
|
|
145
|
+
select-only combobox 的合法形态,但按字面拿 `document.activeElement` 去核对会判成缺陷。
|
|
146
|
+
已补一段说明「活动项 ≠ DOM 焦点」,并指出判据是按哪一种断的。
|
|
147
|
+
|
|
148
|
+
### 文档 · 无障碍人工测试补上「标题层级」这一格
|
|
149
|
+
|
|
150
|
+
九个页面的 `H` 键逐级跳从"未执行"变成实测结论:NVDA 念出的 **107/107 条**级别与页面
|
|
151
|
+
结构逐条一致,九页走到底都念「往下没有标题」。证据是读屏交给语音合成器的原文,不是
|
|
152
|
+
人耳旁听 —— 与第一轮同样的口径。
|
|
153
|
+
|
|
154
|
+
同一轮把机器侧的结构扫描从 9 页扩到**全站 96 页 / 887 个标题**:每页恰好一个 `h1`、
|
|
155
|
+
跳级 0 处、空名 0 个,层级只到 3 级(`h1×96 · h2×581 · h3×210`),DOM 里的标题数与
|
|
156
|
+
无障碍树里的 heading 节点数逐页相等。
|
|
157
|
+
|
|
158
|
+
三条 axe 规则(`heading-order` / `page-has-heading-one` / `empty-heading`)各做了一次
|
|
159
|
+
破坏实验,确认真的会红;外加一条反向对照(合法的 `h3 → h4` 仍然全绿),
|
|
160
|
+
证明它们不是"见到 `h4` 就报"。实验表在
|
|
161
|
+
[docs/A11Y-MANUAL-PROTOCOL.md](docs/A11Y-MANUAL-PROTOCOL.md)。
|
|
162
|
+
|
|
163
|
+
### 文档 · 协议页补了三个实操坑,都是这一轮踩出来的
|
|
164
|
+
|
|
165
|
+
1. 读屏按 **URL** 记忆浏览光标,复用同一个 URL 会从上次的位置继续 ⇒ 看起来像"本页没有标题"。
|
|
166
|
+
2. 加载后按 `↑` **不一定**读到 `h1`(有时落在导语段落上)⇒ 用 `H` 再 `SHIFT+H` 取。
|
|
167
|
+
3. 末端那句「往下没有标题」是**证据**不是失败。
|
|
168
|
+
|
|
169
|
+
### 更正 · 一句话当时没有证据
|
|
170
|
+
|
|
171
|
+
协议页原来写"九个页面都跑过 Tab 遍历……标题层级本轮没做"。前半句没错,但
|
|
172
|
+
"**从页面顶部**按 Tab 走一遍"里的"从页面顶部"没有证据:脚本里那行 `CTRL+HOME` 走的是
|
|
173
|
+
一个不支持 `HOME` 的按键通道,一直返回 `rc=1`,等于空操作。
|
|
174
|
+
Tab 遍历本身走焦点顺序、不依赖浏览光标,所以结论不受影响;这句话已改成它真正证明的东西。
|
|
175
|
+
|
|
19
176
|
## [0.13.2] - 2026-10-30
|
|
20
177
|
|
|
21
178
|
### 修复 · 下拉菜单打开时,焦点没有交给第一项
|
|
@@ -30,10 +187,17 @@
|
|
|
30
187
|
|
|
31
188
|
列头按钮的可访问名里混着 `↕`,读屏念成「公司 · ↕ · 按钮」。
|
|
32
189
|
来源是 `.table__sort::after { content: "↕" }` —— Chromium 把伪元素文本算进了
|
|
33
|
-
可访问名。改为 `content: ""` + `mask`
|
|
190
|
+
可访问名。改为 `content: ""` + `mask` 图像画箭头:仍是 ↕ / ↑ / ↓ 三种形状、颜色走
|
|
34
191
|
`currentColor`,**标记结构不用改**(外部集成方无需动 HTML)。
|
|
35
192
|
|
|
36
|
-
|
|
193
|
+
尺寸不是零差异:箭头 5×12 → 4×10 px(393 视口实测),墨量反而更足(3.7 → 4.9),
|
|
194
|
+
垂直居中也更准;但它的盒子宽了 2px,同一表内右对齐各列最多右移 6px。
|
|
195
|
+
|
|
196
|
+
🔴 2026-10-30 更正:这条原本写着「不要改用 `content: "↕" / ""` —— Firefox 不支持」。
|
|
197
|
+
**那句是错的**:三个引擎实测都接受该语法,也都把 ↕ 从可访问名里去掉了。
|
|
198
|
+
真正的理由是**老引擎**——该语法要 Chromium 108 / Firefox 128 / Safari 17.4,
|
|
199
|
+
不认的引擎会丢掉整条声明、箭头彻底消失,而支持矩阵里有 Chromium 97(Android 百度浏览器)。
|
|
200
|
+
实测矩阵见 [docs/A11Y-MANUAL-PROTOCOL.md](docs/A11Y-MANUAL-PROTOCOL.md)。
|
|
37
201
|
|
|
38
202
|
### 判据 · 两处都补了能抓住它的检查
|
|
39
203
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -8,7 +8,7 @@ npm run gate # 全量:含浏览器类
|
|
|
8
8
|
npm run gate:fast # 只跑静态层,不用装浏览器
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
全量是 **
|
|
11
|
+
全量是 **214 道门禁**(4 路并发实测约 9 分钟 / 退出码 0;单道耗时累加 1691 秒,
|
|
12
12
|
组件层要起浏览器,还要逐张比 96 张视觉截图。0.11.15 起默认 4 路并发)。
|
|
13
13
|
只想确认没改坏静态契约的话用 `gate:fast`,几秒到几分钟。
|
|
14
14
|
|
package/README.zh-CN.md
CHANGED
|
@@ -83,7 +83,7 @@ npm install github:owain323/frontend-lib
|
|
|
83
83
|
|
|
84
84
|
## 组件一览
|
|
85
85
|
|
|
86
|
-
共 **37** 个(stable
|
|
86
|
+
共 **37** 个(stable 37 · 全部通过四条判据)。每个组件目录下有 `README.md`(为什么这么做)和 `demo.html`(能直接抄的结构)。
|
|
87
87
|
|
|
88
88
|
### primitives · 基础件(15)
|
|
89
89
|
|
package/START-HERE.md
CHANGED
|
@@ -39,26 +39,26 @@
|
|
|
39
39
|
| 标签 / 徽章 | `02-primitives/badge/badge.css` | 141 | — | 有状态标记就要("已披露""停牌") |
|
|
40
40
|
| 卡片 | `02-primitives/card/card.css` | 246 | — | 有分组内容就要 |
|
|
41
41
|
| 单选 / 多选 | `02-primitives/choice/choice.css` | 395 | — | 单选 / 多选就要 |
|
|
42
|
-
| 下拉选择 | `02-primitives/select/select.css` | 151 | **必需
|
|
43
|
-
| 可搜索选择 | `02-primitives/combobox/combobox.css` | 223 | **必需
|
|
42
|
+
| 下拉选择 | `02-primitives/select/select.css` | 151 | **必需 541** (`select.js`) | 原生 select 的外观或交互不够用就要 |
|
|
43
|
+
| 可搜索选择 | `02-primitives/combobox/combobox.css` | 223 | **必需 477** (`combobox.js`) | 选项多到要搜索,或要选多个并留成标签就要 |
|
|
44
44
|
| 开关 | `02-primitives/switch/switch.css` | 370 | — | 有"立即生效的设置"就要 |
|
|
45
45
|
| 分隔线 | `02-primitives/separator/separator.css` | 73 | — | 要分开内容块就要 |
|
|
46
46
|
| 加载骨架 | `02-primitives/skeleton/skeleton.css` | 96 | — | 数据还没到、先把版式占住就要 |
|
|
47
47
|
| 进度条 / 环 | `02-primitives/progress/progress.css` | 172 | — | 有耗时过程要给进度就要 |
|
|
48
|
-
| 浮层气泡 | `02-primitives/popover/popover.css` | 187 | **必需
|
|
48
|
+
| 浮层气泡 | `02-primitives/popover/popover.css` | 187 | **必需 323** (`popover.js`) | 要一段信息贴着触发元素浮出来就要 |
|
|
49
49
|
| 日期区间 | `02-primitives/date-range/date-range.css` | 206 | **必需 301** (`date-range.js`) | 要选起止日期就要 |
|
|
50
50
|
| 标签页 | `03-patterns/tabs/tabs.css` | 172 | **必需 316** (`tabs.js`) | 同层级内容切换就要 |
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
| 文字提示 | `03-patterns/tooltip/tooltip.css` | 89 | **必需
|
|
51
|
+
| 折叠面板 | `03-patterns/accordion/accordion.css` | 453 | **必需 389** (`accordion.js`) | 长表单分段就要 |
|
|
52
|
+
| 动作菜单 | `03-patterns/dropdown/dropdown.css` | 135 | **必需 547** (`dropdown.js`) | 要一组动作(新建 / 导出 / 删除)就要 |
|
|
53
|
+
| 文字提示 | `03-patterns/tooltip/tooltip.css` | 89 | **必需 280** (`tooltip.js`) | 图标按钮或缩写需要一句话解释就要 |
|
|
54
54
|
| 分页 | `03-patterns/pagination/pagination.css` | 195 | **必需 401** (`pagination.js`) | 数据多到要翻页就要 |
|
|
55
55
|
| 树形 | `03-patterns/tree/tree.css` | 122 | **必需 464** (`tree.js`) | 层级数据(文件 / 组织 / 分类)要展开就要 |
|
|
56
|
-
|
|
|
57
|
-
| 弹窗 /
|
|
56
|
+
| 侧边抽屉 | `03-patterns/drawer/drawer.css` | 129 | **必需 336** (`drawer.js`) | 要从侧边滑出面板就要 |
|
|
57
|
+
| 弹窗 / 吐司 | `03-patterns/overlay/overlay.css` | 319 | **必需 638** (`overlay.js`) | 需要弹层(吐司 / 模态)就要 |
|
|
58
58
|
| 状态(空 / 加载 / 错 / 成功) | `03-patterns/states/states.css` | 252 | — | **凡是会异步取数就要** |
|
|
59
59
|
| 表单校验 | `03-patterns/form-validation/form-validation.css` | 263 | — | 提交前要校验就要 |
|
|
60
60
|
| 列表增删 | `03-patterns/list/list.css` | 162 | 可选 201 (`flip.js`,FLIP 增删动画,纯增强) | 列表会动态增删就要 |
|
|
61
|
-
| 导航 / 目录 | `03-patterns/nav/nav.css` | 314 | **必需
|
|
61
|
+
| 导航 / 目录 | `03-patterns/nav/nav.css` | 314 | **必需 425** (`nav.js`) · 可选 111 (`toc.js`,自动生成目录,纯增强) | 顶部要导航,或长文要目录就要 |
|
|
62
62
|
| 长文排版 | `03-patterns/content/content.css` | 286 | — | 写文档 / 长文就要 |
|
|
63
63
|
| delta | `02-primitives/delta/delta.css` | 113 | — | |
|
|
64
64
|
| metric | `02-primitives/metric/metric.css` | 157 | — | |
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
| analysis-report | `04-recipes/analysis-report/analysis-report.css` | 187 | — | |
|
|
67
67
|
| detail-page | `04-recipes/detail-page/detail-page.css` | 190 | — | |
|
|
68
68
|
| report | `04-recipes/report/report.css` | 364 | — | |
|
|
69
|
-
| table | `04-recipes/table/table.css` |
|
|
69
|
+
| table | `04-recipes/table/table.css` | 387 | — | |
|
|
70
70
|
| bar | `09-assets/bar/bar.css` | 166 | **必需 320** (`bar.js`) | |
|
|
71
71
|
| model-viewer | `09-assets/model-viewer/model-viewer.css` | 143 | **必需 423** (`model-viewer.js`) | |
|
|
72
72
|
| scientific-plot | `09-assets/scientific-plot/scientific-plot.css` | 141 | **必需 610** (`scientific-plot.js`) | |
|
package/SUPPORT.md
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
> ⇒ 这比"没验证"更糟:它给了一个**错误的**证据来源,
|
|
12
12
|
> 读者会以为"至少 Firefox 跑过了"。
|
|
13
13
|
> 现在引擎是真的切换了,并且每个作业都要**自己报 UA** 自证(见下)。
|
|
14
|
+
>
|
|
15
|
+
> 🔴 2026-10-30 更正(反方向):
|
|
16
|
+
> 这一页曾把桌面 Firefox 标成 ⚠️,理由是「没装 firefox 时门禁显式 SKIP」。
|
|
17
|
+
> **那句推测是错的**:CI 的 `浏览器矩阵(firefox)` 作业一直在跑,而且是绿的
|
|
18
|
+
> (UA 自报 `X11; Linux x86_64; rv:155.0 Gecko/20100101 Firefox/155.0`)。
|
|
19
|
+
> ⚠️ 与 ❌ 都会低估覆盖面,和 ✅ 高估一样是失真。
|
|
14
20
|
|
|
15
21
|
## 状态定义
|
|
16
22
|
|
|
@@ -31,7 +37,7 @@
|
|
|
31
37
|
|---|---|---|---|
|
|
32
38
|
| 桌面 | Chromium | ✅ | 全部组件契约、暗色对比度、响应式三档、视觉回归 |
|
|
33
39
|
| 桌面 | WebKit | ✅ | nightly 矩阵的组件契约(select / combobox / date-range / overlay / tree / dropdown / popover / tooltip) |
|
|
34
|
-
| 桌面 | Firefox |
|
|
40
|
+
| 桌面 | Firefox | ✅ | 与 WebKit 同一批组件契约(select / combobox / date-range / overlay / tree / dropdown / popover / tooltip),CI 作业自报 Gecko UA;另外 8 个组件(accordion / drawer / table / tabs / choice / input / overlay / dropdown)用 `FL_ENGINE=firefox` 在本机复跑同样全绿 |
|
|
35
41
|
|
|
36
42
|
### 🔴 怎么知道"跑的真的是这个引擎"
|
|
37
43
|
|
package/ai/START-HERE.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
> 它的值直接从 `ai/components.json` 与目录实际内容读出来 —— **不信上面的说法,信这个**。
|
|
31
31
|
|
|
32
32
|
<!-- ==== AI-INVENTORY-BEGIN ==== -->
|
|
33
|
-
- 组件 **37** 个(`
|
|
33
|
+
- 组件 **37** 个(`stable` 37)
|
|
34
34
|
- `04-recipes/`(页面级示例):`analysis-report`(组合型研究报告:正文 + 辅助栏并排,窄屏退回单栏) · `data-showcase`(四种数据表达方式对照:我到底该用哪个) · `detail-page`(实体详情页骨架:字段齐全 / 部分缺失 / 无数据,三态同构(DOM 不变)) · `longform`(单栏长文排版与阅读节奏) · `report`(报告版式系统 v1:共享基础 + 三种版式取舍(杂志 / 科研技术 / 金融研究)) · `table`(专业数据表格:列级格式与单位,三种用途预设)
|
|
35
35
|
- `09-assets/`(图表与可视化):`bar`(柱状图,含图表规范的裁剪铁律) · `echarts-adapter`(把令牌喂给 ECharts,按需引入;本库不含 ECharts) · `model-viewer`(惰性加载的 3D 模型查看器) · `scientific-plot`(二维科学绘图:坐标轴 / 误差棒 / 置信区间带 / 对数轴) · `sparkline`(迷你趋势线,没有坐标轴的走势提示)
|
|
36
36
|
<!-- ==== AI-INVENTORY-END ==== -->
|
package/ai/behaviors.json
CHANGED
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"budget": {
|
|
31
|
-
"manual":
|
|
32
|
-
"gap":
|
|
31
|
+
"manual": 0,
|
|
32
|
+
"gap": 0
|
|
33
33
|
},
|
|
34
34
|
"matrix": {
|
|
35
35
|
"dismissable": {
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"展开/收起是内容显隐,没有'关闭'语义;Esc 不参与"
|
|
55
55
|
],
|
|
56
56
|
"drawer": [
|
|
57
|
-
"
|
|
58
|
-
"0.8.6
|
|
57
|
+
"core",
|
|
58
|
+
"0.14.0 收编(推翻 0.8.6 的\"暂不做\"):核新增 `outside` 开关,模态层不进栈,于是\"点遮罩关闭\"仍然成立而\"点其它地方\"不会被判成 outside。删掉手写的 Esc 分支与 `if (e.target === host) close()`;inside 只放抽屉本体,遮罩**不在** inside 里(放进去就等于声明\"点遮罩算内部\")。判据 drawer-check ⑧(点内部不关)/ ⑨(点遮罩关)。"
|
|
59
59
|
],
|
|
60
60
|
"dropdown": [
|
|
61
61
|
"core",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"0.11.2 收编:抽屉的「Esc 关闭」直接走核。⚠️ 遮罩**不能**写进 inside —— 写进去就等于声明\"点遮罩算内部\",那一句\"点遮罩关闭\"的注释会立刻变成谎话(实测过,关不掉)"
|
|
75
75
|
],
|
|
76
76
|
"overlay": [
|
|
77
|
-
"
|
|
78
|
-
"0.8.6
|
|
77
|
+
"core",
|
|
78
|
+
"0.14.0 收编(推翻 0.8.6 的\"不收编\"):dialog 是模态,点外部不关 —— 这正是核新开关 `outside:false` 的语义(模态层不进栈)。Esc 走核;点遮罩带 danger / closeOnBackdrop 两个前置条件,仍是组件自己的语义,没并进核。判据 overlay-check ⑤c(body 上的外部点击不关)/ ⑤b(点遮罩关且焦点归位)。"
|
|
79
79
|
],
|
|
80
80
|
"pagination": [
|
|
81
81
|
"n/a",
|
|
@@ -222,8 +222,8 @@
|
|
|
222
222
|
"aria-activedescendant 模式:焦点常驻按钮"
|
|
223
223
|
],
|
|
224
224
|
"accordion": [
|
|
225
|
-
"
|
|
226
|
-
"APG
|
|
225
|
+
"n/a",
|
|
226
|
+
"**判据纠正(2026-10-30 核对 APG 原文)**:此前这里写 `gap`,理由是\"APG 把方向键列为可选增强,本库未实现\"。核对后这条不成立 —— APG Accordion 的 Keyboard Interaction 只有 `Enter`/`Space`、`Tab`、`Shift+Tab` 三项,**没有**方向键 / Home / End,也没有\"可选\"字样(w3.org/WAI/ARIA/apg/patterns/accordion,2026-10-30 取)。加上本库是移动端组件:方向键就是页面滚动键,抢过来会直接和滚动打架。⇒ 不适用,不是缺口。组件 README 第 68 行早就写了同一个结论,是矩阵没跟上。"
|
|
227
227
|
],
|
|
228
228
|
"drawer": [
|
|
229
229
|
"n/a",
|
|
@@ -316,8 +316,8 @@
|
|
|
316
316
|
"没有候选项集合"
|
|
317
317
|
],
|
|
318
318
|
"dropdown": [
|
|
319
|
-
"
|
|
320
|
-
"APG
|
|
319
|
+
"core",
|
|
320
|
+
"0.15.0 收编:APG Menu 的 `(Optional)` 项「按字符跳项」走 typeahead 核。删掉自写的匹配逻辑(本来也没有),接线时补了两件事:① 匹配前剥掉开头的装饰符号(带勾选的项文本是「✓ 按日期」,不剥就永远匹配不到,同 table 列头 ↕ 那类问题);② open/close 都清缓冲。判据 dropdown-check ⑦(首字母)/ ⑦b(同字母循环)/ ⑦c(前缀累积)/ ⑦d(不匹配不动)/ ⑦e(关着时打字不开菜单)/ ⑦f(装饰符号不进匹配文本),五条各有独立突变体。"
|
|
321
321
|
],
|
|
322
322
|
"flip": [
|
|
323
323
|
"n/a",
|
package/ai/components.json
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
3
|
"generatedBy": "05-audit/gen-ai-contract.py",
|
|
4
|
-
"libraryVersion": "0.
|
|
4
|
+
"libraryVersion": "0.15.0",
|
|
5
5
|
"$comment": "机器可读的组件契约。slots = BEM 元素名(在本库里元素名就是插槽名),states.enum = 组件会在 DOM 上写出的 data-* 取值,states.legacyClass = 仍未收敛到 data 属性的历史状态类(INVARIANT I-8 未达标)。",
|
|
6
6
|
"maturityCriteria": {
|
|
7
7
|
"alpha": "缺 demo.html 或缺 05-audit 下的专属门禁 ⇒ 还没被机器验证过,不要用",
|
|
8
8
|
"beta": "有 demo + 专属门禁,但**下面至少有一条没过**:① 状态仍用 .is-* class 表达(违反 INVARIANT I-8);② 该收编到微行为核的行为还是**手写副本**(manual)或**根本没做**(gap) ⇒ 改一处不会全库跟着变(I-14 的病)",
|
|
9
9
|
"stable": "以上四条全过:有 demo + 专属门禁 + 状态全在 data-* 属性上 + 行为零欠账",
|
|
10
10
|
"$note": "成熟度是【计算出来的】,不是拍的。手写把某个组件改成 stable 而它还有 manual / gap ⇒ 门禁会红。",
|
|
11
|
-
"$whyFourth": "🔴 为什么第四条判据不能省:用前三条时多数组件会**全是 stable** ⇒ ladder 没有任何鉴别力(对标 Primer 六档阶梯,每档都有硬判据)。补上「行为欠账」这一条后:**stable
|
|
12
|
-
"$strictImpact": "
|
|
11
|
+
"$whyFourth": "🔴 为什么第四条判据不能省:用前三条时多数组件会**全是 stable** ⇒ ladder 没有任何鉴别力(对标 Primer 六档阶梯,每档都有硬判据)。补上「行为欠账」这一条后:**stable 37 / beta 0**(本行由生成器算出,不要手改)。全库达标时 ladder 靠**正向控制**证明还有鉴别力(maturity-gate ③:塞一条合成债务必须降 beta),不是靠\"总得留几个 beta\"。",
|
|
12
|
+
"$strictImpact": "✅ strict 档位放行全部 37 个组件 —— 没有任何组件带着未收编的手写行为或未做的行为(本行由生成器算出,不要手改)"
|
|
13
13
|
},
|
|
14
14
|
"summary": {
|
|
15
15
|
"total": 37,
|
|
16
16
|
"byMaturity": {
|
|
17
|
-
"stable":
|
|
18
|
-
"beta": 4
|
|
17
|
+
"stable": 37
|
|
19
18
|
},
|
|
20
19
|
"byTier": {
|
|
21
20
|
"primitive": 15,
|
|
@@ -244,14 +243,12 @@
|
|
|
244
243
|
"tier": "pattern",
|
|
245
244
|
"purpose": "可折叠分组",
|
|
246
245
|
"rootClass": "accordion",
|
|
247
|
-
"maturity": "
|
|
246
|
+
"maturity": "stable",
|
|
248
247
|
"maturityReasons": {
|
|
249
248
|
"hasDemo": true,
|
|
250
249
|
"hasDedicatedGate": true,
|
|
251
250
|
"statesOnDomAttributes": true,
|
|
252
|
-
"behaviorDebt": [
|
|
253
|
-
"roving:gap"
|
|
254
|
-
]
|
|
251
|
+
"behaviorDebt": []
|
|
255
252
|
},
|
|
256
253
|
"gates": [
|
|
257
254
|
"accordion-contract.js"
|
|
@@ -346,14 +343,12 @@
|
|
|
346
343
|
"tier": "pattern",
|
|
347
344
|
"purpose": "侧边抽屉,含遮罩",
|
|
348
345
|
"rootClass": "drawer",
|
|
349
|
-
"maturity": "
|
|
346
|
+
"maturity": "stable",
|
|
350
347
|
"maturityReasons": {
|
|
351
348
|
"hasDemo": true,
|
|
352
349
|
"hasDedicatedGate": true,
|
|
353
350
|
"statesOnDomAttributes": true,
|
|
354
|
-
"behaviorDebt": [
|
|
355
|
-
"dismissable:manual"
|
|
356
|
-
]
|
|
351
|
+
"behaviorDebt": []
|
|
357
352
|
},
|
|
358
353
|
"gates": [
|
|
359
354
|
"drawer-check.js"
|
|
@@ -403,14 +398,12 @@
|
|
|
403
398
|
"tier": "pattern",
|
|
404
399
|
"purpose": "下拉菜单",
|
|
405
400
|
"rootClass": "dd",
|
|
406
|
-
"maturity": "
|
|
401
|
+
"maturity": "stable",
|
|
407
402
|
"maturityReasons": {
|
|
408
403
|
"hasDemo": true,
|
|
409
404
|
"hasDedicatedGate": true,
|
|
410
405
|
"statesOnDomAttributes": true,
|
|
411
|
-
"behaviorDebt": [
|
|
412
|
-
"typeahead:gap"
|
|
413
|
-
]
|
|
406
|
+
"behaviorDebt": []
|
|
414
407
|
},
|
|
415
408
|
"gates": [
|
|
416
409
|
"dropdown-check.js"
|
|
@@ -636,14 +629,12 @@
|
|
|
636
629
|
"tier": "pattern",
|
|
637
630
|
"purpose": "吐司提示与模态遮罩",
|
|
638
631
|
"rootClass": "toast",
|
|
639
|
-
"maturity": "
|
|
632
|
+
"maturity": "stable",
|
|
640
633
|
"maturityReasons": {
|
|
641
634
|
"hasDemo": true,
|
|
642
635
|
"hasDedicatedGate": true,
|
|
643
636
|
"statesOnDomAttributes": true,
|
|
644
|
-
"behaviorDebt": [
|
|
645
|
-
"dismissable:manual"
|
|
646
|
-
]
|
|
637
|
+
"behaviorDebt": []
|
|
647
638
|
},
|
|
648
639
|
"gates": [
|
|
649
640
|
"overlay-check.js",
|
|
@@ -2,6 +2,7 @@ var flDismissable = function (opts) {
|
|
|
2
2
|
var opt = opts || {};
|
|
3
3
|
var escOn = opt.escOn || [];
|
|
4
4
|
var inside = opt.inside || [];
|
|
5
|
+
var outside = opt.outside !== false;
|
|
5
6
|
var when = opt.when || function () { return true; };
|
|
6
7
|
var onDismiss = opt.onDismiss || function () {};
|
|
7
8
|
|
|
@@ -42,7 +43,8 @@ var rec = { when: when, isInside: isInside, onDismiss: onDismiss };
|
|
|
42
43
|
for (var j = 0; j < escOn.length; j++) {
|
|
43
44
|
if (escOn[j]) escOn[j].addEventListener('keydown', onKey);
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
+
|
|
47
|
+
if (outside) shared.stack.push(rec);
|
|
46
48
|
if (!shared.bound) {
|
|
47
49
|
shared.bound = true;
|
|
48
50
|
document.addEventListener('click', onDocClick, true);
|