vevet
Version:
Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.
105 lines • 4.09 kB
JavaScript
/**
* Recursively retrieves the top parent element of a given element within a container.
*/
function getTopParent(ref, topParent) {
var _a;
if ((ref === null || ref === void 0 ? void 0 : ref.parentElement) === topParent) {
return ref;
}
return getTopParent((_a = ref === null || ref === void 0 ? void 0 : ref.parentElement) !== null && _a !== void 0 ? _a : null, topParent);
}
export function childOf(element, parent) {
if (element === parent) {
return true;
}
if (element !== null) {
return childOf(element.parentNode, parent);
}
return false;
}
/**
* Wraps each word in the container into lines, based on their vertical position.
*/
export function wrapLines({ container, hasLinesWrapper, wordsMeta, lineClassName, lineWrapperClassName, tagName, }) {
const linesMeta = [];
let lineIndex = -1;
let prevTop = Infinity;
// Create lines by wrapping words
wordsMeta.forEach((wordMeta) => {
var _a;
const currentTop = Math.round(wordMeta.element.offsetTop);
const topParent = getTopParent(wordMeta.element, container);
// create new line if the top position changes
if (currentTop !== prevTop) {
prevTop = currentTop;
lineIndex += 1;
const element = document.createElement(tagName);
element.style.display = 'block';
element.classList.add(lineClassName);
let wrapper;
if (hasLinesWrapper) {
wrapper = document.createElement(tagName);
wrapper.style.display = 'block';
wrapper.classList.add(lineWrapperClassName);
wrapper.appendChild(element);
}
linesMeta[lineIndex] = { element, wrapper, nodes: [], words: [] };
}
const currentLine = linesMeta[lineIndex];
const isInList = !!linesMeta.find(({ nodes }) => nodes.includes(topParent));
if (!isInList) {
currentLine.nodes.push(topParent);
if (((_a = topParent.nextSibling) === null || _a === void 0 ? void 0 : _a.nodeType) === 3) {
currentLine.nodes.push(topParent.nextSibling);
}
}
});
// Append line elements to the container
linesMeta.forEach((line) => {
var _a;
container.insertBefore((_a = line.wrapper) !== null && _a !== void 0 ? _a : line.element, line.nodes[0]);
const fragment = document.createDocumentFragment();
fragment.append(...line.nodes);
line.element.append(fragment);
});
// Hide any extra <br> elements after lines
const hiddenBr = [];
linesMeta.forEach((line) => {
var _a;
const nextSibling = ((_a = line.wrapper) !== null && _a !== void 0 ? _a : line.element).nextElementSibling;
if (nextSibling instanceof HTMLBRElement) {
nextSibling.style.display = 'none';
hiddenBr.push(nextSibling);
}
});
// Associate words with the corresponding lines
linesMeta.forEach((line) => {
line.words.push(...wordsMeta.filter((word) => childOf(word.element, line.element)));
});
// Destroy method to undo the line wrapping
const destroy = () => {
let isSuccess = true;
hiddenBr.forEach((br) => {
// eslint-disable-next-line no-param-reassign
br.style.display = '';
});
linesMeta.forEach((line) => {
var _a;
line.nodes.forEach((node) => {
var _a;
const reference = (_a = line.wrapper) !== null && _a !== void 0 ? _a : line.element;
if (reference.parentElement) {
container.insertBefore(node, reference);
}
else {
isSuccess = false;
}
});
line.element.remove();
(_a = line.wrapper) === null || _a === void 0 ? void 0 : _a.remove();
});
return isSuccess;
};
return { linesMeta, destroy };
}
//# sourceMappingURL=wrapLines.js.map