refactor: split document pane runtime
This commit is contained in:
@@ -1,3 +1,14 @@
|
||||
import {
|
||||
buildPaneRuntime,
|
||||
clearSecondaryParams,
|
||||
createSecondaryPaneWidthController,
|
||||
currentUrl,
|
||||
installSecondaryPaneCloseButtons,
|
||||
installSecondaryPaneOpenListeners,
|
||||
pushUrlState,
|
||||
replaceUrlState,
|
||||
secondaryQueryParamNames,
|
||||
} from './document-pane-runtime.js';
|
||||
import {
|
||||
conflictDetectionKeyBelongsToSession,
|
||||
conflictDetectionKeyFromBody,
|
||||
@@ -37,154 +48,15 @@ import {
|
||||
const panesBootstrap = parseJsonScript(PANES_BOOTSTRAP_ID);
|
||||
if (!panesBootstrap || !Array.isArray(panesBootstrap.panes)) return;
|
||||
|
||||
const paneRouteConfig = {
|
||||
primary: {
|
||||
sourceKindParam: 'sourceKind',
|
||||
rootUriParam: 'rootUri',
|
||||
secondary: false,
|
||||
},
|
||||
secondary: {
|
||||
documentIdParam: 'secondaryDocumentId',
|
||||
sourceKindParam: 'secondarySourceKind',
|
||||
rootUriParam: 'secondaryRootUri',
|
||||
secondary: true,
|
||||
},
|
||||
};
|
||||
const secondaryQueryParamNames = [
|
||||
paneRouteConfig.secondary.documentIdParam,
|
||||
paneRouteConfig.secondary.sourceKindParam,
|
||||
paneRouteConfig.secondary.rootUriParam,
|
||||
];
|
||||
const currentUrl = () => new URL(window.location.href);
|
||||
const replaceUrlState = (url) => {
|
||||
window.history.replaceState({}, '', url.pathname + url.search + url.hash);
|
||||
};
|
||||
const pushUrlState = (url) => {
|
||||
window.history.pushState({}, '', url.pathname + url.search + url.hash);
|
||||
};
|
||||
const clearSecondaryParams = () => {
|
||||
const url = currentUrl();
|
||||
secondaryQueryParamNames.forEach((name) => url.searchParams.delete(name));
|
||||
replaceUrlState(url);
|
||||
};
|
||||
if (panesBootstrap.secondaryInvalid === true) clearSecondaryParams();
|
||||
|
||||
const secondaryUrlForDocument = (documentId, detail = {}) => {
|
||||
const url = currentUrl();
|
||||
url.searchParams.set(paneRouteConfig.secondary.documentIdParam, documentId);
|
||||
const detailSourceKind = typeof detail.sourceKind === 'string' ? detail.sourceKind.trim() : '';
|
||||
const detailRootUri = typeof detail.rootUri === 'string' ? detail.rootUri.trim() : '';
|
||||
const primarySourceKind = (url.searchParams.get(paneRouteConfig.primary.sourceKindParam) || '').trim();
|
||||
const primaryRootUri = (url.searchParams.get(paneRouteConfig.primary.rootUriParam) || '').trim();
|
||||
const secondarySourceKind = detailSourceKind || primarySourceKind;
|
||||
const secondaryRootUri = detailRootUri || primaryRootUri;
|
||||
if (secondarySourceKind) url.searchParams.set(paneRouteConfig.secondary.sourceKindParam, secondarySourceKind);
|
||||
else url.searchParams.delete(paneRouteConfig.secondary.sourceKindParam);
|
||||
if (secondaryRootUri) url.searchParams.set(paneRouteConfig.secondary.rootUriParam, secondaryRootUri);
|
||||
else url.searchParams.delete(paneRouteConfig.secondary.rootUriParam);
|
||||
return { url, sourceKind: secondarySourceKind, rootUri: secondaryRootUri };
|
||||
};
|
||||
|
||||
const openDocumentInSecondaryPane = (documentId, detail = {}) => {
|
||||
const id = typeof documentId === 'string' ? documentId.trim() : '';
|
||||
if (!id) return false;
|
||||
const target = secondaryUrlForDocument(id, detail);
|
||||
if (typeof window.__mnoteDocumentPaneRuntime?.openSecondaryDocument === 'function') {
|
||||
void window.__mnoteDocumentPaneRuntime.openSecondaryDocument({
|
||||
documentId: id,
|
||||
workspaceId: typeof detail.workspaceId === 'string' ? detail.workspaceId.trim() : '',
|
||||
sourceKind: target.sourceKind || null,
|
||||
rootUri: target.rootUri || null,
|
||||
url: target.url,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
window.location.assign(target.url.pathname + target.url.search + target.url.hash);
|
||||
return true;
|
||||
};
|
||||
|
||||
window.addEventListener('tree.page.open-right', (event) => {
|
||||
const detail = event?.detail && typeof event.detail === 'object' ? event.detail : {};
|
||||
const documentId = typeof detail.documentId === 'string' ? detail.documentId.trim() : '';
|
||||
openDocumentInSecondaryPane(documentId, detail);
|
||||
});
|
||||
|
||||
window.addEventListener('tree.page.open', (event) => {
|
||||
const detail = event?.detail && typeof event.detail === 'object' ? event.detail : {};
|
||||
const openTarget = typeof detail.openTarget === 'string' ? detail.openTarget.trim().toLowerCase() : '';
|
||||
if (openTarget !== 'side') return;
|
||||
const documentId = typeof detail.documentId === 'string' ? detail.documentId.trim() : '';
|
||||
openDocumentInSecondaryPane(documentId, detail);
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-mnote-pane-close="secondary"]').forEach((button) => {
|
||||
button.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const url = currentUrl();
|
||||
secondaryQueryParamNames.forEach((name) => url.searchParams.delete(name));
|
||||
if (typeof window.__mnoteDocumentPaneRuntime?.closeSecondaryDocument === 'function') {
|
||||
window.__mnoteDocumentPaneRuntime.closeSecondaryDocument({ url });
|
||||
return;
|
||||
}
|
||||
window.location.assign(url.pathname + url.search + url.hash);
|
||||
});
|
||||
});
|
||||
|
||||
const workspace = document.querySelector('[data-testid="mnote-document-workspace"]');
|
||||
const resizer = document.querySelector('[data-document-pane-resizer="true"]');
|
||||
const SECONDARY_WIDTH_KEY = 'mnote.document.secondary.width';
|
||||
const applyStoredSecondaryWidth = () => {
|
||||
if (!(workspace instanceof HTMLElement)) return;
|
||||
if (workspace.getAttribute('data-has-secondary-pane') !== 'true') {
|
||||
workspace.style.removeProperty('grid-template-columns');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const raw = window.localStorage ? window.localStorage.getItem(SECONDARY_WIDTH_KEY) : '';
|
||||
const width = Number(raw || 0);
|
||||
if (Number.isFinite(width) && width >= 320) {
|
||||
workspace.style.gridTemplateColumns = `minmax(0, 1fr) 6px minmax(320px, ${Math.round(width)}px)`;
|
||||
}
|
||||
} catch (_) {}
|
||||
};
|
||||
installSecondaryPaneOpenListeners();
|
||||
installSecondaryPaneCloseButtons();
|
||||
const { applyStoredSecondaryWidth } = createSecondaryPaneWidthController();
|
||||
applyStoredSecondaryWidth();
|
||||
if (workspace instanceof HTMLElement && resizer instanceof HTMLElement) {
|
||||
resizer.addEventListener('pointerdown', (event) => {
|
||||
if (workspace.getAttribute('data-has-secondary-pane') !== 'true') return;
|
||||
event.preventDefault();
|
||||
const move = (nextEvent) => {
|
||||
const rect = workspace.getBoundingClientRect();
|
||||
const width = Math.min(Math.max(320, rect.right - nextEvent.clientX), Math.max(420, rect.width - 360));
|
||||
workspace.style.gridTemplateColumns = `minmax(0, 1fr) 6px minmax(320px, ${Math.round(width)}px)`;
|
||||
try {
|
||||
if (window.localStorage) window.localStorage.setItem(SECONDARY_WIDTH_KEY, String(Math.round(width)));
|
||||
} catch (_) {}
|
||||
};
|
||||
const up = () => {
|
||||
window.removeEventListener('pointermove', move);
|
||||
window.removeEventListener('pointerup', up);
|
||||
};
|
||||
window.addEventListener('pointermove', move);
|
||||
window.addEventListener('pointerup', up);
|
||||
});
|
||||
}
|
||||
|
||||
const buildPaneRuntime = (paneDescriptor) => {
|
||||
const paneRole = typeof paneDescriptor?.role === 'string' ? paneDescriptor.role : 'primary';
|
||||
const paneRoot = document.querySelector(`${ROOT_SELECTOR}[data-pane-role="${paneRole}"]`);
|
||||
const observability = document.querySelector(`[data-editor-host-observability][data-pane-role="${paneRole}"]`);
|
||||
if (!(paneRoot instanceof HTMLElement)) return null;
|
||||
if (!paneDescriptor?.aggregate || !paneDescriptor?.bootstrap) return null;
|
||||
return {
|
||||
paneRole,
|
||||
root: paneRoot,
|
||||
observability,
|
||||
aggregate: paneDescriptor.aggregate,
|
||||
bootstrap: paneDescriptor.bootstrap,
|
||||
};
|
||||
};
|
||||
|
||||
const paneRuntimes = panesBootstrap.panes.map(buildPaneRuntime).filter(Boolean);
|
||||
const paneRuntimes = panesBootstrap.panes
|
||||
.map((paneDescriptor) => buildPaneRuntime(paneDescriptor, ROOT_SELECTOR))
|
||||
.filter(Boolean);
|
||||
|
||||
const loadRuntime = async () => {
|
||||
if (window.__mnoteLeptosTiptapRuntimePromise) {
|
||||
|
||||
Reference in New Issue
Block a user