@featherk/composables 0.0.4 → 0.0.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 +124 -0
- package/dist/featherk-composables.es.js +154 -142
- package/dist/featherk-composables.umd.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/types/kendo.d.ts +1 -1
- package/package.json +2 -4
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
|
|
2
|
+
# useGridA11y — Integration Quick Reference
|
|
3
|
+
|
|
4
|
+
## BIG DISCLAIMER
|
|
5
|
+
|
|
6
|
+
> This package is experimental and NOT READY FOR PRODUCTION. Do not use this package in production environments. The API, types, and build output are unstable. Tests and documentation are incomplete. You may encounter breaking changes, missing features, or rough edges. Use only for local experimentation or as a reference.
|
|
7
|
+
|
|
8
|
+
## Compatibility
|
|
9
|
+
|
|
10
|
+
> The current version of `useGridA11y` composable was developed targeting Kendo UI for Vue version 6.4.1.
|
|
11
|
+
>
|
|
12
|
+
> *This library is not affiliated with or approved by Telerik.*
|
|
13
|
+
|
|
14
|
+
## Short description
|
|
15
|
+
|
|
16
|
+
`@featherk/composables` provides small Vue 3 composables intended to augment and improve accessibility and behavior for Kendo UI components (Telerik). The library is developed alongside a demo app that shows usage patterns and integration points.
|
|
17
|
+
|
|
18
|
+
### Notes
|
|
19
|
+
|
|
20
|
+
- The composable expects a Grid ref (a Vue ref to the Grid component).
|
|
21
|
+
- For row-level navigation, disable the Grid's cell-level dynamic tabindex behavior (omit or set `navigatable="false"` on the Grid).
|
|
22
|
+
- The composable returns helpers for keyboard handling, focus management, and sort/filter interactions.
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
- Vue 3 (script setup)
|
|
27
|
+
- `@progress/kendo-vue-grid` installed
|
|
28
|
+
- `@featherk/composables` installed
|
|
29
|
+
|
|
30
|
+
Install (if needed)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @featherk/composables
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 1) Minimal import + setup
|
|
37
|
+
|
|
38
|
+
Place inside a `<script setup lang="ts">` block. Provide a Grid ref and call the composable.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { ref } from 'vue';
|
|
42
|
+
import { useGridA11y } from '@featherk/composables';
|
|
43
|
+
|
|
44
|
+
const gridRef = ref(null);
|
|
45
|
+
|
|
46
|
+
const { activeFilterButton, handleGridKeyDown, handleSortChange } = useGridA11y(gridRef);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 2) Wire keyboard handler on the Grid
|
|
50
|
+
|
|
51
|
+
Template snippet showing essential bindings (keep other Grid props as required by your app):
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<Grid
|
|
55
|
+
ref="gridRef"
|
|
56
|
+
:dataItems="dataResult.data"
|
|
57
|
+
:dataItemKey="'id'"
|
|
58
|
+
:rowRender="renderRow"
|
|
59
|
+
@keydown="handleGridKeyDown" <!-- keyboard navigation -->
|
|
60
|
+
@sortchange="handleSortChange" <!-- composable-aware sort handling -->
|
|
61
|
+
navigatable="false" <!-- optional: prefer row-level nav -->
|
|
62
|
+
/>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 3) Provide an accessible row renderer (`aria-label`)
|
|
66
|
+
|
|
67
|
+
> Not part of `@featherk/composable`, but good practice
|
|
68
|
+
|
|
69
|
+
Kendo Grid `rowRender` allows you to add an `aria-label` so screen readers announce row contents.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const renderRow = (h: any, trElement: any, defaultSlots: any, props: any) => {
|
|
73
|
+
const ariaLabel = `Name: ${props.dataItem.name}, Price: ${props.dataItem.price}`;
|
|
74
|
+
// merge existing props and add aria-label
|
|
75
|
+
const merged = { ...trElement.props, 'aria-label': ariaLabel };
|
|
76
|
+
return h('tr', merged, defaultSlots);
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 4) Focus the active filter button after filter changes
|
|
81
|
+
|
|
82
|
+
The composable returns `activeFilterButton` (a ref) which you can focus after DOM updates.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { nextTick } from 'vue';
|
|
86
|
+
|
|
87
|
+
function onFilterChange(event: any) {
|
|
88
|
+
// update your filter state and data here
|
|
89
|
+
|
|
90
|
+
nextTick(() => {
|
|
91
|
+
if (activeFilterButton.value) {
|
|
92
|
+
activeFilterButton.value.focus();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 5) Custom sort handling with composable helper
|
|
99
|
+
|
|
100
|
+
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.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
const optionalCustomSort = (event: any) => {
|
|
104
|
+
loader.value = true;
|
|
105
|
+
// example async
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
loader.value = false;
|
|
108
|
+
// apply sort state and reload data
|
|
109
|
+
}, 200);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
function onSortChange(event: any) {
|
|
113
|
+
handleSortChange(event, optionalCustomSort);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 6) Summary checklist
|
|
118
|
+
|
|
119
|
+
- Import and call `useGridA11y(gridRef)`
|
|
120
|
+
- Bind returned keyboard handler to Grid `@keydown`
|
|
121
|
+
- Bind returned sort handler to Grid `@sortchange` (and optionally pass a custom callback)
|
|
122
|
+
- Use returned `activeFilterButton` to manage focus after filter updates
|
|
123
|
+
- Provide a `rowRender` that adds a descriptive `aria-label` for each row
|
|
124
|
+
- Set `navigatable="false"` on the Grid to prefer row-level navigation
|
|
@@ -1,75 +1,75 @@
|
|
|
1
1
|
import { ref as E, onMounted as S, onBeforeUnmount as M, nextTick as p, onUnmounted as N } from "vue";
|
|
2
|
-
const G = (
|
|
3
|
-
const
|
|
4
|
-
let
|
|
5
|
-
const
|
|
6
|
-
const o =
|
|
7
|
-
[" ", "Spacebar", "Space", "Enter"].includes(o) && (
|
|
8
|
-
}, g = (
|
|
9
|
-
if (!
|
|
10
|
-
if (
|
|
11
|
-
|
|
2
|
+
const G = (b) => {
|
|
3
|
+
const s = E(null);
|
|
4
|
+
let f = null, u = null;
|
|
5
|
+
const m = [], v = ".k-table-row[data-grid-row-index] [tabindex]", k = () => b?.value?.columns, h = (t) => {
|
|
6
|
+
const o = t.key || t.code;
|
|
7
|
+
[" ", "Spacebar", "Space", "Enter"].includes(o) && (t.preventDefault(), t.stopPropagation(), s.value = t.target, t.target.click());
|
|
8
|
+
}, g = (t) => {
|
|
9
|
+
if (!s.value) return;
|
|
10
|
+
if (t.code === "Escape") {
|
|
11
|
+
t.preventDefault(), t.stopPropagation(), s.value && s.value.focus();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const o = Array.from(
|
|
15
15
|
document.querySelectorAll(
|
|
16
16
|
".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item"
|
|
17
17
|
)
|
|
18
|
-
),
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
const
|
|
18
|
+
), e = document.querySelector(".k-filter-menu-container");
|
|
19
|
+
if (e) {
|
|
20
|
+
if (t.code === "Tab") {
|
|
21
|
+
const n = [
|
|
22
22
|
".k-filter-menu-container .k-dropdownlist[tabindex='0']",
|
|
23
23
|
".k-filter-menu-container input.k-input-inner:not([tabindex='-1']):not([disabled])",
|
|
24
24
|
".k-filter-menu-container button:not([tabindex='-1']):not([disabled])"
|
|
25
|
-
],
|
|
26
|
-
|
|
25
|
+
], r = Array.from(
|
|
26
|
+
e.querySelectorAll(n.join(","))
|
|
27
27
|
);
|
|
28
|
-
if (
|
|
29
|
-
const a =
|
|
30
|
-
(
|
|
28
|
+
if (r.length === 0) return;
|
|
29
|
+
const a = r.findIndex(
|
|
30
|
+
(l) => l === document.activeElement
|
|
31
31
|
);
|
|
32
32
|
let i;
|
|
33
|
-
a === -1 ? i = 0 :
|
|
33
|
+
a === -1 ? i = 0 : t.shiftKey ? i = (a - 1 + r.length) % r.length : i = (a + 1) % r.length, t.preventDefault(), t.stopPropagation(), r[i]?.focus();
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
} else if (
|
|
37
|
-
|
|
38
|
-
const
|
|
36
|
+
} else if (t.code === "ArrowUp" || t.code === "ArrowDown") {
|
|
37
|
+
t.preventDefault(), t.stopPropagation();
|
|
38
|
+
const n = o.findIndex(
|
|
39
39
|
(a) => a === document.activeElement
|
|
40
40
|
);
|
|
41
|
-
let
|
|
42
|
-
|
|
41
|
+
let r = n;
|
|
42
|
+
t.code === "ArrowUp" ? r = n > 0 ? n - 1 : o.length - 1 : t.code === "ArrowDown" && (r = n < o.length - 1 ? n + 1 : 0), o[r]?.focus();
|
|
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
46
|
}, x = () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
o.addedNodes.forEach((
|
|
50
|
-
if (
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
const a =
|
|
47
|
+
f = new MutationObserver((t) => {
|
|
48
|
+
t.forEach((o) => {
|
|
49
|
+
o.addedNodes.forEach((e) => {
|
|
50
|
+
if (e.nodeType === Node.ELEMENT_NODE) {
|
|
51
|
+
const n = e;
|
|
52
|
+
if (n.classList.contains("k-animation-container")) {
|
|
53
|
+
const a = s.value;
|
|
54
54
|
a && (a.dataset.featherKSortable === "true" || p(() => {
|
|
55
|
-
|
|
55
|
+
n.querySelectorAll(
|
|
56
56
|
".k-columnmenu-item-wrapper"
|
|
57
|
-
).forEach((
|
|
58
|
-
|
|
57
|
+
).forEach((d) => {
|
|
58
|
+
d.textContent?.toLowerCase().includes("sort") && d.remove();
|
|
59
59
|
});
|
|
60
|
-
})),
|
|
60
|
+
})), n.addEventListener(
|
|
61
61
|
"keydown",
|
|
62
62
|
g
|
|
63
63
|
), p(() => {
|
|
64
64
|
const i = () => {
|
|
65
|
-
const
|
|
66
|
-
|
|
65
|
+
const l = Array.from(
|
|
66
|
+
n.querySelectorAll(
|
|
67
67
|
".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item"
|
|
68
68
|
)
|
|
69
69
|
);
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
else if (
|
|
70
|
+
if (l.length === 1)
|
|
71
|
+
l[0].focus(), l[0].click(), i.attempts = 0;
|
|
72
|
+
else if (l.length > 1) {
|
|
73
73
|
i.attempts = 0;
|
|
74
74
|
return;
|
|
75
75
|
} else
|
|
@@ -78,7 +78,7 @@ const G = (m) => {
|
|
|
78
78
|
i();
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
n.querySelectorAll(
|
|
82
82
|
".k-animation-container"
|
|
83
83
|
).forEach((a) => {
|
|
84
84
|
a.addEventListener(
|
|
@@ -87,13 +87,13 @@ const G = (m) => {
|
|
|
87
87
|
);
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
-
}), o.removedNodes.forEach((
|
|
91
|
-
if (
|
|
92
|
-
const
|
|
93
|
-
|
|
90
|
+
}), o.removedNodes.forEach((e) => {
|
|
91
|
+
if (e.nodeType === Node.ELEMENT_NODE) {
|
|
92
|
+
const n = e;
|
|
93
|
+
n.classList.contains("k-animation-container") && n.removeEventListener(
|
|
94
94
|
"keydown",
|
|
95
95
|
g
|
|
96
|
-
),
|
|
96
|
+
), n.querySelectorAll(
|
|
97
97
|
".k-animation-container"
|
|
98
98
|
).forEach((a) => {
|
|
99
99
|
a.removeEventListener(
|
|
@@ -104,15 +104,15 @@ const G = (m) => {
|
|
|
104
104
|
}
|
|
105
105
|
});
|
|
106
106
|
});
|
|
107
|
-
}),
|
|
107
|
+
}), f.observe(document.body, {
|
|
108
108
|
childList: !0,
|
|
109
109
|
subtree: !0
|
|
110
110
|
});
|
|
111
|
-
}, L = (
|
|
112
|
-
if (!
|
|
111
|
+
}, L = (t) => {
|
|
112
|
+
if (!t.type || !t)
|
|
113
113
|
return;
|
|
114
|
-
console.log("handleGridKeyDown",
|
|
115
|
-
const o =
|
|
114
|
+
console.log("handleGridKeyDown", t, t.code);
|
|
115
|
+
const o = t.target;
|
|
116
116
|
if (o && [
|
|
117
117
|
"ArrowDown",
|
|
118
118
|
"ArrowLeft",
|
|
@@ -120,86 +120,98 @@ const G = (m) => {
|
|
|
120
120
|
"ArrowUp",
|
|
121
121
|
"Enter",
|
|
122
122
|
"Space"
|
|
123
|
-
].includes(
|
|
124
|
-
if (
|
|
125
|
-
|
|
123
|
+
].includes(t.code)) {
|
|
124
|
+
if (t.preventDefault(), o.classList.contains("k-grid-header-menu") && o.classList.contains("k-grid-column-menu")) {
|
|
125
|
+
s.value = o;
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
|
-
const
|
|
128
|
+
const e = o.closest(
|
|
129
129
|
".k-table-row[data-grid-row-index]"
|
|
130
130
|
);
|
|
131
|
-
if (
|
|
132
|
-
if (["ArrowDown", "ArrowUp"].includes(
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
if (e) {
|
|
132
|
+
if (["ArrowDown", "ArrowUp"].includes(t.code)) {
|
|
133
|
+
const n = (a, i) => {
|
|
134
|
+
let l = i === "next" ? a.nextElementSibling : a.previousElementSibling;
|
|
135
|
+
for (; l; ) {
|
|
136
|
+
const d = l;
|
|
137
|
+
try {
|
|
138
|
+
if (d.hasAttribute && d.hasAttribute("tabindex"))
|
|
139
|
+
return d;
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
l = i === "next" ? l.nextElementSibling : l.previousElementSibling;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}, r = t.code === "ArrowDown" ? n(e, "next") : n(e, "previous");
|
|
146
|
+
r && (e.setAttribute("tabindex", "-1"), r.setAttribute("tabindex", "0"), r.focus());
|
|
135
147
|
return;
|
|
136
148
|
}
|
|
137
|
-
if (["ArrowLeft", "ArrowRight"].includes(
|
|
138
|
-
|
|
139
|
-
const
|
|
149
|
+
if (["ArrowLeft", "ArrowRight"].includes(t.code)) {
|
|
150
|
+
t.preventDefault();
|
|
151
|
+
const n = e.querySelectorAll(
|
|
140
152
|
v
|
|
141
153
|
);
|
|
142
|
-
if (
|
|
143
|
-
let
|
|
154
|
+
if (n.length === 0) return;
|
|
155
|
+
let r = Array.from(n).findIndex(
|
|
144
156
|
(a) => a === document.activeElement
|
|
145
157
|
);
|
|
146
|
-
if (
|
|
147
|
-
|
|
158
|
+
if (r === -1 && document.activeElement === e) {
|
|
159
|
+
n[0].focus();
|
|
148
160
|
return;
|
|
149
161
|
}
|
|
150
|
-
|
|
162
|
+
t.code === "ArrowRight" ? r = r === n.length - 1 ? 0 : r + 1 : t.code === "ArrowLeft" && (r = r === n.length - 1 ? r - 1 : n.length - 1), n[r].focus();
|
|
151
163
|
return;
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
}
|
|
155
167
|
}, D = () => {
|
|
156
168
|
p(() => {
|
|
157
|
-
const
|
|
158
|
-
|
|
169
|
+
const t = b.value.$el.closest(".k-grid");
|
|
170
|
+
t && t.classList.add("fk-grid");
|
|
159
171
|
});
|
|
160
172
|
}, T = () => {
|
|
161
173
|
try {
|
|
162
|
-
const
|
|
174
|
+
const t = () => {
|
|
163
175
|
try {
|
|
164
|
-
const
|
|
165
|
-
|
|
176
|
+
const n = Array.from(
|
|
177
|
+
b.value.$el.querySelectorAll(
|
|
166
178
|
".k-table-row[data-grid-row-index]"
|
|
167
179
|
)
|
|
168
180
|
);
|
|
169
|
-
if (!
|
|
181
|
+
if (!n || n.length === 0 || n.filter(
|
|
170
182
|
(i) => i.getAttribute("tabindex") === "0"
|
|
171
183
|
).length === 1) return;
|
|
172
|
-
const a =
|
|
184
|
+
const a = n.find(
|
|
173
185
|
(i) => i === document.activeElement || i.contains(document.activeElement)
|
|
174
186
|
);
|
|
175
|
-
if (
|
|
187
|
+
if (n.forEach((i) => i.setAttribute("tabindex", "-1")), a) {
|
|
176
188
|
a.setAttribute("tabindex", "0");
|
|
177
189
|
return;
|
|
178
190
|
}
|
|
179
|
-
|
|
180
|
-
} catch (
|
|
181
|
-
console.error("ensureSingleTabindex error:",
|
|
191
|
+
n[0].setAttribute("tabindex", "0");
|
|
192
|
+
} catch (n) {
|
|
193
|
+
console.error("ensureSingleTabindex error:", n);
|
|
182
194
|
}
|
|
183
195
|
}, o = Array.from(
|
|
184
|
-
|
|
196
|
+
b.value.$el.querySelectorAll(
|
|
185
197
|
".k-table-row[data-grid-row-index]"
|
|
186
198
|
)
|
|
187
199
|
);
|
|
188
|
-
o.length > 0 && o.forEach((
|
|
189
|
-
|
|
200
|
+
o.length > 0 && o.forEach((n, r) => {
|
|
201
|
+
n.setAttribute("tabindex", r === 0 ? "0" : "-1");
|
|
190
202
|
});
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}), u.observe(
|
|
195
|
-
} catch (
|
|
196
|
-
console.error("Error setting up row navigation:",
|
|
203
|
+
const e = b.value.$el.querySelector(".k-table-tbody");
|
|
204
|
+
e && (u = new MutationObserver(() => {
|
|
205
|
+
t();
|
|
206
|
+
}), u.observe(e, { childList: !0, subtree: !0 }));
|
|
207
|
+
} catch (t) {
|
|
208
|
+
console.error("Error setting up row navigation:", t);
|
|
197
209
|
}
|
|
198
210
|
}, q = () => {
|
|
199
211
|
p(() => {
|
|
200
212
|
const o = document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");
|
|
201
|
-
o && o.forEach((
|
|
202
|
-
|
|
213
|
+
o && o.forEach((e) => {
|
|
214
|
+
e.setAttribute("role", "button"), e.addEventListener(
|
|
203
215
|
"keydown",
|
|
204
216
|
h
|
|
205
217
|
);
|
|
@@ -207,40 +219,40 @@ const G = (m) => {
|
|
|
207
219
|
});
|
|
208
220
|
}, C = () => {
|
|
209
221
|
const o = document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");
|
|
210
|
-
o && o.forEach((
|
|
211
|
-
|
|
222
|
+
o && o.forEach((e) => {
|
|
223
|
+
e.removeEventListener(
|
|
212
224
|
"keydown",
|
|
213
225
|
h
|
|
214
226
|
);
|
|
215
|
-
}),
|
|
227
|
+
}), f && (f.disconnect(), f = null), u && (u.disconnect(), u = null), m.forEach((e) => e()), m.length = 0;
|
|
216
228
|
}, I = () => {
|
|
217
229
|
document.querySelectorAll(
|
|
218
230
|
".k-grid-header .k-table-thead th"
|
|
219
|
-
).forEach((
|
|
220
|
-
const
|
|
231
|
+
).forEach((e, n) => {
|
|
232
|
+
const r = e.querySelector(
|
|
221
233
|
".k-grid-header-menu.k-grid-column-menu"
|
|
222
234
|
);
|
|
223
|
-
if (!
|
|
235
|
+
if (!r) return;
|
|
224
236
|
const a = k();
|
|
225
|
-
if (a && a[
|
|
226
|
-
const c = a[
|
|
227
|
-
|
|
237
|
+
if (a && a[n]) {
|
|
238
|
+
const c = a[n].field ?? "";
|
|
239
|
+
e.setAttribute("data-feather-k-field", c), e.setAttribute(
|
|
228
240
|
"data-feather-k-filterable",
|
|
229
|
-
a[
|
|
230
|
-
),
|
|
241
|
+
a[n].filterable === !1 ? "false" : "true"
|
|
242
|
+
), e.setAttribute(
|
|
231
243
|
"data-feather-k-sortable",
|
|
232
|
-
a[
|
|
244
|
+
a[n].sortable === !1 ? "false" : "true"
|
|
233
245
|
);
|
|
234
246
|
}
|
|
235
|
-
const i =
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
c.target?.closest(".k-column-resizer") || (
|
|
247
|
+
const i = e.dataset.featherKFilterable !== "false", l = e.dataset.featherKSortable !== "false";
|
|
248
|
+
r.setAttribute("tabindex", "-1"), i ? (e.setAttribute("tabindex", "0"), e.setAttribute("role", "button"), e.setAttribute("aria-haspopup", "menu"), e.style.cursor = "pointer") : (e.setAttribute("tabindex", "0"), e.setAttribute("role", "columnheader"), e.removeAttribute("aria-haspopup"), e.style.cursor = "default");
|
|
249
|
+
const d = (c) => {
|
|
250
|
+
c.target?.closest(".k-column-resizer") || (s.value = e, r.click());
|
|
239
251
|
}, w = (c) => {
|
|
240
252
|
if (i)
|
|
241
|
-
|
|
242
|
-
else if (
|
|
243
|
-
|
|
253
|
+
s.value = e, c.preventDefault(), c.stopPropagation(), d(c);
|
|
254
|
+
else if (l) {
|
|
255
|
+
s.value = e;
|
|
244
256
|
const y = new KeyboardEvent("keydown", {
|
|
245
257
|
key: "Enter",
|
|
246
258
|
code: "Enter",
|
|
@@ -249,58 +261,58 @@ const G = (m) => {
|
|
|
249
261
|
bubbles: !0,
|
|
250
262
|
cancelable: !0
|
|
251
263
|
});
|
|
252
|
-
|
|
264
|
+
e.dispatchEvent(y);
|
|
253
265
|
}
|
|
254
266
|
};
|
|
255
|
-
|
|
256
|
-
|
|
267
|
+
e.addEventListener("click", w), m.push(() => {
|
|
268
|
+
e.removeEventListener("click", w);
|
|
257
269
|
});
|
|
258
270
|
const A = (c) => {
|
|
259
|
-
if ((c.code === "Enter" || c.code === "Space") && (i ||
|
|
260
|
-
if (
|
|
261
|
-
c.preventDefault(), c.stopPropagation(),
|
|
262
|
-
else if (
|
|
263
|
-
const y =
|
|
271
|
+
if ((c.code === "Enter" || c.code === "Space") && (i || l)) {
|
|
272
|
+
if (s.value = e, s.value.focus(), i)
|
|
273
|
+
c.preventDefault(), c.stopPropagation(), d(c);
|
|
274
|
+
else if (l) {
|
|
275
|
+
const y = e.querySelector(".k-link");
|
|
264
276
|
y && y.click();
|
|
265
277
|
}
|
|
266
278
|
}
|
|
267
279
|
};
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
e.addEventListener("keydown", A, !0), m.push(() => {
|
|
281
|
+
e.removeEventListener("keydown", A, !0);
|
|
270
282
|
});
|
|
271
283
|
});
|
|
272
284
|
const o = document.querySelector(".k-grid-header .k-table-thead");
|
|
273
285
|
if (o) {
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
286
|
+
const e = (n) => {
|
|
287
|
+
const r = n.target.closest("th");
|
|
288
|
+
r && (n.code === "Enter" || n.code === "Space") && r.dataset.featherKFilterable === "false" && r.dataset.featherKSortable === "false" && (n.preventDefault(), n.stopImmediatePropagation());
|
|
277
289
|
};
|
|
278
290
|
o.addEventListener(
|
|
279
291
|
"keydown",
|
|
280
|
-
|
|
292
|
+
e,
|
|
281
293
|
!0
|
|
282
294
|
// NOTE: capture phase
|
|
283
|
-
),
|
|
295
|
+
), m.push(() => {
|
|
284
296
|
o.removeEventListener(
|
|
285
297
|
"keydown",
|
|
286
|
-
|
|
298
|
+
e,
|
|
287
299
|
!0
|
|
288
300
|
);
|
|
289
301
|
});
|
|
290
302
|
}
|
|
291
|
-
}, K = function(
|
|
292
|
-
const
|
|
293
|
-
if (!
|
|
294
|
-
const
|
|
295
|
-
if (!
|
|
303
|
+
}, K = function(t, o) {
|
|
304
|
+
const e = t?.event.event.target, n = k();
|
|
305
|
+
if (!e || !n) return;
|
|
306
|
+
const r = e.classList.contains("k-link"), a = e.classList.contains("k-columnmenu-item"), i = n.find((l) => l.field === t.event.field)?.sortable && !0;
|
|
307
|
+
if (!r) {
|
|
296
308
|
if (a && !i) {
|
|
297
|
-
(
|
|
298
|
-
(
|
|
309
|
+
(t.event.sort && void 0)?.filter(
|
|
310
|
+
(d) => d.field !== t.event.field
|
|
299
311
|
);
|
|
300
312
|
return;
|
|
301
313
|
}
|
|
302
314
|
typeof o == "function" && p(() => {
|
|
303
|
-
|
|
315
|
+
s.value && s.value.focus(), o(t);
|
|
304
316
|
});
|
|
305
317
|
}
|
|
306
318
|
};
|
|
@@ -309,14 +321,14 @@ const G = (m) => {
|
|
|
309
321
|
}), M(() => {
|
|
310
322
|
C();
|
|
311
323
|
}), {
|
|
312
|
-
activeFilterButton:
|
|
324
|
+
activeFilterButton: s,
|
|
313
325
|
handleGridKeyDown: L,
|
|
314
326
|
handleSortChange: K
|
|
315
327
|
};
|
|
316
328
|
};
|
|
317
|
-
function B(
|
|
318
|
-
const { activeByDefault:
|
|
319
|
-
function
|
|
329
|
+
function B(b = {}) {
|
|
330
|
+
const { activeByDefault: s = !1, autoActivateDelay: f = 0 } = b, u = E(s);
|
|
331
|
+
function m() {
|
|
320
332
|
u.value = !0;
|
|
321
333
|
}
|
|
322
334
|
function v() {
|
|
@@ -326,13 +338,13 @@ function B(m = {}) {
|
|
|
326
338
|
u.value = !u.value;
|
|
327
339
|
}
|
|
328
340
|
return S(() => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
},
|
|
341
|
+
f > 0 && !s && setTimeout(() => {
|
|
342
|
+
m();
|
|
343
|
+
}, f);
|
|
332
344
|
}), N(() => {
|
|
333
345
|
}), {
|
|
334
346
|
isGridActive: u,
|
|
335
|
-
activateGrid:
|
|
347
|
+
activateGrid: m,
|
|
336
348
|
deactivateGrid: v,
|
|
337
349
|
toggleGrid: k
|
|
338
350
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(m,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],c):(m=typeof globalThis<"u"?globalThis:m||self,c(m.FeatherKComposables={},m.Vue))})(this,(function(m,c){"use strict";const S=k=>{const s=c.ref(null);let b=null,d=null;const p=[],w=".k-table-row[data-grid-row-index] [tabindex]",g=()=>k?.value?.columns,v=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 n=[".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])"],r=Array.from(e.querySelectorAll(n.join(",")));if(r.length===0)return;const a=r.findIndex(l=>l===document.activeElement);let i;a===-1?i=0:t.shiftKey?i=(a-1+r.length)%r.length:i=(a+1)%r.length,t.preventDefault(),t.stopPropagation(),r[i]?.focus();return}}else if(t.code==="ArrowUp"||t.code==="ArrowDown"){t.preventDefault(),t.stopPropagation();const n=o.findIndex(a=>a===document.activeElement);let r=n;t.code==="ArrowUp"?r=n>0?n-1:o.length-1:t.code==="ArrowDown"&&(r=n<o.length-1?n+1:0),o[r]?.focus();return}t.code==="Tab"&&(t.preventDefault(),t.stopPropagation(),t.shiftKey?(s.value?.previousElementSibling).focus():(s.value?.nextElementSibling).focus())},L=()=>{b=new MutationObserver(t=>{t.forEach(o=>{o.addedNodes.forEach(e=>{if(e.nodeType===Node.ELEMENT_NODE){const n=e;if(n.classList.contains("k-animation-container")){const a=s.value;a&&(a.dataset.featherKSortable==="true"||c.nextTick(()=>{n.querySelectorAll(".k-columnmenu-item-wrapper").forEach(f=>{f.textContent?.toLowerCase().includes("sort")&&f.remove()})})),n.addEventListener("keydown",y),c.nextTick(()=>{const i=()=>{const l=Array.from(n.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()})}n.querySelectorAll(".k-animation-container").forEach(a=>{a.addEventListener("keydown",y)})}}),o.removedNodes.forEach(e=>{if(e.nodeType===Node.ELEMENT_NODE){const n=e;n.classList.contains("k-animation-container")&&n.removeEventListener("keydown",y),n.querySelectorAll(".k-animation-container").forEach(a=>{a.removeEventListener("keydown",y)})}})})}),b.observe(document.body,{childList:!0,subtree:!0})},T=t=>{if(!t.type||!t)return;console.log("handleGridKeyDown",t,t.code);const o=t.target;if(o&&["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 n=(a,i)=>{let l=i==="next"?a.nextElementSibling:a.previousElementSibling;for(;l;){const f=l;try{if(f.hasAttribute&&f.hasAttribute("tabindex"))return f}catch{}l=i==="next"?l.nextElementSibling:l.previousElementSibling}return null},r=t.code==="ArrowDown"?n(e,"next"):n(e,"previous");r&&(e.setAttribute("tabindex","-1"),r.setAttribute("tabindex","0"),r.focus());return}if(["ArrowLeft","ArrowRight"].includes(t.code)){t.preventDefault();const n=e.querySelectorAll(w);if(n.length===0)return;let r=Array.from(n).findIndex(a=>a===document.activeElement);if(r===-1&&document.activeElement===e){n[0].focus();return}t.code==="ArrowRight"?r=r===n.length-1?0:r+1:t.code==="ArrowLeft"&&(r=r===n.length-1?r-1:n.length-1),n[r].focus();return}}}},D=()=>{c.nextTick(()=>{const t=k.value.$el.closest(".k-grid");t&&t.classList.add("fk-grid")})},q=()=>{try{const t=()=>{try{const n=Array.from(k.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]"));if(!n||n.length===0||n.filter(i=>i.getAttribute("tabindex")==="0").length===1)return;const a=n.find(i=>i===document.activeElement||i.contains(document.activeElement));if(n.forEach(i=>i.setAttribute("tabindex","-1")),a){a.setAttribute("tabindex","0");return}n[0].setAttribute("tabindex","0")}catch(n){console.error("ensureSingleTabindex error:",n)}},o=Array.from(k.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]"));o.length>0&&o.forEach((n,r)=>{n.setAttribute("tabindex",r===0?"0":"-1")});const e=k.value.$el.querySelector(".k-table-tbody");e&&(d=new MutationObserver(()=>{t()}),d.observe(e,{childList:!0,subtree:!0}))}catch(t){console.error("Error setting up row navigation:",t)}},C=()=>{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",v)}),L(),K()})},I=()=>{const o=document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");o&&o.forEach(e=>{e.removeEventListener("keydown",v)}),b&&(b.disconnect(),b=null),d&&(d.disconnect(),d=null),p.forEach(e=>e()),p.length=0},K=()=>{document.querySelectorAll(".k-grid-header .k-table-thead th").forEach((e,n)=>{const r=e.querySelector(".k-grid-header-menu.k-grid-column-menu");if(!r)return;const a=g();if(a&&a[n]){const u=a[n].field??"";e.setAttribute("data-feather-k-field",u),e.setAttribute("data-feather-k-filterable",a[n].filterable===!1?"false":"true"),e.setAttribute("data-feather-k-sortable",a[n].sortable===!1?"false":"true")}const i=e.dataset.featherKFilterable!=="false",l=e.dataset.featherKSortable!=="false";r.setAttribute("tabindex","-1"),i?(e.setAttribute("tabindex","0"),e.setAttribute("role","button"),e.setAttribute("aria-haspopup","menu"),e.style.cursor="pointer"):(e.setAttribute("tabindex","0"),e.setAttribute("role","columnheader"),e.removeAttribute("aria-haspopup"),e.style.cursor="default");const f=u=>{u.target?.closest(".k-column-resizer")||(s.value=e,r.click())},A=u=>{if(i)s.value=e,u.preventDefault(),u.stopPropagation(),f(u);else if(l){s.value=e;const h=new KeyboardEvent("keydown",{key:"Enter",code:"Enter",keyCode:13,which:13,bubbles:!0,cancelable:!0});e.dispatchEvent(h)}};e.addEventListener("click",A),p.push(()=>{e.removeEventListener("click",A)});const E=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 h=e.querySelector(".k-link");h&&h.click()}}};e.addEventListener("keydown",E,!0),p.push(()=>{e.removeEventListener("keydown",E,!0)})});const o=document.querySelector(".k-grid-header .k-table-thead");if(o){const e=n=>{const r=n.target.closest("th");r&&(n.code==="Enter"||n.code==="Space")&&r.dataset.featherKFilterable==="false"&&r.dataset.featherKSortable==="false"&&(n.preventDefault(),n.stopImmediatePropagation())};o.addEventListener("keydown",e,!0),p.push(()=>{o.removeEventListener("keydown",e,!0)})}},M=function(t,o){const e=t?.event.event.target,n=g();if(!e||!n)return;const r=e.classList.contains("k-link"),a=e.classList.contains("k-columnmenu-item"),i=n.find(l=>l.field===t.event.field)?.sortable&&!0;if(!r){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)})}};return c.onMounted(()=>{D(),q(),C()}),c.onBeforeUnmount(()=>{I()}),{activeFilterButton:s,handleGridKeyDown:T,handleSortChange:M}};function x(k={}){const{activeByDefault:s=!1,autoActivateDelay:b=0}=k,d=c.ref(s);function p(){d.value=!0}function w(){d.value=!1}function g(){d.value=!d.value}return c.onMounted(()=>{b>0&&!s&&setTimeout(()=>{p()},b)}),c.onUnmounted(()=>{}),{isGridActive:d,activateGrid:p,deactivateGrid:w,toggleGrid:g}}m.useGridA11y=S,m.useGridComposableEx=x,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { useGridA11y } from
|
|
2
|
-
export { useGridComposableEx } from
|
|
1
|
+
export { useGridA11y } from "./useGridA11y";
|
|
2
|
+
export { useGridComposableEx } from "./useGridComposableEx";
|
package/dist/types/kendo.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featherk/composables",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"main": "dist/featherk-composables.umd.js",
|
|
5
5
|
"module": "dist/featherk-composables.es.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"build": "vite build && npm run build:types",
|
|
12
12
|
"build:types": "vue-tsc --emitDeclarationOnly --declaration --declarationDir dist",
|
|
13
13
|
"test": "vitest",
|
|
14
|
-
"lint": "eslint src --ext .ts,.vue",
|
|
15
14
|
"clean": "rm -rf dist",
|
|
16
15
|
"prepublishOnly": "npm run build"
|
|
17
16
|
},
|
|
@@ -21,7 +20,6 @@
|
|
|
21
20
|
"devDependencies": {
|
|
22
21
|
"@types/node": "^24.5.2",
|
|
23
22
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
24
|
-
"eslint": "^8.0.0",
|
|
25
23
|
"typescript": "~5.8.3",
|
|
26
24
|
"vite": "^7.1.7",
|
|
27
25
|
"vitest": "^3.2.4",
|
|
@@ -31,4 +29,4 @@
|
|
|
31
29
|
"access": "public"
|
|
32
30
|
},
|
|
33
31
|
"license": "MIT"
|
|
34
|
-
}
|
|
32
|
+
}
|