@amaui/ui-react
Version:
UI for React
388 lines (387 loc) • 21.2 kB
JavaScript
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;
;