@react-md/core 6.2.1 → 6.3.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.
Files changed (153) hide show
  1. package/dist/datetime/NativeDateField.d.ts +24 -0
  2. package/dist/datetime/NativeDateField.js +63 -0
  3. package/dist/datetime/NativeDateField.js.map +1 -0
  4. package/dist/datetime/NativeTimeField.d.ts +26 -0
  5. package/dist/datetime/NativeTimeField.js +63 -0
  6. package/dist/datetime/NativeTimeField.js.map +1 -0
  7. package/dist/datetime/useDateField.d.ts +120 -0
  8. package/dist/datetime/useDateField.js +35 -0
  9. package/dist/datetime/useDateField.js.map +1 -0
  10. package/dist/datetime/useTimeField.d.ts +124 -0
  11. package/dist/datetime/useTimeField.js +65 -0
  12. package/dist/datetime/useTimeField.js.map +1 -0
  13. package/dist/datetime/utils.d.ts +34 -0
  14. package/dist/datetime/utils.js +27 -0
  15. package/dist/datetime/utils.js.map +1 -0
  16. package/dist/draggable/utils.d.ts +3 -6
  17. package/dist/draggable/utils.js.map +1 -1
  18. package/dist/expansion-panel/ExpansionList.js +1 -1
  19. package/dist/expansion-panel/ExpansionList.js.map +1 -1
  20. package/dist/expansion-panel/useExpansionList.d.ts +2 -7
  21. package/dist/expansion-panel/useExpansionList.js.map +1 -1
  22. package/dist/form/FormMessage.js +3 -1
  23. package/dist/form/FormMessage.js.map +1 -1
  24. package/dist/form/FormMessageContainer.d.ts +2 -1
  25. package/dist/form/FormMessageContainer.js +3 -2
  26. package/dist/form/FormMessageContainer.js.map +1 -1
  27. package/dist/form/FormMessageCounter.d.ts +3 -2
  28. package/dist/form/FormMessageCounter.js +5 -2
  29. package/dist/form/FormMessageCounter.js.map +1 -1
  30. package/dist/form/Listbox.d.ts +3 -10
  31. package/dist/form/Listbox.js +8 -27
  32. package/dist/form/Listbox.js.map +1 -1
  33. package/dist/form/ListboxProvider.d.ts +17 -0
  34. package/dist/form/ListboxProvider.js +33 -1
  35. package/dist/form/ListboxProvider.js.map +1 -1
  36. package/dist/form/NativeSelect.js +1 -0
  37. package/dist/form/NativeSelect.js.map +1 -1
  38. package/dist/form/TextArea.js +1 -0
  39. package/dist/form/TextArea.js.map +1 -1
  40. package/dist/form/TextField.js +1 -0
  41. package/dist/form/TextField.js.map +1 -1
  42. package/dist/form/_form-message.scss +13 -0
  43. package/dist/form/_text-field.scss +12 -3
  44. package/dist/form/formMessageContainerStyles.d.ts +7 -0
  45. package/dist/form/formMessageContainerStyles.js +4 -2
  46. package/dist/form/formMessageContainerStyles.js.map +1 -1
  47. package/dist/form/sliderUtils.d.ts +3 -7
  48. package/dist/form/sliderUtils.js.map +1 -1
  49. package/dist/form/types.d.ts +13 -0
  50. package/dist/form/types.js.map +1 -1
  51. package/dist/form/useCombobox.d.ts +6 -2
  52. package/dist/form/useCombobox.js +8 -9
  53. package/dist/form/useCombobox.js.map +1 -1
  54. package/dist/form/useFormReset.d.ts +4 -1
  55. package/dist/form/useFormReset.js +9 -4
  56. package/dist/form/useFormReset.js.map +1 -1
  57. package/dist/form/useNumberField.d.ts +5 -5
  58. package/dist/form/useNumberField.js +10 -2
  59. package/dist/form/useNumberField.js.map +1 -1
  60. package/dist/form/useSelectCombobox.js +2 -2
  61. package/dist/form/useSelectCombobox.js.map +1 -1
  62. package/dist/form/useTextField.d.ts +76 -59
  63. package/dist/form/useTextField.js +7 -1
  64. package/dist/form/useTextField.js.map +1 -1
  65. package/dist/interaction/utils.d.ts +14 -0
  66. package/dist/interaction/utils.js +23 -12
  67. package/dist/interaction/utils.js.map +1 -1
  68. package/dist/menu/MenuBar.js +1 -1
  69. package/dist/menu/MenuBar.js.map +1 -1
  70. package/dist/menu/MenuItemTextField.d.ts +1 -2
  71. package/dist/menu/MenuItemTextField.js.map +1 -1
  72. package/dist/menu/MenuWidget.js +3 -2
  73. package/dist/menu/MenuWidget.js.map +1 -1
  74. package/dist/movement/constants.d.ts +10 -0
  75. package/dist/movement/constants.js +20 -4
  76. package/dist/movement/constants.js.map +1 -1
  77. package/dist/movement/types.d.ts +59 -10
  78. package/dist/movement/types.js.map +1 -1
  79. package/dist/movement/useKeyboardMovementProvider.d.ts +5 -1
  80. package/dist/movement/useKeyboardMovementProvider.js +171 -73
  81. package/dist/movement/useKeyboardMovementProvider.js.map +1 -1
  82. package/dist/tabs/useTabList.js +1 -1
  83. package/dist/tabs/useTabList.js.map +1 -1
  84. package/dist/test-utils/drag.d.ts +6 -9
  85. package/dist/transition/useCarousel.d.ts +2 -2
  86. package/dist/transition/useCarousel.js.map +1 -1
  87. package/dist/tree/Tree.js +1 -1
  88. package/dist/tree/Tree.js.map +1 -1
  89. package/dist/tree/useTreeMovement.d.ts +2 -1
  90. package/dist/tree/useTreeMovement.js +2 -1
  91. package/dist/tree/useTreeMovement.js.map +1 -1
  92. package/dist/types.d.ts +14 -0
  93. package/dist/types.js.map +1 -1
  94. package/dist/utils/getMiddleOfRange.d.ts +2 -3
  95. package/dist/utils/getMiddleOfRange.js.map +1 -1
  96. package/dist/utils/getPercentage.d.ts +2 -9
  97. package/dist/utils/getPercentage.js +1 -1
  98. package/dist/utils/getPercentage.js.map +1 -1
  99. package/dist/utils/getRangeSteps.d.ts +2 -3
  100. package/dist/utils/getRangeSteps.js +0 -3
  101. package/dist/utils/getRangeSteps.js.map +1 -1
  102. package/dist/utils/nearest.d.ts +2 -3
  103. package/dist/utils/nearest.js +0 -3
  104. package/dist/utils/nearest.js.map +1 -1
  105. package/dist/utils/trigonometry.d.ts +31 -0
  106. package/dist/utils/trigonometry.js +25 -0
  107. package/dist/utils/trigonometry.js.map +1 -0
  108. package/dist/window-splitter/useWindowSplitter.d.ts +1 -1
  109. package/dist/window-splitter/useWindowSplitter.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/datetime/NativeDateField.tsx +92 -0
  112. package/src/datetime/NativeTimeField.tsx +94 -0
  113. package/src/datetime/useDateField.ts +193 -0
  114. package/src/datetime/useTimeField.ts +233 -0
  115. package/src/datetime/utils.ts +48 -0
  116. package/src/draggable/utils.ts +3 -6
  117. package/src/expansion-panel/ExpansionList.tsx +2 -1
  118. package/src/expansion-panel/useExpansionList.ts +6 -12
  119. package/src/form/FormMessage.tsx +4 -0
  120. package/src/form/FormMessageContainer.tsx +8 -4
  121. package/src/form/FormMessageCounter.tsx +17 -6
  122. package/src/form/Listbox.tsx +18 -46
  123. package/src/form/ListboxProvider.ts +61 -1
  124. package/src/form/NativeSelect.tsx +1 -0
  125. package/src/form/TextArea.tsx +1 -0
  126. package/src/form/TextField.tsx +1 -0
  127. package/src/form/formMessageContainerStyles.ts +10 -2
  128. package/src/form/sliderUtils.ts +3 -7
  129. package/src/form/types.ts +15 -0
  130. package/src/form/useCombobox.ts +15 -10
  131. package/src/form/useFormReset.ts +12 -5
  132. package/src/form/useNumberField.ts +17 -14
  133. package/src/form/useSelectCombobox.ts +2 -2
  134. package/src/form/useTextField.ts +102 -69
  135. package/src/interaction/utils.ts +18 -20
  136. package/src/menu/MenuBar.tsx +1 -1
  137. package/src/menu/MenuItemTextField.tsx +1 -3
  138. package/src/menu/MenuWidget.tsx +4 -2
  139. package/src/movement/constants.ts +26 -4
  140. package/src/movement/types.ts +84 -19
  141. package/src/movement/useKeyboardMovementProvider.ts +209 -95
  142. package/src/tabs/useTabList.ts +1 -1
  143. package/src/test-utils/drag.ts +8 -12
  144. package/src/transition/useCarousel.ts +2 -2
  145. package/src/tree/Tree.tsx +1 -1
  146. package/src/tree/useTreeMovement.ts +4 -0
  147. package/src/types.ts +16 -0
  148. package/src/utils/getMiddleOfRange.ts +2 -3
  149. package/src/utils/getPercentage.ts +3 -11
  150. package/src/utils/getRangeSteps.ts +3 -3
  151. package/src/utils/nearest.ts +3 -3
  152. package/src/utils/trigonometry.ts +46 -0
  153. package/src/window-splitter/useWindowSplitter.ts +3 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/movement/types.ts"],"sourcesContent":["import type {\n FocusEvent,\n FocusEventHandler,\n KeyboardEvent,\n KeyboardEventHandler,\n MouseEvent,\n MouseEventHandler,\n} from \"react\";\n\nimport type {\n NonNullMutableRef,\n NonNullRef,\n UseStateSetter,\n} from \"../types.js\";\n\n/**\n * Set this to `\"roving\"` when:\n * - there are a group of focusable elements that have a `tabIndex={-1}`\n * - the container element defaults to having a `tabIndex={0}`\n * - if the container is focused, it should no longer be included in the normal\n * tab flow. Instead, the current focused element should be included instead\n * by changing its `tabIndex` from `-1` to `0`\n *\n * Set this to `\"virtual\"` when:\n * - the container element should never lose focus\n * - the \"focused\" element only gains focus styles instead of being focused\n * - the container element specifies an `aria-activedescendant` pointing to one\n * of the ids for the child \"focusable\" elements\n *\n * @since 6.0.0\n */\nexport type TabIndexBehavior = \"roving\" | \"virtual\";\n\n/**\n * This should be used for specific widgets that should not include all\n * focusable elements and instead only specific elements.\n *\n * @example\n * ```ts\n * const getExpansionPanelsOnly: GetFocusableElements = (container) =>\n * [...container.querySelectorAll(\".rmd-expansion-panel__button\")];\n *\n * const getTreeItemsOnly: GetFocusableElements = (container) =>\n * [...container.querySelectorAll(\"[role='treeitem']\")];\n * ```\n *\n * @defaultValue `getFocusableElements`\n * @see the default `getFocusableElements` function.\n */\nexport type GetFocusableElements = (\n container: HTMLElement,\n programmatic: boolean\n) => readonly HTMLElement[];\n\n/**\n * @since 5.0.0\n */\nexport interface KeyboardMovementConfiguration {\n /**\n * A list of keys that will attempt to increment the focus index by 1.\n *\n * @defaultValue `[\"ArrowDown\"]`\n */\n incrementKeys?: readonly string[];\n\n /**\n * A list of keys that will attempt to decrement the focus index by 1.\n *\n * @defaultValue `[\"ArrowUp\"]`\n */\n decrementKeys?: readonly string[];\n\n /**\n * A list of keys that will set the focus index to `0`.\n *\n * @defaultValue `[\"Home\"]`\n */\n jumpToFirstKeys?: readonly string[];\n\n /**\n * A list of keys that will set the focus index to the last focusable index.\n *\n * @defaultValue `[\"End\"]`\n */\n jumpToLastKeys?: readonly string[];\n}\n\n/**\n * The defined {@link KeyboardMovementConfiguration} that should be used for\n * custom keyboard focus behavior.\n *\n * @since 5.0.0\n */\nexport type KeyboardMovementConfig = Required<KeyboardMovementConfiguration>;\n\n/**\n * @since 5.0.0\n */\nexport interface KeyboardMovementBehavior {\n /**\n * Boolean if pressing a letter will focus the next item in the\n * {@link KeyboardMovementProvider} that starts with the same letter.\n *\n * @defaultValue `false`\n */\n searchable?: boolean;\n\n /**\n * Boolean if the {@link KeyboardMovementProvider} should allow the focus behavior\n * to loop from the first to last or last to first item instead of preventing\n * any new focus behavior. In other words... if the last item is focused and\n * the user presses a key that should advance the focus to the next focusable\n * element, should the focus stay on the current element or loop back and\n * focus the first focusable item.\n *\n * @defaultValue `false`\n */\n loopable?: boolean;\n\n /**\n * Boolean if elements that are `aria-disabled` or `disabled` should still be\n * able to gain focus.\n *\n * @defaultValue `false`\n */\n includeDisabled?: boolean;\n\n /**\n * Boolean if the keyboard movement is horizontal instead of vertical. This\n * updates the default keyboard config to use `ArrowRight` and `ArrowLeft`\n * instead of `ArrowDown` and `ArrowUp`,\n *\n * @since 5.1.2\n * @defaultValue `false`\n */\n horizontal?: boolean;\n}\n\n/**\n * @since 5.0.0\n * @since 6.0.0 Removed `attach`, `detach` and `watching`\n * @internal\n */\nexport interface KeyboardMovementContext\n extends Required<KeyboardMovementBehavior> {\n /** {@inheritDoc KeyboardMovementConfig} */\n config: NonNullRef<KeyboardMovementConfig>;\n\n /** @see {@link TabIndexBehavior} */\n tabIndexBehavior: TabIndexBehavior | undefined;\n\n /**\n * Note: This will only update if the {@link KeyboardMovementProviderOptions.tabIndexBehavior}\n * has been set to `\"roving\"` or `\"virtual\"`.\n */\n activeDescendantId: string;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface FocusableIndexOptions {\n focusables: readonly HTMLElement[];\n includeDisabled: boolean;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type GetDefaultFocusedIndex = (options: FocusableIndexOptions) => number;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type ExtendKeyDown<E extends HTMLElement> = (\n movementData: KeyboardMovementExtensionData<E>\n) => void;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementFocusChangeEvent {\n index: number;\n element: HTMLElement;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type KeyboardMovementFocusChangeEventHandler = (\n event: KeyboardMovementFocusChangeEvent\n) => void;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementExtensionData<E extends HTMLElement>\n extends KeyboardMovementContext {\n event: KeyboardEvent<E>;\n currentFocusIndex: NonNullMutableRef<number>;\n setFocusIndex: (index: number, focusables: readonly HTMLElement[]) => void;\n setActiveDescendantId: (id: string) => void;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProviderOptions<E extends HTMLElement>\n extends KeyboardMovementBehavior,\n KeyboardMovementConfiguration {\n /** @see {@link TabIndexBehavior} */\n tabIndexBehavior?: TabIndexBehavior;\n\n onClick?: (event: MouseEvent<E>) => void;\n onFocus?: (event: FocusEvent<E>) => void;\n onKeyDown?: (event: KeyboardEvent<E>) => void;\n\n /** @defaultValue `false` */\n disabled?: boolean;\n\n /**\n * This is used to implement custom keyboard movement for the `keydown` event.\n */\n extendKeyDown?: ExtendKeyDown<E>;\n\n /**\n * Triggered whenever the focus changes.\n */\n onFocusChange?: KeyboardMovementFocusChangeEventHandler;\n\n /**\n * From what I've understood so far, programmatically focusable elements\n * should only be included when disabled elements via `aria-disabled` are\n * allowed.\n *\n * @defaultValue `includeDisabled`\n */\n programmatic?: boolean;\n\n /** @see {@link GetFocusableElements} */\n getFocusableElements?: GetFocusableElements;\n\n /**\n * This can be used to set the initial focus index whenever the container\n * element is first focused or the focus index is `-1` on other focus events.\n */\n getDefaultFocusedIndex?: GetDefaultFocusedIndex;\n\n /**\n * This was added to support editable combobox behavior. As the user types or\n * uses native input keyboard behavior, the focus index should be reset to\n * `-1` so that the next \"ArrowDown\" event focuses the first option again\n * instead of the last selected one.\n *\n * @defaultValue `false`\n */\n isNegativeOneAllowed?: boolean;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProps<E extends HTMLElement> {\n /**\n * This will only be provided if the {@link KeyboardMovementContext.tabIndexBehavior}\n * is set to `\"virtual\"`.\n */\n \"aria-activedescendant\"?: string;\n\n /**\n * This will not be provided if the {@link KeyboardMovementContext.tabIndexBehavior}\n * is `undefined`. Otherwise:\n * - `0` when `\"virtual\"`\n * - `0` when `\"roving\"` and the container element has not been focused at\n * least once\n * - `-1` when `\"roving\"` and the container has been focused at least once\n * - a child element **should** have a `tabIndex={0}` instead\n */\n tabIndex?: number;\n onClick: MouseEventHandler<E>;\n onFocus: FocusEventHandler<E>;\n onKeyDown: KeyboardEventHandler<E>;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProviderImplementation<E extends HTMLElement> {\n movementProps: Readonly<KeyboardMovementProps<E>>;\n movementContext: Readonly<KeyboardMovementContext>;\n currentFocusIndex: NonNullMutableRef<number>;\n activeDescendantId: string;\n setActiveDescendantId: UseStateSetter<string>;\n}\n"],"names":[],"mappings":"AAoSA;;;CAGC,GACD,WAMC"}
1
+ {"version":3,"sources":["../../src/movement/types.ts"],"sourcesContent":["import {\n type HTMLAttributes,\n type KeyboardEvent,\n type Ref,\n type RefCallback,\n type RefObject,\n} from \"react\";\n\nimport {\n type NonNullMutableRef,\n type NonNullRef,\n type UseStateSetter,\n} from \"../types.js\";\n\n/**\n * Set this to `\"roving\"` when:\n * - there are a group of focusable elements that have a `tabIndex={-1}`\n * - the container element defaults to having a `tabIndex={0}`\n * - if the container is focused, it should no longer be included in the normal\n * tab flow. Instead, the current focused element should be included instead\n * by changing its `tabIndex` from `-1` to `0`\n *\n * Set this to `\"virtual\"` when:\n * - the container element should never lose focus\n * - the \"focused\" element only gains focus styles instead of being focused\n * - the container element specifies an `aria-activedescendant` pointing to one\n * of the ids for the child \"focusable\" elements\n *\n * @since 6.0.0\n */\nexport type TabIndexBehavior = \"roving\" | \"virtual\";\n\n/**\n * This should be used for specific widgets that should not include all\n * focusable elements and instead only specific elements.\n *\n * @example\n * ```ts\n * const getExpansionPanelsOnly: GetFocusableElements = (container) =>\n * [...container.querySelectorAll(\".rmd-expansion-panel__button\")];\n *\n * const getTreeItemsOnly: GetFocusableElements = (container) =>\n * [...container.querySelectorAll(\"[role='treeitem']\")];\n * ```\n *\n * @defaultValue `getFocusableElements`\n * @see the default `getFocusableElements` function.\n */\nexport type GetFocusableElements = (\n container: HTMLElement,\n programmatic: boolean\n) => readonly HTMLElement[];\n\n/**\n * @since 5.0.0\n */\nexport interface KeyboardMovementConfiguration {\n /**\n * A list of keys that will attempt to increment the focus index by 1.\n *\n * @defaultValue `[\"ArrowDown\"]`\n */\n incrementKeys?: readonly string[];\n\n /**\n * A list of keys that will attempt to decrement the focus index by 1.\n *\n * @defaultValue `[\"ArrowUp\"]`\n */\n decrementKeys?: readonly string[];\n\n /**\n * A list of keys that will set the focus index to `0`.\n *\n * @defaultValue `[\"Home\"]`\n */\n jumpToFirstKeys?: readonly string[];\n\n /**\n * A list of keys that will set the focus index to the last focusable index.\n *\n * @defaultValue `[\"End\"]`\n */\n jumpToLastKeys?: readonly string[];\n}\n\n/**\n * The defined {@link KeyboardMovementConfiguration} that should be used for\n * custom keyboard focus behavior.\n *\n * @since 5.0.0\n */\nexport type KeyboardMovementConfig = Required<KeyboardMovementConfiguration>;\n\n/**\n * @since 5.0.0\n */\nexport interface KeyboardMovementBehavior {\n /**\n * Boolean if pressing a letter will focus the next item in the\n * {@link KeyboardMovementProvider} that starts with the same letter.\n *\n * @defaultValue `false`\n */\n searchable?: boolean;\n\n /**\n * Boolean if the {@link KeyboardMovementProvider} should allow the focus behavior\n * to loop from the first to last or last to first item instead of preventing\n * any new focus behavior. In other words... if the last item is focused and\n * the user presses a key that should advance the focus to the next focusable\n * element, should the focus stay on the current element or loop back and\n * focus the first focusable item.\n *\n * @defaultValue `false`\n */\n loopable?: boolean;\n\n /**\n * Boolean if elements that are `aria-disabled` or `disabled` should still be\n * able to gain focus.\n *\n * @defaultValue `false`\n */\n includeDisabled?: boolean;\n\n /**\n * Boolean if the keyboard movement is horizontal instead of vertical. This\n * updates the default keyboard config to use `ArrowRight` and `ArrowLeft`\n * instead of `ArrowDown` and `ArrowUp`,\n *\n * @since 5.1.2\n * @defaultValue `false`\n */\n horizontal?: boolean;\n}\n\n/**\n * @since 6.3.0\n */\nexport interface KeyboardFocusFromKeyOptions {\n key: string;\n /** @defaultValue `false` */\n reversed?: boolean;\n\n /** @defaultValue `getFocusableElementsFromRef()` */\n focusables?: readonly HTMLElement[];\n}\n\n/**\n * @since 6.3.0\n */\nexport type KeyboardFocusAction = (focusables?: readonly HTMLElement[]) => void;\n\n/**\n * @since 5.0.0\n * @since 6.0.0 Removed `attach`, `detach` and `watching`\n * @internal\n */\nexport interface KeyboardMovementContext\n extends Required<KeyboardMovementBehavior> {\n /** {@inheritDoc KeyboardMovementConfig} */\n config: NonNullRef<KeyboardMovementConfig>;\n\n /** @see {@link TabIndexBehavior} */\n tabIndexBehavior: TabIndexBehavior | undefined;\n\n /**\n * Note: This will only update if the {@link KeyboardMovementProviderOptions.tabIndexBehavior}\n * has been set to `\"roving\"` or `\"virtual\"`.\n */\n activeDescendantId: string;\n\n /**\n * @since 6.3.0\n */\n focusFirst: KeyboardFocusAction;\n\n /**\n * @since 6.3.0\n */\n focusLast: KeyboardFocusAction;\n\n /**\n * @since 6.3.0\n */\n focusNext: KeyboardFocusAction;\n\n /**\n * @since 6.3.0\n */\n focusPrevious: KeyboardFocusAction;\n\n /**\n * @since 6.3.0\n */\n focusFromKey: (options: KeyboardFocusFromKeyOptions) => void;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface FocusableIndexOptions {\n focusables: readonly HTMLElement[];\n includeDisabled: boolean;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type GetDefaultFocusedIndex = (options: FocusableIndexOptions) => number;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type ExtendKeyDown<E extends HTMLElement> = (\n movementData: KeyboardMovementExtensionData<E>\n) => void;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementFocusChangeEvent {\n index: number;\n element: HTMLElement;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport type KeyboardMovementFocusChangeEventHandler = (\n event: KeyboardMovementFocusChangeEvent\n) => void;\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementExtensionData<E extends HTMLElement>\n extends KeyboardMovementContext {\n event: KeyboardEvent<E>;\n currentFocusIndex: NonNullMutableRef<number>;\n setFocusIndex: (index: number, focusables: readonly HTMLElement[]) => void;\n setActiveDescendantId: (id: string) => void;\n}\n\n/**\n * @since 6.3.0\n */\nexport type KeyboardMovementEventHandlers<E extends HTMLElement> = Pick<\n HTMLAttributes<E>,\n \"onClick\" | \"onFocus\" | \"onKeyDown\"\n>;\n\n/**\n * @since 6.3.0\n */\nexport interface SimpleKeyboardMovementWrapperOptions<E extends HTMLElement>\n extends KeyboardMovementEventHandlers<E> {\n ref?: Ref<E>;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProviderOptions<E extends HTMLElement>\n extends KeyboardMovementBehavior,\n SimpleKeyboardMovementWrapperOptions<E>,\n KeyboardMovementConfiguration {\n /** @see {@link TabIndexBehavior} */\n tabIndexBehavior?: TabIndexBehavior;\n\n /** @defaultValue `false` */\n disabled?: boolean;\n\n /**\n * This is used to implement custom keyboard movement for the `keydown` event.\n */\n extendKeyDown?: ExtendKeyDown<E>;\n\n /**\n * Triggered whenever the focus changes.\n */\n onFocusChange?: KeyboardMovementFocusChangeEventHandler;\n\n /**\n * From what I've understood so far, programmatically focusable elements\n * should only be included when disabled elements via `aria-disabled` are\n * allowed.\n *\n * @defaultValue `includeDisabled`\n */\n programmatic?: boolean;\n\n /** @see {@link GetFocusableElements} */\n getFocusableElements?: GetFocusableElements;\n\n /**\n * This can be used to set the initial focus index whenever the container\n * element is first focused or the focus index is `-1` on other focus events.\n */\n getDefaultFocusedIndex?: GetDefaultFocusedIndex;\n\n /**\n * This was added to support editable combobox behavior. As the user types or\n * uses native input keyboard behavior, the focus index should be reset to\n * `-1` so that the next \"ArrowDown\" event focuses the first option again\n * instead of the last selected one.\n *\n * @defaultValue `false`\n */\n isNegativeOneAllowed?: boolean;\n\n /**\n * This was added to support spinbutton groups so the user can either use the\n * ArrowLeft, ArrowRight, or Tab keys to move. Without this, switching\n * between tab and the arrow keys would have the wrong tab index.\n *\n * @since 6.3.0\n * @defaultValue `false`\n */\n trackTabKeys?: boolean;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProps<E extends HTMLElement>\n extends Required<KeyboardMovementEventHandlers<E>> {\n /**\n * This will only be provided if the {@link KeyboardMovementContext.tabIndexBehavior}\n * is set to `\"virtual\"`.\n */\n \"aria-activedescendant\"?: string;\n\n /**\n * This will not be provided if the {@link KeyboardMovementContext.tabIndexBehavior}\n * is `undefined`. Otherwise:\n * - `0` when `\"virtual\"`\n * - `0` when `\"roving\"` and the container element has not been focused at\n * least once\n * - `-1` when `\"roving\"` and the container has been focused at least once\n * - a child element **should** have a `tabIndex={0}` instead\n */\n tabIndex?: number;\n\n ref: RefCallback<E>;\n}\n\n/**\n * @since 6.0.0\n * @internal\n */\nexport interface KeyboardMovementProviderImplementation<E extends HTMLElement> {\n nodeRef: RefObject<E>;\n movementProps: Readonly<KeyboardMovementProps<E>>;\n movementContext: Readonly<KeyboardMovementContext>;\n currentFocusIndex: NonNullMutableRef<number>;\n activeDescendantId: string;\n setActiveDescendantId: UseStateSetter<string>;\n}\n"],"names":[],"mappings":"AAoWA;;;CAGC,GACD,WAOC"}
@@ -1,4 +1,8 @@
1
- import type { KeyboardMovementContext, KeyboardMovementProviderImplementation, KeyboardMovementProviderOptions } from "./types.js";
1
+ import { type KeyboardMovementContext, type KeyboardMovementProviderImplementation, type KeyboardMovementProviderOptions } from "./types.js";
2
+ /**
3
+ * @since 6.3.0
4
+ */
5
+ export declare const DEFAULT_KEYBOARD_MOVEMENT_CONTEXT: Readonly<KeyboardMovementContext>;
2
6
  export declare const KeyboardMovementProvider: import("react").Provider<KeyboardMovementContext>;
3
7
  /**
4
8
  * @since 5.0.0
@@ -1,16 +1,19 @@
1
1
  "use client";
2
- import { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
2
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { getFocusableElements as defaultGetFocusableElements } from "../focus/utils.js";
4
4
  import { useUserInteractionMode } from "../interaction/UserInteractionModeProvider.js";
5
5
  import { useDir } from "../typography/WritingDirectionProvider.js";
6
+ import { useEnsuredRef } from "../useEnsuredRef.js";
6
7
  import { useIsomorphicLayoutEffect } from "../useIsomorphicLayoutEffect.js";
7
8
  import { DEFAULT_KEYBOARD_MOVEMENT, DEFAULT_LTR_KEYBOARD_MOVEMENT, DEFAULT_RTL_KEYBOARD_MOVEMENT } from "./constants.js";
8
9
  import { findMatchIndex } from "./findMatchIndex.js";
9
10
  import { getFirstFocusableIndex, getLastFocusableIndex, getNextFocusableIndex, getSearchText, getVirtualFocusDefaultIndex, isElementDisabled, isNotFocusable, isSearchableEvent, recalculateFocusIndex } from "./utils.js";
11
+ const noop = ()=>{
12
+ // do nothing
13
+ };
10
14
  /**
11
- * @since 5.0.0
12
- * @internal
13
- */ const context = createContext({
15
+ * @since 6.3.0
16
+ */ export const DEFAULT_KEYBOARD_MOVEMENT_CONTEXT = {
14
17
  config: {
15
18
  current: DEFAULT_KEYBOARD_MOVEMENT
16
19
  },
@@ -19,8 +22,17 @@ import { getFirstFocusableIndex, getLastFocusableIndex, getNextFocusableIndex, g
19
22
  horizontal: false,
20
23
  includeDisabled: false,
21
24
  tabIndexBehavior: undefined,
22
- activeDescendantId: ""
23
- });
25
+ activeDescendantId: "",
26
+ focusFirst: noop,
27
+ focusLast: noop,
28
+ focusNext: noop,
29
+ focusPrevious: noop,
30
+ focusFromKey: noop
31
+ };
32
+ /**
33
+ * @since 5.0.0
34
+ * @internal
35
+ */ const context = createContext(DEFAULT_KEYBOARD_MOVEMENT_CONTEXT);
24
36
  context.displayName = "KeyboardMovement";
25
37
  export const { Provider: KeyboardMovementProvider } = context;
26
38
  /**
@@ -29,9 +41,6 @@ export const { Provider: KeyboardMovementProvider } = context;
29
41
  */ export function useKeyboardMovementContext() {
30
42
  return useContext(context);
31
43
  }
32
- const noop = ()=>{
33
- // do nothing
34
- };
35
44
  const returnNegative1 = ()=>-1;
36
45
  /**
37
46
  * Implements the custom keyboard movement behavior throughout react-md. Using
@@ -154,7 +163,8 @@ const returnNegative1 = ()=>-1;
154
163
  * @since 6.0.0
155
164
  * @internal
156
165
  */ export function useKeyboardMovementProvider(options = {}) {
157
- const { onClick = noop, onFocus = noop, onKeyDown = noop, loopable = false, disabled, searchable = false, horizontal = false, includeDisabled = false, tabIndexBehavior, extendKeyDown = noop, onFocusChange = noop, programmatic = includeDisabled, incrementKeys: propIncrementKeys, decrementKeys: propDecrementKeys, jumpToFirstKeys: propJumpToFirstKeys, jumpToLastKeys: propJumpToLastKeys, getFocusableElements = defaultGetFocusableElements, getDefaultFocusedIndex = returnNegative1, isNegativeOneAllowed = false } = options;
166
+ const { ref: propRef, onClick = noop, onFocus = noop, onKeyDown = noop, loopable = false, disabled, searchable = false, horizontal = false, trackTabKeys = false, includeDisabled = false, tabIndexBehavior, extendKeyDown = noop, onFocusChange = noop, programmatic = includeDisabled, incrementKeys: propIncrementKeys, decrementKeys: propDecrementKeys, jumpToFirstKeys: propJumpToFirstKeys, jumpToLastKeys: propJumpToLastKeys, getFocusableElements = defaultGetFocusableElements, getDefaultFocusedIndex = returnNegative1, isNegativeOneAllowed = false } = options;
167
+ const [nodeRef, nodeRefCallback] = useEnsuredRef(propRef);
158
168
  const isRTL = useDir().dir === "rtl";
159
169
  let defaults;
160
170
  if (horizontal) {
@@ -176,42 +186,153 @@ const returnNegative1 = ()=>-1;
176
186
  useIsomorphicLayoutEffect(()=>{
177
187
  config.current = configuration;
178
188
  });
189
+ const mode = useUserInteractionMode();
190
+ const refocus = useRef(false);
191
+ const currentFocusIndex = useRef(-1);
179
192
  const [activeDescendantId, setActiveDescendantId] = useState("");
193
+ if (process.env.NODE_ENV !== "production") {
194
+ // this fixes issues during hot reloading and using the `useId()` hook
195
+ // eslint-disable-next-line react-hooks/rules-of-hooks
196
+ useEffect(()=>{
197
+ return ()=>{
198
+ setActiveDescendantId("");
199
+ };
200
+ }, []);
201
+ }
202
+ let tabIndex;
203
+ if (tabIndexBehavior) {
204
+ tabIndex = disabled || tabIndexBehavior === "roving" && activeDescendantId ? -1 : 0;
205
+ }
206
+ const getFocusableElementsFromRef = useCallback(()=>{
207
+ const container = nodeRef.current;
208
+ if (!container) {
209
+ return [];
210
+ }
211
+ return getFocusableElements(container, programmatic);
212
+ }, [
213
+ getFocusableElements,
214
+ nodeRef,
215
+ programmatic
216
+ ]);
217
+ const updateFocusIndex = useCallback((index, focusables = getFocusableElementsFromRef())=>{
218
+ if (currentFocusIndex.current === index || index === -1) {
219
+ return;
220
+ }
221
+ currentFocusIndex.current = index;
222
+ const focused = focusables[index];
223
+ if (tabIndexBehavior) {
224
+ focused.scrollIntoView({
225
+ block: "nearest",
226
+ inline: "nearest"
227
+ });
228
+ setActiveDescendantId(focused.id);
229
+ }
230
+ if (tabIndexBehavior !== "virtual") {
231
+ focused.focus();
232
+ }
233
+ onFocusChange({
234
+ index,
235
+ element: focused
236
+ });
237
+ }, [
238
+ getFocusableElementsFromRef,
239
+ onFocusChange,
240
+ tabIndexBehavior
241
+ ]);
242
+ const focusNext = useCallback((focusables = getFocusableElementsFromRef())=>{
243
+ updateFocusIndex(getNextFocusableIndex({
244
+ loopable,
245
+ increment: true,
246
+ focusables,
247
+ includeDisabled: true,
248
+ currentFocusIndex: currentFocusIndex.current
249
+ }), focusables);
250
+ }, [
251
+ getFocusableElementsFromRef,
252
+ loopable,
253
+ updateFocusIndex
254
+ ]);
255
+ const focusPrevious = useCallback((focusables = getFocusableElementsFromRef())=>{
256
+ updateFocusIndex(getNextFocusableIndex({
257
+ loopable,
258
+ increment: false,
259
+ focusables,
260
+ includeDisabled: true,
261
+ currentFocusIndex: currentFocusIndex.current
262
+ }), focusables);
263
+ }, [
264
+ getFocusableElementsFromRef,
265
+ loopable,
266
+ updateFocusIndex
267
+ ]);
268
+ const focusFirst = useCallback((focusables = getFocusableElementsFromRef())=>{
269
+ updateFocusIndex(getFirstFocusableIndex({
270
+ focusables,
271
+ includeDisabled
272
+ }), focusables);
273
+ }, [
274
+ getFocusableElementsFromRef,
275
+ includeDisabled,
276
+ updateFocusIndex
277
+ ]);
278
+ const focusLast = useCallback((focusables = getFocusableElementsFromRef())=>{
279
+ updateFocusIndex(getLastFocusableIndex({
280
+ focusables,
281
+ includeDisabled
282
+ }), focusables);
283
+ }, [
284
+ getFocusableElementsFromRef,
285
+ includeDisabled,
286
+ updateFocusIndex
287
+ ]);
288
+ const focusFromKey = useCallback((options)=>{
289
+ const { key, reversed, focusables = getFocusableElementsFromRef() } = options;
290
+ if (!searchable) {
291
+ return;
292
+ }
293
+ const index = findMatchIndex({
294
+ value: key,
295
+ values: focusables.map((element)=>getSearchText(element, !isNotFocusable(element, includeDisabled))),
296
+ startIndex: reversed ? -1 : currentFocusIndex.current
297
+ });
298
+ updateFocusIndex(index, focusables);
299
+ }, [
300
+ getFocusableElementsFromRef,
301
+ includeDisabled,
302
+ searchable,
303
+ updateFocusIndex
304
+ ]);
180
305
  const movementContext = useMemo(()=>({
181
306
  config,
182
307
  loopable,
183
308
  searchable,
184
309
  horizontal,
310
+ focusFirst,
311
+ focusLast,
312
+ focusNext,
313
+ focusPrevious,
314
+ focusFromKey,
185
315
  includeDisabled,
186
316
  tabIndexBehavior,
187
317
  activeDescendantId
188
318
  }), [
189
319
  activeDescendantId,
320
+ focusFirst,
321
+ focusFromKey,
322
+ focusLast,
323
+ focusNext,
324
+ focusPrevious,
190
325
  horizontal,
191
326
  includeDisabled,
192
327
  loopable,
193
328
  searchable,
194
329
  tabIndexBehavior
195
330
  ]);
196
- const currentFocusIndex = useRef(-1);
197
- const mode = useUserInteractionMode();
198
- const refocus = useRef(false);
199
- if (process.env.NODE_ENV !== "production") {
200
- // this fixes issues during hot reloading and using the `useId()` hook
201
- // eslint-disable-next-line react-hooks/rules-of-hooks
202
- useEffect(()=>{
203
- return ()=>{
204
- setActiveDescendantId("");
205
- };
206
- }, []);
207
- }
208
- let tabIndex;
209
- if (tabIndexBehavior) {
210
- tabIndex = disabled || tabIndexBehavior === "roving" && activeDescendantId ? -1 : 0;
211
- }
212
331
  return {
332
+ nodeRef,
213
333
  movementProps: {
214
334
  "aria-activedescendant": tabIndexBehavior === "virtual" ? activeDescendantId : undefined,
335
+ ref: nodeRefCallback,
215
336
  tabIndex,
216
337
  // Note: This used to be on the `onFocus` event, but this causes issues in
217
338
  // Chromium browsers for drag and drop behavior
@@ -312,25 +433,7 @@ const returnNegative1 = ()=>-1;
312
433
  const setFocusIndex = (index, focusables)=>{
313
434
  event.preventDefault();
314
435
  event.stopPropagation();
315
- if (currentFocusIndex.current === index || index === -1) {
316
- return;
317
- }
318
- currentFocusIndex.current = index;
319
- const focused = focusables[index];
320
- if (tabIndexBehavior) {
321
- focused.scrollIntoView({
322
- block: "nearest",
323
- inline: "nearest"
324
- });
325
- setActiveDescendantId(focused.id);
326
- }
327
- if (tabIndexBehavior !== "virtual") {
328
- focused.focus();
329
- }
330
- onFocusChange({
331
- index,
332
- element: focused
333
- });
436
+ updateFocusIndex(index, focusables);
334
437
  };
335
438
  extendKeyDown({
336
439
  event,
@@ -368,44 +471,39 @@ const returnNegative1 = ()=>-1;
368
471
  }
369
472
  const { incrementKeys, decrementKeys, jumpToFirstKeys, jumpToLastKeys } = config.current;
370
473
  if (searchable && isSearchableEvent(event)) {
371
- const focusables = getFocusableElements(currentTarget, programmatic);
372
- const index = findMatchIndex({
373
- value: key,
374
- values: focusables.map((element)=>getSearchText(element, !isNotFocusable(element, includeDisabled))),
375
- startIndex: shiftKey ? -1 : currentFocusIndex.current
474
+ event.preventDefault();
475
+ event.stopPropagation();
476
+ focusFromKey({
477
+ key,
478
+ reversed: shiftKey
376
479
  });
377
- setFocusIndex(index, focusables);
480
+ return;
481
+ }
482
+ if (trackTabKeys && key === "Tab") {
483
+ currentFocusIndex.current += event.shiftKey ? -1 : 1;
378
484
  return;
379
485
  }
380
486
  const jumpToFirst = jumpToFirstKeys.includes(key);
381
487
  const jumpToLast = !jumpToFirst && jumpToLastKeys.includes(key);
382
488
  const increment = !jumpToFirst && !jumpToLast && incrementKeys.includes(key);
383
489
  const decrement = !jumpToFirst && !jumpToLast && !increment && decrementKeys.includes(key);
384
- if (!jumpToFirst && !jumpToLast && !increment && !decrement) {
385
- return;
386
- }
387
- const focusables = getFocusableElements(currentTarget, programmatic);
388
- let index;
389
490
  if (jumpToFirst) {
390
- index = getFirstFocusableIndex({
391
- focusables,
392
- includeDisabled
393
- });
491
+ event.preventDefault();
492
+ event.stopPropagation();
493
+ focusFirst();
394
494
  } else if (jumpToLast) {
395
- index = getLastFocusableIndex({
396
- focusables,
397
- includeDisabled
398
- });
399
- } else {
400
- index = getNextFocusableIndex({
401
- loopable,
402
- increment,
403
- focusables,
404
- includeDisabled,
405
- currentFocusIndex: currentFocusIndex.current
406
- });
495
+ event.preventDefault();
496
+ event.stopPropagation();
497
+ focusLast();
498
+ } else if (increment) {
499
+ event.preventDefault();
500
+ event.stopPropagation();
501
+ focusNext();
502
+ } else if (decrement) {
503
+ event.preventDefault();
504
+ event.stopPropagation();
505
+ focusPrevious();
407
506
  }
408
- setFocusIndex(index, focusables);
409
507
  }
410
508
  },
411
509
  movementContext,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/movement/useKeyboardMovementProvider.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { getFocusableElements as defaultGetFocusableElements } from \"../focus/utils.js\";\nimport { useUserInteractionMode } from \"../interaction/UserInteractionModeProvider.js\";\nimport { useDir } from \"../typography/WritingDirectionProvider.js\";\nimport { useIsomorphicLayoutEffect } from \"../useIsomorphicLayoutEffect.js\";\nimport {\n DEFAULT_KEYBOARD_MOVEMENT,\n DEFAULT_LTR_KEYBOARD_MOVEMENT,\n DEFAULT_RTL_KEYBOARD_MOVEMENT,\n} from \"./constants.js\";\nimport { findMatchIndex } from \"./findMatchIndex.js\";\nimport type {\n KeyboardMovementConfig,\n KeyboardMovementConfiguration,\n KeyboardMovementContext,\n KeyboardMovementProviderImplementation,\n KeyboardMovementProviderOptions,\n} from \"./types.js\";\nimport {\n getFirstFocusableIndex,\n getLastFocusableIndex,\n getNextFocusableIndex,\n getSearchText,\n getVirtualFocusDefaultIndex,\n isElementDisabled,\n isNotFocusable,\n isSearchableEvent,\n recalculateFocusIndex,\n} from \"./utils.js\";\n\n/**\n * @since 5.0.0\n * @internal\n */\nconst context = createContext<KeyboardMovementContext>({\n config: { current: DEFAULT_KEYBOARD_MOVEMENT },\n loopable: false,\n searchable: false,\n horizontal: false,\n includeDisabled: false,\n tabIndexBehavior: undefined,\n activeDescendantId: \"\",\n});\ncontext.displayName = \"KeyboardMovement\";\nexport const { Provider: KeyboardMovementProvider } = context;\n\n/**\n * @since 5.0.0\n * @internal\n */\nexport function useKeyboardMovementContext(): Readonly<KeyboardMovementContext> {\n return useContext(context);\n}\n\nconst noop = (): void => {\n // do nothing\n};\n\nconst returnNegative1 = (): number => -1;\n\n/**\n * Implements the custom keyboard movement behavior throughout react-md. Using\n * the \"Find References\" will be the best way to see example usage.\n *\n * @example Default Keyboard Movement for any Focusable Element\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider();\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * {children}\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n *\n * @example Active Descendant Movement\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementContext,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n * import { useId } from \"react\";\n *\n * function Child(): ReactElement {\n * const id = useId()\n * const { activeDescendantId } = useKeyboardMovementContext();\n *\n * return (\n * <div\n * {...props}\n * id={id}\n * className={cnb(id === activeDescendantId && \"focused-class-name\")}\n * >\n * Some Content\n * </div>\n * );\n * }\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider({\n * loopable: true,\n * searchable: true,\n * tabIndexBehavior: \"virtual\",\n * });\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * <Child />\n * <Child />\n * <Child />\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n *\n * @example Roving Tab Index\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementContext,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n * import { useId } from \"react\";\n *\n * function Child(): ReactElement {\n * const id = useId()\n * const { activeDescendantId } = useKeyboardMovementContext();\n *\n * return (\n * <div\n * {...props}\n * id={id}\n * tabIndex={id === activeDescendantId ? 0 : -1}\n * >\n * Some Content\n * </div>\n * );\n * }\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider({\n * loopable: true,\n * searchable: true,\n * tabIndexBehavior: \"roving\",\n * });\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * <Child />\n * <Child />\n * <Child />\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n * @since 6.0.0\n * @internal\n */\nexport function useKeyboardMovementProvider<E extends HTMLElement>(\n options: KeyboardMovementProviderOptions<E> = {}\n): KeyboardMovementProviderImplementation<E> {\n const {\n onClick = noop,\n onFocus = noop,\n onKeyDown = noop,\n loopable = false,\n disabled,\n searchable = false,\n horizontal = false,\n includeDisabled = false,\n tabIndexBehavior,\n extendKeyDown = noop,\n onFocusChange = noop,\n programmatic = includeDisabled,\n incrementKeys: propIncrementKeys,\n decrementKeys: propDecrementKeys,\n jumpToFirstKeys: propJumpToFirstKeys,\n jumpToLastKeys: propJumpToLastKeys,\n getFocusableElements = defaultGetFocusableElements,\n getDefaultFocusedIndex = returnNegative1,\n isNegativeOneAllowed = false,\n } = options;\n\n const isRTL = useDir().dir === \"rtl\";\n let defaults: Readonly<Required<KeyboardMovementConfiguration>>;\n if (horizontal) {\n defaults = isRTL\n ? DEFAULT_RTL_KEYBOARD_MOVEMENT\n : DEFAULT_LTR_KEYBOARD_MOVEMENT;\n } else {\n defaults = DEFAULT_KEYBOARD_MOVEMENT;\n }\n\n const incrementKeys = propIncrementKeys || defaults.incrementKeys;\n const decrementKeys = propDecrementKeys || defaults.decrementKeys;\n const jumpToFirstKeys = propJumpToFirstKeys || defaults.jumpToFirstKeys;\n const jumpToLastKeys = propJumpToLastKeys || defaults.jumpToLastKeys;\n\n const configuration: KeyboardMovementConfig = {\n incrementKeys,\n decrementKeys,\n jumpToFirstKeys,\n jumpToLastKeys,\n };\n const config = useRef(configuration);\n useIsomorphicLayoutEffect(() => {\n config.current = configuration;\n });\n\n const [activeDescendantId, setActiveDescendantId] = useState(\"\");\n const movementContext = useMemo<KeyboardMovementContext>(\n () => ({\n config,\n loopable,\n searchable,\n horizontal,\n includeDisabled,\n tabIndexBehavior,\n activeDescendantId,\n }),\n [\n activeDescendantId,\n horizontal,\n includeDisabled,\n loopable,\n searchable,\n tabIndexBehavior,\n ]\n );\n const currentFocusIndex = useRef(-1);\n const mode = useUserInteractionMode();\n const refocus = useRef(false);\n\n if (process.env.NODE_ENV !== \"production\") {\n // this fixes issues during hot reloading and using the `useId()` hook\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n return () => {\n setActiveDescendantId(\"\");\n };\n }, []);\n }\n\n let tabIndex: number | undefined;\n if (tabIndexBehavior) {\n tabIndex =\n disabled || (tabIndexBehavior === \"roving\" && activeDescendantId)\n ? -1\n : 0;\n }\n\n return {\n movementProps: {\n \"aria-activedescendant\":\n tabIndexBehavior === \"virtual\" ? activeDescendantId : undefined,\n tabIndex,\n\n // Note: This used to be on the `onFocus` event, but this causes issues in\n // Chromium browsers for drag and drop behavior\n onClick(event) {\n onClick(event);\n if (disabled) {\n return;\n }\n\n // This makes it so you can click an element with a mouse and then\n // keyboard navigate from that element instead of the last keyboard focus\n // element\n const { currentTarget, target } = event;\n if (target === currentTarget || !(target instanceof HTMLElement)) {\n return;\n }\n\n const focusables = getFocusableElements(currentTarget, programmatic);\n const focusedIndex = focusables.findIndex(\n (element) => element === target || element.contains(target)\n );\n if (focusedIndex === -1 || !focusables.length) {\n return;\n }\n\n currentFocusIndex.current = focusedIndex;\n const focused = focusables[focusedIndex];\n if (tabIndexBehavior) {\n setActiveDescendantId(focused.id);\n }\n\n // need to force focus back to the container element when using\n // aria activedescendant\n if (tabIndexBehavior === \"virtual\") {\n refocus.current = true;\n currentTarget.focus();\n }\n\n onFocusChange({\n index: focusedIndex,\n element: focused,\n });\n },\n onFocus(event) {\n onFocus(event);\n if (event.isPropagationStopped() || refocus.current) {\n refocus.current = false;\n return;\n }\n\n if (\n (mode !== \"keyboard\" && tabIndexBehavior !== \"virtual\") ||\n event.target !== event.currentTarget\n ) {\n return;\n }\n\n const focusables = getFocusableElements(\n event.currentTarget,\n programmatic\n );\n if (!focusables.length) {\n return;\n }\n\n let defaultFocusIndex = getDefaultFocusedIndex({\n focusables,\n includeDisabled,\n });\n\n // This allows my custom `getDefaultFocusedIndex` implementations to\n // have a nice fallback without having to re-implement the \"focus\n // first\" behavior\n if (!isNegativeOneAllowed && defaultFocusIndex === -1) {\n if (tabIndexBehavior === \"virtual\") {\n // virtual keyboard navigation **must** always focus at least one element\n defaultFocusIndex = getVirtualFocusDefaultIndex({\n focusables,\n includeDisabled,\n activeDescendantId,\n });\n } else {\n defaultFocusIndex = getFirstFocusableIndex({\n focusables,\n includeDisabled,\n });\n }\n }\n\n if (defaultFocusIndex === -1) {\n return;\n }\n\n currentFocusIndex.current = defaultFocusIndex;\n const focused = focusables[defaultFocusIndex];\n if (tabIndexBehavior) {\n setActiveDescendantId(focused.id);\n }\n\n if (tabIndexBehavior !== \"virtual\") {\n focused.focus();\n } else {\n focused.scrollIntoView({ block: \"nearest\" });\n }\n\n onFocusChange({\n index: defaultFocusIndex,\n element: focused,\n });\n },\n onKeyDown(event) {\n onKeyDown(event);\n if (disabled) {\n return;\n }\n\n const { currentTarget } = event;\n\n const setFocusIndex = (\n index: number,\n focusables: readonly HTMLElement[]\n ): void => {\n event.preventDefault();\n event.stopPropagation();\n if (currentFocusIndex.current === index || index === -1) {\n return;\n }\n\n currentFocusIndex.current = index;\n const focused = focusables[index];\n if (tabIndexBehavior) {\n focused.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n setActiveDescendantId(focused.id);\n }\n\n if (tabIndexBehavior !== \"virtual\") {\n focused.focus();\n }\n\n onFocusChange({\n index,\n element: focused,\n });\n };\n\n extendKeyDown({\n event,\n setFocusIndex,\n currentFocusIndex,\n setActiveDescendantId,\n ...movementContext,\n });\n\n if (event.isPropagationStopped()) {\n return;\n }\n\n // TODO: Figure this part out. This is currently required for the tree\n // movement when the asterisk key is pressed. There might be other cases\n // as well.\n if (!isNegativeOneAllowed && currentFocusIndex.current === -1) {\n currentFocusIndex.current = recalculateFocusIndex({\n focusables: getFocusableElements(currentTarget, programmatic),\n includeDisabled,\n tabIndexBehavior,\n activeDescendantId,\n });\n }\n\n const { key, shiftKey } = event;\n if (\n tabIndexBehavior === \"virtual\" &&\n activeDescendantId &&\n (key === \" \" || key === \"Enter\")\n ) {\n if (key === \" \") {\n event.preventDefault();\n }\n\n const focusables = getFocusableElements(currentTarget, programmatic);\n const activeElement = focusables[currentFocusIndex.current];\n if (!activeElement || isElementDisabled(activeElement)) {\n return;\n }\n\n activeElement.click();\n return;\n }\n\n const {\n incrementKeys,\n decrementKeys,\n jumpToFirstKeys,\n jumpToLastKeys,\n } = config.current;\n\n if (searchable && isSearchableEvent(event)) {\n const focusables = getFocusableElements(currentTarget, programmatic);\n const index = findMatchIndex({\n value: key,\n values: focusables.map((element) =>\n getSearchText(element, !isNotFocusable(element, includeDisabled))\n ),\n startIndex: shiftKey ? -1 : currentFocusIndex.current,\n });\n setFocusIndex(index, focusables);\n return;\n }\n\n const jumpToFirst = jumpToFirstKeys.includes(key);\n const jumpToLast = !jumpToFirst && jumpToLastKeys.includes(key);\n const increment =\n !jumpToFirst && !jumpToLast && incrementKeys.includes(key);\n const decrement =\n !jumpToFirst &&\n !jumpToLast &&\n !increment &&\n decrementKeys.includes(key);\n\n if (!jumpToFirst && !jumpToLast && !increment && !decrement) {\n return;\n }\n const focusables = getFocusableElements(currentTarget, programmatic);\n\n let index: number;\n if (jumpToFirst) {\n index = getFirstFocusableIndex({\n focusables,\n includeDisabled,\n });\n } else if (jumpToLast) {\n index = getLastFocusableIndex({\n focusables,\n includeDisabled,\n });\n } else {\n index = getNextFocusableIndex({\n loopable,\n increment,\n focusables,\n includeDisabled,\n currentFocusIndex: currentFocusIndex.current,\n });\n }\n\n setFocusIndex(index, focusables);\n },\n },\n movementContext,\n currentFocusIndex,\n activeDescendantId,\n setActiveDescendantId,\n };\n}\n"],"names":["createContext","useContext","useEffect","useMemo","useRef","useState","getFocusableElements","defaultGetFocusableElements","useUserInteractionMode","useDir","useIsomorphicLayoutEffect","DEFAULT_KEYBOARD_MOVEMENT","DEFAULT_LTR_KEYBOARD_MOVEMENT","DEFAULT_RTL_KEYBOARD_MOVEMENT","findMatchIndex","getFirstFocusableIndex","getLastFocusableIndex","getNextFocusableIndex","getSearchText","getVirtualFocusDefaultIndex","isElementDisabled","isNotFocusable","isSearchableEvent","recalculateFocusIndex","context","config","current","loopable","searchable","horizontal","includeDisabled","tabIndexBehavior","undefined","activeDescendantId","displayName","Provider","KeyboardMovementProvider","useKeyboardMovementContext","noop","returnNegative1","useKeyboardMovementProvider","options","onClick","onFocus","onKeyDown","disabled","extendKeyDown","onFocusChange","programmatic","incrementKeys","propIncrementKeys","decrementKeys","propDecrementKeys","jumpToFirstKeys","propJumpToFirstKeys","jumpToLastKeys","propJumpToLastKeys","getDefaultFocusedIndex","isNegativeOneAllowed","isRTL","dir","defaults","configuration","setActiveDescendantId","movementContext","currentFocusIndex","mode","refocus","process","env","NODE_ENV","tabIndex","movementProps","event","currentTarget","target","HTMLElement","focusables","focusedIndex","findIndex","element","contains","length","focused","id","focus","index","isPropagationStopped","defaultFocusIndex","scrollIntoView","block","setFocusIndex","preventDefault","stopPropagation","inline","key","shiftKey","activeElement","click","value","values","map","startIndex","jumpToFirst","includes","jumpToLast","increment","decrement"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/movement/useKeyboardMovementProvider.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { getFocusableElements as defaultGetFocusableElements } from \"../focus/utils.js\";\nimport { useUserInteractionMode } from \"../interaction/UserInteractionModeProvider.js\";\nimport { useDir } from \"../typography/WritingDirectionProvider.js\";\nimport { useEnsuredRef } from \"../useEnsuredRef.js\";\nimport { useIsomorphicLayoutEffect } from \"../useIsomorphicLayoutEffect.js\";\nimport {\n DEFAULT_KEYBOARD_MOVEMENT,\n DEFAULT_LTR_KEYBOARD_MOVEMENT,\n DEFAULT_RTL_KEYBOARD_MOVEMENT,\n} from \"./constants.js\";\nimport { findMatchIndex } from \"./findMatchIndex.js\";\nimport {\n type KeyboardFocusFromKeyOptions,\n type KeyboardMovementConfig,\n type KeyboardMovementConfiguration,\n type KeyboardMovementContext,\n type KeyboardMovementProviderImplementation,\n type KeyboardMovementProviderOptions,\n} from \"./types.js\";\nimport {\n getFirstFocusableIndex,\n getLastFocusableIndex,\n getNextFocusableIndex,\n getSearchText,\n getVirtualFocusDefaultIndex,\n isElementDisabled,\n isNotFocusable,\n isSearchableEvent,\n recalculateFocusIndex,\n} from \"./utils.js\";\n\nconst noop = (): void => {\n // do nothing\n};\n\n/**\n * @since 6.3.0\n */\nexport const DEFAULT_KEYBOARD_MOVEMENT_CONTEXT: Readonly<KeyboardMovementContext> =\n {\n config: { current: DEFAULT_KEYBOARD_MOVEMENT },\n loopable: false,\n searchable: false,\n horizontal: false,\n includeDisabled: false,\n tabIndexBehavior: undefined,\n activeDescendantId: \"\",\n focusFirst: noop,\n focusLast: noop,\n focusNext: noop,\n focusPrevious: noop,\n focusFromKey: noop,\n };\n\n/**\n * @since 5.0.0\n * @internal\n */\nconst context = createContext<KeyboardMovementContext>(\n DEFAULT_KEYBOARD_MOVEMENT_CONTEXT\n);\ncontext.displayName = \"KeyboardMovement\";\nexport const { Provider: KeyboardMovementProvider } = context;\n\n/**\n * @since 5.0.0\n * @internal\n */\nexport function useKeyboardMovementContext(): Readonly<KeyboardMovementContext> {\n return useContext(context);\n}\n\nconst returnNegative1 = (): number => -1;\n\n/**\n * Implements the custom keyboard movement behavior throughout react-md. Using\n * the \"Find References\" will be the best way to see example usage.\n *\n * @example Default Keyboard Movement for any Focusable Element\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider();\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * {children}\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n *\n * @example Active Descendant Movement\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementContext,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n * import { useId } from \"react\";\n *\n * function Child(): ReactElement {\n * const id = useId()\n * const { activeDescendantId } = useKeyboardMovementContext();\n *\n * return (\n * <div\n * {...props}\n * id={id}\n * className={cnb(id === activeDescendantId && \"focused-class-name\")}\n * >\n * Some Content\n * </div>\n * );\n * }\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider({\n * loopable: true,\n * searchable: true,\n * tabIndexBehavior: \"virtual\",\n * });\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * <Child />\n * <Child />\n * <Child />\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n *\n * @example Roving Tab Index\n * ```tsx\n * import {\n * KeyboardMovementProvider,\n * useKeyboardMovementContext,\n * useKeyboardMovementProvider,\n * } from \"@react-md/core/movement/useKeyboardMovementProvider\";\n * import type { ReactElement, ReactNode } from \"react\";\n * import { useId } from \"react\";\n *\n * function Child(): ReactElement {\n * const id = useId()\n * const { activeDescendantId } = useKeyboardMovementContext();\n *\n * return (\n * <div\n * {...props}\n * id={id}\n * tabIndex={id === activeDescendantId ? 0 : -1}\n * >\n * Some Content\n * </div>\n * );\n * }\n *\n * function Example({ children }: { children: ReactNode }): ReactElement {\n * const { movementContext, movementProps } = useKeyboardMovementProvider({\n * loopable: true,\n * searchable: true,\n * tabIndexBehavior: \"roving\",\n * });\n *\n * // any focusable element child can be focused with the arrow , home, and\n * // end keys\n * return (\n * <KeyboardMovementProvider value={movementContext}>\n * <div {...movementProps}>\n * <Child />\n * <Child />\n * <Child />\n * </div>\n * </KeyboardMovementProvider>\n * );\n * }\n * ```\n * @since 6.0.0\n * @internal\n */\nexport function useKeyboardMovementProvider<E extends HTMLElement>(\n options: KeyboardMovementProviderOptions<E> = {}\n): KeyboardMovementProviderImplementation<E> {\n const {\n ref: propRef,\n onClick = noop,\n onFocus = noop,\n onKeyDown = noop,\n loopable = false,\n disabled,\n searchable = false,\n horizontal = false,\n trackTabKeys = false,\n includeDisabled = false,\n tabIndexBehavior,\n extendKeyDown = noop,\n onFocusChange = noop,\n programmatic = includeDisabled,\n incrementKeys: propIncrementKeys,\n decrementKeys: propDecrementKeys,\n jumpToFirstKeys: propJumpToFirstKeys,\n jumpToLastKeys: propJumpToLastKeys,\n getFocusableElements = defaultGetFocusableElements,\n getDefaultFocusedIndex = returnNegative1,\n isNegativeOneAllowed = false,\n } = options;\n\n const [nodeRef, nodeRefCallback] = useEnsuredRef(propRef);\n\n const isRTL = useDir().dir === \"rtl\";\n let defaults: Readonly<Required<KeyboardMovementConfiguration>>;\n if (horizontal) {\n defaults = isRTL\n ? DEFAULT_RTL_KEYBOARD_MOVEMENT\n : DEFAULT_LTR_KEYBOARD_MOVEMENT;\n } else {\n defaults = DEFAULT_KEYBOARD_MOVEMENT;\n }\n\n const incrementKeys = propIncrementKeys || defaults.incrementKeys;\n const decrementKeys = propDecrementKeys || defaults.decrementKeys;\n const jumpToFirstKeys = propJumpToFirstKeys || defaults.jumpToFirstKeys;\n const jumpToLastKeys = propJumpToLastKeys || defaults.jumpToLastKeys;\n\n const configuration: KeyboardMovementConfig = {\n incrementKeys,\n decrementKeys,\n jumpToFirstKeys,\n jumpToLastKeys,\n };\n const config = useRef(configuration);\n useIsomorphicLayoutEffect(() => {\n config.current = configuration;\n });\n\n const mode = useUserInteractionMode();\n const refocus = useRef(false);\n const currentFocusIndex = useRef(-1);\n const [activeDescendantId, setActiveDescendantId] = useState(\"\");\n\n if (process.env.NODE_ENV !== \"production\") {\n // this fixes issues during hot reloading and using the `useId()` hook\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useEffect(() => {\n return () => {\n setActiveDescendantId(\"\");\n };\n }, []);\n }\n\n let tabIndex: number | undefined;\n if (tabIndexBehavior) {\n tabIndex =\n disabled || (tabIndexBehavior === \"roving\" && activeDescendantId)\n ? -1\n : 0;\n }\n\n const getFocusableElementsFromRef = useCallback(() => {\n const container = nodeRef.current;\n if (!container) {\n return [];\n }\n\n return getFocusableElements(container, programmatic);\n }, [getFocusableElements, nodeRef, programmatic]);\n const updateFocusIndex = useCallback(\n (index: number, focusables = getFocusableElementsFromRef()) => {\n if (currentFocusIndex.current === index || index === -1) {\n return;\n }\n\n currentFocusIndex.current = index;\n const focused = focusables[index];\n if (tabIndexBehavior) {\n focused.scrollIntoView({\n block: \"nearest\",\n inline: \"nearest\",\n });\n setActiveDescendantId(focused.id);\n }\n\n if (tabIndexBehavior !== \"virtual\") {\n focused.focus();\n }\n\n onFocusChange({\n index,\n element: focused,\n });\n },\n [getFocusableElementsFromRef, onFocusChange, tabIndexBehavior]\n );\n\n const focusNext = useCallback(\n (focusables = getFocusableElementsFromRef()) => {\n updateFocusIndex(\n getNextFocusableIndex({\n loopable,\n increment: true,\n focusables,\n includeDisabled: true,\n currentFocusIndex: currentFocusIndex.current,\n }),\n focusables\n );\n },\n [getFocusableElementsFromRef, loopable, updateFocusIndex]\n );\n const focusPrevious = useCallback(\n (focusables = getFocusableElementsFromRef()) => {\n updateFocusIndex(\n getNextFocusableIndex({\n loopable,\n increment: false,\n focusables,\n includeDisabled: true,\n currentFocusIndex: currentFocusIndex.current,\n }),\n focusables\n );\n },\n [getFocusableElementsFromRef, loopable, updateFocusIndex]\n );\n const focusFirst = useCallback(\n (focusables = getFocusableElementsFromRef()) => {\n updateFocusIndex(\n getFirstFocusableIndex({\n focusables,\n includeDisabled,\n }),\n focusables\n );\n },\n [getFocusableElementsFromRef, includeDisabled, updateFocusIndex]\n );\n const focusLast = useCallback(\n (focusables = getFocusableElementsFromRef()) => {\n updateFocusIndex(\n getLastFocusableIndex({\n focusables,\n includeDisabled,\n }),\n focusables\n );\n },\n [getFocusableElementsFromRef, includeDisabled, updateFocusIndex]\n );\n const focusFromKey = useCallback(\n (options: KeyboardFocusFromKeyOptions) => {\n const {\n key,\n reversed,\n focusables = getFocusableElementsFromRef(),\n } = options;\n if (!searchable) {\n return;\n }\n\n const index = findMatchIndex({\n value: key,\n values: focusables.map((element) =>\n getSearchText(element, !isNotFocusable(element, includeDisabled))\n ),\n startIndex: reversed ? -1 : currentFocusIndex.current,\n });\n updateFocusIndex(index, focusables);\n },\n [getFocusableElementsFromRef, includeDisabled, searchable, updateFocusIndex]\n );\n\n const movementContext = useMemo<KeyboardMovementContext>(\n () => ({\n config,\n loopable,\n searchable,\n horizontal,\n focusFirst,\n focusLast,\n focusNext,\n focusPrevious,\n focusFromKey,\n includeDisabled,\n tabIndexBehavior,\n activeDescendantId,\n }),\n [\n activeDescendantId,\n focusFirst,\n focusFromKey,\n focusLast,\n focusNext,\n focusPrevious,\n horizontal,\n includeDisabled,\n loopable,\n searchable,\n tabIndexBehavior,\n ]\n );\n\n return {\n nodeRef,\n movementProps: {\n \"aria-activedescendant\":\n tabIndexBehavior === \"virtual\" ? activeDescendantId : undefined,\n ref: nodeRefCallback,\n tabIndex,\n\n // Note: This used to be on the `onFocus` event, but this causes issues in\n // Chromium browsers for drag and drop behavior\n onClick(event) {\n onClick(event);\n if (disabled) {\n return;\n }\n\n // This makes it so you can click an element with a mouse and then\n // keyboard navigate from that element instead of the last keyboard focus\n // element\n const { currentTarget, target } = event;\n if (target === currentTarget || !(target instanceof HTMLElement)) {\n return;\n }\n\n const focusables = getFocusableElements(currentTarget, programmatic);\n const focusedIndex = focusables.findIndex(\n (element) => element === target || element.contains(target)\n );\n if (focusedIndex === -1 || !focusables.length) {\n return;\n }\n\n currentFocusIndex.current = focusedIndex;\n const focused = focusables[focusedIndex];\n if (tabIndexBehavior) {\n setActiveDescendantId(focused.id);\n }\n\n // need to force focus back to the container element when using\n // aria activedescendant\n if (tabIndexBehavior === \"virtual\") {\n refocus.current = true;\n currentTarget.focus();\n }\n\n onFocusChange({\n index: focusedIndex,\n element: focused,\n });\n },\n onFocus(event) {\n onFocus(event);\n if (event.isPropagationStopped() || refocus.current) {\n refocus.current = false;\n return;\n }\n\n if (\n (mode !== \"keyboard\" && tabIndexBehavior !== \"virtual\") ||\n event.target !== event.currentTarget\n ) {\n return;\n }\n\n const focusables = getFocusableElements(\n event.currentTarget,\n programmatic\n );\n if (!focusables.length) {\n return;\n }\n\n let defaultFocusIndex = getDefaultFocusedIndex({\n focusables,\n includeDisabled,\n });\n\n // This allows my custom `getDefaultFocusedIndex` implementations to\n // have a nice fallback without having to re-implement the \"focus\n // first\" behavior\n if (!isNegativeOneAllowed && defaultFocusIndex === -1) {\n if (tabIndexBehavior === \"virtual\") {\n // virtual keyboard navigation **must** always focus at least one element\n defaultFocusIndex = getVirtualFocusDefaultIndex({\n focusables,\n includeDisabled,\n activeDescendantId,\n });\n } else {\n defaultFocusIndex = getFirstFocusableIndex({\n focusables,\n includeDisabled,\n });\n }\n }\n\n if (defaultFocusIndex === -1) {\n return;\n }\n\n currentFocusIndex.current = defaultFocusIndex;\n const focused = focusables[defaultFocusIndex];\n if (tabIndexBehavior) {\n setActiveDescendantId(focused.id);\n }\n\n if (tabIndexBehavior !== \"virtual\") {\n focused.focus();\n } else {\n focused.scrollIntoView({ block: \"nearest\" });\n }\n\n onFocusChange({\n index: defaultFocusIndex,\n element: focused,\n });\n },\n onKeyDown(event) {\n onKeyDown(event);\n if (disabled) {\n return;\n }\n\n const { currentTarget } = event;\n\n const setFocusIndex = (\n index: number,\n focusables: readonly HTMLElement[]\n ): void => {\n event.preventDefault();\n event.stopPropagation();\n updateFocusIndex(index, focusables);\n };\n\n extendKeyDown({\n event,\n setFocusIndex,\n currentFocusIndex,\n setActiveDescendantId,\n ...movementContext,\n });\n\n if (event.isPropagationStopped()) {\n return;\n }\n\n // TODO: Figure this part out. This is currently required for the tree\n // movement when the asterisk key is pressed. There might be other cases\n // as well.\n if (!isNegativeOneAllowed && currentFocusIndex.current === -1) {\n currentFocusIndex.current = recalculateFocusIndex({\n focusables: getFocusableElements(currentTarget, programmatic),\n includeDisabled,\n tabIndexBehavior,\n activeDescendantId,\n });\n }\n\n const { key, shiftKey } = event;\n if (\n tabIndexBehavior === \"virtual\" &&\n activeDescendantId &&\n (key === \" \" || key === \"Enter\")\n ) {\n if (key === \" \") {\n event.preventDefault();\n }\n\n const focusables = getFocusableElements(currentTarget, programmatic);\n const activeElement = focusables[currentFocusIndex.current];\n if (!activeElement || isElementDisabled(activeElement)) {\n return;\n }\n\n activeElement.click();\n return;\n }\n\n const {\n incrementKeys,\n decrementKeys,\n jumpToFirstKeys,\n jumpToLastKeys,\n } = config.current;\n\n if (searchable && isSearchableEvent(event)) {\n event.preventDefault();\n event.stopPropagation();\n focusFromKey({ key, reversed: shiftKey });\n return;\n }\n\n if (trackTabKeys && key === \"Tab\") {\n currentFocusIndex.current += event.shiftKey ? -1 : 1;\n return;\n }\n\n const jumpToFirst = jumpToFirstKeys.includes(key);\n const jumpToLast = !jumpToFirst && jumpToLastKeys.includes(key);\n const increment =\n !jumpToFirst && !jumpToLast && incrementKeys.includes(key);\n const decrement =\n !jumpToFirst &&\n !jumpToLast &&\n !increment &&\n decrementKeys.includes(key);\n\n if (jumpToFirst) {\n event.preventDefault();\n event.stopPropagation();\n focusFirst();\n } else if (jumpToLast) {\n event.preventDefault();\n event.stopPropagation();\n focusLast();\n } else if (increment) {\n event.preventDefault();\n event.stopPropagation();\n focusNext();\n } else if (decrement) {\n event.preventDefault();\n event.stopPropagation();\n focusPrevious();\n }\n },\n },\n movementContext,\n currentFocusIndex,\n activeDescendantId,\n setActiveDescendantId,\n };\n}\n"],"names":["createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","getFocusableElements","defaultGetFocusableElements","useUserInteractionMode","useDir","useEnsuredRef","useIsomorphicLayoutEffect","DEFAULT_KEYBOARD_MOVEMENT","DEFAULT_LTR_KEYBOARD_MOVEMENT","DEFAULT_RTL_KEYBOARD_MOVEMENT","findMatchIndex","getFirstFocusableIndex","getLastFocusableIndex","getNextFocusableIndex","getSearchText","getVirtualFocusDefaultIndex","isElementDisabled","isNotFocusable","isSearchableEvent","recalculateFocusIndex","noop","DEFAULT_KEYBOARD_MOVEMENT_CONTEXT","config","current","loopable","searchable","horizontal","includeDisabled","tabIndexBehavior","undefined","activeDescendantId","focusFirst","focusLast","focusNext","focusPrevious","focusFromKey","context","displayName","Provider","KeyboardMovementProvider","useKeyboardMovementContext","returnNegative1","useKeyboardMovementProvider","options","ref","propRef","onClick","onFocus","onKeyDown","disabled","trackTabKeys","extendKeyDown","onFocusChange","programmatic","incrementKeys","propIncrementKeys","decrementKeys","propDecrementKeys","jumpToFirstKeys","propJumpToFirstKeys","jumpToLastKeys","propJumpToLastKeys","getDefaultFocusedIndex","isNegativeOneAllowed","nodeRef","nodeRefCallback","isRTL","dir","defaults","configuration","mode","refocus","currentFocusIndex","setActiveDescendantId","process","env","NODE_ENV","tabIndex","getFocusableElementsFromRef","container","updateFocusIndex","index","focusables","focused","scrollIntoView","block","inline","id","focus","element","increment","key","reversed","value","values","map","startIndex","movementContext","movementProps","event","currentTarget","target","HTMLElement","focusedIndex","findIndex","contains","length","isPropagationStopped","defaultFocusIndex","setFocusIndex","preventDefault","stopPropagation","shiftKey","activeElement","click","jumpToFirst","includes","jumpToLast","decrement"],"mappings":"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"}
@@ -74,6 +74,7 @@ const noop = ()=>{
74
74
  const forwardRef = useRef(null);
75
75
  const backwardRef = useRef(null);
76
76
  const { movementProps, movementContext } = useKeyboardMovementProvider({
77
+ ref: tabListRef,
77
78
  onClick (event) {
78
79
  onClick(event);
79
80
  if (event.isPropagationStopped() || !(event.target instanceof Element)) {
@@ -125,7 +126,6 @@ const noop = ()=>{
125
126
  return {
126
127
  elementProps: {
127
128
  "aria-orientation": vertical ? "vertical" : "horizontal",
128
- ref: tabListRef,
129
129
  style: {
130
130
  ...style,
131
131
  ...disableTransition ? undefined : indicatorStyles
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tabs/useTabList.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n CSSProperties,\n FocusEventHandler,\n KeyboardEventHandler,\n MouseEventHandler,\n Ref,\n RefObject,\n} from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useAppSize } from \"../media-queries/AppSizeProvider.js\";\nimport type {\n KeyboardMovementContext,\n KeyboardMovementProps,\n} from \"../movement/types.js\";\nimport { useKeyboardMovementProvider } from \"../movement/useKeyboardMovementProvider.js\";\nimport { useDir } from \"../typography/WritingDirectionProvider.js\";\nimport { useEnsuredRef } from \"../useEnsuredRef.js\";\nimport { useResizeObserver } from \"../useResizeObserver.js\";\nimport {\n type TabListActivationMode,\n type TabListScrollButtonsBehavior,\n} from \"./types.js\";\nimport { getTabRoleOnly, scrollTabIntoView } from \"./utils.js\";\n\nconst TAB_SIZE_VAR = \"--rmd-tab-size\";\nconst TAB_OFFSET_VAR = \"--rmd-tab-offset\";\n\nconst noop = (): void => {\n // do nothing\n};\n\nexport type TabWidthVar = typeof TAB_SIZE_VAR;\nexport type TabOffsetVar = typeof TAB_OFFSET_VAR;\n\nexport type IndicatorCSSProperties = CSSProperties &\n Record<TabWidthVar | TabOffsetVar, string>;\n\nexport interface TabListHookOptions {\n ref: Ref<HTMLDivElement> | undefined;\n style: CSSProperties | undefined;\n activeIndex: number;\n setActiveIndex: (nextActiveIndex: number) => void;\n scrollButtons: TabListScrollButtonsBehavior;\n activationMode: TabListActivationMode;\n vertical: boolean;\n onClick: MouseEventHandler<HTMLDivElement> | undefined;\n onFocus: FocusEventHandler<HTMLDivElement> | undefined;\n onKeyDown: KeyboardEventHandler<HTMLDivElement> | undefined;\n disableTransition: boolean;\n}\n\nexport interface TabListHookReturnValue {\n elementProps: KeyboardMovementProps<HTMLDivElement> & {\n \"aria-orientation\": \"horizontal\" | \"vertical\";\n style: CSSProperties;\n ref: Ref<HTMLDivElement>;\n onClick: MouseEventHandler<HTMLDivElement>;\n };\n movementContext: KeyboardMovementContext;\n backwardProps: {\n ref: RefObject<HTMLDivElement>;\n type: \"back\";\n vertical: boolean;\n disableTransition?: boolean;\n };\n forwardProps: {\n ref: RefObject<HTMLDivElement>;\n type: \"forward\";\n vertical: boolean;\n disableTransition?: boolean;\n };\n showScrollButtons: boolean;\n}\n\n/**\n * @internal\n */\nexport function useTabList(\n options: TabListHookOptions\n): TabListHookReturnValue {\n const {\n ref: propRef,\n style,\n activeIndex,\n scrollButtons,\n onClick = noop,\n onFocus,\n onKeyDown,\n activationMode,\n vertical,\n setActiveIndex,\n disableTransition,\n } = options;\n\n const isRTL = useDir().dir === \"rtl\";\n const { isPhone } = useAppSize();\n const isScrollObserverEnabled =\n scrollButtons === \"auto\" ||\n (scrollButtons === \"auto-tablet-or-above\" && !isPhone);\n const [autoScrollButtons, setAutoScrollButtons] = useState(false);\n const showScrollButtons =\n scrollButtons === true ||\n (scrollButtons === \"tablet-or-above\" && !isPhone) ||\n (isScrollObserverEnabled && autoScrollButtons);\n\n const [indicatorStyles, setIndicatorStyles] =\n useState<IndicatorCSSProperties>(() => {\n const tabWidth = `${100 / 3}%`;\n return {\n [TAB_SIZE_VAR]: tabWidth,\n [TAB_OFFSET_VAR]: \"0px\",\n };\n });\n\n const [nodeRef, ref] = useEnsuredRef(propRef);\n const tabListRef = useResizeObserver({\n ref,\n disabled: disableTransition && !isScrollObserverEnabled,\n disableHeight: disableTransition && !isScrollObserverEnabled && !vertical,\n disableWidth: disableTransition && !isScrollObserverEnabled && vertical,\n onUpdate: useCallback(\n (entry) => {\n // this is kind of hacky -- the styles should update when switching\n // between RTL or when the scroll buttons appear, but they aren't\n // required for any styles. Just reference them so that the hooks\n // eslint rule doesn't show a warning...\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n isRTL;\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n showScrollButtons;\n\n if (isScrollObserverEnabled && nodeRef.current) {\n setAutoScrollButtons(\n nodeRef.current.scrollWidth > nodeRef.current.offsetWidth\n );\n }\n\n const activeTab = getTabRoleOnly(entry.target)[activeIndex];\n if (!activeTab || disableTransition) {\n return;\n }\n\n const size = vertical ? activeTab.offsetHeight : activeTab.offsetWidth;\n const offset = vertical ? activeTab.offsetTop : activeTab.offsetLeft;\n const cssVars: IndicatorCSSProperties = {\n [TAB_SIZE_VAR]: `${size}px`,\n [TAB_OFFSET_VAR]: `${offset}px`,\n };\n\n setIndicatorStyles((prevStyles) => {\n if (\n prevStyles &&\n prevStyles[TAB_SIZE_VAR] === cssVars[TAB_SIZE_VAR] &&\n prevStyles[TAB_OFFSET_VAR] === cssVars[TAB_OFFSET_VAR]\n ) {\n return prevStyles;\n }\n\n return cssVars;\n });\n },\n [\n activeIndex,\n disableTransition,\n isRTL,\n isScrollObserverEnabled,\n nodeRef,\n showScrollButtons,\n vertical,\n ]\n ),\n });\n const forwardRef = useRef<HTMLDivElement>(null);\n const backwardRef = useRef<HTMLDivElement>(null);\n const { movementProps, movementContext } = useKeyboardMovementProvider({\n onClick(event) {\n onClick(event);\n if (event.isPropagationStopped() || !(event.target instanceof Element)) {\n return;\n }\n\n const clickedTab = event.target.closest(\"[role='tab']\");\n const tabs = getTabRoleOnly(event.currentTarget);\n const i = tabs.findIndex((tab) => tab === clickedTab);\n if (i !== -1) {\n setActiveIndex(i);\n }\n },\n onFocus,\n onKeyDown,\n onFocusChange(event) {\n const { index } = event;\n if (activationMode === \"automatic\") {\n setActiveIndex(index);\n } else if (scrollButtons) {\n scrollTabIntoView({\n activeIndex: index,\n backward: backwardRef.current,\n container: nodeRef.current,\n forward: forwardRef.current,\n vertical,\n });\n }\n },\n loopable: true,\n searchable: true,\n horizontal: !vertical,\n includeDisabled: true,\n tabIndexBehavior: \"roving\",\n getFocusableElements: getTabRoleOnly,\n });\n\n useEffect(() => {\n scrollTabIntoView({\n activeIndex,\n backward: backwardRef.current,\n container: nodeRef.current,\n forward: forwardRef.current,\n vertical,\n });\n }, [activeIndex, nodeRef, vertical]);\n\n return {\n elementProps: {\n \"aria-orientation\": vertical ? \"vertical\" : \"horizontal\",\n ref: tabListRef,\n style: {\n ...style,\n ...(disableTransition ? undefined : indicatorStyles),\n },\n ...movementProps,\n },\n backwardProps: {\n ref: backwardRef,\n type: \"back\",\n vertical,\n disableTransition,\n },\n forwardProps: {\n ref: forwardRef,\n type: \"forward\",\n vertical,\n disableTransition,\n },\n movementContext,\n showScrollButtons,\n };\n}\n"],"names":["useCallback","useEffect","useRef","useState","useAppSize","useKeyboardMovementProvider","useDir","useEnsuredRef","useResizeObserver","getTabRoleOnly","scrollTabIntoView","TAB_SIZE_VAR","TAB_OFFSET_VAR","noop","useTabList","options","ref","propRef","style","activeIndex","scrollButtons","onClick","onFocus","onKeyDown","activationMode","vertical","setActiveIndex","disableTransition","isRTL","dir","isPhone","isScrollObserverEnabled","autoScrollButtons","setAutoScrollButtons","showScrollButtons","indicatorStyles","setIndicatorStyles","tabWidth","nodeRef","tabListRef","disabled","disableHeight","disableWidth","onUpdate","entry","current","scrollWidth","offsetWidth","activeTab","target","size","offsetHeight","offset","offsetTop","offsetLeft","cssVars","prevStyles","forwardRef","backwardRef","movementProps","movementContext","event","isPropagationStopped","Element","clickedTab","closest","tabs","currentTarget","i","findIndex","tab","onFocusChange","index","backward","container","forward","loopable","searchable","horizontal","includeDisabled","tabIndexBehavior","getFocusableElements","elementProps","undefined","backwardProps","type","forwardProps"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/tabs/useTabList.ts"],"sourcesContent":["\"use client\";\n\nimport type {\n CSSProperties,\n FocusEventHandler,\n KeyboardEventHandler,\n MouseEventHandler,\n Ref,\n RefObject,\n} from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useAppSize } from \"../media-queries/AppSizeProvider.js\";\nimport type {\n KeyboardMovementContext,\n KeyboardMovementProps,\n} from \"../movement/types.js\";\nimport { useKeyboardMovementProvider } from \"../movement/useKeyboardMovementProvider.js\";\nimport { useDir } from \"../typography/WritingDirectionProvider.js\";\nimport { useEnsuredRef } from \"../useEnsuredRef.js\";\nimport { useResizeObserver } from \"../useResizeObserver.js\";\nimport {\n type TabListActivationMode,\n type TabListScrollButtonsBehavior,\n} from \"./types.js\";\nimport { getTabRoleOnly, scrollTabIntoView } from \"./utils.js\";\n\nconst TAB_SIZE_VAR = \"--rmd-tab-size\";\nconst TAB_OFFSET_VAR = \"--rmd-tab-offset\";\n\nconst noop = (): void => {\n // do nothing\n};\n\nexport type TabWidthVar = typeof TAB_SIZE_VAR;\nexport type TabOffsetVar = typeof TAB_OFFSET_VAR;\n\nexport type IndicatorCSSProperties = CSSProperties &\n Record<TabWidthVar | TabOffsetVar, string>;\n\nexport interface TabListHookOptions {\n ref: Ref<HTMLDivElement> | undefined;\n style: CSSProperties | undefined;\n activeIndex: number;\n setActiveIndex: (nextActiveIndex: number) => void;\n scrollButtons: TabListScrollButtonsBehavior;\n activationMode: TabListActivationMode;\n vertical: boolean;\n onClick: MouseEventHandler<HTMLDivElement> | undefined;\n onFocus: FocusEventHandler<HTMLDivElement> | undefined;\n onKeyDown: KeyboardEventHandler<HTMLDivElement> | undefined;\n disableTransition: boolean;\n}\n\nexport interface TabListHookReturnValue {\n elementProps: KeyboardMovementProps<HTMLDivElement> & {\n \"aria-orientation\": \"horizontal\" | \"vertical\";\n style: CSSProperties;\n ref: Ref<HTMLDivElement>;\n onClick: MouseEventHandler<HTMLDivElement>;\n };\n movementContext: KeyboardMovementContext;\n backwardProps: {\n ref: RefObject<HTMLDivElement>;\n type: \"back\";\n vertical: boolean;\n disableTransition?: boolean;\n };\n forwardProps: {\n ref: RefObject<HTMLDivElement>;\n type: \"forward\";\n vertical: boolean;\n disableTransition?: boolean;\n };\n showScrollButtons: boolean;\n}\n\n/**\n * @internal\n */\nexport function useTabList(\n options: TabListHookOptions\n): TabListHookReturnValue {\n const {\n ref: propRef,\n style,\n activeIndex,\n scrollButtons,\n onClick = noop,\n onFocus,\n onKeyDown,\n activationMode,\n vertical,\n setActiveIndex,\n disableTransition,\n } = options;\n\n const isRTL = useDir().dir === \"rtl\";\n const { isPhone } = useAppSize();\n const isScrollObserverEnabled =\n scrollButtons === \"auto\" ||\n (scrollButtons === \"auto-tablet-or-above\" && !isPhone);\n const [autoScrollButtons, setAutoScrollButtons] = useState(false);\n const showScrollButtons =\n scrollButtons === true ||\n (scrollButtons === \"tablet-or-above\" && !isPhone) ||\n (isScrollObserverEnabled && autoScrollButtons);\n\n const [indicatorStyles, setIndicatorStyles] =\n useState<IndicatorCSSProperties>(() => {\n const tabWidth = `${100 / 3}%`;\n return {\n [TAB_SIZE_VAR]: tabWidth,\n [TAB_OFFSET_VAR]: \"0px\",\n };\n });\n\n const [nodeRef, ref] = useEnsuredRef(propRef);\n const tabListRef = useResizeObserver({\n ref,\n disabled: disableTransition && !isScrollObserverEnabled,\n disableHeight: disableTransition && !isScrollObserverEnabled && !vertical,\n disableWidth: disableTransition && !isScrollObserverEnabled && vertical,\n onUpdate: useCallback(\n (entry) => {\n // this is kind of hacky -- the styles should update when switching\n // between RTL or when the scroll buttons appear, but they aren't\n // required for any styles. Just reference them so that the hooks\n // eslint rule doesn't show a warning...\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n isRTL;\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n showScrollButtons;\n\n if (isScrollObserverEnabled && nodeRef.current) {\n setAutoScrollButtons(\n nodeRef.current.scrollWidth > nodeRef.current.offsetWidth\n );\n }\n\n const activeTab = getTabRoleOnly(entry.target)[activeIndex];\n if (!activeTab || disableTransition) {\n return;\n }\n\n const size = vertical ? activeTab.offsetHeight : activeTab.offsetWidth;\n const offset = vertical ? activeTab.offsetTop : activeTab.offsetLeft;\n const cssVars: IndicatorCSSProperties = {\n [TAB_SIZE_VAR]: `${size}px`,\n [TAB_OFFSET_VAR]: `${offset}px`,\n };\n\n setIndicatorStyles((prevStyles) => {\n if (\n prevStyles &&\n prevStyles[TAB_SIZE_VAR] === cssVars[TAB_SIZE_VAR] &&\n prevStyles[TAB_OFFSET_VAR] === cssVars[TAB_OFFSET_VAR]\n ) {\n return prevStyles;\n }\n\n return cssVars;\n });\n },\n [\n activeIndex,\n disableTransition,\n isRTL,\n isScrollObserverEnabled,\n nodeRef,\n showScrollButtons,\n vertical,\n ]\n ),\n });\n const forwardRef = useRef<HTMLDivElement>(null);\n const backwardRef = useRef<HTMLDivElement>(null);\n const { movementProps, movementContext } = useKeyboardMovementProvider({\n ref: tabListRef,\n onClick(event) {\n onClick(event);\n if (event.isPropagationStopped() || !(event.target instanceof Element)) {\n return;\n }\n\n const clickedTab = event.target.closest(\"[role='tab']\");\n const tabs = getTabRoleOnly(event.currentTarget);\n const i = tabs.findIndex((tab) => tab === clickedTab);\n if (i !== -1) {\n setActiveIndex(i);\n }\n },\n onFocus,\n onKeyDown,\n onFocusChange(event) {\n const { index } = event;\n if (activationMode === \"automatic\") {\n setActiveIndex(index);\n } else if (scrollButtons) {\n scrollTabIntoView({\n activeIndex: index,\n backward: backwardRef.current,\n container: nodeRef.current,\n forward: forwardRef.current,\n vertical,\n });\n }\n },\n loopable: true,\n searchable: true,\n horizontal: !vertical,\n includeDisabled: true,\n tabIndexBehavior: \"roving\",\n getFocusableElements: getTabRoleOnly,\n });\n\n useEffect(() => {\n scrollTabIntoView({\n activeIndex,\n backward: backwardRef.current,\n container: nodeRef.current,\n forward: forwardRef.current,\n vertical,\n });\n }, [activeIndex, nodeRef, vertical]);\n\n return {\n elementProps: {\n \"aria-orientation\": vertical ? \"vertical\" : \"horizontal\",\n style: {\n ...style,\n ...(disableTransition ? undefined : indicatorStyles),\n },\n ...movementProps,\n },\n backwardProps: {\n ref: backwardRef,\n type: \"back\",\n vertical,\n disableTransition,\n },\n forwardProps: {\n ref: forwardRef,\n type: \"forward\",\n vertical,\n disableTransition,\n },\n movementContext,\n showScrollButtons,\n };\n}\n"],"names":["useCallback","useEffect","useRef","useState","useAppSize","useKeyboardMovementProvider","useDir","useEnsuredRef","useResizeObserver","getTabRoleOnly","scrollTabIntoView","TAB_SIZE_VAR","TAB_OFFSET_VAR","noop","useTabList","options","ref","propRef","style","activeIndex","scrollButtons","onClick","onFocus","onKeyDown","activationMode","vertical","setActiveIndex","disableTransition","isRTL","dir","isPhone","isScrollObserverEnabled","autoScrollButtons","setAutoScrollButtons","showScrollButtons","indicatorStyles","setIndicatorStyles","tabWidth","nodeRef","tabListRef","disabled","disableHeight","disableWidth","onUpdate","entry","current","scrollWidth","offsetWidth","activeTab","target","size","offsetHeight","offset","offsetTop","offsetLeft","cssVars","prevStyles","forwardRef","backwardRef","movementProps","movementContext","event","isPropagationStopped","Element","clickedTab","closest","tabs","currentTarget","i","findIndex","tab","onFocusChange","index","backward","container","forward","loopable","searchable","horizontal","includeDisabled","tabIndexBehavior","getFocusableElements","elementProps","undefined","backwardProps","type","forwardProps"],"mappings":"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"}
@@ -1,19 +1,16 @@
1
- interface XYCoords {
2
- x: number;
3
- y: number;
4
- }
1
+ import { type Point } from "../types.js";
5
2
  interface BaseDragOptions {
6
- to?: XYCoords | Element;
7
- from?: XYCoords;
8
- delta?: XYCoords;
3
+ to?: Point | Element;
4
+ from?: Point;
5
+ delta?: Point;
9
6
  steps?: number;
10
7
  duration?: number;
11
8
  }
12
9
  type DragOptions = BaseDragOptions & ({
13
- to: XYCoords | Element;
10
+ to: Point | Element;
14
11
  delta?: never;
15
12
  } | {
16
- delta: XYCoords;
13
+ delta: Point;
17
14
  to?: never;
18
15
  });
19
16
  /**
@@ -1,5 +1,5 @@
1
- import type { UseStateSetter } from "../types.js";
2
- import type { SlideDirection } from "./SlideContainer.js";
1
+ import { type UseStateSetter } from "../types.js";
2
+ import { type SlideDirection } from "./SlideContainer.js";
3
3
  /** @since 6.0.0 */
4
4
  export interface CarouselSlideState {
5
5
  direction: SlideDirection;