@featherk/composables 0.0.6 → 0.0.7

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 CHANGED
@@ -21,6 +21,12 @@
21
21
  - For row-level navigation, disable the Grid's cell-level dynamic tabindex behavior (omit or set `navigatable="false"` on the Grid).
22
22
  - The composable returns helpers for keyboard handling, focus management, and sort/filter interactions.
23
23
 
24
+ ### Styling and the .fk-grid class
25
+
26
+ - The composable will add the CSS class `.fk-grid` to the Grid's root element (see `setupGridStyling()` in the source). The composable itself does NOT include any CSS (no inline styles or stylesheet).
27
+ - The `.fk-grid` class is a hook used by the FeatherK stylesheet to apply visual styles. To see visual indicators (for example, the active/filtered status), you must include the appropriate FeatherK stylesheet for Kendo in your application.
28
+ - Ensure you are using the matching FeatherK styling release for correct visuals — e.g. `featherk-q3-2024-v#.css` (replace `#` with the patch version you are using).
29
+
24
30
  ## Prerequisites
25
31
 
26
32
  - Vue 3 (script setup)
@@ -1,8 +1,8 @@
1
1
  import { ref as E, onMounted as S, onBeforeUnmount as M, nextTick as p, onUnmounted as N } from "vue";
2
- const G = (b) => {
2
+ const G = (f) => {
3
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) => {
4
+ let m = null, u = null;
5
+ const b = [], y = ".k-table-row[data-grid-row-index] [tabindex]", k = () => f?.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) => {
@@ -18,52 +18,52 @@ const G = (b) => {
18
18
  ), e = document.querySelector(".k-filter-menu-container");
19
19
  if (e) {
20
20
  if (t.code === "Tab") {
21
- const n = [
21
+ const r = [
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
- ], r = Array.from(
26
- e.querySelectorAll(n.join(","))
25
+ ], n = Array.from(
26
+ e.querySelectorAll(r.join(","))
27
27
  );
28
- if (r.length === 0) return;
29
- const a = r.findIndex(
28
+ if (n.length === 0) return;
29
+ const a = n.findIndex(
30
30
  (l) => l === document.activeElement
31
31
  );
32
32
  let i;
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();
33
+ 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();
34
34
  return;
35
35
  }
36
36
  } else if (t.code === "ArrowUp" || t.code === "ArrowDown") {
37
37
  t.preventDefault(), t.stopPropagation();
38
- const n = o.findIndex(
38
+ const r = o.findIndex(
39
39
  (a) => a === document.activeElement
40
40
  );
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();
41
+ let n = r;
42
+ 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();
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
- f = new MutationObserver((t) => {
47
+ m = new MutationObserver((t) => {
48
48
  t.forEach((o) => {
49
49
  o.addedNodes.forEach((e) => {
50
50
  if (e.nodeType === Node.ELEMENT_NODE) {
51
- const n = e;
52
- if (n.classList.contains("k-animation-container")) {
51
+ const r = e;
52
+ if (r.classList.contains("k-animation-container")) {
53
53
  const a = s.value;
54
54
  a && (a.dataset.featherKSortable === "true" || p(() => {
55
- n.querySelectorAll(
55
+ r.querySelectorAll(
56
56
  ".k-columnmenu-item-wrapper"
57
57
  ).forEach((d) => {
58
58
  d.textContent?.toLowerCase().includes("sort") && d.remove();
59
59
  });
60
- })), n.addEventListener(
60
+ })), r.addEventListener(
61
61
  "keydown",
62
62
  g
63
63
  ), p(() => {
64
64
  const i = () => {
65
65
  const l = Array.from(
66
- n.querySelectorAll(
66
+ r.querySelectorAll(
67
67
  ".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item"
68
68
  )
69
69
  );
@@ -78,7 +78,7 @@ const G = (b) => {
78
78
  i();
79
79
  });
80
80
  }
81
- n.querySelectorAll(
81
+ r.querySelectorAll(
82
82
  ".k-animation-container"
83
83
  ).forEach((a) => {
84
84
  a.addEventListener(
@@ -89,11 +89,11 @@ const G = (b) => {
89
89
  }
90
90
  }), o.removedNodes.forEach((e) => {
91
91
  if (e.nodeType === Node.ELEMENT_NODE) {
92
- const n = e;
93
- n.classList.contains("k-animation-container") && n.removeEventListener(
92
+ const r = e;
93
+ r.classList.contains("k-animation-container") && r.removeEventListener(
94
94
  "keydown",
95
95
  g
96
- ), n.querySelectorAll(
96
+ ), r.querySelectorAll(
97
97
  ".k-animation-container"
98
98
  ).forEach((a) => {
99
99
  a.removeEventListener(
@@ -104,7 +104,7 @@ const G = (b) => {
104
104
  }
105
105
  });
106
106
  });
107
- }), f.observe(document.body, {
107
+ }), m.observe(document.body, {
108
108
  childList: !0,
109
109
  subtree: !0
110
110
  });
@@ -113,101 +113,119 @@ const G = (b) => {
113
113
  return;
114
114
  console.log("handleGridKeyDown", t, t.code);
115
115
  const o = t.target;
116
- if (o && [
117
- "ArrowDown",
118
- "ArrowLeft",
119
- "ArrowRight",
120
- "ArrowUp",
121
- "Enter",
122
- "Space"
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
- return;
116
+ if (o) {
117
+ if (t.code === "Escape") {
118
+ const e = document.activeElement?.closest(".k-table-row[data-grid-row-index]");
119
+ if (e) {
120
+ t.preventDefault(), t.stopPropagation();
121
+ try {
122
+ Array.from(
123
+ f?.value.$el.querySelectorAll(
124
+ ".k-table-row[data-grid-row-index]"
125
+ )
126
+ ).forEach((n) => n.setAttribute("tabindex", "-1"));
127
+ } catch {
128
+ }
129
+ e.setAttribute("tabindex", "0"), e.focus();
130
+ return;
131
+ }
127
132
  }
128
- const e = o.closest(
129
- ".k-table-row[data-grid-row-index]"
130
- );
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());
133
+ if ([
134
+ "ArrowDown",
135
+ "ArrowLeft",
136
+ "ArrowRight",
137
+ "ArrowUp",
138
+ "Enter",
139
+ "Space"
140
+ ].includes(t.code)) {
141
+ if (t.preventDefault(), o.classList.contains("k-grid-header-menu") && o.classList.contains("k-grid-column-menu")) {
142
+ s.value = o;
147
143
  return;
148
144
  }
149
- if (["ArrowLeft", "ArrowRight"].includes(t.code)) {
150
- t.preventDefault();
151
- const n = e.querySelectorAll(
152
- v
153
- );
154
- if (n.length === 0) return;
155
- let r = Array.from(n).findIndex(
156
- (a) => a === document.activeElement
157
- );
158
- if (r === -1 && document.activeElement === e) {
159
- n[0].focus();
145
+ const e = o.closest(
146
+ ".k-table-row[data-grid-row-index]"
147
+ );
148
+ if (e) {
149
+ if (["ArrowDown", "ArrowUp"].includes(t.code)) {
150
+ const r = (a, i) => {
151
+ let l = i === "next" ? a.nextElementSibling : a.previousElementSibling;
152
+ for (; l; ) {
153
+ const d = l;
154
+ try {
155
+ if (d.hasAttribute && d.classList.contains("k-table-row"))
156
+ return d;
157
+ } catch {
158
+ }
159
+ l = i === "next" ? l.nextElementSibling : l.previousElementSibling;
160
+ }
161
+ return null;
162
+ }, n = t.code === "ArrowDown" ? r(e, "next") : r(e, "previous");
163
+ n && (e.setAttribute("tabindex", "-1"), n.setAttribute("tabindex", "0"), n.focus());
164
+ return;
165
+ }
166
+ if (["ArrowLeft", "ArrowRight"].includes(t.code)) {
167
+ t.preventDefault();
168
+ const r = e.querySelectorAll(
169
+ y
170
+ );
171
+ if (r.length === 0) return;
172
+ let n = Array.from(r).findIndex(
173
+ (a) => a === document.activeElement
174
+ );
175
+ if (n === -1 && document.activeElement === e) {
176
+ r[0].focus();
177
+ return;
178
+ }
179
+ 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();
160
180
  return;
161
181
  }
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();
163
- return;
164
182
  }
165
183
  }
166
184
  }
167
185
  }, D = () => {
168
186
  p(() => {
169
- const t = b.value.$el.closest(".k-grid");
187
+ const t = f.value.$el.closest(".k-grid");
170
188
  t && t.classList.add("fk-grid");
171
189
  });
172
- }, T = () => {
190
+ }, q = () => {
173
191
  try {
174
192
  const t = () => {
175
193
  try {
176
- const n = Array.from(
177
- b.value.$el.querySelectorAll(
194
+ const r = Array.from(
195
+ f.value.$el.querySelectorAll(
178
196
  ".k-table-row[data-grid-row-index]"
179
197
  )
180
198
  );
181
- if (!n || n.length === 0 || n.filter(
199
+ if (!r || r.length === 0 || r.filter(
182
200
  (i) => i.getAttribute("tabindex") === "0"
183
201
  ).length === 1) return;
184
- const a = n.find(
202
+ const a = r.find(
185
203
  (i) => i === document.activeElement || i.contains(document.activeElement)
186
204
  );
187
- if (n.forEach((i) => i.setAttribute("tabindex", "-1")), a) {
205
+ if (r.forEach((i) => i.setAttribute("tabindex", "-1")), a) {
188
206
  a.setAttribute("tabindex", "0");
189
207
  return;
190
208
  }
191
- n[0].setAttribute("tabindex", "0");
192
- } catch (n) {
193
- console.error("ensureSingleTabindex error:", n);
209
+ r[0].setAttribute("tabindex", "0");
210
+ } catch (r) {
211
+ console.error("ensureSingleTabindex error:", r);
194
212
  }
195
213
  }, o = Array.from(
196
- b.value.$el.querySelectorAll(
214
+ f.value.$el.querySelectorAll(
197
215
  ".k-table-row[data-grid-row-index]"
198
216
  )
199
217
  );
200
- o.length > 0 && o.forEach((n, r) => {
201
- n.setAttribute("tabindex", r === 0 ? "0" : "-1");
218
+ o.length > 0 && o.forEach((r, n) => {
219
+ r.setAttribute("tabindex", n === 0 ? "0" : "-1");
202
220
  });
203
- const e = b.value.$el.querySelector(".k-table-tbody");
221
+ const e = f.value.$el.querySelector(".k-table-tbody");
204
222
  e && (u = new MutationObserver(() => {
205
223
  t();
206
224
  }), u.observe(e, { childList: !0, subtree: !0 }));
207
225
  } catch (t) {
208
226
  console.error("Error setting up row navigation:", t);
209
227
  }
210
- }, q = () => {
228
+ }, T = () => {
211
229
  p(() => {
212
230
  const o = document.querySelectorAll(".k-grid-header .k-grid-header-menu.k-grid-column-menu");
213
231
  o && o.forEach((e) => {
@@ -224,36 +242,36 @@ const G = (b) => {
224
242
  "keydown",
225
243
  h
226
244
  );
227
- }), f && (f.disconnect(), f = null), u && (u.disconnect(), u = null), m.forEach((e) => e()), m.length = 0;
245
+ }), m && (m.disconnect(), m = null), u && (u.disconnect(), u = null), b.forEach((e) => e()), b.length = 0;
228
246
  }, I = () => {
229
247
  document.querySelectorAll(
230
248
  ".k-grid-header .k-table-thead th"
231
- ).forEach((e, n) => {
232
- const r = e.querySelector(
249
+ ).forEach((e, r) => {
250
+ const n = e.querySelector(
233
251
  ".k-grid-header-menu.k-grid-column-menu"
234
252
  );
235
- if (!r) return;
253
+ if (!n) return;
236
254
  const a = k();
237
- if (a && a[n]) {
238
- const c = a[n].field ?? "";
255
+ if (a && a[r]) {
256
+ const c = a[r].field ?? "";
239
257
  e.setAttribute("data-feather-k-field", c), e.setAttribute(
240
258
  "data-feather-k-filterable",
241
- a[n].filterable === !1 ? "false" : "true"
259
+ a[r].filterable === !1 ? "false" : "true"
242
260
  ), e.setAttribute(
243
261
  "data-feather-k-sortable",
244
- a[n].sortable === !1 ? "false" : "true"
262
+ a[r].sortable === !1 ? "false" : "true"
245
263
  );
246
264
  }
247
265
  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");
266
+ n.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
267
  const d = (c) => {
250
- c.target?.closest(".k-column-resizer") || (s.value = e, r.click());
251
- }, w = (c) => {
268
+ c.target?.closest(".k-column-resizer") || (s.value = e, n.click());
269
+ }, v = (c) => {
252
270
  if (i)
253
271
  s.value = e, c.preventDefault(), c.stopPropagation(), d(c);
254
272
  else if (l) {
255
273
  s.value = e;
256
- const y = new KeyboardEvent("keydown", {
274
+ const w = new KeyboardEvent("keydown", {
257
275
  key: "Enter",
258
276
  code: "Enter",
259
277
  keyCode: 13,
@@ -261,38 +279,38 @@ const G = (b) => {
261
279
  bubbles: !0,
262
280
  cancelable: !0
263
281
  });
264
- e.dispatchEvent(y);
282
+ e.dispatchEvent(w);
265
283
  }
266
284
  };
267
- e.addEventListener("click", w), m.push(() => {
268
- e.removeEventListener("click", w);
285
+ e.addEventListener("click", v), b.push(() => {
286
+ e.removeEventListener("click", v);
269
287
  });
270
288
  const A = (c) => {
271
289
  if ((c.code === "Enter" || c.code === "Space") && (i || l)) {
272
290
  if (s.value = e, s.value.focus(), i)
273
291
  c.preventDefault(), c.stopPropagation(), d(c);
274
292
  else if (l) {
275
- const y = e.querySelector(".k-link");
276
- y && y.click();
293
+ const w = e.querySelector(".k-link");
294
+ w && w.click();
277
295
  }
278
296
  }
279
297
  };
280
- e.addEventListener("keydown", A, !0), m.push(() => {
298
+ e.addEventListener("keydown", A, !0), b.push(() => {
281
299
  e.removeEventListener("keydown", A, !0);
282
300
  });
283
301
  });
284
302
  const o = document.querySelector(".k-grid-header .k-table-thead");
285
303
  if (o) {
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());
304
+ const e = (r) => {
305
+ const n = r.target.closest("th");
306
+ n && (r.code === "Enter" || r.code === "Space") && n.dataset.featherKFilterable === "false" && n.dataset.featherKSortable === "false" && (r.preventDefault(), r.stopImmediatePropagation());
289
307
  };
290
308
  o.addEventListener(
291
309
  "keydown",
292
310
  e,
293
311
  !0
294
312
  // NOTE: capture phase
295
- ), m.push(() => {
313
+ ), b.push(() => {
296
314
  o.removeEventListener(
297
315
  "keydown",
298
316
  e,
@@ -301,10 +319,10 @@ const G = (b) => {
301
319
  });
302
320
  }
303
321
  }, 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) {
322
+ const e = t?.event.event.target, r = k();
323
+ if (!e || !r) return;
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
+ if (!n) {
308
326
  if (a && !i) {
309
327
  (t.event.sort && void 0)?.filter(
310
328
  (d) => d.field !== t.event.field
@@ -317,7 +335,7 @@ const G = (b) => {
317
335
  }
318
336
  };
319
337
  return S(() => {
320
- D(), T(), q();
338
+ D(), q(), T();
321
339
  }), M(() => {
322
340
  C();
323
341
  }), {
@@ -326,30 +344,30 @@ const G = (b) => {
326
344
  handleSortChange: K
327
345
  };
328
346
  };
329
- function B(b = {}) {
330
- const { activeByDefault: s = !1, autoActivateDelay: f = 0 } = b, u = E(s);
331
- function m() {
347
+ function P(f = {}) {
348
+ const { activeByDefault: s = !1, autoActivateDelay: m = 0 } = f, u = E(s);
349
+ function b() {
332
350
  u.value = !0;
333
351
  }
334
- function v() {
352
+ function y() {
335
353
  u.value = !1;
336
354
  }
337
355
  function k() {
338
356
  u.value = !u.value;
339
357
  }
340
358
  return S(() => {
341
- f > 0 && !s && setTimeout(() => {
342
- m();
343
- }, f);
359
+ m > 0 && !s && setTimeout(() => {
360
+ b();
361
+ }, m);
344
362
  }), N(() => {
345
363
  }), {
346
364
  isGridActive: u,
347
- activateGrid: m,
348
- deactivateGrid: v,
365
+ activateGrid: b,
366
+ deactivateGrid: y,
349
367
  toggleGrid: k
350
368
  };
351
369
  }
352
370
  export {
353
371
  G as useGridA11y,
354
- B as useGridComposableEx
372
+ P as useGridComposableEx
355
373
  };
@@ -1 +1 @@
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"})}));
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=b=>{const s=c.ref(null);let p=null,d=null;const k=[],h=".k-table-row[data-grid-row-index] [tabindex]",g=()=>b?.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=()=>{p=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)})}})})}),p.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){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(b?.value.$el.querySelectorAll(".k-table-row[data-grid-row-index]")).forEach(r=>r.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 n=(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},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(h);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=b.value.$el.closest(".k-grid");t&&t.classList.add("fk-grid")})},q=()=>{try{const t=()=>{try{const n=Array.from(b.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(b.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=b.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)}),p&&(p.disconnect(),p=null),d&&(d.disconnect(),d=null),k.forEach(e=>e()),k.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 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 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 w=e.querySelector(".k-link");w&&w.click()}}};e.addEventListener("keydown",E,!0),k.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),k.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(b={}){const{activeByDefault:s=!1,autoActivateDelay:p=0}=b,d=c.ref(s);function k(){d.value=!0}function h(){d.value=!1}function g(){d.value=!d.value}return c.onMounted(()=>{p>0&&!s&&setTimeout(()=>{k()},p)}),c.onUnmounted(()=>{}),{isGridActive:d,activateGrid:k,deactivateGrid:h,toggleGrid:g}}m.useGridA11y=S,m.useGridComposableEx=x,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
@@ -3,9 +3,6 @@
3
3
  *
4
4
  * Composable that augments a Kendo-based grid with accessible keyboard navigation,
5
5
  * focus management, and custom behaviors for column header menus (filter/sort).
6
- *
7
- * (Contents preserved from previous grid-keyboard-navigation.ts; only the export name
8
- * and filename were changed to reflect the new composable name.)
9
6
  */
10
7
  import { type Ref } from "vue";
11
8
  import type { GridSortChangeEvent } from "./types/kendo";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featherk/composables",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "main": "dist/featherk-composables.umd.js",
5
5
  "module": "dist/featherk-composables.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,4 +29,4 @@
29
29
  "access": "public"
30
30
  },
31
31
  "license": "MIT"
32
- }
32
+ }