first commit
This commit is contained in:
462
wp-includes/js/dist/script-modules/block-library/image/view.js
vendored
Normal file
462
wp-includes/js/dist/script-modules/block-library/image/view.js
vendored
Normal file
@@ -0,0 +1,462 @@
|
||||
// packages/block-library/build-module/image/view.mjs
|
||||
import {
|
||||
store,
|
||||
getContext,
|
||||
getElement,
|
||||
getConfig,
|
||||
withSyncEvent,
|
||||
withScope
|
||||
} from "@wordpress/interactivity";
|
||||
|
||||
// packages/block-library/build-module/image/constants.mjs
|
||||
var IMAGE_PRELOAD_DELAY = 200;
|
||||
|
||||
// packages/block-library/build-module/image/view.mjs
|
||||
var isTouching = false;
|
||||
var lastTouchTime = 0;
|
||||
var touchStartEvent = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
startTime: 0
|
||||
};
|
||||
var focusableSelectors = [
|
||||
".wp-lightbox-close-button",
|
||||
".wp-lightbox-navigation-button"
|
||||
];
|
||||
function getImageSrc({ uploadedSrc }) {
|
||||
return uploadedSrc || "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
|
||||
}
|
||||
function getImageSrcset({ lightboxSrcset }) {
|
||||
return lightboxSrcset || "";
|
||||
}
|
||||
var { state, actions, callbacks } = store(
|
||||
"core/image",
|
||||
{
|
||||
state: {
|
||||
selectedImageId: null,
|
||||
selectedGalleryId: null,
|
||||
preloadTimers: /* @__PURE__ */ new Map(),
|
||||
preloadedImageIds: /* @__PURE__ */ new Set(),
|
||||
get galleryImages() {
|
||||
if (!state.selectedGalleryId) {
|
||||
return [state.selectedImageId];
|
||||
}
|
||||
return Object.entries(state.metadata).filter(
|
||||
([, value]) => value.galleryId === state.selectedGalleryId
|
||||
).sort(([, a], [, b]) => {
|
||||
const orderA = a.order ?? 0;
|
||||
const orderB = b.order ?? 0;
|
||||
return orderA - orderB;
|
||||
}).map(([key]) => key);
|
||||
},
|
||||
get selectedImageIndex() {
|
||||
return state.galleryImages.findIndex(
|
||||
(id) => id === state.selectedImageId
|
||||
);
|
||||
},
|
||||
get selectedImage() {
|
||||
return state.metadata[state.selectedImageId];
|
||||
},
|
||||
get hasNavigationIcon() {
|
||||
const { navigationButtonType } = state.selectedImage;
|
||||
return navigationButtonType === "icon" || navigationButtonType === "both";
|
||||
},
|
||||
get hasNavigationText() {
|
||||
const { navigationButtonType } = state.selectedImage;
|
||||
return navigationButtonType === "text" || navigationButtonType === "both";
|
||||
},
|
||||
get thisImage() {
|
||||
const { imageId } = getContext();
|
||||
return state.metadata[imageId];
|
||||
},
|
||||
get hasNavigation() {
|
||||
return state.galleryImages.length > 1;
|
||||
},
|
||||
get hasNextImage() {
|
||||
return state.selectedImageIndex + 1 < state.galleryImages.length;
|
||||
},
|
||||
get hasPreviousImage() {
|
||||
return state.selectedImageIndex - 1 >= 0;
|
||||
},
|
||||
get overlayOpened() {
|
||||
return state.selectedImageId !== null;
|
||||
},
|
||||
get roleAttribute() {
|
||||
return state.overlayOpened ? "dialog" : null;
|
||||
},
|
||||
get ariaModal() {
|
||||
return state.overlayOpened ? "true" : null;
|
||||
},
|
||||
get ariaLabel() {
|
||||
return state.selectedImage.customAriaLabel || getConfig().defaultAriaLabel;
|
||||
},
|
||||
get closeButtonAriaLabel() {
|
||||
return state.hasNavigationText ? void 0 : getConfig().closeButtonText;
|
||||
},
|
||||
get prevButtonAriaLabel() {
|
||||
return state.hasNavigationText ? void 0 : getConfig().prevButtonText;
|
||||
},
|
||||
get nextButtonAriaLabel() {
|
||||
return state.hasNavigationText ? void 0 : getConfig().nextButtonText;
|
||||
},
|
||||
get enlargedSrc() {
|
||||
return getImageSrc(state.selectedImage);
|
||||
},
|
||||
get enlargedSrcset() {
|
||||
return getImageSrcset(state.selectedImage);
|
||||
},
|
||||
get figureStyles() {
|
||||
return state.overlayOpened && `${state.selectedImage.figureStyles?.replace(
|
||||
/margin[^;]*;?/g,
|
||||
""
|
||||
)};`;
|
||||
},
|
||||
get imgStyles() {
|
||||
return state.overlayOpened && `${state.selectedImage.imgStyles?.replace(
|
||||
/;$/,
|
||||
""
|
||||
)}; object-fit:cover;`;
|
||||
},
|
||||
get isContentHidden() {
|
||||
const ctx = getContext();
|
||||
return state.overlayEnabled && state.selectedImageId === ctx.imageId;
|
||||
},
|
||||
get isContentVisible() {
|
||||
const ctx = getContext();
|
||||
return !state.overlayEnabled && state.selectedImageId === ctx.imageId;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
showLightbox() {
|
||||
const { imageId } = getContext();
|
||||
if (!state.metadata[imageId].imageRef?.complete) {
|
||||
return;
|
||||
}
|
||||
state.scrollTopReset = document.documentElement.scrollTop;
|
||||
state.scrollLeftReset = document.documentElement.scrollLeft;
|
||||
state.selectedImageId = imageId;
|
||||
const { galleryId } = getContext("core/gallery") || {};
|
||||
state.selectedGalleryId = galleryId || null;
|
||||
state.overlayEnabled = true;
|
||||
callbacks.setOverlayStyles();
|
||||
},
|
||||
hideLightbox() {
|
||||
if (state.overlayEnabled) {
|
||||
state.overlayEnabled = false;
|
||||
setTimeout(function() {
|
||||
state.selectedImage.buttonRef.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
state.selectedImageId = null;
|
||||
state.selectedGalleryId = null;
|
||||
}, 450);
|
||||
}
|
||||
},
|
||||
showPreviousImage: withSyncEvent((event) => {
|
||||
event.stopPropagation();
|
||||
const nextIndex = state.hasPreviousImage ? state.selectedImageIndex - 1 : state.galleryImages.length - 1;
|
||||
state.selectedImageId = state.galleryImages[nextIndex];
|
||||
callbacks.setOverlayStyles();
|
||||
}),
|
||||
showNextImage: withSyncEvent((event) => {
|
||||
event.stopPropagation();
|
||||
const nextIndex = state.hasNextImage ? state.selectedImageIndex + 1 : 0;
|
||||
state.selectedImageId = state.galleryImages[nextIndex];
|
||||
callbacks.setOverlayStyles();
|
||||
}),
|
||||
handleKeydown: withSyncEvent((event) => {
|
||||
if (state.overlayEnabled) {
|
||||
if (event.key === "Escape") {
|
||||
actions.hideLightbox();
|
||||
} else if (event.key === "ArrowLeft") {
|
||||
actions.showPreviousImage(event);
|
||||
} else if (event.key === "ArrowRight") {
|
||||
actions.showNextImage(event);
|
||||
} else if (event.key === "Tab") {
|
||||
const focusableElements = Array.from(
|
||||
document.querySelectorAll(focusableSelectors)
|
||||
);
|
||||
const firstFocusableElement = focusableElements[0];
|
||||
const lastFocusableElement = focusableElements[focusableElements.length - 1];
|
||||
if (event.shiftKey && event.target === firstFocusableElement) {
|
||||
event.preventDefault();
|
||||
lastFocusableElement.focus();
|
||||
} else if (!event.shiftKey && event.target === lastFocusableElement) {
|
||||
event.preventDefault();
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
handleTouchMove: withSyncEvent((event) => {
|
||||
if (state.overlayEnabled) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}),
|
||||
handleTouchStart(event) {
|
||||
isTouching = true;
|
||||
const t = event.touches && event.touches[0];
|
||||
if (t) {
|
||||
touchStartEvent.startX = t.clientX;
|
||||
touchStartEvent.startY = t.clientY;
|
||||
touchStartEvent.startTime = Date.now();
|
||||
}
|
||||
},
|
||||
handleTouchEnd: withSyncEvent((event) => {
|
||||
const touchEndEvent = event.changedTouches && event.changedTouches[0] || event.touches && event.touches[0];
|
||||
const now = Date.now();
|
||||
if (touchEndEvent && state.overlayEnabled) {
|
||||
const deltaX = touchEndEvent.clientX - touchStartEvent.startX;
|
||||
const deltaY = touchEndEvent.clientY - touchStartEvent.startY;
|
||||
const absDeltaX = Math.abs(deltaX);
|
||||
const absDeltaY = Math.abs(deltaY);
|
||||
const elapsedMs = now - touchStartEvent.startTime;
|
||||
const isHorizontalSwipe = (
|
||||
// Swipe distance is greater than 50px
|
||||
absDeltaX > 50 && // Horizontal movement is much larger than the vertical movement
|
||||
absDeltaX > absDeltaY * 1.5 && // Fast action of less than 800ms
|
||||
elapsedMs < 800
|
||||
);
|
||||
if (isHorizontalSwipe) {
|
||||
event.preventDefault();
|
||||
if (deltaX < 0) {
|
||||
actions.showNextImage(event);
|
||||
} else {
|
||||
actions.showPreviousImage(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastTouchTime = now;
|
||||
isTouching = false;
|
||||
}),
|
||||
handleScroll() {
|
||||
if (state.overlayOpened) {
|
||||
if (!isTouching && Date.now() - lastTouchTime > 450) {
|
||||
window.scrollTo(
|
||||
state.scrollLeftReset,
|
||||
state.scrollTopReset
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
preloadImage() {
|
||||
const { imageId } = getContext();
|
||||
if (state.preloadedImageIds.has(imageId)) {
|
||||
return;
|
||||
}
|
||||
const imageMetadata = state.metadata[imageId];
|
||||
const imageLink = document.createElement("link");
|
||||
imageLink.rel = "preload";
|
||||
imageLink.as = "image";
|
||||
imageLink.href = getImageSrc(imageMetadata);
|
||||
const srcset = getImageSrcset(imageMetadata);
|
||||
if (srcset) {
|
||||
imageLink.setAttribute("imagesrcset", srcset);
|
||||
imageLink.setAttribute("imagesizes", "100vw");
|
||||
}
|
||||
document.head.appendChild(imageLink);
|
||||
state.preloadedImageIds.add(imageId);
|
||||
},
|
||||
preloadImageWithDelay() {
|
||||
const { imageId } = getContext();
|
||||
actions.cancelPreload();
|
||||
const timerId = setTimeout(
|
||||
withScope(() => {
|
||||
actions.preloadImage();
|
||||
state.preloadTimers.delete(imageId);
|
||||
}),
|
||||
IMAGE_PRELOAD_DELAY
|
||||
);
|
||||
state.preloadTimers.set(imageId, timerId);
|
||||
},
|
||||
cancelPreload() {
|
||||
const { imageId } = getContext();
|
||||
if (state.preloadTimers.has(imageId)) {
|
||||
clearTimeout(state.preloadTimers.get(imageId));
|
||||
state.preloadTimers.delete(imageId);
|
||||
}
|
||||
}
|
||||
},
|
||||
callbacks: {
|
||||
setOverlayStyles() {
|
||||
if (!state.overlayEnabled) {
|
||||
return;
|
||||
}
|
||||
let {
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
offsetWidth: originalWidth,
|
||||
offsetHeight: originalHeight
|
||||
} = state.selectedImage.imageRef;
|
||||
let { x: screenPosX, y: screenPosY } = state.selectedImage.imageRef.getBoundingClientRect();
|
||||
const naturalRatio = naturalWidth / naturalHeight;
|
||||
let originalRatio = originalWidth / originalHeight;
|
||||
if (state.selectedImage.scaleAttr === "contain") {
|
||||
if (naturalRatio > originalRatio) {
|
||||
const heightWithoutSpace = originalWidth / naturalRatio;
|
||||
screenPosY += (originalHeight - heightWithoutSpace) / 2;
|
||||
originalHeight = heightWithoutSpace;
|
||||
} else {
|
||||
const widthWithoutSpace = originalHeight * naturalRatio;
|
||||
screenPosX += (originalWidth - widthWithoutSpace) / 2;
|
||||
originalWidth = widthWithoutSpace;
|
||||
}
|
||||
}
|
||||
originalRatio = originalWidth / originalHeight;
|
||||
let imgMaxWidth = parseFloat(
|
||||
state.selectedImage.targetWidth && state.selectedImage.targetWidth !== "none" ? state.selectedImage.targetWidth : naturalWidth
|
||||
);
|
||||
let imgMaxHeight = parseFloat(
|
||||
state.selectedImage.targetHeight && state.selectedImage.targetHeight !== "none" ? state.selectedImage.targetHeight : naturalHeight
|
||||
);
|
||||
let imgRatio = imgMaxWidth / imgMaxHeight;
|
||||
let containerMaxWidth = imgMaxWidth;
|
||||
let containerMaxHeight = imgMaxHeight;
|
||||
let containerWidth = imgMaxWidth;
|
||||
let containerHeight = imgMaxHeight;
|
||||
if (naturalRatio.toFixed(2) !== imgRatio.toFixed(2)) {
|
||||
if (naturalRatio > imgRatio) {
|
||||
const reducedHeight = imgMaxWidth / naturalRatio;
|
||||
if (imgMaxHeight - reducedHeight > imgMaxWidth) {
|
||||
imgMaxHeight = reducedHeight;
|
||||
imgMaxWidth = reducedHeight * naturalRatio;
|
||||
} else {
|
||||
imgMaxHeight = imgMaxWidth / naturalRatio;
|
||||
}
|
||||
} else {
|
||||
const reducedWidth = imgMaxHeight * naturalRatio;
|
||||
if (imgMaxWidth - reducedWidth > imgMaxHeight) {
|
||||
imgMaxWidth = reducedWidth;
|
||||
imgMaxHeight = reducedWidth / naturalRatio;
|
||||
} else {
|
||||
imgMaxWidth = imgMaxHeight * naturalRatio;
|
||||
}
|
||||
}
|
||||
containerWidth = imgMaxWidth;
|
||||
containerHeight = imgMaxHeight;
|
||||
imgRatio = imgMaxWidth / imgMaxHeight;
|
||||
if (originalRatio > imgRatio) {
|
||||
containerMaxWidth = imgMaxWidth;
|
||||
containerMaxHeight = containerMaxWidth / originalRatio;
|
||||
} else {
|
||||
containerMaxHeight = imgMaxHeight;
|
||||
containerMaxWidth = containerMaxHeight * originalRatio;
|
||||
}
|
||||
}
|
||||
if (originalWidth > containerWidth || originalHeight > containerHeight) {
|
||||
containerWidth = originalWidth;
|
||||
containerHeight = originalHeight;
|
||||
}
|
||||
let horizontalPadding = 0;
|
||||
let verticalPadding = 160;
|
||||
if (480 < window.innerWidth) {
|
||||
horizontalPadding = 80;
|
||||
verticalPadding = 160;
|
||||
}
|
||||
if (960 < window.innerWidth) {
|
||||
horizontalPadding = state.hasNavigation ? 320 : 80;
|
||||
verticalPadding = 80;
|
||||
}
|
||||
const targetMaxWidth = Math.min(
|
||||
window.innerWidth - horizontalPadding,
|
||||
containerWidth
|
||||
);
|
||||
const targetMaxHeight = Math.min(
|
||||
window.innerHeight - verticalPadding,
|
||||
containerHeight
|
||||
);
|
||||
const targetContainerRatio = targetMaxWidth / targetMaxHeight;
|
||||
if (originalRatio > targetContainerRatio) {
|
||||
containerWidth = targetMaxWidth;
|
||||
containerHeight = containerWidth / originalRatio;
|
||||
} else {
|
||||
containerHeight = targetMaxHeight;
|
||||
containerWidth = containerHeight * originalRatio;
|
||||
}
|
||||
const containerScale = originalWidth / containerWidth;
|
||||
const lightboxImgWidth = imgMaxWidth * (containerWidth / containerMaxWidth);
|
||||
const lightboxImgHeight = imgMaxHeight * (containerHeight / containerMaxHeight);
|
||||
state.overlayStyles = `
|
||||
--wp--lightbox-initial-top-position: ${screenPosY}px;
|
||||
--wp--lightbox-initial-left-position: ${screenPosX}px;
|
||||
--wp--lightbox-container-width: ${containerWidth + 1}px;
|
||||
--wp--lightbox-container-height: ${containerHeight + 1}px;
|
||||
--wp--lightbox-image-width: ${lightboxImgWidth}px;
|
||||
--wp--lightbox-image-height: ${lightboxImgHeight}px;
|
||||
--wp--lightbox-scale: ${containerScale};
|
||||
--wp--lightbox-scrollbar-width: ${window.innerWidth - document.documentElement.clientWidth}px;
|
||||
`;
|
||||
},
|
||||
setButtonStyles() {
|
||||
const { ref } = getElement();
|
||||
if (!ref) {
|
||||
return;
|
||||
}
|
||||
const { imageId } = getContext();
|
||||
state.metadata[imageId].imageRef = ref;
|
||||
state.metadata[imageId].currentSrc = ref.currentSrc;
|
||||
const {
|
||||
naturalWidth,
|
||||
naturalHeight,
|
||||
offsetWidth,
|
||||
offsetHeight
|
||||
} = ref;
|
||||
if (naturalWidth === 0 || naturalHeight === 0) {
|
||||
return;
|
||||
}
|
||||
const figure = ref.parentElement;
|
||||
const figureWidth = ref.parentElement.clientWidth;
|
||||
let figureHeight = ref.parentElement.clientHeight;
|
||||
const caption = figure.querySelector("figcaption");
|
||||
if (caption) {
|
||||
const captionComputedStyle = window.getComputedStyle(caption);
|
||||
if (!["absolute", "fixed"].includes(
|
||||
captionComputedStyle.position
|
||||
)) {
|
||||
figureHeight = figureHeight - caption.offsetHeight - parseFloat(captionComputedStyle.marginTop) - parseFloat(captionComputedStyle.marginBottom);
|
||||
}
|
||||
}
|
||||
const buttonOffsetTop = figureHeight - offsetHeight;
|
||||
const buttonOffsetRight = figureWidth - offsetWidth;
|
||||
let buttonTop = buttonOffsetTop + 16;
|
||||
let buttonRight = buttonOffsetRight + 16;
|
||||
if (state.metadata[imageId].scaleAttr === "contain") {
|
||||
const naturalRatio = naturalWidth / naturalHeight;
|
||||
const offsetRatio = offsetWidth / offsetHeight;
|
||||
if (naturalRatio >= offsetRatio) {
|
||||
const referenceHeight = offsetWidth / naturalRatio;
|
||||
buttonTop = (offsetHeight - referenceHeight) / 2 + buttonOffsetTop + 16;
|
||||
buttonRight = buttonOffsetRight + 16;
|
||||
} else {
|
||||
const referenceWidth = offsetHeight * naturalRatio;
|
||||
buttonTop = buttonOffsetTop + 16;
|
||||
buttonRight = (offsetWidth - referenceWidth) / 2 + buttonOffsetRight + 16;
|
||||
}
|
||||
}
|
||||
state.metadata[imageId].buttonTop = buttonTop;
|
||||
state.metadata[imageId].buttonRight = buttonRight;
|
||||
},
|
||||
setOverlayFocus() {
|
||||
if (state.overlayEnabled) {
|
||||
const { ref } = getElement();
|
||||
ref.focus();
|
||||
}
|
||||
},
|
||||
setInertElements() {
|
||||
document.querySelectorAll("body > :not(.wp-lightbox-overlay)").forEach((el) => {
|
||||
if (state.overlayEnabled) {
|
||||
el.setAttribute("inert", "");
|
||||
} else {
|
||||
el.removeAttribute("inert");
|
||||
}
|
||||
});
|
||||
},
|
||||
initTriggerButton() {
|
||||
const { imageId } = getContext();
|
||||
const { ref } = getElement();
|
||||
state.metadata[imageId].buttonRef = ref;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ lock: true }
|
||||
);
|
||||
Reference in New Issue
Block a user