vxe-pc-ui
Version:
A vue based PC component library
227 lines (226 loc) • 6.47 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _vue = require("vue");
var _xeUtils = _interopRequireDefault(require("xe-utils"));
var _core = require("@vxe-ui/core");
var _dom = require("../../ui/src/dom");
var _anchorLink = _interopRequireDefault(require("./anchor-link"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = exports.default = (0, _vue.defineComponent)({
name: 'VxeAnchor',
props: {
modelValue: String,
options: Array,
container: [String, Object, Function],
showMarker: {
type: Boolean,
default: true
}
},
emits: ['update:modelValue', 'change', 'click'],
setup(props, context) {
const {
slots,
emit
} = context;
const xID = _xeUtils.default.uniqueId();
const refElem = (0, _vue.ref)();
const refMarkerElem = (0, _vue.ref)();
const reactData = (0, _vue.reactive)({
activeHref: null,
staticLinks: [],
containerElem: null
});
const refMaps = {
refElem
};
const computeAllHrefList = (0, _vue.computed)(() => {
const list = [];
_xeUtils.default.eachTree(reactData.staticLinks, item => {
list.push(item.href || '');
}, {
children: 'children'
});
return list;
});
const computeMaps = {};
const $xeAnchor = {
xID,
props,
context,
reactData,
getRefMaps: () => refMaps,
getComputeMaps: () => computeMaps
};
const anchorMethods = {
dispatchEvent(type, params, evnt) {
emit(type, (0, _core.createEvent)(evnt, {
$anchor: $xeAnchor
}, params));
}
};
const getContainerElem = () => {
const {
container
} = props;
if (container) {
if (_xeUtils.default.isElement(container)) {
return container;
}
if (_xeUtils.default.isString(container)) {
return document.querySelector(container);
}
if (_xeUtils.default.isFunction(container)) {
return container({
$anchor: $xeAnchor
});
}
}
return null;
};
const emitEvent = value => {
reactData.activeHref = value;
emit('update:modelValue', value);
};
const handleContainerScrollEvent = () => {
const allHrefList = computeAllHrefList.value;
const {
containerElem
} = reactData;
if (containerElem) {
const wrapperElList = containerElem.querySelectorAll(allHrefList.map(href => `${href}`).join(','));
for (let i = 0; i < wrapperElList.length; i++) {
const wrapperEl = wrapperElList[i];
const wrapperRect = wrapperEl.getBoundingClientRect();
if (wrapperRect.top > 0) {
const href = wrapperEl.id;
reactData.activeHref = `#${href}`;
break;
}
}
}
};
const removeContainerElemScroll = () => {
const {
containerElem
} = reactData;
if (containerElem) {
containerElem.removeEventListener('scroll', handleContainerScrollEvent);
}
};
const updateContainerElem = () => {
const containerElem = getContainerElem();
reactData.containerElem = containerElem;
if (containerElem) {
containerElem.addEventListener('scroll', handleContainerScrollEvent, {
passive: false
});
}
};
const updateMarkerPos = () => {
(0, _vue.nextTick)(() => {
const {
activeHref
} = reactData;
const elem = refElem.value;
const markerEl = refMarkerElem.value;
if (elem && markerEl) {
if (activeHref) {
const linkEl = elem.querySelector(`[href="${activeHref}"]`);
if (linkEl) {
const {
top
} = (0, _dom.getOffsetPos)(linkEl, elem);
markerEl.style.top = `${top}px`;
}
}
}
});
};
const anchorPrivateMethods = {
handleClickLink(evnt, href) {
evnt.preventDefault();
const targetEl = document.getElementById(`${href}`.replace('#', ''));
if (targetEl) {
targetEl.scrollIntoView({
behavior: 'smooth'
});
}
emitEvent(href);
anchorMethods.dispatchEvent('click', {
href
}, evnt);
}
};
Object.assign($xeAnchor, anchorMethods, anchorPrivateMethods);
const renderSubItems = options => {
const itemVNs = [];
if (options) {
options.forEach(item => {
const subItems = item.children;
if (subItems && subItems.length) {
itemVNs.push((0, _vue.h)(_anchorLink.default, {
content: item.content,
title: item.title,
href: item.href
}, {
sub: () => renderSubItems(subItems)
}));
} else {
itemVNs.push((0, _vue.h)(_anchorLink.default, {
content: item.content,
title: item.title,
href: item.href
}));
}
});
}
return itemVNs;
};
const renderVN = () => {
const {
options,
showMarker
} = props;
const defaultSlot = slots.default;
return (0, _vue.h)('div', {
ref: refElem,
class: ['vxe-anchor', {
'is--marker': showMarker
}]
}, [(0, _vue.h)('div', {
class: 'vxe-anchor--list'
}, defaultSlot ? defaultSlot({}) : renderSubItems(options)), showMarker ? (0, _vue.h)('div', {
ref: refMarkerElem,
class: 'vxe-anchor--marker'
}) : (0, _vue.createCommentVNode)()]);
};
(0, _vue.watch)(() => props.modelValue, val => {
reactData.activeHref = val;
});
(0, _vue.watch)(() => reactData.activeHref, () => {
updateMarkerPos();
});
(0, _vue.watch)(() => props.container, () => {
removeContainerElemScroll();
updateContainerElem();
});
(0, _vue.onMounted)(() => {
(0, _vue.nextTick)(() => {
updateContainerElem();
});
});
(0, _vue.onBeforeUnmount)(() => {
removeContainerElemScroll();
});
(0, _vue.provide)('$xeAnchor', $xeAnchor);
$xeAnchor.renderVN = renderVN;
return $xeAnchor;
},
render() {
return this.renderVN();
}
});