@featherk/composables 0.1.4 → 0.1.6
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 +38 -18
- package/dist/featherk-composables.es.js +97 -91
- package/dist/featherk-composables.umd.js +1 -1
- package/dist/useGridA11y.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
- The composable expects a Grid ref (a Vue ref to the Grid component).
|
|
23
23
|
- For row-level navigation, disable the Grid's cell-level dynamic tabindex behavior (omit or set `navigatable="false"` on the Grid).
|
|
24
|
-
- The composable returns helpers for keyboard handling, focus management, and sort/filter interactions.
|
|
24
|
+
- The composable returns helpers for keyboard handling, focus management, initialization (new `initA11y()`), and sort/filter interactions.
|
|
25
25
|
|
|
26
26
|
### Styling and the `.fk-grid` class
|
|
27
27
|
|
|
@@ -46,16 +46,42 @@ npm install @featherk/composables
|
|
|
46
46
|
Place inside a `<script setup lang="ts">` block. Provide a Grid ref and call the composable.
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import { ref } from 'vue';
|
|
49
|
+
import { ref, onMounted, watch } from 'vue';
|
|
50
50
|
import { useGridA11y } from '@featherk/composables';
|
|
51
51
|
|
|
52
52
|
const gridRef = ref(null);
|
|
53
|
+
const dataResult = ref({ data: [] }); // example data container
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
activeFilterButton,
|
|
57
|
+
handleGridKeyDown,
|
|
58
|
+
handleSortChange,
|
|
59
|
+
initA11y // NEW: must be called after Grid + data are present in DOM
|
|
60
|
+
} = useGridA11y(gridRef);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2) Initialize accessibility after Grid mounts and data is available
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
Call `initA11y()` once the Grid element is in the DOM and the initial (non-empty) data set is ready. If data loads async, watch it. `initA11y()` performs initial attribute setup, internal bookkeeping, and prepares focus targets.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
onMounted(() => {
|
|
69
|
+
// Adjust source as needed for your data state
|
|
70
|
+
watch(
|
|
71
|
+
() => dataResult.value.data,
|
|
72
|
+
(rows) => {
|
|
73
|
+
if (rows && rows.length && gridRef.value) {
|
|
74
|
+
initA11y(); // safe to call again; will no-op after first successful init
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{ immediate: true }
|
|
78
|
+
);
|
|
79
|
+
});
|
|
56
80
|
```
|
|
57
81
|
|
|
58
|
-
|
|
82
|
+
If data can change from empty to non-empty multiple times, the composable guards against redundant full initialization.
|
|
83
|
+
|
|
84
|
+
### 3) Wire keyboard handler on the Grid
|
|
59
85
|
|
|
60
86
|
Template snippet showing essential bindings (keep other `Grid` props as required by your app):
|
|
61
87
|
|
|
@@ -71,7 +97,7 @@ Template snippet showing essential bindings (keep other `Grid` props as required
|
|
|
71
97
|
/>
|
|
72
98
|
```
|
|
73
99
|
|
|
74
|
-
###
|
|
100
|
+
### 4) Provide an accessible row renderer (`aria-label`)
|
|
75
101
|
|
|
76
102
|
> Not part of `@featherk/composable`, but good practice
|
|
77
103
|
|
|
@@ -80,22 +106,18 @@ Kendo Grid `rowRender` allows you to add an `aria-label` so screen readers annou
|
|
|
80
106
|
```ts
|
|
81
107
|
const renderRow = (h: any, trElement: any, defaultSlots: any, props: any) => {
|
|
82
108
|
const ariaLabel = `Name: ${props.dataItem.name}, Price: ${props.dataItem.price}`;
|
|
83
|
-
// merge existing props and add aria-label
|
|
84
109
|
const merged = { ...trElement.props, 'aria-label': ariaLabel };
|
|
85
110
|
return h('tr', merged, defaultSlots);
|
|
86
111
|
};
|
|
87
112
|
```
|
|
88
113
|
|
|
89
|
-
###
|
|
90
|
-
|
|
91
|
-
The composable returns `activeFilterButton` (a ref) which you can focus after DOM updates.
|
|
114
|
+
### 5) Focus the active filter button after filter changes
|
|
92
115
|
|
|
93
116
|
```ts
|
|
94
117
|
import { nextTick } from 'vue';
|
|
95
118
|
|
|
96
119
|
function onFilterChange(event: any) {
|
|
97
|
-
// update
|
|
98
|
-
|
|
120
|
+
// update filter state + reload data
|
|
99
121
|
nextTick(() => {
|
|
100
122
|
if (activeFilterButton.value) {
|
|
101
123
|
activeFilterButton.value.focus();
|
|
@@ -104,14 +126,11 @@ function onFilterChange(event: any) {
|
|
|
104
126
|
}
|
|
105
127
|
```
|
|
106
128
|
|
|
107
|
-
###
|
|
108
|
-
|
|
109
|
-
If you need to show a loader or call an API, pass a custom callback into the composable's sort helper so the composable does its internal work and your app performs side effects.
|
|
129
|
+
### 6) Custom sort handling with composable helper
|
|
110
130
|
|
|
111
131
|
```ts
|
|
112
132
|
const optionalCustomSort = (event: any) => {
|
|
113
133
|
loader.value = true;
|
|
114
|
-
// example async
|
|
115
134
|
setTimeout(() => {
|
|
116
135
|
loader.value = false;
|
|
117
136
|
// apply sort state and reload data
|
|
@@ -123,11 +142,12 @@ function onSortChange(event: any) {
|
|
|
123
142
|
}
|
|
124
143
|
```
|
|
125
144
|
|
|
126
|
-
###
|
|
145
|
+
### 7) Summary checklist
|
|
127
146
|
|
|
128
147
|
- Import and call `useGridA11y(gridRef)`
|
|
148
|
+
- Wait for Grid mount + data, then call `initA11y()`
|
|
129
149
|
- Bind returned keyboard handler to `Grid` `@keydown`
|
|
130
|
-
- Bind returned sort handler to `Grid` `@sortchange` (
|
|
150
|
+
- Bind returned sort handler to `Grid` `@sortchange` (optionally pass a custom callback)
|
|
131
151
|
- Use returned `activeFilterButton` to manage focus after filter updates
|
|
132
152
|
- Provide a `rowRender` that adds a descriptive `aria-label` for each row
|
|
133
153
|
- Set `navigatable="false"` on the `Grid` to prefer row-level navigation
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ref as
|
|
2
|
-
const
|
|
3
|
-
const s =
|
|
4
|
-
let
|
|
5
|
-
const b = [],
|
|
1
|
+
import { ref as x, onBeforeUnmount as N, nextTick as k, onMounted as G, onUnmounted as P } from "vue";
|
|
2
|
+
const O = (m) => {
|
|
3
|
+
const s = x(null);
|
|
4
|
+
let p = !1, u = null, f = null;
|
|
5
|
+
const b = [], w = ".k-table-row[data-grid-row-index] [tabindex]", A = () => m?.value?.columns, h = (t) => {
|
|
6
6
|
const o = t.key || t.code;
|
|
7
7
|
[" ", "Spacebar", "Space", "Enter"].includes(o) && (t.preventDefault(), t.stopPropagation(), s.value = t.target, t.target.click());
|
|
8
8
|
}, g = (t) => {
|
|
@@ -43,15 +43,15 @@ const G = (f) => {
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
t.code === "Tab" && (t.preventDefault(), t.stopPropagation(), t.shiftKey ? (s.value?.previousElementSibling).focus() : (s.value?.nextElementSibling).focus());
|
|
46
|
-
},
|
|
47
|
-
|
|
46
|
+
}, L = () => {
|
|
47
|
+
u = new MutationObserver((t) => {
|
|
48
48
|
t.forEach((o) => {
|
|
49
49
|
o.addedNodes.forEach((e) => {
|
|
50
50
|
if (e.nodeType === Node.ELEMENT_NODE) {
|
|
51
51
|
const r = e;
|
|
52
52
|
if (r.classList.contains("k-animation-container")) {
|
|
53
53
|
const a = s.value;
|
|
54
|
-
a && (a.dataset.featherKSortable === "true" ||
|
|
54
|
+
a && (a.dataset.featherKSortable === "true" || k(() => {
|
|
55
55
|
r.querySelectorAll(
|
|
56
56
|
".k-columnmenu-item-wrapper"
|
|
57
57
|
).forEach((d) => {
|
|
@@ -60,7 +60,7 @@ const G = (f) => {
|
|
|
60
60
|
})), r.addEventListener(
|
|
61
61
|
"keydown",
|
|
62
62
|
g
|
|
63
|
-
),
|
|
63
|
+
), k(() => {
|
|
64
64
|
const i = () => {
|
|
65
65
|
const l = Array.from(
|
|
66
66
|
r.querySelectorAll(
|
|
@@ -104,14 +104,13 @@ const G = (f) => {
|
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
|
-
}),
|
|
107
|
+
}), u.observe(document.body, {
|
|
108
108
|
childList: !0,
|
|
109
109
|
subtree: !0
|
|
110
110
|
});
|
|
111
|
-
},
|
|
111
|
+
}, D = (t) => {
|
|
112
112
|
if (!t.type || !t)
|
|
113
113
|
return;
|
|
114
|
-
console.log("handleGridKeyDown", t, t.code);
|
|
115
114
|
const o = t.target;
|
|
116
115
|
if (o) {
|
|
117
116
|
if (t.code === "Escape") {
|
|
@@ -120,7 +119,7 @@ const G = (f) => {
|
|
|
120
119
|
t.preventDefault(), t.stopPropagation();
|
|
121
120
|
try {
|
|
122
121
|
Array.from(
|
|
123
|
-
|
|
122
|
+
m?.value.$el.querySelectorAll(
|
|
124
123
|
".k-table-row[data-grid-row-index]"
|
|
125
124
|
)
|
|
126
125
|
).forEach((n) => n.setAttribute("tabindex", "-1"));
|
|
@@ -166,7 +165,7 @@ const G = (f) => {
|
|
|
166
165
|
if (["ArrowLeft", "ArrowRight"].includes(t.code)) {
|
|
167
166
|
t.preventDefault();
|
|
168
167
|
const r = e.querySelectorAll(
|
|
169
|
-
|
|
168
|
+
w
|
|
170
169
|
);
|
|
171
170
|
if (r.length === 0) return;
|
|
172
171
|
let n = Array.from(r).findIndex(
|
|
@@ -182,68 +181,69 @@ const G = (f) => {
|
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
183
|
}
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
const t =
|
|
184
|
+
}, q = () => {
|
|
185
|
+
k(() => {
|
|
186
|
+
const t = m.value.$el.closest(".k-grid");
|
|
188
187
|
t && t.classList.add("fk-grid");
|
|
189
188
|
});
|
|
190
|
-
}, q = () => {
|
|
191
|
-
try {
|
|
192
|
-
const t = () => {
|
|
193
|
-
try {
|
|
194
|
-
const r = Array.from(
|
|
195
|
-
f.value.$el.querySelectorAll(
|
|
196
|
-
".k-table-row[data-grid-row-index]"
|
|
197
|
-
)
|
|
198
|
-
);
|
|
199
|
-
if (!r || r.length === 0 || r.filter(
|
|
200
|
-
(i) => i.getAttribute("tabindex") === "0"
|
|
201
|
-
).length === 1) return;
|
|
202
|
-
const a = r.find(
|
|
203
|
-
(i) => i === document.activeElement || i.contains(document.activeElement)
|
|
204
|
-
);
|
|
205
|
-
if (r.forEach((i) => i.setAttribute("tabindex", "-1")), a) {
|
|
206
|
-
a.setAttribute("tabindex", "0");
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
r[0].setAttribute("tabindex", "0");
|
|
210
|
-
} catch (r) {
|
|
211
|
-
console.error("ensureSingleTabindex error:", r);
|
|
212
|
-
}
|
|
213
|
-
}, o = Array.from(
|
|
214
|
-
f.value.$el.querySelectorAll(
|
|
215
|
-
".k-table-row[data-grid-row-index]"
|
|
216
|
-
)
|
|
217
|
-
);
|
|
218
|
-
o.length > 0 && o.forEach((r, n) => {
|
|
219
|
-
r.setAttribute("tabindex", n === 0 ? "0" : "-1");
|
|
220
|
-
});
|
|
221
|
-
const e = f.value.$el.querySelector(".k-table-tbody");
|
|
222
|
-
e && (u = new MutationObserver(() => {
|
|
223
|
-
t();
|
|
224
|
-
}), u.observe(e, { childList: !0, subtree: !0 }));
|
|
225
|
-
} catch (t) {
|
|
226
|
-
console.error("Error setting up row navigation:", t);
|
|
227
|
-
}
|
|
228
189
|
}, T = () => {
|
|
229
|
-
|
|
190
|
+
if (!f)
|
|
191
|
+
try {
|
|
192
|
+
const t = () => {
|
|
193
|
+
try {
|
|
194
|
+
const r = Array.from(
|
|
195
|
+
m.value.$el.querySelectorAll(
|
|
196
|
+
".k-table-row[data-grid-row-index]"
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
if (!r || r.length === 0 || r.filter(
|
|
200
|
+
(i) => i.getAttribute("tabindex") === "0"
|
|
201
|
+
).length === 1) return;
|
|
202
|
+
const a = r.find(
|
|
203
|
+
(i) => i === document.activeElement || i.contains(document.activeElement)
|
|
204
|
+
);
|
|
205
|
+
if (r.forEach((i) => i.setAttribute("tabindex", "-1")), a) {
|
|
206
|
+
a.setAttribute("tabindex", "0");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
r[0].setAttribute("tabindex", "0");
|
|
210
|
+
} catch (r) {
|
|
211
|
+
console.error("ensureSingleTabindex error:", r);
|
|
212
|
+
}
|
|
213
|
+
}, o = Array.from(
|
|
214
|
+
m.value.$el.querySelectorAll(
|
|
215
|
+
".k-table-row[data-grid-row-index]"
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
o.length > 0 && o.forEach((r, n) => {
|
|
219
|
+
r.setAttribute("tabindex", n === 0 ? "0" : "-1");
|
|
220
|
+
});
|
|
221
|
+
const e = m.value.$el.querySelector(".k-table-tbody");
|
|
222
|
+
e && (f = new MutationObserver(() => {
|
|
223
|
+
t();
|
|
224
|
+
}), f.observe(e, { childList: !0, subtree: !0 }));
|
|
225
|
+
} catch (t) {
|
|
226
|
+
console.error("Error setting up row navigation:", t);
|
|
227
|
+
}
|
|
228
|
+
}, I = () => {
|
|
229
|
+
k(() => {
|
|
230
230
|
const o = document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");
|
|
231
231
|
o && o.forEach((e) => {
|
|
232
232
|
e.setAttribute("role", "button"), e.addEventListener(
|
|
233
233
|
"keydown",
|
|
234
234
|
h
|
|
235
235
|
);
|
|
236
|
-
}),
|
|
236
|
+
}), L(), C();
|
|
237
237
|
});
|
|
238
|
-
},
|
|
238
|
+
}, E = () => {
|
|
239
239
|
const o = document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");
|
|
240
240
|
o && o.forEach((e) => {
|
|
241
241
|
e.removeEventListener(
|
|
242
242
|
"keydown",
|
|
243
243
|
h
|
|
244
244
|
);
|
|
245
|
-
}),
|
|
246
|
-
},
|
|
245
|
+
}), u && (u.disconnect(), u = null), f && (f.disconnect(), f = null), b.forEach((e) => e()), b.length = 0;
|
|
246
|
+
}, C = () => {
|
|
247
247
|
document.querySelectorAll(
|
|
248
248
|
".k-grid-header .k-table-thead th"
|
|
249
249
|
).forEach((e, r) => {
|
|
@@ -251,7 +251,7 @@ const G = (f) => {
|
|
|
251
251
|
".k-grid-header-menu.k-grid-column-menu"
|
|
252
252
|
);
|
|
253
253
|
if (!n) return;
|
|
254
|
-
const a =
|
|
254
|
+
const a = A();
|
|
255
255
|
if (a && a[r]) {
|
|
256
256
|
const c = a[r].field ?? "";
|
|
257
257
|
e.setAttribute("data-feather-k-field", c), e.setAttribute(
|
|
@@ -271,7 +271,7 @@ const G = (f) => {
|
|
|
271
271
|
s.value = e, c.preventDefault(), c.stopPropagation(), d(c);
|
|
272
272
|
else if (l) {
|
|
273
273
|
s.value = e;
|
|
274
|
-
const
|
|
274
|
+
const y = new KeyboardEvent("keydown", {
|
|
275
275
|
key: "Enter",
|
|
276
276
|
code: "Enter",
|
|
277
277
|
keyCode: 13,
|
|
@@ -279,24 +279,24 @@ const G = (f) => {
|
|
|
279
279
|
bubbles: !0,
|
|
280
280
|
cancelable: !0
|
|
281
281
|
});
|
|
282
|
-
e.dispatchEvent(
|
|
282
|
+
e.dispatchEvent(y);
|
|
283
283
|
}
|
|
284
284
|
};
|
|
285
285
|
e.addEventListener("click", v), b.push(() => {
|
|
286
286
|
e.removeEventListener("click", v);
|
|
287
287
|
});
|
|
288
|
-
const
|
|
288
|
+
const S = (c) => {
|
|
289
289
|
if ((c.code === "Enter" || c.code === "Space") && (i || l)) {
|
|
290
290
|
if (s.value = e, s.value.focus(), i)
|
|
291
291
|
c.preventDefault(), c.stopPropagation(), d(c);
|
|
292
292
|
else if (l) {
|
|
293
|
-
const
|
|
294
|
-
|
|
293
|
+
const y = e.querySelector(".k-link");
|
|
294
|
+
y && y.click();
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
};
|
|
298
|
-
e.addEventListener("keydown",
|
|
299
|
-
e.removeEventListener("keydown",
|
|
298
|
+
e.addEventListener("keydown", S, !0), b.push(() => {
|
|
299
|
+
e.removeEventListener("keydown", S, !0);
|
|
300
300
|
});
|
|
301
301
|
});
|
|
302
302
|
const o = document.querySelector(".k-grid-header .k-table-thead");
|
|
@@ -319,7 +319,7 @@ const G = (f) => {
|
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
321
|
}, K = function(t, o) {
|
|
322
|
-
const e = t?.event.event.target, r =
|
|
322
|
+
const e = t?.event.event.target, r = A();
|
|
323
323
|
if (!e || !r) return;
|
|
324
324
|
const n = e.classList.contains("k-link"), a = e.classList.contains("k-columnmenu-item"), i = r.find((l) => l.field === t.event.field)?.sortable && !0;
|
|
325
325
|
if (!n) {
|
|
@@ -329,45 +329,51 @@ const G = (f) => {
|
|
|
329
329
|
);
|
|
330
330
|
return;
|
|
331
331
|
}
|
|
332
|
-
typeof o == "function" &&
|
|
332
|
+
typeof o == "function" && k(() => {
|
|
333
333
|
s.value && s.value.focus(), o(t);
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
|
+
}, M = () => {
|
|
337
|
+
if (!p)
|
|
338
|
+
try {
|
|
339
|
+
q(), T(), I(), p = !0;
|
|
340
|
+
} catch (t) {
|
|
341
|
+
console.error("initA11y failed:", t), E();
|
|
342
|
+
}
|
|
336
343
|
};
|
|
337
|
-
return
|
|
338
|
-
|
|
339
|
-
}), M(() => {
|
|
340
|
-
C();
|
|
344
|
+
return N(() => {
|
|
345
|
+
E();
|
|
341
346
|
}), {
|
|
342
347
|
activeFilterButton: s,
|
|
343
|
-
handleGridKeyDown:
|
|
344
|
-
handleSortChange: K
|
|
348
|
+
handleGridKeyDown: D,
|
|
349
|
+
handleSortChange: K,
|
|
350
|
+
initA11y: M
|
|
345
351
|
};
|
|
346
352
|
};
|
|
347
|
-
function
|
|
348
|
-
const { activeByDefault: s = !1, autoActivateDelay:
|
|
349
|
-
function
|
|
353
|
+
function B(m = {}) {
|
|
354
|
+
const { activeByDefault: s = !1, autoActivateDelay: p = 0 } = m, u = x(s);
|
|
355
|
+
function f() {
|
|
350
356
|
u.value = !0;
|
|
351
357
|
}
|
|
352
|
-
function
|
|
358
|
+
function b() {
|
|
353
359
|
u.value = !1;
|
|
354
360
|
}
|
|
355
|
-
function
|
|
361
|
+
function w() {
|
|
356
362
|
u.value = !u.value;
|
|
357
363
|
}
|
|
358
|
-
return
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
},
|
|
362
|
-
}),
|
|
364
|
+
return G(() => {
|
|
365
|
+
p > 0 && !s && setTimeout(() => {
|
|
366
|
+
f();
|
|
367
|
+
}, p);
|
|
368
|
+
}), P(() => {
|
|
363
369
|
}), {
|
|
364
370
|
isGridActive: u,
|
|
365
|
-
activateGrid:
|
|
366
|
-
deactivateGrid:
|
|
367
|
-
toggleGrid:
|
|
371
|
+
activateGrid: f,
|
|
372
|
+
deactivateGrid: b,
|
|
373
|
+
toggleGrid: w
|
|
368
374
|
};
|
|
369
375
|
}
|
|
370
376
|
export {
|
|
371
|
-
|
|
372
|
-
|
|
377
|
+
O as useGridA11y,
|
|
378
|
+
B as useGridComposableEx
|
|
373
379
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(b,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],c):(b=typeof globalThis<"u"?globalThis:b||self,c(b.FeatherKComposables={},b.Vue))})(this,(function(b,c){"use strict";const L=p=>{const s=c.ref(null);let g=!1,d=null,m=null;const k=[],h=".k-table-row[data-grid-row-index] [tabindex]",v=()=>p?.value?.columns,E=t=>{const o=t.key||t.code;[" ","Spacebar","Space","Enter"].includes(o)&&(t.preventDefault(),t.stopPropagation(),s.value=t.target,t.target.click())},y=t=>{if(!s.value)return;if(t.code==="Escape"){t.preventDefault(),t.stopPropagation(),s.value&&s.value.focus();return}const o=Array.from(document.querySelectorAll(".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item")),e=document.querySelector(".k-filter-menu-container");if(e){if(t.code==="Tab"){const r=[".k-filter-menu-container .k-dropdownlist[tabindex='0']",".k-filter-menu-container input.k-input-inner:not([tabindex='-1']):not([disabled])",".k-filter-menu-container button:not([tabindex='-1']):not([disabled])"],n=Array.from(e.querySelectorAll(r.join(",")));if(n.length===0)return;const a=n.findIndex(l=>l===document.activeElement);let i;a===-1?i=0:t.shiftKey?i=(a-1+n.length)%n.length:i=(a+1)%n.length,t.preventDefault(),t.stopPropagation(),n[i]?.focus();return}}else if(t.code==="ArrowUp"||t.code==="ArrowDown"){t.preventDefault(),t.stopPropagation();const r=o.findIndex(a=>a===document.activeElement);let n=r;t.code==="ArrowUp"?n=r>0?r-1:o.length-1:t.code==="ArrowDown"&&(n=r<o.length-1?r+1:0),o[n]?.focus();return}t.code==="Tab"&&(t.preventDefault(),t.stopPropagation(),t.shiftKey?(s.value?.previousElementSibling).focus():(s.value?.nextElementSibling).focus())},D=()=>{d=new MutationObserver(t=>{t.forEach(o=>{o.addedNodes.forEach(e=>{if(e.nodeType===Node.ELEMENT_NODE){const r=e;if(r.classList.contains("k-animation-container")){const a=s.value;a&&(a.dataset.featherKSortable==="true"||c.nextTick(()=>{r.querySelectorAll(".k-columnmenu-item-wrapper").forEach(f=>{f.textContent?.toLowerCase().includes("sort")&&f.remove()})})),r.addEventListener("keydown",y),c.nextTick(()=>{const i=()=>{const l=Array.from(r.querySelectorAll(".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item"));if(l.length===1)l[0].focus(),l[0].click(),i.attempts=0;else if(l.length>1){i.attempts=0;return}else i.attempts===void 0&&(i.attempts=0),i.attempts++<3&&setTimeout(i,200)};i()})}r.querySelectorAll(".k-animation-container").forEach(a=>{a.addEventListener("keydown",y)})}}),o.removedNodes.forEach(e=>{if(e.nodeType===Node.ELEMENT_NODE){const r=e;r.classList.contains("k-animation-container")&&r.removeEventListener("keydown",y),r.querySelectorAll(".k-animation-container").forEach(a=>{a.removeEventListener("keydown",y)})}})})}),d.observe(document.body,{childList:!0,subtree:!0})},q=t=>{if(!t.type||!t)return;const o=t.target;if(o){if(t.code==="Escape"){const e=document.activeElement?.closest(".k-table-row[data-grid-row-index]");if(e){t.preventDefault(),t.stopPropagation();try{Array.from(p?.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]")).forEach(n=>n.setAttribute("tabindex","-1"))}catch{}e.setAttribute("tabindex","0"),e.focus();return}}if(["ArrowDown","ArrowLeft","ArrowRight","ArrowUp","Enter","Space"].includes(t.code)){if(t.preventDefault(),o.classList.contains("k-grid-header-menu")&&o.classList.contains("k-grid-column-menu")){s.value=o;return}const e=o.closest(".k-table-row[data-grid-row-index]");if(e){if(["ArrowDown","ArrowUp"].includes(t.code)){const r=(a,i)=>{let l=i==="next"?a.nextElementSibling:a.previousElementSibling;for(;l;){const f=l;try{if(f.hasAttribute&&f.classList.contains("k-table-row"))return f}catch{}l=i==="next"?l.nextElementSibling:l.previousElementSibling}return null},n=t.code==="ArrowDown"?r(e,"next"):r(e,"previous");n&&(e.setAttribute("tabindex","-1"),n.setAttribute("tabindex","0"),n.focus());return}if(["ArrowLeft","ArrowRight"].includes(t.code)){t.preventDefault();const r=e.querySelectorAll(h);if(r.length===0)return;let n=Array.from(r).findIndex(a=>a===document.activeElement);if(n===-1&&document.activeElement===e){r[0].focus();return}t.code==="ArrowRight"?n=n===r.length-1?0:n+1:t.code==="ArrowLeft"&&(n=n===r.length-1?n-1:r.length-1),r[n].focus();return}}}}},C=()=>{c.nextTick(()=>{const t=p.value.$el.closest(".k-grid");t&&t.classList.add("fk-grid")})},I=()=>{if(!m)try{const t=()=>{try{const r=Array.from(p.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]"));if(!r||r.length===0||r.filter(i=>i.getAttribute("tabindex")==="0").length===1)return;const a=r.find(i=>i===document.activeElement||i.contains(document.activeElement));if(r.forEach(i=>i.setAttribute("tabindex","-1")),a){a.setAttribute("tabindex","0");return}r[0].setAttribute("tabindex","0")}catch(r){console.error("ensureSingleTabindex error:",r)}},o=Array.from(p.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]"));o.length>0&&o.forEach((r,n)=>{r.setAttribute("tabindex",n===0?"0":"-1")});const e=p.value.$el.querySelector(".k-table-tbody");e&&(m=new MutationObserver(()=>{t()}),m.observe(e,{childList:!0,subtree:!0}))}catch(t){console.error("Error setting up row navigation:",t)}},K=()=>{c.nextTick(()=>{const o=document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");o&&o.forEach(e=>{e.setAttribute("role","button"),e.addEventListener("keydown",E)}),D(),M()})},S=()=>{const o=document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");o&&o.forEach(e=>{e.removeEventListener("keydown",E)}),d&&(d.disconnect(),d=null),m&&(m.disconnect(),m=null),k.forEach(e=>e()),k.length=0},M=()=>{document.querySelectorAll(".k-grid-header .k-table-thead th").forEach((e,r)=>{const n=e.querySelector(".k-grid-header-menu.k-grid-column-menu");if(!n)return;const a=v();if(a&&a[r]){const u=a[r].field??"";e.setAttribute("data-feather-k-field",u),e.setAttribute("data-feather-k-filterable",a[r].filterable===!1?"false":"true"),e.setAttribute("data-feather-k-sortable",a[r].sortable===!1?"false":"true")}const i=e.dataset.featherKFilterable!=="false",l=e.dataset.featherKSortable!=="false";n.setAttribute("tabindex","-1"),i||l?e.style.cursor="pointer":e.style.cursor="default",i?(e.setAttribute("tabindex","0"),e.setAttribute("role","button"),e.setAttribute("aria-haspopup","menu")):(e.setAttribute("tabindex","0"),e.setAttribute("role","columnheader"),e.removeAttribute("aria-haspopup"));const f=u=>{u.target?.closest(".k-column-resizer")||(s.value=e,n.click())},A=u=>{if(i)s.value=e,u.preventDefault(),u.stopPropagation(),f(u);else if(l){s.value=e;const w=new KeyboardEvent("keydown",{key:"Enter",code:"Enter",keyCode:13,which:13,bubbles:!0,cancelable:!0});e.dispatchEvent(w)}};e.addEventListener("click",A),k.push(()=>{e.removeEventListener("click",A)});const x=u=>{if((u.code==="Enter"||u.code==="Space")&&(i||l)){if(s.value=e,s.value.focus(),i)u.preventDefault(),u.stopPropagation(),f(u);else if(l){const w=e.querySelector(".k-link");w&&w.click()}}};e.addEventListener("keydown",x,!0),k.push(()=>{e.removeEventListener("keydown",x,!0)})});const o=document.querySelector(".k-grid-header .k-table-thead");if(o){const e=r=>{const n=r.target.closest("th");n&&(r.code==="Enter"||r.code==="Space")&&n.dataset.featherKFilterable==="false"&&n.dataset.featherKSortable==="false"&&(r.preventDefault(),r.stopImmediatePropagation())};o.addEventListener("keydown",e,!0),k.push(()=>{o.removeEventListener("keydown",e,!0)})}},G=function(t,o){const e=t?.event.event.target,r=v();if(!e||!r)return;const n=e.classList.contains("k-link"),a=e.classList.contains("k-columnmenu-item"),i=r.find(l=>l.field===t.event.field)?.sortable&&!0;if(!n){if(a&&!i){(t.event.sort&&void 0)?.filter(f=>f.field!==t.event.field);return}typeof o=="function"&&c.nextTick(()=>{s.value&&s.value.focus(),o(t)})}},N=()=>{if(!g)try{C(),I(),K(),g=!0}catch(t){console.error("initA11y failed:",t),S()}};return c.onBeforeUnmount(()=>{S()}),{activeFilterButton:s,handleGridKeyDown:q,handleSortChange:G,initA11y:N}};function T(p={}){const{activeByDefault:s=!1,autoActivateDelay:g=0}=p,d=c.ref(s);function m(){d.value=!0}function k(){d.value=!1}function h(){d.value=!d.value}return c.onMounted(()=>{g>0&&!s&&setTimeout(()=>{m()},g)}),c.onUnmounted(()=>{}),{isGridActive:d,activateGrid:m,deactivateGrid:k,toggleGrid:h}}b.useGridA11y=L,b.useGridComposableEx=T,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/useGridA11y.d.ts
CHANGED
|
@@ -10,4 +10,5 @@ export declare const useGridA11y: (gridRef: Ref<any>) => {
|
|
|
10
10
|
activeFilterButton: Ref<HTMLElement, HTMLElement>;
|
|
11
11
|
handleGridKeyDown: (e: KeyboardEvent) => void;
|
|
12
12
|
handleSortChange: (event?: GridSortChangeEvent, customHandler?: any) => void;
|
|
13
|
+
initA11y: () => void;
|
|
13
14
|
};
|