diff --git a/src/Umbraco.Web.UI.Client/.storybook/preview.js b/src/Umbraco.Web.UI.Client/.storybook/preview.js
index 0f6a0ce713..9e30c4c31a 100644
--- a/src/Umbraco.Web.UI.Client/.storybook/preview.js
+++ b/src/Umbraco.Web.UI.Client/.storybook/preview.js
@@ -55,11 +55,11 @@ customElements.define('umb-storybook', UmbStoryBookElement);
const storybookProvider = (story) => html` ${story()} `;
const dataTypeStoreProvider = (story) => html`
- ${story()}
+ new UmbDataTypeDetailStore(host)}>${story()}
`;
const documentTypeStoreProvider = (story) => html`
- new UmbDocumentTypeDetailStore(host)}
>${story()}
`;
diff --git a/src/Umbraco.Web.UI.Client/libs/element/context-provider.element.ts b/src/Umbraco.Web.UI.Client/libs/element/context-provider.element.ts
index 7155397b84..9d04a92ab2 100644
--- a/src/Umbraco.Web.UI.Client/libs/element/context-provider.element.ts
+++ b/src/Umbraco.Web.UI.Client/libs/element/context-provider.element.ts
@@ -1,9 +1,18 @@
import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { UmbLitElement } from './lit-element.element';
+import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
@customElement('umb-context-provider')
export class UmbContextProviderElement extends UmbLitElement {
+
+ /**
+ * The value to provide to the context.
+ * @required
+ */
+ @property({ type: Object, attribute: false })
+ create?: (host:UmbControllerHostInterface) => unknown;
+
/**
* The value to provide to the context.
* @required
@@ -23,7 +32,9 @@ export class UmbContextProviderElement extends UmbLitElement {
if (!this.key) {
throw new Error('The key property is required.');
}
- if (!this.value) {
+ if (this.create) {
+ this.value = this.create(this);
+ } else if (!this.value) {
throw new Error('The value property is required.');
}
this.provideContext(this.key, this.value);
diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts
index e24fcb66c4..303e3b3874 100644
--- a/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts
+++ b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts
@@ -1,5 +1,5 @@
import { DeepState } from "./deep-state";
-import { appendToFrozenArray } from "./append-to-frozen-array.method";
+import { pushToUniqueArray } from "./push-to-unique-array.method";
/**
* @export
@@ -21,15 +21,15 @@ export class ArrayState extends DeepState {
/**
* @method append
* @param {unknown} unique - The unique value to remove.
- * @returns ArrayState
+ * @return {ArrayState} Reference to it self.
* @description - Remove some new data of this Subject.
* @example Example remove entry with key '1'
* const data = [
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
- * const mySubject = new ArrayState(data, (x) => x.key);
- * mySubject.remove([1]);
+ * const myState = new ArrayState(data, (x) => x.key);
+ * myState.remove([1]);
*/
remove(uniques: unknown[]) {
let next = this.getValue();
@@ -51,7 +51,7 @@ export class ArrayState extends DeepState {
/**
* @method filter
* @param {unknown} filterMethod - The unique value to remove.
- * @returns ArrayState
+ * @return {ArrayState} Reference to it self.
* @description - Remove some new data of this Subject.
* @example Example remove entry with key '1'
* const data = [
@@ -59,8 +59,8 @@ export class ArrayState extends DeepState {
* { key: 2, value: 'bar'},
* { key: 3, value: 'poo'}
* ];
- * const mySubject = new ArrayState(data, (x) => x.key);
- * mySubject.filter((entry) => entry.key !== 1);
+ * const myState = new ArrayState(data, (x) => x.key);
+ * myState.filter((entry) => entry.key !== 1);
*
* Result:
* [
@@ -75,48 +75,55 @@ export class ArrayState extends DeepState {
}
/**
- * @method append
- * @param {Partial} partialData - A object containing some of the data for this Subject.
- * @returns ArrayState
+ * @method appendOne
+ * @param {T} entry - new data to be added in this Subject.
+ * @return {ArrayState} Reference to it self.
* @description - Append some new data to this Subject.
* @example Example append some data.
* const data = [
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
- * const mySubject = new ArrayState(data);
- * mySubject.append({ key: 1, value: 'replaced-foo'});
+ * const myState = new ArrayState(data);
+ * myState.append({ key: 1, value: 'replaced-foo'});
*/
appendOne(entry: T) {
- this.next(appendToFrozenArray(this.getValue(), entry, this._getUnique))
+ const next = [...this.getValue()];
+ if(this._getUnique) {
+ pushToUniqueArray(next, entry, this._getUnique);
+ } else {
+ next.push(entry);
+ }
+ this.next(next);
return this;
}
/**
* @method append
* @param {T[]} entries - A array of new data to be added in this Subject.
- * @returns ArrayState
+ * @return {ArrayState} Reference to it self.
* @description - Append some new data to this Subject, if it compares to existing data it will replace it.
* @example Example append some data.
* const data = [
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
- * const mySubject = new ArrayState(data);
- * mySubject.append([
+ * const myState = new ArrayState(data);
+ * myState.append([
* { key: 1, value: 'replaced-foo'},
* { key: 3, value: 'another-bla'}
* ]);
*/
append(entries: T[]) {
- // TODO: stop calling appendOne for each but make sure to handle this in one.
- entries.forEach(x => this.appendOne(x))
-
- /*
- const unFrozenDataSet = [...this.getValue()];
-
- this.next(unFrozenDataSet);
- */
+ if(this._getUnique) {
+ const next = [...this.getValue()];
+ entries.forEach(entry => {
+ pushToUniqueArray(next, entry, this._getUnique!);
+ });
+ this.next(next);
+ } else {
+ this.next([...this.getValue(), ...entries]);
+ }
return this;
}
}
diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts
index 5175616f20..e726a075bb 100644
--- a/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts
+++ b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts
@@ -12,15 +12,17 @@ import { DeepState } from "./deep-state";
export class ObjectState extends DeepState {
/**
- * @method append
- * @param {Partial} partialData - A object containing some of the data for this Subject.
+ * @method update
+ * @param {Partial} partialData - A object containing some of the data to update in this Subject.
* @description - Append some new data to this Object.
+ * @return {ObjectState} Reference to it self.
* @example Example append some data.
* const data = {key: 'myKey', value: 'myInitialValue'};
- * const mySubject = new ObjectState(data)
- * mySubject.append({value: 'myNewValue'})
+ * const myState = new ObjectState(data);
+ * myState.update({value: 'myNewValue'});
*/
update(partialData: Partial) {
this.next({ ...this.getValue(), ...partialData });
+ return this;
}
}
diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/push-to-unique-array.method.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/push-to-unique-array.method.ts
new file mode 100644
index 0000000000..35e4e3169c
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/observable-api/push-to-unique-array.method.ts
@@ -0,0 +1,22 @@
+/**
+ * @export
+ * @method pushToUniqueArray
+ * @param {T[]} data - An array of objects.
+ * @param {T} entry - The object to insert or replace with.
+ * @param {getUniqueMethod: (entry: T) => unknown} [getUniqueMethod] - Method to get the unique value of an entry.
+ * @description - Append or replaces an item of an Array.
+ * @example Example append new entry for a Array. Where the key is unique and the item will be updated if matched with existing.
+ * const entry = {key: 'myKey', value: 'myValue'};
+ * const newDataSet = pushToUniqueArray([], entry, x => x.key === key);
+ * mySubject.next(newDataSet);
+ */
+export function pushToUniqueArray(data: T[], entry: T, getUniqueMethod: (entry: T) => unknown): T[] {
+ const unique = getUniqueMethod(entry);
+ const indexToReplace = data.findIndex((x) => getUniqueMethod(x) === unique);
+ if (indexToReplace !== -1) {
+ data[indexToReplace] = entry;
+ } else {
+ data.push(entry);
+ }
+ return data;
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
index b76b29f580..3f89ad1f4b 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
@@ -46,9 +46,9 @@ export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext x.alias);
+ const newDataSet = appendToFrozenArray(this._data.getValue().data, entry, x => x.alias);
- this.update({data: newDataSet});
+ this._data.update({data: newDataSet});
}
/*
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts
index 76852a829f..b55c24255e 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts
@@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, UmbNotificationDefaultData } from '@umbraco-cms/notification';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api';
-import { DeepState, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api';
+import { ObjectState, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api';
import { UmbContentStore } from '@umbraco-cms/store';
import type { ContentTreeItem } from '@umbraco-cms/backend-api';
@@ -33,7 +33,7 @@ export abstract class UmbWorkspaceContentContext<
constructor(host: UmbControllerHostInterface, defaultData: ContentTypeType, storeAlias: string, entityType: string) {
this._host = host;
- this._data = new DeepState(defaultData);
+ this._data = new ObjectState(defaultData);
this.data = this._data.asObservable();
this.name = createObservablePart(this._data, (data) => data.name);
diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json
index 57d8845a35..3a3e9d3560 100644
--- a/src/Umbraco.Web.UI.Client/tsconfig.json
+++ b/src/Umbraco.Web.UI.Client/tsconfig.json
@@ -21,6 +21,7 @@
"baseUrl": ".",
"paths": {
"@umbraco-cms/css": ["libs/css/custom-properties.css"],
+ "@umbraco-cms/modal": ["src/core/modal"],
"@umbraco-cms/models": ["libs/models"],
"@umbraco-cms/backend-api": ["libs/backend-api"],
"@umbraco-cms/context-api": ["libs/context-api"],