ant-design-vue
Version:
An enterprise-class UI design language and Vue-based implementation
499 lines • 19.3 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _typeof from "@babel/runtime/helpers/esm/typeof";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
var _excluded = ["type", "disabled", "content", "class", "style"];
import { Fragment as _Fragment, resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue";
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import warning from '../_util/warning';
import TransButton from '../_util/transButton';
import raf from '../_util/raf';
import { isStyleSupport } from '../_util/styleChecker';
import Editable from './Editable';
import measure from './util';
import Typography from './Typography';
import ResizeObserver from '../vc-resize-observer';
import Tooltip from '../tooltip';
import copy from '../_util/copy-to-clipboard';
import CheckOutlined from "@ant-design/icons-vue/es/icons/CheckOutlined";
import CopyOutlined from "@ant-design/icons-vue/es/icons/CopyOutlined";
import EditOutlined from "@ant-design/icons-vue/es/icons/EditOutlined";
import { defineComponent, reactive, ref, onMounted, onBeforeUnmount, watch, watchEffect, nextTick, computed, toRaw } from 'vue';
import useConfigInject from '../_util/hooks/useConfigInject';
import omit from '../_util/omit';
import useMergedState from '../_util/hooks/useMergedState';
var isLineClampSupport = isStyleSupport('webkitLineClamp');
var isTextOverflowSupport = isStyleSupport('textOverflow');
var ELLIPSIS_STR = '...';
export var baseProps = function baseProps() {
return {
editable: {
type: [Boolean, Object],
default: undefined
},
copyable: {
type: [Boolean, Object],
default: undefined
},
prefixCls: String,
component: String,
type: String,
disabled: {
type: Boolean,
default: undefined
},
ellipsis: {
type: [Boolean, Object],
default: undefined
},
code: {
type: Boolean,
default: undefined
},
mark: {
type: Boolean,
default: undefined
},
underline: {
type: Boolean,
default: undefined
},
delete: {
type: Boolean,
default: undefined
},
strong: {
type: Boolean,
default: undefined
},
keyboard: {
type: Boolean,
default: undefined
},
content: String,
'onUpdate:content': Function
};
};
var Base = defineComponent({
compatConfig: {
MODE: 3
},
name: 'Base',
inheritAttrs: false,
props: baseProps(),
// emits: ['update:content'],
setup: function setup(props, _ref) {
var slots = _ref.slots,
attrs = _ref.attrs,
emit = _ref.emit;
var _useConfigInject = useConfigInject('typography', props),
prefixCls = _useConfigInject.prefixCls,
direction = _useConfigInject.direction;
var state = reactive({
copied: false,
ellipsisText: '',
ellipsisContent: null,
isEllipsis: false,
expanded: false,
clientRendered: false,
//locale
expandStr: '',
copyStr: '',
copiedStr: '',
editStr: '',
copyId: undefined,
rafId: undefined,
prevProps: undefined,
originContent: ''
});
var contentRef = ref();
var editIcon = ref();
var ellipsis = computed(function () {
var ellipsis = props.ellipsis;
if (!ellipsis) return {};
return _objectSpread({
rows: 1,
expandable: false
}, _typeof(ellipsis) === 'object' ? ellipsis : null);
});
onMounted(function () {
state.clientRendered = true;
});
onBeforeUnmount(function () {
clearTimeout(state.copyId);
raf.cancel(state.rafId);
});
watch([function () {
return ellipsis.value.rows;
}, function () {
return props.content;
}], function () {
nextTick(function () {
resizeOnNextFrame();
});
}, {
flush: 'post',
deep: true,
immediate: true
});
watchEffect(function () {
if (props.content === undefined) {
warning(!props.editable, 'Typography', 'When `editable` is enabled, please use `content` instead of children');
warning(!props.ellipsis, 'Typography', 'When `ellipsis` is enabled, please use `content` instead of children');
}
});
function getChildrenText() {
var _contentRef$value, _contentRef$value$$el;
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;
}
// =============== Expand ===============
function onExpandClick(e) {
var onExpand = ellipsis.value.onExpand;
state.expanded = true;
onExpand === null || onExpand === void 0 ? void 0 : onExpand(e);
}
// ================ Edit ================
function onEditClick(e) {
e.preventDefault();
state.originContent = props.content;
triggerEdit(true);
}
function onEditChange(value) {
onContentChange(value);
triggerEdit(false);
}
function onContentChange(value) {
var onChange = editable.value.onChange;
if (value !== props.content) {
emit('update:content', value);
onChange === null || onChange === void 0 ? void 0 : onChange(value);
}
}
function onEditCancel() {
var _editable$value$onCan, _editable$value;
(_editable$value$onCan = (_editable$value = editable.value).onCancel) === null || _editable$value$onCan === void 0 ? void 0 : _editable$value$onCan.call(_editable$value);
triggerEdit(false);
}
// ================ Copy ================
function onCopyClick(e) {
e.preventDefault();
e.stopPropagation();
var copyable = props.copyable;
var copyConfig = _objectSpread({}, _typeof(copyable) === 'object' ? copyable : null);
if (copyConfig.text === undefined) {
copyConfig.text = getChildrenText();
}
copy(copyConfig.text || '');
state.copied = true;
nextTick(function () {
if (copyConfig.onCopy) {
copyConfig.onCopy();
}
state.copyId = setTimeout(function () {
state.copied = false;
}, 3000);
});
}
var editable = computed(function () {
var editable = props.editable;
if (!editable) return {
editing: false
};
return _objectSpread({}, _typeof(editable) === 'object' ? editable : null);
});
var _useMergedState = useMergedState(false, {
value: computed(function () {
return editable.value.editing;
})
}),
_useMergedState2 = _slicedToArray(_useMergedState, 2),
editing = _useMergedState2[0],
setEditing = _useMergedState2[1];
function triggerEdit(edit) {
var onStart = editable.value.onStart;
if (edit && onStart) {
onStart();
}
setEditing(edit);
}
watch(editing, function (val) {
if (!val) {
var _editIcon$value;
(_editIcon$value = editIcon.value) === null || _editIcon$value === void 0 ? void 0 : _editIcon$value.focus();
}
}, {
flush: 'post'
});
// ============== Ellipsis ==============
function resizeOnNextFrame() {
raf.cancel(state.rafId);
state.rafId = raf(function () {
// Do not bind `syncEllipsis`. It need for test usage on prototype
syncEllipsis();
});
}
var canUseCSSEllipsis = computed(function () {
var _ellipsis$value = ellipsis.value,
rows = _ellipsis$value.rows,
expandable = _ellipsis$value.expandable,
suffix = _ellipsis$value.suffix,
onEllipsis = _ellipsis$value.onEllipsis,
tooltip = _ellipsis$value.tooltip;
if (suffix || tooltip) return false;
// Can't use css ellipsis since we need to provide the place for button
if (props.editable || props.copyable || expandable || onEllipsis) {
return false;
}
if (rows === 1) {
return isTextOverflowSupport;
}
return isLineClampSupport;
});
var syncEllipsis = function syncEllipsis() {
var _contentRef$value2, _contentRef$value3;
var ellipsisText = state.ellipsisText,
isEllipsis = state.isEllipsis;
var _ellipsis$value2 = ellipsis.value,
rows = _ellipsis$value2.rows,
suffix = _ellipsis$value2.suffix,
onEllipsis = _ellipsis$value2.onEllipsis;
if (!rows || rows < 0 || !((_contentRef$value2 = contentRef.value) !== null && _contentRef$value2 !== void 0 && _contentRef$value2.$el) || state.expanded || props.content === undefined) return;
// Do not measure if css already support ellipsis
if (canUseCSSEllipsis.value) return;
var _measure = measure((_contentRef$value3 = contentRef.value) === null || _contentRef$value3 === void 0 ? void 0 : _contentRef$value3.$el, {
rows: rows,
suffix: suffix
}, props.content, renderOperations(true), ELLIPSIS_STR),
content = _measure.content,
text = _measure.text,
ell = _measure.ellipsis;
if (ellipsisText !== text || state.isEllipsis !== ell) {
state.ellipsisText = text;
state.ellipsisContent = content;
state.isEllipsis = ell;
if (isEllipsis !== ell && onEllipsis) {
onEllipsis(ell);
}
}
};
function wrapperDecorations(_ref2, content) {
var mark = _ref2.mark,
code = _ref2.code,
underline = _ref2.underline,
del = _ref2.delete,
strong = _ref2.strong,
keyboard = _ref2.keyboard;
var currentContent = content;
function wrap(needed, Tag) {
if (!needed) return;
var _currentContent = function () {
return currentContent;
}();
currentContent = _createVNode(Tag, null, {
default: function _default() {
return [_currentContent];
}
});
}
wrap(strong, 'strong');
wrap(underline, 'u');
wrap(del, 'del');
wrap(code, 'code');
wrap(mark, 'mark');
wrap(keyboard, 'kbd');
return currentContent;
}
function renderExpand(forceRender) {
var _ellipsis$value3 = ellipsis.value,
expandable = _ellipsis$value3.expandable,
symbol = _ellipsis$value3.symbol;
if (!expandable) return null;
// force render expand icon for measure usage or it will cause dead loop
if (!forceRender && (state.expanded || !state.isEllipsis)) return null;
var expandContent = (slots.ellipsisSymbol ? slots.ellipsisSymbol() : symbol) || state.expandStr;
return _createVNode("a", {
"key": "expand",
"class": "".concat(prefixCls.value, "-expand"),
"onClick": onExpandClick,
"aria-label": state.expandStr
}, [expandContent]);
}
function renderEdit() {
if (!props.editable) return;
var _props$editable = props.editable,
tooltip = _props$editable.tooltip,
_props$editable$trigg = _props$editable.triggerType,
triggerType = _props$editable$trigg === void 0 ? ['icon'] : _props$editable$trigg;
var icon = slots.editableIcon ? slots.editableIcon() : _createVNode(EditOutlined, {
"role": "button"
}, null);
var title = slots.editableTooltip ? slots.editableTooltip() : state.editStr;
var ariaLabel = typeof title === 'string' ? title : '';
return triggerType.indexOf('icon') !== -1 ? _createVNode(Tooltip, {
"key": "edit",
"title": tooltip === false ? '' : title
}, {
default: function _default() {
return [_createVNode(TransButton, {
"ref": editIcon,
"class": "".concat(prefixCls.value, "-edit"),
"onClick": onEditClick,
"aria-label": ariaLabel
}, {
default: function _default() {
return [icon];
}
})];
}
}) : null;
}
function renderCopy() {
if (!props.copyable) return;
var tooltip = props.copyable.tooltip;
var defaultTitle = state.copied ? state.copiedStr : state.copyStr;
var title = slots.copyableTooltip ? slots.copyableTooltip({
copied: state.copied
}) : defaultTitle;
var ariaLabel = typeof title === 'string' ? title : '';
var defaultIcon = state.copied ? _createVNode(CheckOutlined, null, null) : _createVNode(CopyOutlined, null, null);
var icon = slots.copyableIcon ? slots.copyableIcon({
copied: !!state.copied
}) : defaultIcon;
return _createVNode(Tooltip, {
"key": "copy",
"title": tooltip === false ? '' : title
}, {
default: function _default() {
return [_createVNode(TransButton, {
"class": ["".concat(prefixCls.value, "-copy"), _defineProperty({}, "".concat(prefixCls.value, "-copy-success"), state.copied)],
"onClick": onCopyClick,
"aria-label": ariaLabel
}, {
default: function _default() {
return [icon];
}
})];
}
});
}
function renderEditInput() {
var className = attrs.class,
style = attrs.style;
var _editable$value2 = editable.value,
maxlength = _editable$value2.maxlength,
autoSize = _editable$value2.autoSize,
onEnd = _editable$value2.onEnd;
return _createVNode(Editable, {
"class": className,
"style": style,
"prefixCls": prefixCls.value,
"value": props.content,
"originContent": state.originContent,
"maxlength": maxlength,
"autoSize": autoSize,
"onSave": onEditChange,
"onChange": onContentChange,
"onCancel": onEditCancel,
"onEnd": onEnd,
"direction": direction.value
}, {
enterIcon: slots.editableEnterIcon
});
}
function renderOperations(forceRenderExpanded) {
return [renderExpand(forceRenderExpanded), renderEdit(), renderCopy()].filter(function (node) {
return node;
});
}
return function () {
var _slots$default;
var _editable$value$trigg = editable.value.triggerType,
triggerType = _editable$value$trigg === void 0 ? ['icon'] : _editable$value$trigg;
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;
if (editing.value) {
return renderEditInput();
}
return _createVNode(LocaleReceiver, {
"componentName": "Text",
"children": function children(locale) {
var _ref4;
var _props$attrs = _objectSpread(_objectSpread({}, props), attrs),
type = _props$attrs.type,
disabled = _props$attrs.disabled,
content = _props$attrs.content,
className = _props$attrs.class,
style = _props$attrs.style,
restProps = _objectWithoutProperties(_props$attrs, _excluded);
var _ellipsis$value4 = ellipsis.value,
rows = _ellipsis$value4.rows,
suffix = _ellipsis$value4.suffix,
tooltip = _ellipsis$value4.tooltip;
var edit = locale.edit,
copyStr = locale.copy,
copied = locale.copied,
expand = locale.expand;
state.editStr = edit;
state.copyStr = copyStr;
state.copiedStr = copied;
state.expandStr = expand;
var textProps = omit(restProps, ['prefixCls', 'editable', 'copyable', 'ellipsis', 'mark', 'code', 'delete', 'underline', 'strong', 'keyboard', 'onUpdate:content']);
var cssEllipsis = canUseCSSEllipsis.value;
var cssTextOverflow = rows === 1 && cssEllipsis;
var cssLineClamp = rows && rows > 1 && cssEllipsis;
var textNode = _children;
var ariaLabel;
// Only use js ellipsis when css ellipsis not support
if (rows && state.isEllipsis && !state.expanded && !cssEllipsis) {
var _restContent;
var _title = restProps.title;
var restContent = _title || '';
if (!_title && (typeof _children === 'string' || typeof _children === 'number')) {
restContent = String(_children);
}
// show rest content as title on symbol
restContent = (_restContent = restContent) === null || _restContent === void 0 ? void 0 : _restContent.slice(String(state.ellipsisContent || '').length);
// We move full content to outer element to avoid repeat read the content by accessibility
textNode = _createVNode(_Fragment, null, [toRaw(state.ellipsisContent), _createVNode("span", {
"title": restContent,
"aria-hidden": "true"
}, [ELLIPSIS_STR]), suffix]);
} else {
textNode = _createVNode(_Fragment, null, [_children, suffix]);
}
textNode = wrapperDecorations(props, textNode);
var showTooltip = tooltip && rows && state.isEllipsis && !state.expanded && !cssEllipsis;
var title = slots.ellipsisTooltip ? slots.ellipsisTooltip() : tooltip;
return _createVNode(ResizeObserver, {
"onResize": resizeOnNextFrame,
"disabled": !rows
}, {
default: function _default() {
return [_createVNode(Typography, _objectSpread({
"ref": contentRef,
"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],
"style": _objectSpread(_objectSpread({}, style), {}, {
WebkitLineClamp: cssLineClamp ? rows : undefined
}),
"aria-label": ariaLabel,
"direction": direction.value,
"onClick": triggerType.indexOf('text') !== -1 ? onEditClick : function () {}
}, textProps), {
default: function _default() {
return [showTooltip ? _createVNode(Tooltip, {
"title": tooltip === true ? _children : title
}, {
default: function _default() {
return [_createVNode("span", null, [textNode])];
}
}) : textNode, renderOperations()];
}
})];
}
});
}
}, null);
};
}
});
export default Base;