Preview segment (#19203)
* add new manifest * wip element * add segment to preview context * add segment icon * open preview route on the same server * Update preview.context.ts * clean up * pass culture and segment to preview window
This commit is contained in:
@@ -15,6 +15,13 @@ export const manifests: Array<ManifestPreviewAppProvider> = [
|
||||
element: () => import('./preview-culture.element.js'),
|
||||
weight: 300,
|
||||
},
|
||||
{
|
||||
type: 'previewApp',
|
||||
alias: 'Umb.PreviewApps.Segment',
|
||||
name: 'Preview: Segment Switcher',
|
||||
element: () => import('./preview-segment.element.js'),
|
||||
weight: 290,
|
||||
},
|
||||
{
|
||||
type: 'previewApp',
|
||||
alias: 'Umb.PreviewApps.OpenWebsite',
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { UMB_PREVIEW_CONTEXT } from '../preview.context.js';
|
||||
import {
|
||||
css,
|
||||
customElement,
|
||||
html,
|
||||
nothing,
|
||||
repeat,
|
||||
state,
|
||||
type PropertyValues,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbSegmentCollectionRepository, type UmbSegmentCollectionItemModel } from '@umbraco-cms/backoffice/segment';
|
||||
|
||||
@customElement('umb-preview-segment')
|
||||
export class UmbPreviewSegmentElement extends UmbLitElement {
|
||||
#segmentRepository = new UmbSegmentCollectionRepository(this);
|
||||
|
||||
@state()
|
||||
private _segment?: UmbSegmentCollectionItemModel;
|
||||
|
||||
@state()
|
||||
private _segments: Array<UmbSegmentCollectionItemModel> = [];
|
||||
|
||||
protected override firstUpdated(_changedProperties: PropertyValues): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
this.#loadSegments();
|
||||
}
|
||||
|
||||
async #loadSegments() {
|
||||
const { data } = await this.#segmentRepository.requestCollection({ skip: 0, take: 100 });
|
||||
this._segments = data?.items ?? [];
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const segment = searchParams.get('segment');
|
||||
|
||||
if (segment && segment !== this._segment?.unique) {
|
||||
this._segment = this._segments.find((c) => c.unique === segment);
|
||||
}
|
||||
}
|
||||
|
||||
async #onClick(segment?: UmbSegmentCollectionItemModel) {
|
||||
if (this._segment === segment) return;
|
||||
this._segment = segment;
|
||||
|
||||
const previewContext = await this.getContext(UMB_PREVIEW_CONTEXT);
|
||||
previewContext?.updateIFrame({ segment: segment?.unique });
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this._segments.length <= 1) return nothing;
|
||||
return html`
|
||||
<uui-button look="primary" popovertarget="segments-popover">
|
||||
<div>
|
||||
<uui-icon name="icon-filter"></uui-icon>
|
||||
<span>${this._segment?.name ?? 'Segments'}</span>
|
||||
</div>
|
||||
</uui-button>
|
||||
<uui-popover-container id="segments-popover" placement="top-end">
|
||||
<umb-popover-layout>
|
||||
<uui-menu-item label="Default" ?active=${!this._segment} @click=${() => this.#onClick()}></uui-menu-item>
|
||||
${repeat(
|
||||
this._segments,
|
||||
(item) => item.unique,
|
||||
(item) => html`
|
||||
<uui-menu-item
|
||||
label=${item.name}
|
||||
?active=${item.unique === this._segment?.unique}
|
||||
@click=${() => this.#onClick(item)}>
|
||||
</uui-menu-item>
|
||||
`,
|
||||
)}
|
||||
</umb-popover-layout>
|
||||
</uui-popover-container>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
border-left: 1px solid var(--uui-color-header-contrast);
|
||||
--uui-button-font-weight: 400;
|
||||
--uui-button-padding-left-factor: 3;
|
||||
--uui-button-padding-right-factor: 3;
|
||||
}
|
||||
|
||||
uui-button > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
umb-popover-layout {
|
||||
--uui-color-surface: var(--uui-color-header-surface);
|
||||
--uui-color-border: var(--uui-color-header-surface);
|
||||
color: var(--uui-color-header-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export { UmbPreviewSegmentElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-preview-segment': UmbPreviewSegmentElement;
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,28 @@ import { UMB_SERVER_CONTEXT } from '@umbraco-cms/backoffice/server';
|
||||
|
||||
const UMB_LOCALSTORAGE_SESSION_KEY = 'umb:previewSessions';
|
||||
|
||||
interface UmbPreviewIframeArgs {
|
||||
className?: string;
|
||||
culture?: string;
|
||||
height?: string;
|
||||
segment?: string;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
interface UmbPreviewUrlArgs {
|
||||
culture?: string | null;
|
||||
rnd?: number;
|
||||
segment?: string | null;
|
||||
serverUrl?: string;
|
||||
unique?: string | null;
|
||||
}
|
||||
|
||||
export class UmbPreviewContext extends UmbContextBase {
|
||||
#unique?: string | null;
|
||||
#culture?: string | null;
|
||||
#segment?: string | null;
|
||||
#serverUrl: string = '';
|
||||
#webSocket?: WebSocket;
|
||||
#unique?: string | null;
|
||||
|
||||
#iframeReady = new UmbBooleanState(false);
|
||||
public readonly iframeReady = this.#iframeReady.asObservable();
|
||||
@@ -30,8 +47,9 @@ export class UmbPreviewContext extends UmbContextBase {
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
this.#culture = params.get('culture');
|
||||
this.#unique = params.get('id');
|
||||
this.#culture = params.get('culture');
|
||||
this.#segment = params.get('segment');
|
||||
|
||||
if (!this.#unique) {
|
||||
console.error('No unique ID found in query string.');
|
||||
@@ -75,16 +93,46 @@ export class UmbPreviewContext extends UmbContextBase {
|
||||
return Math.max(Number(localStorage.getItem(UMB_LOCALSTORAGE_SESSION_KEY)), 0) || 0;
|
||||
}
|
||||
|
||||
#setPreviewUrl(args?: { serverUrl?: string; unique?: string | null; culture?: string | null; rnd?: number }) {
|
||||
#setPreviewUrl(args?: UmbPreviewUrlArgs) {
|
||||
const host = args?.serverUrl || this.#serverUrl;
|
||||
const path = args?.unique || this.#unique;
|
||||
const params = new URLSearchParams();
|
||||
const unique = args?.unique || this.#unique;
|
||||
|
||||
if (!unique) {
|
||||
throw new Error('No unique ID found in query string.');
|
||||
}
|
||||
|
||||
const url = new URL(unique, host);
|
||||
const params = new URLSearchParams(url.search);
|
||||
|
||||
const culture = args?.culture || this.#culture;
|
||||
const segment = args?.segment || this.#segment;
|
||||
|
||||
if (culture) params.set('culture', culture);
|
||||
if (args?.rnd) params.set('rnd', args.rnd.toString());
|
||||
const cultureParam = 'culture';
|
||||
const rndParam = 'rnd';
|
||||
const segmentParam = 'segment';
|
||||
|
||||
this.#previewUrl.setValue(`${host}/${path}?${params}`);
|
||||
if (culture) {
|
||||
params.set(cultureParam, culture);
|
||||
} else {
|
||||
params.delete(cultureParam);
|
||||
}
|
||||
|
||||
if (args?.rnd) {
|
||||
params.set(rndParam, args.rnd.toString());
|
||||
} else {
|
||||
params.delete(rndParam);
|
||||
}
|
||||
|
||||
if (segment) {
|
||||
params.set(segmentParam, segment);
|
||||
} else {
|
||||
params.delete(segmentParam);
|
||||
}
|
||||
|
||||
const previewUrl = new URL(url.pathname + '?' + params.toString(), host);
|
||||
const previewUrlString = previewUrl.toString();
|
||||
|
||||
this.#previewUrl.setValue(previewUrlString);
|
||||
}
|
||||
|
||||
#setSessionCount(sessions: number) {
|
||||
@@ -165,8 +213,9 @@ export class UmbPreviewContext extends UmbContextBase {
|
||||
this.#setSessionCount(sessions);
|
||||
}
|
||||
|
||||
async updateIFrame(args?: { culture?: string; className?: string; height?: string; width?: string }) {
|
||||
if (!args) return;
|
||||
#currentArgs: UmbPreviewIframeArgs = {};
|
||||
async updateIFrame(args?: UmbPreviewIframeArgs) {
|
||||
const mergedArgs = { ...this.#currentArgs, ...args };
|
||||
|
||||
const wrapper = this.getIFrameWrapper();
|
||||
if (!wrapper) return;
|
||||
@@ -185,20 +234,32 @@ export class UmbPreviewContext extends UmbContextBase {
|
||||
window.addEventListener('resize', scaleIFrame);
|
||||
wrapper.addEventListener('transitionend', scaleIFrame);
|
||||
|
||||
if (args.culture) {
|
||||
this.#iframeReady.setValue(false);
|
||||
this.#iframeReady.setValue(false);
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('culture', args.culture);
|
||||
const newRelativePathQuery = window.location.pathname + '?' + params.toString();
|
||||
history.pushState(null, '', newRelativePathQuery);
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
this.#setPreviewUrl({ culture: args.culture });
|
||||
if (mergedArgs.culture) {
|
||||
params.set('culture', mergedArgs.culture);
|
||||
} else {
|
||||
params.delete('culture');
|
||||
}
|
||||
|
||||
if (args.className) wrapper.className = args.className;
|
||||
if (args.height) wrapper.style.height = args.height;
|
||||
if (args.width) wrapper.style.width = args.width;
|
||||
if (mergedArgs.segment) {
|
||||
params.set('segment', mergedArgs.segment);
|
||||
} else {
|
||||
params.delete('segment');
|
||||
}
|
||||
|
||||
const newRelativePathQuery = window.location.pathname + '?' + params.toString();
|
||||
history.pushState(null, '', newRelativePathQuery);
|
||||
|
||||
this.#currentArgs = mergedArgs;
|
||||
|
||||
this.#setPreviewUrl({ culture: mergedArgs.culture, segment: mergedArgs.segment });
|
||||
|
||||
if (mergedArgs.className) wrapper.className = mergedArgs.className;
|
||||
if (mergedArgs.height) wrapper.style.height = mergedArgs.height;
|
||||
if (mergedArgs.width) wrapper.style.width = mergedArgs.width;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { UmbDocumentValidationRepository } from '../repository/validation/index.
|
||||
import { UMB_DOCUMENT_CONFIGURATION_CONTEXT } from '../index.js';
|
||||
import { UMB_DOCUMENT_DETAIL_MODEL_VARIANT_SCAFFOLD, UMB_DOCUMENT_WORKSPACE_ALIAS } from './constants.js';
|
||||
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
|
||||
import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import {
|
||||
type UmbPublishableWorkspaceContext,
|
||||
UmbWorkspaceIsNewRedirectController,
|
||||
@@ -362,13 +362,13 @@ export class UmbDocumentWorkspaceContext
|
||||
const unique = this.getUnique();
|
||||
if (!unique) throw new Error('Unique is missing');
|
||||
|
||||
let culture = UMB_INVARIANT_CULTURE;
|
||||
let firstVariantId = UmbVariantId.CreateInvariant();
|
||||
|
||||
// Save document (the active variant) before previewing.
|
||||
const { selected } = await this._determineVariantOptions();
|
||||
if (selected.length > 0) {
|
||||
culture = selected[0];
|
||||
const variantIds = [UmbVariantId.FromString(culture)];
|
||||
firstVariantId = UmbVariantId.FromString(selected[0]);
|
||||
const variantIds = [firstVariantId];
|
||||
const saveData = await this._data.constructData(variantIds);
|
||||
await this.runMandatoryValidationForSaveData(saveData);
|
||||
await this.performCreateOrUpdate(variantIds, saveData);
|
||||
@@ -383,11 +383,15 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
|
||||
const backofficePath = serverContext.getBackofficePath();
|
||||
const previewUrl = new URL(ensurePathEndsWithSlash(backofficePath) + 'preview', serverContext.getServerUrl());
|
||||
const previewUrl = new URL(ensurePathEndsWithSlash(backofficePath) + 'preview', window.location.origin);
|
||||
previewUrl.searchParams.set('id', unique);
|
||||
|
||||
if (culture && culture !== UMB_INVARIANT_CULTURE) {
|
||||
previewUrl.searchParams.set('culture', culture);
|
||||
if (firstVariantId.culture) {
|
||||
previewUrl.searchParams.set('culture', firstVariantId.culture);
|
||||
}
|
||||
|
||||
if (firstVariantId.segment) {
|
||||
previewUrl.searchParams.set('segment', firstVariantId.segment);
|
||||
}
|
||||
|
||||
const previewWindow = window.open(previewUrl.toString(), `umbpreview-${unique}`);
|
||||
|
||||
Reference in New Issue
Block a user