Merge branch 'v16/dev' into contrib
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 254 KiB |
4
src/Umbraco.Web.UI.Client/package-lock.json
generated
4
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "16.0.0-rc",
|
||||
"version": "16.1.0-rc",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"version": "16.0.0-rc",
|
||||
"version": "16.1.0-rc",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"./src/packages/*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@umbraco-cms/backoffice",
|
||||
"license": "MIT",
|
||||
"version": "16.0.0-rc",
|
||||
"version": "16.1.0-rc",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": null,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 254 KiB |
@@ -29,7 +29,8 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
@state()
|
||||
private _value: Array<UmbBlockGridTypeAreaTypePermission> = [];
|
||||
|
||||
_blockTypes: Array<UmbBlockTypeWithGroupKey> = [];
|
||||
@state()
|
||||
private _blockTypes?: Array<UmbBlockTypeWithGroupKey>;
|
||||
|
||||
@state()
|
||||
private _blockTypesWithElementName: Array<{ type: UmbBlockTypeWithGroupKey; name: string }> = [];
|
||||
@@ -48,7 +49,7 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
this.observe(this.#itemsManager.items, (items) => {
|
||||
this._blockTypesWithElementName = items
|
||||
.map((item) => {
|
||||
const blockType = this._blockTypes.find((block) => block.contentElementTypeKey === item.unique);
|
||||
const blockType = this._blockTypes?.find((block) => block.contentElementTypeKey === item.unique);
|
||||
if (blockType) {
|
||||
return { type: blockType, name: item.name };
|
||||
}
|
||||
@@ -123,9 +124,12 @@ export class UmbPropertyEditorUIBlockGridAreaTypePermissionElement
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this._blockTypesWithElementName.length === 0) {
|
||||
if (this._blockTypes === undefined) {
|
||||
return nothing;
|
||||
}
|
||||
if (this._blockTypesWithElementName.length === 0) {
|
||||
return 'There must be one Block Type created before permissions can be configured.';
|
||||
}
|
||||
return html`<div id="permissions">
|
||||
${repeat(
|
||||
this._value,
|
||||
|
||||
@@ -170,7 +170,7 @@ export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthC
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
background: var(--uui-color-surface, #f4f4f4);
|
||||
background: var(--uui-color-background, #f4f4f4);
|
||||
|
||||
--curves-color: var(--umb-login-curves-color, #f5c1bc);
|
||||
--curves-display: var(--umb-login-curves-display, inline);
|
||||
@@ -204,13 +204,13 @@ export class UmbAppAuthModalElement extends UmbModalBaseElement<UmbModalAppAuthC
|
||||
}
|
||||
|
||||
#curve-top {
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
top: -9%;
|
||||
right: -9%;
|
||||
}
|
||||
|
||||
#curve-bottom {
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
bottom: -0.5%;
|
||||
left: -0.1%;
|
||||
}
|
||||
|
||||
#content-container {
|
||||
|
||||
@@ -151,7 +151,10 @@ export class UmbInputDocumentTypeElement extends UmbFormControlMixin<string | un
|
||||
|
||||
#getPickableFilter() {
|
||||
if (this.documentTypesOnly) {
|
||||
return (x: UmbDocumentTypeTreeItemModel) => x.isFolder === false && x.isElement === false;
|
||||
/* TODO: We do not have the same model in the tree and during the search, so theoretically, we cannot use the same filter.
|
||||
The search item model does not include "isFolder," so it checks for falsy intentionally.
|
||||
We need to investigate getting this typed correctly. [MR] */
|
||||
return (x: UmbDocumentTypeTreeItemModel) => !x.isFolder && x.isElement === false;
|
||||
}
|
||||
if (this.elementTypesOnly) {
|
||||
return (x: UmbDocumentTypeTreeItemModel) => x.isElement;
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -178,13 +178,13 @@ export class UmbAuthLayoutElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#curve-top {
|
||||
top: 0;
|
||||
right: 0;
|
||||
top: -9%;
|
||||
right: -9%;
|
||||
}
|
||||
|
||||
#curve-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
bottom: -0.5%;
|
||||
left: -0.1%;
|
||||
}
|
||||
|
||||
#logo-on-image,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
|
||||
"version": "16.0.0-rc2",
|
||||
"version": "16.1.0-rc",
|
||||
"assemblyVersion": {
|
||||
"precision": "build"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user