<umb-content-workspace-property> DX (#19399)
* introduce umb-content-workspace-property to improve dx * make property responsible for observing the view guard * Update src/Umbraco.Web.UI.Client/src/packages/content/content/global-components/content-workspace-property.element.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * context consumer update tests * no need to import when exporting * only observe aliases * merge the two component for less complexity * added property settings * ensure this works with extension begin removed --------- Co-authored-by: Niels Lyngsø <nsl@umbraco.dk> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
// State which holds all the properties of the current container, this is a composition of all properties from the containers that matches our target [NL]
|
||||
#propertyStructure = new UmbArrayState<UmbPropertyTypeModel>([], (x) => x.unique);
|
||||
readonly propertyStructure = this.#propertyStructure.asObservable();
|
||||
readonly propertyAliases = this.#propertyStructure.asObservablePart((x) => x.map((e) => e.alias));
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host);
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '../constants.js';
|
||||
import { html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import { UmbDataPathPropertyValueQuery } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
@customElement('umb-content-workspace-property')
|
||||
export class UmbContentWorkspacePropertyElement extends UmbLitElement {
|
||||
private _alias?: string | undefined;
|
||||
|
||||
@property({ type: String, attribute: 'alias' })
|
||||
public get alias(): string | undefined {
|
||||
return this._alias;
|
||||
}
|
||||
public set alias(value: string | undefined) {
|
||||
this._alias = value;
|
||||
this.#observePropertyType();
|
||||
}
|
||||
|
||||
@state()
|
||||
_datasetVariantId?: UmbVariantId;
|
||||
|
||||
@state()
|
||||
_dataPath?: string;
|
||||
|
||||
@state()
|
||||
_viewable?: boolean;
|
||||
|
||||
@state()
|
||||
_writeable?: boolean;
|
||||
|
||||
@state()
|
||||
_workspaceContext?: typeof UMB_CONTENT_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
_propertyType?: UmbPropertyTypeModel;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// The Property Dataset is local to the active variant, we use this to retrieve the variant we like to gather the value from.
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (datasetContext) => {
|
||||
this._datasetVariantId = datasetContext?.getVariantId();
|
||||
});
|
||||
|
||||
// The Content Workspace Context is used to retrieve the property type we like to observe.
|
||||
// This gives us the configuration from the property type as part of the data type.
|
||||
this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, async (workspaceContext) => {
|
||||
this._workspaceContext = workspaceContext;
|
||||
this.#observePropertyType();
|
||||
});
|
||||
}
|
||||
|
||||
async #observePropertyType() {
|
||||
if (!this._alias || !this._workspaceContext) return;
|
||||
|
||||
this.observe(await this._workspaceContext?.structure.propertyStructureByAlias(this._alias), (propertyType) => {
|
||||
this._propertyType = propertyType;
|
||||
this.#checkViewGuard();
|
||||
});
|
||||
}
|
||||
|
||||
#checkViewGuard() {
|
||||
if (!this._workspaceContext || !this._propertyType || !this._datasetVariantId) return;
|
||||
|
||||
const propertyVariantId = new UmbVariantId(
|
||||
this._propertyType.variesByCulture ? this._datasetVariantId.culture : null,
|
||||
this._propertyType.variesBySegment ? this._datasetVariantId.segment : null,
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this._workspaceContext.propertyViewGuard.isPermittedForVariantAndProperty(
|
||||
propertyVariantId,
|
||||
this._propertyType,
|
||||
this._datasetVariantId,
|
||||
),
|
||||
(permitted) => {
|
||||
this._viewable = permitted;
|
||||
},
|
||||
`umbObservePropertyViewGuard`,
|
||||
);
|
||||
}
|
||||
|
||||
override willUpdate(changedProperties: Map<string, any>) {
|
||||
super.willUpdate(changedProperties);
|
||||
if (
|
||||
changedProperties.has('_propertyType') ||
|
||||
changedProperties.has('_datasetVariantId') ||
|
||||
changedProperties.has('_workspaceContext')
|
||||
) {
|
||||
if (this._datasetVariantId && this._propertyType && this._workspaceContext) {
|
||||
const propertyVariantId = new UmbVariantId(
|
||||
this._propertyType.variesByCulture ? this._datasetVariantId.culture : null,
|
||||
this._propertyType.variesBySegment ? this._datasetVariantId.segment : null,
|
||||
);
|
||||
this._dataPath = `$.values[${UmbDataPathPropertyValueQuery({
|
||||
alias: this._propertyType.alias,
|
||||
culture: propertyVariantId.culture,
|
||||
segment: propertyVariantId.segment,
|
||||
})}].value`;
|
||||
|
||||
this.observe(
|
||||
this._workspaceContext.propertyWriteGuard.isPermittedForVariantAndProperty(
|
||||
propertyVariantId,
|
||||
this._propertyType,
|
||||
propertyVariantId,
|
||||
),
|
||||
(write) => {
|
||||
this._writeable = write;
|
||||
},
|
||||
'observeView',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this._viewable) return nothing;
|
||||
if (!this._dataPath || this._writeable === undefined) return nothing;
|
||||
|
||||
return html`<umb-property-type-based-property
|
||||
data-path=${this._dataPath}
|
||||
.property=${this._propertyType}
|
||||
?readonly=${!this._writeable}></umb-property-type-based-property>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbContentWorkspacePropertyElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-content-workspace-property': UmbContentWorkspacePropertyElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './content-workspace-property.element.js';
|
||||
@@ -2,9 +2,11 @@ export * from './collection/index.js';
|
||||
export * from './components/index.js';
|
||||
export * from './constants.js';
|
||||
export * from './controller/merge-content-variant-data.controller.js';
|
||||
export * from './global-components/index.js';
|
||||
export * from './manager/index.js';
|
||||
export * from './property-dataset-context/index.js';
|
||||
export * from './workspace/index.js';
|
||||
|
||||
export type * from './repository/index.js';
|
||||
export type * from './types.js';
|
||||
export type * from './variant-picker/index.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '../../content-workspace.context-token.js';
|
||||
import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type {
|
||||
UmbContentTypeModel,
|
||||
@@ -8,17 +8,10 @@ import type {
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
|
||||
import './content-editor-property.element.js';
|
||||
|
||||
@customElement('umb-content-workspace-view-edit-properties')
|
||||
export class UmbContentWorkspaceViewEditPropertiesElement extends UmbLitElement {
|
||||
#workspaceContext?: typeof UMB_CONTENT_WORKSPACE_CONTEXT.TYPE;
|
||||
#propertyStructureHelper = new UmbContentTypePropertyStructureHelper<UmbContentTypeModel>(this);
|
||||
#properties?: Array<UmbPropertyTypeModel>;
|
||||
#visiblePropertiesUniques: Array<string> = [];
|
||||
|
||||
@property({ type: String, attribute: 'container-id', reflect: false })
|
||||
public get containerId(): string | null | undefined {
|
||||
@@ -29,86 +22,36 @@ export class UmbContentWorkspaceViewEditPropertiesElement extends UmbLitElement
|
||||
}
|
||||
|
||||
@state()
|
||||
_datasetVariantId?: UmbVariantId;
|
||||
_properties: Array<string> = [];
|
||||
|
||||
@state()
|
||||
_visibleProperties?: Array<UmbPropertyTypeModel>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (datasetContext) => {
|
||||
this._datasetVariantId = datasetContext?.getVariantId();
|
||||
this.#processPropertyStructure();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, (workspaceContext) => {
|
||||
this.#workspaceContext = workspaceContext;
|
||||
this.#propertyStructureHelper.setStructureManager(
|
||||
// Assuming its the same content model type that we are working with here... [NL]
|
||||
workspaceContext?.structure as unknown as UmbContentTypeStructureManager<UmbContentTypeModel>,
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this.#propertyStructureHelper.propertyStructure,
|
||||
this.#propertyStructureHelper.propertyAliases,
|
||||
(properties) => {
|
||||
this.#properties = properties;
|
||||
this.#processPropertyStructure();
|
||||
this._properties = properties;
|
||||
},
|
||||
'observePropertyStructure',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#processPropertyStructure() {
|
||||
if (!this.#workspaceContext || !this.#properties || !this.#propertyStructureHelper || !this._datasetVariantId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const propertyViewGuard = this.#workspaceContext.propertyViewGuard;
|
||||
|
||||
this.#properties.forEach((property) => {
|
||||
const propertyVariantId = new UmbVariantId(
|
||||
property.variesByCulture ? this._datasetVariantId?.culture : null,
|
||||
property.variesBySegment ? this._datasetVariantId?.segment : null,
|
||||
);
|
||||
this.observe(
|
||||
propertyViewGuard.isPermittedForVariantAndProperty(propertyVariantId, property, this._datasetVariantId!),
|
||||
(permitted) => {
|
||||
if (permitted) {
|
||||
this.#visiblePropertiesUniques.push(property.unique);
|
||||
this.#calculateVisibleProperties();
|
||||
} else {
|
||||
const index = this.#visiblePropertiesUniques.indexOf(property.unique);
|
||||
if (index !== -1) {
|
||||
this.#visiblePropertiesUniques.splice(index, 1);
|
||||
this.#calculateVisibleProperties();
|
||||
}
|
||||
}
|
||||
},
|
||||
`propertyViewGuard-permittedForVariantAndProperty-${property.unique}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#calculateVisibleProperties() {
|
||||
this._visibleProperties = this.#properties!.filter((property) =>
|
||||
this.#visiblePropertiesUniques.includes(property.unique),
|
||||
);
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this._datasetVariantId && this._visibleProperties
|
||||
? repeat(
|
||||
this._visibleProperties,
|
||||
(property) => property.alias,
|
||||
(property) =>
|
||||
html`<umb-content-workspace-view-edit-property
|
||||
class="property"
|
||||
.variantId=${this._datasetVariantId}
|
||||
.property=${property}></umb-content-workspace-view-edit-property>`,
|
||||
)
|
||||
: nothing;
|
||||
return repeat(
|
||||
this._properties,
|
||||
(property) => property,
|
||||
(property) =>
|
||||
html`<umb-content-workspace-property class="property" alias=${property}></umb-content-workspace-property>`,
|
||||
);
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { UMB_CONTENT_WORKSPACE_CONTEXT } from '../../content-workspace.context-token.js';
|
||||
import { html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbDataPathPropertyValueQuery } from '@umbraco-cms/backoffice/validation';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
|
||||
@customElement('umb-content-workspace-view-edit-property')
|
||||
export class UmbContentWorkspaceViewEditPropertyElement extends UmbLitElement {
|
||||
//
|
||||
@property({ attribute: false })
|
||||
variantId?: UmbVariantId;
|
||||
|
||||
@property({ attribute: false })
|
||||
property?: UmbPropertyTypeModel;
|
||||
|
||||
@state()
|
||||
_dataPath?: string;
|
||||
|
||||
@state()
|
||||
_writeable?: boolean;
|
||||
|
||||
@state()
|
||||
_context?: typeof UMB_CONTENT_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, (context) => {
|
||||
this._context = context;
|
||||
});
|
||||
}
|
||||
|
||||
override willUpdate(changedProperties: Map<string, any>) {
|
||||
super.willUpdate(changedProperties);
|
||||
if (changedProperties.has('property') || changedProperties.has('variantId') || changedProperties.has('_context')) {
|
||||
if (this.variantId && this.property && this._context) {
|
||||
const propertyVariantId = new UmbVariantId(
|
||||
this.property.variesByCulture ? this.variantId.culture : null,
|
||||
this.property.variesBySegment ? this.variantId.segment : null,
|
||||
);
|
||||
this._dataPath = `$.values[${UmbDataPathPropertyValueQuery({
|
||||
alias: this.property.alias,
|
||||
culture: propertyVariantId.culture,
|
||||
segment: propertyVariantId.segment,
|
||||
})}].value`;
|
||||
|
||||
this.observe(
|
||||
this._context.propertyWriteGuard.isPermittedForVariantAndProperty(
|
||||
propertyVariantId,
|
||||
this.property,
|
||||
this.variantId,
|
||||
),
|
||||
(write) => {
|
||||
this._writeable = write;
|
||||
},
|
||||
'observeView',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this._dataPath || this._writeable === undefined) return nothing;
|
||||
|
||||
return html`<umb-property-type-based-property
|
||||
data-path=${this._dataPath}
|
||||
.property=${this.property}
|
||||
?readonly=${!this._writeable}></umb-property-type-based-property>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbContentWorkspaceViewEditPropertyElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-content-workspace-view-edit-property': UmbContentWorkspaceViewEditPropertyElement;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export class UmbTreeItemElement extends UmbExtensionElementAndApiSlotElementBase
|
||||
// This method gets all extensions based on a type, then filters them based on the entity type. and then we get the alias of the first one [NL]
|
||||
createObservablePart(
|
||||
umbExtensionsRegistry.byTypeAndFilter(this.getExtensionType(), filterByEntityType),
|
||||
(x) => x[0].alias,
|
||||
(x) => x[0]?.alias,
|
||||
),
|
||||
(alias) => {
|
||||
this.alias = alias;
|
||||
|
||||
Reference in New Issue
Block a user