UNPKG

@amaui/ui-react

Version:
388 lines (387 loc) 21.2 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@amaui/utils"); const style_react_1 = require("@amaui/style-react"); const IconMaterialExpandLessW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialExpandLessW100")); const IconMaterialExpandMoreW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialExpandMoreW100")); const IconMaterialNavigateNextW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialNavigateNextW100")); const IconMaterialNavigateBeforeW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialNavigateBeforeW100")); const Surface_1 = __importDefault(require("../Surface")); const Line_1 = __importDefault(require("../Line")); const IconButton_1 = __importDefault(require("../IconButton")); const Divider_1 = __importDefault(require("../Divider")); const useMediaQuery_1 = __importDefault(require("../useMediaQuery")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: { position: 'relative', overflow: 'hidden', flex: '0 0 auto' }, size_small: { minHeight: '39px' }, size_regular: { minHeight: '44px' }, size_large: { minHeight: '62px' }, tabs: { position: 'relative' }, orientation_horizontal: { width: '100%' }, tabs_orientation_horizontal: { height: '100%', overflowX: 'auto' }, tabs_orientation_vertical: { width: '100%', overflowY: 'auto' }, arrow: { flex: '0 0 auto', alignSelf: 'center', justifySelf: 'center', transition: theme.methods.transitions.make(['opacity'], { duration: 'xxs' }), '&[disabled]': { opacity: '0' } }, line: { display: 'inline-block', position: 'absolute', background: 'currentColor', transition: theme.methods.transitions.make(['top', 'left', 'width']), zIndex: '1' }, line_version_primary_size_small_orientation_horizontal: { height: '2px', bottom: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} ${theme.methods.shape.radius.value(40, 'px')} 0 0` }, line_version_primary_size_regular_orientation_horizontal: { height: '3px', bottom: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} ${theme.methods.shape.radius.value(40, 'px')} 0 0` }, line_version_primary_size_large_orientation_horizontal: { height: '4px', bottom: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} ${theme.methods.shape.radius.value(40, 'px')} 0 0` }, line_version_primary_size_small_orientation_vertical: { width: '2px', insetInlineEnd: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} 0 0 ${theme.methods.shape.radius.value(40, 'px')}` }, line_version_primary_size_regular_orientation_vertical: { width: '3px', insetInlineEnd: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} 0 0 ${theme.methods.shape.radius.value(40, 'px')}` }, line_version_primary_size_large_orientation_vertical: { width: '4px', insetInlineEnd: '0', borderRadius: `${theme.methods.shape.radius.value(40, 'px')} 0 0 ${theme.methods.shape.radius.value(40, 'px')}` }, line_version_secondary_size_small_orientation_horizontal: { height: '1px', bottom: '0' }, line_version_secondary_size_regular_orientation_horizontal: { height: '2px', bottom: '0' }, line_version_secondary_size_large_orientation_horizontal: { height: '3px', bottom: '0' }, line_version_secondary_size_small_orientation_vertical: { width: '1px', insetInlineEnd: '0' }, line_version_secondary_size_regular_orientation_vertical: { width: '2px', insetInlineEnd: '0' }, line_version_secondary_size_large_orientation_vertical: { width: '3px', insetInlineEnd: '0' }, divider: { position: 'absolute', '&.amaui-Divider-root': { margin: '0px', background: 'currentColor', opacity: '0.14', zIndex: '1' } }, divider_orientation_horizontal: { '&.amaui-Divider-root': { left: '0', right: '0', bottom: '0' } }, divider_orientation_vertical: { '&.amaui-Divider-root': { top: '0', bottom: '0', insetInlineEnd: '0' } }, fixed: { position: 'fixed', top: '0', insetInline: '0' } }), { name: 'amaui-Tabs' }); const Tabs = react_1.default.forwardRef((props_, ref) => { var _a, _b, _c, _d, _e, _f, _g; const theme = (0, style_react_1.useAmauiTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.amauiTabs) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const Line = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Line) || Line_1.default; }, [theme]); const Surface = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Surface) || Surface_1.default; }, [theme]); const IconButton = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.IconButton) || IconButton_1.default; }, [theme]); const Divider = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Divider) || Divider_1.default; }, [theme]); const { tonal = true, color = 'primary', version = 'primary', value: value_ = 0, valueDefault, onChange: onChange_, isActive, activateOnFocus, align = 'flex-start', justify = 'flex-start', orientation = 'horizontal', size = 'regular', initialLineUpdateTimeout = 0, arrows, arrowsMobile, fixed, noDivider, IconStart = IconMaterialNavigateBeforeW100_1.default, IconEnd = IconMaterialNavigateNextW100_1.default, IconTop = IconMaterialExpandLessW100_1.default, IconBottom = IconMaterialExpandMoreW100_1.default, SurfaceProps, Component = 'div', className, children } = props, other = __rest(props, ["tonal", "color", "version", "value", "valueDefault", "onChange", "isActive", "activateOnFocus", "align", "justify", "orientation", "size", "initialLineUpdateTimeout", "arrows", "arrowsMobile", "fixed", "noDivider", "IconStart", "IconEnd", "IconTop", "IconBottom", "SurfaceProps", "Component", "className", "children"]); const { classes } = useStyle(); const setMoveValue = react_1.default.useState({})[1]; const [init, setInit] = react_1.default.useState(false); const [lineValues, setLineValues] = react_1.default.useState({}); const [value, setValue] = react_1.default.useState(valueDefault !== undefined ? valueDefault : value_); const refs = { root: react_1.default.useRef(undefined), tabsRoot: react_1.default.useRef(undefined), value: react_1.default.useRef(undefined), props: react_1.default.useRef(undefined), mobile: react_1.default.useRef(undefined), version: react_1.default.useRef(undefined), isActive: react_1.default.useRef(undefined), initialLineUpdateTimeout: react_1.default.useRef(undefined) }; const mobile = (0, useMediaQuery_1.default)('(pointer: coarse)', { element: refs.root.current }); refs.value.current = value; refs.props.current = props; refs.mobile.current = mobile; refs.version.current = version; refs.isActive.current = isActive; refs.initialLineUpdateTimeout.current = initialLineUpdateTimeout; react_1.default.useEffect(() => { // Update lineValues value // with the first tab // For elements within a modal // being transitioned, minor bug fix setTimeout(updateLine, refs.initialLineUpdateTimeout.current); const observerMethod = () => updateLine(); // Mutation observer const observerMutation = new ResizeObserver(observerMethod); observerMutation.observe(refs.tabsRoot.current); // Update theme subscription method const method = () => updateLine(); theme.subscriptions.update.subscribe(method); setInit(true); return () => { // Unsubscribe theme.subscriptions.update.unsubscribe(method); observerMutation.disconnect(); }; }, []); const onScroll = react_1.default.useCallback((event) => { const useArrows_ = refs.props.current.arrows && (!refs.mobile.current || refs.props.current.arrowsMobile); if (useArrows_) { setMoveValue({ left: refs.tabsRoot.current.scrollLeft, top: refs.tabsRoot.current.scrollTop, }); } }, []); react_1.default.useEffect(() => { if (init) { if (value_ !== refs.value.current) { setValue(value_); // Update lineValues value updateLine(); } } }, [value_]); const onChange = (valueItem, index) => { // Update inner or controlled if (!props.hasOwnProperty('value')) { setValue(valueItem); // Update line updateLine(); } if ((0, utils_1.is)('function', onChange_)) onChange_(valueItem); }; const move = (forward_ = true) => { const forward = theme.direction === 'ltr' || orientation === 'vertical' ? forward_ : !forward_; const rect = refs.tabsRoot.current.getBoundingClientRect(); let moveValue_; if (orientation === 'horizontal') moveValue_ = { left: refs.tabsRoot.current.scrollLeft + (forward ? 1 : -1) * rect.width, behavior: 'smooth' }; else moveValue_ = { top: refs.tabsRoot.current.scrollTop + (forward ? 1 : -1) * rect.height, behavior: 'smooth' }; refs.tabsRoot.current.scrollTo(moveValue_); }; const updateLine = async () => { var _a, _b, _c; await (0, utils_1.wait)(40); const valueProp = refs.value.current; const tabs = Array.from(((_a = refs.tabsRoot.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`[data-amaui-tab-value]`)) || []); const tab = tabs.find(item => (0, utils_1.is)('function', refs.isActive.current) ? refs.isActive.current(valueProp, item.dataset.amauiTabValue) : String(item.dataset.amauiTabValue) === String(valueProp)); if (tab) { const rect = { parent: (_b = refs.tabsRoot.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect(), tab: tab.getBoundingClientRect(), line: (_c = (refs.version.current === 'primary' ? tab.children[1] : tab)) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect() }; // Update if (rect.parent && rect.line && rect.tab) { // Update left scroll if (orientation === 'horizontal') { if ((rect.tab.x < rect.parent.x) || (rect.parent.x + rect.parent.width < rect.tab.x + rect.tab.width)) { let left = refs.tabsRoot.current.scrollLeft; if (rect.tab.x < rect.parent.x) left += rect.tab.x - rect.parent.x; else left += (rect.tab.x + rect.tab.width) - (rect.parent.x + rect.parent.width); // Update refs.tabsRoot.current.scrollTo({ left, behavior: 'smooth' }); } } else { if ((rect.tab.y < rect.parent.y) || (rect.parent.y + rect.parent.height < rect.tab.y + rect.tab.height)) { let top = refs.tabsRoot.current.scrollTop; if (rect.tab.y < rect.parent.y) top += rect.tab.y - rect.parent.y; else top += (rect.tab.y + rect.tab.height) - (rect.parent.y + rect.parent.height); // Update refs.tabsRoot.current.scrollTo({ top, behavior: 'smooth' }); } } // Update lineValues value setLineValues({ x: rect.line.x - rect.parent.x + refs.tabsRoot.current.scrollLeft, y: rect.line.y - rect.parent.y + refs.tabsRoot.current.scrollTop, width: rect.line.width, height: rect.line.height }); } } }; const direction = orientation === 'horizontal' ? 'row' : 'column'; const propPosition = orientation === 'horizontal' ? 'left' : 'top'; const propMain = orientation === 'horizontal' ? 'width' : 'height'; const useArrows = arrows && (!mobile || arrowsMobile); const ArrowPre = ((0, jsx_runtime_1.jsx)(IconButton, Object.assign({ color: 'inherit', onClick: () => move(false), className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-arrow' ], classes.arrow ]), disabled: ((_a = refs.tabsRoot.current) === null || _a === void 0 ? void 0 : _a[orientation === 'horizontal' ? 'scrollLeft' : 'scrollTop']) === 0 }, { children: orientation === 'horizontal' ? (0, jsx_runtime_1.jsx)(IconStart, {}) : (0, jsx_runtime_1.jsx)(IconTop, {}) }))); const ArrowPost = ((0, jsx_runtime_1.jsx)(IconButton, Object.assign({ color: 'inherit', onClick: () => move(), className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-arrow' ], classes.arrow ]), disabled: orientation === 'horizontal' ? Math.ceil(((_b = refs.tabsRoot.current) === null || _b === void 0 ? void 0 : _b.clientWidth) + ((_c = refs.tabsRoot.current) === null || _c === void 0 ? void 0 : _c.scrollLeft)) === ((_d = refs.tabsRoot.current) === null || _d === void 0 ? void 0 : _d.scrollWidth) : Math.ceil(((_e = refs.tabsRoot.current) === null || _e === void 0 ? void 0 : _e.clientHeight) + ((_f = refs.tabsRoot.current) === null || _f === void 0 ? void 0 : _f.scrollTop)) === ((_g = refs.tabsRoot.current) === null || _g === void 0 ? void 0 : _g.scrollHeight) }, { children: orientation === 'horizontal' ? (0, jsx_runtime_1.jsx)(IconEnd, {}) : (0, jsx_runtime_1.jsx)(IconBottom, {}) }))); const TabElements = react_1.default.Children.toArray(children).filter((item) => { var _a, _b; return (_b = (_a = item === null || item === void 0 ? void 0 : item.type) === null || _a === void 0 ? void 0 : _a.displayName) === null || _b === void 0 ? void 0 : _b.endsWith('Tab'); }); const tabActive = TabElements.find((item, index) => { const valueItem = item.props.value !== undefined ? item.props.value : index; return (0, utils_1.is)('function', refs.isActive.current) ? refs.isActive.current(value, valueItem) : value === valueItem; }); return ((0, jsx_runtime_1.jsxs)(Surface, Object.assign({ ref: item => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = item; } refs.root.current = item; }, tonal: tonal, color: color, gap: 0, direction: direction, align: align, justify: justify, role: 'tablist', "aria-orientation": orientation, Component: Line, AdditionalProps: { Component } }, SurfaceProps, { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-root', `amaui-Tabs-version-${version}`, `amaui-Tabs-size-${size}` ], SurfaceProps === null || SurfaceProps === void 0 ? void 0 : SurfaceProps.className, className, classes.root, classes[`size_${size}`], classes[`orientation_${orientation}`], fixed && classes.fixed ]) }, other, { children: [useArrows && ArrowPre, !noDivider && ((0, jsx_runtime_1.jsx)(Divider, { tonal: tonal, color: color, orientation: orientation, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-divider' ], classes.divider, classes[`divider_orientation_${orientation}`] ]) })), (0, jsx_runtime_1.jsxs)(Line, Object.assign({ ref: refs.tabsRoot, gap: 0, direction: direction, align: 'flex-start', justify: 'flex-start', onScroll: onScroll, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-tabs' ], classes.tabs, classes[`tabs_orientation_${orientation}`] ]) }, { children: [tabActive && ((0, jsx_runtime_1.jsx)("span", { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Tabs', theme) && [ 'amaui-Tabs-line' ], classes.line, classes[`line_version_${version}_size_${size}_orientation_${orientation}`], orientation === 'vertical' && theme.direction === 'rtl' && classes[`line_version_${version}_orientation_vertical_rtl`] ]), style: { [propPosition]: orientation === 'horizontal' ? lineValues.x : lineValues.y, [propMain]: lineValues[propMain] } })), TabElements.map((item, index) => { const valueItem = item.props.value !== undefined ? item.props.value : index; return (react_1.default.cloneElement(item, { tonal: item.props.tonal !== undefined ? item.props.tonal : tonal, color: item.props.color !== undefined ? item.props.color : color, version: item.props.version !== undefined ? item.props.version : version, value: item.props.value !== undefined ? item.props.value : index, size: item.props.size !== undefined ? item.props.size : size, index, onChange, activateOnFocus: item.props.activateOnFocus !== undefined ? item.props.activateOnFocus : activateOnFocus, active: (0, utils_1.is)('function', refs.isActive.current) ? refs.isActive.current(value, valueItem) : value === valueItem, onClick: (event) => { onChange(valueItem, index); if ((0, utils_1.is)('function', item.props.onClick)) item.props.onClick(event); } })); })] })), useArrows && ArrowPost] }))); }); Tabs.displayName = 'amaui-Tabs'; exports.default = Tabs;