From e9967fa22781d922ba4eb8fb44fe72d200ac4b14 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 10 Nov 2025 21:04:10 +0000
Subject: [PATCH] =IconsManager.php update
---
assets/js/concise/Media.js | 235 +++++++++++++++-------------------------------------------
1 files changed, 63 insertions(+), 172 deletions(-)
diff --git a/assets/js/concise/Media.js b/assets/js/concise/Media.js
index 3f733a0..ab20c7f 100644
--- a/assets/js/concise/Media.js
+++ b/assets/js/concise/Media.js
@@ -1,207 +1,98 @@
-/*
-SITE GALLERY MANAGER
-Handles two main functions:
-
-1. Responsive Images
-- Automatically loads appropriate image sizes based on screen width
-- Listens for resize events and updates images accordingly
-- Uses data-small, data-medium, and data-full attributes on images
-
-2. Gallery Management
-- HTML Structure:
- <a class="open-gallery" data-opens="gallery-id" data-focus="image-to-show">
- <figure><img...></figure>
- </a>
-
- <dialog class="gallery" id="gallery-id">
- - Main image display (.images with multiple <figure>s)
- - Navigation bar on bottom (nav.thumbnails) with:
- - Previous/Next buttons
- - Thumbnail list (toggles with .open class)
- - Expand button (toggles .expanded class)
- - Visibility toggle (toggles .hide-info class)
- - Close button
- </dialog>
-
-Key Features:
-- Opens in modal dialog with keyboard navigation
-- Manages thumbnail navigation state
-- Handles responsive image loading
-- Uses class-based state management (open, expanded, hide-info)
-- All event handling through delegation
-- Built on UIHandler base class for state management
-
-State Management:
-- Dialog: open/closed
-- Thumbnails: collapsed/open/expanded
-- Info panel: visible/hidden
-- Active image: tracked with .focused class
-*/
class Media {
constructor() {
this.currentWidth = window.innerWidth;
- this.initElements();
- if (this.images.length === 0) {
- return;
- }
- this.store = new window.jvbStore({
- name: 'images',
- TTL: 604800
- });
- this.touch = {
- x: null,
- y: null
- }
- this.a11y = window.jvbA11y;
- this.debouncer = window.debouncer;
- this.isTouching = false;
+ this.images = document.querySelectorAll('.wp-site-blocks img[data-small]');
+
+ if (this.images.length === 0) return;
+
+ // Immediately load visible images
+ this.loadVisibleImages();
+
this.initListeners();
- document.addEventListener('beforeunload', this.cleanup);
}
- initElements(){
- this.images = document.querySelectorAll('.wp-site-blocks img');
- this.gallery = document.querySelector('dialog.gallery');
- this.modal = new window.jvbModal(this.gallery, {
- openMessage: 'Opened Gallery',
- closeMessage: 'Closed Gallery',
- open: '.open-gallery',
- close: '.close'
- });
+ loadVisibleImages() {
+ // Load first image immediately, plus any in viewport
+ this.images.forEach((img, index) => {
+ const rect = img.getBoundingClientRect();
+ const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
- this.modal.subscribe((event, data) => {
- if (event === 'modal-open') {
- this.openGallery();
- } else if (event === 'modal-close') {
- this.closeGallery();
+ // Always load first image, or if currently visible
+ if (index === 0 || isVisible) {
+ this.loadAppropriateImage(img);
+ img.dataset.loaded = 'true'; // Mark so we don't observe it
}
});
}
initListeners() {
this.resizeHandler = this.handleResize.bind(this);
- this.clickHandler = this.handleClick.bind(this);
- this.keysHandler = this.handleKeys.bind(this);
- this.touchStartHandler = this.handleTouchStart.bind(this);
- this.touchEndHandler = this.handleTouchEnd.bind(this);
-
- document.addEventListener('click', this.clickHandler);
window.addEventListener('resize', this.resizeHandler);
- console.log('window hash: ',window.location.hash);
- let target = document.querySelector(window.location.hash);
- if (target && target.tagName === 'LI') {
- let trigger = target.querySelector('.open-gallery');
- if (trigger) {
- this.openGallery(trigger);
- }
- }
- this.observer = new IntersectionObserver((entries) =>{
+ // Only observe images that weren't immediately loaded
+ this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
- let img = entry.target;
- if (!img.closest('dialog')) {
- this.loadAppropriateImage(img);
- this.observer.unobserve(img);
- }
+ this.loadAppropriateImage(entry.target);
+ this.observer.unobserve(entry.target);
}
- })
+ });
}, {
- root: null,
rootMargin: '50px',
- threshold: .1
+ threshold: 0.1
});
+
this.images.forEach(img => {
- if (!img.closest('dialog')) {
+ if (!img.dataset.loaded) {
this.observer.observe(img);
}
- })
+ });
}
- initTouchHandling() {
- this.isTouching = true;
- this.touch.x = 0;
- this.touch.y = 0;
-
- this.gallery.addEventListener('touchstart', this.touchStartHandler);
- this.gallery.addEventListener('touchend', this.touchEndHandler);
- }
- cancelTouchHandling() {
- this.isTouching = false;
- this.gallery.removeEventListener('touchstart', this.touchStartHandler);
- this.gallery.removeEventListener('touchend', this.touchEndHandler);
- }
-
- handleTouchStart(e) {
- this.touch.x = e.touches[0].clientX;
- this.touch.y = e.touches[0].clientY;
- }
- handleTouchEnd(e) {
- const diffX = e.changedTouches[0].clientX - this.touch.x;
- const diffY = e.changedTouches[0].clientY - this.touch.y;
-
- if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) {
- this.navigateImages({target: this.gallery}, diffX < 0 ? 'next' : 'previous');
- }
- }
- handleResize(e) {
- window.debouncer.schedule(
- 'resize',
- ()=> {
- const currentWidth = window.innerWidth;
- if (Math.abs(currentWidth - this.currentWidth) > 100) {
- this.currentWidth = currentWidth;
- this.handleImageResize();
- }
- },
- 150
- );
- }
- handleClick(e) {
-
- }
- handleKeys(e) {
- //Escape handled by Modal.js
- if (e.key === 'Tab') {
- if (e.shiftKey) {
-
- } else {
-
+ handleResize() {
+ window.debouncer.schedule('image-resize', () => {
+ const newWidth = window.innerWidth;
+ if (Math.abs(newWidth - this.currentWidth) > 100) {
+ this.currentWidth = newWidth;
+ this.updateVisibleImages();
}
+ }, 150);
+ }
+
+ updateVisibleImages() {
+ this.images.forEach(img => {
+ const rect = img.getBoundingClientRect();
+ if (rect.top < window.innerHeight && rect.bottom > 0) {
+ this.loadAppropriateImage(img, true);
+ }
+ });
+ }
+
+ loadAppropriateImage(img, forceUpdate = false) {
+ const targetSize = this.getTargetSize();
+ const newSrc = img.dataset[targetSize];
+
+ if (newSrc && (forceUpdate || newSrc !== img.currentSrc)) {
+ img.src = newSrc;
}
}
- openGallery(target = null) {
- document.addEventListener('keydown', this.clickHandler);
- const id = target.dataset.opens;
- this.initTouchHandling();
- let focus;
- if (!target) {
- focus = this.images[0];
- } else {
- focus = target.dataset.focus;
- }
- if (focus) {
- this.updateImage(this.gallery.querySelector(`#${focus}`));
- }
- }
-
- closeGallery() {
- this.cancelTouchHandling();
- document.removeEventListener('keydown', this.keysHandler);
- //Restore focus
- if (this.lastFocusedElement) {
- this.lastFocusedElement.focus();
- }
+ getTargetSize() {
+ if (this.currentWidth < 768) return 'small';
+ if (this.currentWidth < 1200) return 'medium';
+ return 'full';
}
cleanup() {
- this.observer.disconnect();
- window.removeEventListener('resize', this.resizeHandler)
- if (this.isTouching) {
- this.cancelTouchHandling();
- document.removeEventListener('keydown', this.keysHandler);
- }
- document.removeEventListener('click', this.clickHandler);
+ this.observer?.disconnect();
+ window.removeEventListener('resize', this.resizeHandler);
}
}
+
+window.isLoaded = false;
+document.addEventListener('readystatechange', () => {
+ if (!window.isLoaded && document.querySelector('.wp-site-blocks img')) {
+ window.jvbMedia = new Media();
+ window.isLoaded = true;
+ }
+});
--
Gitblit v1.10.0