index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. "use strict";
  2. "use client";
  3. var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
  4. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
  5. Object.defineProperty(exports, "__esModule", {
  6. value: true
  7. });
  8. exports.default = void 0;
  9. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  10. var React = _interopRequireWildcard(require("react"));
  11. var _EditOutlined = _interopRequireDefault(require("@ant-design/icons/EditOutlined"));
  12. var _classnames = _interopRequireDefault(require("classnames"));
  13. var _rcResizeObserver = _interopRequireDefault(require("rc-resize-observer"));
  14. var _toArray = _interopRequireDefault(require("rc-util/lib/Children/toArray"));
  15. var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
  16. var _useMergedState = _interopRequireDefault(require("rc-util/lib/hooks/useMergedState"));
  17. var _omit = _interopRequireDefault(require("rc-util/lib/omit"));
  18. var _ref = require("rc-util/lib/ref");
  19. var _styleChecker = require("../../_util/styleChecker");
  20. var _configProvider = require("../../config-provider");
  21. var _useLocale = _interopRequireDefault(require("../../locale/useLocale"));
  22. var _tooltip = _interopRequireDefault(require("../../tooltip"));
  23. var _Editable = _interopRequireDefault(require("../Editable"));
  24. var _useCopyClick = _interopRequireDefault(require("../hooks/useCopyClick"));
  25. var _useMergedConfig = _interopRequireDefault(require("../hooks/useMergedConfig"));
  26. var _usePrevious = _interopRequireDefault(require("../hooks/usePrevious"));
  27. var _useTooltipProps = _interopRequireDefault(require("../hooks/useTooltipProps"));
  28. var _Typography = _interopRequireDefault(require("../Typography"));
  29. var _CopyBtn = _interopRequireDefault(require("./CopyBtn"));
  30. var _Ellipsis = _interopRequireDefault(require("./Ellipsis"));
  31. var _EllipsisTooltip = _interopRequireDefault(require("./EllipsisTooltip"));
  32. var _util = require("./util");
  33. var __rest = void 0 && (void 0).__rest || function (s, e) {
  34. var t = {};
  35. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  36. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  37. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  38. }
  39. return t;
  40. };
  41. function wrapperDecorations({
  42. mark,
  43. code,
  44. underline,
  45. delete: del,
  46. strong,
  47. keyboard,
  48. italic
  49. }, content) {
  50. let currentContent = content;
  51. function wrap(tag, needed) {
  52. if (!needed) {
  53. return;
  54. }
  55. currentContent = /*#__PURE__*/React.createElement(tag, {}, currentContent);
  56. }
  57. wrap('strong', strong);
  58. wrap('u', underline);
  59. wrap('del', del);
  60. wrap('code', code);
  61. wrap('mark', mark);
  62. wrap('kbd', keyboard);
  63. wrap('i', italic);
  64. return currentContent;
  65. }
  66. const ELLIPSIS_STR = '...';
  67. const DECORATION_PROPS = ['delete', 'mark', 'code', 'underline', 'strong', 'keyboard', 'italic'];
  68. const Base = /*#__PURE__*/React.forwardRef((props, ref) => {
  69. var _a;
  70. const {
  71. prefixCls: customizePrefixCls,
  72. className,
  73. style,
  74. type,
  75. disabled,
  76. children,
  77. ellipsis,
  78. editable,
  79. copyable,
  80. component,
  81. title
  82. } = props,
  83. restProps = __rest(props, ["prefixCls", "className", "style", "type", "disabled", "children", "ellipsis", "editable", "copyable", "component", "title"]);
  84. const {
  85. getPrefixCls,
  86. direction
  87. } = React.useContext(_configProvider.ConfigContext);
  88. const [textLocale] = (0, _useLocale.default)('Text');
  89. const typographyRef = React.useRef(null);
  90. const editIconRef = React.useRef(null);
  91. // ============================ MISC ============================
  92. const prefixCls = getPrefixCls('typography', customizePrefixCls);
  93. const textProps = (0, _omit.default)(restProps, DECORATION_PROPS);
  94. // ========================== Editable ==========================
  95. const [enableEdit, editConfig] = (0, _useMergedConfig.default)(editable);
  96. const [editing, setEditing] = (0, _useMergedState.default)(false, {
  97. value: editConfig.editing
  98. });
  99. const {
  100. triggerType = ['icon']
  101. } = editConfig;
  102. const triggerEdit = edit => {
  103. var _a;
  104. if (edit) {
  105. (_a = editConfig.onStart) === null || _a === void 0 ? void 0 : _a.call(editConfig);
  106. }
  107. setEditing(edit);
  108. };
  109. // Focus edit icon when back
  110. const prevEditing = (0, _usePrevious.default)(editing);
  111. (0, _useLayoutEffect.default)(() => {
  112. var _a;
  113. if (!editing && prevEditing) {
  114. (_a = editIconRef.current) === null || _a === void 0 ? void 0 : _a.focus();
  115. }
  116. }, [editing]);
  117. const onEditClick = e => {
  118. e === null || e === void 0 ? void 0 : e.preventDefault();
  119. triggerEdit(true);
  120. };
  121. const onEditChange = value => {
  122. var _a;
  123. (_a = editConfig.onChange) === null || _a === void 0 ? void 0 : _a.call(editConfig, value);
  124. triggerEdit(false);
  125. };
  126. const onEditCancel = () => {
  127. var _a;
  128. (_a = editConfig.onCancel) === null || _a === void 0 ? void 0 : _a.call(editConfig);
  129. triggerEdit(false);
  130. };
  131. // ========================== Copyable ==========================
  132. const [enableCopy, copyConfig] = (0, _useMergedConfig.default)(copyable);
  133. const {
  134. copied,
  135. copyLoading,
  136. onClick: onCopyClick
  137. } = (0, _useCopyClick.default)({
  138. copyConfig,
  139. children
  140. });
  141. // ========================== Ellipsis ==========================
  142. const [isLineClampSupport, setIsLineClampSupport] = React.useState(false);
  143. const [isTextOverflowSupport, setIsTextOverflowSupport] = React.useState(false);
  144. const [isJsEllipsis, setIsJsEllipsis] = React.useState(false);
  145. const [isNativeEllipsis, setIsNativeEllipsis] = React.useState(false);
  146. const [isNativeVisible, setIsNativeVisible] = React.useState(true);
  147. const [enableEllipsis, ellipsisConfig] = (0, _useMergedConfig.default)(ellipsis, {
  148. expandable: false,
  149. symbol: isExpanded => isExpanded ? textLocale === null || textLocale === void 0 ? void 0 : textLocale.collapse : textLocale === null || textLocale === void 0 ? void 0 : textLocale.expand
  150. });
  151. const [expanded, setExpanded] = (0, _useMergedState.default)(ellipsisConfig.defaultExpanded || false, {
  152. value: ellipsisConfig.expanded
  153. });
  154. const mergedEnableEllipsis = enableEllipsis && (!expanded || ellipsisConfig.expandable === 'collapsible');
  155. // Shared prop to reduce bundle size
  156. const {
  157. rows = 1
  158. } = ellipsisConfig;
  159. const needMeasureEllipsis = React.useMemo(() =>
  160. // Disable ellipsis
  161. mergedEnableEllipsis && (
  162. // Provide suffix
  163. ellipsisConfig.suffix !== undefined || ellipsisConfig.onEllipsis ||
  164. // Can't use css ellipsis since we need to provide the place for button
  165. ellipsisConfig.expandable || enableEdit || enableCopy), [mergedEnableEllipsis, ellipsisConfig, enableEdit, enableCopy]);
  166. (0, _useLayoutEffect.default)(() => {
  167. if (enableEllipsis && !needMeasureEllipsis) {
  168. setIsLineClampSupport((0, _styleChecker.isStyleSupport)('webkitLineClamp'));
  169. setIsTextOverflowSupport((0, _styleChecker.isStyleSupport)('textOverflow'));
  170. }
  171. }, [needMeasureEllipsis, enableEllipsis]);
  172. const [cssEllipsis, setCssEllipsis] = React.useState(mergedEnableEllipsis);
  173. const canUseCssEllipsis = React.useMemo(() => {
  174. if (needMeasureEllipsis) {
  175. return false;
  176. }
  177. if (rows === 1) {
  178. return isTextOverflowSupport;
  179. }
  180. return isLineClampSupport;
  181. }, [needMeasureEllipsis, isTextOverflowSupport, isLineClampSupport]);
  182. // We use effect to change from css ellipsis to js ellipsis.
  183. // To make SSR still can see the ellipsis.
  184. (0, _useLayoutEffect.default)(() => {
  185. setCssEllipsis(canUseCssEllipsis && mergedEnableEllipsis);
  186. }, [canUseCssEllipsis, mergedEnableEllipsis]);
  187. const isMergedEllipsis = mergedEnableEllipsis && (cssEllipsis ? isNativeEllipsis : isJsEllipsis);
  188. const cssTextOverflow = mergedEnableEllipsis && rows === 1 && cssEllipsis;
  189. const cssLineClamp = mergedEnableEllipsis && rows > 1 && cssEllipsis;
  190. // >>>>> Expand
  191. const onExpandClick = (e, info) => {
  192. var _a;
  193. setExpanded(info.expanded);
  194. (_a = ellipsisConfig.onExpand) === null || _a === void 0 ? void 0 : _a.call(ellipsisConfig, e, info);
  195. };
  196. const [ellipsisWidth, setEllipsisWidth] = React.useState(0);
  197. const onResize = ({
  198. offsetWidth
  199. }) => {
  200. setEllipsisWidth(offsetWidth);
  201. };
  202. // >>>>> JS Ellipsis
  203. const onJsEllipsis = jsEllipsis => {
  204. var _a;
  205. setIsJsEllipsis(jsEllipsis);
  206. // Trigger if changed
  207. if (isJsEllipsis !== jsEllipsis) {
  208. (_a = ellipsisConfig.onEllipsis) === null || _a === void 0 ? void 0 : _a.call(ellipsisConfig, jsEllipsis);
  209. }
  210. };
  211. // >>>>> Native ellipsis
  212. React.useEffect(() => {
  213. const textEle = typographyRef.current;
  214. if (enableEllipsis && cssEllipsis && textEle) {
  215. const currentEllipsis = (0, _util.isEleEllipsis)(textEle);
  216. if (isNativeEllipsis !== currentEllipsis) {
  217. setIsNativeEllipsis(currentEllipsis);
  218. }
  219. }
  220. }, [enableEllipsis, cssEllipsis, children, cssLineClamp, isNativeVisible, ellipsisWidth]);
  221. // https://github.com/ant-design/ant-design/issues/36786
  222. // Use IntersectionObserver to check if element is invisible
  223. React.useEffect(() => {
  224. const textEle = typographyRef.current;
  225. if (typeof IntersectionObserver === 'undefined' || !textEle || !cssEllipsis || !mergedEnableEllipsis) {
  226. return;
  227. }
  228. /* eslint-disable-next-line compat/compat */
  229. const observer = new IntersectionObserver(() => {
  230. setIsNativeVisible(!!textEle.offsetParent);
  231. });
  232. observer.observe(textEle);
  233. return () => {
  234. observer.disconnect();
  235. };
  236. }, [cssEllipsis, mergedEnableEllipsis]);
  237. // ========================== Tooltip ===========================
  238. const tooltipProps = (0, _useTooltipProps.default)(ellipsisConfig.tooltip, editConfig.text, children);
  239. const topAriaLabel = React.useMemo(() => {
  240. if (!enableEllipsis || cssEllipsis) {
  241. return undefined;
  242. }
  243. return [editConfig.text, children, title, tooltipProps.title].find(_util.isValidText);
  244. }, [enableEllipsis, cssEllipsis, title, tooltipProps.title, isMergedEllipsis]);
  245. // =========================== Render ===========================
  246. // >>>>>>>>>>> Editing input
  247. if (editing) {
  248. return /*#__PURE__*/React.createElement(_Editable.default, {
  249. value: (_a = editConfig.text) !== null && _a !== void 0 ? _a : typeof children === 'string' ? children : '',
  250. onSave: onEditChange,
  251. onCancel: onEditCancel,
  252. onEnd: editConfig.onEnd,
  253. prefixCls: prefixCls,
  254. className: className,
  255. style: style,
  256. direction: direction,
  257. component: component,
  258. maxLength: editConfig.maxLength,
  259. autoSize: editConfig.autoSize,
  260. enterIcon: editConfig.enterIcon
  261. });
  262. }
  263. // >>>>>>>>>>> Typography
  264. // Expand
  265. const renderExpand = () => {
  266. const {
  267. expandable,
  268. symbol
  269. } = ellipsisConfig;
  270. return expandable ? (/*#__PURE__*/React.createElement("button", {
  271. type: "button",
  272. key: "expand",
  273. className: `${prefixCls}-${expanded ? 'collapse' : 'expand'}`,
  274. onClick: e => onExpandClick(e, {
  275. expanded: !expanded
  276. }),
  277. "aria-label": expanded ? textLocale.collapse : textLocale === null || textLocale === void 0 ? void 0 : textLocale.expand
  278. }, typeof symbol === 'function' ? symbol(expanded) : symbol)) : null;
  279. };
  280. // Edit
  281. const renderEdit = () => {
  282. if (!enableEdit) {
  283. return;
  284. }
  285. const {
  286. icon,
  287. tooltip,
  288. tabIndex
  289. } = editConfig;
  290. const editTitle = (0, _toArray.default)(tooltip)[0] || (textLocale === null || textLocale === void 0 ? void 0 : textLocale.edit);
  291. const ariaLabel = typeof editTitle === 'string' ? editTitle : '';
  292. return triggerType.includes('icon') ? (/*#__PURE__*/React.createElement(_tooltip.default, {
  293. key: "edit",
  294. title: tooltip === false ? '' : editTitle
  295. }, /*#__PURE__*/React.createElement("button", {
  296. type: "button",
  297. ref: editIconRef,
  298. className: `${prefixCls}-edit`,
  299. onClick: onEditClick,
  300. "aria-label": ariaLabel,
  301. tabIndex: tabIndex
  302. }, icon || /*#__PURE__*/React.createElement(_EditOutlined.default, {
  303. role: "button"
  304. })))) : null;
  305. };
  306. // Copy
  307. const renderCopy = () => {
  308. if (!enableCopy) {
  309. return null;
  310. }
  311. return /*#__PURE__*/React.createElement(_CopyBtn.default, Object.assign({
  312. key: "copy"
  313. }, copyConfig, {
  314. prefixCls: prefixCls,
  315. copied: copied,
  316. locale: textLocale,
  317. onCopy: onCopyClick,
  318. loading: copyLoading,
  319. iconOnly: children === null || children === undefined
  320. }));
  321. };
  322. const renderOperations = canEllipsis => [canEllipsis && renderExpand(), renderEdit(), renderCopy()];
  323. const renderEllipsis = canEllipsis => [canEllipsis && !expanded && (/*#__PURE__*/React.createElement("span", {
  324. "aria-hidden": true,
  325. key: "ellipsis"
  326. }, ELLIPSIS_STR)), ellipsisConfig.suffix, renderOperations(canEllipsis)];
  327. return /*#__PURE__*/React.createElement(_rcResizeObserver.default, {
  328. onResize: onResize,
  329. disabled: !mergedEnableEllipsis
  330. }, resizeRef => (/*#__PURE__*/React.createElement(_EllipsisTooltip.default, {
  331. tooltipProps: tooltipProps,
  332. enableEllipsis: mergedEnableEllipsis,
  333. isEllipsis: isMergedEllipsis
  334. }, /*#__PURE__*/React.createElement(_Typography.default, Object.assign({
  335. className: (0, _classnames.default)({
  336. [`${prefixCls}-${type}`]: type,
  337. [`${prefixCls}-disabled`]: disabled,
  338. [`${prefixCls}-ellipsis`]: enableEllipsis,
  339. [`${prefixCls}-ellipsis-single-line`]: cssTextOverflow,
  340. [`${prefixCls}-ellipsis-multiple-line`]: cssLineClamp
  341. }, className),
  342. prefixCls: customizePrefixCls,
  343. style: Object.assign(Object.assign({}, style), {
  344. WebkitLineClamp: cssLineClamp ? rows : undefined
  345. }),
  346. component: component,
  347. ref: (0, _ref.composeRef)(resizeRef, typographyRef, ref),
  348. direction: direction,
  349. onClick: triggerType.includes('text') ? onEditClick : undefined,
  350. "aria-label": topAriaLabel === null || topAriaLabel === void 0 ? void 0 : topAriaLabel.toString(),
  351. title: title
  352. }, textProps), /*#__PURE__*/React.createElement(_Ellipsis.default, {
  353. enableMeasure: mergedEnableEllipsis && !cssEllipsis,
  354. text: children,
  355. rows: rows,
  356. width: ellipsisWidth,
  357. onEllipsis: onJsEllipsis,
  358. expanded: expanded,
  359. miscDeps: [copied, expanded, copyLoading, enableEdit, enableCopy, textLocale].concat((0, _toConsumableArray2.default)(DECORATION_PROPS.map(key => props[key])))
  360. }, (node, canEllipsis) => wrapperDecorations(props, /*#__PURE__*/React.createElement(React.Fragment, null, node.length > 0 && canEllipsis && !expanded && topAriaLabel ? (/*#__PURE__*/React.createElement("span", {
  361. key: "show-content",
  362. "aria-hidden": true
  363. }, node)) : node, renderEllipsis(canEllipsis))))))));
  364. });
  365. var _default = exports.default = Base;