UNPKG

@opentiny/vue-renderless

Version:

An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.

557 lines (556 loc) 16.7 kB
import "../chunk-G2ADBYYC.js"; import { isServer, browserInfo } from "@opentiny/utils"; import { isNull } from "@opentiny/utils"; import { debounce } from "@opentiny/utils"; import { isEqual } from "@opentiny/utils"; import { addResizeListener } from "@opentiny/utils"; import { KEY_CODE, CASCADER } from "@opentiny/utils"; const initMigratingProps = () => ({ expandTrigger: { newProp: CASCADER.PropsExpandTri, type: String }, changeOnSelect: { newProp: CASCADER.PropsCheckStric, type: Boolean }, hoverThreshold: { newProp: CASCADER.PropsHover, type: Number } }); const kebabCase = (str) => { const hyphenateRE = /([^-])([A-Z])/g; return str.replace(hyphenateRE, "$1-$2").replace(hyphenateRE, "$1-$2").toLowerCase(); }; const getConfig = ({ parent, props }) => () => { const config = props.props || {}; const { $attrs } = parent; const migratingProps = initMigratingProps(); Object.keys(migratingProps).forEach((oldProp) => { const { newProp, type } = migratingProps[oldProp]; let oldValue = $attrs[oldProp] || $attrs[kebabCase(oldProp)]; if (isNull(config[newProp]) && !isNull(oldProp)) { if (oldValue === "" && type === Boolean) { oldValue = true; } config[newProp] = oldValue; } }); return config; }; const computClearVisible = ({ props, state }) => () => { if (!props.clearable || state.isDisabled || state.filtering || !state.inputHover) { return false; } return state.multiple ? !!state.checkedNodes.filter((node) => !node.isDisabled).length : !!state.presentText; }; const computePresentContent = ({ api, state }) => () => { if (state.config.multiple) { api.computePresentTags(); state.presentText = state.presentTags.length ? " " : null; } else { api.computePresentText(); } }; const watchValue = ({ api, state }) => (value) => { if (!isEqual(value, state.checkedValue)) { state.userChangeValue = true; state.checkedValue = value; setTimeout(api.computePresentContent); } }; const watchCheckedValue = ({ nextTick, constants, dispatch, api, emit, state, props }) => (value) => { state.presentText = state.presentTags.length ? "" : null; const { checkStrictly, multiple } = state.config; nextTick(() => { api.computePresentContent(); }); if (!multiple && !checkStrictly && state.dropDownVisible) { api.toggleDropDownVisible(false); } nextTick(() => { dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormChange, [ state.multiple ? state.presentText : state.inputValue ]); }); if (state.userChangeValue) { if (props.changeCompat) { emit("update:modelValue", value); emit("change", value); } state.userChangeValue = false; } else { emit("update:modelValue", value); emit("change", value); } setTimeout(api.updateStyle); }; const isEmpty = (val) => { if (isNull(val)) { return true; } if (typeof val === "boolean") { return false; } if (typeof val === "number") { return !val; } if (val instanceof Error) { return val.message === ""; } const type = Object.prototype.toString.call(val); if (~["[object String]", "[object Array]"].indexOf(type)) { return !val.length; } if (~["[object File]", "[object Map]", "[object Set]"].indexOf(type)) { return !val.size; } if (type === "[object Object]") { return !Object.keys(val).length; } return false; }; const selfMounted = ({ api, parent, props, vm, state }) => () => { const { input } = vm.$refs; const inputSizeMap = { medium: 36, small: 32, mini: 28 }; input && (input.$parent.popperElm = vm.$refs.popper); if (input && input.$el) { state.inputInitialHeight = input.$el.offsetHeight || inputSizeMap[state.realSize] || 30; } if (!isEmpty(props.modelValue)) { api.computePresentContent(); } api.filterHandler = debounce(props.debounce, () => { if (!state.inputValue) { state.filtering = false; return; } const before = props.beforeFilter(state.inputValue); if (before && before.then) { before.then(api.getSuggestions); } else if (before !== false) { api.getSuggestions(); } else { state.filtering = false; } }); addResizeListener(parent.$el, api.updateStyle); }; const toggleDropDownVisible = ({ emit, vm, state, updatePopper }) => (visible) => { if (state.isDisabled) { return; } const { input } = vm.$refs; visible = !isNull(visible) ? visible : !state.dropDownVisible; if (visible !== state.dropDownVisible) { state.dropDownVisible = visible; if (visible) { updatePopper(); state.panel.scrollIntoView(); } input && input.getInput && input.getInput().setAttribute("aria-expanded", visible); emit("visible-change", visible); } }; const handleDropdownLeave = (state) => () => { state.filtering = false; state.inputValue = state.presentText; }; const handleKeyDown = ({ api }) => (event) => { switch (event.keyCode) { case KEY_CODE.Enter: api.toggleDropDownVisible(); break; case KEY_CODE.ArrowDown: api.toggleDropDownVisible(true); api.focusFirstNode(); event.preventDefault(); break; case KEY_CODE.Escape: case KEY_CODE.Tab: api.toggleDropDownVisible(false); break; default: break; } }; const handleFocus = (emit) => (e) => { emit("focus", e); }; const handleBlur = ({ emit, api, props }) => (e) => { if (props.filterable) { api.computePresentContent(); } emit("blur", e); }; const handleInput = ({ api, state, vm }) => (val, event) => { event = event || window.event; if (!event) { return; } const reference = vm.$refs.reference; const key = "init-flag"; const value = "true"; const isIE = browserInfo.name === "ie"; if (isIE && reference.getAttribute(key) !== value && !event.target.value) { reference.setAttribute(key, value); return; } !state.dropDownVisible && api.toggleDropDownVisible(true); state.presentText = val; if (event && event.isComposing) { return; } if (val) { api.filterHandler(); } else { state.filtering = false; } }; const handleClear = (state) => (_event) => { state.presentText = ""; state.panel.clearCheckedNodes(); }; const handleExpandChange = ({ constants, dispatch, emit, nextTick, state, updatePopper }) => (value) => { nextTick(() => { dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormBlur, [ state.multiple ? state.presentText : state.inputValue ]); updatePopper(); }); emit("expand-change", value); emit("active-item-change", value); }; const focusFirstNode = ({ vm, state }) => () => { const { popper, suggestionPanel } = vm.$refs; let firstNode = null; if (state.filtering && suggestionPanel) { firstNode = suggestionPanel.$el.querySelector(CASCADER.SugItem); } else { const firstMenu = popper.querySelector(CASCADER.CascaderMenu); firstNode = firstMenu.querySelector(CASCADER.CascaderNodeTab); } if (firstNode) { firstNode.focus(); !state.filtering && firstNode.click(); } }; const computePresentText = ({ props, state }) => () => { if (!isServer) { if (!isEmpty(state.checkedValue)) { const node = state.panel.getNodeByValue(state.checkedValue); if (node && (state.config.checkStrictly || node.isLeaf)) { state.presentText = node.getText(props.showAllLevels, props.separator); return; } } state.inputValue = null; state.presentText = null; } }; const computePresentTags = ({ api, props, state }) => () => { const checkedNodes = api.getCheckedNodes(state.leafOnly); const tags = []; const genTag = (node) => { const text = node.getText(props.showAllLevels, props.separator); const closable = !state.isDisabled && !node.isDisabled; return { node, key: node.uid, text, hitState: false, closable }; }; if (checkedNodes.length) { const [first, ...rest] = checkedNodes; const restCount = rest.length; tags.push(genTag(first)); if (restCount) { if (props.collapseTags) { tags.push({ key: -1, text: `+ ${restCount}`, closable: false }); } else { rest.forEach((node) => tags.push(genTag(node))); } } } state.checkedNodes = checkedNodes; state.presentTags = tags; state.inputValue = null; state.presentText = null; if (props.hoverExpand) { api.calcCollapseTags(); } }; const calcCollapseTags = ({ state, vm, nextTick }) => () => { nextTick(() => { const content = vm.$refs["tags-content"]; if (state.inputHover || state.dropDownVisible) { return state.isHidden = true; } if (content) { const { width: contentWidth, paddingLeft, paddingRight } = content && window.getComputedStyle(content); const contentWidthTotal = content && parseFloat(contentWidth) - parseFloat(paddingLeft) - parseFloat(paddingRight); const tagsLength = content.querySelector(".tiny-cascader__tags-collapse"); const { width: collapseTagContentWidth, marginRight, marginLeft } = tagsLength && window.getComputedStyle(tagsLength); const collapseTagWidth = tagsLength && parseFloat(collapseTagContentWidth) + parseFloat(marginRight) + parseFloat(marginLeft); const tags = Array.from(content.querySelectorAll(".tiny-tag")); let { total, dom, idx } = { total: collapseTagWidth, dom: null, idx: 0 }; tags.forEach((tag, index) => { if (tag !== tagsLength) { const { width: tagContentWidth, marginRight: marginRight2, marginLeft: marginLeft2 } = tag && window.getComputedStyle(tag); total += parseFloat(tagContentWidth) + parseFloat(marginRight2) + parseFloat(marginLeft2); } if (tag !== tagsLength && total > contentWidthTotal && !dom) { dom = tag; idx = index; } }); let isOneLine = total - collapseTagWidth <= contentWidthTotal; if (isOneLine) { return state.isHidden = true; } if (dom) { dom.before(tagsLength); state.isHidden = false; } else { state.isHidden = true; } state.collapseTagsLength = tags.length - idx; } }); }; const watchInputHover = ({ vm }) => (newVal) => { const [inputHover, dropDownVisible] = newVal; if (!inputHover && !dropDownVisible) { const content = vm.$refs["tags-content"]; content && content.scrollTo({ top: 0, left: 0 }); } }; const handleMouseenter = ({ vm, state }) => ($event) => { const dom = $event.target; const textNode = dom && dom.querySelector("span"); const { tooltip } = vm.$refs; if (textNode && tooltip && textNode.scrollWidth > textNode.offsetWidth) { const text = textNode.textContent; tooltip.state.referenceElm = dom; tooltip.state.popperElm && (tooltip.state.popperElm.style.display = "none"); tooltip.doDestroy(); state.tooltipVisible = true; state.tooltipContent = text; setTimeout(tooltip.updatePopper, 20); } }; const handleMouseleave = ({ state }) => () => state.tooltipVisible = false; const getSuggestions = ({ nextTick, props, state, updatePopper }) => () => { let filterMethod = null; if (!(props.filterMethod && typeof props.filterMethod === "function")) { filterMethod = (node, keyword) => ~node.text.indexOf(keyword); } else { filterMethod = props.filterMethod; } const suggestions = state.panel.getFlattedNodes(state.leafOnly).filter((node) => { if (node.isDisabled) { return false; } ; node.text = node.getText(props.showAllLevels, props.separator) || ""; return filterMethod == null ? void 0 : filterMethod(node, state.inputValue); }); if (state.multiple) { state.presentTags.forEach((tag) => { tag.hitState = false; }); } else { suggestions.forEach((node) => { node.checked = isEqual(state.checkedValue, node.getValueByOption()); }); } state.filtering = true; state.suggestions = suggestions; nextTick(() => updatePopper()); }; const handleSuggestionKeyDown = ({ api }) => (event) => { event = event || window.event; if (!event) { return; } const { keyCode, target } = event; if (keyCode === KEY_CODE.Enter) { target.click(); } else if (keyCode === KEY_CODE.ArrowUp) { const prev = target.previousElementSibling; prev && prev.focus(); } else if (keyCode === KEY_CODE.ArrowDown) { const next = target.nextElementSibling; next && next.focus(); } else if (~[KEY_CODE.Escape, KEY_CODE.Tab].indexOf(keyCode)) { api.toggleDropDownVisible(false); } }; const handleDelete = ({ api, state }) => () => { const lastIndex = state.presentTags.length - 1; const lastTag = state.presentTags[lastIndex]; state.pressDeleteCount = state.inputValue ? 0 : state.pressDeleteCount + 1; if (!lastTag) { return; } if (state.pressDeleteCount) { if (lastTag.hitState) { api.deleteTag(lastIndex); } else { lastTag.hitState = true; } } }; const handleSuggestionClick = ({ api, state }) => (index) => { const targetNode = state.suggestions[index]; if (state.multiple) { const { checked } = targetNode; targetNode.doCheck(!checked); state.panel.calculateMultiCheckedValue(); } else { state.checkedValue = targetNode.getValueByOption(); api.toggleDropDownVisible(false); } }; const deleteTag = ({ emit, state, api }) => (index) => { const val = state.checkedValue[index]; state.checkedValue = state.checkedValue.filter((n, i) => i !== index); api.handleMouseleave(); emit("remove-tag", val); }; const updateStyle = ({ parent, vm, state, updatePopper, nextTick, props }) => () => { const $el = parent.$el; const { suggestionPanel } = vm.$refs; const inputInner = $el.querySelector(CASCADER.InputClass); if (!inputInner) { return; } const tags = $el.querySelector(CASCADER.TagClass); let suggestionPanelEl = null; if (suggestionPanel) { suggestionPanelEl = suggestionPanel.$el; const suggestionList = suggestionPanelEl.querySelector(CASCADER.ListClass); suggestionList.style.minWidth = inputInner.offsetWidth + "px"; } if (tags) { const offsetHeight = tags.offsetHeight; let height = 0; if (props.hoverExpand && !state.inputHover && !state.dropDownVisible) { height = state.inputInitialHeight + "px"; } else { height = Math.max(offsetHeight + 4, state.inputInitialHeight) + "px"; } inputInner.style.height = height; updatePopper(); } else { nextTick(() => { if (state.multiple && state.isDisplayOnly) { inputInner.style.height = "auto"; } }); } }; const getCheckedNodes = (state) => (leafOnly) => state.panel.getCheckedNodes(leafOnly, state.checkedValue); const setInputHeightToTag = ({ nextTick, parent, state }) => () => { nextTick(() => { var _a; const parentEl = parent.$el; const height = ((_a = parentEl == null ? void 0 : parentEl.querySelector(CASCADER.TagClass)) == null ? void 0 : _a.offsetHeight) + 6 + "px"; if (!state.isDisplayOnly) { const inputDom = parentEl == null ? void 0 : parentEl.querySelector(CASCADER.InputClass); inputDom && (inputDom.style.height = height); } else { const inputDom = parentEl == null ? void 0 : parentEl.querySelector(CASCADER.InputClass); inputDom && (inputDom.style.height = "auto"); } }); }; const presentTextHandler = ({ state, value }) => { if (state.inputValue) { if (state.inputValue !== value) { state.inputValue = value; } } else { if (value) { state.inputValue = value; } } }; const handleBeforeUpdate = ({ props, api, state }) => () => { if (!isEmpty(props.modelValue) && !props.filterable || props.hoverExpand && state.multiple) { api.computePresentContent(); } }; const showPlaceholder = ({ props, state, t, constants }) => () => { let placeholder = null; placeholder = state.multiple && state.presentTags.length || state.present ? "" : props.placeholder || t(constants.placeholder); return placeholder; }; export { calcCollapseTags, computClearVisible, computePresentContent, computePresentTags, computePresentText, deleteTag, focusFirstNode, getCheckedNodes, getConfig, getSuggestions, handleBeforeUpdate, handleBlur, handleClear, handleDelete, handleDropdownLeave, handleExpandChange, handleFocus, handleInput, handleKeyDown, handleMouseenter, handleMouseleave, handleSuggestionClick, handleSuggestionKeyDown, isEmpty, presentTextHandler, selfMounted, setInputHeightToTag, showPlaceholder, toggleDropDownVisible, updateStyle, watchCheckedValue, watchInputHover, watchValue };