@react-aria/tabs
Advanced tools
Comparing version 3.0.0-nightly.2771 to 3.0.0-nightly-07431f4b1-241030
289
dist/main.js
@@ -1,36 +0,13 @@ | ||
var { | ||
useLocale | ||
} = require("@react-aria/i18n"); | ||
var $4eeea1c984cc0628$exports = require("./useTab.main.js"); | ||
var $8db1928b18472a1f$exports = require("./useTabPanel.main.js"); | ||
var $f2b4a4926440e901$exports = require("./useTabList.main.js"); | ||
var { | ||
mergeProps, | ||
useLabels, | ||
useId | ||
} = require("@react-aria/utils"); | ||
var { | ||
useLayoutEffect, | ||
useState, | ||
useMemo | ||
} = require("react"); | ||
var { | ||
getFocusableTreeWalker | ||
} = require("@react-aria/focus"); | ||
var { | ||
useSelectableItem, | ||
useSelectableCollection | ||
} = require("@react-aria/selection"); | ||
var { | ||
usePress | ||
} = require("@react-aria/interactions"); | ||
var _babelRuntimeHelpersExtends = $parcel$interopDefault(require("@babel/runtime/helpers/extends")); | ||
function $parcel$interopDefault(a) { | ||
return a && a.__esModule ? a.default : a; | ||
function $parcel$export(e, n, v, s) { | ||
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); | ||
} | ||
$parcel$export(module.exports, "useTab", () => $4eeea1c984cc0628$exports.useTab); | ||
$parcel$export(module.exports, "useTabPanel", () => $8db1928b18472a1f$exports.useTabPanel); | ||
$parcel$export(module.exports, "useTabList", () => $f2b4a4926440e901$exports.useTabList); | ||
/* | ||
@@ -46,255 +23,7 @@ * Copyright 2020 Adobe. All rights reserved. | ||
* governing permissions and limitations under the License. | ||
*/ | ||
const $c685c043bb86d16b2fcf2a65974f3$export$tabsIds = new WeakMap(); | ||
*/ | ||
function $c685c043bb86d16b2fcf2a65974f3$export$generateId(state, key, role) { | ||
if (typeof key === 'string') { | ||
key = key.replace(/\s+/g, ''); | ||
} | ||
let baseId = $c685c043bb86d16b2fcf2a65974f3$export$tabsIds.get(state); | ||
return baseId + "-" + role + "-" + key; | ||
} | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab. | ||
* When selected, the associated tab panel is shown. | ||
*/ | ||
function useTab(props, state, ref) { | ||
let { | ||
key, | ||
isDisabled: propsDisabled | ||
} = props; | ||
let { | ||
selectionManager: manager, | ||
selectedKey | ||
} = state; | ||
let isSelected = key === selectedKey; | ||
let { | ||
itemProps | ||
} = useSelectableItem({ | ||
selectionManager: manager, | ||
key, | ||
ref | ||
}); | ||
let isDisabled = propsDisabled || state.disabledKeys.has(key); | ||
let { | ||
pressProps | ||
} = usePress(_babelRuntimeHelpersExtends({}, itemProps, { | ||
isDisabled | ||
})); | ||
let tabId = $c685c043bb86d16b2fcf2a65974f3$export$generateId(state, key, 'tab'); | ||
let tabPanelId = $c685c043bb86d16b2fcf2a65974f3$export$generateId(state, key, 'tabpanel'); | ||
let { | ||
tabIndex | ||
} = pressProps; | ||
return { | ||
tabProps: _babelRuntimeHelpersExtends({}, pressProps, { | ||
id: tabId, | ||
'aria-selected': isSelected, | ||
'aria-disabled': isDisabled || undefined, | ||
'aria-controls': isSelected ? tabPanelId : undefined, | ||
tabIndex: isDisabled ? undefined : tabIndex, | ||
role: 'tab' | ||
}) | ||
}; | ||
} | ||
exports.useTab = useTab; | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab panel. A tab panel is a container for | ||
* the contents of a tab, and is shown when the tab is selected. | ||
*/ | ||
function useTabPanel(props, state, ref) { | ||
let [tabIndex, setTabIndex] = useState(0); // The tabpanel should have tabIndex=0 when there are no tabbable elements within it. | ||
// Otherwise, tabbing from the focused tab should go directly to the first tabbable element | ||
// within the tabpanel. | ||
useLayoutEffect(() => { | ||
if (ref != null && ref.current) { | ||
let update = () => { | ||
// Detect if there are any tabbable elements and update the tabIndex accordingly. | ||
let walker = getFocusableTreeWalker(ref.current, { | ||
tabbable: true | ||
}); | ||
setTabIndex(walker.nextNode() ? undefined : 0); | ||
}; | ||
update(); // Update when new elements are inserted, or the tabIndex/disabled attribute updates. | ||
let observer = new MutationObserver(update); | ||
observer.observe(ref.current, { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
attributeFilter: ['tabIndex', 'disabled'] | ||
}); | ||
return () => { | ||
observer.disconnect(); | ||
}; | ||
} | ||
}, [ref]); | ||
const id = $c685c043bb86d16b2fcf2a65974f3$export$generateId(state, state == null ? void 0 : state.selectedKey, 'tabpanel'); | ||
const tabPanelProps = useLabels(_babelRuntimeHelpersExtends({}, props, { | ||
id, | ||
'aria-labelledby': $c685c043bb86d16b2fcf2a65974f3$export$generateId(state, state == null ? void 0 : state.selectedKey, 'tab') | ||
})); | ||
return { | ||
tabPanelProps: mergeProps(tabPanelProps, { | ||
tabIndex, | ||
role: 'tabpanel', | ||
'aria-describedby': props['aria-describedby'], | ||
'aria-details': props['aria-details'] | ||
}) | ||
}; | ||
} | ||
exports.useTabPanel = useTabPanel; | ||
class $c98ba41732a4afea84f1af0a5428958$export$TabsKeyboardDelegate { | ||
constructor(collection, direction, orientation, disabledKeys) { | ||
if (disabledKeys === void 0) { | ||
disabledKeys = new Set(); | ||
} | ||
this.collection = void 0; | ||
this.flipDirection = void 0; | ||
this.disabledKeys = void 0; | ||
this.orientation = void 0; | ||
this.collection = collection; | ||
this.flipDirection = direction === 'rtl' && orientation === 'horizontal'; | ||
this.orientation = orientation; | ||
this.disabledKeys = disabledKeys; | ||
} | ||
getKeyLeftOf(key) { | ||
if (this.flipDirection) { | ||
return this.getNextKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
} | ||
getKeyRightOf(key) { | ||
if (this.flipDirection) { | ||
return this.getPreviousKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
} | ||
} | ||
getKeyAbove(key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
getKeyBelow(key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
} | ||
getFirstKey() { | ||
let key = this.collection.getFirstKey(); | ||
if (this.disabledKeys.has(key)) { | ||
key = this.getNextKey(key); | ||
} | ||
return key; | ||
} | ||
getLastKey() { | ||
let key = this.collection.getLastKey(); | ||
if (this.disabledKeys.has(key)) { | ||
key = this.getPreviousKey(key); | ||
} | ||
return key; | ||
} | ||
getNextKey(key) { | ||
do { | ||
key = this.collection.getKeyAfter(key); | ||
if (key == null) { | ||
key = this.collection.getFirstKey(); | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
return key; | ||
} | ||
getPreviousKey(key) { | ||
do { | ||
key = this.collection.getKeyBefore(key); | ||
if (key == null) { | ||
key = this.collection.getLastKey(); | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
return key; | ||
} | ||
} | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab list. | ||
* Tabs organize content into multiple sections and allow users to navigate between them. | ||
*/ | ||
function useTabList(props, state, ref) { | ||
let { | ||
orientation = 'horizontal', | ||
keyboardActivation = 'automatic' | ||
} = props; | ||
let { | ||
collection, | ||
selectionManager: manager, | ||
disabledKeys | ||
} = state; | ||
let { | ||
direction | ||
} = useLocale(); | ||
let delegate = useMemo(() => new $c98ba41732a4afea84f1af0a5428958$export$TabsKeyboardDelegate(collection, direction, orientation, disabledKeys), [collection, disabledKeys, orientation, direction]); | ||
let { | ||
collectionProps | ||
} = useSelectableCollection({ | ||
ref, | ||
selectionManager: manager, | ||
keyboardDelegate: delegate, | ||
selectOnFocus: keyboardActivation === 'automatic', | ||
disallowEmptySelection: true | ||
}); // Compute base id for all tabs | ||
let tabsId = useId(); | ||
$c685c043bb86d16b2fcf2a65974f3$export$tabsIds.set(state, tabsId); | ||
let tabListLabelProps = useLabels(_babelRuntimeHelpersExtends({}, props, { | ||
id: tabsId | ||
})); | ||
return { | ||
tabListProps: _babelRuntimeHelpersExtends({}, mergeProps(collectionProps, tabListLabelProps), { | ||
role: 'tablist', | ||
'aria-orientation': orientation, | ||
tabIndex: undefined | ||
}) | ||
}; | ||
} | ||
exports.useTabList = useTabList; | ||
//# sourceMappingURL=main.js.map |
@@ -1,8 +0,4 @@ | ||
import { useLocale } from "@react-aria/i18n"; | ||
import { mergeProps, useLabels, useId } from "@react-aria/utils"; | ||
import { useLayoutEffect, useState, useMemo } from "react"; | ||
import { getFocusableTreeWalker } from "@react-aria/focus"; | ||
import { useSelectableItem, useSelectableCollection } from "@react-aria/selection"; | ||
import { usePress } from "@react-aria/interactions"; | ||
import _babelRuntimeHelpersEsmExtends from "@babel/runtime/helpers/esm/extends"; | ||
import {useTab as $0175d55c2a017ebc$export$fdf4756d5b8ef90a} from "./useTab.module.js"; | ||
import {useTabPanel as $34bce698202e07cb$export$fae0121b5afe572d} from "./useTabPanel.module.js"; | ||
import {useTabList as $58d314389b21fa3f$export$773e389e644c5874} from "./useTabList.module.js"; | ||
@@ -19,249 +15,8 @@ /* | ||
* governing permissions and limitations under the License. | ||
*/ | ||
const $c5c378b07d37fb3729fd37493414d657$export$tabsIds = new WeakMap(); | ||
*/ | ||
function $c5c378b07d37fb3729fd37493414d657$export$generateId(state, key, role) { | ||
if (typeof key === 'string') { | ||
key = key.replace(/\s+/g, ''); | ||
} | ||
let baseId = $c5c378b07d37fb3729fd37493414d657$export$tabsIds.get(state); | ||
return baseId + "-" + role + "-" + key; | ||
} | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab. | ||
* When selected, the associated tab panel is shown. | ||
*/ | ||
export function useTab(props, state, ref) { | ||
let { | ||
key, | ||
isDisabled: propsDisabled | ||
} = props; | ||
let { | ||
selectionManager: manager, | ||
selectedKey | ||
} = state; | ||
let isSelected = key === selectedKey; | ||
let { | ||
itemProps | ||
} = useSelectableItem({ | ||
selectionManager: manager, | ||
key, | ||
ref | ||
}); | ||
let isDisabled = propsDisabled || state.disabledKeys.has(key); | ||
let { | ||
pressProps | ||
} = usePress(_babelRuntimeHelpersEsmExtends({}, itemProps, { | ||
isDisabled | ||
})); | ||
let tabId = $c5c378b07d37fb3729fd37493414d657$export$generateId(state, key, 'tab'); | ||
let tabPanelId = $c5c378b07d37fb3729fd37493414d657$export$generateId(state, key, 'tabpanel'); | ||
let { | ||
tabIndex | ||
} = pressProps; | ||
return { | ||
tabProps: _babelRuntimeHelpersEsmExtends({}, pressProps, { | ||
id: tabId, | ||
'aria-selected': isSelected, | ||
'aria-disabled': isDisabled || undefined, | ||
'aria-controls': isSelected ? tabPanelId : undefined, | ||
tabIndex: isDisabled ? undefined : tabIndex, | ||
role: 'tab' | ||
}) | ||
}; | ||
} | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab panel. A tab panel is a container for | ||
* the contents of a tab, and is shown when the tab is selected. | ||
*/ | ||
export function useTabPanel(props, state, ref) { | ||
let [tabIndex, setTabIndex] = useState(0); // The tabpanel should have tabIndex=0 when there are no tabbable elements within it. | ||
// Otherwise, tabbing from the focused tab should go directly to the first tabbable element | ||
// within the tabpanel. | ||
useLayoutEffect(() => { | ||
if (ref != null && ref.current) { | ||
let update = () => { | ||
// Detect if there are any tabbable elements and update the tabIndex accordingly. | ||
let walker = getFocusableTreeWalker(ref.current, { | ||
tabbable: true | ||
}); | ||
setTabIndex(walker.nextNode() ? undefined : 0); | ||
}; | ||
update(); // Update when new elements are inserted, or the tabIndex/disabled attribute updates. | ||
let observer = new MutationObserver(update); | ||
observer.observe(ref.current, { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
attributeFilter: ['tabIndex', 'disabled'] | ||
}); | ||
return () => { | ||
observer.disconnect(); | ||
}; | ||
} | ||
}, [ref]); | ||
const id = $c5c378b07d37fb3729fd37493414d657$export$generateId(state, state == null ? void 0 : state.selectedKey, 'tabpanel'); | ||
const tabPanelProps = useLabels(_babelRuntimeHelpersEsmExtends({}, props, { | ||
id, | ||
'aria-labelledby': $c5c378b07d37fb3729fd37493414d657$export$generateId(state, state == null ? void 0 : state.selectedKey, 'tab') | ||
})); | ||
return { | ||
tabPanelProps: mergeProps(tabPanelProps, { | ||
tabIndex, | ||
role: 'tabpanel', | ||
'aria-describedby': props['aria-describedby'], | ||
'aria-details': props['aria-details'] | ||
}) | ||
}; | ||
} | ||
class $ace72fda23df319bd9db38f00073d9$export$TabsKeyboardDelegate { | ||
constructor(collection, direction, orientation, disabledKeys) { | ||
if (disabledKeys === void 0) { | ||
disabledKeys = new Set(); | ||
} | ||
this.collection = void 0; | ||
this.flipDirection = void 0; | ||
this.disabledKeys = void 0; | ||
this.orientation = void 0; | ||
this.collection = collection; | ||
this.flipDirection = direction === 'rtl' && orientation === 'horizontal'; | ||
this.orientation = orientation; | ||
this.disabledKeys = disabledKeys; | ||
} | ||
getKeyLeftOf(key) { | ||
if (this.flipDirection) { | ||
return this.getNextKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
} | ||
getKeyRightOf(key) { | ||
if (this.flipDirection) { | ||
return this.getPreviousKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
} | ||
} | ||
getKeyAbove(key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
getKeyBelow(key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
} | ||
getFirstKey() { | ||
let key = this.collection.getFirstKey(); | ||
if (this.disabledKeys.has(key)) { | ||
key = this.getNextKey(key); | ||
} | ||
return key; | ||
} | ||
getLastKey() { | ||
let key = this.collection.getLastKey(); | ||
if (this.disabledKeys.has(key)) { | ||
key = this.getPreviousKey(key); | ||
} | ||
return key; | ||
} | ||
getNextKey(key) { | ||
do { | ||
key = this.collection.getKeyAfter(key); | ||
if (key == null) { | ||
key = this.collection.getFirstKey(); | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
return key; | ||
} | ||
getPreviousKey(key) { | ||
do { | ||
key = this.collection.getKeyBefore(key); | ||
if (key == null) { | ||
key = this.collection.getLastKey(); | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
return key; | ||
} | ||
} | ||
/** | ||
* Provides the behavior and accessibility implementation for a tab list. | ||
* Tabs organize content into multiple sections and allow users to navigate between them. | ||
*/ | ||
export function useTabList(props, state, ref) { | ||
let { | ||
orientation = 'horizontal', | ||
keyboardActivation = 'automatic' | ||
} = props; | ||
let { | ||
collection, | ||
selectionManager: manager, | ||
disabledKeys | ||
} = state; | ||
let { | ||
direction | ||
} = useLocale(); | ||
let delegate = useMemo(() => new $ace72fda23df319bd9db38f00073d9$export$TabsKeyboardDelegate(collection, direction, orientation, disabledKeys), [collection, disabledKeys, orientation, direction]); | ||
let { | ||
collectionProps | ||
} = useSelectableCollection({ | ||
ref, | ||
selectionManager: manager, | ||
keyboardDelegate: delegate, | ||
selectOnFocus: keyboardActivation === 'automatic', | ||
disallowEmptySelection: true | ||
}); // Compute base id for all tabs | ||
let tabsId = useId(); | ||
$c5c378b07d37fb3729fd37493414d657$export$tabsIds.set(state, tabsId); | ||
let tabListLabelProps = useLabels(_babelRuntimeHelpersEsmExtends({}, props, { | ||
id: tabsId | ||
})); | ||
return { | ||
tabListProps: _babelRuntimeHelpersEsmExtends({}, mergeProps(collectionProps, tabListLabelProps), { | ||
role: 'tablist', | ||
'aria-orientation': orientation, | ||
tabIndex: undefined | ||
}) | ||
}; | ||
} | ||
export {$0175d55c2a017ebc$export$fdf4756d5b8ef90a as useTab, $34bce698202e07cb$export$fae0121b5afe572d as useTabPanel, $58d314389b21fa3f$export$773e389e644c5874 as useTabList}; | ||
//# sourceMappingURL=module.js.map |
import { AriaTabProps, AriaTabPanelProps, AriaTabListProps } from "@react-types/tabs"; | ||
import { HTMLAttributes, RefObject } from "react"; | ||
import { DOMAttributes, FocusableElement, RefObject } from "@react-types/shared"; | ||
import { TabListState } from "@react-stately/tabs"; | ||
interface TabAria { | ||
export interface TabAria { | ||
/** Props for the tab element. */ | ||
tabProps: HTMLAttributes<HTMLElement>; | ||
tabProps: DOMAttributes; | ||
/** Whether the tab is currently selected. */ | ||
isSelected: boolean; | ||
/** Whether the tab is disabled. */ | ||
isDisabled: boolean; | ||
/** Whether the tab is currently in a pressed state. */ | ||
isPressed: boolean; | ||
} | ||
@@ -12,6 +18,6 @@ /** | ||
*/ | ||
export function useTab<T>(props: AriaTabProps, state: TabListState<T>, ref: RefObject<HTMLElement>): TabAria; | ||
interface TabPanelAria { | ||
export function useTab<T>(props: AriaTabProps, state: TabListState<T>, ref: RefObject<FocusableElement | null>): TabAria; | ||
export interface TabPanelAria { | ||
/** Props for the tab panel element. */ | ||
tabPanelProps: HTMLAttributes<HTMLElement>; | ||
tabPanelProps: DOMAttributes; | ||
} | ||
@@ -22,6 +28,8 @@ /** | ||
*/ | ||
export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T>, ref: RefObject<HTMLElement>): TabPanelAria; | ||
interface TabListAria { | ||
export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T> | null, ref: RefObject<Element | null>): TabPanelAria; | ||
export interface AriaTabListOptions<T> extends Omit<AriaTabListProps<T>, 'children'> { | ||
} | ||
export interface TabListAria { | ||
/** Props for the tablist container. */ | ||
tabListProps: HTMLAttributes<HTMLElement>; | ||
tabListProps: DOMAttributes; | ||
} | ||
@@ -32,4 +40,6 @@ /** | ||
*/ | ||
export function useTabList<T>(props: AriaTabListProps<T>, state: TabListState<T>, ref: RefObject<HTMLElement>): TabListAria; | ||
export function useTabList<T>(props: AriaTabListOptions<T>, state: TabListState<T>, ref: RefObject<HTMLElement | null>): TabListAria; | ||
export type { AriaTabListProps, AriaTabPanelProps, AriaTabProps } from '@react-types/tabs'; | ||
export type { Orientation } from '@react-types/shared'; | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@react-aria/tabs", | ||
"version": "3.0.0-nightly.2771+756016d40", | ||
"version": "3.0.0-nightly-07431f4b1-241030", | ||
"description": "Spectrum UI components in React", | ||
@@ -8,2 +8,7 @@ "license": "Apache-2.0", | ||
"module": "dist/module.js", | ||
"exports": { | ||
"types": "./dist/types.d.ts", | ||
"import": "./dist/import.mjs", | ||
"require": "./dist/main.js" | ||
}, | ||
"types": "dist/types.d.ts", | ||
@@ -21,15 +26,14 @@ "source": "src/index.ts", | ||
"dependencies": { | ||
"@babel/runtime": "^7.6.2", | ||
"@react-aria/focus": "3.0.0-nightly.1086+756016d40", | ||
"@react-aria/i18n": "3.0.0-nightly.1086+756016d40", | ||
"@react-aria/interactions": "3.0.0-nightly.1086+756016d40", | ||
"@react-aria/selection": "3.0.0-nightly.1086+756016d40", | ||
"@react-aria/utils": "3.0.0-nightly.1086+756016d40", | ||
"@react-stately/list": "3.2.4-nightly.2771+756016d40", | ||
"@react-stately/tabs": "3.0.0-nightly.1086+756016d40", | ||
"@react-types/shared": "3.0.0-nightly.1086+756016d40", | ||
"@react-types/tabs": "3.0.0-nightly.2771+756016d40" | ||
"@react-aria/focus": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-aria/i18n": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-aria/selection": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-aria/utils": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-stately/tabs": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-types/shared": "^3.0.0-nightly-07431f4b1-241030", | ||
"@react-types/tabs": "^3.0.0-nightly-07431f4b1-241030", | ||
"@swc/helpers": "^0.5.0" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.8.0 || ^17.0.0-rc.1" | ||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", | ||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0" | ||
}, | ||
@@ -39,3 +43,3 @@ "publishConfig": { | ||
}, | ||
"gitHead": "756016d40d65de089987df04d087cba8a2d26185" | ||
} | ||
"stableVersion": "3.9.7" | ||
} |
@@ -12,5 +12,9 @@ /* | ||
*/ | ||
export * from './useTab'; | ||
export * from './useTabPanel'; | ||
export * from './useTabList'; | ||
export {useTab} from './useTab'; | ||
export {useTabPanel} from './useTabPanel'; | ||
export {useTabList} from './useTabList'; | ||
export type {AriaTabListProps, AriaTabPanelProps, AriaTabProps} from '@react-types/tabs'; | ||
export type {Orientation} from '@react-types/shared'; | ||
export type {TabAria} from './useTab'; | ||
export type {TabPanelAria} from './useTabPanel'; | ||
export type {AriaTabListOptions, TabListAria} from './useTabList'; |
@@ -13,16 +13,15 @@ /* | ||
import {Collection, Direction, KeyboardDelegate, Orientation} from '@react-types/shared'; | ||
import {Key} from 'react'; | ||
import {Collection, Direction, Key, KeyboardDelegate, Node, Orientation} from '@react-types/shared'; | ||
export class TabsKeyboardDelegate<T> implements KeyboardDelegate { | ||
private collection: Collection<T>; | ||
private collection: Collection<Node<T>>; | ||
private flipDirection: boolean; | ||
private disabledKeys: Set<Key>; | ||
private orientation: Orientation; | ||
private tabDirection: boolean; | ||
constructor(collection: Collection<T>, direction: Direction, orientation: Orientation, disabledKeys: Set<Key> = new Set()) { | ||
constructor(collection: Collection<Node<T>>, direction: Direction, orientation: Orientation, disabledKeys: Set<Key> = new Set()) { | ||
this.collection = collection; | ||
this.flipDirection = direction === 'rtl' && orientation === 'horizontal'; | ||
this.orientation = orientation; | ||
this.disabledKeys = disabledKeys; | ||
this.tabDirection = orientation === 'horizontal'; | ||
} | ||
@@ -33,8 +32,4 @@ | ||
return this.getNextKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
return this.getPreviousKey(key); | ||
} | ||
@@ -45,22 +40,9 @@ | ||
return this.getPreviousKey(key); | ||
} else { | ||
if (this.orientation === 'horizontal') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
} | ||
return this.getNextKey(key); | ||
} | ||
getKeyAbove(key: Key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getPreviousKey(key); | ||
} | ||
return null; | ||
} | ||
getKeyBelow(key: Key) { | ||
if (this.orientation === 'vertical') { | ||
return this.getNextKey(key); | ||
} | ||
return null; | ||
private isDisabled(key: Key) { | ||
return this.disabledKeys.has(key) || !!this.collection.getItem(key)?.props?.isDisabled; | ||
} | ||
@@ -70,3 +52,3 @@ | ||
let key = this.collection.getFirstKey(); | ||
if (this.disabledKeys.has(key)) { | ||
if (key != null && this.isDisabled(key)) { | ||
key = this.getNextKey(key); | ||
@@ -79,3 +61,3 @@ } | ||
let key = this.collection.getLastKey(); | ||
if (this.disabledKeys.has(key)) { | ||
if (key != null && this.isDisabled(key)) { | ||
key = this.getPreviousKey(key); | ||
@@ -85,3 +67,17 @@ } | ||
} | ||
getKeyAbove(key: Key) { | ||
if (this.tabDirection) { | ||
return null; | ||
} | ||
return this.getPreviousKey(key); | ||
} | ||
getKeyBelow(key: Key) { | ||
if (this.tabDirection) { | ||
return null; | ||
} | ||
return this.getNextKey(key); | ||
} | ||
getNextKey(key) { | ||
@@ -93,3 +89,3 @@ do { | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
} while (this.isDisabled(key)); | ||
return key; | ||
@@ -104,5 +100,5 @@ } | ||
} | ||
} while (this.disabledKeys.has(key)); | ||
} while (this.isDisabled(key)); | ||
return key; | ||
} | ||
} |
@@ -14,11 +14,17 @@ /* | ||
import {AriaTabProps} from '@react-types/tabs'; | ||
import {DOMAttributes, FocusableElement, RefObject} from '@react-types/shared'; | ||
import {filterDOMProps, mergeProps, useLinkProps} from '@react-aria/utils'; | ||
import {generateId} from './utils'; | ||
import {HTMLAttributes, RefObject} from 'react'; | ||
import {TabListState} from '@react-stately/tabs'; | ||
import {usePress} from '@react-aria/interactions'; | ||
import {useSelectableItem} from '@react-aria/selection'; | ||
interface TabAria { | ||
export interface TabAria { | ||
/** Props for the tab element. */ | ||
tabProps: HTMLAttributes<HTMLElement> | ||
tabProps: DOMAttributes, | ||
/** Whether the tab is currently selected. */ | ||
isSelected: boolean, | ||
/** Whether the tab is disabled. */ | ||
isDisabled: boolean, | ||
/** Whether the tab is currently in a pressed state. */ | ||
isPressed: boolean | ||
} | ||
@@ -33,5 +39,5 @@ | ||
state: TabListState<T>, | ||
ref: RefObject<HTMLElement> | ||
ref: RefObject<FocusableElement | null> | ||
): TabAria { | ||
let {key, isDisabled: propsDisabled} = props; | ||
let {key, isDisabled: propsDisabled, shouldSelectOnPressUp} = props; | ||
let {selectionManager: manager, selectedKey} = state; | ||
@@ -41,17 +47,23 @@ | ||
let {itemProps} = useSelectableItem({ | ||
let isDisabled = propsDisabled || state.isDisabled || state.selectionManager.isDisabled(key); | ||
let {itemProps, isPressed} = useSelectableItem({ | ||
selectionManager: manager, | ||
key, | ||
ref | ||
ref, | ||
isDisabled, | ||
shouldSelectOnPressUp, | ||
linkBehavior: 'selection' | ||
}); | ||
let isDisabled = propsDisabled || state.disabledKeys.has(key); | ||
let {pressProps} = usePress({...itemProps, isDisabled}); | ||
let tabId = generateId(state, key, 'tab'); | ||
let tabPanelId = generateId(state, key, 'tabpanel'); | ||
let {tabIndex} = pressProps; | ||
let {tabIndex} = itemProps; | ||
let item = state.collection.getItem(key); | ||
let domProps = filterDOMProps(item?.props, {labelable: true}); | ||
delete domProps.id; | ||
let linkProps = useLinkProps(item?.props); | ||
return { | ||
tabProps: { | ||
...pressProps, | ||
tabProps: mergeProps(domProps, linkProps, itemProps, { | ||
id: tabId, | ||
@@ -63,5 +75,7 @@ 'aria-selected': isSelected, | ||
role: 'tab' | ||
} | ||
}), | ||
isSelected, | ||
isDisabled, | ||
isPressed | ||
}; | ||
} | ||
@@ -14,3 +14,3 @@ /* | ||
import {AriaTabListProps} from '@react-types/tabs'; | ||
import {HTMLAttributes, RefObject, useMemo} from 'react'; | ||
import {DOMAttributes, RefObject} from '@react-types/shared'; | ||
import {mergeProps, useId, useLabels} from '@react-aria/utils'; | ||
@@ -21,10 +21,12 @@ import {TabListState} from '@react-stately/tabs'; | ||
import {useLocale} from '@react-aria/i18n'; | ||
import {useMemo} from 'react'; | ||
import {useSelectableCollection} from '@react-aria/selection'; | ||
interface TabListAria { | ||
export interface AriaTabListOptions<T> extends Omit<AriaTabListProps<T>, 'children'> {} | ||
export interface TabListAria { | ||
/** Props for the tablist container. */ | ||
tabListProps: HTMLAttributes<HTMLElement> | ||
tabListProps: DOMAttributes | ||
} | ||
/** | ||
@@ -34,3 +36,3 @@ * Provides the behavior and accessibility implementation for a tab list. | ||
*/ | ||
export function useTabList<T>(props: AriaTabListProps<T>, state: TabListState<T>, ref: RefObject<HTMLElement>): TabListAria { | ||
export function useTabList<T>(props: AriaTabListOptions<T>, state: TabListState<T>, ref: RefObject<HTMLElement | null>): TabListAria { | ||
let { | ||
@@ -57,3 +59,5 @@ orientation = 'horizontal', | ||
selectOnFocus: keyboardActivation === 'automatic', | ||
disallowEmptySelection: true | ||
disallowEmptySelection: true, | ||
scrollRef: ref, | ||
linkBehavior: 'selection' | ||
}); | ||
@@ -60,0 +64,0 @@ |
@@ -14,11 +14,11 @@ /* | ||
import {AriaTabPanelProps} from '@react-types/tabs'; | ||
import {DOMAttributes, RefObject} from '@react-types/shared'; | ||
import {generateId} from './utils'; | ||
import {getFocusableTreeWalker} from '@react-aria/focus'; | ||
import {HTMLAttributes, RefObject, useLayoutEffect, useState} from 'react'; | ||
import {mergeProps, useLabels} from '@react-aria/utils'; | ||
import {TabListState} from '@react-stately/tabs'; | ||
import {useHasTabbableChild} from '@react-aria/focus'; | ||
interface TabPanelAria { | ||
export interface TabPanelAria { | ||
/** Props for the tab panel element. */ | ||
tabPanelProps: HTMLAttributes<HTMLElement> | ||
tabPanelProps: DOMAttributes | ||
} | ||
@@ -31,34 +31,9 @@ | ||
*/ | ||
export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T>, ref: RefObject<HTMLElement>): TabPanelAria { | ||
let [tabIndex, setTabIndex] = useState(0); | ||
export function useTabPanel<T>(props: AriaTabPanelProps, state: TabListState<T> | null, ref: RefObject<Element | null>): TabPanelAria { | ||
// The tabpanel should have tabIndex=0 when there are no tabbable elements within it. | ||
// Otherwise, tabbing from the focused tab should go directly to the first tabbable element | ||
// within the tabpanel. | ||
useLayoutEffect(() => { | ||
if (ref?.current) { | ||
let update = () => { | ||
// Detect if there are any tabbable elements and update the tabIndex accordingly. | ||
let walker = getFocusableTreeWalker(ref.current, {tabbable: true}); | ||
setTabIndex(walker.nextNode() ? undefined : 0); | ||
}; | ||
let tabIndex = useHasTabbableChild(ref) ? undefined : 0; | ||
update(); | ||
// Update when new elements are inserted, or the tabIndex/disabled attribute updates. | ||
let observer = new MutationObserver(update); | ||
observer.observe(ref.current, { | ||
subtree: true, | ||
childList: true, | ||
attributes: true, | ||
attributeFilter: ['tabIndex', 'disabled'] | ||
}); | ||
return () => { | ||
observer.disconnect(); | ||
}; | ||
} | ||
}, [ref]); | ||
const id = generateId(state, state?.selectedKey, 'tabpanel'); | ||
const id = generateId(state, props.id ?? state?.selectedKey, 'tabpanel'); | ||
const tabPanelProps = useLabels({...props, id, 'aria-labelledby': generateId(state, state?.selectedKey, 'tab')}); | ||
@@ -65,0 +40,0 @@ |
@@ -13,3 +13,3 @@ /* | ||
import {Key} from 'react'; | ||
import {Key} from '@react-types/shared'; | ||
import {TabListState} from '@react-stately/tabs'; | ||
@@ -19,3 +19,7 @@ | ||
export function generateId<T>(state: TabListState<T>, key: Key, role: string) { | ||
export function generateId<T>(state: TabListState<T> | null, key: Key | null | undefined, role: string) { | ||
if (!state) { | ||
// this case should only happen in the first render before the tabs are registered | ||
return ''; | ||
} | ||
if (typeof key === 'string') { | ||
@@ -22,0 +26,0 @@ key = key.replace(/\s+/g, ''); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 10 instances in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
91343
10
40
1098
10
60
+ Added@swc/helpers@^0.5.0
+ Added@formatjs/ecma402-abstract@2.3.3(transitive)
+ Added@formatjs/fast-memoize@2.2.6(transitive)
+ Added@formatjs/icu-messageformat-parser@2.11.1(transitive)
+ Added@formatjs/icu-skeleton-parser@1.8.13(transitive)
+ Added@formatjs/intl-localematcher@0.6.0(transitive)
+ Added@internationalized/date@3.7.0(transitive)
+ Added@internationalized/message@3.1.6(transitive)
+ Added@internationalized/number@3.6.0(transitive)
+ Added@internationalized/string@3.2.5(transitive)
+ Added@react-aria/focus@3.19.1(transitive)
+ Added@react-aria/i18n@3.12.5(transitive)
+ Added@react-aria/interactions@3.23.0(transitive)
+ Added@react-aria/selection@3.22.0(transitive)
+ Added@react-aria/ssr@3.9.7(transitive)
+ Added@react-aria/utils@3.27.0(transitive)
+ Added@react-stately/collections@3.12.1(transitive)
+ Added@react-stately/list@3.11.2(transitive)
+ Added@react-stately/selection@3.19.0(transitive)
+ Added@react-stately/tabs@3.7.1(transitive)
+ Added@react-stately/utils@3.10.5(transitive)
+ Added@react-types/shared@3.27.0(transitive)
+ Added@react-types/tabs@3.3.12(transitive)
+ Added@swc/helpers@0.5.15(transitive)
+ Addedclsx@2.1.1(transitive)
+ Addeddecimal.js@10.5.0(transitive)
+ Addedintl-messageformat@10.7.15(transitive)
+ Addedreact@19.0.0(transitive)
+ Addedreact-dom@19.0.0(transitive)
+ Addedscheduler@0.25.0(transitive)
+ Addedtslib@2.8.1(transitive)
- Removed@babel/runtime@^7.6.2
- Removed@react-aria/interactions@3.0.0-nightly.1086+756016d40
- Removed@babel/runtime@7.26.9(transitive)
- Removedjs-tokens@4.0.0(transitive)
- Removedloose-envify@1.4.0(transitive)
- Removedobject-assign@4.1.1(transitive)
- Removedreact@17.0.2(transitive)
- Removedregenerator-runtime@0.14.1(transitive)
Updated@react-aria/selection@^3.0.0-nightly-07431f4b1-241030