diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts
index ebbec1134f..21ef577a45 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts
@@ -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;
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;
+ const hasInvalidRules = this.#invalidBlockTypeLimits.length > 0;
return hasInvalidRules === false;
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-type-workspace.modal-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-type-workspace.modal-token.ts
index f45bce646e..19d76caa88 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-type-workspace.modal-token.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/workspace/block-grid-type-workspace.modal-token.ts
@@ -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, UmbWorkspaceModalValue>;
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts
index 4beee2607a..0172936a37 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts
@@ -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()}
- ${this._showContentEdit && this._workspaceEditContentPath
- ? html`
-
- ${this._contentInvalid
- ? html`!`
- : nothing}
- `
- : nothing}
- ${this._hasSettings && this._workspaceEditSettingsPath
- ? html`
-
- ${this._settingsInvalid
- ? html`!`
- : nothing}
- `
- : nothing}
- this.#context.requestDelete()}>
-
-
+ ${this.#renderEditContentAction()} ${this.#renderEditSettingsAction()} ${this.#renderDeleteAction()}
${!this._showContentEdit && this._contentInvalid
? html`!`
@@ -286,6 +269,45 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
`;
}
+ #renderEditContentAction() {
+ return html` ${this._showContentEdit && this._workspaceEditContentPath
+ ? html`
+
+ ${this._contentInvalid
+ ? html`!`
+ : nothing}
+ `
+ : nothing}`;
+ }
+
+ #renderEditSettingsAction() {
+ return html`
+ ${this._hasSettings && this._workspaceEditSettingsPath
+ ? html`
+
+ ${this._settingsInvalid
+ ? html`!`
+ : nothing}
+ `
+ : nothing}
+ `;
+ }
+
+ #renderDeleteAction() {
+ if (this.readonly) return nothing;
+ return html` this.#context.requestDelete()}>
+
+ `;
+ }
+
override render() {
return this.#renderBlock();
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/manifests.ts
index c3d07105cc..e734bfaad1 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/manifests.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/manifests.ts
@@ -14,6 +14,7 @@ export const manifests: Array = [
propertyEditorSchemaAlias: UMB_BLOCK_LIST_PROPERTY_EDITOR_ALIAS,
icon: 'icon-thumbnail-list',
group: 'lists',
+ supportsReadOnly: true,
settings: {
properties: [
{
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts
index 2d60a0d689..5aa7bfe843 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts
@@ -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)}
+
+
+ `,
+ )}
+ ${this.#renderCreateButton()} ${this.#renderPasteButton()} `;
+ }
+
+ #renderInlineCreateButton(index: number) {
+ if (this.readonly) return nothing;
+ return html``;
+ }
+
+ #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`
-
- `,
- )}
-
-
-
-
-
- `;
+
+ return html`
+
+ `;
+ }
+
+ #renderPasteButton() {
+ if (this.readonly) return nothing;
+ return html`
+
+
+
+ `;
}
static override styles = [
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts
index df113d9b81..f44da55be5 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts
@@ -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;
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-property-dataset.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-property-dataset.context.ts
index c7451f7806..4db36b5497 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-property-dataset.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-property-dataset.context.ts
@@ -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);
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts
index 9ff8c1d3ad..19e304d8db 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts
@@ -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(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 {
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) => {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/state-manager/state.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/state-manager/state.manager.ts
index af6e8247c5..890f31b6e2 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/state-manager/state.manager.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/state-manager/state.manager.ts
@@ -76,6 +76,15 @@ export class UmbStateManager 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
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts
index e4f45a5518..e3b13b79be 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts
@@ -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();
});
diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts
index e6579bf1f6..df1cc59049 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts
@@ -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}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts
index 24cc03526e..f17353719b 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts
@@ -62,7 +62,8 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<
+ .variantLanguageOptions=${this._options}
+ .pickableFilter=${this.data?.pickableFilter}>
All new variants will be saved.
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-with-descendants-modal/document-publish-with-descendants-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-with-descendants-modal/document-publish-with-descendants-modal.element.ts
index 23a3a49e2c..1c1899711c 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-with-descendants-modal/document-publish-with-descendants-modal.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-with-descendants-modal/document-publish-with-descendants-modal.element.ts
@@ -82,7 +82,8 @@ export class UmbDocumentPublishWithDescendantsModalElement extends UmbModalBaseE
+ .variantLanguageOptions=${this._options}
+ .pickableFilter=${this.data?.pickableFilter}>
+ .variantLanguageOptions=${this._options}
+ .pickableFilter=${this.data?.pickableFilter}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts
index aa4b3e1575..643285640c 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts
@@ -101,21 +101,28 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement<
return repeat(
this._options,
(option) => option.unique,
- (option) => html`
-
this.#selectionManager.select(option.unique)}
- @deselected=${() => this.#selectionManager.deselect(option.unique)}
- ?selected=${this.#isSelected(option.unique)}>
-
- ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
-
- ${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`
+
this.#selectionManager.select(option.unique)}
+ @deselected=${() => this.#selectionManager.deselect(option.unique)}
+ ?selected=${this.#isSelected(option.unique)}>
+
+ ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
+
+ ${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))}
+ `;
+ }
+
#renderPublishDateInput(option: UmbDocumentVariantOptionModel) {
return html`
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts
index de6c4da99f..52d69df997 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts
@@ -29,33 +29,45 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement {
@state()
_selection: Array = [];
+ /**
+ * 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`
- this.selectionManager.select(option.unique)}
- @deselected=${() => this.selectionManager.deselect(option.unique)}
- ?selected=${this._selection.includes(option.unique)}>
-
- ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
-
- `,
+ (option) => html` ${this.#renderItem(option)} `,
)
: html`
There are no available variants
`;
}
+ #renderItem(option: UmbDocumentVariantOptionModel) {
+ const pickable = this.pickableFilter ? this.pickableFilter(option) : () => true;
+ return html`
+ this.selectionManager.select(option.unique)}
+ @deselected=${() => this.selectionManager.deselect(option.unique)}
+ ?selected=${this._selection.includes(option.unique)}>
+
+ ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)}
+
+ `;
+ }
+
static renderLabel(option: UmbDocumentVariantOptionModel) {
return html`
-
- ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name}
-
+
${option.language.name}
${UmbDocumentVariantLanguagePickerElement.renderVariantStatus(option)}
${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED
? html`
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts
index d131490619..d3e91cdf89 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts
@@ -2,6 +2,7 @@ import type { UmbDocumentVariantOptionModel } from '../types.js';
export interface UmbDocumentVariantPickerData {
options: Array
;
+ pickableFilter?: (variantOption: UmbDocumentVariantOptionModel) => boolean;
}
export interface UmbDocumentVariantPickerValue {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/unpublish-modal/document-unpublish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/unpublish-modal/document-unpublish-modal.element.ts
index 6838d56fa3..141b7cfbc6 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/unpublish-modal/document-unpublish-modal.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/unpublish-modal/document-unpublish-modal.element.ts
@@ -101,7 +101,8 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement<
+ .variantLanguageOptions=${this._options}
+ .pickableFilter=${this.data?.pickableFilter}>
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts
index 9186d6db4b..74b0629683 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts
@@ -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(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 },
})