@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
1 lines • 6.42 kB
Source Map (JSON)
{"version":3,"file":"create-scoped-keydown-handler.cjs","names":["findElementAncestor"],"sources":["../../../../src/core/utils/create-scoped-keydown-handler/create-scoped-keydown-handler.ts"],"sourcesContent":["import { findElementAncestor } from '../find-element-ancestor/find-element-ancestor';\n\nfunction getPreviousIndex(current: number, elements: HTMLButtonElement[], loop: boolean) {\n for (let i = current - 1; i >= 0; i -= 1) {\n if (!elements[i].disabled) {\n return i;\n }\n }\n\n if (loop) {\n for (let i = elements.length - 1; i > -1; i -= 1) {\n if (!elements[i].disabled) {\n return i;\n }\n }\n }\n\n return current;\n}\n\nfunction getNextIndex(current: number, elements: HTMLButtonElement[], loop: boolean) {\n for (let i = current + 1; i < elements.length; i += 1) {\n if (!elements[i].disabled) {\n return i;\n }\n }\n\n if (loop) {\n for (let i = 0; i < elements.length; i += 1) {\n if (!elements[i].disabled) {\n return i;\n }\n }\n }\n\n return current;\n}\n\n/** Validates that target element is on the same level as sibling, used to filter out children that have the same sibling selector */\nfunction onSameLevel(\n target: HTMLButtonElement,\n sibling: HTMLButtonElement,\n parentSelector: string\n) {\n return (\n findElementAncestor(target, parentSelector) === findElementAncestor(sibling, parentSelector)\n );\n}\n\ninterface GetElementsSiblingsInput {\n /** Selector used to find parent node, for example '[role=\"tablist\"]', '.mantine-Text-root' */\n parentSelector: string;\n\n /** Selector used to find element siblings, for example '[data-tab]' */\n siblingSelector: string;\n\n /** Determines whether next/previous indices should loop */\n loop?: boolean;\n\n /** Determines which arrow keys will be used */\n orientation: 'vertical' | 'horizontal';\n\n /** Text direction */\n dir?: 'rtl' | 'ltr';\n\n /** Determines whether element should be clicked when focused with keyboard event */\n activateOnFocus?: boolean;\n\n /** External keydown event */\n onKeyDown?: (event: React.KeyboardEvent<HTMLButtonElement>) => void;\n}\n\nexport function createScopedKeydownHandler({\n parentSelector,\n siblingSelector,\n onKeyDown,\n loop = true,\n activateOnFocus = false,\n dir = 'rtl',\n orientation,\n}: GetElementsSiblingsInput) {\n return (event: React.KeyboardEvent<HTMLButtonElement>) => {\n onKeyDown?.(event);\n\n const elements = Array.from(\n findElementAncestor(event.currentTarget, parentSelector)?.querySelectorAll<HTMLButtonElement>(\n siblingSelector\n ) || []\n ).filter((node) => onSameLevel(event.currentTarget, node, parentSelector));\n\n const current = elements.findIndex((el) => event.currentTarget === el);\n const _nextIndex = getNextIndex(current, elements, loop);\n const _previousIndex = getPreviousIndex(current, elements, loop);\n const nextIndex = dir === 'rtl' ? _previousIndex : _nextIndex;\n const previousIndex = dir === 'rtl' ? _nextIndex : _previousIndex;\n\n switch (event.key) {\n case 'ArrowRight': {\n if (orientation === 'horizontal') {\n event.stopPropagation();\n event.preventDefault();\n elements[nextIndex].focus();\n activateOnFocus && elements[nextIndex].click();\n }\n\n break;\n }\n\n case 'ArrowLeft': {\n if (orientation === 'horizontal') {\n event.stopPropagation();\n event.preventDefault();\n elements[previousIndex].focus();\n activateOnFocus && elements[previousIndex].click();\n }\n\n break;\n }\n\n case 'ArrowUp': {\n if (orientation === 'vertical') {\n event.stopPropagation();\n event.preventDefault();\n elements[_previousIndex].focus();\n activateOnFocus && elements[_previousIndex].click();\n }\n\n break;\n }\n\n case 'ArrowDown': {\n if (orientation === 'vertical') {\n event.stopPropagation();\n event.preventDefault();\n elements[_nextIndex].focus();\n activateOnFocus && elements[_nextIndex].click();\n }\n\n break;\n }\n\n case 'Home': {\n event.stopPropagation();\n event.preventDefault();\n !elements[0].disabled && elements[0].focus();\n break;\n }\n\n case 'End': {\n event.stopPropagation();\n event.preventDefault();\n const last = elements.length - 1;\n !elements[last].disabled && elements[last].focus();\n break;\n }\n }\n };\n}\n"],"mappings":";;;AAEA,SAAS,iBAAiB,SAAiB,UAA+B,MAAe;CACvF,KAAK,IAAI,IAAI,UAAU,GAAG,KAAK,GAAG,KAAK,GACrC,IAAI,CAAC,SAAS,GAAG,UACf,OAAO;CAIX,IAAI;OACG,IAAI,IAAI,SAAS,SAAS,GAAG,IAAI,IAAI,KAAK,GAC7C,IAAI,CAAC,SAAS,GAAG,UACf,OAAO;CAAA;CAKb,OAAO;AACT;AAEA,SAAS,aAAa,SAAiB,UAA+B,MAAe;CACnF,KAAK,IAAI,IAAI,UAAU,GAAG,IAAI,SAAS,QAAQ,KAAK,GAClD,IAAI,CAAC,SAAS,GAAG,UACf,OAAO;CAIX,IAAI;OACG,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GACxC,IAAI,CAAC,SAAS,GAAG,UACf,OAAO;CAAA;CAKb,OAAO;AACT;;AAGA,SAAS,YACP,QACA,SACA,gBACA;CACA,OACEA,8BAAAA,oBAAoB,QAAQ,cAAc,MAAMA,8BAAAA,oBAAoB,SAAS,cAAc;AAE/F;AAyBA,SAAgB,2BAA2B,EACzC,gBACA,iBACA,WACA,OAAO,MACP,kBAAkB,OAClB,MAAM,OACN,eAC2B;CAC3B,QAAQ,UAAkD;EACxD,YAAY,KAAK;EAEjB,MAAM,WAAW,MAAM,KACrBA,8BAAAA,oBAAoB,MAAM,eAAe,cAAc,GAAG,iBACxD,eACF,KAAK,CAAC,CACR,EAAE,QAAQ,SAAS,YAAY,MAAM,eAAe,MAAM,cAAc,CAAC;EAEzE,MAAM,UAAU,SAAS,WAAW,OAAO,MAAM,kBAAkB,EAAE;EACrE,MAAM,aAAa,aAAa,SAAS,UAAU,IAAI;EACvD,MAAM,iBAAiB,iBAAiB,SAAS,UAAU,IAAI;EAC/D,MAAM,YAAY,QAAQ,QAAQ,iBAAiB;EACnD,MAAM,gBAAgB,QAAQ,QAAQ,aAAa;EAEnD,QAAQ,MAAM,KAAd;GACE,KAAK;IACH,IAAI,gBAAgB,cAAc;KAChC,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SAAS,WAAW,MAAM;KAC1B,mBAAmB,SAAS,WAAW,MAAM;IAC/C;IAEA;GAGF,KAAK;IACH,IAAI,gBAAgB,cAAc;KAChC,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SAAS,eAAe,MAAM;KAC9B,mBAAmB,SAAS,eAAe,MAAM;IACnD;IAEA;GAGF,KAAK;IACH,IAAI,gBAAgB,YAAY;KAC9B,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SAAS,gBAAgB,MAAM;KAC/B,mBAAmB,SAAS,gBAAgB,MAAM;IACpD;IAEA;GAGF,KAAK;IACH,IAAI,gBAAgB,YAAY;KAC9B,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SAAS,YAAY,MAAM;KAC3B,mBAAmB,SAAS,YAAY,MAAM;IAChD;IAEA;GAGF,KAAK;IACH,MAAM,gBAAgB;IACtB,MAAM,eAAe;IACrB,CAAC,SAAS,GAAG,YAAY,SAAS,GAAG,MAAM;IAC3C;GAGF,KAAK,OAAO;IACV,MAAM,gBAAgB;IACtB,MAAM,eAAe;IACrB,MAAM,OAAO,SAAS,SAAS;IAC/B,CAAC,SAAS,MAAM,YAAY,SAAS,MAAM,MAAM;IACjD;GACF;EACF;CACF;AACF"}