mirror of
https://github.com/zadam/trilium.git
synced 2025-12-05 06:54:23 +01:00
feat(client): implement photoswipe at different points
This commit is contained in:
parent
78c27dbe04
commit
5024e27885
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import mediaViewer from './media_viewer.js';
|
import mediaViewer from './media_viewer.js';
|
||||||
import galleryManager from './gallery_manager.js';
|
import galleryManager from './gallery_manager.js';
|
||||||
|
import appContext from '../components/app_context.js';
|
||||||
import type { MediaItem } from './media_viewer.js';
|
import type { MediaItem } from './media_viewer.js';
|
||||||
import type { GalleryItem } from './gallery_manager.js';
|
import type { GalleryItem } from './gallery_manager.js';
|
||||||
|
|
||||||
@ -97,9 +98,11 @@ class CKEditorPhotoSwipeIntegration {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make image clickable
|
// Make image clickable and mark it as PhotoSwipe-enabled
|
||||||
img.style.cursor = 'zoom-in';
|
img.style.cursor = 'zoom-in';
|
||||||
img.style.transition = 'opacity 0.2s';
|
img.style.transition = 'opacity 0.2s';
|
||||||
|
img.classList.add('photoswipe-enabled');
|
||||||
|
img.setAttribute('data-photoswipe', 'true');
|
||||||
|
|
||||||
// Store event handlers for cleanup
|
// Store event handlers for cleanup
|
||||||
const mouseEnterHandler = () => {
|
const mouseEnterHandler = () => {
|
||||||
@ -121,6 +124,24 @@ class CKEditorPhotoSwipeIntegration {
|
|||||||
// Store handlers for cleanup
|
// Store handlers for cleanup
|
||||||
(img as any)._photoswipeHandlers = { mouseEnterHandler, mouseLeaveHandler };
|
(img as any)._photoswipeHandlers = { mouseEnterHandler, mouseLeaveHandler };
|
||||||
|
|
||||||
|
// Add double-click handler to prevent default navigation behavior
|
||||||
|
const dblClickHandler = (e: MouseEvent) => {
|
||||||
|
// Only prevent double-click in specific contexts to avoid breaking other features
|
||||||
|
if (img.closest('.attachment-detail-wrapper') ||
|
||||||
|
img.closest('.note-detail-editable-text') ||
|
||||||
|
img.closest('.note-detail-readonly-text')) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
// Trigger the same behavior as single click (open lightbox)
|
||||||
|
img.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
img.addEventListener('dblclick', dblClickHandler, true); // Use capture phase to ensure we get it first
|
||||||
|
(img as any)._photoswipeHandlers.dblClickHandler = dblClickHandler;
|
||||||
|
|
||||||
// Add click handler
|
// Add click handler
|
||||||
img.addEventListener('click', (e) => {
|
img.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -194,6 +215,27 @@ class CKEditorPhotoSwipeIntegration {
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
|
// Check if we're in attachment detail view and need to reset viewScope
|
||||||
|
const activeContext = appContext.tabManager.getActiveContext();
|
||||||
|
if (activeContext?.viewScope?.viewMode === 'attachments') {
|
||||||
|
// Get the note ID from the image source
|
||||||
|
const attachmentMatch = img.src.match(/\/api\/attachments\/([A-Za-z0-9_]+)\/image\//);
|
||||||
|
if (attachmentMatch) {
|
||||||
|
const currentAttachmentId = activeContext.viewScope.attachmentId;
|
||||||
|
if (currentAttachmentId === attachmentMatch[1]) {
|
||||||
|
// Actually reset the viewScope instead of just logging
|
||||||
|
try {
|
||||||
|
if (activeContext.note) {
|
||||||
|
activeContext.setNote(activeContext.note.noteId, {
|
||||||
|
viewScope: { viewMode: 'default' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to reset viewScope after PhotoSwipe close:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Restore focus to the image
|
// Restore focus to the image
|
||||||
img.focus();
|
img.focus();
|
||||||
}
|
}
|
||||||
@ -429,6 +471,9 @@ class CKEditorPhotoSwipeIntegration {
|
|||||||
if (handlers) {
|
if (handlers) {
|
||||||
img.removeEventListener('mouseenter', handlers.mouseEnterHandler);
|
img.removeEventListener('mouseenter', handlers.mouseEnterHandler);
|
||||||
img.removeEventListener('mouseleave', handlers.mouseLeaveHandler);
|
img.removeEventListener('mouseleave', handlers.mouseLeaveHandler);
|
||||||
|
if (handlers.dblClickHandler) {
|
||||||
|
img.removeEventListener('dblclick', handlers.dblClickHandler, true);
|
||||||
|
}
|
||||||
delete (img as any)._photoswipeHandlers;
|
delete (img as any)._photoswipeHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import contentRenderer from "../services/content_renderer.js";
|
|||||||
import toastService from "../services/toast.js";
|
import toastService from "../services/toast.js";
|
||||||
import type FAttachment from "../entities/fattachment.js";
|
import type FAttachment from "../entities/fattachment.js";
|
||||||
import type { EventData } from "../components/app_context.js";
|
import type { EventData } from "../components/app_context.js";
|
||||||
|
import appContext from "../components/app_context.js";
|
||||||
import mediaViewer from "../services/media_viewer.js";
|
import mediaViewer from "../services/media_viewer.js";
|
||||||
import type { MediaItem } from "../services/media_viewer.js";
|
import type { MediaItem } from "../services/media_viewer.js";
|
||||||
|
|
||||||
@ -114,7 +115,9 @@ const TPL = /*html*/`
|
|||||||
<div class="attachment-actions-container"></div>
|
<div class="attachment-actions-container"></div>
|
||||||
<h4 class="attachment-title"></h4>
|
<h4 class="attachment-title"></h4>
|
||||||
<div class="attachment-details"></div>
|
<div class="attachment-details"></div>
|
||||||
<div style="flex: 1 1;"></div>
|
<button class="btn btn-sm back-to-note-btn" style="margin-left: auto;" title="Back to Note">
|
||||||
|
<span class="bx bx-arrow-back"></span> Back to Note
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="attachment-deletion-warning alert alert-info" style="margin-top: 15px;"></div>
|
<div class="attachment-deletion-warning alert alert-info" style="margin-top: 15px;"></div>
|
||||||
@ -151,6 +154,14 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
|||||||
this.$wrapper = this.$widget.find(".attachment-detail-wrapper");
|
this.$wrapper = this.$widget.find(".attachment-detail-wrapper");
|
||||||
this.$wrapper.addClass(this.isFullDetail ? "full-detail" : "list-view");
|
this.$wrapper.addClass(this.isFullDetail ? "full-detail" : "list-view");
|
||||||
|
|
||||||
|
// Setup back to note button (only show in full detail mode)
|
||||||
|
if (this.isFullDetail) {
|
||||||
|
const $backBtn = this.$wrapper.find('.back-to-note-btn');
|
||||||
|
$backBtn.on('click', () => this.handleBackToNote());
|
||||||
|
} else {
|
||||||
|
this.$wrapper.find('.back-to-note-btn').hide();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isFullDetail) {
|
if (!this.isFullDetail) {
|
||||||
const $link = await linkService.createLink(this.attachment.ownerId, {
|
const $link = await linkService.createLink(this.attachment.ownerId, {
|
||||||
title: this.attachment.title,
|
title: this.attachment.title,
|
||||||
@ -255,6 +266,15 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
|||||||
console.log('Attachment image opened in lightbox');
|
console.log('Attachment image opened in lightbox');
|
||||||
},
|
},
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
|
// Check if we're in attachment detail view and reset viewScope if needed
|
||||||
|
const activeContext = appContext.tabManager.getActiveContext();
|
||||||
|
if (activeContext?.viewScope?.viewMode === 'attachments' &&
|
||||||
|
activeContext?.viewScope?.attachmentId === this.attachment.attachmentId) {
|
||||||
|
// Reset to normal note view when closing lightbox from attachment detail
|
||||||
|
activeContext.setNote(this.attachment.ownerId, {
|
||||||
|
viewScope: { viewMode: 'default' }
|
||||||
|
});
|
||||||
|
}
|
||||||
// Restore focus to the image
|
// Restore focus to the image
|
||||||
$img.focus();
|
$img.focus();
|
||||||
}
|
}
|
||||||
@ -307,6 +327,28 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleBackToNote() {
|
||||||
|
try {
|
||||||
|
const activeContext = appContext.tabManager.getActiveContext();
|
||||||
|
if (!activeContext) {
|
||||||
|
console.warn('No active context available for navigation');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.attachment.ownerId) {
|
||||||
|
console.error('Cannot navigate back: no owner ID available');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await activeContext.setNote(this.attachment.ownerId, {
|
||||||
|
viewScope: { viewMode: 'default' }
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to navigate back to note:', error);
|
||||||
|
toastService.showError('Failed to navigate back to note');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
// Remove all event handlers before cleanup
|
// Remove all event handlers before cleanup
|
||||||
const $contentWrapper = this.$wrapper?.find('.attachment-content-wrapper');
|
const $contentWrapper = this.$wrapper?.find('.attachment-content-wrapper');
|
||||||
@ -318,6 +360,9 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove back button handler
|
||||||
|
this.$wrapper?.find('.back-to-note-btn').off('click');
|
||||||
|
|
||||||
super.cleanup();
|
super.cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import contentRenderer from "../../services/content_renderer.js";
|
|||||||
import utils from "../../services/utils.js";
|
import utils from "../../services/utils.js";
|
||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import attributes from "../../services/attributes.js";
|
import attributes from "../../services/attributes.js";
|
||||||
|
import ckeditorPhotoswipeIntegration from "../../services/ckeditor_photoswipe_integration.js";
|
||||||
|
|
||||||
export default class AbstractTextTypeWidget extends TypeWidget {
|
export default class AbstractTextTypeWidget extends TypeWidget {
|
||||||
doRender() {
|
doRender() {
|
||||||
@ -35,7 +36,29 @@ export default class AbstractTextTypeWidget extends TypeWidget {
|
|||||||
const parsedImage = await this.parseFromImage($img);
|
const parsedImage = await this.parseFromImage($img);
|
||||||
|
|
||||||
if (parsedImage) {
|
if (parsedImage) {
|
||||||
|
// Check if this is an attachment image and PhotoSwipe is available
|
||||||
|
if (parsedImage.viewScope?.attachmentId) {
|
||||||
|
// Instead of navigating to attachment detail, trigger PhotoSwipe
|
||||||
|
// Check if the image is already processed by PhotoSwipe
|
||||||
|
const imgElement = $img[0] as HTMLImageElement;
|
||||||
|
|
||||||
|
// Check if PhotoSwipe is integrated with this image using multiple reliable indicators
|
||||||
|
const hasPhotoSwipe = imgElement.classList.contains('photoswipe-enabled') ||
|
||||||
|
imgElement.hasAttribute('data-photoswipe') ||
|
||||||
|
imgElement.style.cursor === 'zoom-in';
|
||||||
|
|
||||||
|
if (hasPhotoSwipe) {
|
||||||
|
// Image has PhotoSwipe integration, trigger click to open lightbox
|
||||||
|
$img.trigger('click');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, fall back to opening attachment detail (but with improved navigation)
|
||||||
appContext.tabManager.getActiveContext()?.setNote(parsedImage.noteId, { viewScope: parsedImage.viewScope });
|
appContext.tabManager.getActiveContext()?.setNote(parsedImage.noteId, { viewScope: parsedImage.viewScope });
|
||||||
|
} else {
|
||||||
|
// Regular note image, navigate normally
|
||||||
|
appContext.tabManager.getActiveContext()?.setNote(parsedImage.noteId, { viewScope: parsedImage.viewScope });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
window.open($img.prop("src"), "_blank");
|
window.open($img.prop("src"), "_blank");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user