refactor
This commit is contained in:
@@ -8,11 +8,11 @@ import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observab
|
||||
* @export
|
||||
* @class UmbSelectionManager
|
||||
*/
|
||||
export class UmbSelectionManager extends UmbBaseController {
|
||||
export class UmbSelectionManager<ValueType = string | null> extends UmbBaseController {
|
||||
#selectable = new UmbBooleanState(false);
|
||||
public readonly selectable = this.#selectable.asObservable();
|
||||
|
||||
#selection = new UmbArrayState(<Array<string | null>>[], (x) => x);
|
||||
#selection = new UmbArrayState(<Array<ValueType>>[], (x) => x);
|
||||
public readonly selection = this.#selection.asObservable();
|
||||
|
||||
#multiple = new UmbBooleanState(false);
|
||||
@@ -51,10 +51,10 @@ export class UmbSelectionManager extends UmbBaseController {
|
||||
|
||||
/**
|
||||
* Sets the current selection.
|
||||
* @param {Array<string | null>} value
|
||||
* @param {Array<ValueType>} value
|
||||
* @memberof UmbSelectionManager
|
||||
*/
|
||||
public setSelection(value: Array<string | null>) {
|
||||
public setSelection(value: Array<ValueType>) {
|
||||
if (this.getSelectable() === false) return;
|
||||
if (value === undefined) throw new Error('Value cannot be undefined');
|
||||
const newSelection = this.getMultiple() ? value : value.slice(0, 1);
|
||||
@@ -87,20 +87,20 @@ export class UmbSelectionManager extends UmbBaseController {
|
||||
|
||||
/**
|
||||
* Toggles the given unique id in the current selection.
|
||||
* @param {(string | null)} unique
|
||||
* @param {(ValueType)} unique
|
||||
* @memberof UmbSelectionManager
|
||||
*/
|
||||
public toggleSelect(unique: string | null) {
|
||||
public toggleSelect(unique: ValueType) {
|
||||
if (this.getSelectable() === false) return;
|
||||
this.isSelected(unique) ? this.deselect(unique) : this.select(unique);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given unique id to the current selection.
|
||||
* @param {(string | null)} unique
|
||||
* @param {(ValueType)} unique
|
||||
* @memberof UmbSelectionManager
|
||||
*/
|
||||
public select(unique: string | null) {
|
||||
public select(unique: ValueType) {
|
||||
if (this.getSelectable() === false) return;
|
||||
if (this.isSelected(unique)) return;
|
||||
const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique];
|
||||
@@ -110,10 +110,10 @@ export class UmbSelectionManager extends UmbBaseController {
|
||||
|
||||
/**
|
||||
* Removes the given unique id from the current selection.
|
||||
* @param {(string | null)} unique
|
||||
* @param {(ValueType)} unique
|
||||
* @memberof UmbSelectionManager
|
||||
*/
|
||||
public deselect(unique: string | null) {
|
||||
public deselect(unique: ValueType) {
|
||||
if (this.getSelectable() === false) return;
|
||||
const newSelection = this.getSelection().filter((x) => x !== unique);
|
||||
this.#selection.setValue(newSelection);
|
||||
@@ -122,11 +122,11 @@ export class UmbSelectionManager extends UmbBaseController {
|
||||
|
||||
/**
|
||||
* Returns true if the given unique id is selected.
|
||||
* @param {(string | null)} unique
|
||||
* @param {(ValueType)} unique
|
||||
* @return {*}
|
||||
* @memberof UmbSelectionManager
|
||||
*/
|
||||
public isSelected(unique: string | null) {
|
||||
public isSelected(unique: ValueType) {
|
||||
return this.getSelection().includes(unique);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,4 +11,8 @@ export interface UmbVariantModel {
|
||||
export interface UmbVariantOptionModel<VariantType extends UmbVariantModel = UmbVariantModel> {
|
||||
variant?: VariantType;
|
||||
language: UmbLanguageDetailModel;
|
||||
/**
|
||||
* The unique identifier is a VariantId string.
|
||||
*/
|
||||
unique: string;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ export function variantPropertiesObjectToString(variant: UmbObjectWithVariantPro
|
||||
|
||||
export const UMB_INVARIANT_CULTURE = 'invariant';
|
||||
|
||||
/**
|
||||
* An identifier representing a Variant. This is at current state a culture and a segment.
|
||||
* The identifier is not specific for ContentType Variants, but is used for many type of identification of a culture and a segment. One case is any property of a ContentType can be resolved into a VariantId depending on their structural settings such as Vary by Culture and Vary by Segmentation.
|
||||
*/
|
||||
export class UmbVariantId {
|
||||
public static Create(variantData: UmbObjectWithVariantProperties): UmbVariantId {
|
||||
return Object.freeze(new UmbVariantId(variantData.culture, variantData.segment));
|
||||
@@ -30,7 +34,7 @@ export class UmbVariantId {
|
||||
public readonly segment: string | null = null;
|
||||
public readonly schedule: { publishTime?: string | null; unpublishTime?: string | null } | null = null;
|
||||
|
||||
constructor(culture: string | null, segment: string | null) {
|
||||
constructor(culture?: string | null, segment?: string | null) {
|
||||
this.culture = (culture === UMB_INVARIANT_CULTURE ? null : culture?.toLowerCase()) ?? null;
|
||||
this.segment = segment ?? null;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,12 @@ import {
|
||||
UUIInputEvent,
|
||||
type UUIPopoverContainerElement,
|
||||
} from '@umbraco-cms/backoffice/external/uui';
|
||||
import { css, html, nothing, customElement, state, ifDefined, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, nothing, customElement, state, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UMB_WORKSPACE_SPLIT_VIEW_CONTEXT, type ActiveVariant } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbDocumentVariantModel } from '@umbraco-cms/backoffice/document';
|
||||
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
|
||||
import { UmbArrayState, combineObservables } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbDocumentWorkspaceContext } from '@umbraco-cms/backoffice/document';
|
||||
|
||||
type UmbDocumentVariantOption = {
|
||||
culture: string | null;
|
||||
@@ -38,9 +35,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
_activeVariants: Array<ActiveVariant> = [];
|
||||
|
||||
@state()
|
||||
get _activeVariantsCultures(): string[] {
|
||||
return this._activeVariants.map((el) => el.culture ?? '') ?? [];
|
||||
}
|
||||
_activeVariantsCultures: string[] = [];
|
||||
|
||||
#splitViewContext?: typeof UMB_WORKSPACE_SPLIT_VIEW_CONTEXT.TYPE;
|
||||
#datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
|
||||
@@ -57,9 +52,6 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
@state()
|
||||
private _variantSelectorOpen = false;
|
||||
|
||||
#languageRepository = new UmbLanguageCollectionRepository(this);
|
||||
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -67,37 +59,36 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
this.#splitViewContext = instance;
|
||||
this.#observeVariants();
|
||||
this.#observeActiveVariants();
|
||||
this.#observeCurrentVariant();
|
||||
});
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (instance) => {
|
||||
this.#datasetContext = instance;
|
||||
this.#observeDatasetContext();
|
||||
this.#observeCurrentVariant();
|
||||
});
|
||||
|
||||
this.#loadLanguages();
|
||||
}
|
||||
|
||||
async #loadLanguages() {
|
||||
const { data: languages } = await this.#languageRepository.requestCollection({});
|
||||
if (!languages) return;
|
||||
this.#languages.setValue(languages.items);
|
||||
}
|
||||
|
||||
async #observeVariants() {
|
||||
if (!this.#splitViewContext) return;
|
||||
|
||||
const workspaceContext = this.#splitViewContext.getWorkspaceContext();
|
||||
// NOTICE: This is dirty (the TypeScript casting), we can only accept doing this so far because we currently only use the Variant Selector on Document Workspace. [NL]
|
||||
// This would need a refactor to enable the code below to work with different ContentTypes. Main problem here is the state, which is not generic for them all. [NL]
|
||||
const workspaceContext = this.#splitViewContext.getWorkspaceContext() as UmbDocumentWorkspaceContext;
|
||||
if (!workspaceContext) throw new Error('Split View Workspace context not found');
|
||||
|
||||
this.observe(
|
||||
workspaceContext.allowedVariants,
|
||||
(variants) => {
|
||||
this._variants = variants.map<UmbDocumentVariantOption>((variant) => {
|
||||
workspaceContext.variantOptions,
|
||||
(options) => {
|
||||
this._variants = options.map<UmbDocumentVariantOption>((option) => {
|
||||
const name = option.variant?.name ?? option.language.name;
|
||||
const segment = option.variant?.segment ?? null;
|
||||
return {
|
||||
culture: variant.culture,
|
||||
segment: variant.segment,
|
||||
title: variant.name + (variant.segment ? ` — ${variant.segment}` : ''),
|
||||
displayName: variant.name + (variant.segment ? ` — ${variant.segment}` : ''),
|
||||
state: (variant as UmbDocumentVariantModel).state ?? DocumentVariantStateModel.NOT_CREATED,
|
||||
// Notice the option object has a unique property, but it's not used here. (Its equivalent to a UmbVariantId string) [NL]
|
||||
culture: option.language.unique,
|
||||
segment: segment,
|
||||
title: name + (segment ? ` — ${segment}` : ''),
|
||||
displayName: name + (segment ? ` — ${segment}` : ''),
|
||||
state: option.variant?.state ?? DocumentVariantStateModel.NOT_CREATED,
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -115,6 +106,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
(activeVariants) => {
|
||||
if (activeVariants) {
|
||||
this._activeVariants = activeVariants;
|
||||
this._activeVariantsCultures = this._activeVariants.map((el) => el.culture ?? '') ?? [];
|
||||
}
|
||||
},
|
||||
'_observeActiveVariants',
|
||||
@@ -124,24 +116,6 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
|
||||
async #observeDatasetContext() {
|
||||
if (!this.#datasetContext) return;
|
||||
|
||||
const variantId = this.#datasetContext.getVariantId();
|
||||
|
||||
const culture = variantId.culture;
|
||||
const segment = variantId.segment;
|
||||
|
||||
this.observe(
|
||||
this.#languages.asObservablePart(
|
||||
(languages) => languages.find((language) => language.unique === variantId.culture)?.name ?? '',
|
||||
),
|
||||
(languageName) => {
|
||||
this._variantDisplayName = (languageName ? languageName : '') + (segment ? ' — ' + segment : '');
|
||||
this._variantTitleName =
|
||||
(languageName ? `${languageName} (${culture})` : '') + (segment ? ' — ' + segment : '');
|
||||
},
|
||||
'_languages',
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this.#datasetContext.name,
|
||||
(name) => {
|
||||
@@ -151,6 +125,30 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
);
|
||||
}
|
||||
|
||||
async #observeCurrentVariant() {
|
||||
if (!this.#datasetContext || !this.#splitViewContext) return;
|
||||
const workspaceContext = this.#splitViewContext.getWorkspaceContext();
|
||||
if (!workspaceContext) return;
|
||||
|
||||
const variantId = this.#datasetContext.getVariantId();
|
||||
// Find the variant option matching this, to get the language name...
|
||||
|
||||
const culture = variantId.culture;
|
||||
const segment = variantId.segment;
|
||||
|
||||
this.observe(
|
||||
workspaceContext.variantOptions,
|
||||
(options) => {
|
||||
const option = options.find((option) => option.language.unique === culture);
|
||||
const languageName = option?.language.name;
|
||||
this._variantDisplayName = (languageName ? languageName : '') + (segment ? ` — ${segment}` : '');
|
||||
this._variantTitleName =
|
||||
(languageName ? `${languageName} (${culture})` : '') + (segment ? ` — ${segment}` : '');
|
||||
},
|
||||
'_currentLanguage',
|
||||
);
|
||||
}
|
||||
|
||||
#handleInput(event: UUIInputEvent) {
|
||||
if (event instanceof UUIInputEvent) {
|
||||
const target = event.composedPath()[0] as UUIInputElement;
|
||||
|
||||
@@ -2,17 +2,18 @@ import type { UmbWorkspaceSplitViewManager } from '../workspace-split-view-manag
|
||||
import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbVariantId, UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbVariantId, UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export interface UmbVariantableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface {
|
||||
export interface UmbVariantableWorkspaceContextInterface<VariantType extends UmbVariantModel = UmbVariantModel>
|
||||
extends UmbSaveableWorkspaceContextInterface {
|
||||
// Name:
|
||||
getName(variantId?: UmbVariantId): string | undefined;
|
||||
setName(name: string, variantId?: UmbVariantId): void;
|
||||
|
||||
// Variant:
|
||||
variants: Observable<Array<UmbVariantModel>>;
|
||||
allowedVariants: Observable<Array<UmbVariantModel>>;
|
||||
variantOptions: Observable<Array<UmbVariantOptionModel<VariantType>>>;
|
||||
splitView: UmbWorkspaceSplitViewManager;
|
||||
getVariant(variantId: UmbVariantId): UmbVariantModel | undefined;
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export class UmbWorkspaceSplitViewManager {
|
||||
const currentVariant = this.getActiveVariants()[0];
|
||||
const workspaceRoute = this.getWorkspaceRoute();
|
||||
if (currentVariant && workspaceRoute) {
|
||||
history.pushState(null, '', `${workspaceRoute}/${new UmbVariantId(currentVariant)}_&_${newVariant.toString()}`);
|
||||
history.pushState(null, '', `${workspaceRoute}/${UmbVariantId.Create(currentVariant)}_&_${newVariant}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -83,7 +83,7 @@ export class UmbWorkspaceSplitViewManager {
|
||||
if (activeVariants && index < activeVariants.length) {
|
||||
const newVariants = activeVariants.filter((x) => x.index !== index);
|
||||
|
||||
const variantPart: string = newVariants.map((v) => new UmbVariantId(v).toString()).join('_&_');
|
||||
const variantPart: string = newVariants.map((v) => UmbVariantId.Create(v)).join('_&_');
|
||||
|
||||
history.pushState(null, '', `${workspaceRoute}/${variantPart}`);
|
||||
return true;
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/index.js';
|
||||
import type { UmbDocumentPublishingRepository } from '../repository/index.js';
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbPublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentPublishingRepository> {
|
||||
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
|
||||
|
||||
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string, entityType: string) {
|
||||
super(host, repositoryAlias, unique, entityType);
|
||||
|
||||
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (context) => {
|
||||
this.#variantManagerContext = context;
|
||||
});
|
||||
}
|
||||
|
||||
async execute() {
|
||||
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
await this.#variantManagerContext.publish(this.unique);
|
||||
throw new Error('This action not implemented.');
|
||||
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
//await this.#variantManagerContext.publish(this.unique);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/index.js';
|
||||
import type { UmbDocumentPublishingRepository } from '../repository/index.js';
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentPublishingRepository> {
|
||||
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
|
||||
|
||||
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string, entityType: string) {
|
||||
super(host, repositoryAlias, unique, entityType);
|
||||
|
||||
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (context) => {
|
||||
this.#variantManagerContext = context;
|
||||
});
|
||||
}
|
||||
|
||||
async execute() {
|
||||
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
await this.#variantManagerContext.unpublish(this.unique);
|
||||
throw new Error('This action not implemented.');
|
||||
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
//await this.#variantManagerContext.unpublish(this.unique);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbDocumentVariantState, type UmbDocumentVariantModel } from '../types.js';
|
||||
import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../types.js';
|
||||
import { UmbDocumentDetailRepository } from '../repository/detail/document-detail.repository.js';
|
||||
import {
|
||||
UMB_DOCUMENT_LANGUAGE_PICKER_MODAL,
|
||||
@@ -47,9 +47,9 @@ export class UmbDocumentVariantManagerContext
|
||||
* @returns The selected variants to perform the operation on.
|
||||
*/
|
||||
async pickVariants(
|
||||
availableVariants: Array<UmbDocumentVariantModel>,
|
||||
type: UmbDocumentVariantPickerModalData['type'],
|
||||
activeVariantCultures?: Array<string>,
|
||||
availableVariants: Array<UmbDocumentVariantOptionModel>,
|
||||
activeVariantCultures?: Array<UmbVariantId>,
|
||||
): Promise<UmbVariantId[]> {
|
||||
// If there is only one variant, we don't need to select anything.
|
||||
if (availableVariants.length === 1) {
|
||||
@@ -60,7 +60,7 @@ export class UmbDocumentVariantManagerContext
|
||||
|
||||
const modalData: UmbDocumentVariantPickerModalData = {
|
||||
type,
|
||||
variants: availableVariants,
|
||||
options: availableVariants,
|
||||
};
|
||||
|
||||
const modalContext = this.#modalManagerContext.open(UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, {
|
||||
@@ -75,6 +75,7 @@ export class UmbDocumentVariantManagerContext
|
||||
const selectedVariants = result.selection.map((x) => x?.toLowerCase() ?? '');
|
||||
|
||||
// Match the result to the available variants.
|
||||
// Why would this be needed if the modal is only showing available variants? [NL]
|
||||
const variantIds = availableVariants
|
||||
.filter((x) => selectedVariants.includes(x.culture!))
|
||||
.map((x) => UmbVariantId.Create(x));
|
||||
@@ -90,8 +91,8 @@ export class UmbDocumentVariantManagerContext
|
||||
const { data } = await this.#documentRepository.requestByUnique(documentUnique);
|
||||
if (!data) throw new Error('Document not found');
|
||||
const variantIds = await this.pickVariants(
|
||||
data.variants,
|
||||
'publish',
|
||||
data.variants,
|
||||
this.#appLanguageCulture ? [this.#appLanguageCulture] : undefined,
|
||||
);
|
||||
if (variantIds.length) {
|
||||
@@ -111,8 +112,8 @@ export class UmbDocumentVariantManagerContext
|
||||
const variants = data.variants.filter((variant) => variant.state === UmbDocumentVariantState.PUBLISHED);
|
||||
|
||||
const variantIds = await this.pickVariants(
|
||||
variants,
|
||||
'unpublish',
|
||||
variants,
|
||||
this.#appLanguageCulture ? [this.#appLanguageCulture] : undefined,
|
||||
);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './document-variant-manager.context.js';
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { ManifestGlobalContext } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestGlobalContext> = [
|
||||
{
|
||||
type: 'globalContext',
|
||||
alias: 'Umb.GlobalContext.DocumentVariantManager',
|
||||
name: 'Document Variant Manager Context',
|
||||
js: () => import('./document-variant-manager.context.js'),
|
||||
},
|
||||
];
|
||||
@@ -12,7 +12,6 @@ import { manifests as modalManifests } from './modals/manifests.js';
|
||||
import { manifests as treeManifests } from './tree/manifests.js';
|
||||
import { manifests as userPermissionManifests } from './user-permissions/manifests.js';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests.js';
|
||||
import { manifests as globalContextManifests } from './global-contexts/manifests.js';
|
||||
|
||||
export const manifests = [
|
||||
...breadcrumbManifests,
|
||||
@@ -29,5 +28,4 @@ export const manifests = [
|
||||
...treeManifests,
|
||||
...userPermissionManifests,
|
||||
...workspaceManifests,
|
||||
...globalContextManifests,
|
||||
];
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { type UmbDocumentVariantModel, UmbDocumentVariantState } from '../../types.js';
|
||||
import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js';
|
||||
import type {
|
||||
UmbDocumentVariantPickerModalValue,
|
||||
UmbDocumentVariantPickerModalData,
|
||||
} from './document-variant-picker-modal.token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@@ -13,7 +13,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
|
||||
UmbDocumentVariantPickerModalData,
|
||||
UmbDocumentVariantPickerModalValue
|
||||
> {
|
||||
#selectionManager = new UmbSelectionManager(this);
|
||||
#selectionManager = new UmbSelectionManager<string>(this);
|
||||
|
||||
@state()
|
||||
_selection: Array<string> = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.observe(this.#selectionManager.selection, (selection) => {
|
||||
this._selection = selection;
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
@@ -21,16 +31,18 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
|
||||
this.#selectionManager.setMultiple(true);
|
||||
|
||||
// Make sure all mandatory variants are selected when not in unpublish mode
|
||||
// TODO: Currently only supports culture variants, not segment variants, but as well our Selection Manager also only supports a single string value pr. selection... [NL]
|
||||
this.#selectionManager.setSelection(this.value?.selection ?? []);
|
||||
|
||||
if (this.data?.type !== 'unpublish') {
|
||||
this.#selectMandatoryVariants();
|
||||
}
|
||||
}
|
||||
|
||||
#selectMandatoryVariants() {
|
||||
this.data?.variants.forEach((variant) => {
|
||||
if (variant.isMandatory) {
|
||||
this.#selectionManager.select(variant.culture);
|
||||
this.data?.options.forEach((variant) => {
|
||||
if (variant.language?.isMandatory) {
|
||||
this.#selectionManager.select(variant.unique);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -87,17 +99,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
|
||||
return html`<umb-body-layout headline=${this.localize.term(this.#headline)}>
|
||||
<p id="subtitle">${this.localize.term(this.#subtitle)}</p>
|
||||
${repeat(
|
||||
this.data?.variants ?? [],
|
||||
(item) => item.culture,
|
||||
(item) => html`
|
||||
this.data?.options ?? [],
|
||||
(option) => option.unique,
|
||||
(option) => html`
|
||||
<uui-menu-item
|
||||
selectable
|
||||
label=${item.name}
|
||||
@selected=${() => this.#selectionManager.select(item.culture)}
|
||||
@deselected=${() => this.#selectionManager.deselect(item.culture)}
|
||||
?selected=${this.#selectionManager.isSelected(item.culture)}>
|
||||
label=${option.variant?.name ?? option.language.name}
|
||||
@selected=${() => this.#selectionManager.select(option.unique)}
|
||||
@deselected=${() => this.#selectionManager.deselect(option.unique)}
|
||||
?selected=${this._selection.includes(option.language.unique)}>
|
||||
<uui-icon slot="icon" name="icon-globe"></uui-icon>
|
||||
${this.#renderLabel(item)}
|
||||
${this.#renderLabel(option)}
|
||||
</uui-menu-item>
|
||||
`,
|
||||
)}
|
||||
@@ -114,11 +126,14 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
|
||||
</umb-body-layout> `;
|
||||
}
|
||||
|
||||
#renderLabel(variant: UmbDocumentVariantModel) {
|
||||
#renderLabel(option: UmbDocumentVariantOptionModel) {
|
||||
return html`<div class="label" slot="label">
|
||||
<strong>${variant.segment ? variant.segment + ' - ' : ''}${variant.name}</strong>
|
||||
<div class="label-status">${this.#renderVariantStatus(variant)}</div>
|
||||
${variant.isMandatory && variant.state !== UmbDocumentVariantState.PUBLISHED
|
||||
<strong
|
||||
>${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ??
|
||||
option.language.name}</strong
|
||||
>
|
||||
<div class="label-status">${this.#renderVariantStatus(option)}</div>
|
||||
${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED
|
||||
? html`<div class="label-status">
|
||||
<umb-localize key="languages_mandatoryLanguage">Mandatory language</umb-localize>
|
||||
</div>`
|
||||
@@ -126,16 +141,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderVariantStatus(variant: UmbDocumentVariantModel) {
|
||||
switch (variant.state) {
|
||||
#renderVariantStatus(option: UmbDocumentVariantOptionModel) {
|
||||
switch (option.variant?.state) {
|
||||
case UmbDocumentVariantState.PUBLISHED:
|
||||
return this.localize.term('content_published');
|
||||
case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES:
|
||||
return this.localize.term('content_publishedPendingChanges');
|
||||
case UmbDocumentVariantState.NOT_CREATED:
|
||||
case UmbDocumentVariantState.DRAFT:
|
||||
default:
|
||||
return this.localize.term('content_unpublished');
|
||||
case UmbDocumentVariantState.NOT_CREATED:
|
||||
default:
|
||||
return this.localize.term('content_notCreated');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,29 @@ import { html } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
const modalData: UmbDocumentVariantPickerModalData = {
|
||||
type: 'save',
|
||||
variants: [
|
||||
options: [
|
||||
{
|
||||
name: 'English',
|
||||
culture: 'en-us',
|
||||
state: UmbDocumentVariantState.PUBLISHED,
|
||||
createDate: '2021-08-25T14:00:00Z',
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
isMandatory: true,
|
||||
unique: 'en-us',
|
||||
variant: {
|
||||
name: 'English variant name',
|
||||
culture: 'en-us',
|
||||
state: UmbDocumentVariantState.PUBLISHED,
|
||||
createDate: '2021-08-25T14:00:00Z',
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
},
|
||||
language: {
|
||||
entityType: 'language',
|
||||
name: 'English',
|
||||
unique: 'en-us',
|
||||
isDefault: true,
|
||||
isMandatory: true,
|
||||
fallbackIsoCode: null,
|
||||
},
|
||||
},
|
||||
/*
|
||||
// TODO: We do not support segments currently
|
||||
{
|
||||
name: 'English',
|
||||
culture: 'en-us',
|
||||
@@ -31,17 +43,27 @@ const modalData: UmbDocumentVariantPickerModalData = {
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: 'GTM',
|
||||
isMandatory: true,
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'Danish',
|
||||
culture: 'da-dk',
|
||||
state: UmbDocumentVariantState.NOT_CREATED,
|
||||
createDate: null,
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
isMandatory: false,
|
||||
unique: 'da-dk',
|
||||
variant: {
|
||||
name: 'Danish variant name',
|
||||
culture: 'da-dk',
|
||||
state: UmbDocumentVariantState.NOT_CREATED,
|
||||
createDate: null,
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
},
|
||||
language: {
|
||||
entityType: 'language',
|
||||
name: 'Danish',
|
||||
unique: 'da-dk',
|
||||
isDefault: false,
|
||||
isMandatory: false,
|
||||
fallbackIsoCode: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -80,7 +102,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
|
||||
publishDate: '2021-08-25T14:00:00Z',
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
isMandatory: true,
|
||||
},
|
||||
{
|
||||
name: 'English',
|
||||
@@ -90,7 +111,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
|
||||
publishDate: '2021-08-25T14:00:00Z',
|
||||
updateDate: null,
|
||||
segment: 'GTM',
|
||||
isMandatory: false,
|
||||
},
|
||||
{
|
||||
name: 'Danish',
|
||||
@@ -100,7 +120,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
segment: null,
|
||||
isMandatory: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS } from '../manifests.js';
|
||||
import type { UmbDocumentVariantModel } from '../../types.js';
|
||||
import type { UmbDocumentVariantOptionModel } from '../../types.js';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentVariantPickerModalData {
|
||||
type: 'save' | 'publish' | 'schedule' | 'unpublish';
|
||||
variants: Array<UmbDocumentVariantModel>;
|
||||
options: Array<UmbDocumentVariantOptionModel>;
|
||||
}
|
||||
|
||||
export interface UmbDocumentVariantPickerModalValue {
|
||||
selection: Array<string | null>;
|
||||
selection: Array<string>;
|
||||
}
|
||||
|
||||
export const UMB_DOCUMENT_LANGUAGE_PICKER_MODAL = new UmbModalToken<
|
||||
|
||||
@@ -46,7 +46,7 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
|
||||
},
|
||||
isTrashed: false,
|
||||
values: [],
|
||||
variants: [this.createVariantScaffold()],
|
||||
variants: [],
|
||||
...preset,
|
||||
};
|
||||
|
||||
@@ -57,6 +57,8 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
|
||||
* Creates a new variant scaffold.
|
||||
* @returns A new variant scaffold.
|
||||
*/
|
||||
/*
|
||||
// TDOD: remove if not used
|
||||
createVariantScaffold(): UmbDocumentVariantModel {
|
||||
return {
|
||||
state: null,
|
||||
@@ -66,9 +68,9 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
|
||||
publishDate: null,
|
||||
createDate: null,
|
||||
updateDate: null,
|
||||
isMandatory: false,
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches a Document with the given id from the server
|
||||
@@ -107,7 +109,6 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
|
||||
publishDate: variant.publishDate || null,
|
||||
createDate: variant.createDate,
|
||||
updateDate: variant.updateDate,
|
||||
isMandatory: false, // TODO: this is not correct. It will be solved when we know where to get the isMandatory from
|
||||
};
|
||||
}),
|
||||
urls: data.urls.map((url) => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbDocumentEntityType } from './entity.js';
|
||||
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbReferenceById } from '@umbraco-cms/backoffice/models';
|
||||
import { DocumentVariantStateModel as UmbDocumentVariantState } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
export { UmbDocumentVariantState };
|
||||
@@ -22,7 +22,6 @@ export interface UmbDocumentDetailModel {
|
||||
export interface UmbDocumentVariantModel extends UmbVariantModel {
|
||||
state: UmbDocumentVariantState | null;
|
||||
publishDate: string | null;
|
||||
isMandatory: boolean;
|
||||
}
|
||||
|
||||
export interface UmbDocumentUrlInfoModel {
|
||||
@@ -36,3 +35,5 @@ export interface UmbDocumentValueModel<ValueType = unknown> {
|
||||
alias: string;
|
||||
value: ValueType;
|
||||
}
|
||||
|
||||
export interface UmbDocumentVariantOptionModel extends UmbVariantOptionModel<UmbDocumentVariantModel> {}
|
||||
|
||||
@@ -1,64 +1,36 @@
|
||||
import type { UmbDocumentVariantOptionModel } from '../types.js';
|
||||
import { UmbDocumentWorkspaceSplitViewElement } from './document-workspace-split-view.element.js';
|
||||
import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from './document-workspace.context-token.js';
|
||||
import { customElement, state, css, html } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import type { ActiveVariant } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbRoute, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
|
||||
import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
// TODO: This seem fully identical with Media Workspace Editor, so we can refactor this to a generic component. [NL]
|
||||
@customElement('umb-document-workspace-editor')
|
||||
export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
|
||||
//private _defaultVariant?: VariantViewModelBaseModel;
|
||||
|
||||
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well.
|
||||
//
|
||||
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well. [NL]
|
||||
private splitViewElement = new UmbDocumentWorkspaceSplitViewElement();
|
||||
|
||||
#workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
_routes?: Array<UmbRoute>;
|
||||
|
||||
@state()
|
||||
_availableVariants: Array<UmbVariantModel> = [];
|
||||
|
||||
@state()
|
||||
_workspaceSplitViews: Array<ActiveVariant> = [];
|
||||
|
||||
#workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance;
|
||||
this.#observeVariants();
|
||||
this.#observeSplitViews();
|
||||
});
|
||||
}
|
||||
|
||||
#observeVariants() {
|
||||
if (!this.#workspaceContext) return;
|
||||
this.observe(
|
||||
this.#workspaceContext.allowedVariants,
|
||||
(variants) => {
|
||||
this._availableVariants = variants;
|
||||
this._generateRoutes();
|
||||
},
|
||||
'_observeVariants',
|
||||
);
|
||||
}
|
||||
|
||||
#observeSplitViews() {
|
||||
if (!this.#workspaceContext) return;
|
||||
this.observe(
|
||||
this.#workspaceContext.splitView.activeVariantsInfo,
|
||||
(variants) => {
|
||||
this._workspaceSplitViews = variants;
|
||||
},
|
||||
'_observeSplitViews',
|
||||
);
|
||||
// TODO: the variantOptions observable is like too broad as this will be triggered then there is any change in the variant options, we need to only update routes when there is a relevant change to them. [NL]
|
||||
this.observe(this.#workspaceContext.variantOptions, (options) => this._generateRoutes(options), '_observeVariants');
|
||||
}
|
||||
|
||||
private _handleVariantFolderPart(index: number, folderPart: string) {
|
||||
@@ -68,17 +40,18 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
|
||||
this.#workspaceContext?.splitView.setActiveVariant(index, culture, segment);
|
||||
}
|
||||
|
||||
private async _generateRoutes() {
|
||||
if (!this._availableVariants || this._availableVariants.length === 0) return;
|
||||
private async _generateRoutes(options: Array<UmbDocumentVariantOptionModel>) {
|
||||
if (!options || options.length === 0) return;
|
||||
|
||||
// Generate split view routes for all available routes
|
||||
const routes: Array<UmbRoute> = [];
|
||||
|
||||
// Split view routes:
|
||||
this._availableVariants.forEach((variantA) => {
|
||||
this._availableVariants.forEach((variantB) => {
|
||||
options.forEach((variantA) => {
|
||||
options.forEach((variantB) => {
|
||||
routes.push({
|
||||
path: new UmbVariantId(variantA).toString() + '_&_' + new UmbVariantId(variantB).toString(),
|
||||
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
|
||||
path: variantA.unique + '_&_' + variantB.unique,
|
||||
component: this.splitViewElement,
|
||||
setup: (_component, info) => {
|
||||
// Set split view/active info..
|
||||
@@ -92,9 +65,10 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
|
||||
});
|
||||
|
||||
// Single view:
|
||||
this._availableVariants.forEach((variant) => {
|
||||
options.forEach((variant) => {
|
||||
routes.push({
|
||||
path: new UmbVariantId(variant).toString(),
|
||||
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
|
||||
path: variant.unique,
|
||||
component: this.splitViewElement,
|
||||
setup: (_component, info) => {
|
||||
// cause we might come from a split-view, we need to reset index 1.
|
||||
@@ -108,11 +82,21 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
|
||||
// Using first single view as the default route for now (hence the math below):
|
||||
routes.push({
|
||||
path: '',
|
||||
redirectTo: routes[this._availableVariants.length * this._availableVariants.length]?.path,
|
||||
redirectTo: routes[options.length * options.length]?.path,
|
||||
});
|
||||
}
|
||||
|
||||
const oldValue = this._routes;
|
||||
|
||||
// is there any differences in the amount ot the paths? [NL]
|
||||
// TODO: if we make a memorization function as the observer, we can avoid this check and avoid the whole build of routes. [NL]
|
||||
if (oldValue && oldValue.length === routes.length) {
|
||||
// is there any differences in the paths? [NL]
|
||||
const hasDifferences = oldValue.some((route, index) => route.path !== routes[index].path);
|
||||
if (!hasDifferences) return;
|
||||
}
|
||||
this._routes = routes;
|
||||
this.requestUpdate('_routes', oldValue);
|
||||
}
|
||||
|
||||
private _gotWorkspaceRoute = (e: UmbRouterSlotInitEvent) => {
|
||||
|
||||
@@ -2,12 +2,15 @@ import { UmbDocumentTypeDetailRepository } from '../../document-types/repository
|
||||
import { UmbDocumentPropertyDataContext } from '../property-dataset-context/document-property-dataset-context.js';
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
|
||||
import { UmbDocumentDetailRepository } from '../repository/index.js';
|
||||
import { UmbDocumentVariantState, type UmbDocumentDetailModel, type UmbDocumentVariantModel } from '../types.js';
|
||||
import type { UmbDocumentVariantPickerModalData } from '../modals/index.js';
|
||||
import type { UmbDocumentDetailModel, UmbDocumentVariantModel, UmbDocumentVariantOptionModel } from '../types.js';
|
||||
import { UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, type UmbDocumentVariantPickerModalData } from '../modals/index.js';
|
||||
import { UmbDocumentPublishingRepository } from '../repository/publishing/index.js';
|
||||
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/document-variant-manager.context.js';
|
||||
import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import {
|
||||
type UmbObjectWithVariantProperties,
|
||||
UmbVariantId,
|
||||
variantPropertiesObjectToString,
|
||||
} from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type';
|
||||
import {
|
||||
UmbEditableWorkspaceContextBase,
|
||||
@@ -25,11 +28,12 @@ import {
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
|
||||
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
type EntityType = UmbDocumentDetailModel;
|
||||
export class UmbDocumentWorkspaceContext
|
||||
extends UmbEditableWorkspaceContextBase<EntityType>
|
||||
implements UmbVariantableWorkspaceContextInterface, UmbPublishableWorkspaceContextInterface
|
||||
implements UmbVariantableWorkspaceContextInterface<UmbDocumentVariantModel>, UmbPublishableWorkspaceContextInterface
|
||||
{
|
||||
//
|
||||
public readonly repository = new UmbDocumentDetailRepository(this);
|
||||
@@ -40,7 +44,7 @@ export class UmbDocumentWorkspaceContext
|
||||
*/
|
||||
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
|
||||
#getDataPromise?: Promise<any>;
|
||||
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
|
||||
// TODo: Optimize this so it uses either a App Language Context? [NL]
|
||||
#languageRepository = new UmbLanguageCollectionRepository(this);
|
||||
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
|
||||
public readonly languages = this.#languages.asObservable();
|
||||
@@ -54,26 +58,18 @@ export class UmbDocumentWorkspaceContext
|
||||
readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.documentType.unique);
|
||||
readonly contentTypeHasCollection = this.#currentData.asObservablePart((data) => !!data?.documentType.collection);
|
||||
readonly variants = this.#currentData.asObservablePart((data) => data?.variants ?? []);
|
||||
readonly allowedVariants = combineObservables([this.variants, this.languages], ([variants, languages]) => {
|
||||
const missingLanguages = languages.filter((x) => !variants.some((v) => v.culture === x.unique));
|
||||
const newVariants = variants.concat(
|
||||
missingLanguages.map(
|
||||
(language) =>
|
||||
({
|
||||
state: UmbDocumentVariantState.NOT_CREATED,
|
||||
isMandatory: language.isMandatory,
|
||||
culture: language.unique,
|
||||
segment: null,
|
||||
name: language.name,
|
||||
createDate: null,
|
||||
publishDate: null,
|
||||
updateDate: null,
|
||||
}) as UmbDocumentVariantModel,
|
||||
),
|
||||
);
|
||||
return newVariants;
|
||||
readonly variantOptions = combineObservables([this.variants, this.languages], ([variants, languages]) => {
|
||||
return languages.map((language) => {
|
||||
return {
|
||||
variant: variants.find((x) => x.culture === language.unique),
|
||||
language,
|
||||
// TODO: When including segments, this should be updated to include the segment as well. [NL]
|
||||
unique: language.unique, // This must be a variantId string!
|
||||
} as UmbDocumentVariantOptionModel;
|
||||
});
|
||||
});
|
||||
readonly changedVariants = new UmbArrayState<UmbVariantId>([], (x) => x.compare);
|
||||
|
||||
readonly changedVariants = new UmbArrayState<UmbObjectWithVariantProperties>([], variantPropertiesObjectToString);
|
||||
readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []);
|
||||
readonly templateId = this.#currentData.asObservablePart((data) => data?.template?.unique || null);
|
||||
|
||||
@@ -83,16 +79,13 @@ export class UmbDocumentWorkspaceContext
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_DOCUMENT_WORKSPACE_ALIAS);
|
||||
|
||||
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (instance) => {
|
||||
this.#variantManagerContext = instance;
|
||||
});
|
||||
|
||||
this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique));
|
||||
|
||||
this.loadLanguages();
|
||||
}
|
||||
|
||||
async loadLanguages() {
|
||||
// TODO: If we don't end up having a Global Context for languages, then we should at least change this into using a asObservable which should be returned from the repository. [Nl]
|
||||
const { data } = await this.#languageRepository.requestCollection({});
|
||||
this.#languages.setValue(data?.items ?? []);
|
||||
}
|
||||
@@ -222,50 +215,13 @@ export class UmbDocumentWorkspaceContext
|
||||
const data = this.getData();
|
||||
if (!data) throw new Error('Data is missing');
|
||||
if (!data.unique) throw new Error('Unique is missing');
|
||||
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
|
||||
const activeVariants = this.splitView.getActiveVariants();
|
||||
const activeVariant = activeVariants.length ? activeVariants[0] : undefined;
|
||||
|
||||
// Calculate variants
|
||||
const pickedVariants: string[] = [];
|
||||
const availableVariants = await firstValueFrom(this.allowedVariants);
|
||||
let allowedVariants = data.variants;
|
||||
const pickedVariants = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant).toString());
|
||||
const allowedVariants = await firstValueFrom(this.variantOptions);
|
||||
|
||||
// Make sure that the active variant is in the allowed variants
|
||||
if (activeVariant) {
|
||||
const activeVariantInAvailableVariants = availableVariants.find(
|
||||
(x) => x.culture === activeVariant.culture && x.segment === activeVariant.segment,
|
||||
);
|
||||
if (activeVariantInAvailableVariants) {
|
||||
pickedVariants.push(activeVariantInAvailableVariants.culture!);
|
||||
allowedVariants = appendToFrozenArray(
|
||||
allowedVariants,
|
||||
activeVariantInAvailableVariants,
|
||||
(x) =>
|
||||
x.culture === activeVariantInAvailableVariants.culture &&
|
||||
x.segment === activeVariantInAvailableVariants.segment,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the changed variants are in the allowed variants
|
||||
const changedVariants = this.changedVariants.getValue();
|
||||
if (changedVariants.length) {
|
||||
pickedVariants.push(...changedVariants.map((x) => x.culture!));
|
||||
const changedVariantsInAvailableVariants = availableVariants.filter((x) =>
|
||||
changedVariants.some((y) => y.equal(new UmbVariantId(x))),
|
||||
);
|
||||
for (const changedVariant of changedVariantsInAvailableVariants) {
|
||||
allowedVariants = appendToFrozenArray(
|
||||
allowedVariants,
|
||||
changedVariant,
|
||||
(x) => changedVariant.culture === x.culture && changedVariant.segment === x.segment,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const selectedVariants = await this.#variantManagerContext.pickVariants(allowedVariants, type, pickedVariants);
|
||||
const selectedVariants = await this.pickVariants(type, allowedVariants, pickedVariants);
|
||||
|
||||
// If no variants are selected, we don't save anything.
|
||||
if (!selectedVariants.length) return [];
|
||||
@@ -281,6 +237,35 @@ export class UmbDocumentWorkspaceContext
|
||||
return selectedVariants;
|
||||
}
|
||||
|
||||
// TODO: refactor this part so it can be utilized by others? [NL]
|
||||
async pickVariants(
|
||||
type: UmbDocumentVariantPickerModalData['type'],
|
||||
availableVariants: Array<UmbDocumentVariantOptionModel>,
|
||||
selectedVariants?: Array<string>,
|
||||
): Promise<UmbVariantId[]> {
|
||||
// If there is only one variant, we don't need to select anything.
|
||||
if (availableVariants.length === 1) {
|
||||
// TODO: we are missing a good way to make a variantId from a variantOptionModel. [NL]
|
||||
return [new UmbVariantId(availableVariants[0].language.unique, null)];
|
||||
}
|
||||
|
||||
const modalManagerContext = await this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, () => {}).asPromise();
|
||||
|
||||
const modalData: UmbDocumentVariantPickerModalData = {
|
||||
type,
|
||||
options: availableVariants,
|
||||
};
|
||||
|
||||
const modalContext = modalManagerContext.open(UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, {
|
||||
data: modalData,
|
||||
value: { selection: selectedVariants?.map((x) => x.toString()) ?? [] },
|
||||
});
|
||||
|
||||
const result = await modalContext.onSubmit().catch(() => undefined);
|
||||
|
||||
return result?.selection.map((x) => UmbVariantId.FromString(x)) ?? [];
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this.#createOrSave('save');
|
||||
const data = this.getData();
|
||||
@@ -304,9 +289,11 @@ export class UmbDocumentWorkspaceContext
|
||||
const unique = this.getEntityId();
|
||||
|
||||
if (!unique) throw new Error('Unique is missing');
|
||||
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
|
||||
|
||||
this.#variantManagerContext.unpublish(unique);
|
||||
//this.#variantManagerContext.unpublish(unique);
|
||||
alert('not implemented');
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
async delete() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbMediaEntityType } from './entity.js';
|
||||
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { MediaUrlInfoModel, MediaValueModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
export interface UmbMediaDetailModel {
|
||||
@@ -12,3 +12,5 @@ export interface UmbMediaDetailModel {
|
||||
values: Array<MediaValueModel>;
|
||||
variants: Array<UmbVariantModel>;
|
||||
}
|
||||
|
||||
export interface UmbMediaVariantOptionModel extends UmbVariantOptionModel<UmbVariantModel> {}
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
import type { UmbMediaVariantOptionModel } from '../types.js';
|
||||
import { UmbMediaWorkspaceSplitViewElement } from './media-workspace-split-view.element.js';
|
||||
import { UMB_MEDIA_WORKSPACE_CONTEXT } from './media-workspace.context-token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { customElement, state, css, html } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbRoute, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router';
|
||||
import type { ActiveVariant } from '@umbraco-cms/backoffice/workspace';
|
||||
@customElement('umb-media-workspace-editor')
|
||||
export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
|
||||
//private _defaultVariant?: VariantViewModelBaseModel;
|
||||
|
||||
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well.
|
||||
//
|
||||
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well. [NL]
|
||||
private splitViewElement = new UmbMediaWorkspaceSplitViewElement();
|
||||
|
||||
@state()
|
||||
_routes?: Array<UmbRoute>;
|
||||
|
||||
@state()
|
||||
_availableVariants: Array<UmbVariantModel> = [];
|
||||
|
||||
@state()
|
||||
_workspaceSplitViews: Array<ActiveVariant> = [];
|
||||
|
||||
#workspaceContext?: typeof UMB_MEDIA_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
constructor() {
|
||||
@@ -31,31 +22,12 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
|
||||
this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance;
|
||||
this.#observeVariants();
|
||||
this.#observeSplitViews();
|
||||
});
|
||||
}
|
||||
|
||||
#observeVariants() {
|
||||
if (!this.#workspaceContext) return;
|
||||
this.observe(
|
||||
this.#workspaceContext.allowedVariants,
|
||||
(variants) => {
|
||||
this._availableVariants = variants;
|
||||
this._generateRoutes();
|
||||
},
|
||||
'_observeVariants',
|
||||
);
|
||||
}
|
||||
|
||||
#observeSplitViews() {
|
||||
if (!this.#workspaceContext) return;
|
||||
this.observe(
|
||||
this.#workspaceContext.splitView.activeVariantsInfo,
|
||||
(variants) => {
|
||||
this._workspaceSplitViews = variants;
|
||||
},
|
||||
'_observeSplitViews',
|
||||
);
|
||||
this.observe(this.#workspaceContext.variantOptions, (options) => this._generateRoutes(options), '_observeVariants');
|
||||
}
|
||||
|
||||
private _handleVariantFolderPart(index: number, folderPart: string) {
|
||||
@@ -65,17 +37,18 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
|
||||
this.#workspaceContext?.splitView.setActiveVariant(index, culture, segment);
|
||||
}
|
||||
|
||||
private _generateRoutes() {
|
||||
if (!this._availableVariants || this._availableVariants.length === 0) return;
|
||||
private async _generateRoutes(variants: Array<UmbMediaVariantOptionModel>) {
|
||||
if (!variants || variants.length === 0) return;
|
||||
|
||||
// Generate split view routes for all available routes
|
||||
const routes: Array<UmbRoute> = [];
|
||||
|
||||
// Split view routes:
|
||||
this._availableVariants.forEach((variantA) => {
|
||||
this._availableVariants.forEach((variantB) => {
|
||||
variants.forEach((variantA) => {
|
||||
variants.forEach((variantB) => {
|
||||
routes.push({
|
||||
path: UmbVariantId.Create(variantA).toString() + '_&_' + UmbVariantId.Create(variantB).toString(),
|
||||
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
|
||||
path: variantA.unique + '_&_' + variantB.unique,
|
||||
component: this.splitViewElement,
|
||||
setup: (_component, info) => {
|
||||
// Set split view/active info..
|
||||
@@ -89,9 +62,10 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
|
||||
});
|
||||
|
||||
// Single view:
|
||||
this._availableVariants.forEach((variant) => {
|
||||
variants.forEach((variant) => {
|
||||
routes.push({
|
||||
path: UmbVariantId.Create(variant).toString(),
|
||||
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
|
||||
path: variant.unique,
|
||||
component: this.splitViewElement,
|
||||
setup: (_component, info) => {
|
||||
// cause we might come from a split-view, we need to reset index 1.
|
||||
@@ -105,11 +79,21 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
|
||||
// Using first single view as the default route for now (hence the math below):
|
||||
routes.push({
|
||||
path: '',
|
||||
redirectTo: routes[this._availableVariants.length * this._availableVariants.length]?.path,
|
||||
redirectTo: routes[variants.length * variants.length]?.path,
|
||||
});
|
||||
}
|
||||
|
||||
const oldValue = this._routes;
|
||||
|
||||
// is there any differences in the amount ot the paths? [NL]
|
||||
// TODO: if we make a memorization function as the observer, we can avoid this check and avoid the whole build of routes. [NL]
|
||||
if (oldValue && oldValue.length === routes.length) {
|
||||
// is there any differences in the paths? [NL]
|
||||
const hasDifferences = oldValue.some((route, index) => route.path !== routes[index].path);
|
||||
if (!hasDifferences) return;
|
||||
}
|
||||
this._routes = routes;
|
||||
this.requestUpdate('_routes', oldValue);
|
||||
}
|
||||
|
||||
private _gotWorkspaceRoute = (e: UmbRouterSlotInitEvent) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { UmbMediaTypeDetailRepository } from '../../media-types/repository/detai
|
||||
import { UmbMediaPropertyDataContext } from '../property-dataset-context/media-property-dataset-context.js';
|
||||
import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js';
|
||||
import { UmbMediaDetailRepository } from '../repository/index.js';
|
||||
import type { UmbMediaDetailModel } from '../types.js';
|
||||
import type { UmbMediaDetailModel, UmbMediaVariantOptionModel } from '../types.js';
|
||||
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type';
|
||||
import {
|
||||
@@ -33,8 +33,11 @@ export class UmbMediaWorkspaceContext
|
||||
*/
|
||||
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
|
||||
#getDataPromise?: Promise<any>;
|
||||
// TODo: Optimize this so it uses either a App Language Context? [NL]
|
||||
#languageRepository = new UmbLanguageCollectionRepository(this);
|
||||
#languageCollection = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
|
||||
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
|
||||
public readonly languages = this.#languages.asObservable();
|
||||
|
||||
public isLoaded() {
|
||||
return this.#getDataPromise;
|
||||
}
|
||||
@@ -43,22 +46,16 @@ export class UmbMediaWorkspaceContext
|
||||
readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.mediaType.unique);
|
||||
|
||||
readonly variants = this.#currentData.asObservablePart((data) => data?.variants || []);
|
||||
readonly allowedVariants = combineObservables(
|
||||
[this.variants, this.#languageCollection.asObservable()],
|
||||
([variants, languages]) => {
|
||||
const missingLanguages = languages.filter((x) => !variants.some((v) => v.culture === x.unique));
|
||||
const newVariants = variants.concat(
|
||||
missingLanguages.map((x) => ({
|
||||
culture: x.unique,
|
||||
segment: null,
|
||||
name: x.name,
|
||||
createDate: '',
|
||||
updateDate: '',
|
||||
})),
|
||||
);
|
||||
return newVariants;
|
||||
},
|
||||
);
|
||||
readonly variantOptions = combineObservables([this.variants, this.languages], ([variants, languages]) => {
|
||||
return languages.map((language) => {
|
||||
return {
|
||||
variant: variants.find((x) => x.culture === language.unique),
|
||||
language,
|
||||
// TODO: When including segments, this should be updated to include the segment as well. [NL]
|
||||
unique: language.unique, // This must be a variantId string!
|
||||
} as UmbMediaVariantOptionModel;
|
||||
});
|
||||
});
|
||||
readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []);
|
||||
|
||||
readonly structure = new UmbContentTypePropertyStructureManager(this, new UmbMediaTypeDetailRepository(this));
|
||||
@@ -73,7 +70,7 @@ export class UmbMediaWorkspaceContext
|
||||
|
||||
async loadLanguages() {
|
||||
const { data } = await this.#languageRepository.requestCollection({});
|
||||
this.#languageCollection.setValue(data?.items ?? []);
|
||||
this.#languages.setValue(data?.items ?? []);
|
||||
}
|
||||
|
||||
async load(unique: string) {
|
||||
|
||||
Reference in New Issue
Block a user