Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions blocks/browse/da-list-item/da-list-item.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { LitElement, html, nothing, until } from 'da-lit';
import { delay, sanitizeName } from '../../shared/utils.js';
import { delay, sanitizeName, formatDate } from '../../shared/utils.js';
import { getNx, getNx2Api } from '../../../scripts/utils.js';
import getEditPath from '../shared.js';
import { formatDate } from '../../edit/da-versions/helpers.js';

// Styles
const { loadStyle } = await import(`${getNx()}/utils/utils.js`);
Expand Down
12 changes: 12 additions & 0 deletions blocks/canvas/editor-utils/editor-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,18 @@ export const editorSelectChange = (() => {
};
})();

// Event observable — no replay. Emits { url, label, date } when a version restore is requested.
export const versionPreviewChange = (() => {
const listeners = new Set();
return {
emit(detail) { listeners.forEach((fn) => fn(detail)); },
subscribe(fn) {
listeners.add(fn);
return () => listeners.delete(fn);
},
};
})();

export function updateDocument(ctx) {
if (ctx.suppressRerender) return undefined;
const body = getInstrumentedHTML(ctx.view);
Expand Down
17 changes: 15 additions & 2 deletions blocks/canvas/ew-editor-doc/ew-editor-doc.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { LitElement, html, nothing } from 'da-lit';
import { yUndo, yRedo, NodeSelection } from 'da-y-wrapper';
import { yUndo, yRedo, NodeSelection, DOMParser as proseDOMParser } from 'da-y-wrapper';
import { getNx } from '../../../scripts/utils.js';
import {
updateDocument, updateCursors, getInstrumentedHTML,
editorHtmlChange, editorSelectChange, getEditor,
editorHtmlChange, editorSelectChange, getEditor, versionPreviewChange,
} from '../editor-utils/editor-utils.js';
import { getActiveBlockIndex, getBlockPositions } from '../editor-utils/blocks.js';
import {
Expand All @@ -18,6 +18,7 @@ import {
wireQuickEditControllerPort,
} from './utils/quick-edit-host.js';
import { initIms as loadIms } from '../../shared/utils.js';
import { fetchVersionDom } from '../../shared/version/compare.js';
import initProse from './prose.js';
import { createTrackingPlugin } from '../editor-utils/prose-diff.js';
import { resolveEditorDocSession } from './utils/load-editor-doc.js';
Expand Down Expand Up @@ -120,6 +121,15 @@ export class EwEditorDoc extends LitElement {
if (view) yRedo(view.state, view.dispatch);
}

async _restoreVersion({ url }) {
const { view } = this._proseContext ?? {};
if (!view) return;
const dom = await fetchVersionDom(url);
if (!dom) return;
const newDoc = proseDOMParser.fromSchema(view.state.schema).parse(dom);
view.dispatch(view.state.tr.replaceWith(0, view.state.doc.content.size, newDoc.content));
}

_setupController() {
const { view, wsProvider } = this._proseContext ?? {};
if (!this.quickEditPort || !view || !wsProvider) return;
Expand Down Expand Up @@ -256,12 +266,15 @@ export class EwEditorDoc extends LitElement {
.subscribe(({ blockIndex, source }) => {
if (source !== 'doc') this._scrollDocToBlock(blockIndex);
});
this._unsubscribeVersionPreview = versionPreviewChange
.subscribe((detail) => this._restoreVersion(detail));
}

disconnectedCallback() {
this.parentElement?.removeEventListener('nx-canvas-editor-active', this._onCanvasEditorActive);
this.parentElement?.removeEventListener('nx-wysiwyg-port-ready', this._onWysiwygPortReady);
this._unsubscribeSelect?.();
this._unsubscribeVersionPreview?.();
this._teardown();
super.disconnectedCallback();
}
Expand Down
14 changes: 14 additions & 0 deletions blocks/canvas/ew-panel-extensions/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,19 @@ function createFileExplorerView() {
};
}

function createVersionsView() {
return {
id: 'versions',
label: 'History',
section: 'Editor',
firstParty: true,
load: async () => {
await import('../ew-version-history/ew-version-history.js');
return document.createElement('ew-version-history');
},
};
}

function extensionToPanelView(ext, section) {
const view = {
id: ext.name,
Expand Down Expand Up @@ -475,6 +488,7 @@ export async function getCanvasToolPanelViews({ org, site }) {
return [
createOutlineView(),
createFileExplorerView(),
createVersionsView(),
...library.map((ext) => extensionToPanelView(ext, 'Library')),
...thirdParty.map((ext) => extensionToPanelView(ext, 'Extensions')),
];
Expand Down
241 changes: 241 additions & 0 deletions blocks/canvas/ew-version-history/ew-version-history.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
:host {
display: block;
height: 100%;
min-height: 0;
font-family: var(--s2-font-family);
}

.ew-version-history {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}

.list-wrap {
flex: 1;
overflow-y: auto;
padding: var(--s2-spacing-75) 0;
}

.version-list {
list-style: none;
margin: 0;
padding: 0;
}

/* ── Create row ─────────────────────────────────── */

.version-create {
padding: var(--s2-spacing-75) var(--s2-spacing-100);
border-bottom: 1px solid var(--s2-gray-200);

button {
background: none;
border: none;
cursor: pointer;
color: var(--s2-blue-700);
font-family: inherit;
font-size: var(--s2-body-size-s);
font-weight: 600;
padding: 0;

&:hover {
text-decoration: underline;
}
}
}

/* ── New version form ───────────────────────────── */

.version-new {
padding: var(--s2-spacing-75) var(--s2-spacing-100);
border-bottom: 1px solid var(--s2-gray-200);
}

.label-input {
display: block;
width: 100%;
box-sizing: border-box;
padding: var(--s2-spacing-75) var(--s2-spacing-100);
border: 1px solid var(--s2-gray-300);
border-radius: var(--s2-corner-radius-75);
font-family: inherit;
font-size: var(--s2-body-size-s);
margin-bottom: var(--s2-spacing-75);

&:focus {
outline: 2px solid var(--s2-blue-700);
outline-offset: -1px;
border-color: transparent;
}
}

.form-actions {
display: flex;
gap: var(--s2-spacing-75);
}

.btn-save,
.btn-cancel {
border-radius: var(--s2-corner-radius-75);
padding: var(--s2-spacing-75) var(--s2-spacing-100);
cursor: pointer;
font-family: inherit;
font-size: var(--s2-body-size-s);
border: 1px solid var(--s2-gray-300);
background: none;
}

.btn-save {
background: var(--s2-blue-700);
color: #fff;
border-color: var(--s2-blue-700);

&:hover {
background: var(--s2-blue-800, #0d66d0);
}
}

.btn-cancel:hover {
background: var(--s2-gray-75);
}

/* ── Version item ───────────────────────────────── */

.version-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--s2-spacing-100);
padding: var(--s2-spacing-100);
border-bottom: 1px solid var(--s2-gray-100);

&:hover {
background: var(--s2-gray-75);
}
&:last-child {
border-bottom: none;
}
}

.version-meta {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}

.version-date {
font-size: var(--s2-body-size-s);
font-weight: 600;
color: var(--s2-gray-800);
}

.version-label {
font-size: var(--s2-body-size-xs, 11px);
font-weight: 500;
color: var(--s2-blue-700);
}

.version-secondary {
font-size: var(--s2-body-size-xs, 11px);
color: var(--s2-gray-600);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.version-actions {
display: flex;
flex-direction: column;
gap: 4px;
flex-shrink: 0;
}

.compare-btn,
.restore-btn {
background: none;
border: 1px solid var(--s2-gray-300);
border-radius: var(--s2-corner-radius-75);
padding: 2px var(--s2-spacing-75);
cursor: pointer;
font-family: inherit;
font-size: var(--s2-body-size-xs, 11px);
color: var(--s2-gray-800);
white-space: nowrap;
}

.restore-btn:hover {
border-color: var(--s2-blue-700);
color: var(--s2-blue-700);
}

.compare-btn:hover {
border-color: var(--s2-gray-600);
}

/* ── Audit group ────────────────────────────────── */

.audit-group {
padding: var(--s2-spacing-75) var(--s2-spacing-100);
border-bottom: 1px solid var(--s2-gray-100);

&:last-child {
border-bottom: none;
}
}

.audit-date {
display: block;
font-size: var(--s2-body-size-xs, 11px);
font-weight: 600;
color: var(--s2-gray-600);
margin-bottom: 4px;
text-transform: uppercase;
letter-spacing: 0.04em;
}

.audit-list {
list-style: none;
margin: 0;
padding: 0 0 0 var(--s2-spacing-75);
}

.audit-entry {
display: flex;
gap: var(--s2-spacing-75);
padding: 2px 0;
}

.audit-time {
flex-shrink: 0;
font-size: var(--s2-body-size-xs, 11px);
color: var(--s2-gray-600);
}

.audit-users {
font-size: var(--s2-body-size-xs, 11px);
color: var(--s2-gray-700);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

/* ── States ─────────────────────────────────────── */

.status-msg,
.placeholder {
padding: var(--s2-spacing-300) var(--s2-spacing-100);
font-size: var(--s2-body-size-s);
color: var(--s2-gray-600);
text-align: center;
list-style: none;
}

.da-compare-modal {
position: fixed;
left: 2.5%;
max-width: 95%;
right: 2.5%;
}
Loading
Loading