@featherk/composables 0.17.0 → 0.18.0
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 +62 -15
- package/dist/address.cjs +2 -0
- package/dist/address.cjs.map +1 -0
- package/dist/address.mjs +6 -0
- package/dist/address.mjs.map +1 -0
- package/dist/chunks/useActiveIdRegistry-BG8VR__Z.mjs +43 -0
- package/dist/chunks/useActiveIdRegistry-BG8VR__Z.mjs.map +1 -0
- package/dist/chunks/useActiveIdRegistry-C1b00tqW.cjs +2 -0
- package/dist/chunks/useActiveIdRegistry-C1b00tqW.cjs.map +1 -0
- package/dist/chunks/useCompositeId-DyrJ0Fqj.cjs +2 -0
- package/dist/chunks/useCompositeId-DyrJ0Fqj.cjs.map +1 -0
- package/dist/chunks/useCompositeId-xg87ehXQ.mjs +9 -0
- package/dist/chunks/useCompositeId-xg87ehXQ.mjs.map +1 -0
- package/dist/chunks/useFieldsetValidationKit-7W1XDbbr.cjs +2 -0
- package/dist/chunks/useFieldsetValidationKit-7W1XDbbr.cjs.map +1 -0
- package/dist/chunks/useFieldsetValidationKit-x0Rh48mE.mjs +64 -0
- package/dist/chunks/useFieldsetValidationKit-x0Rh48mE.mjs.map +1 -0
- package/dist/chunks/useGridRowAction-Di25yeqb.cjs +2 -0
- package/dist/chunks/useGridRowAction-Di25yeqb.cjs.map +1 -0
- package/dist/chunks/useGridRowAction-akjwkV_F.mjs +532 -0
- package/dist/chunks/useGridRowAction-akjwkV_F.mjs.map +1 -0
- package/dist/chunks/useIntersectionObserver-A4xQV_pl.mjs +23 -0
- package/dist/chunks/useIntersectionObserver-A4xQV_pl.mjs.map +1 -0
- package/dist/chunks/useIntersectionObserver-CfgNAo2N.cjs +2 -0
- package/dist/chunks/useIntersectionObserver-CfgNAo2N.cjs.map +1 -0
- package/dist/chunks/useMaskedDateInput-COOksUM_.mjs +276 -0
- package/dist/chunks/useMaskedDateInput-COOksUM_.mjs.map +1 -0
- package/dist/chunks/useMaskedDateInput-wRnYlR3L.cjs +2 -0
- package/dist/chunks/useMaskedDateInput-wRnYlR3L.cjs.map +1 -0
- package/dist/chunks/useMaskedDateRangeInput-DST5aQe0.cjs +2 -0
- package/dist/chunks/useMaskedDateRangeInput-DST5aQe0.cjs.map +1 -0
- package/dist/chunks/useMaskedDateRangeInput-owYSg36r.mjs +328 -0
- package/dist/chunks/useMaskedDateRangeInput-owYSg36r.mjs.map +1 -0
- package/dist/chunks/useMaskedTimeInput-DPd2lysd.cjs +2 -0
- package/dist/chunks/useMaskedTimeInput-DPd2lysd.cjs.map +1 -0
- package/dist/chunks/useMaskedTimeInput-DjtBdvsf.mjs +363 -0
- package/dist/chunks/useMaskedTimeInput-DjtBdvsf.mjs.map +1 -0
- package/dist/chunks/usePopupMenu-CZLtgrXk.mjs +296 -0
- package/dist/chunks/usePopupMenu-CZLtgrXk.mjs.map +1 -0
- package/dist/chunks/usePopupMenu-DeTWakUH.cjs +2 -0
- package/dist/chunks/usePopupMenu-DeTWakUH.cjs.map +1 -0
- package/dist/chunks/usePopupTrap-DQudM6YA.cjs +2 -0
- package/dist/chunks/usePopupTrap-DQudM6YA.cjs.map +1 -0
- package/dist/chunks/usePopupTrap-DeuXHLZR.mjs +105 -0
- package/dist/chunks/usePopupTrap-DeuXHLZR.mjs.map +1 -0
- package/dist/chunks/useUSAddress-BYZZfbHc.cjs +2 -0
- package/dist/chunks/useUSAddress-BYZZfbHc.cjs.map +1 -0
- package/dist/chunks/useUSAddress-CRcqdqrZ.mjs +291 -0
- package/dist/chunks/useUSAddress-CRcqdqrZ.mjs.map +1 -0
- package/dist/chunks/useWindow-C4XFBuJA.mjs +210 -0
- package/dist/chunks/useWindow-C4XFBuJA.mjs.map +1 -0
- package/dist/chunks/useWindow-Yia2I6zX.cjs +2 -0
- package/dist/chunks/useWindow-Yia2I6zX.cjs.map +1 -0
- package/dist/date/useMaskedDateInput.d.ts +4 -0
- package/dist/date.cjs +2 -0
- package/dist/date.cjs.map +1 -0
- package/dist/date.mjs +5 -0
- package/dist/date.mjs.map +1 -0
- package/dist/{docs-manifest.js → docs-manifest.mjs} +24 -24
- package/dist/form.cjs +2 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.mjs +6 -0
- package/dist/form.mjs.map +1 -0
- package/dist/geometry.cjs +2 -0
- package/dist/geometry.cjs.map +1 -0
- package/dist/geometry.mjs +60 -0
- package/dist/geometry.mjs.map +1 -0
- package/dist/grid.cjs +2 -0
- package/dist/grid.cjs.map +1 -0
- package/dist/grid.mjs +7 -0
- package/dist/grid.mjs.map +1 -0
- package/dist/id.cjs +2 -0
- package/dist/id.cjs.map +1 -0
- package/dist/id.mjs +5 -0
- package/dist/id.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +37 -0
- package/dist/index.mjs.map +1 -0
- package/dist/menu.cjs +2 -0
- package/dist/menu.cjs.map +1 -0
- package/dist/menu.mjs +5 -0
- package/dist/menu.mjs.map +1 -0
- package/dist/observer.cjs +2 -0
- package/dist/observer.cjs.map +1 -0
- package/dist/observer.mjs +5 -0
- package/dist/observer.mjs.map +1 -0
- package/dist/range/useMaskedDateRangeInput.d.ts +4 -0
- package/dist/range.cjs +2 -0
- package/dist/range.cjs.map +1 -0
- package/dist/range.mjs +5 -0
- package/dist/range.mjs.map +1 -0
- package/dist/registry.cjs +2 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.mjs +6 -0
- package/dist/registry.mjs.map +1 -0
- package/dist/time/useMaskedTimeInput.d.ts +4 -0
- package/dist/time.cjs +2 -0
- package/dist/time.cjs.map +1 -0
- package/dist/time.mjs +5 -0
- package/dist/time.mjs.map +1 -0
- package/dist/trap.cjs +2 -0
- package/dist/trap.cjs.map +1 -0
- package/dist/trap.mjs +7 -0
- package/dist/trap.mjs.map +1 -0
- package/dist/window.cjs +2 -0
- package/dist/window.cjs.map +1 -0
- package/dist/window.mjs +6 -0
- package/dist/window.mjs.map +1 -0
- package/docs/date/useMaskedDateInput.md +3 -0
- package/docs/range/useMaskedDateRangeInput.md +6 -7
- package/docs/time/useMaskedTimeInput.md +3 -0
- package/package.json +38 -36
- package/dist/featherk-composables.es.js +0 -2628
- package/dist/featherk-composables.es.js.map +0 -1
- package/dist/featherk-composables.umd.js +0 -2
- package/dist/featherk-composables.umd.js.map +0 -1
- /package/dist/{docs-manifest.d.ts → docs-manifest.d.mts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGridRowAction-Di25yeqb.cjs","sources":["../../src/grid/useGridA11y.ts","../../src/grid/useGridActiveFilter.ts","../../src/grid/useGridRowAction.ts"],"sourcesContent":["/**\n * useGridA11y\n *\n * Composable that augments a Kendo-based grid with accessible keyboard navigation,\n * focus management, and custom behaviors for column header menus (filter/sort).\n */\n\nimport { ref, nextTick, onBeforeUnmount, type Ref } from \"vue\";\nimport type {\n SortDescriptor,\n GridColumnProps,\n GridSortChangeEvent,\n} from \"../types/kendo\";\n\nexport const useGridA11y = (\n gridRef: Ref<any>,\n options?: { focusableMenuSelectors?: string[] },\n) => {\n // Reference to the active filter button for focus management\n const activeFilterButton = ref<HTMLElement | null>(null);\n\n let a11yInitialized = false;\n\n // Mutation observer for dynamic elements\n let mutationsObserver: MutationObserver | null = null;\n\n // Observer to watch row changes within this grid instance so we can reassert tabindex\n let rowObserver: MutationObserver | null = null;\n\n const headerThTeardownCallbacks: Array<() => void> = [];\n\n const intraRowNavigationSelector =\n \".k-table-row[data-grid-row-index] [tabindex]\";\n\n const getGridRootElement = (): HTMLElement | null => {\n const host = gridRef?.value?.$el as HTMLElement | undefined;\n if (!host) return null;\n return host.closest(\".k-grid\") as HTMLElement | null;\n };\n\n const isWithinThisGridScope = (target: HTMLElement): boolean => {\n const gridRoot = getGridRootElement();\n if (!gridRoot) return false;\n\n // Preferred scope: Kendo's grid accessibility region.\n const ariaRoot = target.closest(\".k-grid-aria-root\") as HTMLElement | null;\n if (ariaRoot) {\n return gridRoot.contains(ariaRoot);\n }\n\n // Fallback for markup/theme variance across versions.\n const fallbackScope = target.closest(\n \".k-grid-header, .k-grid-content, .k-grid-content-locked\",\n ) as HTMLElement | null;\n return fallbackScope ? gridRoot.contains(fallbackScope) : false;\n };\n\n const isEnterActivation = (event: KeyboardEvent) => {\n return (\n event.key === \"Enter\" ||\n event.code === \"Enter\" ||\n event.code === \"NumpadEnter\"\n );\n };\n\n // Helper to get columns from gridRef\n const getColumns = () => {\n return (gridRef?.value as any)?.columns as Array<GridColumnProps>;\n };\n\n /**\n * Key activation handler for a header's column menu / filter button.\n *\n * Purpose:\n * - Enables keyboard users (Space / Enter) to open the Kendo column menu without\n * triggering unintended default behaviors (like sorting) that Kendo may bind\n * to key events on header elements.\n *\n * Behavior:\n * - Intercepts Space / Enter (including legacy \"Spacebar\") keys.\n * - Prevents default and stops propagation to suppress native scrolling or\n * upstream sort handlers.\n * - Records the event target in `activeFilterButton` so focus can be restored\n * after the menu closes (Escape handling in other logic).\n * - Programmatically clicks the target to open the column menu.\n *\n * Accessibility:\n * - Treats the underlying element as a button activation; complements ARIA\n * role=\"button\" applied elsewhere.\n *\n * Idempotency / Safety:\n * - Safe to attach to multiple buttons; only responds to specified keys.\n *\n * @param event KeyboardEvent originating on the column menu trigger.\n * @returns void\n */\n const handleFilterButtonKeyboardListener = (event: KeyboardEvent) => {\n const key = event.key || event.code;\n if ([\" \", \"Spacebar\", \"Space\"].includes(key) || isEnterActivation(event)) {\n // Prevents the unintended default sorting on keyboard event\n event.preventDefault();\n event.stopPropagation();\n\n // Store reference to the filter button when it is activated\n activeFilterButton.value = event.target as HTMLElement;\n\n // Trigger the click event on the filter button\n (event.target as HTMLElement).click();\n }\n };\n\n /**\n * Keyboard interaction handler for an open Kendo Grid column menu (filter / sort menu).\n *\n * Scope:\n * - Attached to each popup (\".k-animation-container\") when it appears.\n * - Handles navigation inside two possible UI states:\n * 1. Column menu list (\".k-columnmenu-item\") items.\n * 2. Filter form container (\".k-filter-menu-container\") with inputs/buttons.\n *\n * Keys:\n * - Escape:\n * * Closes semantic focus context by returning focus to `activeFilterButton`\n * (the header or trigger element that opened the menu) and exits.\n * - ArrowUp / ArrowDown (only when NOT in a filter form container):\n * * Cycles focus among menu items (wraps at ends).\n * - Tab within filter form container:\n * * Traps focus inside the filter form’s focusable elements; Shift+Tab reverses.\n * - Tab when not in a filter form container:\n * * Cycles focus among sibling trigger buttons adjacent to the originating header\n * (previousElementSibling / nextElementSibling of the active trigger).\n *\n * Focus Model:\n * - Relies on `activeFilterButton` (external ref) to restore focus after Escape or\n * to traverse sibling triggers on plain Tab outside a filter form.\n *\n * Guard Clauses:\n * - Returns immediately if no `activeFilterButton` is set (defensive).\n *\n * Assumptions:\n * - Menu markup follows Kendo’s structure with \".k-animation-container\" wrapper.\n * - Filter form presence is detectable via \".k-filter-menu-container\".\n * - Menu action items carry class \".k-columnmenu-item\".\n *\n * Side Effects:\n * - Calls focus() on controlled elements.\n * - Prevents default + stops propagation for handled keys to avoid native browser\n * tab order / scroll interference or Kendo’s default key handling.\n *\n * @param event KeyboardEvent dispatched on the popup container.\n * @returns void\n */\n const handleFilterMenuKeyboardListener = (event: KeyboardEvent) => {\n if (!activeFilterButton.value) return;\n if (event.code === \"Escape\") {\n event.stopPropagation();\n\n if (activeFilterButton.value) {\n activeFilterButton.value.focus();\n }\n return;\n }\n\n const menuItems = Array.from(\n document.querySelectorAll(\n \".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item\",\n ),\n ) as HTMLElement[];\n\n const filterContainer = document.querySelector(\n \".k-animation-container .k-popup .k-column-menu .k-filter-menu-container\",\n );\n\n if (filterContainer) {\n if (event.code === \"Tab\") {\n const defaultFocusable = [\n // \".k-filter-menu-container .k-dropdownlist[tabindex='0']\",\n // \".k-filter-menu-container input.k-input-inner:not([tabindex='-1']):not([disabled])\",\n // \".k-filter-menu-container button:not([tabindex='-1']):not([disabled])\",\n // \".k-checkbox\",\n \".k-dropdownlist[tabindex='0']\",\n \"input.k-input-inner:not([tabindex='-1']):not([disabled])\",\n \"button:not([tabindex='-1']):not([disabled])\",\n \".k-checkbox\",\n ];\n const focusableSelectors =\n options?.focusableMenuSelectors ?? defaultFocusable;\n const focusableElements = Array.from(\n filterContainer.querySelectorAll(focusableSelectors.join(\",\")),\n ) as HTMLElement[];\n\n if (focusableElements.length === 0) return;\n\n const focusedIndex = focusableElements.findIndex(\n (el) => el === document.activeElement,\n );\n let targetIndex: number;\n if (focusedIndex === -1) {\n targetIndex = 0;\n } else if (event.shiftKey) {\n targetIndex =\n (focusedIndex - 1 + focusableElements.length) %\n focusableElements.length;\n } else {\n targetIndex = (focusedIndex + 1) % focusableElements.length;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n focusableElements[targetIndex]?.focus();\n }\n } else {\n if (event.code === \"ArrowUp\" || event.code === \"ArrowDown\") {\n event.preventDefault();\n event.stopPropagation();\n\n const focusedIndex = menuItems.findIndex(\n (item) => item === document.activeElement,\n );\n\n let targetIndex = focusedIndex;\n if (event.code === \"ArrowUp\") {\n targetIndex =\n focusedIndex > 0 ? focusedIndex - 1 : menuItems.length - 1;\n } else if (event.code === \"ArrowDown\") {\n targetIndex =\n focusedIndex < menuItems.length - 1 ? focusedIndex + 1 : 0;\n }\n\n menuItems[targetIndex]?.focus();\n return;\n }\n }\n\n if (event.code === \"Tab\") {\n event.preventDefault();\n event.stopPropagation();\n\n const currentTh = activeFilterButton.value?.parentElement;\n const targetTh = event.shiftKey\n ? currentTh?.previousElementSibling\n : currentTh?.nextElementSibling;\n const targetButton = targetTh?.querySelector(\n \"button, [role='button'], .k-link\",\n ) as HTMLElement;\n\n targetButton?.focus();\n }\n };\n\n /**\n * Observes the document body for Kendo Grid column menu (filter/sort menu) lifecycle.\n *\n * Responsibilities:\n * - Creates a MutationObserver watching document.body (childList + subtree) for nodes\n * with class \"k-animation-container\" (the popup wrapper Kendo inserts/ removes).\n * - For each added animation container:\n * * If the active header is NOT sortable (data-feather-k-sortable=\"false\"), removes\n * any menu wrapper whose text content includes \"sort\" (case-insensitive) to hide\n * irrelevant sort options from the popup UI.\n * * Attaches a keydown listener (handleFilterMenuKeyboardListener) to enable:\n * - ArrowUp / ArrowDown navigation through menu items\n * - Tab trapping / cycling in filter form content\n * - Escape to return focus to the triggering header/filter button\n * * Uses a retry loop (nextTick + setTimeout) to auto-focus & auto-activate a lone\n * menu item (common when only \"Filter\" is present) improving keyboard efficiency.\n * - For each removed animation container (and any nested ones), detaches the keydown listener.\n *\n * Behavior Notes:\n * - Sort option pruning only occurs when the triggering column is not sortable. The sortable\n * state is inferred from data attribute: data-feather-k-sortable === \"true\".\n * - Active trigger element is read from activeFilterButton ref.\n * - Focus restoration after Escape is handled inside handleFilterMenuKeyboardListener.\n *\n * Idempotency:\n * - Calling multiple times replaces the prior MutationObserver reference (without an\n * explicit disconnect first). Current usage calls it once during initA11y(). If future\n * calls are needed, consider disconnecting mutationsObserver beforehand to avoid\n * duplicate observers.\n *\n * Cleanup:\n * - Observer and event listeners are disconnected/removed by cleanupFilterButtonAccessibility().\n *\n * Side Effects:\n * - Mutates DOM (removal of unwanted sort menu items).\n * - Registers global (document-level subtree) MutationObserver.\n * - Adds / removes keydown event listeners on popup containers.\n *\n * Preconditions:\n * - document.body is available (browser environment).\n * - activeFilterButton may reference the header/filter button that opened the menu.\n *\n * @returns void\n */\n const setupFilterMenuObserver = () => {\n mutationsObserver = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as HTMLElement;\n if (element.classList.contains(\"k-animation-container\")) {\n const column = activeFilterButton.value as HTMLElement;\n if (column) {\n const sortable = column.dataset.featherKSortable === \"true\";\n if (!sortable) {\n nextTick(() => {\n const wrappers = element.querySelectorAll(\n \".k-columnmenu-item-wrapper\",\n );\n wrappers.forEach((wrapper) => {\n const sortMenu = wrapper.textContent\n ?.toLowerCase()\n .includes(\"sort\");\n if (sortMenu) {\n wrapper.remove();\n }\n });\n });\n }\n }\n element.addEventListener(\n \"keydown\",\n handleFilterMenuKeyboardListener,\n );\n nextTick(() => {\n const tryFocusMenuItem = () => {\n const menuItems = Array.from(\n element.querySelectorAll(\n \".k-animation-container .k-popup .k-column-menu .k-columnmenu-item-wrapper .k-columnmenu-item\",\n ),\n ) as HTMLElement[];\n\n if (menuItems.length === 1) {\n menuItems[0].focus();\n menuItems[0].click();\n (tryFocusMenuItem as any).attempts = 0; // reset attempts\n } else if (menuItems.length > 1) {\n (tryFocusMenuItem as any).attempts = 0; // reset attempts\n return;\n } else {\n if ((tryFocusMenuItem as any).attempts === undefined)\n (tryFocusMenuItem as any).attempts = 0;\n if ((tryFocusMenuItem as any).attempts++ < 3) {\n setTimeout(tryFocusMenuItem, 200);\n }\n }\n };\n tryFocusMenuItem();\n });\n }\n\n const animationContainers = element.querySelectorAll(\n \".k-animation-container\",\n );\n animationContainers.forEach((container) => {\n (container as HTMLElement).addEventListener(\n \"keydown\",\n handleFilterMenuKeyboardListener,\n );\n });\n }\n });\n\n mutation.removedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as HTMLElement;\n\n if (element.classList.contains(\"k-animation-container\")) {\n element.removeEventListener(\n \"keydown\",\n handleFilterMenuKeyboardListener,\n );\n }\n\n const animationContainers = element.querySelectorAll(\n \".k-animation-container\",\n );\n animationContainers.forEach((container) => {\n (container as HTMLElement).removeEventListener(\n \"keydown\",\n handleFilterMenuKeyboardListener,\n );\n });\n }\n });\n });\n });\n\n mutationsObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n };\n\n /**\n * Detects if an element is a form control or inside a form-like component.\n *\n * Purpose:\n * - Allows grid keyboard navigation to skip form controls, letting them handle\n * their own keyboard events naturally (e.g., cursor movement in inputs, spinners).\n *\n * Behavior:\n * - Returns true if the element itself is an input, textarea, select, or button.\n * - Returns true if the element is inside a Kendo form component:\n * * .k-picker-wrap (DatePicker, TimePicker, etc.)\n * * .k-input-inner (MaskedTextBox, TextBox, etc.)\n * * .k-textbox (generic Kendo textbox wrapper)\n * * [role=\"combobox\"] (dropdown, autocomplete, etc.)\n * - Returns false otherwise.\n *\n * @param el HTMLElement to check\n * @returns true if el is a form control that should manage its own keyboard events\n */\n const isFormControl = (el: HTMLElement): boolean => {\n const tagName = el.tagName.toLowerCase();\n\n // Check if element itself is a native form control\n if ([\"input\", \"textarea\", \"select\", \"button\"].includes(tagName)) {\n return true;\n }\n\n // Check if element is inside a Kendo form component wrapper\n if (\n el.closest(\n '.k-picker-wrap, .k-input-inner, .k-textbox, [role=\"combobox\"]',\n )\n ) {\n return true;\n }\n\n return false;\n };\n\n /**\n * Keyboard handler for the Grid root element (bind to @keydown on <Grid>).\n *\n * Responsibilities:\n * - ArrowUp / ArrowDown: Move focus (and tabindex=\"0\") between sibling data rows\n * (.k-table-row[data-grid-row-index]).\n * - ArrowLeft / ArrowRight: When focus is on (or within) a data row, cycle focus\n * among tabbable descendants inside that row (as defined by\n * `intraRowNavigationSelector`). If the row itself has focus and no descendant\n * is focused, ArrowRight focuses the first intra-row tabbable element.\n * - Escape: If focus is currently inside a data row descendant, collapse focus\n * back to the parent row element (ensuring it has tabindex=\"0\").\n * - Enter / Space: Reserved for future enhancements and prevented when part of\n * the core navigation key group to avoid unintended native behaviors.\n *\n * Special Cases:\n * - If the event target is a grid column menu button (identified by both\n * \"k-grid-header-menu\" and \"k-grid-column-menu\" classes), the handler stores\n * it in `activeFilterButton` and returns so menu logic can proceed elsewhere.\n * - If the event target is a form control (input, button, Kendo component, etc.),\n * the handler exits early to allow the form control to manage its own keyboard\n * events naturally.\n *\n * Side Effects:\n * - Mutates tabindex attributes on row elements to maintain exactly one row\n * with tabindex=\"0\" in the vertical navigation path.\n * - Calls focus() on target rows or intra-row elements as navigation occurs.\n *\n * Defensive Checks:\n * - Exits early if the event object, its type, or target is missing.\n * - Exits early if target is a form control (via isFormControl).\n *\n * Assumptions:\n * - `gridRef.value.$el` is the Grid root (or inside it) and queryable.\n * - Data rows use the selector \".k-table-row[data-grid-row-index]\".\n *\n * @param e KeyboardEvent from the Grid's @keydown binding.\n * @returns void\n */\n const handleGridKeyDown = (e: KeyboardEvent) => {\n if (!e.type) return;\n\n if (!e) {\n return;\n }\n const target = e.target as HTMLElement;\n if (!target) return;\n\n const row = target.closest(\n \".k-table-row[data-grid-row-index]\",\n ) as HTMLElement | null;\n\n // Scope keyboard handling to this grid instance's a11y/content region.\n // This prevents toolbar and external controls from being intercepted.\n if (!isWithinThisGridScope(target)) {\n return;\n }\n\n // EXIT EARLY: Don't apply grid navigation to form controls.\n // Exception: allow ArrowLeft/ArrowRight intra-row navigation for focusable\n // controls that live inside a data row.\n const isIntraRowHorizontalNavKey = [\"ArrowLeft\", \"ArrowRight\"].includes(\n e.code,\n );\n if (isFormControl(target) && !(row && isIntraRowHorizontalNavKey)) {\n return;\n }\n\n // If Escape pressed and the active element is inside a data row,\n // move focus to the parent .k-table-row and ensure its tabindex=0.\n if (e.code === \"Escape\") {\n const focusedRow = (\n document.activeElement as HTMLElement | null\n )?.closest(\".k-table-row[data-grid-row-index]\") as HTMLElement | null;\n if (focusedRow) {\n // Justified: We're handling Escape to collapse focus from descendant to row.\n // Prevent default to override native browser behavior (dialog close, etc.)\n e.preventDefault();\n // Justified: Stop propagation to prevent parent containers from seeing this Escape.\n e.stopPropagation();\n try {\n const rows = Array.from(\n gridRef?.value.$el.querySelectorAll(\n \".k-table-row[data-grid-row-index]\",\n ) as HTMLElement[],\n );\n rows.forEach((r) => r.setAttribute(\"tabindex\", \"-1\"));\n } catch {\n // ignore if gridRef not available\n }\n focusedRow.setAttribute(\"tabindex\", \"0\");\n focusedRow.focus();\n return;\n }\n }\n\n // Handle navigation keys (only prevent/stop for keys we actually handle)\n if (\n [\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"Enter\",\n \"NumpadEnter\",\n \"Space\",\n ].includes(e.code)\n ) {\n // Check if target is a grid column menu button\n if (\n target.classList.contains(\"k-grid-header-menu\") &&\n target.classList.contains(\"k-grid-column-menu\")\n ) {\n activeFilterButton.value = target as HTMLElement;\n // Let menu logic proceed elsewhere; don't prevent or stop\n return;\n }\n\n // If event came from within a data row, handle row/intra-row navigation\n if (row) {\n if ([\"ArrowDown\", \"ArrowUp\"].includes(e.code)) {\n // Justified: We're handling ArrowUp/Down for vertical row navigation.\n // Prevent default to override native browser scrolling behavior.\n e.preventDefault();\n // Justified: Stop propagation to prevent parent containers from seeing this.\n e.stopPropagation();\n\n // Find the next/previous element sibling that actually has a tabindex.\n const findSiblingRow = (\n start: Element,\n direction: \"next\" | \"previous\",\n ): HTMLElement | null => {\n let sib =\n direction === \"next\"\n ? start.nextElementSibling\n : start.previousElementSibling;\n while (sib) {\n const el = sib as HTMLElement;\n try {\n if (el.classList.contains(\"k-table-row\")) {\n return el;\n }\n } catch {\n // Defensive: some nodes might throw when accessed; skip them.\n }\n sib =\n direction === \"next\"\n ? sib.nextElementSibling\n : sib.previousElementSibling;\n }\n return null;\n };\n\n const targetRow =\n e.code === \"ArrowDown\"\n ? findSiblingRow(row, \"next\")\n : findSiblingRow(row, \"previous\");\n\n if (targetRow) {\n row.setAttribute(\"tabindex\", \"-1\");\n targetRow.setAttribute(\"tabindex\", \"0\");\n targetRow.focus();\n }\n return;\n }\n if ([\"ArrowLeft\", \"ArrowRight\"].includes(e.code)) {\n // Justified: We're handling ArrowLeft/Right for intra-row focus navigation.\n // Prevent default to override native text cursor movement only when we handle the event.\n e.preventDefault();\n // Justified: Stop propagation to prevent parent containers from seeing this.\n e.stopPropagation();\n\n const tabbable = row.querySelectorAll(\n intraRowNavigationSelector,\n ) as NodeListOf<HTMLElement>;\n if (tabbable.length === 0) return;\n\n // Find currently focused element within the row\n let currentIndex = Array.from(tabbable).findIndex(\n (el) => el === document.activeElement,\n );\n\n if (currentIndex === -1 && document.activeElement === row) {\n // If no element is focused, focus the first tabbable element\n (tabbable[0] as HTMLElement).focus();\n return;\n }\n\n if (e.code === \"ArrowRight\") {\n currentIndex =\n currentIndex === tabbable.length - 1 ? 0 : currentIndex + 1;\n } else if (e.code === \"ArrowLeft\") {\n currentIndex =\n currentIndex === 0 ? tabbable.length - 1 : currentIndex - 1;\n }\n (tabbable[currentIndex] as HTMLElement).focus();\n\n return;\n }\n }\n\n // Enter, Space: Not currently handled for grid navigation.\n // Don't prevent by default to allow native behavior or other handlers to respond.\n }\n };\n\n /**\n * Applies a FeatherK-specific CSS hook class to the Kendo Grid root element.\n *\n * Behavior:\n * - On the next Vue microtask (nextTick), locates the closest ancestor with the\n * \".k-grid\" class starting from `gridRef.value.$el`.\n * - Adds the \"fk-grid\" class to that element (no-op if already present).\n *\n * Purpose:\n * - Provides a styling hook so external FeatherK stylesheets can target grids\n * that have been enhanced by this composable without relying on brittle\n * selectors or global overrides.\n *\n * Safety / Idempotency:\n * - Safe to call multiple times; subsequent calls simply re-add the same class.\n * - Silently does nothing if `gridRef.value` or its `$el` is not yet available.\n *\n * Side Effects:\n * - Mutates the DOM by adding a CSS class to the grid root element.\n *\n * No cleanup is currently required (class need not be removed on unmount).\n *\n * @returns void\n */\n const setupGridStyling = () => {\n nextTick(() => {\n const gridHost = gridRef.value?.$el as Element | undefined;\n if (!gridHost || typeof gridHost.closest !== \"function\") return;\n\n const gridElement = gridHost.closest(\".k-grid\") as HTMLElement | null;\n if (gridElement) gridElement.classList.add(\"fk-grid\");\n });\n };\n\n /**\n * Sets up row-level (tr) keyboard navigation for the Kendo Grid instance.\n *\n * Behavior:\n * 1. Ensures exactly one data row ('.k-table-row[data-grid-row-index]') has tabindex=\"0\"\n * (the current keyboard focus target) and all others have tabindex=\"-1\".\n * 2. Establishes an internal helper (ensureSingleTabindex) that:\n * - Re-calculates which row should retain tabindex=\"0\" after DOM changes.\n * - Preserves a focused row if its descendants currently contain focus.\n * - Falls back to the first row when no focused row exists.\n * 3. Applies initial tabindex values immediately (first row gets focusable state).\n * 4. Observes the grid <tbody> ('.k-table-tbody') with a MutationObserver so that when\n * Kendo re-renders rows (sorting, filtering, paging, data updates), tabindex\n * constraints are re-applied with minimal work.\n *\n * Rationale:\n * The default Kendo Grid may introduce multiple tabbable descendants, making\n * keyboard traversal verbose. Restricting tab stops to a single row improves\n * accessibility by:\n * - Reducing unnecessary Tab presses.\n * - Enabling arrow-key driven intra-row navigation (handled elsewhere).\n *\n * Assumptions / Dependencies:\n * - gridRef.value.$el points to (or is inside) a rendered Kendo Grid root.\n * - Data rows carry class 'k-table-row' and attribute 'data-grid-row-index'.\n * - Runs in a browser environment (document, MutationObserver available).\n *\n * Error Handling:\n * - Wrapped in try/catch; failures are logged and do not throw upstream.\n *\n * Idempotency:\n * - Safe to call more than once; repeated calls will attach at most one observer\n * per invocation lifecycle (previous observer persists). Caller is expected\n * to avoid redundant calls or add future cleanup if needed.\n *\n * Side Effects:\n * - Mutates DOM attributes (tabindex) of grid row elements.\n * - Creates a MutationObserver stored in the closure (rowObserver) for teardown\n * via cleanupFilterButtonAccessibility().\n *\n * @returns void\n */\n const setupTrNavigation = () => {\n if (rowObserver) {\n // Already set up\n return;\n }\n try {\n /**\n * Ensure exactly one grid row is keyboard-focusable (tabindex=\"0\") and make all others unfocusable (tabindex=\"-1\").\n *\n * This function:\n * - Queries the grid DOM for rows using the selector \".k-table-row[data-grid-row-index]\".\n * - If no rows are found, it returns immediately.\n * - If exactly one row already has tabindex=\"0\", it does nothing.\n * - Otherwise it clears tabindex to \"-1\" on all matched rows, then:\n * - If the currently focused element is inside one of the matched rows, that row is preserved as tabindex=\"0\".\n * - If there is no such focused row, the first matched row is set to tabindex=\"0\".\n *\n * Notes:\n * - The function mutates DOM attributes (tabindex) of the matched row elements.\n * - It expects to run in a browser environment where `document.activeElement` is available and where\n * `gridRef.value.$el` points to the grid root element containing rows matching the selector.\n * - Any runtime errors encountered while querying or updating the DOM are caught and logged via console.error;\n * the function will not throw.\n *\n * @remarks\n * Use this helper to maintain a single tabbable row for keyboard navigation / accessibility purposes within a custom grid component.\n *\n * @returns void\n *\n * @example\n * // Keep one tabbable row in the grid after rendering or when rows change\n * ensureSingleTabindex();\n */\n const ensureSingleTabindex = () => {\n try {\n const rows = Array.from(\n gridRef.value.$el.querySelectorAll(\n \".k-table-row[data-grid-row-index]\",\n ) as HTMLElement[],\n );\n\n if (!rows || rows.length === 0) return;\n\n // Count rows that currently have tabindex=\"0\"\n const zeroTabRows = rows.filter(\n (r) => r.getAttribute(\"tabindex\") === \"0\",\n );\n\n // If exactly one row already has tabindex=0, nothing to do\n if (zeroTabRows.length === 1) return;\n\n // Prefer to preserve the currently focused row if it's inside the rows\n const focusedRow = rows.find(\n (r) =>\n r === document.activeElement ||\n r.contains(document.activeElement),\n );\n\n // Clear all to -1\n rows.forEach((r) => r.setAttribute(\"tabindex\", \"-1\"));\n\n if (focusedRow) {\n focusedRow.setAttribute(\"tabindex\", \"0\");\n return;\n }\n\n // Fallback: set first row to tabindex=0\n rows[0].setAttribute(\"tabindex\", \"0\");\n } catch (err) {\n console.error(\"ensureSingleTabindex error:\", err);\n }\n };\n\n // Initial setup\n const rows = Array.from(\n gridRef.value.$el.querySelectorAll(\n \".k-table-row[data-grid-row-index]\",\n ) as HTMLElement[],\n );\n if (rows.length > 0) {\n rows.forEach((row, index) => {\n row.setAttribute(\"tabindex\", index === 0 ? \"0\" : \"-1\");\n });\n }\n\n // Observe tbody for row changes (Kendo will recreate rows on sort/filter/paging)\n const tbody = gridRef.value.$el.querySelector(\".k-table-tbody\");\n if (tbody) {\n rowObserver = new MutationObserver(() => {\n // Reassert single tabindex after DOM changes. Keep work minimal.\n ensureSingleTabindex();\n });\n rowObserver.observe(tbody, { childList: true, subtree: true });\n }\n } catch (error) {\n console.error(\"Error setting up row navigation:\", error);\n }\n };\n\n /**\n * Initializes accessibility behavior for grid filter buttons.\n *\n * - Queries the DOM for elements matching the selector\n * \".k-grid-header .k-grid-header-menu.k-grid-column-menu\".\n * - For each matched element, sets the ARIA role to \"button\" and attaches a\n * keydown event listener using handleFilterButtonKeyboardListener.\n * - Invokes setupFilterMenuObserver() to observe changes to filter menus.\n * - Invokes setupHeaderThTriggers() to initialize any header-related triggers.\n *\n * @returns void\n */\n const setupFilterButtonAccessibility = () => {\n nextTick(() => {\n const filterSelector =\n \".k-grid-header .k-grid-header-menu.k-grid-column-menu\";\n const filterButtons = document.querySelectorAll(filterSelector);\n if (filterButtons) {\n filterButtons.forEach((el) => {\n el.setAttribute(\"role\", \"button\");\n (el as HTMLElement).addEventListener(\n \"keydown\",\n handleFilterButtonKeyboardListener,\n );\n });\n }\n\n setupFilterMenuObserver();\n\n setupHeaderThTriggers();\n });\n };\n\n /**\n * Cleans up all accessibility (a11y) resources installed by useGridA11y.\n *\n * Includes:\n * - Removes keydown listeners from column menu / filter buttons.\n * - Disconnects the column menu MutationObserver.\n * - Disconnects the row MutationObserver.\n * - Executes and clears header <th> teardown callbacks.\n *\n * Idempotent: safe to call multiple times.\n */\n const cleanupGridA11y = () => {\n const filterSelector =\n \".k-grid-header .k-grid-header-menu.k-grid-column-menu\";\n const filterButtons = document.querySelectorAll(filterSelector);\n if (filterButtons) {\n filterButtons.forEach((el) => {\n (el as HTMLElement).removeEventListener(\n \"keydown\",\n handleFilterButtonKeyboardListener,\n );\n });\n }\n\n if (mutationsObserver) {\n mutationsObserver.disconnect();\n mutationsObserver = null;\n }\n\n if (rowObserver) {\n rowObserver.disconnect();\n rowObserver = null;\n }\n\n headerThTeardownCallbacks.forEach((teardown) => teardown());\n headerThTeardownCallbacks.length = 0;\n };\n\n /**\n * Initializes accessible interaction handlers for header <th> cells in a Kendo-style grid.\n *\n * Finds all header cells under \".k-grid-header .k-table-thead th\" and for each header cell:\n * - Locates the column menu button (\".k-grid-header-menu.k-grid-column-menu\") and sets its tabindex to -1.\n * - Reads column metadata from getColumns() (external dependency) to set data attributes:\n * - data-feather-k-field (column.field)\n * - data-feather-k-filterable (\"false\" if column.filterable === false, otherwise \"true\")\n * - data-feather-k-sortable (\"false\" if column.sortable === false, otherwise \"true\")\n * - Determines whether the header is filterable or sortable using the above data attributes.\n * - Sets appropriate cursor styles (pointer for interactive headers, default otherwise).\n * - Configures ARIA/sniffable roles:\n * - If filterable: tabindex=\"0\", role=\"button\", aria-haspopup=\"menu\"\n * - If not filterable: tabindex=\"0\", role=\"columnheader\", aria-haspopup is removed\n *\n * Adds event handlers for each header cell:\n * - click handler:\n * - If filterable: prevents default/propagation and opens the column menu (via the menu button click). Also records the header element on activeFilterButton (external ref) for focus return.\n * - If not filterable but sortable: synthesizes an \"Enter\" KeyboardEvent and dispatches it on the header to trigger sort behavior and records activeFilterButton.\n * - Clicks originating from a column resizer (closest \".k-column-resizer\") are ignored.\n * - keydown handler (registered in capture phase for the header cell):\n * - On Enter or Space when filterable or sortable:\n * - Sets activeFilterButton to the header and focuses it.\n * - If filterable: prevents default/propagation and opens the column menu.\n * - If sortable: clicks the first \".k-link\" inside the header to trigger sort.\n *\n * Additionally, attaches a capturing keydown handler to the enclosing THEAD element (\".k-grid-header .k-table-thead\"):\n * - Prevents default/immediate propagation for Enter/Space on headers that are neither filterable nor sortable.\n * - This protects non-interactive header cells from activating default keyboard behavior.\n *\n * All event listeners added by this setup are recorded as teardown callbacks into the\n * headerThTeardownCallbacks array (external), so they can be removed later to avoid leaks.\n *\n * Notes / assumptions:\n * - Relies on globals/closures: document, getColumns(), activeFilterButton (ref-like object), and headerThTeardownCallbacks (array).\n * - Intended to enhance accessibility and keyboard interaction for grid headers by mapping Kendo column behaviors\n * to appropriate ARIA roles, keyboard handling, and focus management.\n *\n * @remarks\n * - Does not return a value; side effects are applied directly to DOM and external state.\n * - Uses attribute-based decisions to maintain compatibility with dynamically-updated column metadata.\n *\n * @returns void\n */\n const setupHeaderThTriggers = () => {\n const headerCells = document.querySelectorAll(\n \".k-grid-header .k-table-thead th\",\n );\n\n headerCells.forEach((th, index) => {\n if ((th as HTMLElement).dataset.featherKHeaderBound === \"true\") {\n return;\n }\n\n const btn = th.querySelector(\n \".k-grid-header-menu.k-grid-column-menu\",\n ) as HTMLElement | null;\n if (!btn) return;\n\n const columns = getColumns();\n\n if (columns && columns[index]) {\n const field = columns[index].field ?? \"\";\n th.setAttribute(\"data-feather-k-field\", field);\n th.setAttribute(\n \"data-feather-k-filterable\",\n columns[index].filterable === false ? \"false\" : \"true\",\n );\n th.setAttribute(\n \"data-feather-k-sortable\",\n columns[index].sortable === false ? \"false\" : \"true\",\n );\n }\n\n const filterable =\n (th as HTMLElement).dataset.featherKFilterable !== \"false\";\n const sortable = (th as HTMLElement).dataset.featherKSortable !== \"false\";\n\n btn.setAttribute(\"tabindex\", \"-1\");\n\n if (filterable || sortable) {\n (th as HTMLElement).style.cursor = \"pointer\";\n } else {\n (th as HTMLElement).style.cursor = \"default\";\n }\n if (filterable) {\n th.setAttribute(\"tabindex\", \"0\");\n th.setAttribute(\"role\", \"button\");\n th.setAttribute(\"aria-haspopup\", \"menu\");\n } else {\n th.setAttribute(\"tabindex\", \"0\");\n th.setAttribute(\"role\", \"columnheader\");\n th.removeAttribute(\"aria-haspopup\");\n }\n const openMenu = (e: Event | KeyboardEvent) => {\n if ((e.target as HTMLElement)?.closest(\".k-column-resizer\")) return;\n\n activeFilterButton.value = th as HTMLElement; // focus return target\n btn.click();\n };\n\n const clickHandler = (e: Event) => {\n if (filterable) {\n activeFilterButton.value = th as HTMLElement;\n e.preventDefault();\n e.stopPropagation();\n openMenu(e);\n } else if (sortable) {\n activeFilterButton.value = th as HTMLElement;\n const columnLink = th.querySelector(\".k-link\");\n if (columnLink) {\n (columnLink as HTMLElement).click();\n }\n }\n };\n (th as HTMLElement).addEventListener(\"click\", clickHandler);\n headerThTeardownCallbacks.push(() => {\n (th as HTMLElement).removeEventListener(\"click\", clickHandler);\n });\n\n // Keyboard activation for sortable/filterable header cells.\n //\n // Enter, Numpad Enter, and Space are intentionally handled identically here:\n // - preventDefault + stopPropagation are applied for ALL three keys so\n // none of them leak to native browser or Kendo's own key handlers,\n // which could trigger a duplicate or conflicting sort/scroll action.\n // - For sortable-only headers the sort is triggered by clicking the\n // .k-link element directly (the same path the browser follows on a\n // real mouse click and what Space was already using). This keeps\n // the asc → desc → unsorted cycle consistent regardless of key used.\n //\n // IMPORTANT: Do not restore the old synthetic KeyboardEvent dispatch\n // (th.dispatchEvent(new KeyboardEvent('keydown', { key:'Enter', ... }))).\n // That approach caused Enter/Numpad Enter to re-enter this handler\n // recursively and be silently swallowed by the isSorting guard in\n // ClaimLog.vue, making every press after the first appear to do nothing.\n const keyHandler = (e: KeyboardEvent) => {\n if (\n (isEnterActivation(e) || e.code === \"Space\") &&\n (filterable || sortable)\n ) {\n e.preventDefault();\n e.stopPropagation();\n\n activeFilterButton.value = th as HTMLElement;\n activeFilterButton.value.focus();\n if (filterable) {\n openMenu(e);\n } else if (sortable) {\n const columnLink = th.querySelector(\".k-link\");\n if (columnLink) {\n (columnLink as HTMLElement).click();\n }\n }\n }\n };\n (th as HTMLElement).addEventListener(\"keydown\", keyHandler, true);\n headerThTeardownCallbacks.push(() => {\n (th as HTMLElement).removeEventListener(\"keydown\", keyHandler, true);\n });\n\n (th as HTMLElement).dataset.featherKHeaderBound = \"true\";\n });\n\n const thead = document.querySelector(\".k-grid-header .k-table-thead\");\n if (\n thead &&\n (thead as HTMLElement).dataset.featherKHeaderBound !== \"true\"\n ) {\n const theadKeyboardHandler = (e: KeyboardEvent) => {\n const th = (e.target as HTMLElement).closest(\"th\");\n if (!th) return;\n if (\n (isEnterActivation(e) || e.code === \"Space\") &&\n th.dataset.featherKFilterable === \"false\" &&\n th.dataset.featherKSortable === \"false\"\n ) {\n e.preventDefault();\n e.stopImmediatePropagation();\n }\n };\n (thead as HTMLElement).addEventListener(\n \"keydown\",\n theadKeyboardHandler,\n true, // NOTE: capture phase\n );\n headerThTeardownCallbacks.push(() => {\n (thead as HTMLElement).removeEventListener(\n \"keydown\",\n theadKeyboardHandler,\n true,\n );\n });\n\n (thead as HTMLElement).dataset.featherKHeaderBound = \"true\";\n }\n };\n\n /**\n * Handles grid sort change events with special-casing for column menu interactions and non-sortable columns.\n *\n * This function inspects the DOM target that triggered the sort change and the grid column metadata to\n * determine whether the default sort handling should proceed or be suppressed. It:\n * - Ignores events coming from the column menu toggle (element with class \"k-link\").\n * - When a column menu item (element with class \"k-columnmenu-item\") is selected for a non-sortable field,\n * prevents modifying the sort descriptors for that field.\n * - Otherwise, if a custom handler is provided, schedules focusing of the active filter button (if present)\n * on the next tick and then invokes the custom handler with the original event.\n *\n * @param event - Optional GridSortChangeEvent. The implementation expects a nested structure where\n * the originating HTMLElement is available at `event.event.event.target`, the field at\n * `event.event.field`, and the current sort descriptors at `event.event.sort`.\n * @param customHandler - Optional callback to be executed when the standard handling path is allowed.\n * The callback will be invoked inside `nextTick` after attempting to focus the\n * active filter button.\n * @returns void\n *\n * @remarks\n * - This function relies on `getColumns()` to obtain column metadata and on an `activeFilterButton` ref\n * to optionally restore focus. It performs early returns when the event target or columns are unavailable,\n * or when the column menu toggle was the source of the event.\n */\n const handleSortChange = function (\n event?: GridSortChangeEvent,\n customHandler?: any,\n ) {\n const target = event?.event?.event.target as HTMLElement;\n const columns = getColumns();\n if (!target || !columns) return;\n\n const menuOpened = target.classList.contains(\"k-grid-header-menu\");\n const menuItemSelected = target.classList.contains(\"k-columnmenu-item\");\n\n const fieldIsSortable =\n columns.find((c) => c.field === event?.event?.field)?.sortable && true;\n\n if (menuOpened) return;\n\n if (menuItemSelected && !fieldIsSortable) {\n const sortDescriptor = event?.event?.sort as SortDescriptor[];\n sortDescriptor?.filter(\n (s: SortDescriptor) => s.field !== event?.event?.field,\n );\n return;\n }\n if (typeof customHandler === \"function\") {\n nextTick(() => {\n if (activeFilterButton.value) activeFilterButton.value.focus();\n customHandler(event);\n // Kendo recreates header <th> nodes after each sort, which discards any\n // previously attached keydown listeners. Re-running setupHeaderThTriggers\n // here (after the sort data has been applied) reattaches those listeners\n // to the new nodes so subsequent Enter/Space presses continue to work.\n nextTick(() => {\n setupHeaderThTriggers();\n });\n });\n }\n };\n\n /**\n * Initialize accessibility (a11y) features for the grid.\n *\n * This function is idempotent — it will perform initialization only once.\n * On the first call it sets the internal `a11yInitialized` flag and runs\n * the following setup routines:\n * - setupGridStyling()\n * - setupTrNavigation()\n * - setupFilterButtonAccessibility()\n *\n * Subsequent calls do nothing.\n *\n * Use this during component setup/mount to ensure grid accessibility helpers\n * are registered exactly once.\n *\n * @returns void\n */\n const initA11y = () => {\n if (a11yInitialized) return;\n try {\n setupGridStyling();\n setupTrNavigation();\n setupFilterButtonAccessibility();\n a11yInitialized = true;\n } catch (err) {\n console.error(\"initA11y failed:\", err);\n // Clean up any partial listeners/observers so a retry can succeed.\n cleanupGridA11y();\n }\n };\n\n onBeforeUnmount(() => {\n cleanupGridA11y();\n });\n return {\n activeFilterButton,\n handleGridKeyDown,\n handleSortChange,\n initA11y,\n };\n};\n","import { type Ref } from \"vue\";\nimport type {\n CompositeFilterDescriptor,\n FilterDescriptor,\n} from \"@progress/kendo-data-query\";\n\n/**\n * Type guard for composite filter groups.\n */\nconst isCompositeFilter = (\n filterItem: unknown,\n): filterItem is CompositeFilterDescriptor => {\n return (\n !!filterItem &&\n typeof filterItem === \"object\" &&\n \"filters\" in filterItem &&\n Array.isArray((filterItem as CompositeFilterDescriptor).filters)\n );\n};\n\n/**\n * Type guard for leaf filter nodes.\n */\nconst isFilterDescriptor = (\n filterItem: unknown,\n): filterItem is FilterDescriptor => {\n return (\n !!filterItem && typeof filterItem === \"object\" && \"field\" in filterItem\n );\n};\n\n/**\n * Recursively checks whether a filter tree contains a filter for a specific field.\n */\nconst hasFieldFilterRecursive = (\n filterItem: unknown,\n field: string,\n): boolean => {\n if (!filterItem || typeof filterItem !== \"object\") {\n return false;\n }\n\n if (!isCompositeFilter(filterItem)) {\n if (!isFilterDescriptor(filterItem)) {\n return false;\n }\n return filterItem.field === field;\n }\n\n return filterItem.filters.some((child) =>\n hasFieldFilterRecursive(child, field),\n );\n};\n\n/**\n * Returns a reactive function that resolves to `\"active\"` when the given\n * field has a filter applied in the current filter tree, or `\"\"` when it\n * does not.\n *\n * Bind the returned function directly to column `headerClassName` inside a\n * `computed` — Vue tracks the `filterRef` dependency automatically; no\n * manual update call is needed.\n *\n * @param filterRef - Reactive grid filter descriptor (the same ref bound to the Grid).\n * @returns `(field: string) => \"active\" | \"\"`\n *\n * @example\n * const activeClass = useGridActiveFilter(filterRef);\n *\n * const columns = computed(() => [\n * { field: \"ProductName\", headerClassName: activeClass(\"ProductName\") },\n * ]);\n */\nexport const useGridActiveFilter = (\n filterRef: Ref<CompositeFilterDescriptor | undefined>,\n): ((field: string) => string) => {\n return (field: string): string => {\n if (!filterRef.value?.filters) return \"\";\n return filterRef.value.filters.some((f) =>\n hasFieldFilterRecursive(f, field),\n )\n ? \"active\"\n : \"\";\n };\n};\n","/**\n * useGridRowAction\n *\n * Composable that provides safe, accessible row click and keyboard activation\n * handlers for Kendo UI for Vue Grid instances.\n *\n * Highlights:\n * - Guards against triggers when clicking interactive elements (buttons, links, inputs, checkboxes).\n * - Suppresses activation when the user is highlighting/selecting text inside a cell.\n * - Restricts mouse actions to primary (left) click.\n * - Provides keyboard activation (Enter / Space) for focused grid rows.\n * - Action-agnostic: executes consumer-supplied `onRowAction` callback with `dataItem` and context.\n */\n\nimport type { Ref } from \"vue\";\nimport type { GridRowClickEvent } from \"../types/kendo\";\n\nexport interface RowActionCoordinates {\n clientX: number;\n clientY: number;\n pageX: number;\n pageY: number;\n screenX: number;\n screenY: number;\n}\n\nexport interface RowActionContext<T = any> {\n /**\n * Data item associated with the activated row.\n */\n dataItem: T;\n /**\n * Row index if provided by Kendo event or row DOM attribute.\n */\n rowIndex?: number;\n /**\n * Field name if click originated on a specific column cell.\n */\n field?: string;\n /**\n * Original native browser event.\n */\n event: Event;\n /**\n * Interaction trigger type ('click' | 'keyboard').\n */\n triggerType: \"click\" | \"keyboard\";\n /**\n * Modifier key states during activation.\n */\n ctrlKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n altKey: boolean;\n /**\n * Pointer coordinates of the activation. Derived from the native mouse event\n * for clicks, or from the focused row's bounding box for keyboard activation.\n * `undefined` when neither source is available.\n */\n coordinates?: RowActionCoordinates;\n}\n\nexport interface UseGridRowActionOptions<T = any> {\n /**\n * Action callback invoked when a row is clicked or activated via keyboard.\n */\n onRowAction: (dataItem: T, context: RowActionContext<T>) => void;\n /**\n * Optional ref to Kendo Grid component instance or array/ref of data items.\n * Used to resolve the row `dataItem` when row is activated via grid-level `@keydown`.\n */\n gridRef?: Ref<any>;\n dataItems?: Ref<T[]> | T[];\n /**\n * Additional CSS selectors inside row cells that should prevent the row action.\n */\n ignoreSelectors?: string[];\n /**\n * Custom predicate function to check if an event target should be ignored.\n * Return `true` to ignore row action, `false` to proceed.\n */\n shouldIgnoreTarget?: (target: HTMLElement, event: Event) => boolean;\n /**\n * Whether to enable Enter/Space keyboard activation on focused grid rows.\n * Default: true\n */\n enableKeyboardAction?: boolean;\n}\n\nexport interface UseGridRowActionReturn<T = any> {\n /**\n * Grid row click handler to bind to Kendo Grid's `@rowclick` event.\n */\n handleRowClick: (event: GridRowClickEvent<T>) => void;\n /**\n * Grid keyboard handler to bind to Kendo Grid's `@keydown` event.\n * Optionally accepts explicit `dataItem` if bound at the row/slot level.\n */\n handleRowKeyDown: (event: KeyboardEvent, explicitDataItem?: T) => void;\n /**\n * Helper function to test if a given DOM element is an ignored target.\n */\n isIgnoredTarget: (target: HTMLElement, event?: Event) => boolean;\n}\n\nconst DEFAULT_IGNORE_SELECTORS = [\n \"button\",\n \"a\",\n \"input\",\n \"select\",\n \"textarea\",\n '[role=\"button\"]',\n '[role=\"checkbox\"]',\n '[role=\"link\"]',\n '[role=\"menuitem\"]',\n '[role=\"option\"]',\n \".k-checkbox\",\n \".k-button\",\n \".k-dropdown\",\n \".k-picker\",\n \".k-hierarchy-cell\",\n \".k-grid-header-menu\",\n \".k-column-menu\",\n];\n\nexport const useGridRowAction = <T = any>(\n options: UseGridRowActionOptions<T>,\n): UseGridRowActionReturn<T> => {\n const {\n onRowAction,\n gridRef,\n dataItems,\n ignoreSelectors = [],\n shouldIgnoreTarget,\n enableKeyboardAction = true,\n } = options;\n\n const combinedIgnoreSelectors = [\n ...DEFAULT_IGNORE_SELECTORS,\n ...ignoreSelectors,\n ];\n\n /**\n * Tests if an event target element or any of its parent elements match ignored selectors\n * or custom predicate filters.\n */\n const isIgnoredTarget = (target: HTMLElement, event?: Event): boolean => {\n if (!target) return false;\n\n // Check if element or ancestor matches any ignored selector\n const selectorString = combinedIgnoreSelectors.join(\",\");\n if (target.closest && target.closest(selectorString)) {\n return true;\n }\n\n // Check custom predicate if provided\n if (shouldIgnoreTarget && event) {\n return shouldIgnoreTarget(target, event);\n }\n\n return false;\n };\n\n /**\n * Reads the row index from the nearest data row element.\n */\n const resolveRowIndex = (target?: HTMLElement | null): number | undefined => {\n const rowEl = target?.closest?.(\n \".k-table-row[data-grid-row-index]\",\n ) as HTMLElement | null;\n const attr = rowEl?.getAttribute(\"data-grid-row-index\");\n if (attr === null || attr === undefined) return undefined;\n const parsed = parseInt(attr, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n };\n\n /**\n * Extracts pointer coordinates from a native mouse/pointer event.\n */\n const getEventCoordinates = (\n nativeEvent: any,\n ): RowActionCoordinates | undefined => {\n if (!nativeEvent || typeof nativeEvent.clientX !== \"number\") {\n return undefined;\n }\n const scrollX = typeof window !== \"undefined\" ? window.scrollX || 0 : 0;\n const scrollY = typeof window !== \"undefined\" ? window.scrollY || 0 : 0;\n const screenX = typeof window !== \"undefined\" ? window.screenX || 0 : 0;\n const screenY = typeof window !== \"undefined\" ? window.screenY || 0 : 0;\n return {\n clientX: nativeEvent.clientX,\n clientY: nativeEvent.clientY,\n pageX:\n typeof nativeEvent.pageX === \"number\"\n ? nativeEvent.pageX\n : nativeEvent.clientX + scrollX,\n pageY:\n typeof nativeEvent.pageY === \"number\"\n ? nativeEvent.pageY\n : nativeEvent.clientY + scrollY,\n screenX:\n typeof nativeEvent.screenX === \"number\"\n ? nativeEvent.screenX\n : nativeEvent.clientX + screenX,\n screenY:\n typeof nativeEvent.screenY === \"number\"\n ? nativeEvent.screenY\n : nativeEvent.clientY + screenY,\n };\n };\n\n /**\n * Derives coordinates from an element's bounding box (keyboard activation).\n */\n const getElementCoordinates = (\n element: HTMLElement | null,\n ): RowActionCoordinates | undefined => {\n if (!element || typeof element.getBoundingClientRect !== \"function\") {\n return undefined;\n }\n const rect = element.getBoundingClientRect();\n if (!rect) return undefined;\n const clientX = rect.left;\n const clientY = rect.bottom;\n const scrollX = typeof window !== \"undefined\" ? window.scrollX || 0 : 0;\n const scrollY = typeof window !== \"undefined\" ? window.scrollY || 0 : 0;\n return {\n clientX,\n clientY,\n pageX: clientX + scrollX,\n pageY: clientY + scrollY,\n screenX: clientX,\n screenY: clientY,\n };\n };\n\n /**\n * Checks if user currently has active text selected inside the target row or container.\n */\n const hasTextSelection = (target?: HTMLElement | null): boolean => {\n if (typeof window === \"undefined\") return false;\n const selection = window.getSelection();\n if (!selection || selection.isCollapsed || !selection.toString().trim()) {\n return false;\n }\n if (target && selection.anchorNode && selection.focusNode) {\n const container =\n target.closest(\".k-table-row\") || target.closest(\".k-grid\") || target;\n const anchorParent =\n selection.anchorNode.nodeType === Node.ELEMENT_NODE\n ? (selection.anchorNode as HTMLElement)\n : selection.anchorNode.parentElement;\n const focusParent =\n selection.focusNode.nodeType === Node.ELEMENT_NODE\n ? (selection.focusNode as HTMLElement)\n : selection.focusNode.parentElement;\n const isAnchorInside = !!(\n anchorParent && container.contains(anchorParent)\n );\n const isFocusInside = !!(focusParent && container.contains(focusParent));\n return isAnchorInside || isFocusInside;\n }\n return true;\n };\n\n /**\n * Primary handler for Kendo Grid `@rowclick` event.\n */\n const handleRowClick = (event: GridRowClickEvent<T>): void => {\n const nativeEvent =\n (event as any)?.htmlEvent ||\n (event as any)?.event ||\n (event as any)?.nativeEvent ||\n (event instanceof Event ? event : undefined);\n\n // Only allow primary (left) mouse clicks\n if (\n nativeEvent &&\n nativeEvent.button !== undefined &&\n nativeEvent.button !== 0\n ) {\n return;\n }\n\n // Guard against ignored child element targets\n const target = (nativeEvent?.target ||\n (event as any)?.target) as HTMLElement | null;\n if (\n target &&\n isIgnoredTarget(target, nativeEvent || (event as unknown as Event))\n ) {\n return;\n }\n\n // Guard against active text highlighting / selection within the row/grid\n if (hasTextSelection(target)) {\n return;\n }\n\n const context: RowActionContext<T> = {\n dataItem: event.dataItem,\n rowIndex: event.rowIndex ?? resolveRowIndex(target),\n field: event.field,\n event: nativeEvent || (event as unknown as Event),\n triggerType: \"click\",\n ctrlKey: !!nativeEvent?.ctrlKey,\n shiftKey: !!nativeEvent?.shiftKey,\n metaKey: !!nativeEvent?.metaKey,\n altKey: !!nativeEvent?.altKey,\n coordinates:\n getEventCoordinates(nativeEvent) ?? getElementCoordinates(target),\n };\n\n onRowAction(event.dataItem, context);\n };\n\n /**\n * Primary handler for Grid `@keydown` event (Enter / Space on row).\n */\n const handleRowKeyDown = (\n event: KeyboardEvent,\n explicitDataItem?: T,\n ): void => {\n if (!enableKeyboardAction) return;\n\n const key = event.key || event.code;\n const isActivationKey =\n key === \"Enter\" ||\n key === \"NumpadEnter\" ||\n key === \" \" ||\n key === \"Space\" ||\n event.code === \"Enter\" ||\n event.code === \"NumpadEnter\" ||\n event.code === \"Space\";\n\n if (!isActivationKey) return;\n\n const target = event.target as HTMLElement | null;\n if (!target) return;\n\n // Check if event target is inside a data row\n const rowEl = target.closest(\n \".k-table-row[data-grid-row-index], .k-table-row\",\n ) as HTMLElement | null;\n if (!rowEl) return;\n\n // Guard against ignored child element targets\n if (isIgnoredTarget(target, event)) {\n return;\n }\n\n // Prevent default scrolling or unintended form submissions on Space / Enter\n event.preventDefault();\n event.stopPropagation();\n\n // Resolve row index from DOM attribute if available\n const rowIndexAttr = rowEl.getAttribute(\"data-grid-row-index\");\n const rowIndex =\n rowIndexAttr !== null ? parseInt(rowIndexAttr, 10) : undefined;\n\n // Resolve data item: explicitDataItem -> dataItems option -> gridRef option\n let resolvedDataItem: T | undefined = explicitDataItem;\n\n if (resolvedDataItem === undefined) {\n const itemsList =\n (Array.isArray(dataItems) ? dataItems : dataItems?.value) ||\n (gridRef?.value as any)?.props?.dataItems ||\n (gridRef?.value as any)?.dataItems;\n\n if (\n itemsList &&\n rowIndex !== undefined &&\n rowIndex >= 0 &&\n rowIndex < itemsList.length\n ) {\n resolvedDataItem = itemsList[rowIndex];\n }\n }\n\n if (resolvedDataItem === undefined) return;\n\n const context: RowActionContext<T> = {\n dataItem: resolvedDataItem,\n rowIndex,\n event,\n triggerType: \"keyboard\",\n ctrlKey: !!event.ctrlKey,\n shiftKey: !!event.shiftKey,\n metaKey: !!event.metaKey,\n altKey: !!event.altKey,\n coordinates: getElementCoordinates(rowEl),\n };\n\n onRowAction(resolvedDataItem, context);\n };\n\n return {\n handleRowClick,\n handleRowKeyDown,\n isIgnoredTarget,\n };\n};\n"],"names":["useGridA11y","gridRef","options","activeFilterButton","ref","a11yInitialized","mutationsObserver","rowObserver","headerThTeardownCallbacks","intraRowNavigationSelector","getGridRootElement","host","isWithinThisGridScope","target","gridRoot","ariaRoot","fallbackScope","isEnterActivation","event","getColumns","handleFilterButtonKeyboardListener","key","handleFilterMenuKeyboardListener","menuItems","filterContainer","defaultFocusable","focusableSelectors","focusableElements","focusedIndex","el","targetIndex","item","currentTh","setupFilterMenuObserver","mutations","mutation","node","element","column","nextTick","wrapper","tryFocusMenuItem","container","isFormControl","tagName","handleGridKeyDown","row","isIntraRowHorizontalNavKey","focusedRow","r","findSiblingRow","start","direction","sib","targetRow","tabbable","currentIndex","setupGridStyling","gridHost","gridElement","setupTrNavigation","ensureSingleTabindex","rows","err","index","tbody","error","setupFilterButtonAccessibility","filterButtons","setupHeaderThTriggers","cleanupGridA11y","teardown","th","btn","columns","field","filterable","sortable","openMenu","e","clickHandler","columnLink","keyHandler","thead","theadKeyboardHandler","handleSortChange","customHandler","menuOpened","menuItemSelected","fieldIsSortable","c","s","initA11y","onBeforeUnmount","isCompositeFilter","filterItem","isFilterDescriptor","hasFieldFilterRecursive","child","useGridActiveFilter","filterRef","f","DEFAULT_IGNORE_SELECTORS","useGridRowAction","onRowAction","dataItems","ignoreSelectors","shouldIgnoreTarget","enableKeyboardAction","combinedIgnoreSelectors","isIgnoredTarget","selectorString","resolveRowIndex","attr","parsed","getEventCoordinates","nativeEvent","scrollX","scrollY","screenX","screenY","getElementCoordinates","rect","clientX","clientY","hasTextSelection","selection","anchorParent","focusParent","isAnchorInside","isFocusInside","context","explicitDataItem","rowEl","rowIndexAttr","rowIndex","resolvedDataItem","itemsList"],"mappings":"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"}
|