@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
JavaScript
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;