Base.js 19 KB

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