Merge branch 'main' into v14/fix/dictionary-item-misleading-description-when-name-not-set
This commit is contained in:
@@ -20,6 +20,14 @@ import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/rou
|
||||
import { pathFolderName } from '@umbraco-cms/backoffice/utils';
|
||||
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
interface UmbBlockGridAreaTypeInvalidRuleType {
|
||||
groupKey?: string;
|
||||
key?: string;
|
||||
name: string;
|
||||
amount: number;
|
||||
minRequirement: number;
|
||||
maxRequirement: number;
|
||||
}
|
||||
export class UmbBlockGridEntriesContext
|
||||
extends UmbBlockEntriesContext<
|
||||
typeof UMB_BLOCK_GRID_MANAGER_CONTEXT,
|
||||
@@ -455,14 +463,7 @@ export class UmbBlockGridEntriesContext
|
||||
}
|
||||
}
|
||||
|
||||
#invalidBlockTypeLimits?: Array<{
|
||||
groupKey?: string;
|
||||
key?: string;
|
||||
name: string;
|
||||
amount: number;
|
||||
minRequirement: number;
|
||||
maxRequirement: number;
|
||||
}>;
|
||||
#invalidBlockTypeLimits?: Array<UmbBlockGridAreaTypeInvalidRuleType>;
|
||||
|
||||
getInvalidBlockTypeLimits() {
|
||||
return this.#invalidBlockTypeLimits ?? [];
|
||||
@@ -476,57 +477,58 @@ export class UmbBlockGridEntriesContext
|
||||
|
||||
const layoutEntries = this._layoutEntries.getValue();
|
||||
|
||||
this.#invalidBlockTypeLimits = [];
|
||||
this.#invalidBlockTypeLimits = this.#areaType.specifiedAllowance
|
||||
.map((rule) => {
|
||||
const minAllowed = rule.minAllowed || 0;
|
||||
const maxAllowed = rule.maxAllowed || 0;
|
||||
|
||||
const hasInvalidRules = this.#areaType.specifiedAllowance.some((rule) => {
|
||||
const minAllowed = rule.minAllowed || 0;
|
||||
const maxAllowed = rule.maxAllowed || 0;
|
||||
// For block groups:
|
||||
if (rule.groupKey) {
|
||||
const groupElementTypeKeys =
|
||||
this._manager
|
||||
?.getBlockTypes()
|
||||
.filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true)
|
||||
.map((x) => x.contentElementTypeKey) ?? [];
|
||||
const groupAmount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentTypeKeyOfContentUdi(entry.contentUdi);
|
||||
return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false;
|
||||
}).length;
|
||||
|
||||
// For block groups:
|
||||
if (rule.groupKey) {
|
||||
const groupElementTypeKeys =
|
||||
this._manager
|
||||
?.getBlockTypes()
|
||||
.filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true)
|
||||
.map((x) => x.contentElementTypeKey) ?? [];
|
||||
const groupAmount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentTypeKeyOf(entry.contentUdi);
|
||||
return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false;
|
||||
}).length;
|
||||
|
||||
if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) {
|
||||
this.#invalidBlockTypeLimits!.push({
|
||||
groupKey: rule.groupKey,
|
||||
name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?',
|
||||
amount: groupAmount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
});
|
||||
return true;
|
||||
if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) {
|
||||
return {
|
||||
groupKey: rule.groupKey,
|
||||
name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?',
|
||||
amount: groupAmount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
// For specific elementTypes:
|
||||
else if (rule.elementTypeKey) {
|
||||
const amount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey;
|
||||
return contentTypeKey === rule.elementTypeKey;
|
||||
}).length;
|
||||
if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) {
|
||||
this.#invalidBlockTypeLimits!.push({
|
||||
key: rule.elementTypeKey,
|
||||
name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?',
|
||||
amount: amount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
});
|
||||
return true;
|
||||
// For specific elementTypes:
|
||||
else if (rule.elementTypeKey) {
|
||||
const amount = layoutEntries.filter((entry) => {
|
||||
const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey;
|
||||
return contentTypeKey === rule.elementTypeKey;
|
||||
}).length;
|
||||
if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) {
|
||||
return {
|
||||
key: rule.elementTypeKey,
|
||||
name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?',
|
||||
amount: amount,
|
||||
minRequirement: minAllowed,
|
||||
maxRequirement: maxAllowed,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Lets fail cause the rule was bad.
|
||||
console.error('Invalid block type limit rule.', rule);
|
||||
return false;
|
||||
});
|
||||
// Lets fail cause the rule was bad.
|
||||
console.error('Invalid block type limit rule.', rule);
|
||||
return undefined;
|
||||
})
|
||||
.filter((x) => x !== undefined) as Array<UmbBlockGridAreaTypeInvalidRuleType>;
|
||||
const hasInvalidRules = this.#invalidBlockTypeLimits.length > 0;
|
||||
return hasInvalidRules === false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export const UMB_BLOCK_GRID_TYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
type: 'sidebar',
|
||||
size: 'large',
|
||||
},
|
||||
data: { entityType: UMB_BLOCK_GRID_TYPE, preset: { allowAtRoot: true } },
|
||||
data: { entityType: UMB_BLOCK_GRID_TYPE, preset: { allowAtRoot: true, allowInAreas: true } },
|
||||
},
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType' | 'preset'>, UmbWorkspaceModalValue>;
|
||||
|
||||
@@ -48,6 +48,15 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
|
||||
}
|
||||
private _contentUdi?: string | undefined;
|
||||
|
||||
/**
|
||||
* Sets the element to readonly mode, meaning value cannot be changed but still able to read and select its content.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public readonly = false;
|
||||
|
||||
#context = new UmbBlockListEntryContext(this);
|
||||
|
||||
@state()
|
||||
@@ -252,33 +261,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
|
||||
>${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()}</umb-extension-slot
|
||||
>
|
||||
<uui-action-bar>
|
||||
${this._showContentEdit && this._workspaceEditContentPath
|
||||
? html`<uui-button
|
||||
label="edit"
|
||||
look="secondary"
|
||||
color=${this._contentInvalid ? 'danger' : ''}
|
||||
href=${this._workspaceEditContentPath}>
|
||||
<uui-icon name="icon-edit"></uui-icon>
|
||||
${this._contentInvalid
|
||||
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
|
||||
: nothing}
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
${this._hasSettings && this._workspaceEditSettingsPath
|
||||
? html`<uui-button
|
||||
label="Edit settings"
|
||||
look="secondary"
|
||||
color=${this._settingsInvalid ? 'danger' : ''}
|
||||
href=${this._workspaceEditSettingsPath}>
|
||||
<uui-icon name="icon-settings"></uui-icon>
|
||||
${this._settingsInvalid
|
||||
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
|
||||
: nothing}
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
<uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
|
||||
<uui-icon name="icon-remove"></uui-icon>
|
||||
</uui-button>
|
||||
${this.#renderEditContentAction()} ${this.#renderEditSettingsAction()} ${this.#renderDeleteAction()}
|
||||
</uui-action-bar>
|
||||
${!this._showContentEdit && this._contentInvalid
|
||||
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
|
||||
@@ -286,6 +269,45 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
|
||||
`;
|
||||
}
|
||||
|
||||
#renderEditContentAction() {
|
||||
return html` ${this._showContentEdit && this._workspaceEditContentPath
|
||||
? html`<uui-button
|
||||
label="edit"
|
||||
look="secondary"
|
||||
color=${this._contentInvalid ? 'danger' : ''}
|
||||
href=${this._workspaceEditContentPath}>
|
||||
<uui-icon name="icon-edit"></uui-icon>
|
||||
${this._contentInvalid
|
||||
? html`<uui-badge attention color="danger" label="Invalid content">!</uui-badge>`
|
||||
: nothing}
|
||||
</uui-button>`
|
||||
: nothing}`;
|
||||
}
|
||||
|
||||
#renderEditSettingsAction() {
|
||||
return html`
|
||||
${this._hasSettings && this._workspaceEditSettingsPath
|
||||
? html`<uui-button
|
||||
label="Edit settings"
|
||||
look="secondary"
|
||||
color=${this._settingsInvalid ? 'danger' : ''}
|
||||
href=${this._workspaceEditSettingsPath}>
|
||||
<uui-icon name="icon-settings"></uui-icon>
|
||||
${this._settingsInvalid
|
||||
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
|
||||
: nothing}
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
#renderDeleteAction() {
|
||||
if (this.readonly) return nothing;
|
||||
return html` <uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
|
||||
<uui-icon name="icon-remove"></uui-icon>
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this.#renderBlock();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export const manifests: Array<ManifestTypes> = [
|
||||
propertyEditorSchemaAlias: UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS,
|
||||
icon: 'icon-thumbnail-list',
|
||||
group: 'lists',
|
||||
supportsReadOnly: true,
|
||||
settings: {
|
||||
properties: [
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { UmbBlockListLayoutModel, UmbBlockListValueModel } from '../../type
|
||||
import type { UmbBlockListEntryElement } from '../../components/block-list-entry/index.js';
|
||||
import { UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS } from './manifests.js';
|
||||
import { UmbLitElement, umbDestroyOnDisconnect } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { html, customElement, property, state, repeat, css } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, customElement, property, state, repeat, css, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbPropertyEditorUiElement, UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import {
|
||||
@@ -113,6 +113,27 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public get readonly() {
|
||||
return this.#readonly;
|
||||
}
|
||||
public set readonly(value) {
|
||||
this.#readonly = value;
|
||||
|
||||
if (this.#readonly) {
|
||||
this.#sorter.disable();
|
||||
} else {
|
||||
this.#sorter.enable();
|
||||
}
|
||||
}
|
||||
#readonly = false;
|
||||
|
||||
@state()
|
||||
private _limitMin?: number;
|
||||
@state()
|
||||
@@ -205,6 +226,32 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html` ${repeat(
|
||||
this._layouts,
|
||||
(x) => x.contentUdi,
|
||||
(layoutEntry, index) => html`
|
||||
${this.#renderInlineCreateButton(index)}
|
||||
<umb-block-list-entry
|
||||
.contentUdi=${layoutEntry.contentUdi}
|
||||
.layout=${layoutEntry}
|
||||
?readonly=${this.readonly}
|
||||
${umbDestroyOnDisconnect()}>
|
||||
</umb-block-list-entry>
|
||||
`,
|
||||
)}
|
||||
<uui-button-group> ${this.#renderCreateButton()} ${this.#renderPasteButton()} </uui-button-group>`;
|
||||
}
|
||||
|
||||
#renderInlineCreateButton(index: number) {
|
||||
if (this.readonly) return nothing;
|
||||
return html`<uui-button-inline-create
|
||||
label=${this._createButtonLabel}
|
||||
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>`;
|
||||
}
|
||||
|
||||
#renderCreateButton() {
|
||||
if (this.readonly) return nothing;
|
||||
|
||||
let createPath: string | undefined;
|
||||
if (this._blocks?.length === 1) {
|
||||
const elementKey = this._blocks[0].contentElementTypeKey;
|
||||
@@ -213,28 +260,22 @@ export class UmbPropertyEditorUIBlockListElement
|
||||
} else {
|
||||
createPath = this._catalogueRouteBuilder?.({ view: 'create', index: -1 });
|
||||
}
|
||||
return html` ${repeat(
|
||||
this._layouts,
|
||||
(x) => x.contentUdi,
|
||||
(layoutEntry, index) =>
|
||||
html`<uui-button-inline-create
|
||||
label=${this._createButtonLabel}
|
||||
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>
|
||||
<umb-block-list-entry
|
||||
.contentUdi=${layoutEntry.contentUdi}
|
||||
.layout=${layoutEntry}
|
||||
${umbDestroyOnDisconnect()}>
|
||||
</umb-block-list-entry> `,
|
||||
)}
|
||||
<uui-button-group>
|
||||
<uui-button look="placeholder" label=${this._createButtonLabel} href=${createPath ?? ''}></uui-button>
|
||||
<uui-button
|
||||
label=${this.localize.term('content_createFromClipboard')}
|
||||
look="placeholder"
|
||||
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
|
||||
<uui-icon name="icon-paste-in"></uui-icon>
|
||||
</uui-button>
|
||||
</uui-button-group>`;
|
||||
|
||||
return html`
|
||||
<uui-button look="placeholder" label=${this._createButtonLabel} href=${createPath ?? ''}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderPasteButton() {
|
||||
if (this.readonly) return nothing;
|
||||
return html`
|
||||
<uui-button
|
||||
label=${this.localize.term('content_createFromClipboard')}
|
||||
look="placeholder"
|
||||
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
|
||||
<uui-icon name="icon-paste-in"></uui-icon>
|
||||
</uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
|
||||
@@ -139,6 +139,9 @@ export abstract class UmbBlockManagerContext<
|
||||
this.#contentTypes.appendOne(data);
|
||||
}
|
||||
|
||||
getContentTypeKeyOfContentUdi(contentUdi: string) {
|
||||
return this.getContentOf(contentUdi)?.contentTypeKey;
|
||||
}
|
||||
contentTypeOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.asObservablePart((source) => source.find((x) => x.unique === contentTypeKey));
|
||||
}
|
||||
@@ -148,9 +151,6 @@ export abstract class UmbBlockManagerContext<
|
||||
getContentTypeNameOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.name;
|
||||
}
|
||||
getContentTypeKeyOf(contentTypeKey: string) {
|
||||
return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.unique;
|
||||
}
|
||||
getContentTypeHasProperties(contentTypeKey: string) {
|
||||
const properties = this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.properties;
|
||||
return properties ? properties.length > 0 : false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT } from './block-element-property-dataset.context-token.js';
|
||||
import type { UmbBlockElementManager } from './block-element-manager.js';
|
||||
import { UMB_BLOCK_WORKSPACE_CONTEXT } from './block-workspace.context-token.js';
|
||||
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
@@ -36,6 +37,16 @@ export class UmbBlockElementPropertyDatasetContext extends UmbControllerBase imp
|
||||
super(host, UMB_PROPERTY_DATASET_CONTEXT.toString());
|
||||
this.#elementManager = elementManager;
|
||||
|
||||
this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (workspace) => {
|
||||
this.observe(
|
||||
workspace.readOnlyState.isOn,
|
||||
(value) => {
|
||||
this.#currentVariantCultureIsReadOnly.setValue(value);
|
||||
},
|
||||
'umbObserveReadOnlyStates',
|
||||
);
|
||||
});
|
||||
|
||||
this.provideContext(UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { UmbClassState, UmbObjectState, UmbStringState } from '@umbraco-cms/back
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UMB_MODAL_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal';
|
||||
import { decodeFilePath } from '@umbraco-cms/backoffice/utils';
|
||||
import { decodeFilePath, UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
|
||||
import {
|
||||
UMB_BLOCK_ENTRIES_CONTEXT,
|
||||
UMB_BLOCK_MANAGER_CONTEXT,
|
||||
@@ -60,6 +60,8 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
|
||||
#variantId = new UmbClassState<UmbVariantId | undefined>(undefined);
|
||||
readonly variantId = this.#variantId.asObservable();
|
||||
|
||||
public readonly readOnlyState = new UmbReadOnlyVariantStateManager(this);
|
||||
|
||||
constructor(host: UmbControllerHost, workspaceArgs: { manifest: ManifestWorkspace }) {
|
||||
super(host, workspaceArgs.manifest.alias);
|
||||
const manifest = workspaceArgs.manifest;
|
||||
@@ -92,6 +94,25 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
|
||||
this.observe(context.variantId, (variantId) => {
|
||||
this.#variantId.setValue(variantId);
|
||||
});
|
||||
|
||||
// If the current property is readonly all inner block content should also be readonly.
|
||||
this.observe(context.isReadOnly, (isReadOnly) => {
|
||||
const unique = 'UMB_PROPERTY_CONTEXT';
|
||||
const variantId = this.#variantId.getValue();
|
||||
if (variantId === undefined) return;
|
||||
|
||||
if (isReadOnly) {
|
||||
const state = {
|
||||
unique,
|
||||
variantId,
|
||||
message: '',
|
||||
};
|
||||
|
||||
this.readOnlyState?.addState(state);
|
||||
} else {
|
||||
this.readOnlyState?.removeState(unique);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.observe(this.variantId, (variantId) => {
|
||||
|
||||
@@ -76,6 +76,15 @@ export class UmbStateManager<StateType extends UmbState = UmbState> extends UmbC
|
||||
this._states.setValue(this._states.getValue().filter((x) => !uniques.includes(x.unique)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all states from the state manager
|
||||
* @returns {StateType[]} {StateType[]} All states in the state manager
|
||||
* @memberof UmbStateManager
|
||||
*/
|
||||
getStates() {
|
||||
return this._states.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all states from the state manager
|
||||
* @memberof UmbStateManager
|
||||
|
||||
@@ -165,6 +165,9 @@ export function UmbFormControlMixin<
|
||||
this._internals = this.attachInternals();
|
||||
|
||||
this.addEventListener('blur', () => {
|
||||
/*if (e.composedPath().some((x) => x === this)) {
|
||||
return;
|
||||
}*/
|
||||
this.pristine = false;
|
||||
this.checkValidity();
|
||||
});
|
||||
|
||||
@@ -101,7 +101,7 @@ export class UmbInputDataTypeElement extends UmbFormControlMixin(UmbLitElement,
|
||||
label="Select Property Editor"
|
||||
look="placeholder"
|
||||
color="default"
|
||||
@focus=${() => {
|
||||
@blur=${() => {
|
||||
this.pristine = false;
|
||||
}}
|
||||
.href=${this._createRoute}></uui-button>
|
||||
|
||||
@@ -62,7 +62,8 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<
|
||||
</p>
|
||||
<umb-document-variant-language-picker
|
||||
.selectionManager=${this.#selectionManager}
|
||||
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
|
||||
.variantLanguageOptions=${this._options}
|
||||
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
|
||||
|
||||
<p><umb-localize key="content_variantsWillBeSaved">All new variants will be saved.</umb-localize></p>
|
||||
|
||||
|
||||
@@ -82,7 +82,8 @@ export class UmbDocumentPublishWithDescendantsModalElement extends UmbModalBaseE
|
||||
|
||||
<umb-document-variant-language-picker
|
||||
.selectionManager=${this.#selectionManager}
|
||||
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
|
||||
.variantLanguageOptions=${this._options}
|
||||
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
|
||||
|
||||
<uui-form-layout-item>
|
||||
<uui-toggle
|
||||
|
||||
@@ -53,7 +53,8 @@ export class UmbDocumentSaveModalElement extends UmbModalBaseElement<
|
||||
|
||||
<umb-document-variant-language-picker
|
||||
.selectionManager=${this.#selectionManager}
|
||||
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
|
||||
.variantLanguageOptions=${this._options}
|
||||
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
|
||||
|
||||
<div slot="actions">
|
||||
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
|
||||
|
||||
@@ -101,21 +101,28 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement<
|
||||
return repeat(
|
||||
this._options,
|
||||
(option) => option.unique,
|
||||
(option) => html`
|
||||
<uui-menu-item
|
||||
selectable
|
||||
label=${option.variant?.name ?? option.language.name}
|
||||
@selected=${() => this.#selectionManager.select(option.unique)}
|
||||
@deselected=${() => this.#selectionManager.deselect(option.unique)}
|
||||
?selected=${this.#isSelected(option.unique)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
|
||||
</uui-menu-item>
|
||||
${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))}
|
||||
`,
|
||||
(option) => this.#renderItem(option),
|
||||
);
|
||||
}
|
||||
|
||||
#renderItem(option: UmbDocumentVariantOptionModel) {
|
||||
const pickable = this.data?.pickableFilter ? this.data.pickableFilter(option) : () => true;
|
||||
|
||||
return html`
|
||||
<uui-menu-item
|
||||
?selectable=${pickable}
|
||||
?disabled=${!pickable}
|
||||
label=${option.variant?.name ?? option.language.name}
|
||||
@selected=${() => this.#selectionManager.select(option.unique)}
|
||||
@deselected=${() => this.#selectionManager.deselect(option.unique)}
|
||||
?selected=${this.#isSelected(option.unique)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
|
||||
</uui-menu-item>
|
||||
${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))}
|
||||
`;
|
||||
}
|
||||
|
||||
#renderPublishDateInput(option: UmbDocumentVariantOptionModel) {
|
||||
return html`<div class="publish-date">
|
||||
<uui-form-layout-item>
|
||||
|
||||
@@ -29,33 +29,45 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement {
|
||||
@state()
|
||||
_selection: Array<string> = [];
|
||||
|
||||
/**
|
||||
* A filter function that determines if an item is pickableFilter or not.
|
||||
* @memberof UmbDocumentVariantLanguagePickerElement
|
||||
* @returns {boolean} - True if the item is pickableFilter, false otherwise.
|
||||
*/
|
||||
@property({ attribute: false })
|
||||
public pickableFilter?: (item: UmbDocumentVariantOptionModel) => boolean;
|
||||
|
||||
override render() {
|
||||
return this.variantLanguageOptions.length
|
||||
? repeat(
|
||||
this.variantLanguageOptions,
|
||||
(option) => option.unique,
|
||||
(option) => html`
|
||||
<uui-menu-item
|
||||
selectable
|
||||
label=${option.variant?.name ?? option.language.name}
|
||||
@selected=${() => this.selectionManager.select(option.unique)}
|
||||
@deselected=${() => this.selectionManager.deselect(option.unique)}
|
||||
?selected=${this._selection.includes(option.unique)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
|
||||
</uui-menu-item>
|
||||
`,
|
||||
(option) => html` ${this.#renderItem(option)} `,
|
||||
)
|
||||
: html`<uui-box>
|
||||
<umb-localize key="content_noVariantsToProcess">There are no available variants</umb-localize>
|
||||
</uui-box>`;
|
||||
}
|
||||
|
||||
#renderItem(option: UmbDocumentVariantOptionModel) {
|
||||
const pickable = this.pickableFilter ? this.pickableFilter(option) : () => true;
|
||||
return html`
|
||||
<uui-menu-item
|
||||
?selectable=${pickable}
|
||||
?disabled=${!pickable}
|
||||
label=${option.variant?.name ?? option.language.name}
|
||||
@selected=${() => this.selectionManager.select(option.unique)}
|
||||
@deselected=${() => this.selectionManager.deselect(option.unique)}
|
||||
?selected=${this._selection.includes(option.unique)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
|
||||
</uui-menu-item>
|
||||
`;
|
||||
}
|
||||
|
||||
static renderLabel(option: UmbDocumentVariantOptionModel) {
|
||||
return html`<div class="label" slot="label">
|
||||
<strong>
|
||||
${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name}
|
||||
</strong>
|
||||
<strong> ${option.language.name} </strong>
|
||||
<div class="label-status">${UmbDocumentVariantLanguagePickerElement.renderVariantStatus(option)}</div>
|
||||
${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED
|
||||
? html`<div class="label-status">
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { UmbDocumentVariantOptionModel } from '../types.js';
|
||||
|
||||
export interface UmbDocumentVariantPickerData {
|
||||
options: Array<UmbDocumentVariantOptionModel>;
|
||||
pickableFilter?: (variantOption: UmbDocumentVariantOptionModel) => boolean;
|
||||
}
|
||||
|
||||
export interface UmbDocumentVariantPickerValue {
|
||||
|
||||
@@ -101,7 +101,8 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement<
|
||||
|
||||
<umb-document-variant-language-picker
|
||||
.selectionManager=${this.#selectionManager}
|
||||
.variantLanguageOptions=${this._options}></umb-document-variant-language-picker>
|
||||
.variantLanguageOptions=${this._options}
|
||||
.pickableFilter=${this.data?.pickableFilter}></umb-document-variant-language-picker>
|
||||
|
||||
<p>
|
||||
<umb-localize key="prompt_confirmUnpublish">
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../paths.js';
|
||||
import { UMB_DOCUMENTS_SECTION_PATH } from '../../section/paths.js';
|
||||
import { UmbDocumentPreviewRepository } from '../repository/preview/index.js';
|
||||
import { sortVariants } from '../utils.js';
|
||||
|
||||
import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js';
|
||||
import { UmbEntityContext } from '@umbraco-cms/backoffice/entity';
|
||||
import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
@@ -68,6 +68,7 @@ import type { UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content
|
||||
import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin';
|
||||
import { UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
|
||||
import { sortVariants } from '../utils.js';
|
||||
|
||||
type EntityType = UmbDocumentDetailModel;
|
||||
export class UmbDocumentWorkspaceContext
|
||||
@@ -104,7 +105,7 @@ export class UmbDocumentWorkspaceContext
|
||||
/*#blueprint = new UmbObjectState<UmbDocumentBlueprintDetailModel | undefined>(undefined);
|
||||
public readonly blueprint = this.#blueprint.asObservable();*/
|
||||
|
||||
readOnlyState = new UmbReadOnlyVariantStateManager(this);
|
||||
public readOnlyState = new UmbReadOnlyVariantStateManager(this);
|
||||
|
||||
public isLoaded() {
|
||||
return this.#getDataPromise;
|
||||
@@ -637,6 +638,11 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
}
|
||||
|
||||
#readOnlyLanguageVariantsFilter = (option: UmbDocumentVariantOptionModel) => {
|
||||
const readOnlyCultures = this.readOnlyState.getStates().map((s) => s.variantId.culture);
|
||||
return readOnlyCultures.includes(option.culture) === false;
|
||||
};
|
||||
|
||||
async #handleSaveAndPreview() {
|
||||
const unique = this.getUnique();
|
||||
if (!unique) throw new Error('Unique is missing');
|
||||
@@ -681,6 +687,7 @@ export class UmbDocumentWorkspaceContext
|
||||
.open(this, UMB_DOCUMENT_PUBLISH_MODAL, {
|
||||
data: {
|
||||
options,
|
||||
pickableFilter: this.#readOnlyLanguageVariantsFilter,
|
||||
},
|
||||
value: { selection: selected },
|
||||
})
|
||||
@@ -786,6 +793,7 @@ export class UmbDocumentWorkspaceContext
|
||||
.open(this, UMB_DOCUMENT_SAVE_MODAL, {
|
||||
data: {
|
||||
options,
|
||||
pickableFilter: this.#readOnlyLanguageVariantsFilter,
|
||||
},
|
||||
value: { selection: selected },
|
||||
})
|
||||
@@ -833,6 +841,7 @@ export class UmbDocumentWorkspaceContext
|
||||
.open(this, UMB_DOCUMENT_SCHEDULE_MODAL, {
|
||||
data: {
|
||||
options,
|
||||
pickableFilter: this.#readOnlyLanguageVariantsFilter,
|
||||
},
|
||||
value: { selection: selected.map((unique) => ({ unique, schedule: {} })) },
|
||||
})
|
||||
@@ -873,6 +882,7 @@ export class UmbDocumentWorkspaceContext
|
||||
.open(this, UMB_DOCUMENT_PUBLISH_WITH_DESCENDANTS_MODAL, {
|
||||
data: {
|
||||
options,
|
||||
pickableFilter: this.#readOnlyLanguageVariantsFilter,
|
||||
},
|
||||
value: { selection: selected },
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user