@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.
@@ -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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
  }