429 lines
17 KiB
JavaScript
429 lines
17 KiB
JavaScript
|
"use strict";
|
||
|
"use client";
|
||
|
|
||
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.default = void 0;
|
||
|
var _CheckOutlined = _interopRequireDefault(require("@ant-design/icons/CheckOutlined"));
|
||
|
var _CopyOutlined = _interopRequireDefault(require("@ant-design/icons/CopyOutlined"));
|
||
|
var _EditOutlined = _interopRequireDefault(require("@ant-design/icons/EditOutlined"));
|
||
|
var _classnames = _interopRequireDefault(require("classnames"));
|
||
|
var _copyToClipboard = _interopRequireDefault(require("copy-to-clipboard"));
|
||
|
var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
|
||
|
var _toArray = _interopRequireDefault(require("rc-util/lib/Children/toArray"));
|
||
|
var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
|
||
|
var _useMergedState = _interopRequireDefault(require("rc-util/lib/hooks/useMergedState"));
|
||
|
var _omit = _interopRequireDefault(require("rc-util/lib/omit"));
|
||
|
var _ref3 = require("rc-util/lib/ref");
|
||
|
var React = _interopRequireWildcard(require("react"));
|
||
|
var _styleChecker = require("../../_util/styleChecker");
|
||
|
var _transButton = _interopRequireDefault(require("../../_util/transButton"));
|
||
|
var _configProvider = require("../../config-provider");
|
||
|
var _useLocale = _interopRequireDefault(require("../../locale/useLocale"));
|
||
|
var _tooltip = _interopRequireDefault(require("../../tooltip"));
|
||
|
var _Editable = _interopRequireDefault(require("../Editable"));
|
||
|
var _Typography = _interopRequireDefault(require("../Typography"));
|
||
|
var _useMergedConfig = _interopRequireDefault(require("../hooks/useMergedConfig"));
|
||
|
var _useUpdatedEffect = _interopRequireDefault(require("../hooks/useUpdatedEffect"));
|
||
|
var _Ellipsis = _interopRequireDefault(require("./Ellipsis"));
|
||
|
var _EllipsisTooltip = _interopRequireDefault(require("./EllipsisTooltip"));
|
||
|
var __rest = void 0 && (void 0).__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;
|
||
|
};
|
||
|
function wrapperDecorations(_ref, content) {
|
||
|
let {
|
||
|
mark,
|
||
|
code,
|
||
|
underline,
|
||
|
delete: del,
|
||
|
strong,
|
||
|
keyboard,
|
||
|
italic
|
||
|
} = _ref;
|
||
|
let currentContent = content;
|
||
|
function wrap(tag, needed) {
|
||
|
if (!needed) {
|
||
|
return;
|
||
|
}
|
||
|
currentContent = /*#__PURE__*/React.createElement(tag, {}, currentContent);
|
||
|
}
|
||
|
wrap('strong', strong);
|
||
|
wrap('u', underline);
|
||
|
wrap('del', del);
|
||
|
wrap('code', code);
|
||
|
wrap('mark', mark);
|
||
|
wrap('kbd', keyboard);
|
||
|
wrap('i', italic);
|
||
|
return currentContent;
|
||
|
}
|
||
|
function getNode(dom, defaultNode, needDom) {
|
||
|
if (dom === true || dom === undefined) {
|
||
|
return defaultNode;
|
||
|
}
|
||
|
return dom || needDom && defaultNode;
|
||
|
}
|
||
|
function toList(val) {
|
||
|
if (val === false) {
|
||
|
return [false, false];
|
||
|
}
|
||
|
return Array.isArray(val) ? val : [val];
|
||
|
}
|
||
|
const ELLIPSIS_STR = '...';
|
||
|
const Base = /*#__PURE__*/React.forwardRef((props, ref) => {
|
||
|
var _a, _b, _c;
|
||
|
const {
|
||
|
prefixCls: customizePrefixCls,
|
||
|
className,
|
||
|
style,
|
||
|
type,
|
||
|
disabled,
|
||
|
children,
|
||
|
ellipsis,
|
||
|
editable,
|
||
|
copyable,
|
||
|
component,
|
||
|
title
|
||
|
} = props,
|
||
|
restProps = __rest(props, ["prefixCls", "className", "style", "type", "disabled", "children", "ellipsis", "editable", "copyable", "component", "title"]);
|
||
|
const {
|
||
|
getPrefixCls,
|
||
|
direction
|
||
|
} = React.useContext(_configProvider.ConfigContext);
|
||
|
const [textLocale] = (0, _useLocale.default)('Text');
|
||
|
const typographyRef = React.useRef(null);
|
||
|
const editIconRef = React.useRef(null);
|
||
|
// ============================ MISC ============================
|
||
|
const prefixCls = getPrefixCls('typography', customizePrefixCls);
|
||
|
const textProps = (0, _omit.default)(restProps, ['mark', 'code', 'delete', 'underline', 'strong', 'keyboard', 'italic']);
|
||
|
// ========================== Editable ==========================
|
||
|
const [enableEdit, editConfig] = (0, _useMergedConfig.default)(editable);
|
||
|
const [editing, setEditing] = (0, _useMergedState.default)(false, {
|
||
|
value: editConfig.editing
|
||
|
});
|
||
|
const {
|
||
|
triggerType = ['icon']
|
||
|
} = editConfig;
|
||
|
const triggerEdit = edit => {
|
||
|
var _a;
|
||
|
if (edit) {
|
||
|
(_a = editConfig.onStart) === null || _a === void 0 ? void 0 : _a.call(editConfig);
|
||
|
}
|
||
|
setEditing(edit);
|
||
|
};
|
||
|
// Focus edit icon when back
|
||
|
(0, _useUpdatedEffect.default)(() => {
|
||
|
var _a;
|
||
|
if (!editing) {
|
||
|
(_a = editIconRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
||
|
}
|
||
|
}, [editing]);
|
||
|
const onEditClick = e => {
|
||
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
||
|
triggerEdit(true);
|
||
|
};
|
||
|
const onEditChange = value => {
|
||
|
var _a;
|
||
|
(_a = editConfig.onChange) === null || _a === void 0 ? void 0 : _a.call(editConfig, value);
|
||
|
triggerEdit(false);
|
||
|
};
|
||
|
const onEditCancel = () => {
|
||
|
var _a;
|
||
|
(_a = editConfig.onCancel) === null || _a === void 0 ? void 0 : _a.call(editConfig);
|
||
|
triggerEdit(false);
|
||
|
};
|
||
|
// ========================== Copyable ==========================
|
||
|
const [enableCopy, copyConfig] = (0, _useMergedConfig.default)(copyable);
|
||
|
const [copied, setCopied] = React.useState(false);
|
||
|
const copyIdRef = React.useRef(null);
|
||
|
const copyOptions = {};
|
||
|
if (copyConfig.format) {
|
||
|
copyOptions.format = copyConfig.format;
|
||
|
}
|
||
|
const cleanCopyId = () => {
|
||
|
if (copyIdRef.current) {
|
||
|
clearTimeout(copyIdRef.current);
|
||
|
}
|
||
|
};
|
||
|
const onCopyClick = e => {
|
||
|
var _a;
|
||
|
e === null || e === void 0 ? void 0 : e.preventDefault();
|
||
|
e === null || e === void 0 ? void 0 : e.stopPropagation();
|
||
|
(0, _copyToClipboard.default)(copyConfig.text || String(children) || '', copyOptions);
|
||
|
setCopied(true);
|
||
|
// Trigger tips update
|
||
|
cleanCopyId();
|
||
|
copyIdRef.current = setTimeout(() => {
|
||
|
setCopied(false);
|
||
|
}, 3000);
|
||
|
(_a = copyConfig.onCopy) === null || _a === void 0 ? void 0 : _a.call(copyConfig, e);
|
||
|
};
|
||
|
React.useEffect(() => cleanCopyId, []);
|
||
|
// ========================== Ellipsis ==========================
|
||
|
const [isLineClampSupport, setIsLineClampSupport] = React.useState(false);
|
||
|
const [isTextOverflowSupport, setIsTextOverflowSupport] = React.useState(false);
|
||
|
const [expanded, setExpanded] = React.useState(false);
|
||
|
const [isJsEllipsis, setIsJsEllipsis] = React.useState(false);
|
||
|
const [isNativeEllipsis, setIsNativeEllipsis] = React.useState(false);
|
||
|
const [isNativeVisible, setIsNativeVisible] = React.useState(true);
|
||
|
const [enableEllipsis, ellipsisConfig] = (0, _useMergedConfig.default)(ellipsis, {
|
||
|
expandable: false
|
||
|
});
|
||
|
const mergedEnableEllipsis = enableEllipsis && !expanded;
|
||
|
// Shared prop to reduce bundle size
|
||
|
const {
|
||
|
rows = 1
|
||
|
} = ellipsisConfig;
|
||
|
const needMeasureEllipsis = React.useMemo(() =>
|
||
|
// Disable ellipsis
|
||
|
!mergedEnableEllipsis ||
|
||
|
// Provide suffix
|
||
|
ellipsisConfig.suffix !== undefined || ellipsisConfig.onEllipsis ||
|
||
|
// Can't use css ellipsis since we need to provide the place for button
|
||
|
ellipsisConfig.expandable || enableEdit || enableCopy, [mergedEnableEllipsis, ellipsisConfig, enableEdit, enableCopy]);
|
||
|
(0, _useLayoutEffect.default)(() => {
|
||
|
if (enableEllipsis && !needMeasureEllipsis) {
|
||
|
setIsLineClampSupport((0, _styleChecker.isStyleSupport)('webkitLineClamp'));
|
||
|
setIsTextOverflowSupport((0, _styleChecker.isStyleSupport)('textOverflow'));
|
||
|
}
|
||
|
}, [needMeasureEllipsis, enableEllipsis]);
|
||
|
const cssEllipsis = React.useMemo(() => {
|
||
|
if (needMeasureEllipsis) {
|
||
|
return false;
|
||
|
}
|
||
|
if (rows === 1) {
|
||
|
return isTextOverflowSupport;
|
||
|
}
|
||
|
return isLineClampSupport;
|
||
|
}, [needMeasureEllipsis, isTextOverflowSupport, isLineClampSupport]);
|
||
|
const isMergedEllipsis = mergedEnableEllipsis && (cssEllipsis ? isNativeEllipsis : isJsEllipsis);
|
||
|
const cssTextOverflow = mergedEnableEllipsis && rows === 1 && cssEllipsis;
|
||
|
const cssLineClamp = mergedEnableEllipsis && rows > 1 && cssEllipsis;
|
||
|
// >>>>> Expand
|
||
|
const onExpandClick = e => {
|
||
|
var _a;
|
||
|
setExpanded(true);
|
||
|
(_a = ellipsisConfig.onExpand) === null || _a === void 0 ? void 0 : _a.call(ellipsisConfig, e);
|
||
|
};
|
||
|
const [ellipsisWidth, setEllipsisWidth] = React.useState(0);
|
||
|
const [ellipsisFontSize, setEllipsisFontSize] = React.useState(0);
|
||
|
const onResize = (_ref2, element) => {
|
||
|
let {
|
||
|
offsetWidth
|
||
|
} = _ref2;
|
||
|
var _a;
|
||
|
setEllipsisWidth(offsetWidth);
|
||
|
setEllipsisFontSize(parseInt((_a = window.getComputedStyle) === null || _a === void 0 ? void 0 : _a.call(window, element).fontSize, 10) || 0);
|
||
|
};
|
||
|
// >>>>> JS Ellipsis
|
||
|
const onJsEllipsis = jsEllipsis => {
|
||
|
var _a;
|
||
|
setIsJsEllipsis(jsEllipsis);
|
||
|
// Trigger if changed
|
||
|
if (isJsEllipsis !== jsEllipsis) {
|
||
|
(_a = ellipsisConfig.onEllipsis) === null || _a === void 0 ? void 0 : _a.call(ellipsisConfig, jsEllipsis);
|
||
|
}
|
||
|
};
|
||
|
// >>>>> Native ellipsis
|
||
|
React.useEffect(() => {
|
||
|
const textEle = typographyRef.current;
|
||
|
if (enableEllipsis && cssEllipsis && textEle) {
|
||
|
const currentEllipsis = cssLineClamp ? textEle.offsetHeight < textEle.scrollHeight : textEle.offsetWidth < textEle.scrollWidth;
|
||
|
if (isNativeEllipsis !== currentEllipsis) {
|
||
|
setIsNativeEllipsis(currentEllipsis);
|
||
|
}
|
||
|
}
|
||
|
}, [enableEllipsis, cssEllipsis, children, cssLineClamp, isNativeVisible]);
|
||
|
// https://github.com/ant-design/ant-design/issues/36786
|
||
|
// Use IntersectionObserver to check if element is invisible
|
||
|
React.useEffect(() => {
|
||
|
const textEle = typographyRef.current;
|
||
|
if (typeof IntersectionObserver === 'undefined' || !textEle || !cssEllipsis || !mergedEnableEllipsis) {
|
||
|
return;
|
||
|
}
|
||
|
/* eslint-disable-next-line compat/compat */
|
||
|
const observer = new IntersectionObserver(() => {
|
||
|
setIsNativeVisible(!!textEle.offsetParent);
|
||
|
});
|
||
|
observer.observe(textEle);
|
||
|
return () => {
|
||
|
observer.disconnect();
|
||
|
};
|
||
|
}, [cssEllipsis, mergedEnableEllipsis]);
|
||
|
// ========================== Tooltip ===========================
|
||
|
let tooltipProps = {};
|
||
|
if (ellipsisConfig.tooltip === true) {
|
||
|
tooltipProps = {
|
||
|
title: (_a = editConfig.text) !== null && _a !== void 0 ? _a : children
|
||
|
};
|
||
|
} else if ( /*#__PURE__*/React.isValidElement(ellipsisConfig.tooltip)) {
|
||
|
tooltipProps = {
|
||
|
title: ellipsisConfig.tooltip
|
||
|
};
|
||
|
} else if (typeof ellipsisConfig.tooltip === 'object') {
|
||
|
tooltipProps = Object.assign({
|
||
|
title: (_b = editConfig.text) !== null && _b !== void 0 ? _b : children
|
||
|
}, ellipsisConfig.tooltip);
|
||
|
} else {
|
||
|
tooltipProps = {
|
||
|
title: ellipsisConfig.tooltip
|
||
|
};
|
||
|
}
|
||
|
const topAriaLabel = React.useMemo(() => {
|
||
|
const isValid = val => ['string', 'number'].includes(typeof val);
|
||
|
if (!enableEllipsis || cssEllipsis) {
|
||
|
return undefined;
|
||
|
}
|
||
|
if (isValid(editConfig.text)) {
|
||
|
return editConfig.text;
|
||
|
}
|
||
|
if (isValid(children)) {
|
||
|
return children;
|
||
|
}
|
||
|
if (isValid(title)) {
|
||
|
return title;
|
||
|
}
|
||
|
if (isValid(tooltipProps.title)) {
|
||
|
return tooltipProps.title;
|
||
|
}
|
||
|
return undefined;
|
||
|
}, [enableEllipsis, cssEllipsis, title, tooltipProps.title, isMergedEllipsis]);
|
||
|
// =========================== Render ===========================
|
||
|
// >>>>>>>>>>> Editing input
|
||
|
if (editing) {
|
||
|
return /*#__PURE__*/React.createElement(_Editable.default, {
|
||
|
value: (_c = editConfig.text) !== null && _c !== void 0 ? _c : typeof children === 'string' ? children : '',
|
||
|
onSave: onEditChange,
|
||
|
onCancel: onEditCancel,
|
||
|
onEnd: editConfig.onEnd,
|
||
|
prefixCls: prefixCls,
|
||
|
className: className,
|
||
|
style: style,
|
||
|
direction: direction,
|
||
|
component: component,
|
||
|
maxLength: editConfig.maxLength,
|
||
|
autoSize: editConfig.autoSize,
|
||
|
enterIcon: editConfig.enterIcon
|
||
|
});
|
||
|
}
|
||
|
// >>>>>>>>>>> Typography
|
||
|
// Expand
|
||
|
const renderExpand = () => {
|
||
|
const {
|
||
|
expandable,
|
||
|
symbol
|
||
|
} = ellipsisConfig;
|
||
|
if (!expandable) return null;
|
||
|
let expandContent;
|
||
|
if (symbol) {
|
||
|
expandContent = symbol;
|
||
|
} else {
|
||
|
expandContent = textLocale === null || textLocale === void 0 ? void 0 : textLocale.expand;
|
||
|
}
|
||
|
return /*#__PURE__*/React.createElement("a", {
|
||
|
key: "expand",
|
||
|
className: `${prefixCls}-expand`,
|
||
|
onClick: onExpandClick,
|
||
|
"aria-label": textLocale === null || textLocale === void 0 ? void 0 : textLocale.expand
|
||
|
}, expandContent);
|
||
|
};
|
||
|
// Edit
|
||
|
const renderEdit = () => {
|
||
|
if (!enableEdit) return;
|
||
|
const {
|
||
|
icon,
|
||
|
tooltip
|
||
|
} = editConfig;
|
||
|
const editTitle = (0, _toArray.default)(tooltip)[0] || (textLocale === null || textLocale === void 0 ? void 0 : textLocale.edit);
|
||
|
const ariaLabel = typeof editTitle === 'string' ? editTitle : '';
|
||
|
return triggerType.includes('icon') ? /*#__PURE__*/React.createElement(_tooltip.default, {
|
||
|
key: "edit",
|
||
|
title: tooltip === false ? '' : editTitle
|
||
|
}, /*#__PURE__*/React.createElement(_transButton.default, {
|
||
|
ref: editIconRef,
|
||
|
className: `${prefixCls}-edit`,
|
||
|
onClick: onEditClick,
|
||
|
"aria-label": ariaLabel
|
||
|
}, icon || /*#__PURE__*/React.createElement(_EditOutlined.default, {
|
||
|
role: "button"
|
||
|
}))) : null;
|
||
|
};
|
||
|
// Copy
|
||
|
const renderCopy = () => {
|
||
|
if (!enableCopy) return;
|
||
|
const {
|
||
|
tooltips,
|
||
|
icon
|
||
|
} = copyConfig;
|
||
|
const tooltipNodes = toList(tooltips);
|
||
|
const iconNodes = toList(icon);
|
||
|
const copyTitle = copied ? getNode(tooltipNodes[1], textLocale === null || textLocale === void 0 ? void 0 : textLocale.copied) : getNode(tooltipNodes[0], textLocale === null || textLocale === void 0 ? void 0 : textLocale.copy);
|
||
|
const systemStr = copied ? textLocale === null || textLocale === void 0 ? void 0 : textLocale.copied : textLocale === null || textLocale === void 0 ? void 0 : textLocale.copy;
|
||
|
const ariaLabel = typeof copyTitle === 'string' ? copyTitle : systemStr;
|
||
|
return /*#__PURE__*/React.createElement(_tooltip.default, {
|
||
|
key: "copy",
|
||
|
title: copyTitle
|
||
|
}, /*#__PURE__*/React.createElement(_transButton.default, {
|
||
|
className: (0, _classnames.default)(`${prefixCls}-copy`, copied && `${prefixCls}-copy-success`),
|
||
|
onClick: onCopyClick,
|
||
|
"aria-label": ariaLabel
|
||
|
}, copied ? getNode(iconNodes[1], /*#__PURE__*/React.createElement(_CheckOutlined.default, null), true) : getNode(iconNodes[0], /*#__PURE__*/React.createElement(_CopyOutlined.default, null), true)));
|
||
|
};
|
||
|
const renderOperations = renderExpanded => [renderExpanded && renderExpand(), renderEdit(), renderCopy()];
|
||
|
const renderEllipsis = needEllipsis => [needEllipsis && /*#__PURE__*/React.createElement("span", {
|
||
|
"aria-hidden": true,
|
||
|
key: "ellipsis"
|
||
|
}, ELLIPSIS_STR), ellipsisConfig.suffix, renderOperations(needEllipsis)];
|
||
|
return /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
|
||
|
onResize: onResize,
|
||
|
disabled: !mergedEnableEllipsis || cssEllipsis
|
||
|
}, resizeRef => /*#__PURE__*/React.createElement(_EllipsisTooltip.default, {
|
||
|
tooltipProps: tooltipProps,
|
||
|
enabledEllipsis: mergedEnableEllipsis,
|
||
|
isEllipsis: isMergedEllipsis
|
||
|
}, /*#__PURE__*/React.createElement(_Typography.default, Object.assign({
|
||
|
className: (0, _classnames.default)({
|
||
|
[`${prefixCls}-${type}`]: type,
|
||
|
[`${prefixCls}-disabled`]: disabled,
|
||
|
[`${prefixCls}-ellipsis`]: enableEllipsis,
|
||
|
[`${prefixCls}-single-line`]: mergedEnableEllipsis && rows === 1,
|
||
|
[`${prefixCls}-ellipsis-single-line`]: cssTextOverflow,
|
||
|
[`${prefixCls}-ellipsis-multiple-line`]: cssLineClamp
|
||
|
}, className),
|
||
|
prefixCls: customizePrefixCls,
|
||
|
style: Object.assign(Object.assign({}, style), {
|
||
|
WebkitLineClamp: cssLineClamp ? rows : undefined
|
||
|
}),
|
||
|
component: component,
|
||
|
ref: (0, _ref3.composeRef)(resizeRef, typographyRef, ref),
|
||
|
direction: direction,
|
||
|
onClick: triggerType.includes('text') ? onEditClick : undefined,
|
||
|
"aria-label": topAriaLabel === null || topAriaLabel === void 0 ? void 0 : topAriaLabel.toString(),
|
||
|
title: title
|
||
|
}, textProps), /*#__PURE__*/React.createElement(_Ellipsis.default, {
|
||
|
enabledMeasure: mergedEnableEllipsis && !cssEllipsis,
|
||
|
text: children,
|
||
|
rows: rows,
|
||
|
width: ellipsisWidth,
|
||
|
fontSize: ellipsisFontSize,
|
||
|
onEllipsis: onJsEllipsis
|
||
|
}, (node, needEllipsis) => {
|
||
|
let renderNode = node;
|
||
|
if (node.length && needEllipsis && topAriaLabel) {
|
||
|
renderNode = /*#__PURE__*/React.createElement("span", {
|
||
|
key: "show-content",
|
||
|
"aria-hidden": true
|
||
|
}, renderNode);
|
||
|
}
|
||
|
const wrappedContext = wrapperDecorations(props, /*#__PURE__*/React.createElement(React.Fragment, null, renderNode, renderEllipsis(needEllipsis)));
|
||
|
return wrappedContext;
|
||
|
}))));
|
||
|
});
|
||
|
var _default = exports.default = Base;
|