@sanity/visual-editing
Version:
[](https://npm-stat.com/charts.html?package=@sanity/visual-editing) [](https://
1,111 lines (1,110 loc) • 46.1 kB
JavaScript
var reactCompilerRuntime = require("react-compiler-runtime"), csm = require("@sanity/client/csm"), react = require("react"), useEffectEvent = require("use-effect-event"), context = require("./context.cjs"), mutations = require("./mutations.cjs"), visualEditingCsm = require("@sanity/visual-editing-csm"), stega = require("@vercel/stega");
function useOptimistic(passthrough, reducer) {
const $ = reactCompilerRuntime.c(21), [pristine, setPristine] = react.useState(!0), [optimistic, setOptimistic] = react.useState(passthrough), [lastEvent, setLastEvent] = react.useState(null), [lastPassthrough, setLastPassthrough] = react.useState(passthrough), actor = mutations.useOptimisticActor();
let t0;
$[0] !== reducer ? (t0 = (action, prevState) => (Array.isArray(reducer) ? reducer : [reducer]).reduce((acc, reducer_0) => reducer_0(acc, {
document: action.document,
id: csm.getPublishedId(action.id),
originalId: action.id,
type: action.type
}), prevState), $[0] = reducer, $[1] = t0) : t0 = $[1];
const reduceStateFromAction = useEffectEvent.useEffectEvent(t0);
let t1;
$[2] !== passthrough ? (t1 = () => setLastPassthrough(passthrough), $[2] = passthrough, $[3] = t1) : t1 = $[3];
const updateLastPassthrough = useEffectEvent.useEffectEvent(t1);
let t2;
$[4] !== actor || $[5] !== reduceStateFromAction || $[6] !== updateLastPassthrough ? (t2 = () => {
if (context.isEmptyActor(actor))
return;
let pristineTimeout;
const rebasedSub = actor.on("rebased.local", (_event) => {
const event = {
document: _event.document,
id: _event.id,
originalId: csm.getPublishedId(_event.id),
type: "mutate"
};
setOptimistic((prevState_0) => reduceStateFromAction(event, prevState_0)), setLastEvent(event), updateLastPassthrough(), setPristine(!1), clearTimeout(pristineTimeout);
}), pristineSub = actor.on("pristine", () => {
pristineTimeout = setTimeout(() => {
react.startTransition(() => setPristine(!0));
}, 15e3);
});
return () => {
rebasedSub.unsubscribe(), pristineSub.unsubscribe();
};
}, $[4] = actor, $[5] = reduceStateFromAction, $[6] = updateLastPassthrough, $[7] = t2) : t2 = $[7];
let t3;
$[8] !== actor ? (t3 = [actor], $[8] = actor, $[9] = t3) : t3 = $[9], react.useEffect(t2, t3);
let t4;
$[10] !== lastEvent || $[11] !== lastPassthrough || $[12] !== passthrough || $[13] !== pristine || $[14] !== reduceStateFromAction ? (t4 = () => {
if (!pristine) {
if (!lastEvent)
throw new Error("No last event found when syncing passthrough");
lastPassthrough !== passthrough && react.startTransition(() => {
setOptimistic(reduceStateFromAction(lastEvent, passthrough)), setLastPassthrough(passthrough);
});
}
}, $[10] = lastEvent, $[11] = lastPassthrough, $[12] = passthrough, $[13] = pristine, $[14] = reduceStateFromAction, $[15] = t4) : t4 = $[15];
let t5;
return $[16] !== lastEvent || $[17] !== lastPassthrough || $[18] !== passthrough || $[19] !== pristine ? (t5 = [lastEvent, lastPassthrough, passthrough, pristine], $[16] = lastEvent, $[17] = lastPassthrough, $[18] = passthrough, $[19] = pristine, $[20] = t5) : t5 = $[20], react.useEffect(t4, t5), pristine ? passthrough : optimistic;
}
const byteToHex = [];
for (let i = 0; i < 256; ++i)
byteToHex.push((i + 256).toString(16).slice(1));
function unsafeStringify(arr, offset = 0) {
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
}
let getRandomValues;
const rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
if (typeof crypto > "u" || !crypto.getRandomValues)
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
getRandomValues = crypto.getRandomValues.bind(crypto);
}
return getRandomValues(rnds8);
}
const randomUUID = typeof crypto < "u" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native = {
randomUUID
};
function v4(options, buf, offset) {
if (native.randomUUID && !options)
return native.randomUUID();
options = options || {};
const rnds = options.random ?? options.rng?.() ?? rng();
if (rnds.length < 16)
throw new Error("Random bytes length must be >= 16");
return rnds[6] = rnds[6] & 15 | 64, rnds[8] = rnds[8] & 63 | 128, unsafeStringify(rnds);
}
function getRect(element) {
const domRect = element.getBoundingClientRect();
return {
x: domRect.x + scrollX,
y: domRect.y + scrollY,
w: domRect.width,
h: domRect.height
};
}
function offsetRect(rect, px, axis) {
return axis === "x" ? {
x: rect.x + px,
y: rect.y,
w: rect.w - 2 * px,
h: rect.h
} : {
x: rect.x,
y: rect.y + px,
w: rect.w,
h: rect.h - 2 * px
};
}
function rayIntersect(l1, l2) {
const {
x1,
y1,
x2,
y2
} = l1, {
x1: x3,
y1: y3,
x2: x4,
y2: y4
} = l2;
if (x1 === x2 && y1 === y2 || x3 === x4 && y3 === y4)
return !1;
const denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (denominator === 0)
return !1;
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator, ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
return !1;
const x = x1 + ua * (x2 - x1), y = y1 + ua * (y2 - y1);
return {
x,
y
};
}
function rectEqual(r1, r2) {
return r1.x === r2.x && r1.y === r2.y && r1.w === r2.w && r1.h === r2.h;
}
function rayRectIntersections(line, rect) {
const rectLines = [{
x1: rect.x,
y1: rect.y,
x2: rect.x + rect.w,
y2: rect.y
}, {
x1: rect.x + rect.w,
y1: rect.y,
x2: rect.x + rect.w,
y2: rect.y + rect.h
}, {
x1: rect.x + rect.w,
y1: rect.y + rect.h,
x2: rect.x,
y2: rect.y + rect.h
}, {
x1: rect.x,
y1: rect.y + rect.h,
x2: rect.x,
y2: rect.y
}], intersections = [];
for (let i = 0; i < rectLines.length; i++) {
const intersection = rayIntersect(line, rectLines[i]);
if (intersection) {
let isDuplicate = !1;
for (let j = 0; j < intersections.length; j++)
intersections[j].x === intersection.x && intersections[j].y === intersection.y && (isDuplicate = !0);
isDuplicate || intersections.push(intersection);
}
}
return intersections.length === 0 ? !1 : intersections.sort((a, b) => pointDist(a, {
x: line.x1,
y: line.y1
}) - pointDist(b, {
x: line.x1,
y: line.y1
}));
}
function pointDist(p1, p2) {
const a = p1.x - p2.x, b = p1.y - p2.y;
return Math.sqrt(a * a + b * b);
}
function pointInBounds(point, bounds) {
const withinX = point.x >= bounds.x && point.x <= bounds.x + bounds.w, withinY = point.y >= bounds.y && point.y <= bounds.y + bounds.h;
return withinX && withinY;
}
function findClosestIntersection(ray, targets, flow) {
const rayOrigin = {
x: ray.x1,
y: ray.y1
};
if (targets.some((t) => pointInBounds(rayOrigin, offsetRect(t, Math.min(t.w, t.h) / 10, flow === "horizontal" ? "x" : "y")))) return null;
let closestIntersection, closestRect;
for (const target of targets) {
const intersections = rayRectIntersections(ray, offsetRect(target, Math.min(target.w, target.h) / 10, flow === "horizontal" ? "x" : "y"));
if (intersections) {
const firstIntersection = intersections[0];
closestIntersection ? pointDist(rayOrigin, firstIntersection) < pointDist(rayOrigin, closestIntersection) && (closestIntersection = firstIntersection, closestRect = target) : (closestIntersection = firstIntersection, closestRect = target);
}
}
return closestRect || null;
}
function scaleRect(rect, scale, origin) {
const {
x,
y,
w,
h
} = rect, {
x: originX,
y: originY
} = origin, newX = originX + (x - originX) * scale, newY = originY + (y - originY) * scale, newWidth = w * scale, newHeight = h * scale;
return {
x: newX,
y: newY,
w: newWidth,
h: newHeight
};
}
function getRectGroupXExtent(rects) {
const minGroupX = Math.max(0, Math.min(...rects.map((r) => r.x))), maxGroupX = Math.min(document.body.offsetWidth, Math.max(...rects.map((r) => r.x + r.w)));
return {
min: minGroupX,
max: maxGroupX,
width: maxGroupX - minGroupX
};
}
function getRectGroupYExtent(rects) {
const minGroupY = Math.max(0, Math.min(...rects.map((r) => r.y))), maxGroupY = Math.min(document.body.scrollHeight, Math.max(...rects.map((r) => r.y + r.h)));
return {
min: minGroupY,
max: maxGroupY,
height: maxGroupY - minGroupY
};
}
function calcTargetFlow(targets) {
return targets.some((t1) => targets.filter((t2) => !rectEqual(t1, t2)).some((t2) => t1.y === t2.y)) ? "horizontal" : "vertical";
}
function calcInsertPosition(origin, targets, flow) {
if (flow === "horizontal") {
const rayLeft = {
x1: origin.x,
y1: origin.y,
x2: origin.x - 1e8,
y2: origin.y
}, rayRight = {
x1: origin.x,
y1: origin.y,
x2: origin.x + 1e8,
y2: origin.y
};
return {
left: findClosestIntersection(rayLeft, targets, flow),
right: findClosestIntersection(rayRight, targets, flow)
};
} else {
const rayTop = {
x1: origin.x,
y1: origin.y,
x2: origin.x,
y2: origin.y - 1e8
}, rayBottom = {
x1: origin.x,
y1: origin.y,
x2: origin.x,
y2: origin.y + 1e8
};
return {
top: findClosestIntersection(rayTop, targets, flow),
bottom: findClosestIntersection(rayBottom, targets, flow)
};
}
}
function findRectSanityData(rect, overlayGroup) {
return overlayGroup.find((e) => rectEqual(getRect(e.elements.element), rect))?.sanity;
}
function resolveInsertPosition(overlayGroup, insertPosition, flow) {
return Object.values(insertPosition).every((v) => v === null) ? null : flow === "horizontal" ? {
left: insertPosition.left ? {
rect: insertPosition.left,
sanity: findRectSanityData(insertPosition.left, overlayGroup)
} : null,
right: insertPosition.right ? {
rect: insertPosition.right,
sanity: findRectSanityData(insertPosition.right, overlayGroup)
} : null
} : {
top: insertPosition.top ? {
rect: insertPosition.top,
sanity: findRectSanityData(insertPosition.top, overlayGroup)
} : null,
bottom: insertPosition.bottom ? {
rect: insertPosition.bottom,
sanity: findRectSanityData(insertPosition.bottom, overlayGroup)
} : null
};
}
function calcMousePos(e) {
const bodyBounds = document.body.getBoundingClientRect();
return {
x: Math.max(bodyBounds.x, Math.min(e.clientX, bodyBounds.x + bodyBounds.width)),
y: e.clientY + window.scrollY
};
}
function calcMousePosInverseTransform(mousePos2) {
const body = document.body, transform = window.getComputedStyle(body).transform;
if (transform === "none")
return {
x: mousePos2.x,
y: mousePos2.y
};
const inverseMatrix = new DOMMatrix(transform).inverse(), transformedPoint = new DOMPoint(mousePos2.x, mousePos2.y).matrixTransform(inverseMatrix);
return {
x: transformedPoint.x,
y: transformedPoint.y
};
}
function buildPreviewSkeleton(mousePos2, element, scaleFactor) {
const bounds = getRect(element), children = [...element.querySelectorAll(":where(h1, h2, h3, h4, p, a, img, span, button):not(:has(*))")];
mousePos2.x <= bounds.x && (mousePos2.x = bounds.x), mousePos2.x >= bounds.x + bounds.w && (mousePos2.x = bounds.x + bounds.w), mousePos2.y >= bounds.y + bounds.h && (mousePos2.y = bounds.y + bounds.h), mousePos2.y <= bounds.y && (mousePos2.y = bounds.y);
const childRects = children.map((child) => {
const rect = scaleRect(getRect(child), scaleFactor, {
x: bounds.x,
y: bounds.y
});
return {
x: rect.x - bounds.x,
y: rect.y - bounds.y,
w: rect.w,
h: rect.h,
tagName: child.tagName
};
});
return {
offsetX: (bounds.x - mousePos2.x) * scaleFactor,
offsetY: (bounds.y - mousePos2.y) * scaleFactor,
w: bounds.w * scaleFactor,
h: bounds.h * scaleFactor,
maxWidth: bounds.w * scaleFactor * 0.75,
childRects
};
}
const minDragDelta = 4;
async function applyMinimapWrapperTransform(target, scaleFactor, minYScaled, handler, rectUpdateFrequency) {
return new Promise((resolve) => {
target.addEventListener("transitionend", () => {
setTimeout(() => {
handler({
type: "overlay/dragEndMinimapTransition"
});
}, rectUpdateFrequency * 2), resolve();
}, {
once: !0
}), handler({
type: "overlay/dragStartMinimapTransition"
}), handler({
type: "overlay/dragToggleMinimap",
display: !0
}), document.body.style.overflow = "hidden", document.body.style.height = "100%", document.documentElement.style.overflow = "initial", document.documentElement.style.height = "100%", setTimeout(() => {
target.style.transformOrigin = "50% 0px", target.style.transition = "transform 150ms ease", target.style.transform = `translate3d(0px, ${-minYScaled + scrollY}px, 0px) scale(${scaleFactor})`;
}, 25);
});
}
function calcMinimapTransformValues(rects, groupHeightOverride) {
let groupHeight = groupHeightOverride || getRectGroupYExtent(rects).height;
const padding = 100;
groupHeight += padding * 2;
const scaleFactor = groupHeight > window.innerHeight ? window.innerHeight / groupHeight : 1, scaledRects = rects.map((r) => scaleRect(r, scaleFactor, {
x: window.innerWidth / 2,
y: 0
})), {
min: minYScaled
} = getRectGroupYExtent(scaledRects);
return {
scaleFactor,
minYScaled: minYScaled - padding * scaleFactor
};
}
function calcGroupBoundsPreview(rects) {
const groupBoundsX = getRectGroupXExtent(rects), groupBoundsY = getRectGroupYExtent(rects), offsetDist = 8, canOffsetX = groupBoundsX.min > offsetDist && groupBoundsX.min + groupBoundsX.width <= window.innerWidth - offsetDist, canOffsetY = groupBoundsY.min > offsetDist && groupBoundsY.min + groupBoundsY.height <= document.body.scrollHeight - offsetDist, canOffset = canOffsetX && canOffsetY;
return {
x: canOffset ? groupBoundsX.min - offsetDist : groupBoundsX.min,
y: canOffset ? groupBoundsY.min - offsetDist : groupBoundsY.min,
w: canOffset ? groupBoundsX.width + offsetDist * 2 : groupBoundsX.width,
h: canOffset ? groupBoundsY.height + offsetDist * 2 : groupBoundsY.height
};
}
async function resetMinimapWrapperTransform(endYOrigin, target, prescaleHeight2, handler, rectUpdateFrequency, previousRootStyleValues2) {
return new Promise((resolve) => {
const transform = window.getComputedStyle(target).transform;
if (new DOMMatrix(transform).a === 1) return;
const maxScroll = prescaleHeight2 - window.innerHeight, prevScrollY = scrollY;
endYOrigin -= window.innerHeight / 2, endYOrigin < 0 && (endYOrigin = 0), target.addEventListener("transitionend", () => {
target.style.transition = "none", target.style.transform = "none", scrollTo({
top: endYOrigin,
behavior: "instant"
}), setTimeout(() => {
handler({
type: "overlay/dragEndMinimapTransition"
}), handler({
type: "overlay/dragToggleMinimap",
display: !1
});
}, rectUpdateFrequency * 2), resolve();
}, {
once: !0
}), handler({
type: "overlay/dragStartMinimapTransition"
}), target.style.transform = `translateY(${Math.max(prevScrollY - endYOrigin, -maxScroll + prevScrollY)}px) scale(1)`, previousRootStyleValues2 && (document.body.style.overflow = previousRootStyleValues2.body.overflow, document.body.style.height = previousRootStyleValues2.body.height, document.documentElement.style.overflow = previousRootStyleValues2.documentElement.overflow, document.documentElement.style.height = previousRootStyleValues2.documentElement.height);
});
}
let minimapScaleApplied = !1, mousePosInverseTransform = {
y: 0
}, mousePos = {
x: 0,
y: 0
}, prescaleHeight = typeof document > "u" ? 0 : document.documentElement.scrollHeight, previousRootStyleValues = null;
function handleOverlayDrag(opts) {
const {
mouseEvent,
element,
overlayGroup,
handler,
target,
onSequenceStart,
onSequenceEnd
} = opts;
if (mouseEvent.button !== 0) return;
window.focus();
const rectUpdateFrequency = 150;
let rects = overlayGroup.map((e) => getRect(e.elements.element));
const flow = element.getAttribute("data-sanity-drag-flow") || calcTargetFlow(rects), dragGroup = element.getAttribute("data-sanity-drag-group"), disableMinimap = !!element.getAttribute("data-sanity-drag-minimap-disable"), preventInsertDefault = !!element.getAttribute("data-sanity-drag-prevent-default"), documentHeightOverride = element.getAttribute("data-unstable_sanity-drag-document-height"), groupHeightOverride = element.getAttribute("data-unstable_sanity-drag-group-height");
let insertPosition = null;
const initialMousePos = calcMousePos(mouseEvent), scaleTarget = document.body, {
minYScaled,
scaleFactor
} = calcMinimapTransformValues(rects, groupHeightOverride ? ~~groupHeightOverride : null);
let sequenceStarted = !1, minimapPromptShown = !1, mousedown = !0;
minimapScaleApplied || (previousRootStyleValues = {
body: {
overflow: window.getComputedStyle(document.body).overflow,
height: window.getComputedStyle(document.body).height
},
documentElement: {
overflow: window.getComputedStyle(document.documentElement).overflow,
height: window.getComputedStyle(document.documentElement).height
}
}, prescaleHeight = documentHeightOverride ? ~~documentHeightOverride : document.documentElement.scrollHeight);
const rectsInterval = setInterval(() => {
rects = overlayGroup.map((e) => getRect(e.elements.element));
}, rectUpdateFrequency), applyMinimap = () => {
if (scaleFactor >= 1) return;
const skeleton = buildPreviewSkeleton(mousePos, element, scaleFactor);
handler({
type: "overlay/dragUpdateSkeleton",
skeleton
}), handler({
type: "overlay/dragToggleMinimapPrompt",
display: !1
}), applyMinimapWrapperTransform(scaleTarget, scaleFactor, minYScaled, handler, rectUpdateFrequency).then(() => {
setTimeout(() => {
handler({
type: "overlay/dragUpdateGroupRect",
groupRect: calcGroupBoundsPreview(rects)
});
}, rectUpdateFrequency * 2);
});
}, handleScroll = (e) => {
Math.abs(e.deltaY) >= 10 && scaleFactor < 1 && !minimapScaleApplied && !minimapPromptShown && !disableMinimap && mousedown && (handler({
type: "overlay/dragToggleMinimapPrompt",
display: !0
}), minimapPromptShown = !0), e.shiftKey && !minimapScaleApplied && !disableMinimap && (window.dispatchEvent(new CustomEvent("unstable_sanity/dragApplyMinimap")), minimapScaleApplied = !0, setTimeout(() => {
applyMinimap();
}, 50));
}, handleMouseMove = (e) => {
if (e.preventDefault(), mousePos = calcMousePos(e), mousePosInverseTransform = calcMousePosInverseTransform(mousePos), Math.abs(pointDist(mousePos, initialMousePos)) < minDragDelta) return;
if (!sequenceStarted) {
const groupRect = calcGroupBoundsPreview(rects), skeleton = buildPreviewSkeleton(mousePos, element, 1);
handler({
type: "overlay/dragStart",
flow
}), handler({
type: "overlay/dragUpdateSkeleton",
skeleton
}), handler({
type: "overlay/dragUpdateGroupRect",
groupRect
}), sequenceStarted = !0, onSequenceStart();
}
handler({
type: "overlay/dragUpdateCursorPosition",
x: mousePos.x,
y: mousePos.y
}), e.shiftKey && !minimapScaleApplied && !disableMinimap && (window.dispatchEvent(new CustomEvent("unstable_sanity/dragApplyMinimap")), minimapScaleApplied = !0, setTimeout(() => {
applyMinimap();
}, 50));
const newInsertPosition = calcInsertPosition(mousePos, rects, flow);
JSON.stringify(insertPosition) !== JSON.stringify(newInsertPosition) && (insertPosition = newInsertPosition, handler({
type: "overlay/dragUpdateInsertPosition",
insertPosition: resolveInsertPosition(overlayGroup, insertPosition, flow)
}));
}, handleMouseUp = () => {
mousedown = !1, handler({
type: "overlay/dragEnd",
target,
insertPosition: insertPosition ? resolveInsertPosition(overlayGroup, insertPosition, flow) : null,
dragGroup,
flow,
preventInsertDefault
}), minimapPromptShown && handler({
type: "overlay/dragToggleMinimapPrompt",
display: !1
}), minimapScaleApplied || (clearInterval(rectsInterval), onSequenceEnd(), removeFrameListeners(), removeKeyListeners()), removeMouseListeners();
}, handleKeyup = (e) => {
if (e.key === "Shift" && minimapScaleApplied) {
minimapScaleApplied = !1;
const skeleton = buildPreviewSkeleton(mousePos, element, 1 / scaleFactor);
handler({
type: "overlay/dragUpdateSkeleton",
skeleton
}), window.dispatchEvent(new CustomEvent("unstable_sanity/dragResetMinimap")), setTimeout(() => {
resetMinimapWrapperTransform(mousePosInverseTransform.y, scaleTarget, prescaleHeight, handler, rectUpdateFrequency, previousRootStyleValues);
}, 50), handler({
type: "overlay/dragUpdateGroupRect",
groupRect: null
}), mousedown || (clearInterval(rectsInterval), removeMouseListeners(), removeFrameListeners(), removeKeyListeners(), onSequenceEnd());
}
}, handleBlur = () => {
handler({
type: "overlay/dragUpdateGroupRect",
groupRect: null
}), window.dispatchEvent(new CustomEvent("unstable_sanity/dragResetMinimap")), setTimeout(() => {
resetMinimapWrapperTransform(mousePosInverseTransform.y, scaleTarget, prescaleHeight, handler, rectUpdateFrequency, previousRootStyleValues).then(() => {
minimapScaleApplied = !1;
});
}, 50), clearInterval(rectsInterval), removeMouseListeners(), removeFrameListeners(), removeKeyListeners(), onSequenceEnd();
}, removeMouseListeners = () => {
window.removeEventListener("mousemove", handleMouseMove), window.removeEventListener("wheel", handleScroll), window.removeEventListener("mouseup", handleMouseUp);
}, removeKeyListeners = () => {
window.removeEventListener("keyup", handleKeyup);
}, removeFrameListeners = () => {
window.removeEventListener("blur", handleBlur);
};
window.addEventListener("blur", handleBlur), window.addEventListener("keyup", handleKeyup), window.addEventListener("wheel", handleScroll), window.addEventListener("mousemove", handleMouseMove), window.addEventListener("mouseup", handleMouseUp);
}
const isElementNode$1 = (target) => target instanceof HTMLElement || target instanceof SVGElement;
function findNonInlineElement(element) {
const {
display
} = window.getComputedStyle(element);
if (display !== "inline") return element;
const parent = element.parentElement;
return parent ? findNonInlineElement(parent) : null;
}
const findOverlayElement = (el) => !el || !isElementNode$1(el) ? null : el.dataset?.sanityOverlayElement ? el : findOverlayElement(el.parentElement);
function testVercelStegaRegex(input) {
return stega.VERCEL_STEGA_REGEX.lastIndex = 0, stega.VERCEL_STEGA_REGEX.test(input);
}
function decodeStega(str, isAltText = !1) {
try {
const decoded = stega.vercelStegaDecode(str);
return !decoded || decoded.origin !== "sanity.io" ? null : (isAltText && (decoded.href = decoded.href?.replace(".alt", "")), decoded);
} catch (err) {
return console.error("Failed to decode stega for string: ", str, "with the original error: ", err), null;
}
}
function testAndDecodeStega(str, isAltText = !1) {
return testVercelStegaRegex(str) ? decodeStega(str, isAltText) : null;
}
const isElementNode = (node) => node.nodeType === Node.ELEMENT_NODE, isImgElement = (el) => el.tagName === "IMG", isTimeElement = (el) => el.tagName === "TIME", isSvgRootElement = (el) => el.tagName.toUpperCase() === "SVG";
function isSanityNode(node) {
return "path" in node;
}
function findCommonPath(first, second) {
let firstParts = first.split("."), secondParts = second.split(".");
const maxLength = Math.min(firstParts.length, secondParts.length);
return firstParts = firstParts.slice(0, maxLength).reverse(), secondParts = secondParts.slice(0, maxLength).reverse(), firstParts.reduce((parts, part, i) => part === secondParts[i] ? [...parts, part] : [], []).reverse().join(".");
}
function findCommonSanityData(nodes) {
if (!nodes.length || !nodes.map((n) => isSanityNode(n)).every((n, _i, arr) => n === arr[0]))
return;
if (!isSanityNode(nodes[0])) return nodes[0];
const sanityNodes = nodes.filter(isSanityNode);
let common = nodes[0];
const consistentValueKeys = ["projectId", "dataset", "id", "baseUrl", "workspace", "tool"];
for (let i = 1; i < sanityNodes.length; i++) {
const node = sanityNodes[i];
if (consistentValueKeys.some((key) => node[key] !== common?.[key])) {
common = void 0;
break;
}
common = {
...common,
path: findCommonPath(common.path, node.path)
};
}
return common;
}
function findSanityNodes(el) {
const mainResults = [];
function createResolvedElement(element, data, reason, preventGrouping) {
const sanity = visualEditingCsm.decodeSanityNodeData(data);
if (!sanity)
return;
const measureElement = findNonInlineElement(element);
if (measureElement)
return {
elements: {
element,
measureElement
},
sanity,
reason,
preventGrouping
};
}
function resolveNode(node) {
const {
nodeType,
parentElement,
textContent
} = node;
if (isElementNode(node) && node.dataset?.sanityEditTarget !== void 0) {
const nodesInTarget = findSanityNodes(node), commonData = findCommonSanityData(nodesInTarget.map((node2) => node2.type === "element" ? node2.commonSanity : void 0).filter((n) => n !== void 0));
if (commonData)
return {
reason: "edit-target",
elements: {
element: node,
measureElement: node
},
sanity: commonData
};
} else if (nodeType === Node.TEXT_NODE && parentElement && textContent) {
const data = testAndDecodeStega(textContent);
return data ? createResolvedElement(parentElement, data, "stega-text", !0) : void 0;
} else if (isElementNode(node)) {
if (node.tagName === "SCRIPT" || node.tagName === "SANITY-VISUAL-EDITING")
return;
if (node.dataset?.sanity)
return createResolvedElement(node, node.dataset.sanity, "data-attribute", !!(node.textContent && testVercelStegaRegex(node.textContent)));
if (node.dataset?.sanityEditInfo)
return createResolvedElement(node, node.dataset.sanityEditInfo, "data-attribute", !!(node.textContent && testVercelStegaRegex(node.textContent)));
if (isImgElement(node)) {
const data = testAndDecodeStega(node.alt, !0);
return data ? createResolvedElement(node, data, "stega-attribute") : void 0;
} else if (isTimeElement(node)) {
const data = testAndDecodeStega(node.dateTime, !0);
return data ? createResolvedElement(node, data, "stega-attribute") : void 0;
} else if (isSvgRootElement(node)) {
if (!node.ariaLabel) return;
const data = testAndDecodeStega(node.ariaLabel, !0);
return data ? createResolvedElement(node, data, "stega-attribute") : void 0;
}
}
}
function processNode(node, _parentGroup) {
const resolvedElement = resolveNode(node);
let parentGroup = _parentGroup;
if (isElementNode(node) && node.dataset?.sanityEditGroup !== void 0 && (parentGroup = {
type: "group",
elements: {
element: node,
measureElement: node
},
targets: []
}, mainResults.push(parentGroup)), resolvedElement) {
const target = {
elements: resolvedElement.elements,
sanity: resolvedElement.sanity,
reason: resolvedElement.reason
};
parentGroup && !resolvedElement.preventGrouping ? parentGroup.targets.push(target) : mainResults.push({
elements: resolvedElement.elements,
type: "element",
targets: [target]
});
}
if (isElementNode(node) && !isImgElement(node) && !(node.tagName === "SCRIPT" || node.tagName === "SANITY-VISUAL-EDITING"))
for (const childNode of node.childNodes)
processNode(childNode, parentGroup);
}
if (el)
for (const node of el.childNodes)
processNode(node, void 0);
return mainResults.map((node) => {
if (node.targets.length === 0 && node.type === "group")
return {
...node,
commonSanity: void 0
};
const commonSanity = node.targets.length === 1 ? node.targets[0].sanity : findCommonSanityData(node.targets.map(({
sanity
}) => sanity).filter((n) => n !== void 0)) || node.targets[0].sanity;
return commonSanity ? {
...node,
commonSanity
} : null;
}).filter((node) => node !== null);
}
function isSanityArrayPath(path) {
const lastDotIndex = path.lastIndexOf(".");
return path.substring(lastDotIndex, path.length).includes("[");
}
function getSanityNodeArrayPath(path) {
if (!isSanityArrayPath(path)) return null;
const split = path.split(".");
return split[split.length - 1] = split[split.length - 1].replace(/\[.*?\]/g, "[]"), split.join(".");
}
function sanityNodesExistInSameArray(sanityNode1, sanityNode2) {
return !isSanityArrayPath(sanityNode1.path) || !isSanityArrayPath(sanityNode2.path) ? !1 : getSanityNodeArrayPath(sanityNode1.path) === getSanityNodeArrayPath(sanityNode2.path);
}
function resolveDragAndDropGroup(element, sanity, elementSet, elementsMap) {
if (!element.getAttribute("data-sanity") || element.getAttribute("data-sanity-drag-disable") || !sanity || !isSanityNode(sanity) || !isSanityArrayPath(sanity.path)) return null;
const targetDragGroup = element.getAttribute("data-sanity-drag-group"), group = [...elementSet].reduce((acc, el) => {
const elData = elementsMap.get(el), elDragDisabled = el.getAttribute("data-sanity-drag-disable"), elDragGroup = el.getAttribute("data-sanity-drag-group"), elHasSanityAttribution = el.getAttribute("data-sanity") !== null, sharedDragGroup = targetDragGroup !== null ? targetDragGroup === elDragGroup : !0;
return elData?.sanity && !elDragDisabled && isSanityNode(elData.sanity) && sanityNodesExistInSameArray(sanity, elData.sanity) && sharedDragGroup && elHasSanityAttribution && acc.push(elData), acc;
}, []);
return group.length <= 1 ? null : group;
}
function createOverlayController({
handler,
overlayElement,
inFrame,
inPopUp,
optimisticActorReady
}) {
let activated = !1;
const elementIdMap = /* @__PURE__ */ new Map(), elementsMap = /* @__PURE__ */ new WeakMap(), elementSet = /* @__PURE__ */ new Set(), measureElements = /* @__PURE__ */ new WeakMap(), cursorMap = /* @__PURE__ */ new WeakMap();
let ro, io, mo, activeDragSequence = !1, hoverStack = [];
const getHoveredElement = () => hoverStack[hoverStack.length - 1];
function addEventHandlers(el, handlers) {
el.addEventListener("click", handlers.click, {
capture: !0
}), el.addEventListener("contextmenu", handlers.contextmenu, {
capture: !0
}), el.addEventListener("mousemove", handlers.mousemove, {
once: !0,
capture: !0
}), el.addEventListener("mousedown", handlers.mousedown, {
capture: !0
});
}
function removeEventHandlers(el, handlers) {
el.removeEventListener("click", handlers.click, {
capture: !0
}), el.removeEventListener("contextmenu", handlers.contextmenu, {
capture: !0
}), el.removeEventListener("mousemove", handlers.mousemove, {
capture: !0
}), el.removeEventListener("mousedown", handlers.mousedown, {
capture: !0
}), el.removeEventListener("mouseenter", handlers.mouseenter), el.removeEventListener("mouseleave", handlers.mouseleave);
}
function activateElement({
id,
elements,
handlers
}) {
const {
element,
measureElement
} = elements;
addEventHandlers(element, handlers), ro.observe(measureElement), handler({
type: "element/activate",
id
});
}
function deactivateElement({
id,
elements,
handlers
}) {
const {
element,
measureElement
} = elements;
removeEventHandlers(element, handlers), ro.unobserve(measureElement), hoverStack = hoverStack.filter((el) => el !== element), handler({
type: "element/deactivate",
id
});
}
function setOverlayCursor(element) {
if (!(!inFrame && !inPopUp || !optimisticActorReady))
for (const hoverstackElement of hoverStack) {
if (element === hoverstackElement) {
const targetSanityData = elementsMap.get(element)?.sanity;
if (!targetSanityData || !isSanityNode(targetSanityData)) return;
if (resolveDragAndDropGroup(element, targetSanityData, elementSet, elementsMap)) {
const existingCursor = element.style.cursor;
existingCursor && cursorMap.set(element, existingCursor), handler({
type: "overlay/setCursor",
element,
cursor: "move"
});
continue;
}
}
restoreOverlayCursor(hoverstackElement);
}
}
function restoreOverlayCursor(element) {
const previousCursor = cursorMap.get(element);
handler({
type: "overlay/setCursor",
element,
cursor: previousCursor
});
}
function registerElement({
type,
elements,
commonSanity,
targets
}) {
const {
element,
measureElement
} = elements, eventHandlers = {
click(event) {
const target = event.target;
if (element === getHoveredElement() && element.contains(target)) {
inFrame && (event.preventDefault(), event.stopPropagation());
const sanity = elementsMap.get(element)?.sanity;
sanity && !activeDragSequence && handler({
type: "element/click",
id,
sanity
});
}
},
contextmenu(event) {
if (!("path" in commonSanity) || !inFrame && !inPopUp || !optimisticActorReady || !commonSanity.path.split(".").pop()?.includes("[_key==")) return;
const target = event.target;
element === getHoveredElement() && element.contains(target) && ((inFrame || inPopUp) && (event.preventDefault(), event.stopPropagation()), handler({
type: "element/contextmenu",
id,
position: {
x: event.clientX,
y: event.clientY
},
sanity: commonSanity
}));
},
mousedown(event) {
if (event.preventDefault(), event.currentTarget !== hoverStack.at(-1) || element.getAttribute("data-sanity-drag-disable") || !inFrame && !inPopUp || !optimisticActorReady) return;
const targetSanityData = elementsMap.get(element)?.sanity;
if (!targetSanityData || !isSanityNode(targetSanityData) || !isSanityArrayPath(targetSanityData.path)) return;
const dragGroup = resolveDragAndDropGroup(element, commonSanity, elementSet, elementsMap);
dragGroup && handleOverlayDrag({
element,
handler,
mouseEvent: event,
overlayGroup: dragGroup,
target: targetSanityData,
onSequenceStart: () => {
activeDragSequence = !0;
},
onSequenceEnd: () => {
setTimeout(() => {
activeDragSequence = !1;
}, 250);
}
});
},
mousemove(event) {
eventHandlers.mouseenter(event);
const el = event.currentTarget;
el && (el.addEventListener("mouseenter", eventHandlers.mouseenter), el.addEventListener("mouseleave", eventHandlers.mouseleave));
},
mouseenter() {
document.querySelector("vercel-live-feedback") && element.closest("[data-vercel-edit-info]") || element.closest("[data-vercel-edit-target]") || (hoverStack.push(element), handler({
type: "element/mouseenter",
id,
rect: getRect(element)
}), setOverlayCursor(element));
},
mouseleave(e) {
function leave() {
hoverStack.pop();
const hoveredElement = getHoveredElement();
if (handler({
type: "element/mouseleave",
id
}), hoveredElement) {
setOverlayCursor(hoveredElement);
const overlayElement2 = elementsMap.get(hoveredElement);
overlayElement2 && handler({
type: "element/mouseenter",
id: overlayElement2.id,
rect: getRect(hoveredElement)
});
}
restoreOverlayCursor(element);
}
function addDeferredLeave(el) {
const deferredLeave = (e2) => {
const {
relatedTarget: relatedTarget2
} = e2;
findOverlayElement(relatedTarget2) ? relatedTarget2 && isElementNode$1(relatedTarget2) && (el.removeEventListener("mouseleave", deferredLeave), addDeferredLeave(relatedTarget2)) : (el.removeEventListener("mouseleave", deferredLeave), leave());
};
el.addEventListener("mouseleave", deferredLeave);
}
const {
relatedTarget
} = e, container = findOverlayElement(relatedTarget), isInteractiveOverlayElement = overlayElement.contains(container);
if (isElementNode$1(container) && isInteractiveOverlayElement)
return addDeferredLeave(container);
leave();
}
}, id = v4(), sanityNode = {
type,
id,
elements,
sanity: commonSanity,
handlers: eventHandlers
};
elementSet.add(element), measureElements.set(measureElement, element), elementIdMap.set(id, element), elementsMap.set(element, sanityNode), io?.observe(element), handler({
type: "element/register",
elementType: type,
id,
element,
rect: getRect(element),
sanity: commonSanity,
dragDisabled: !!element.getAttribute("data-sanity-drag-disable"),
targets: targets.map((target) => ({
sanity: target.sanity,
element: target.elements.element
}))
}), activated && activateElement(sanityNode);
}
function updateElement(resolvedElement) {
const {
element
} = resolvedElement.elements, overlayElement2 = elementsMap.get(element);
overlayElement2 && (elementsMap.set(element, {
...overlayElement2,
sanity: resolvedElement.commonSanity
}), handler({
type: "element/update",
elementType: overlayElement2.type,
id: overlayElement2.id,
rect: getRect(element),
sanity: resolvedElement.commonSanity,
targets: resolvedElement.targets.map((target) => ({
sanity: target.sanity,
element: target.elements.element
}))
}));
}
function parseElements(node) {
const sanityNodes = findSanityNodes(node);
for (const sanityNode of sanityNodes) {
if (sanityNode.type === "group") {
for (const target of sanityNode.targets) {
const overlayElement2 = elementsMap.get(target.elements.element);
overlayElement2 && overlayElement2.type === "element" && unregisterElement(target.elements.element);
}
sanityNode.targets.length === 0 && unregisterElement(sanityNode.elements.element);
}
if (!sanityNode.commonSanity) continue;
const {
element
} = sanityNode.elements;
elementsMap.has(element) ? updateElement(sanityNode) : registerElement(sanityNode);
}
}
function unregisterElement(element) {
const overlayElement2 = elementsMap.get(element);
if (overlayElement2) {
const {
id,
handlers
} = overlayElement2;
removeEventHandlers(element, handlers), ro.unobserve(element), elementsMap.delete(element), elementSet.delete(element), elementIdMap.delete(id), handler({
type: "element/unregister",
id
});
}
}
function handleMutation(mutations2) {
let mutationWasInScope = !1;
for (const mutation of mutations2) {
const {
target,
type
} = mutation, node = type === "characterData" ? target.parentElement : target;
if (!(node === overlayElement || overlayElement.contains(node)) && (mutationWasInScope = !0, isElementNode$1(node))) {
const possibleGroupParent = node.parentElement?.closest("[data-sanity-edit-group]") || null, updateNodeTarget = isElementNode$1(possibleGroupParent) ? possibleGroupParent : node;
parseElements({
childNodes: [updateNodeTarget]
});
}
}
if (mutationWasInScope)
for (const element of elementSet)
element.isConnected || unregisterElement(element), elementsMap.get(element)?.type === "group" && !element.hasAttribute("data-sanity-edit-group") && unregisterElement(element);
}
function updateRect(el) {
const overlayElement2 = elementsMap.get(el);
overlayElement2 && handler({
type: "element/updateRect",
id: overlayElement2.id,
rect: getRect(el)
});
}
function handleResize(entries) {
for (const entry of entries) {
const target = entry.target;
if (isElementNode$1(target)) {
const element = measureElements.get(target);
if (!element) return;
updateRect(element);
}
}
}
function handleIntersection(entries) {
if (activated)
for (const entry of entries) {
const {
target
} = entry, match = isElementNode$1(target) && elementsMap.get(target);
match && (entry.isIntersecting ? activateElement(match) : deactivateElement(match));
}
}
function handleBlur(event) {
const element = findOverlayElement(event.target);
if (element) {
element.dataset.sanityOverlayElement === "capture" && (event.preventDefault(), event.stopPropagation());
return;
}
hoverStack = [], handler({
type: "overlay/blur"
});
}
function handleExclusivePluginClosed() {
hoverStack = [], handler({
type: "overlay/reset-mouse-state"
});
}
function handleWindowResize() {
for (const element of elementSet)
updateRect(element);
}
function handleKeydown(event) {
event.key === "Escape" && (hoverStack = [], handler({
type: "overlay/blur"
}));
}
function handleWindowScroll(event) {
const {
target
} = event;
if (!(target === window.document || !isElementNode$1(target)))
for (const element of elementSet)
target.contains(element) && updateRect(element);
}
function activate() {
activated || (io = new IntersectionObserver(handleIntersection, {
threshold: 0.3
}), elementSet.forEach((element) => io.observe(element)), handler({
type: "overlay/activate"
}), activated = !0);
}
function deactivate() {
activated && (io?.disconnect(), elementSet.forEach((element) => {
const overlayElement2 = elementsMap.get(element);
overlayElement2 && deactivateElement(overlayElement2);
}), handler({
type: "overlay/deactivate"
}), activated = !1);
}
function handleHeaderClick(event) {
const {
id
} = event.detail, element = elementIdMap.get(id);
if (!element) return;
const sanity = elementsMap.get(element)?.sanity;
sanity && handler({
type: "element/click",
id,
sanity
});
}
function destroy() {
window.removeEventListener("click", handleBlur), window.removeEventListener("contextmenu", handleBlur), window.removeEventListener("sanity-overlay/exclusive-plugin-closed", handleExclusivePluginClosed), window.removeEventListener("sanity-overlay/label-click", handleHeaderClick), window.removeEventListener("keydown", handleKeydown), window.removeEventListener("resize", handleWindowResize), window.removeEventListener("scroll", handleWindowScroll), mo.disconnect(), ro.disconnect(), elementSet.forEach((element) => {
unregisterElement(element);
}), elementIdMap.clear(), elementSet.clear(), hoverStack = [], deactivate();
}
function create() {
window.addEventListener("click", handleBlur), window.addEventListener("contextmenu", handleBlur), window.addEventListener("sanity-overlay/exclusive-plugin-closed", handleExclusivePluginClosed), window.addEventListener("sanity-overlay/label-click", handleHeaderClick), window.addEventListener("keydown", handleKeydown), window.addEventListener("resize", handleWindowResize), window.addEventListener("scroll", handleWindowScroll, {
capture: !0,
passive: !0
}), ro = new ResizeObserver(handleResize), mo = new MutationObserver(handleMutation), mo.observe(document.body, {
attributes: !0,
characterData: !0,
childList: !0,
subtree: !0
}), parseElements(document.body), activate();
}
return window.document.fonts.ready.then(() => {
for (const element of elementSet)
updateRect(element);
}), create(), {
activate,
deactivate,
destroy
};
}
const SharedStateContext = react.createContext(null);
exports.SharedStateContext = SharedStateContext;
exports.createOverlayController = createOverlayController;
exports.sanityNodesExistInSameArray = sanityNodesExistInSameArray;
exports.useOptimistic = useOptimistic;
exports.v4 = v4;
//# sourceMappingURL=SharedStateContext.cjs.map
;