UNPKG

preact-arco-design

Version:

Arco Design React UI Library.

456 lines (403 loc) 15.6 kB
var __assign = this && this.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } } return t; }; return __assign.apply(this, arguments); }; var __read = this && this.__read || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) { ar.push(r.value); } } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import React, { useContext, useEffect, useRef, useState, useMemo, useCallback } from "preact/compat"; import { isNumber, isObject } from "../../_util/is"; import ResizeObserver from "../../_util/resizeObserver"; import DropdownIcon from "./dropdown-icon"; import TabNavIcon from "./tab-nav-icon"; import TabHeaderTitle from "./tab-title"; import IconPlus from "../../../icon/react-icon/IconPlus"; import cs from "../../_util/classNames"; import { setTransformStyle } from "../../_util/style"; import { getKeyDownEvent, getRectDiff, updateScrollOffset } from "../utils"; import { TabsContext } from "../tabs"; import TabInk from "./tab-ink"; import IconHover from "../../_class/icon-hover"; import useDomSize from "../hook/useDomSize"; import throttleByRaf from "../../_util/throttleByRaf"; import useHeaderScroll from "../hook/useHeaderScroll"; import { ConfigContext } from "../../ConfigProvider"; var DIRECTION_VERTICAL = 'vertical'; var ALIGN_RIGHT = 'right'; var ALIGN_LEFT = 'left'; var SCROLL_MAP = { delete: true, add: true }; var getHeaderStyle = function getHeaderStyle(_a) { var direction = _a.direction, _b = _a.align, align = _b === void 0 ? ALIGN_LEFT : _b, headerOffset = _a.headerOffset; var value = "translateX(".concat(-headerOffset, "px)"); if (align === ALIGN_RIGHT) { value = "translateX(".concat(headerOffset, "px)"); } if (direction === DIRECTION_VERTICAL) { value = "translateY(".concat(-headerOffset, "px)"); } return setTransformStyle(value); }; var getCurrentHeaderOffset = function getCurrentHeaderOffset(_a) { var direction = _a.direction, _b = _a.align, align = _b === void 0 ? ALIGN_LEFT : _b, headerDom = _a.headerDom, headerWrapperDom = _a.headerWrapperDom; var diffStyle = getRectDiff(headerDom, headerWrapperDom); if (direction === DIRECTION_VERTICAL) return -diffStyle.top; if (align === ALIGN_RIGHT) return diffStyle.right; return -diffStyle.left; }; var TabHeader = React.forwardRef(function (props, ref) { var _a, _b; var ctxProps = useContext(TabsContext); var rtl = useContext(ConfigContext).rtl; var mergeProps = __assign(__assign({}, props), ctxProps); var _c = __read(useDomSize(), 3), headerWrapperRef = _c[0], headerWrapperSize = _c[1], setHeaderWrapperSize = _c[2]; var _d = __read(useDomSize(), 3), headerRef = _d[0], headerSize = _d[1], setHeaderSize = _d[2]; var _e = __read(useDomSize(), 3), scrollWrapperRef = _e[0], scrollWrapperSize = _e[1], setScrollWrapperSize = _e[2]; var titleRef = useRef({}); var _f = __read(useState(0), 2), headerOffset = _f[0], setHeaderOffset = _f[1]; var _g = __read(useState(true), 2), shouldScroll = _g[0], setShouldScroll = _g[1]; var paneChildren = mergeProps.paneChildren, editable = mergeProps.editable, prefixCls = mergeProps.prefixCls, onAddTab = mergeProps.onAddTab, direction = mergeProps.direction, _h = mergeProps.type, type = _h === void 0 ? 'line' : _h, _j = mergeProps.overflow, overflow = _j === void 0 ? 'scroll' : _j, activeTab = mergeProps.activeTab, showAddButton = mergeProps.showAddButton, _k = mergeProps.size, size = _k === void 0 ? 'default' : _k, style = mergeProps.style, tabPosition = mergeProps.tabPosition, className = mergeProps.className, extra = mergeProps.extra, animation = mergeProps.animation, icons = mergeProps.icons, deleteButton = mergeProps.deleteButton, addButton = mergeProps.addButton, renderTabTitle = mergeProps.renderTabTitle, scrollAfterEdit = mergeProps.scrollAfterEdit, _l = mergeProps.scrollPosition, scrollPosition = _l === void 0 ? 'auto' : _l; var scrollConfig = isObject(scrollAfterEdit) ? __assign(__assign({}, SCROLL_MAP), scrollAfterEdit) : SCROLL_MAP; var _m = __read(rtl ? [ALIGN_RIGHT, ALIGN_LEFT] : [ALIGN_LEFT, ALIGN_RIGHT], 2), left = _m[0], right = _m[1]; var align = type === 'capsule' ? right : left; var isScrollable = useMemo(function () { var res = mergeProps.direction === 'vertical' ? scrollWrapperSize.height < headerSize.height : scrollWrapperSize.width < headerSize.width; return res; }, [mergeProps.direction, scrollWrapperSize, headerSize]); var updateScrollWrapperSize = function updateScrollWrapperSize() { if (scrollWrapperRef.current) { var dom = scrollWrapperRef.current; setScrollWrapperSize({ height: dom.offsetHeight, width: dom.offsetWidth }); } }; var onWrapperResize = throttleByRaf(function (entry) { updateScrollWrapperSize(); var dom = entry[0] && entry[0].target; if (dom) { setHeaderWrapperSize({ height: dom.offsetHeight, width: dom.offsetWidth, domRect: dom.getBoundingClientRect() }); } }); var onHeaderResize = throttleByRaf(function (entry) { var dom = entry[0] && entry[0].target; if (dom) { setHeaderSize({ height: dom.offsetHeight, width: dom.offsetWidth, domRect: dom.getBoundingClientRect() }); } }); var getValidOffset = useCallback(function (offset) { var maxOffset = direction === DIRECTION_VERTICAL ? headerSize.height - headerWrapperSize.height : headerSize.width - headerWrapperSize.width; var validOffset = offset; validOffset = Math.min(maxOffset, validOffset); validOffset = Math.max(validOffset, 0); return validOffset; }, [direction, headerSize, headerWrapperSize]); var updateHeaderOffset = function updateHeaderOffset(offset) { var nextOffset = getValidOffset(offset); if (nextOffset !== headerOffset) { setHeaderOffset(nextOffset); } }; useEffect(function () { return function () { onHeaderResize.cancel && onHeaderResize.cancel(); onWrapperResize.cancel && onWrapperResize.cancel(); }; }, []); // 根据激活的 tab 更新 headerOffset,所以依赖里面不能加 headerOffset useEffect(function () { if (!shouldScroll) { setShouldScroll(true); return; } var getActiveTabOffset = function getActiveTabOffset() { var currentTitleNode = titleRef.current[activeTab]; if (!currentTitleNode || !isScrollable) { return 0; } var diffStyle = getRectDiff(currentTitleNode, headerWrapperRef.current); var currentOffset = getCurrentHeaderOffset({ direction: direction, align: align, headerDom: headerRef.current, headerWrapperDom: headerWrapperRef.current }); // 垂直方向的 offset 计算,不分type if (direction === 'vertical') { var nextOffset_1 = currentOffset; var scrollAlign_1 = scrollPosition; var topOffset = currentOffset + diffStyle.top; var bottomOffset = currentOffset + diffStyle.bottom; if (scrollAlign_1 === 'auto') { scrollAlign_1 = diffStyle.top < 0 ? 'start' : diffStyle.bottom > 0 ? 'end' : scrollPosition; } if (scrollAlign_1 === 'start') { nextOffset_1 = topOffset; } else if (scrollAlign_1 === 'end') { nextOffset_1 = bottomOffset; } else if (scrollAlign_1 === 'center') { nextOffset_1 = topOffset - (diffStyle.top - diffStyle.bottom) / 2; } else if (isNumber(scrollAlign_1)) { nextOffset_1 = Math.max(topOffset - scrollAlign_1, bottomOffset); } return nextOffset_1; } // 水平方向的 offset 计算,分为 capsule 和其他,因为 capsule 是右对齐 if (align === 'right') { var startOffset_1 = currentOffset - diffStyle.left; var endOffset_1 = currentOffset - diffStyle.right; var scrollAlign_2 = scrollPosition; var nextOffset_2 = currentOffset; if (scrollPosition === 'auto') { scrollAlign_2 = diffStyle.left < 0 ? 'start' : diffStyle.right > 0 ? 'end' : scrollPosition; } if (scrollAlign_2 === 'start') { nextOffset_2 = startOffset_1; } else if (scrollAlign_2 === 'end') { nextOffset_2 = endOffset_1; } else if (scrollAlign_2 === 'center') { nextOffset_2 = startOffset_1 + (diffStyle.left - diffStyle.right) / 2; } else if (isNumber(scrollAlign_2)) { nextOffset_2 = Math.min(startOffset_1 + scrollAlign_2, endOffset_1); } return nextOffset_2; } var nextOffset = currentOffset; var scrollAlign = scrollPosition; var startOffset = currentOffset + diffStyle.left; var endOffset = currentOffset + diffStyle.right; if (scrollPosition === 'auto') { scrollAlign = diffStyle.left < 0 ? 'start' : diffStyle.right > 0 ? 'end' : scrollPosition; } if (scrollAlign === 'start') { nextOffset = startOffset; } else if (scrollAlign === 'end') { nextOffset = endOffset; } else if (scrollAlign === 'center') { nextOffset = startOffset - (diffStyle.left - diffStyle.right) / 2; } else if (isNumber(scrollAlign)) { nextOffset = Math.max(startOffset - scrollAlign, endOffset); } return nextOffset; }; updateScrollOffset(headerWrapperRef.current, direction); var offset = getActiveTabOffset(); offset = getValidOffset(offset); setHeaderOffset(offset); }, [activeTab, direction, overflow, isScrollable, type, getValidOffset, scrollPosition]); var headerStyle = getHeaderStyle({ direction: direction, align: align, headerOffset: headerOffset }); var isDropdown = isScrollable && overflow === 'dropdown' && direction !== 'vertical'; var isScroll = isScrollable && !isDropdown; var isEditable = editable && (type === 'card' || type === 'card-gutter' || type === 'line'); var handleDelete = function handleDelete(child) { mergeProps.onDeleteTab && mergeProps.onDeleteTab(child.key); setShouldScroll(scrollConfig.delete); }; var handleAdd = function handleAdd() { onAddTab && onAddTab(); setShouldScroll(scrollConfig.add); }; var renderAddIcon = function renderAddIcon(isEditable) { return isEditable && showAddButton && React.createElement("div", __assign({ className: "".concat(prefixCls, "-add-icon"), "aria-label": "add tab", tabIndex: 0, role: "button", onClick: handleAdd }, getKeyDownEvent({ onPressEnter: handleAdd })), addButton || React.createElement(IconHover, { prefix: "".concat(prefixCls, "-add") }, React.createElement("span", { className: "".concat(prefixCls, "-add") }, (icons === null || icons === void 0 ? void 0 : icons.add) || React.createElement(IconPlus, null)))); }; useHeaderScroll({ headerWrapperRef: headerWrapperRef, headerOffset: headerOffset, align: align, direction: direction, isScrollable: isScrollable, onScroll: function onScroll(offset) { updateHeaderOffset(offset); } }); return React.createElement("div", { className: cs("".concat(prefixCls, "-header-nav"), "".concat(prefixCls, "-header-nav-").concat(direction), "".concat(prefixCls, "-header-nav-").concat(tabPosition), "".concat(prefixCls, "-header-size-").concat(size), "".concat(prefixCls, "-header-nav-").concat(type), className), style: style, ref: ref }, React.createElement("div", { className: cs("".concat(prefixCls, "-header-scroll"), (_a = {}, _a["".concat(prefixCls, "-header-overflow-scroll")] = isScroll, _a["".concat(prefixCls, "-header-overflow-dropdown")] = isDropdown, _a)), ref: scrollWrapperRef }, isScroll && React.createElement(TabNavIcon, { iconPos: "prev", rtl: rtl, prefixCls: prefixCls, currentOffset: headerOffset, headerSize: headerSize, headerWrapperSize: headerWrapperSize, // getRef={(name) => getCalcArguments()[name]} direction: direction, align: align, onChange: updateHeaderOffset }), React.createElement(ResizeObserver, { onResize: onWrapperResize }, React.createElement("div", { className: "".concat(prefixCls, "-header-wrapper"), ref: headerWrapperRef }, React.createElement(ResizeObserver, { onResize: onHeaderResize }, React.createElement("div", { className: cs("".concat(prefixCls, "-header"), (_b = {}, _b["".concat(prefixCls, "-header-no-padding")] = !props.headerPadding && direction === 'horizontal' && ['line', 'text'].indexOf(type) > -1, _b)), ref: headerRef, style: headerStyle }, paneChildren.map(function (child, index) { return React.createElement(TabHeaderTitle, __assign({ key: index, ref: function ref(node) { titleRef.current[child.key] = node; }, tabKey: child.key }, child.props, { prefixCls: prefixCls, onDeleteTab: function onDeleteTab() { return handleDelete(child); }, renderTitle: props.children || renderTabTitle, onClickTab: function onClickTab() { mergeProps.onClickTab && mergeProps.onClickTab(child.key); }, isActive: activeTab === child.key, editable: isEditable && child.props.closable !== false, deleteIcon: icons === null || icons === void 0 ? void 0 : icons.delete, deleteButton: deleteButton, getIdPrefix: ctxProps.getIdPrefix, index: index })); }), type === 'line' && React.createElement(TabInk, { disabled: !!paneChildren.find(function (child) { return child && child.props && child.props.disabled && child.key === activeTab; }), prefixCls: prefixCls, animation: animation, direction: direction, getTitleRef: function getTitleRef(key) { return titleRef.current[key]; }, activeTab: activeTab, getHeaderRef: function getHeaderRef() { return headerRef; } }))), !isScrollable && renderAddIcon(isEditable))), isScroll && React.createElement(TabNavIcon, { prefixCls: prefixCls, rtl: rtl, currentOffset: headerOffset, headerSize: headerSize, headerWrapperSize: headerWrapperSize, direction: direction, align: align, onChange: updateHeaderOffset }), isDropdown && React.createElement(DropdownIcon, { onClickTab: mergeProps.onClickTab, paneChildren: paneChildren, prefixCls: prefixCls, currentOffset: headerOffset, headerSize: headerSize, headerWrapperSize: headerWrapperSize, getTitleRef: function getTitleRef(key) { return titleRef.current[key]; }, direction: direction }), (isEditable && isScrollable || extra) && React.createElement("div", { className: "".concat(prefixCls, "-header-extra") }, isScrollable && renderAddIcon(isEditable), extra))); }); TabHeader.displayName = 'TabHeader'; export default TabHeader;