Base.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.default = exports.baseProps = void 0;
  7. var _vue = require("vue");
  8. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
  9. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  10. var _LocaleReceiver = _interopRequireDefault(require("../locale-provider/LocaleReceiver"));
  11. var _warning = _interopRequireDefault(require("../_util/warning"));
  12. var _transButton = _interopRequireDefault(require("../_util/transButton"));
  13. var _raf = _interopRequireDefault(require("../_util/raf"));
  14. var _styleChecker = require("../_util/styleChecker");
  15. var _Editable = _interopRequireDefault(require("./Editable"));
  16. var _util = _interopRequireDefault(require("./util"));
  17. var _Typography = _interopRequireDefault(require("./Typography"));
  18. var _vcResizeObserver = _interopRequireDefault(require("../vc-resize-observer"));
  19. var _tooltip = _interopRequireDefault(require("../tooltip"));
  20. var _copyToClipboard = _interopRequireDefault(require("../_util/copy-to-clipboard"));
  21. var _CheckOutlined = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/CheckOutlined"));
  22. var _CopyOutlined = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/CopyOutlined"));
  23. var _EditOutlined = _interopRequireDefault(require("@ant-design/icons-vue/lib/icons/EditOutlined"));
  24. var _useConfigInject = _interopRequireDefault(require("../config-provider/hooks/useConfigInject"));
  25. var _omit = _interopRequireDefault(require("../_util/omit"));
  26. var _useMergedState = _interopRequireDefault(require("../_util/hooks/useMergedState"));
  27. var _propsUtil = require("../_util/props-util");
  28. var __rest = void 0 && (void 0).__rest || function (s, e) {
  29. var t = {};
  30. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  31. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  32. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  33. }
  34. return t;
  35. };
  36. const isLineClampSupport = (0, _styleChecker.isStyleSupport)('webkitLineClamp');
  37. const isTextOverflowSupport = (0, _styleChecker.isStyleSupport)('textOverflow');
  38. const ELLIPSIS_STR = '...';
  39. const baseProps = () => ({
  40. editable: {
  41. type: [Boolean, Object],
  42. default: undefined
  43. },
  44. copyable: {
  45. type: [Boolean, Object],
  46. default: undefined
  47. },
  48. prefixCls: String,
  49. component: String,
  50. type: String,
  51. disabled: {
  52. type: Boolean,
  53. default: undefined
  54. },
  55. ellipsis: {
  56. type: [Boolean, Object],
  57. default: undefined
  58. },
  59. code: {
  60. type: Boolean,
  61. default: undefined
  62. },
  63. mark: {
  64. type: Boolean,
  65. default: undefined
  66. },
  67. underline: {
  68. type: Boolean,
  69. default: undefined
  70. },
  71. delete: {
  72. type: Boolean,
  73. default: undefined
  74. },
  75. strong: {
  76. type: Boolean,
  77. default: undefined
  78. },
  79. keyboard: {
  80. type: Boolean,
  81. default: undefined
  82. },
  83. content: String,
  84. 'onUpdate:content': Function
  85. });
  86. exports.baseProps = baseProps;
  87. const Base = (0, _vue.defineComponent)({
  88. compatConfig: {
  89. MODE: 3
  90. },
  91. name: 'TypographyBase',
  92. inheritAttrs: false,
  93. props: baseProps(),
  94. // emits: ['update:content'],
  95. setup(props, _ref) {
  96. let {
  97. slots,
  98. attrs,
  99. emit
  100. } = _ref;
  101. const {
  102. prefixCls,
  103. direction
  104. } = (0, _useConfigInject.default)('typography', props);
  105. const state = (0, _vue.reactive)({
  106. copied: false,
  107. ellipsisText: '',
  108. ellipsisContent: null,
  109. isEllipsis: false,
  110. expanded: false,
  111. clientRendered: false,
  112. //locale
  113. expandStr: '',
  114. copyStr: '',
  115. copiedStr: '',
  116. editStr: '',
  117. copyId: undefined,
  118. rafId: undefined,
  119. prevProps: undefined,
  120. originContent: ''
  121. });
  122. const contentRef = (0, _vue.ref)();
  123. const editIcon = (0, _vue.ref)();
  124. const ellipsis = (0, _vue.computed)(() => {
  125. const ellipsis = props.ellipsis;
  126. if (!ellipsis) return {};
  127. return (0, _extends2.default)({
  128. rows: 1,
  129. expandable: false
  130. }, typeof ellipsis === 'object' ? ellipsis : null);
  131. });
  132. (0, _vue.onMounted)(() => {
  133. state.clientRendered = true;
  134. syncEllipsis();
  135. });
  136. (0, _vue.onBeforeUnmount)(() => {
  137. clearTimeout(state.copyId);
  138. _raf.default.cancel(state.rafId);
  139. });
  140. (0, _vue.watch)([() => ellipsis.value.rows, () => props.content], () => {
  141. (0, _vue.nextTick)(() => {
  142. resizeOnNextFrame();
  143. });
  144. }, {
  145. flush: 'post',
  146. deep: true
  147. });
  148. (0, _vue.watchEffect)(() => {
  149. if (props.content === undefined) {
  150. (0, _warning.default)(!props.editable, 'Typography', 'When `editable` is enabled, please use `content` instead of children');
  151. (0, _warning.default)(!props.ellipsis, 'Typography', 'When `ellipsis` is enabled, please use `content` instead of children');
  152. }
  153. });
  154. function getChildrenText() {
  155. var _a;
  156. return props.ellipsis || props.editable ? props.content : (_a = (0, _propsUtil.findDOMNode)(contentRef.value)) === null || _a === void 0 ? void 0 : _a.innerText;
  157. }
  158. // =============== Expand ===============
  159. function onExpandClick(e) {
  160. const {
  161. onExpand
  162. } = ellipsis.value;
  163. state.expanded = true;
  164. onExpand === null || onExpand === void 0 ? void 0 : onExpand(e);
  165. }
  166. // ================ Edit ================
  167. function onEditClick(e) {
  168. e.preventDefault();
  169. state.originContent = props.content;
  170. triggerEdit(true);
  171. }
  172. function onEditChange(value) {
  173. onContentChange(value);
  174. triggerEdit(false);
  175. }
  176. function onContentChange(value) {
  177. const {
  178. onChange
  179. } = editable.value;
  180. if (value !== props.content) {
  181. emit('update:content', value);
  182. onChange === null || onChange === void 0 ? void 0 : onChange(value);
  183. }
  184. }
  185. function onEditCancel() {
  186. var _a, _b;
  187. (_b = (_a = editable.value).onCancel) === null || _b === void 0 ? void 0 : _b.call(_a);
  188. triggerEdit(false);
  189. }
  190. // ================ Copy ================
  191. function onCopyClick(e) {
  192. e.preventDefault();
  193. e.stopPropagation();
  194. const {
  195. copyable
  196. } = props;
  197. const copyConfig = (0, _extends2.default)({}, typeof copyable === 'object' ? copyable : null);
  198. if (copyConfig.text === undefined) {
  199. copyConfig.text = getChildrenText();
  200. }
  201. (0, _copyToClipboard.default)(copyConfig.text || '');
  202. state.copied = true;
  203. (0, _vue.nextTick)(() => {
  204. if (copyConfig.onCopy) {
  205. copyConfig.onCopy(e);
  206. }
  207. state.copyId = setTimeout(() => {
  208. state.copied = false;
  209. }, 3000);
  210. });
  211. }
  212. const editable = (0, _vue.computed)(() => {
  213. const editable = props.editable;
  214. if (!editable) return {
  215. editing: false
  216. };
  217. return (0, _extends2.default)({}, typeof editable === 'object' ? editable : null);
  218. });
  219. const [editing, setEditing] = (0, _useMergedState.default)(false, {
  220. value: (0, _vue.computed)(() => {
  221. return editable.value.editing;
  222. })
  223. });
  224. function triggerEdit(edit) {
  225. const {
  226. onStart
  227. } = editable.value;
  228. if (edit && onStart) {
  229. onStart();
  230. }
  231. setEditing(edit);
  232. }
  233. (0, _vue.watch)(editing, val => {
  234. var _a;
  235. if (!val) {
  236. (_a = editIcon.value) === null || _a === void 0 ? void 0 : _a.focus();
  237. }
  238. }, {
  239. flush: 'post'
  240. });
  241. // ============== Ellipsis ==============
  242. function resizeOnNextFrame(sizeInfo) {
  243. if (sizeInfo) {
  244. const {
  245. width,
  246. height
  247. } = sizeInfo;
  248. if (!width || !height) return;
  249. }
  250. _raf.default.cancel(state.rafId);
  251. state.rafId = (0, _raf.default)(() => {
  252. // Do not bind `syncEllipsis`. It need for test usage on prototype
  253. syncEllipsis();
  254. });
  255. }
  256. const canUseCSSEllipsis = (0, _vue.computed)(() => {
  257. const {
  258. rows,
  259. expandable,
  260. suffix,
  261. onEllipsis,
  262. tooltip
  263. } = ellipsis.value;
  264. if (suffix || tooltip) return false;
  265. // Can't use css ellipsis since we need to provide the place for button
  266. if (props.editable || props.copyable || expandable || onEllipsis) {
  267. return false;
  268. }
  269. if (rows === 1) {
  270. return isTextOverflowSupport;
  271. }
  272. return isLineClampSupport;
  273. });
  274. const syncEllipsis = () => {
  275. const {
  276. ellipsisText,
  277. isEllipsis
  278. } = state;
  279. const {
  280. rows,
  281. suffix,
  282. onEllipsis
  283. } = ellipsis.value;
  284. if (!rows || rows < 0 || !(0, _propsUtil.findDOMNode)(contentRef.value) || state.expanded || props.content === undefined) return;
  285. // Do not measure if css already support ellipsis
  286. if (canUseCSSEllipsis.value) return;
  287. const {
  288. content,
  289. text,
  290. ellipsis: ell
  291. } = (0, _util.default)((0, _propsUtil.findDOMNode)(contentRef.value), {
  292. rows,
  293. suffix
  294. }, props.content, renderOperations(true), ELLIPSIS_STR);
  295. if (ellipsisText !== text || state.isEllipsis !== ell) {
  296. state.ellipsisText = text;
  297. state.ellipsisContent = content;
  298. state.isEllipsis = ell;
  299. if (isEllipsis !== ell && onEllipsis) {
  300. onEllipsis(ell);
  301. }
  302. }
  303. };
  304. function wrapperDecorations(_ref2, content) {
  305. let {
  306. mark,
  307. code,
  308. underline,
  309. delete: del,
  310. strong,
  311. keyboard
  312. } = _ref2;
  313. let currentContent = content;
  314. function wrap(needed, Tag) {
  315. if (!needed) return;
  316. const _currentContent = function () {
  317. return currentContent;
  318. }();
  319. currentContent = (0, _vue.createVNode)(Tag, null, {
  320. default: () => [_currentContent]
  321. });
  322. }
  323. wrap(strong, 'strong');
  324. wrap(underline, 'u');
  325. wrap(del, 'del');
  326. wrap(code, 'code');
  327. wrap(mark, 'mark');
  328. wrap(keyboard, 'kbd');
  329. return currentContent;
  330. }
  331. function renderExpand(forceRender) {
  332. const {
  333. expandable,
  334. symbol
  335. } = ellipsis.value;
  336. if (!expandable) return null;
  337. // force render expand icon for measure usage or it will cause dead loop
  338. if (!forceRender && (state.expanded || !state.isEllipsis)) return null;
  339. const expandContent = (slots.ellipsisSymbol ? slots.ellipsisSymbol() : symbol) || state.expandStr;
  340. return (0, _vue.createVNode)("a", {
  341. "key": "expand",
  342. "class": `${prefixCls.value}-expand`,
  343. "onClick": onExpandClick,
  344. "aria-label": state.expandStr
  345. }, [expandContent]);
  346. }
  347. function renderEdit() {
  348. if (!props.editable) return;
  349. const {
  350. tooltip,
  351. triggerType = ['icon']
  352. } = props.editable;
  353. const icon = slots.editableIcon ? slots.editableIcon() : (0, _vue.createVNode)(_EditOutlined.default, {
  354. "role": "button"
  355. }, null);
  356. const title = slots.editableTooltip ? slots.editableTooltip() : state.editStr;
  357. const ariaLabel = typeof title === 'string' ? title : '';
  358. return triggerType.indexOf('icon') !== -1 ? (0, _vue.createVNode)(_tooltip.default, {
  359. "key": "edit",
  360. "title": tooltip === false ? '' : title
  361. }, {
  362. default: () => [(0, _vue.createVNode)(_transButton.default, {
  363. "ref": editIcon,
  364. "class": `${prefixCls.value}-edit`,
  365. "onClick": onEditClick,
  366. "aria-label": ariaLabel
  367. }, {
  368. default: () => [icon]
  369. })]
  370. }) : null;
  371. }
  372. function renderCopy() {
  373. if (!props.copyable) return;
  374. const {
  375. tooltip
  376. } = props.copyable;
  377. const defaultTitle = state.copied ? state.copiedStr : state.copyStr;
  378. const title = slots.copyableTooltip ? slots.copyableTooltip({
  379. copied: state.copied
  380. }) : defaultTitle;
  381. const ariaLabel = typeof title === 'string' ? title : '';
  382. const defaultIcon = state.copied ? (0, _vue.createVNode)(_CheckOutlined.default, null, null) : (0, _vue.createVNode)(_CopyOutlined.default, null, null);
  383. const icon = slots.copyableIcon ? slots.copyableIcon({
  384. copied: !!state.copied
  385. }) : defaultIcon;
  386. return (0, _vue.createVNode)(_tooltip.default, {
  387. "key": "copy",
  388. "title": tooltip === false ? '' : title
  389. }, {
  390. default: () => [(0, _vue.createVNode)(_transButton.default, {
  391. "class": [`${prefixCls.value}-copy`, {
  392. [`${prefixCls.value}-copy-success`]: state.copied
  393. }],
  394. "onClick": onCopyClick,
  395. "aria-label": ariaLabel
  396. }, {
  397. default: () => [icon]
  398. })]
  399. });
  400. }
  401. function renderEditInput() {
  402. const {
  403. class: className,
  404. style
  405. } = attrs;
  406. const {
  407. maxlength,
  408. autoSize,
  409. onEnd
  410. } = editable.value;
  411. return (0, _vue.createVNode)(_Editable.default, {
  412. "class": className,
  413. "style": style,
  414. "prefixCls": prefixCls.value,
  415. "value": props.content,
  416. "originContent": state.originContent,
  417. "maxlength": maxlength,
  418. "autoSize": autoSize,
  419. "onSave": onEditChange,
  420. "onChange": onContentChange,
  421. "onCancel": onEditCancel,
  422. "onEnd": onEnd,
  423. "direction": direction.value,
  424. "component": props.component
  425. }, {
  426. enterIcon: slots.editableEnterIcon
  427. });
  428. }
  429. function renderOperations(forceRenderExpanded) {
  430. return [renderExpand(forceRenderExpanded), renderEdit(), renderCopy()].filter(node => node);
  431. }
  432. return () => {
  433. var _a;
  434. const {
  435. triggerType = ['icon']
  436. } = editable.value;
  437. const children = props.ellipsis || props.editable ? props.content !== undefined ? props.content : (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots) : slots.default ? slots.default() : props.content;
  438. if (editing.value) {
  439. return renderEditInput();
  440. }
  441. return (0, _vue.createVNode)(_LocaleReceiver.default, {
  442. "componentName": "Text",
  443. "children": locale => {
  444. const _a = (0, _extends2.default)((0, _extends2.default)({}, props), attrs),
  445. {
  446. type,
  447. disabled,
  448. content,
  449. class: className,
  450. style
  451. } = _a,
  452. restProps = __rest(_a, ["type", "disabled", "content", "class", "style"]);
  453. const {
  454. rows,
  455. suffix,
  456. tooltip
  457. } = ellipsis.value;
  458. const {
  459. edit,
  460. copy: copyStr,
  461. copied,
  462. expand
  463. } = locale;
  464. state.editStr = edit;
  465. state.copyStr = copyStr;
  466. state.copiedStr = copied;
  467. state.expandStr = expand;
  468. const textProps = (0, _omit.default)(restProps, ['prefixCls', 'editable', 'copyable', 'ellipsis', 'mark', 'code', 'delete', 'underline', 'strong', 'keyboard', 'onUpdate:content']);
  469. const cssEllipsis = canUseCSSEllipsis.value;
  470. const cssTextOverflow = rows === 1 && cssEllipsis;
  471. const cssLineClamp = rows && rows > 1 && cssEllipsis;
  472. let textNode = children;
  473. let ariaLabel;
  474. // Only use js ellipsis when css ellipsis not support
  475. if (rows && state.isEllipsis && !state.expanded && !cssEllipsis) {
  476. const {
  477. title
  478. } = restProps;
  479. let restContent = title || '';
  480. if (!title && (typeof children === 'string' || typeof children === 'number')) {
  481. restContent = String(children);
  482. }
  483. // show rest content as title on symbol
  484. restContent = restContent === null || restContent === void 0 ? void 0 : restContent.slice(String(state.ellipsisContent || '').length);
  485. // We move full content to outer element to avoid repeat read the content by accessibility
  486. textNode = (0, _vue.createVNode)(_vue.Fragment, null, [(0, _vue.toRaw)(state.ellipsisContent), (0, _vue.createVNode)("span", {
  487. "title": restContent,
  488. "aria-hidden": "true"
  489. }, [ELLIPSIS_STR]), suffix]);
  490. } else {
  491. textNode = (0, _vue.createVNode)(_vue.Fragment, null, [children, suffix]);
  492. }
  493. textNode = wrapperDecorations(props, textNode);
  494. const showTooltip = tooltip && rows && state.isEllipsis && !state.expanded && !cssEllipsis;
  495. const title = slots.ellipsisTooltip ? slots.ellipsisTooltip() : tooltip;
  496. return (0, _vue.createVNode)(_vcResizeObserver.default, {
  497. "onResize": resizeOnNextFrame,
  498. "disabled": !rows
  499. }, {
  500. default: () => [(0, _vue.createVNode)(_Typography.default, (0, _objectSpread2.default)({
  501. "ref": contentRef,
  502. "class": [{
  503. [`${prefixCls.value}-${type}`]: type,
  504. [`${prefixCls.value}-disabled`]: disabled,
  505. [`${prefixCls.value}-ellipsis`]: rows,
  506. [`${prefixCls.value}-single-line`]: rows === 1 && !state.isEllipsis,
  507. [`${prefixCls.value}-ellipsis-single-line`]: cssTextOverflow,
  508. [`${prefixCls.value}-ellipsis-multiple-line`]: cssLineClamp
  509. }, className],
  510. "style": (0, _extends2.default)((0, _extends2.default)({}, style), {
  511. WebkitLineClamp: cssLineClamp ? rows : undefined
  512. }),
  513. "aria-label": ariaLabel,
  514. "direction": direction.value,
  515. "onClick": triggerType.indexOf('text') !== -1 ? onEditClick : () => {}
  516. }, textProps), {
  517. default: () => [showTooltip ? (0, _vue.createVNode)(_tooltip.default, {
  518. "title": tooltip === true ? children : title
  519. }, {
  520. default: () => [(0, _vue.createVNode)("span", null, [textNode])]
  521. }) : textNode, renderOperations()]
  522. })]
  523. });
  524. }
  525. }, null);
  526. };
  527. }
  528. });
  529. var _default = exports.default = Base;