naive-ui
Version:
A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast
264 lines (263 loc) • 9.96 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.baseAnchorPropKeys = exports.baseAnchorProps = void 0;
const lodash_1 = require("lodash");
const seemly_1 = require("seemly");
const vooks_1 = require("vooks");
const vue_1 = require("vue");
const _internal_1 = require("../../_internal");
const _utils_1 = require("../../_utils");
const Link_1 = require("./Link");
const utils_1 = require("./utils");
exports.baseAnchorProps = {
type: {
type: String,
default: 'rail'
},
showRail: {
type: Boolean,
default: true
},
showBackground: {
type: Boolean,
default: true
},
bound: {
type: Number,
default: 12
},
internalScrollable: Boolean,
ignoreGap: Boolean,
offsetTarget: [String, Object, Function]
};
exports.baseAnchorPropKeys = (0, _utils_1.keysOf)(exports.baseAnchorProps);
exports.default = (0, vue_1.defineComponent)({
name: 'BaseAnchor',
props: Object.assign(Object.assign({}, exports.baseAnchorProps), { mergedClsPrefix: {
type: String,
required: true
} }),
setup(props) {
const collectedLinkHrefs = [];
const titleEls = [];
const activeHrefRef = (0, vue_1.ref)(null);
const slotRef = (0, vue_1.ref)(null);
const barRef = (0, vue_1.ref)(null);
const selfRef = (0, vue_1.ref)(null);
const isBlockTypeRef = (0, vue_1.computed)(() => {
return props.type === 'block';
});
const mergedShowRailRef = (0, vue_1.computed)(() => {
return !isBlockTypeRef.value && props.showRail;
});
function disableTransitionOneTick() {
const { value: barEl } = barRef;
const { value: slotEl } = slotRef;
if (barEl) {
barEl.style.transition = 'none';
}
if (slotEl) {
slotEl.style.transition = 'none';
}
if (titleEls) {
titleEls.forEach((titleEl) => {
titleEl.style.transition = 'none';
});
}
void (0, vue_1.nextTick)(() => {
const { value: nextBarEl } = barRef;
const { value: nextSlotEl } = slotRef;
if (nextBarEl) {
void nextBarEl.offsetWidth;
nextBarEl.style.transition = '';
}
if (nextSlotEl) {
void nextSlotEl.offsetWidth;
nextSlotEl.style.transition = '';
}
if (titleEls) {
titleEls.forEach((titleEl) => {
void titleEl.offsetWidth;
titleEl.style.transition = '';
});
}
});
}
function updateBarPosition(linkTitleEl, transition = true) {
const { value: barEl } = barRef;
const { value: slotEl } = slotRef;
const { value: selfEl } = selfRef;
if (!selfEl || !barEl)
return;
if (!transition) {
barEl.style.transition = 'none';
if (slotEl)
slotEl.style.transition = 'none';
}
const { offsetHeight, offsetWidth } = linkTitleEl;
const { top: linkTitleClientTop, left: linkTitleClientLeft } = linkTitleEl.getBoundingClientRect();
const { top: anchorClientTop, left: anchorClientLeft } = selfEl.getBoundingClientRect();
const offsetTop = linkTitleClientTop - anchorClientTop;
const offsetLeft = linkTitleClientLeft - anchorClientLeft;
barEl.style.top = `${offsetTop}px`;
barEl.style.height = `${offsetHeight}px`;
if (slotEl) {
slotEl.style.top = `${offsetTop}px`;
slotEl.style.height = `${offsetHeight}px`;
slotEl.style.maxWidth = `${offsetWidth + offsetLeft}px`;
}
void barEl.offsetHeight;
if (slotEl)
void slotEl.offsetHeight;
if (!transition) {
barEl.style.transition = '';
if (slotEl)
slotEl.style.transition = '';
}
}
const handleScroll = (0, lodash_1.throttle)(() => {
_handleScroll(true);
}, 128);
function setActiveHref(href, transition = true) {
const idMatchResult = /^#([^#]+)$/.exec(href);
if (!idMatchResult)
return;
const linkEl = document.getElementById(idMatchResult[1]);
if (!linkEl)
return;
activeHrefRef.value = href;
linkEl.scrollIntoView();
if (!transition) {
disableTransitionOneTick();
}
handleScroll();
}
function _handleScroll(transition = true) {
var _a;
const links = [];
const offsetTarget = (0, seemly_1.unwrapElement)((_a = props.offsetTarget) !== null && _a !== void 0 ? _a : document);
collectedLinkHrefs.forEach((href) => {
const idMatchResult = /#([^#]+)$/.exec(href);
if (!idMatchResult)
return;
const linkEl = document.getElementById(idMatchResult[1]);
if (linkEl && offsetTarget) {
const { top, height } = (0, utils_1.getOffset)(linkEl, offsetTarget);
links.push({
top,
height,
href
});
}
});
links.sort((a, b) => {
// ascend top
if (a.top > b.top) {
return 1;
// descend height
}
else if (a.top === b.top && a.height < b.height) {
return -1;
}
return -1;
});
const currentActiveHref = activeHrefRef.value;
const { bound, ignoreGap } = props;
const activeLink = links.reduce((prevLink, link) => {
if (link.top + link.height < 0) {
if (ignoreGap) {
return link;
}
else {
return prevLink;
}
}
if (link.top <= bound) {
if (prevLink === null) {
return link;
}
else if (link.top === prevLink.top) {
if (link.href === currentActiveHref) {
return link;
}
else {
return prevLink;
}
}
else if (link.top > prevLink.top) {
return link;
}
else {
return prevLink;
}
}
return prevLink;
}, null);
if (!transition)
disableTransitionOneTick();
if (activeLink) {
activeHrefRef.value = activeLink.href;
}
else {
activeHrefRef.value = null;
}
}
(0, vue_1.provide)(Link_1.anchorInjectionKey, {
activeHref: activeHrefRef,
mergedClsPrefix: (0, vue_1.toRef)(props, 'mergedClsPrefix'),
updateBarPosition,
setActiveHref,
collectedLinkHrefs,
titleEls
});
(0, vue_1.onMounted)(() => {
document.addEventListener('scroll', handleScroll, true);
setActiveHref(window.location.hash);
_handleScroll(false);
});
(0, vooks_1.onFontsReady)(() => {
setActiveHref(window.location.hash);
_handleScroll(false);
});
(0, vue_1.onBeforeUnmount)(() => {
document.removeEventListener('scroll', handleScroll, true);
});
(0, vue_1.watch)(activeHrefRef, (value) => {
if (value === null) {
const { value: slotEl } = slotRef;
if (slotEl && !isBlockTypeRef.value) {
slotEl.style.maxWidth = '0';
}
}
});
return {
selfRef,
barRef,
slotRef,
setActiveHref,
activeHref: activeHrefRef,
isBlockType: isBlockTypeRef,
mergedShowRail: mergedShowRailRef
};
},
render() {
var _a;
const { mergedClsPrefix, mergedShowRail, isBlockType, $slots } = this;
const Anchor = ((0, vue_1.h)("div", { class: [
`${mergedClsPrefix}-anchor`,
isBlockType && `${mergedClsPrefix}-anchor--block`,
mergedShowRail && `${mergedClsPrefix}-anchor--show-rail`
], ref: "selfRef" },
mergedShowRail && this.showBackground ? ((0, vue_1.h)("div", { ref: "slotRef", class: `${mergedClsPrefix}-anchor-link-background` })) : null,
mergedShowRail ? ((0, vue_1.h)("div", { class: `${mergedClsPrefix}-anchor-rail` },
(0, vue_1.h)("div", { ref: "barRef", class: [
`${mergedClsPrefix}-anchor-rail__bar`,
this.activeHref !== null
&& `${mergedClsPrefix}-anchor-rail__bar--active`
] }))) : null, (_a = $slots.default) === null || _a === void 0 ? void 0 :
_a.call($slots)));
return this.internalScrollable ? ((0, vue_1.h)(_internal_1.NScrollbar, null, {
default: () => Anchor
})) : (Anchor);
}
});
;