@apollo-design/a11y 0.0.0-stage → 0.1.9
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/README.md +113 -2
- package/dist/combobox.mjs +37 -0
- package/dist/focus-restore.mjs +86 -0
- package/dist/index.d.mts +455 -0
- package/dist/index.d.ts +455 -0
- package/dist/index.mjs +8 -0
- package/dist/live-region.mjs +87 -0
- package/dist/roving.mjs +54 -0
- package/dist/use-active-descendant.mjs +24 -0
- package/dist/use-roving-focus.mjs +97 -0
- package/dist/use-typeahead.mjs +41 -0
- package/package.json +32 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useControlledValue } from '@apollo-design/utils';
|
|
2
|
+
import { toValue, computed, watch } from 'vue';
|
|
3
|
+
import { NO_ACTIVE_INDEX, resolveRovingEnd, resolveRovingHome, getRovingTabIndex, getRovingOffset, moveRovingIndex } from './roving.mjs';
|
|
4
|
+
|
|
5
|
+
function useRovingFocus(options) {
|
|
6
|
+
const getCount = () => Math.max(0, Math.trunc(toValue(options.count)));
|
|
7
|
+
const normalizeIndex = (index) => {
|
|
8
|
+
const count = getCount();
|
|
9
|
+
return count === 0 || index === NO_ACTIVE_INDEX ? NO_ACTIVE_INDEX : Math.min(Math.max(index, 0), count - 1);
|
|
10
|
+
};
|
|
11
|
+
const [state, setState] = useControlledValue({
|
|
12
|
+
getValue: () => toValue(options.activeIndex),
|
|
13
|
+
defaultValue: NO_ACTIVE_INDEX,
|
|
14
|
+
onChange: (index) => options.onChange?.(index)
|
|
15
|
+
});
|
|
16
|
+
const activeIndex = computed({
|
|
17
|
+
get: () => normalizeIndex(state.value),
|
|
18
|
+
set: (index) => setActive(index)
|
|
19
|
+
});
|
|
20
|
+
let pendingFocus;
|
|
21
|
+
const focusAcceptedItem = () => {
|
|
22
|
+
if (pendingFocus === void 0 || pendingFocus !== activeIndex.value) return;
|
|
23
|
+
if (pendingFocus === NO_ACTIVE_INDEX || toValue(options.focusOnMove) === false) {
|
|
24
|
+
pendingFocus = void 0;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const item = options.getItem?.(pendingFocus);
|
|
28
|
+
if (item) {
|
|
29
|
+
pendingFocus = void 0;
|
|
30
|
+
item.focus();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function setActive(index, focus = true) {
|
|
34
|
+
const next = normalizeIndex(index);
|
|
35
|
+
pendingFocus = focus && toValue(options.focusOnMove) !== false ? next : void 0;
|
|
36
|
+
setState(next);
|
|
37
|
+
focusAcceptedItem();
|
|
38
|
+
}
|
|
39
|
+
watch(
|
|
40
|
+
activeIndex,
|
|
41
|
+
(index) => {
|
|
42
|
+
if (pendingFocus !== void 0 && pendingFocus !== index) pendingFocus = void 0;
|
|
43
|
+
focusAcceptedItem();
|
|
44
|
+
},
|
|
45
|
+
{ flush: "post" }
|
|
46
|
+
);
|
|
47
|
+
watch(
|
|
48
|
+
getCount,
|
|
49
|
+
() => {
|
|
50
|
+
pendingFocus = void 0;
|
|
51
|
+
const next = normalizeIndex(state.value);
|
|
52
|
+
if (next !== state.value) setState(next);
|
|
53
|
+
},
|
|
54
|
+
{ flush: "sync" }
|
|
55
|
+
);
|
|
56
|
+
const move = (offset) => {
|
|
57
|
+
if (offset === 0) return;
|
|
58
|
+
setActive(
|
|
59
|
+
moveRovingIndex(activeIndex.value, offset, getCount(), toValue(options.loop) ?? true)
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
const onKeyDown = (event) => {
|
|
63
|
+
if (event.defaultPrevented || event.isComposing || getCount() === 0) return false;
|
|
64
|
+
if (event.key === "Home") {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
setActive(resolveRovingHome(getCount()));
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (event.key === "End") {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
setActive(resolveRovingEnd(getCount()));
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
const offset = getRovingOffset(
|
|
75
|
+
toValue(options.orientation) ?? "vertical",
|
|
76
|
+
toValue(options.rtl) ?? false,
|
|
77
|
+
event.key
|
|
78
|
+
);
|
|
79
|
+
if (offset === null) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
move(offset);
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
activeIndex,
|
|
88
|
+
getTabIndex: (index) => getRovingTabIndex(activeIndex.value, index),
|
|
89
|
+
setActive,
|
|
90
|
+
move,
|
|
91
|
+
moveHome: () => setActive(resolveRovingHome(getCount())),
|
|
92
|
+
moveEnd: () => setActive(resolveRovingEnd(getCount())),
|
|
93
|
+
onKeyDown
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { useRovingFocus };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ref, toValue } from 'vue';
|
|
2
|
+
import { isTypeaheadKey, pushTypeaheadChar, INITIAL_TYPEAHEAD_STATE, findTypeaheadIndex } from './combobox.mjs';
|
|
3
|
+
import { NO_ACTIVE_INDEX } from './roving.mjs';
|
|
4
|
+
|
|
5
|
+
function useTypeahead(options) {
|
|
6
|
+
let state = INITIAL_TYPEAHEAD_STATE;
|
|
7
|
+
const buffer = ref("");
|
|
8
|
+
const reset = () => {
|
|
9
|
+
state = INITIAL_TYPEAHEAD_STATE;
|
|
10
|
+
buffer.value = "";
|
|
11
|
+
};
|
|
12
|
+
const onKeyDown = (event) => {
|
|
13
|
+
if (!isTypeaheadKey(event)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const labels = toValue(options.labels);
|
|
17
|
+
const from = toValue(options.activeIndex) ?? NO_ACTIVE_INDEX;
|
|
18
|
+
const accumulated = pushTypeaheadChar(
|
|
19
|
+
state,
|
|
20
|
+
event.key,
|
|
21
|
+
(options.now ?? Date.now)(),
|
|
22
|
+
options.resetDelay
|
|
23
|
+
);
|
|
24
|
+
let index = findTypeaheadIndex(labels, accumulated.buffer, from);
|
|
25
|
+
if (index === NO_ACTIVE_INDEX) {
|
|
26
|
+
index = findTypeaheadIndex(labels, event.key, from);
|
|
27
|
+
state = index === NO_ACTIVE_INDEX ? accumulated : { buffer: event.key, lastAt: accumulated.lastAt };
|
|
28
|
+
} else {
|
|
29
|
+
state = accumulated;
|
|
30
|
+
}
|
|
31
|
+
buffer.value = state.buffer;
|
|
32
|
+
if (index === NO_ACTIVE_INDEX) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
options.onMatch?.(index);
|
|
36
|
+
return true;
|
|
37
|
+
};
|
|
38
|
+
return { buffer, onKeyDown, reset };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { useTypeahead };
|
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollo-design/a11y",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "运行时无障碍原语。antd 把焦点管理、roving tabindex、live region 等散落在各组件与 rc 包里,本项目把它们收敛为可复用的原语。",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.mjs",
|
|
9
|
+
"module": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@apollo-design/utils": "0.1.9"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"vue": "^3.5.42"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@apollo-design/test-utils": "0.1.9"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "unbuild --config ../../scripts/unbuild-preserve-modules.mjs",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"lint": "vue-tsc --noEmit"
|
|
34
|
+
}
|
|
6
35
|
}
|