| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 // packages/block-library/build-module/image/view.mjs 2 import { 3 store, 4 getContext, 5 getElement, 6 getConfig, 7 withSyncEvent, 8 withScope 9 } from "@wordpress/interactivity"; 10 11 // packages/block-library/build-module/image/constants.mjs 12 var IMAGE_PRELOAD_DELAY = 200; 13 14 // packages/block-library/build-module/image/view.mjs 15 var isTouching = false; 16 var lastTouchTime = 0; 17 var touchStartEvent = { 18 startX: 0, 19 startY: 0, 20 startTime: 0 21 }; 22 var focusableSelectors = [ 23 ".wp-lightbox-close-button", 24 ".wp-lightbox-navigation-button" 25 ]; 26 function getImageSrc({ uploadedSrc }) { 27 return uploadedSrc || "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="; 28 } 29 function getImageSrcset({ lightboxSrcset }) { 30 return lightboxSrcset || ""; 31 } 32 var { state, actions, callbacks } = store( 33 "core/image", 34 { 35 state: { 36 selectedImageId: null, 37 selectedGalleryId: null, 38 preloadTimers: /* @__PURE__ */ new Map(), 39 preloadedImageIds: /* @__PURE__ */ new Set(), 40 get galleryImages() { 41 if (!state.selectedGalleryId) { 42 return [state.selectedImageId]; 43 } 44 return Object.entries(state.metadata).filter( 45 ([, value]) => value.galleryId === state.selectedGalleryId 46 ).sort(([, a], [, b]) => { 47 const orderA = a.order ?? 0; 48 const orderB = b.order ?? 0; 49 return orderA - orderB; 50 }).map(([key]) => key); 51 }, 52 get selectedImageIndex() { 53 return state.galleryImages.findIndex( 54 (id) => id === state.selectedImageId 55 ); 56 }, 57 get selectedImage() { 58 return state.metadata[state.selectedImageId]; 59 }, 60 get hasNavigationIcon() { 61 const { navigationButtonType } = state.selectedImage; 62 return navigationButtonType === "icon" || navigationButtonType === "both"; 63 }, 64 get hasNavigationText() { 65 const { navigationButtonType } = state.selectedImage; 66 return navigationButtonType === "text" || navigationButtonType === "both"; 67 }, 68 get thisImage() { 69 const { imageId } = getContext(); 70 return state.metadata[imageId]; 71 }, 72 get hasNavigation() { 73 return state.galleryImages.length > 1; 74 }, 75 get hasNextImage() { 76 return state.selectedImageIndex + 1 < state.galleryImages.length; 77 }, 78 get hasPreviousImage() { 79 return state.selectedImageIndex - 1 >= 0; 80 }, 81 get overlayOpened() { 82 return state.selectedImageId !== null; 83 }, 84 get roleAttribute() { 85 return state.overlayOpened ? "dialog" : null; 86 }, 87 get ariaModal() { 88 return state.overlayOpened ? "true" : null; 89 }, 90 get ariaLabel() { 91 return state.selectedImage.customAriaLabel || getConfig().defaultAriaLabel; 92 }, 93 get closeButtonAriaLabel() { 94 return state.hasNavigationText ? void 0 : getConfig().closeButtonText; 95 }, 96 get prevButtonAriaLabel() { 97 return state.hasNavigationText ? void 0 : getConfig().prevButtonText; 98 }, 99 get nextButtonAriaLabel() { 100 return state.hasNavigationText ? void 0 : getConfig().nextButtonText; 101 }, 102 get enlargedSrc() { 103 return getImageSrc(state.selectedImage); 104 }, 105 get enlargedSrcset() { 106 return getImageSrcset(state.selectedImage); 107 }, 108 get figureStyles() { 109 return state.overlayOpened && `$state.selectedImage.figureStyles?.replace( 110 /margin[^;]*;?/g, 111 "" 112 )};`; 113 }, 114 get imgStyles() { 115 return state.overlayOpened && `$state.selectedImage.imgStyles?.replace( 116 /;$/, 117 "" 118 )}; object-fit:cover;`; 119 }, 120 get isContentHidden() { 121 const ctx = getContext(); 122 return state.overlayEnabled && state.selectedImageId === ctx.imageId; 123 }, 124 get isContentVisible() { 125 const ctx = getContext(); 126 return !state.overlayEnabled && state.selectedImageId === ctx.imageId; 127 } 128 }, 129 actions: { 130 showLightbox() { 131 const { imageId } = getContext(); 132 if (!state.metadata[imageId].imageRef?.complete) { 133 return; 134 } 135 state.scrollTopReset = document.documentElement.scrollTop; 136 state.scrollLeftReset = document.documentElement.scrollLeft; 137 state.selectedImageId = imageId; 138 const { galleryId } = getContext("core/gallery") || {}; 139 state.selectedGalleryId = galleryId || null; 140 state.overlayEnabled = true; 141 callbacks.setOverlayStyles(); 142 }, 143 hideLightbox() { 144 if (state.overlayEnabled) { 145 state.overlayEnabled = false; 146 setTimeout(function() { 147 state.selectedImage.buttonRef.focus({ 148 preventScroll: true 149 }); 150 state.selectedImageId = null; 151 state.selectedGalleryId = null; 152 }, 450); 153 } 154 }, 155 showPreviousImage: withSyncEvent((event) => { 156 event.stopPropagation(); 157 const nextIndex = state.hasPreviousImage ? state.selectedImageIndex - 1 : state.galleryImages.length - 1; 158 state.selectedImageId = state.galleryImages[nextIndex]; 159 callbacks.setOverlayStyles(); 160 }), 161 showNextImage: withSyncEvent((event) => { 162 event.stopPropagation(); 163 const nextIndex = state.hasNextImage ? state.selectedImageIndex + 1 : 0; 164 state.selectedImageId = state.galleryImages[nextIndex]; 165 callbacks.setOverlayStyles(); 166 }), 167 handleKeydown: withSyncEvent((event) => { 168 if (state.overlayEnabled) { 169 if (event.key === "Escape") { 170 actions.hideLightbox(); 171 } else if (event.key === "ArrowLeft") { 172 actions.showPreviousImage(event); 173 } else if (event.key === "ArrowRight") { 174 actions.showNextImage(event); 175 } else if (event.key === "Tab") { 176 const focusableElements = Array.from( 177 document.querySelectorAll(focusableSelectors) 178 ); 179 const firstFocusableElement = focusableElements[0]; 180 const lastFocusableElement = focusableElements[focusableElements.length - 1]; 181 if (event.shiftKey && event.target === firstFocusableElement) { 182 event.preventDefault(); 183 lastFocusableElement.focus(); 184 } else if (!event.shiftKey && event.target === lastFocusableElement) { 185 event.preventDefault(); 186 firstFocusableElement.focus(); 187 } 188 } 189 } 190 }), 191 handleTouchMove: withSyncEvent((event) => { 192 if (state.overlayEnabled) { 193 event.preventDefault(); 194 } 195 }), 196 handleTouchStart(event) { 197 isTouching = true; 198 const t = event.touches && event.touches[0]; 199 if (t) { 200 touchStartEvent.startX = t.clientX; 201 touchStartEvent.startY = t.clientY; 202 touchStartEvent.startTime = Date.now(); 203 } 204 }, 205 handleTouchEnd: withSyncEvent((event) => { 206 const touchEndEvent = event.changedTouches && event.changedTouches[0] || event.touches && event.touches[0]; 207 const now = Date.now(); 208 if (touchEndEvent && state.overlayEnabled) { 209 const deltaX = touchEndEvent.clientX - touchStartEvent.startX; 210 const deltaY = touchEndEvent.clientY - touchStartEvent.startY; 211 const absDeltaX = Math.abs(deltaX); 212 const absDeltaY = Math.abs(deltaY); 213 const elapsedMs = now - touchStartEvent.startTime; 214 const isHorizontalSwipe = ( 215 // Swipe distance is greater than 50px 216 absDeltaX > 50 && // Horizontal movement is much larger than the vertical movement 217 absDeltaX > absDeltaY * 1.5 && // Fast action of less than 800ms 218 elapsedMs < 800 219 ); 220 if (isHorizontalSwipe) { 221 event.preventDefault(); 222 if (deltaX < 0) { 223 actions.showNextImage(event); 224 } else { 225 actions.showPreviousImage(event); 226 } 227 } 228 } 229 lastTouchTime = now; 230 isTouching = false; 231 }), 232 handleScroll() { 233 if (state.overlayOpened) { 234 if (!isTouching && Date.now() - lastTouchTime > 450) { 235 window.scrollTo( 236 state.scrollLeftReset, 237 state.scrollTopReset 238 ); 239 } 240 } 241 }, 242 preloadImage() { 243 const { imageId } = getContext(); 244 if (state.preloadedImageIds.has(imageId)) { 245 return; 246 } 247 const imageMetadata = state.metadata[imageId]; 248 const imageLink = document.createElement("link"); 249 imageLink.rel = "preload"; 250 imageLink.as = "image"; 251 imageLink.href = getImageSrc(imageMetadata); 252 const srcset = getImageSrcset(imageMetadata); 253 if (srcset) { 254 imageLink.setAttribute("imagesrcset", srcset); 255 imageLink.setAttribute("imagesizes", "100vw"); 256 } 257 document.head.appendChild(imageLink); 258 state.preloadedImageIds.add(imageId); 259 }, 260 preloadImageWithDelay() { 261 const { imageId } = getContext(); 262 actions.cancelPreload(); 263 const timerId = setTimeout( 264 withScope(() => { 265 actions.preloadImage(); 266 state.preloadTimers.delete(imageId); 267 }), 268 IMAGE_PRELOAD_DELAY 269 ); 270 state.preloadTimers.set(imageId, timerId); 271 }, 272 cancelPreload() { 273 const { imageId } = getContext(); 274 if (state.preloadTimers.has(imageId)) { 275 clearTimeout(state.preloadTimers.get(imageId)); 276 state.preloadTimers.delete(imageId); 277 } 278 } 279 }, 280 callbacks: { 281 setOverlayStyles() { 282 if (!state.overlayEnabled) { 283 return; 284 } 285 let { 286 naturalWidth, 287 naturalHeight, 288 offsetWidth: originalWidth, 289 offsetHeight: originalHeight 290 } = state.selectedImage.imageRef; 291 let { x: screenPosX, y: screenPosY } = state.selectedImage.imageRef.getBoundingClientRect(); 292 const naturalRatio = naturalWidth / naturalHeight; 293 let originalRatio = originalWidth / originalHeight; 294 if (state.selectedImage.scaleAttr === "contain") { 295 if (naturalRatio > originalRatio) { 296 const heightWithoutSpace = originalWidth / naturalRatio; 297 screenPosY += (originalHeight - heightWithoutSpace) / 2; 298 originalHeight = heightWithoutSpace; 299 } else { 300 const widthWithoutSpace = originalHeight * naturalRatio; 301 screenPosX += (originalWidth - widthWithoutSpace) / 2; 302 originalWidth = widthWithoutSpace; 303 } 304 } 305 originalRatio = originalWidth / originalHeight; 306 let imgMaxWidth = parseFloat( 307 state.selectedImage.targetWidth && state.selectedImage.targetWidth !== "none" ? state.selectedImage.targetWidth : naturalWidth 308 ); 309 let imgMaxHeight = parseFloat( 310 state.selectedImage.targetHeight && state.selectedImage.targetHeight !== "none" ? state.selectedImage.targetHeight : naturalHeight 311 ); 312 let imgRatio = imgMaxWidth / imgMaxHeight; 313 let containerMaxWidth = imgMaxWidth; 314 let containerMaxHeight = imgMaxHeight; 315 let containerWidth = imgMaxWidth; 316 let containerHeight = imgMaxHeight; 317 if (naturalRatio.toFixed(2) !== imgRatio.toFixed(2)) { 318 if (naturalRatio > imgRatio) { 319 const reducedHeight = imgMaxWidth / naturalRatio; 320 if (imgMaxHeight - reducedHeight > imgMaxWidth) { 321 imgMaxHeight = reducedHeight; 322 imgMaxWidth = reducedHeight * naturalRatio; 323 } else { 324 imgMaxHeight = imgMaxWidth / naturalRatio; 325 } 326 } else { 327 const reducedWidth = imgMaxHeight * naturalRatio; 328 if (imgMaxWidth - reducedWidth > imgMaxHeight) { 329 imgMaxWidth = reducedWidth; 330 imgMaxHeight = reducedWidth / naturalRatio; 331 } else { 332 imgMaxWidth = imgMaxHeight * naturalRatio; 333 } 334 } 335 containerWidth = imgMaxWidth; 336 containerHeight = imgMaxHeight; 337 imgRatio = imgMaxWidth / imgMaxHeight; 338 if (originalRatio > imgRatio) { 339 containerMaxWidth = imgMaxWidth; 340 containerMaxHeight = containerMaxWidth / originalRatio; 341 } else { 342 containerMaxHeight = imgMaxHeight; 343 containerMaxWidth = containerMaxHeight * originalRatio; 344 } 345 } 346 if (originalWidth > containerWidth || originalHeight > containerHeight) { 347 containerWidth = originalWidth; 348 containerHeight = originalHeight; 349 } 350 let horizontalPadding = 0; 351 let verticalPadding = 160; 352 if (480 < window.innerWidth) { 353 horizontalPadding = 80; 354 verticalPadding = 160; 355 } 356 if (960 < window.innerWidth) { 357 horizontalPadding = state.hasNavigation ? 320 : 80; 358 verticalPadding = 80; 359 } 360 const targetMaxWidth = Math.min( 361 window.innerWidth - horizontalPadding, 362 containerWidth 363 ); 364 const targetMaxHeight = Math.min( 365 window.innerHeight - verticalPadding, 366 containerHeight 367 ); 368 const targetContainerRatio = targetMaxWidth / targetMaxHeight; 369 if (originalRatio > targetContainerRatio) { 370 containerWidth = targetMaxWidth; 371 containerHeight = containerWidth / originalRatio; 372 } else { 373 containerHeight = targetMaxHeight; 374 containerWidth = containerHeight * originalRatio; 375 } 376 const containerScale = originalWidth / containerWidth; 377 const lightboxImgWidth = imgMaxWidth * (containerWidth / containerMaxWidth); 378 const lightboxImgHeight = imgMaxHeight * (containerHeight / containerMaxHeight); 379 state.overlayStyles = ` 380 --wp--lightbox-initial-top-position: $screenPosY}px; 381 --wp--lightbox-initial-left-position: $screenPosX}px; 382 --wp--lightbox-container-width: $containerWidth + 1}px; 383 --wp--lightbox-container-height: $containerHeight + 1}px; 384 --wp--lightbox-image-width: $lightboxImgWidth}px; 385 --wp--lightbox-image-height: $lightboxImgHeight}px; 386 --wp--lightbox-scale: $containerScale}; 387 --wp--lightbox-scrollbar-width: $window.innerWidth - document.documentElement.clientWidth}px; 388 `; 389 }, 390 setButtonStyles() { 391 const { ref } = getElement(); 392 if (!ref) { 393 return; 394 } 395 const { imageId } = getContext(); 396 state.metadata[imageId].imageRef = ref; 397 state.metadata[imageId].currentSrc = ref.currentSrc; 398 const { 399 naturalWidth, 400 naturalHeight, 401 offsetWidth, 402 offsetHeight 403 } = ref; 404 if (naturalWidth === 0 || naturalHeight === 0) { 405 return; 406 } 407 const figure = ref.parentElement; 408 const figureWidth = ref.parentElement.clientWidth; 409 let figureHeight = ref.parentElement.clientHeight; 410 const caption = figure.querySelector("figcaption"); 411 if (caption) { 412 const captionComputedStyle = window.getComputedStyle(caption); 413 if (!["absolute", "fixed"].includes( 414 captionComputedStyle.position 415 )) { 416 figureHeight = figureHeight - caption.offsetHeight - parseFloat(captionComputedStyle.marginTop) - parseFloat(captionComputedStyle.marginBottom); 417 } 418 } 419 const buttonOffsetTop = figureHeight - offsetHeight; 420 const buttonOffsetRight = figureWidth - offsetWidth; 421 let buttonTop = buttonOffsetTop + 16; 422 let buttonRight = buttonOffsetRight + 16; 423 if (state.metadata[imageId].scaleAttr === "contain") { 424 const naturalRatio = naturalWidth / naturalHeight; 425 const offsetRatio = offsetWidth / offsetHeight; 426 if (naturalRatio >= offsetRatio) { 427 const referenceHeight = offsetWidth / naturalRatio; 428 buttonTop = (offsetHeight - referenceHeight) / 2 + buttonOffsetTop + 16; 429 buttonRight = buttonOffsetRight + 16; 430 } else { 431 const referenceWidth = offsetHeight * naturalRatio; 432 buttonTop = buttonOffsetTop + 16; 433 buttonRight = (offsetWidth - referenceWidth) / 2 + buttonOffsetRight + 16; 434 } 435 } 436 state.metadata[imageId].buttonTop = buttonTop; 437 state.metadata[imageId].buttonRight = buttonRight; 438 }, 439 setOverlayFocus() { 440 if (state.overlayEnabled) { 441 const { ref } = getElement(); 442 ref.focus(); 443 } 444 }, 445 setInertElements() { 446 document.querySelectorAll("body > :not(.wp-lightbox-overlay)").forEach((el) => { 447 if (state.overlayEnabled) { 448 el.setAttribute("inert", ""); 449 } else { 450 el.removeAttribute("inert"); 451 } 452 }); 453 }, 454 initTriggerButton() { 455 const { imageId } = getContext(); 456 const { ref } = getElement(); 457 state.metadata[imageId].buttonRef = ref; 458 } 459 } 460 }, 461 { lock: true } 462 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |