refactoring of property -> context communication
This commit is contained in:
@@ -31,7 +31,7 @@ export class UmbContentPropertyElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
@property()
|
||||
value?: object;
|
||||
value?: object | string;
|
||||
|
||||
@state()
|
||||
private _propertyEditorUIAlias?: string;
|
||||
|
||||
@@ -84,6 +84,9 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
* @default ''
|
||||
*/
|
||||
@property({ type: String })
|
||||
public get alias(): string {
|
||||
return this._propertyContext.getAlias() || '';
|
||||
}
|
||||
public set alias(alias: string) {
|
||||
this._propertyContext.setAlias(alias);
|
||||
}
|
||||
@@ -108,11 +111,14 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
/**
|
||||
* Property Editor UI Alias. Render the Property Editor UI registered for this alias.
|
||||
* @public
|
||||
* @type {object}
|
||||
* @type {object | string}
|
||||
* @attr
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Object, attribute: false })
|
||||
@property({ attribute: false })
|
||||
public get value() {
|
||||
return this._propertyContext.getValue() as any;
|
||||
}
|
||||
public set value(value: object | string) {
|
||||
this._propertyContext.setValue(value);
|
||||
}
|
||||
@@ -144,7 +150,6 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
|
||||
|
||||
this.observe(this._propertyContext.label, (label) => {
|
||||
console.log("_propertyContext replied with label", label)
|
||||
this._label = label;
|
||||
});
|
||||
this.observe(this._propertyContext.label, (description) => {
|
||||
@@ -152,7 +157,7 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
});
|
||||
|
||||
// TODO: move event to context. maybe rename to property-editor-value-change.
|
||||
this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener);
|
||||
this.addEventListener('property-editor-value-change', this._onPropertyEditorChange as any as EventListener);
|
||||
|
||||
|
||||
}
|
||||
@@ -200,9 +205,9 @@ export class UmbPropertyElement extends UmbLitElement {
|
||||
const target = e.composedPath()[0] as any;
|
||||
this.value = target.value;
|
||||
|
||||
// TODO: update context.
|
||||
//TODO: Property-Context: Figure out the requirements for this. Cause currently the alias-prop(getter) is required, but its not obvious.
|
||||
|
||||
|
||||
// TODO: Confusing with the little detail of the event name that changed here..
|
||||
this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true }));
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
@@ -81,6 +81,9 @@ export class UmbWorkspacePropertyContext<ValueType> {
|
||||
public setAlias(alias: WorkspacePropertyData<ValueType>['alias']) {
|
||||
this.update({alias: alias});
|
||||
}
|
||||
public getAlias() {
|
||||
return this._data.getValue().alias;
|
||||
}
|
||||
public setLabel(label: WorkspacePropertyData<ValueType>['label']) {
|
||||
this.update({label: label});
|
||||
}
|
||||
@@ -90,6 +93,9 @@ export class UmbWorkspacePropertyContext<ValueType> {
|
||||
public setValue(value: WorkspacePropertyData<ValueType>['value']) {
|
||||
this.update({value: value});
|
||||
}
|
||||
public getValue() {
|
||||
return this._data.getValue().value;
|
||||
}
|
||||
public setConfig(config: WorkspacePropertyData<ValueType>['config']) {
|
||||
this.update({config: config});
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@ import { UmbNotificationService } from '../../../../../core/notification';
|
||||
import { UmbNotificationDefaultData } from '../../../../../core/notification/layouts/default';
|
||||
import { UmbWorkspaceContext } from '../workspace-context/workspace.context';
|
||||
import { UmbNodeStoreBase } from '@umbraco-cms/stores/store';
|
||||
import { ContentTreeItem } from '@umbraco-cms/backend-api';
|
||||
import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin';
|
||||
import { UmbContextConsumerController } from 'src/core/context-api/consume/context-consumer.controller';
|
||||
import { UmbObserverController } from '@umbraco-cms/observable-api';
|
||||
import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller';
|
||||
import type { ContentDetails } from '@umbraco-cms/models';
|
||||
|
||||
// TODO: Consider if its right to have this many class-inheritance of WorkspaceContext
|
||||
// TODO: Could we extract this code into a 'Manager' of its own, which will be instantiated by the concrete Workspace Context. This will be more transparent and 'reuseable'
|
||||
export class UmbWorkspaceContentContext<
|
||||
ContentTypeType extends ContentTreeItem = ContentTreeItem,
|
||||
ContentTypeType extends ContentDetails = ContentDetails,
|
||||
StoreType extends UmbNodeStoreBase<ContentTypeType> = UmbNodeStoreBase<ContentTypeType>
|
||||
> extends UmbWorkspaceContext<ContentTypeType> {
|
||||
|
||||
@@ -34,6 +34,10 @@ export class UmbWorkspaceContentContext<
|
||||
) {
|
||||
super(host, defaultData);
|
||||
|
||||
this.entityType = entityType;
|
||||
|
||||
host.addEventListener('property-value-change', this._onPropertyValueChange);
|
||||
|
||||
new UmbContextConsumerController(
|
||||
host,
|
||||
'umbNotificationService',
|
||||
@@ -42,15 +46,13 @@ export class UmbWorkspaceContentContext<
|
||||
}
|
||||
);
|
||||
|
||||
this.entityType = entityType;
|
||||
|
||||
new UmbContextConsumerController(host, storeAlias, (_instance: StoreType) => {
|
||||
this._store = _instance;
|
||||
if (!this._store) {
|
||||
// TODO: make sure to break the application in a good way.
|
||||
return;
|
||||
}
|
||||
this._readyToLoad();
|
||||
this._observeStore();
|
||||
|
||||
// TODO: first provide when we have umbNotificationService as well.
|
||||
new UmbContextProviderController(this._host, 'umbWorkspaceContext', this);
|
||||
@@ -60,7 +62,7 @@ export class UmbWorkspaceContentContext<
|
||||
load(entityKey: string) {
|
||||
this.#isNew = false;
|
||||
this.entityKey = entityKey;
|
||||
this._readyToLoad();
|
||||
this._observeStore();
|
||||
}
|
||||
|
||||
create(parentKey: string | null) {
|
||||
@@ -69,7 +71,7 @@ export class UmbWorkspaceContentContext<
|
||||
console.log("I'm new, and I will be created under ", parentKey)
|
||||
}
|
||||
|
||||
protected _readyToLoad(): void {
|
||||
protected _observeStore(): void {
|
||||
if(!this._store || !this.entityKey) {
|
||||
return;
|
||||
}
|
||||
@@ -88,6 +90,43 @@ export class UmbWorkspaceContentContext<
|
||||
return this._store;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO: Property-Context: I would like ot investigate how this would work as methods. That do require that a property-context gets the workspace context. But this connection would be more safe.
|
||||
private _onPropertyValueChange = (e: Event) => {
|
||||
const target = e.composedPath()[0] as any;
|
||||
|
||||
console.log("_onPropertyValueChange context", target.alias, target);
|
||||
|
||||
const property = this.getData().data.find((x) => x.alias === target.alias);
|
||||
if (property) {
|
||||
this._setPropertyValue(property.alias, target.value);
|
||||
} else {
|
||||
console.error('property was not found', target.alias);
|
||||
}
|
||||
|
||||
// We need to stop the event, so it does not bubble up to parent workspace contexts.
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
private _setPropertyValue(alias: string, value: unknown) {
|
||||
|
||||
console.log("about to change prop", this.getData());
|
||||
const newDataSet = this.getData().data.map((entry) => {
|
||||
if (entry.alias === alias) {
|
||||
return {alias: alias, value: value};
|
||||
}
|
||||
return entry;
|
||||
});
|
||||
|
||||
|
||||
const part = {data: newDataSet};
|
||||
console.log("result", part)
|
||||
this.update(part as Partial<ContentTypeType>);
|
||||
}
|
||||
|
||||
public save(): Promise<void> {
|
||||
if(!this._store) {
|
||||
// TODO: more beautiful error:
|
||||
|
||||
@@ -49,54 +49,6 @@ export class UmbWorkspaceContentElement extends UmbLitElement {
|
||||
@property()
|
||||
alias!: string;
|
||||
|
||||
// TODO: use a NodeDetails type here:
|
||||
@state()
|
||||
_content?: ContentTypeTypes;
|
||||
|
||||
private _workspaceContext?: UmbWorkspaceContentContext<ContentTypeTypes, UmbNodeStoreBase<ContentTypeTypes>>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext('umbWorkspaceContext', (instance) => {
|
||||
this._workspaceContext = instance;
|
||||
this._observeWorkspace();
|
||||
});
|
||||
|
||||
this.addEventListener('property-value-change', this._onPropertyValueChange);
|
||||
}
|
||||
|
||||
private async _observeWorkspace() {
|
||||
if (!this._workspaceContext) return;
|
||||
|
||||
this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => {
|
||||
this._content = data;
|
||||
});
|
||||
}
|
||||
|
||||
private _onPropertyValueChange = (e: Event) => {
|
||||
const target = e.composedPath()[0] as any;
|
||||
|
||||
console.log("_onPropertyValueChange", target.alias, target);
|
||||
|
||||
// TODO: Set value.
|
||||
const property = this._content?.properties.find((x) => x.alias === target.alias);
|
||||
if (property) {
|
||||
this._setPropertyValue(property.alias, target.value);
|
||||
} else {
|
||||
console.error('property was not found', target.alias);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: How do we ensure this is a change of this document and not nested documents? Should the event be stopped at this spot at avoid such.
|
||||
private _setPropertyValue(alias: string, value: unknown) {
|
||||
this._content?.data.forEach((data) => {
|
||||
if (data.alias === alias) {
|
||||
data.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-layout alias=${this.alias}>
|
||||
|
||||
@@ -29,7 +29,7 @@ export class UmbPropertyActionClearElement extends UmbLitElement implements UmbP
|
||||
private _clearValue() {
|
||||
// TODO: how do we want to update the value? Testing an event based approach. We need to test an api based approach too.
|
||||
this.value = '';
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import { Observable, ReplaySubject } from 'rxjs';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller';
|
||||
import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin';
|
||||
import type { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin';
|
||||
|
||||
export class UmbPropertyActionMenuContext {
|
||||
|
||||
private _isOpen: ReplaySubject<boolean> = new ReplaySubject(1);
|
||||
public readonly isOpen: Observable<boolean> = this._isOpen.asObservable();
|
||||
private _isOpen = new BehaviorSubject(false);
|
||||
public readonly isOpen = this._isOpen.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
new UmbContextProviderController(host, 'umbPropertyActionMenu', this);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this._isOpen.next(!this._isOpen.getValue());
|
||||
}
|
||||
open() {
|
||||
this._isOpen.next(true);
|
||||
}
|
||||
|
||||
close() {
|
||||
this._isOpen.next(false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
|
||||
|
||||
import '../property-action/property-action.element';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import { UmbObserverController } from '@umbraco-cms/observable-api';
|
||||
|
||||
@customElement('umb-property-action-menu')
|
||||
export class UmbPropertyActionMenuElement extends UmbLitElement {
|
||||
@@ -40,13 +41,19 @@ export class UmbPropertyActionMenuElement extends UmbLitElement {
|
||||
`,
|
||||
];
|
||||
|
||||
@property()
|
||||
public propertyEditorUIAlias = '';
|
||||
|
||||
|
||||
// TODO: we need to investigate context api vs values props and events
|
||||
@property()
|
||||
public value?: string;
|
||||
|
||||
@property()
|
||||
set propertyEditorUIAlias(alias: string) {
|
||||
this._observeActions(alias);
|
||||
}
|
||||
|
||||
private _actionsObserver?: UmbObserverController<ManifestPropertyAction[]>;
|
||||
|
||||
@state()
|
||||
private _actions: Array<ManifestPropertyAction> = [];
|
||||
|
||||
@@ -55,22 +62,25 @@ export class UmbPropertyActionMenuElement extends UmbLitElement {
|
||||
|
||||
private _propertyActionMenuContext = new UmbPropertyActionMenuContext(this);
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._observePropertyActions();
|
||||
this._observePropertyActionMenuOpenState();
|
||||
this.observe(this._propertyActionMenuContext.isOpen, (value) => {
|
||||
this._open = value;
|
||||
});
|
||||
}
|
||||
|
||||
private _observePropertyActions() {
|
||||
this.observe(
|
||||
private _observeActions(alias: string) {
|
||||
this._actionsObserver?.destroy();
|
||||
this._actionsObserver = this.observe(
|
||||
umbExtensionsRegistry
|
||||
.extensionsOfType('propertyAction')
|
||||
.pipe(
|
||||
map((propertyActions) =>
|
||||
propertyActions.filter((propertyAction) =>
|
||||
propertyAction.meta.propertyEditors.includes(this.propertyEditorUIAlias)
|
||||
map((propertyActions) => {
|
||||
return propertyActions.filter((propertyAction) =>
|
||||
propertyAction.meta.propertyEditors.includes(alias)
|
||||
)
|
||||
}
|
||||
)
|
||||
),
|
||||
(manifests) => {
|
||||
@@ -79,23 +89,17 @@ export class UmbPropertyActionMenuElement extends UmbLitElement {
|
||||
);
|
||||
}
|
||||
|
||||
private _observePropertyActionMenuOpenState() {
|
||||
this.observe(this._propertyActionMenuContext.isOpen, (value) => {
|
||||
this._open = value;
|
||||
});
|
||||
}
|
||||
|
||||
private _toggleMenu() {
|
||||
this._open ? this._propertyActionMenuContext.close() : this._propertyActionMenuContext.open();
|
||||
this._propertyActionMenuContext.toggle();
|
||||
}
|
||||
|
||||
private _handleClose(event: CustomEvent) {
|
||||
this._open = false;
|
||||
this._propertyActionMenuContext.close();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this._actions.length > 0) {
|
||||
return (this._actions.length > 0) ?
|
||||
html`
|
||||
<uui-popover id="popover" placement="bottom-start" .open=${this._open} @close="${this._handleClose}">
|
||||
<uui-button
|
||||
@@ -116,8 +120,7 @@ export class UmbPropertyActionMenuElement extends UmbLitElement {
|
||||
)}
|
||||
</div>
|
||||
</uui-popover>
|
||||
`;
|
||||
}
|
||||
return '';
|
||||
`
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement {
|
||||
private _setValue(newValue: Array<string>) {
|
||||
this.value = newValue;
|
||||
this._observePickedDocuments();
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
private _renderItem(item: FolderTreeItem) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export class UmbPropertyEditorUINumberElement extends LitElement {
|
||||
|
||||
private onInput(e: InputEvent) {
|
||||
this.value = (e.target as HTMLInputElement).value;
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -21,7 +21,7 @@ export class UmbPropertyEditorUITextBoxElement extends LitElement {
|
||||
|
||||
private onInput(e: InputEvent) {
|
||||
this.value = (e.target as HTMLInputElement).value;
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class UmbPropertyEditorUITextareaElement extends UmbLitElement {
|
||||
|
||||
private onInput(e: InputEvent) {
|
||||
this.value = (e.target as HTMLInputElement).value;
|
||||
this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -24,6 +24,14 @@ export interface Entity {
|
||||
parentKey: string;
|
||||
}
|
||||
|
||||
export interface ContentDetails {
|
||||
key: string; // TODO: Remove this when the backend is fixed
|
||||
isTrashed: boolean; // TODO: remove only temp part of refactor
|
||||
properties: Array<ContentProperty>;
|
||||
data: Array<ContentPropertyData>;
|
||||
//layout?: any; // TODO: define layout type - make it non-optional
|
||||
}
|
||||
|
||||
export interface UserEntity extends Entity {
|
||||
type: 'user';
|
||||
}
|
||||
@@ -95,7 +103,7 @@ export interface ContentPropertyData {
|
||||
}
|
||||
|
||||
// Documents
|
||||
export interface DocumentDetails extends DocumentTreeItem {
|
||||
export interface DocumentDetails extends DocumentTreeItem, ContentDetails {
|
||||
key: string; // TODO: Remove this when the backend is fixed
|
||||
isTrashed: boolean; // TODO: remove only temp part of refactor
|
||||
properties: Array<ContentProperty>;
|
||||
|
||||
Reference in New Issue
Block a user