UNPKG

@atlaskit/tabs

Version:

Tabs are used to organize content by grouping similar information on the same page.

254 lines (249 loc) • 11.3 kB
/* tab-list.tsx generated by @compiled/babel-plugin v3.0.2 */ import "./tab-list.compiled.css"; import * as React from 'react'; import { ax, ix } from "@compiled/react/runtime"; import { Children, createRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useIsReducedMotion } from '@atlaskit/motion/use-is-reduced-motion'; import { fg } from '@atlaskit/platform-feature-flags/fg'; import { TabContext } from '../internal/tab-context'; import { TabMotionContext } from '../internal/tab-motion-context'; import useTabList from '../use-tab-list'; const baseStyles = null; const tabListStyles = null; const tabListMotionStyles = null; const getSelectionDirection = (previous, next) => next > previous ? 'right' : 'left'; const getInitialMotionSelection = (selected, isReducedMotion) => ({ direction: 'right', exits: new Map(), isReducedMotion, selected, selectedState: isReducedMotion ? 'visible' : 'entering' }); const reconcileMotionSelection = (motionSelection, selected, isReducedMotion) => { if (isReducedMotion) { if (motionSelection.isReducedMotion && motionSelection.selected === selected && motionSelection.selectedState === 'visible' && motionSelection.exits.size === 0) { return motionSelection; } return { direction: motionSelection.selected === selected ? motionSelection.direction : getSelectionDirection(motionSelection.selected, selected), exits: new Map(), isReducedMotion: true, selected, selectedState: 'visible' }; } let currentSelection = motionSelection; if (currentSelection.isReducedMotion) { currentSelection = { ...currentSelection, isReducedMotion: false }; } if (currentSelection.selected === selected) { return currentSelection; } const direction = getSelectionDirection(currentSelection.selected, selected); const exits = new Map(currentSelection.exits); exits.set(currentSelection.selected, direction); exits.delete(selected); return { direction, exits, isReducedMotion: false, selected, selectedState: 'entering' }; }; const isTabMotionDirection = value => value === 'left' || value === 'right'; const isTabMotionState = value => value === 'entering' || value === 'exiting' || value === 'visible'; const isMatchingMotion = (motionSelection, index, state, direction) => { if (index === motionSelection.selected) { return state === motionSelection.selectedState && direction === motionSelection.direction; } return state === 'exiting' && motionSelection.exits.get(index) === direction; }; const TabMotionProvider = ({ children, length, selected }) => { const prefersReducedMotion = useIsReducedMotion(); const [canUseReducedMotion, setCanUseReducedMotion] = useState(false); useEffect(() => { if (prefersReducedMotion) { setCanUseReducedMotion(true); } }, [prefersReducedMotion]); const isReducedMotion = canUseReducedMotion && prefersReducedMotion; const [motionSelection, setMotionSelection] = useState(() => getInitialMotionSelection(selected, isReducedMotion)); const renderedMotionSelection = reconcileMotionSelection(motionSelection, selected, isReducedMotion); if (renderedMotionSelection !== motionSelection) { setMotionSelection(renderedMotionSelection); } const getTabMotionAttributes = useCallback(index => { if (index === renderedMotionSelection.selected) { return { 'data-motion-capable': 'true', 'data-motion-direction': renderedMotionSelection.direction, 'data-motion-state': renderedMotionSelection.selectedState }; } const exitDirection = renderedMotionSelection.exits.get(index); if (exitDirection !== undefined) { return { 'data-motion-capable': 'true', 'data-motion-direction': exitDirection, 'data-motion-state': 'exiting' }; } return { 'data-motion-capable': 'true' }; }, [renderedMotionSelection]); const motionContextValue = useMemo(() => ({ getTabMotionAttributes }), [getTabMotionAttributes]); const onAnimationEndCapture = useCallback(event => { const target = event.target; if (!(target instanceof HTMLElement) || !target.matches('[role="tab"]') || event.pseudoElement !== '::after') { return; } const position = Number(target.getAttribute('aria-posinset')); const state = target.getAttribute('data-motion-state'); const direction = target.getAttribute('data-motion-direction'); if (!Number.isInteger(position) || position < 1 || position > length || !isTabMotionState(state) || !isTabMotionDirection(direction)) { return; } const index = position - 1; if (!isMatchingMotion(renderedMotionSelection, index, state, direction)) { return; } setMotionSelection(currentSelection => { const reconciledSelection = reconcileMotionSelection(currentSelection, selected, isReducedMotion); if (!isMatchingMotion(reconciledSelection, index, state, direction)) { return reconciledSelection; } if (state === 'entering') { return { ...reconciledSelection, selectedState: 'visible' }; } if (state === 'exiting') { const exits = new Map(reconciledSelection.exits); exits.delete(index); return { ...reconciledSelection, exits }; } return reconciledSelection; }); }, [isReducedMotion, length, renderedMotionSelection, selected]); return /*#__PURE__*/React.createElement(TabMotionContext.Provider, { value: motionContextValue }, children(onAnimationEndCapture)); }; /** * __TabList__ * * A TabList groups `Tab` components together. * * - [Examples](https://atlassian.design/components/tabs/examples) * - [Code](https://atlassian.design/components/tabs/code) * - [Usage](https://atlassian.design/components/tabs/usage) */ const TabList = ({ children }) => { const { tabsId, selected, onChange } = useTabList(); const isMotionUpliftEnabled = fg('platform-dst-motion-uplift-tab'); const ref = /*#__PURE__*/createRef(); // Don't include any conditional children const childrenArray = Children.toArray(children).filter(Boolean); const length = childrenArray.length; const selectTabByIndex = useCallback(index => { var _ref$current; const newSelectedNode = (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.querySelector(`[id='${tabsId}-${index}']`); if (newSelectedNode) { newSelectedNode.focus(); } onChange(index); }, [tabsId, ref, onChange]); const onKeyDown = useCallback(e => { if (!['ArrowRight', 'ArrowLeft', 'Home', 'End'].includes(e.key)) { return; } // preventing horizontal or vertical scroll e.preventDefault(); const lastTabIndex = length - 1; if (['Home', 'End'].includes(e.key)) { const newSelected = e.key === 'Home' ? 0 : lastTabIndex; selectTabByIndex(newSelected); return; } // We use aria-posinset so we don't rely on the selected variable // If we used the selected variable this would regenerate each time // and create an unstable reference const selectedIndex = parseInt(e.currentTarget.getAttribute('aria-posinset') || '0') - 1; const modifier = e.key === 'ArrowRight' ? 1 : -1; let newSelected = selectedIndex + modifier; if (newSelected < 0 || newSelected >= length) { // Cycling focus to move from last to first and from first to last newSelected = newSelected < 0 ? lastTabIndex : 0; } selectTabByIndex(newSelected); }, [length, selectTabByIndex]); // Memoized so the function isn't recreated each time const getTabWithContext = useCallback(({ tab, isSelected, index }) => /*#__PURE__*/React.createElement(TabContext.Provider, { value: { onClick: () => onChange(index), onKeyDown, 'aria-setsize': length, role: 'tab', id: `${tabsId}-${index}`, 'aria-posinset': index + 1, 'aria-selected': isSelected, 'aria-controls': `${tabsId}-${index}-tab`, tabIndex: isSelected ? 0 : -1 }, key: index }, tab), [length, onKeyDown, onChange, tabsId]); const tabs = childrenArray.map((child, index) => getTabWithContext({ tab: child, index, isSelected: index === selected })); if (!isMotionUpliftEnabled) { return /*#__PURE__*/React.createElement("div", { role: "tablist", ref: ref, className: ax(["_1e0c1txw _kqswh2mm _85i5ze3t _1q51ze3t _y4tize3t _bozgze3t", "_k48p1wq8 _ahbqx0bf _gpbcidpf _10vzidpf _1mmwidpf _15plidpf _qwyt1qi0 _7hip15vq _1fud15vq _bb0mh2mm _1quzazsu _rzxytlke _1ofh12x7 _pryi12x7 _1a85u2gc _rmpau2gc _1dze1l2s _1tms1q9c _fiizidpf _1xrmidpf _xyihidpf _166qidpf _1lzu1uh4 _24g71kw7 _140sidpf _lycustnw _15d8b3bt _1fztidpf _wd7eu2gc _1olcu2gc _1oazazsu _w9ewidpf _170tidpf _y1g1idpf _1nvfidpf _1b8d1uh4 _1n121kw7 _7p9oidpf _o2e1stnw _16u6b3bt _1yk1idpf _1lbou2gc _1c9uu2gc _1i20i7uo _bppridpf _1mbxidpf _kn0bidpf _wsgdidpf _rsmzz0c1 _1m0e1kw7 _93pdidpf _1sglstnw _1ksob3bt _1p9sidpf _1qa1u2gc _1jjcu2gc _fiem6x5g _ipw1z0c1 _gmk66x5g _pascidpf _eid3idpf _zr3eidpf _fntnidpf _1mp41kw7 _kfgte4h9 _1cs8stnw _1rus1l7x _1kt9b3bt _1fkridpf _1enwidpf _z5wtu2gc"]) }, tabs); } return ( /*#__PURE__*/ // Only styles that affect the TabList itself have been applied via primitives. // The other styles applied through the CSS prop are there for styling children // through inheritance. This is important for custom cases that use the useTab(), // which applies accessibility atributes that we use as a styling hook. React.createElement(TabMotionProvider, { length: length, selected: selected }, onAnimationEndCapture => /*#__PURE__*/React.createElement("div", { role: "tablist", ref: ref, onAnimationEndCapture: onAnimationEndCapture, className: ax(["_1e0c1txw _kqswh2mm _85i5ze3t _1q51ze3t _y4tize3t _bozgze3t", "_k48p1wq8 _ahbqx0bf _gpbcidpf _10vzidpf _1mmwidpf _15plidpf _qwyt1qi0 _7hip15vq _1fud15vq _bb0mh2mm _1quzazsu _rzxytlke _1ofh12x7 _pryi12x7 _1a85u2gc _rmpau2gc _1dze1l2s _1tms1q9c _fiizidpf _1xrmidpf _xyihidpf _166qidpf _1lzu1uh4 _24g71kw7 _140sidpf _lycustnw _15d8b3bt _1fztidpf _wd7eu2gc _1olcu2gc _1oazazsu _w9ewidpf _170tidpf _y1g1idpf _1nvfidpf _1b8d1uh4 _1n121kw7 _7p9oidpf _o2e1stnw _16u6b3bt _1yk1idpf _1lbou2gc _1c9uu2gc _1i20i7uo _bppridpf _1mbxidpf _kn0bidpf _wsgdidpf _rsmzz0c1 _1m0e1kw7 _93pdidpf _1sglstnw _1ksob3bt _1p9sidpf _1qa1u2gc _1jjcu2gc _fiem6x5g _ipw1z0c1 _gmk66x5g _pascidpf _eid3idpf _zr3eidpf _fntnidpf _1mp41kw7 _kfgte4h9 _1cs8stnw _1rus1l7x _1kt9b3bt _1fkridpf _1enwidpf _z5wtu2gc", "_ty7m1erp _7gbuidpf _182cidpf _hsamidpf _15xxidpf _1bmz1erp _xcwz1uh4 _a2t91kw7 _12ydidpf _ozqtstnw _1oi3b3bt _dt1cidpf _1g64u2gc _1gh0u2gc _10f5idpf _d825glyw _k8qridpf _1q5jidpf _b1stidpf _15toidpf _k8nvz0c1 _1qjz1kw7 _stm6idpf _l0fpstnw _1rrikb7n _wqilb3bt _1fzxidpf _1lk3u2gc _1huvu2gc _bzrxglyw _a0efz7ft _1a1nzznr _siz91kin _1rw111ou _13lhzznr _10puz7ft _1sbx11ou _144y1kin _1tjxkb7n _1y4gkb7n _sr6xglyw _vedkglyw _1mn4z0c1 _1oj46x5g _19bsglyw _1uu4glyw _ne04glyw _1s41glyw"]) }, tabs)) ); }; export default TabList;