UNPKG

@adaptabletools/adaptable

Version:

Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

123 lines (122 loc) 5.49 kB
import * as React from 'react'; import { Box, Flex } from 'rebass'; import { useRef } from 'react'; import join from '../utils/join'; import useProperty from '../utils/useProperty'; const TabContext = React.createContext({ selectedIndex: 0, tabCount: 0, setSelectedIndex: (index) => { }, }); const isTab = (c) => { return c.type === Tab || SupportedTabMenuComps.has(c.type); }; const isTabContent = (c) => { return c.type === Content || SupportedTabContentComps.has(c.type); }; export const Tabs = (props) => { const { autoFocus = true, children, onValueChange: _onValueChange, defaultValue: _defaultValue, value: _value, keyboardNavigation = true, selectedIndex: _selectedIndex, defaultSelectedIndex: _defaultSelectedIndex, onSelectedIndexChange: _onSelectedIndexChange, ...boxProps } = props; let [selectedIndex, doSetSelectedIndex] = useProperty(props, 'selectedIndex', 0); const [selectedValue, setSelectedValue] = useProperty(props, 'value', 0); const allChildren = React.Children.toArray(children); const values = []; const tabs = allChildren.filter(isTab).map((tab, index) => { values.push(tab.props.value); return React.cloneElement(tab, { // @ts-ignore ignore index, keyboardNavigation, autoFocus, }); }); const contents = allChildren.filter(isTabContent); const contentValues = contents.map((c) => c.props.value); const shouldUseValue = (props.value !== undefined || props.defaultValue !== undefined) && props.selectedIndex === undefined && props.defaultSelectedIndex === undefined; if (shouldUseValue) { const indexOfValue = values.indexOf(selectedValue); if (indexOfValue > -1) { selectedIndex = indexOfValue; } const reorderContents = contentValues.filter((contentValue) => values.indexOf(contentValue) != -1).length === tabs.length; if (reorderContents) { contents.sort((c1, c2) => { const v1 = c1.props.value; const v2 = c2.props.value; return values.indexOf(v1) - values.indexOf(v2); }); } } const setSelectedIndex = (index) => { if (shouldUseValue) { const selectedValue = values[index]; setSelectedValue(selectedValue); } doSetSelectedIndex(index); }; const currentContent = contents[selectedIndex]; const { value, ...contentProps } = (currentContent?.props || {}); return (React.createElement(TabContext.Provider, { value: { selectedIndex, tabCount: tabs.length, setSelectedIndex, } }, React.createElement(Box, { ...boxProps, className: join(boxProps.className, 'ab-Tabs'), "data-selected-index": selectedIndex }, React.createElement(Flex, { flexDirection: "row", className: "ab-Tabs__Strip" }, React.createElement(Box, { className: "ab-Tabs__Filler" }), tabs, React.createElement(Flex, { flex: 1, className: "ab-Tabs__Filler" })), React.createElement(Box, { padding: 2, ...contentProps, className: `${contentProps.className || ''} ab-Tabs__Body` }, currentContent)))); }; export const Tab = (props) => { const { index, autoFocus, keyboardNavigation = true, className: propsClassName, ...boxProps } = props; const context = React.useContext(TabContext); const baseClassName = 'ab-Tabs__Tab'; const active = context.selectedIndex === props.index; const beforeActive = context.selectedIndex === props.index + 1; const className = join(propsClassName, baseClassName, active ? `${baseClassName}--active` : '', beforeActive ? `${baseClassName}--before-active` : ''); const ref = useRef(null); React.useEffect(() => { if (active && autoFocus && keyboardNavigation) { ref.current?.focus(); } }, [active, autoFocus]); return (React.createElement(Box, { ref: ref, padding: 2, tabIndex: active ? 0 : -1, ...boxProps, className: className, onClick: (event) => { context.setSelectedIndex(props.index); props.onClick?.(event); }, onKeyDown: (event) => { if (keyboardNavigation) { if (event.key === 'Enter') { context.setSelectedIndex(props.index); } if (event.key === 'ArrowLeft') { event.preventDefault(); let nextIndex = props.index - 1; if (nextIndex < 0) { nextIndex = context.tabCount - 1; } context.setSelectedIndex(nextIndex); } if (event.key === 'ArrowRight') { event.preventDefault(); let nextIndex = props.index + 1; if (nextIndex > context.tabCount - 1) { nextIndex = 0; } context.setSelectedIndex(nextIndex); } } props.onKeyDown?.(event); } })); }; export const SupportedTabMenuComps = new Set(); SupportedTabMenuComps.add(Tab); export const Content = (props) => { return React.createElement(React.Fragment, null, props.children); }; export const SupportedTabContentComps = new Set(); SupportedTabContentComps.add(Content); Tabs.Tab = Tab; Tabs.Content = Content;