@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 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
- const { activeFilterButton, handleGridKeyDown, handleSortChange } =
55
- useGridA11y(gridRef);
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
- ### 2) Wire keyboard handler on the Grid
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
- ### 3) Provide an accessible row renderer (`aria-label`)
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
- ### 4) Focus the active filter button after filter changes
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 your filter state and data here
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
- ### 5) Custom sort handling with composable helper
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
- ### 6) Summary checklist
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` (and optionally pass a custom callback)
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 E, onMounted as S, onBeforeUnmount as M, nextTick as p, onUnmounted as N } from "vue";
2
- const G = (f) => {
3
- const s = E(null);
4
- let m = null, u = null;
5
- const b = [], y = ".k-table-row[data-grid-row-index] [tabindex]", k = () => f?.value?.columns, h = (t) => {
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
- }, x = () => {
47
- m = new MutationObserver((t) => {
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" || p(() => {
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
- ), p(() => {
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
- }), m.observe(document.body, {
107
+ }), u.observe(document.body, {
108
108
  childList: !0,
109
109
  subtree: !0
110
110
  });
111
- }, L = (t) => {
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
- f?.value.$el.querySelectorAll(
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
- y
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
- }, D = () => {
186
- p(() => {
187
- const t = f.value.$el.closest(".k-grid");
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
- p(() => {
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
- }), x(), I();
236
+ }), L(), C();
237
237
  });
238
- }, C = () => {
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
- }), m && (m.disconnect(), m = null), u && (u.disconnect(), u = null), b.forEach((e) => e()), b.length = 0;
246
- }, I = () => {
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 = k();
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 w = new KeyboardEvent("keydown", {
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(w);
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 A = (c) => {
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 w = e.querySelector(".k-link");
294
- w && w.click();
293
+ const y = e.querySelector(".k-link");
294
+ y && y.click();
295
295
  }
296
296
  }
297
297
  };
298
- e.addEventListener("keydown", A, !0), b.push(() => {
299
- e.removeEventListener("keydown", A, !0);
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 = k();
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" && p(() => {
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 S(() => {
338
- D(), q(), T();
339
- }), M(() => {
340
- C();
344
+ return N(() => {
345
+ E();
341
346
  }), {
342
347
  activeFilterButton: s,
343
- handleGridKeyDown: L,
344
- handleSortChange: K
348
+ handleGridKeyDown: D,
349
+ handleSortChange: K,
350
+ initA11y: M
345
351
  };
346
352
  };
347
- function P(f = {}) {
348
- const { activeByDefault: s = !1, autoActivateDelay: m = 0 } = f, u = E(s);
349
- function b() {
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 y() {
358
+ function b() {
353
359
  u.value = !1;
354
360
  }
355
- function k() {
361
+ function w() {
356
362
  u.value = !u.value;
357
363
  }
358
- return S(() => {
359
- m > 0 && !s && setTimeout(() => {
360
- b();
361
- }, m);
362
- }), N(() => {
364
+ return G(() => {
365
+ p > 0 && !s && setTimeout(() => {
366
+ f();
367
+ }, p);
368
+ }), P(() => {
363
369
  }), {
364
370
  isGridActive: u,
365
- activateGrid: b,
366
- deactivateGrid: y,
367
- toggleGrid: k
371
+ activateGrid: f,
372
+ deactivateGrid: b,
373
+ toggleGrid: w
368
374
  };
369
375
  }
370
376
  export {
371
- G as useGridA11y,
372
- P as useGridComposableEx
377
+ O as useGridA11y,
378
+ B as useGridComposableEx
373
379
  };
@@ -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=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||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,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"})}));
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"})}));
@@ -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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featherk/composables",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "main": "dist/featherk-composables.umd.js",
5
5
  "module": "dist/featherk-composables.es.js",
6
6
  "types": "dist/index.d.ts",