@pierre/diffs
Version:
263 lines (262 loc) • 10.5 kB
JavaScript
//#region src/managers/ResizeManager.ts
var ResizeManager = class ResizeManager {
static resizeObserver;
static managersByElement = /* @__PURE__ */ new Map();
static getResizeObserver() {
const resizeObserver = ResizeManager.resizeObserver ?? new ResizeObserver(ResizeManager.handleSharedResizeEntries);
ResizeManager.resizeObserver = resizeObserver;
return resizeObserver;
}
static handleSharedResizeEntries(entries) {
const entriesByManager = /* @__PURE__ */ new Map();
for (const entry of entries) {
const manager = ResizeManager.managersByElement.get(entry.target);
if (manager == null) continue;
const managerEntries = entriesByManager.get(manager);
if (managerEntries == null) entriesByManager.set(manager, [entry]);
else managerEntries.push(entry);
}
for (const [manager, managerEntries] of entriesByManager) manager.handleResizeEntries(managerEntries);
}
observedNodes = /* @__PURE__ */ new Map();
setup(pre, disableAnnotations) {
const annotationUpdates = /* @__PURE__ */ new Set();
let columnCount = 0;
const observedNodes = new Map(this.observedNodes);
this.observedNodes.clear();
for (const element of pre.children) {
if (columnCount === 2) break;
const codeElement = (() => {
if (element instanceof HTMLElement && element.tagName === "CODE") return element;
})();
if (codeElement == null) continue;
columnCount++;
let item = observedNodes.get(codeElement);
if (item != null && item.type !== "code") throw new Error("ResizeManager.setup: somehow a code node is being used for an annotation, should be impossible");
let numberElement = codeElement.firstElementChild;
if (!(numberElement instanceof HTMLElement)) numberElement = null;
if (item != null) {
this.observedNodes.set(codeElement, item);
observedNodes.delete(codeElement);
if (item.numberElement !== numberElement) {
if (item.numberElement != null) {
this.unobserve(item.numberElement);
observedNodes.delete(item.numberElement);
}
if (numberElement != null) {
this.observe(numberElement);
observedNodes.delete(numberElement);
this.observedNodes.set(numberElement, item);
}
item.numberElement = numberElement;
item.numberWidth = 0;
} else if (item.numberElement != null) {
observedNodes.delete(item.numberElement);
this.observedNodes.set(item.numberElement, item);
} else item.numberWidth = 0;
} else {
item = {
type: "code",
codeElement,
numberElement,
codeWidth: "auto",
numberWidth: 0
};
this.observedNodes.set(codeElement, item);
this.observe(codeElement);
if (numberElement != null) {
this.observedNodes.set(numberElement, item);
this.observe(numberElement);
}
}
}
if (columnCount > 1 && !disableAnnotations) {
const annotationElements = pre.querySelectorAll("[data-line-annotation*=\",\"]");
const elementMap = /* @__PURE__ */ new Map();
for (const element of annotationElements) {
if (!(element instanceof HTMLElement)) continue;
const lineAnnotation = element.getAttribute("data-line-annotation") ?? "";
if (!/^-?\d+,-?\d+$/.test(lineAnnotation)) {
console.error("DiffFileRenderer.setupResizeObserver: Invalid element or annotation", {
lineAnnotation,
element
});
continue;
}
let pairs = elementMap.get(lineAnnotation);
if (pairs == null) {
pairs = [];
elementMap.set(lineAnnotation, pairs);
}
pairs.push(element);
}
for (const [key, pair] of elementMap) {
if (pair.length !== 2) {
console.error("DiffFileRenderer.setupResizeObserver: Bad Pair", key, pair);
continue;
}
const [container1, container2] = pair;
const child1 = container1.firstElementChild;
const child2 = container2.firstElementChild;
if (!(container1 instanceof HTMLElement) || !(container2 instanceof HTMLElement) || !(child1 instanceof HTMLElement) || !(child2 instanceof HTMLElement)) continue;
let item = observedNodes.get(child1);
if (item != null) {
this.observedNodes.set(child1, item);
this.observedNodes.set(child2, item);
observedNodes.delete(child1);
observedNodes.delete(child2);
continue;
}
const child1Height = child1.getBoundingClientRect().height;
const child2Height = child2.getBoundingClientRect().height;
item = {
type: "annotations",
column1: {
container: container1,
child: child1,
childHeight: child1Height
},
column2: {
container: container2,
child: child2,
childHeight: child2Height
},
currentHeight: "auto"
};
annotationUpdates.add({
child1,
child2,
item,
newHeight: Math.max(child1Height, child2Height)
});
}
for (const pendingUpdate of annotationUpdates) {
this.applyNewHeight(pendingUpdate.item, pendingUpdate.newHeight);
this.observedNodes.set(pendingUpdate.child1, pendingUpdate.item);
this.observedNodes.set(pendingUpdate.child2, pendingUpdate.item);
this.observe(pendingUpdate.child1);
this.observe(pendingUpdate.child2);
}
annotationUpdates.clear();
}
for (const [element, item] of observedNodes) {
this.unobserve(element);
if (item.type === "code") cleanupStaleCodeItem(item);
else cleanupStaleAnnotationItem(item);
}
observedNodes.clear();
}
cleanUp() {
for (const element of this.observedNodes.keys()) this.unobserve(element);
this.observedNodes.clear();
}
observe(element) {
const { managersByElement } = ResizeManager;
const owner = managersByElement.get(element);
if (owner === this) return;
else if (owner != null && owner !== this) throw new Error("ResizeManager.observe: element is already owned by another ResizeManager");
managersByElement.set(element, this);
ResizeManager.getResizeObserver().observe(element);
}
unobserve(element) {
const { managersByElement, resizeObserver } = ResizeManager;
const owner = managersByElement.get(element);
if (owner == null) return;
else if (owner !== this) throw new Error("ResizeManager.unobserve: element is owned by another ResizeManager");
managersByElement.delete(element);
resizeObserver?.unobserve(element);
if (resizeObserver != null && managersByElement.size === 0) {
resizeObserver.disconnect();
ResizeManager.resizeObserver = void 0;
}
}
handleResizeEntries(entries) {
const codeUpdates = /* @__PURE__ */ new Map();
const annotationUpdates = /* @__PURE__ */ new Set();
for (const entry of entries) {
const { target, borderBoxSize, contentBoxSize } = entry;
if (!(target instanceof HTMLElement)) {
console.error("ResizeManager.handleResizeEntries: Invalid element for ResizeObserver", entry);
continue;
}
const item = this.observedNodes.get(target);
if (item == null) {
console.error("ResizeManager.handleResizeEntries: Not a valid observed node", entry);
continue;
}
if (item.type === "annotations") {
const column = (() => {
if (target === item.column1.child) return item.column1;
if (target === item.column2.child) return item.column2;
})();
if (column == null) {
console.error(`ResizeManager.handleResizeEntries: Couldn't find a column for`, {
item,
target
});
continue;
}
column.childHeight = borderBoxSize[0].blockSize;
annotationUpdates.add(item);
} else if (item.type === "code") {
const update = codeUpdates.get(item) ?? {};
const inlineSize = contentBoxSize[0].inlineSize;
if (target === item.codeElement) update.codeInlineSize = inlineSize;
else if (target === item.numberElement) update.numberInlineSize = inlineSize;
codeUpdates.set(item, update);
}
}
this.applyAnnotationUpdates(annotationUpdates);
annotationUpdates.clear();
this.applyColumnUpdates(codeUpdates);
codeUpdates.clear();
}
applyAnnotationUpdates(annotationUpdates) {
for (const item of annotationUpdates) this.applyNewHeight(item, Math.max(item.column1.childHeight, item.column2.childHeight));
}
applyColumnUpdates = (queuedUpdates) => {
for (const [item, update] of queuedUpdates) {
const nextCodeWidth = update.codeInlineSize != null ? resolveCodeWidth(update.codeInlineSize) : item.codeWidth;
const nextNumberWidth = update.numberInlineSize != null ? resolveNumberWidth(update.numberInlineSize) : item.numberWidth;
const codeWidthChanged = nextCodeWidth !== item.codeWidth;
const numberWidthChanged = nextNumberWidth !== item.numberWidth;
if (!codeWidthChanged && !numberWidthChanged) continue;
item.codeWidth = nextCodeWidth;
item.numberWidth = nextNumberWidth;
if (codeWidthChanged) item.codeElement.style.setProperty("--diffs-column-width", `${typeof nextCodeWidth === "number" ? `${nextCodeWidth}px` : "auto"}`);
if (numberWidthChanged) item.codeElement.style.setProperty("--diffs-column-number-width", `${nextNumberWidth === 0 ? "auto" : `${nextNumberWidth}px`}`);
if (codeWidthChanged || numberWidthChanged && nextCodeWidth !== "auto") {
const targetWidth = typeof nextCodeWidth === "number" ? Math.max(nextCodeWidth - nextNumberWidth, 0) : 0;
item.codeElement.style.setProperty("--diffs-column-content-width", `${targetWidth > 0 ? `${targetWidth}px` : "auto"}`);
}
}
};
applyNewHeight(item, newHeight) {
if (newHeight !== item.currentHeight) {
item.currentHeight = Math.max(newHeight, 0);
item.column1.container.style.setProperty("--diffs-annotation-min-height", `${item.currentHeight}px`);
item.column2.container.style.setProperty("--diffs-annotation-min-height", `${item.currentHeight}px`);
}
}
};
function resolveCodeWidth(inlineSize) {
const width = Math.max(Math.floor(inlineSize), 0);
return width === 0 ? "auto" : width;
}
function resolveNumberWidth(inlineSize) {
return Math.max(Math.ceil(inlineSize), 0);
}
function cleanupStaleCodeItem(item) {
if (item.codeElement.isConnected) {
item.codeElement.style.removeProperty("--diffs-column-content-width");
item.codeElement.style.removeProperty("--diffs-column-number-width");
item.codeElement.style.removeProperty("--diffs-column-width");
}
}
function cleanupStaleAnnotationItem(item) {
if (item.column1.container.isConnected) item.column1.container.style.removeProperty("--diffs-annotation-min-height");
if (item.column2.container.isConnected) item.column2.container.style.removeProperty("--diffs-annotation-min-height");
}
//#endregion
export { ResizeManager };
//# sourceMappingURL=ResizeManager.js.map