Merge branch 'main' into feature/multiple-text-string-property-editor

This commit is contained in:
Mads Rasmussen
2023-01-24 17:17:11 +01:00
64 changed files with 378 additions and 333 deletions

View File

@@ -5,7 +5,7 @@
* @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return.
* @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different.
* @description - Creates a RxJS Observable from RxJS Subject.
* @example <caption>Example append new entry for a UniqueBehaviorSubject which is an array. Where the key is unique and the item will be updated if matched with existing.</caption>
* @example <caption>Example append new entry for a ArrayState or a part of DeepState/ObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing.</caption>
* const entry = {key: 'myKey', value: 'myValue'};
* const newDataSet = appendToFrozenArray(mySubject.getValue(), entry, x => x.key === key);
* mySubject.next(newDataSet);

View File

@@ -1,13 +1,13 @@
import { expect } from '@open-wc/testing';
import { UniqueArrayBehaviorSubject } from './unique-array-behavior-subject';
import { ArrayState } from './array-state';
import { createObservablePart } from '@umbraco-cms/observable-api';
describe('UniqueArrayBehaviorSubject', () => {
describe('ArrayState', () => {
type ObjectType = {key: string, another: string};
type ArrayType = ObjectType[];
let subject: UniqueArrayBehaviorSubject<ObjectType>;
let subject: ArrayState<ObjectType>;
let initialData: ArrayType;
beforeEach(() => {
@@ -16,7 +16,7 @@ describe('UniqueArrayBehaviorSubject', () => {
{key: '2', another: 'myValue2'},
{key: '3', another: 'myValue3'}
];
subject = new UniqueArrayBehaviorSubject(initialData, x => x.key);
subject = new ArrayState(initialData, x => x.key);
});

View File

@@ -1,17 +1,17 @@
import { UniqueBehaviorSubject } from "./unique-behavior-subject";
import { DeepState } from "./deep-state";
import { appendToFrozenArray } from "./append-to-frozen-array.method";
/**
* @export
* @class UniqueObjectBehaviorSubject
* @extends {UniqueBehaviorSubject<T>}
* @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* @class ArrayState
* @extends {DeepState<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*
* The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object.
* The ArrayState provides methods to append data when the data is an Object.
*/
export class UniqueArrayBehaviorSubject<T> extends UniqueBehaviorSubject<T[]> {
export class ArrayState<T> extends DeepState<T[]> {
constructor(initialData: T[], private _getUnique?: (entry: T) => unknown) {
@@ -27,7 +27,7 @@ export class UniqueArrayBehaviorSubject<T> extends UniqueBehaviorSubject<T[]> {
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
* const mySubject = new UniqueArrayBehaviorSubject(data, (x) => x.key);
* const mySubject = new ArrayState(data, (x) => x.key);
* mySubject.remove([1]);
*/
remove(uniques: unknown[]) {
@@ -54,7 +54,7 @@ export class UniqueArrayBehaviorSubject<T> extends UniqueBehaviorSubject<T[]> {
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
* const mySubject = new UniqueArrayBehaviorSubject(data);
* const mySubject = new ArrayState(data);
* mySubject.append({ key: 1, value: 'replaced-foo'});
*/
appendOne(entry: T) {
@@ -70,7 +70,7 @@ export class UniqueArrayBehaviorSubject<T> extends UniqueBehaviorSubject<T[]> {
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
* const mySubject = new UniqueArrayBehaviorSubject(data);
* const mySubject = new ArrayState(data);
* mySubject.append([
* { key: 1, value: 'replaced-foo'},
* { key: 3, value: 'another-bla'}

View File

@@ -0,0 +1,19 @@
import { BehaviorSubject } from "rxjs";
/**
* @export
* @class BasicState
* @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value.
*/
export class BasicState<T> extends BehaviorSubject<T> {
constructor(initialData: T) {
super(initialData);
}
next(newData: T): void {
if(newData !== this.getValue()) {
super.next(newData);
}
}
}

View File

@@ -1,5 +1,5 @@
import { distinctUntilChanged, map, Observable, shareReplay } from "rxjs";
import { MappingFunction, MemoizationFunction, defaultMemoization } from "./unique-behavior-subject";
import { MappingFunction, MemoizationFunction, defaultMemoization } from "./deep-state";
/**
* @export
@@ -12,7 +12,7 @@ import { MappingFunction, MemoizationFunction, defaultMemoization } from "./uniq
* public readonly myPart = CreateObservablePart(this._data, (data) => data.myPart);
*/
export function createObservablePart<T, R>(
export function createObservablePart<R, T>(
source$: Observable<T>,
mappingFunction: MappingFunction<T, R>,
memoizationFunction?: MemoizationFunction<R>

View File

@@ -1,17 +1,17 @@
import { expect } from '@open-wc/testing';
import { UniqueBehaviorSubject } from './unique-behavior-subject';
import { DeepState } from './deep-state';
import { createObservablePart } from '@umbraco-cms/observable-api';
describe('UniqueBehaviorSubject', () => {
describe('DeepState', () => {
type ObjectType = {key: string, another: string};
let subject: UniqueBehaviorSubject<ObjectType>;
let subject: DeepState<ObjectType>;
let initialData: ObjectType;
beforeEach(() => {
initialData = {key: 'some', another: 'myValue'};
subject = new UniqueBehaviorSubject(initialData);
subject = new DeepState(initialData);
});

View File

@@ -3,7 +3,7 @@ import { BehaviorSubject } from "rxjs";
// TODO: Should this handle array as well?
function deepFreeze<T>(inObj: T): T {
if(typeof inObj === 'object') {
if(inObj != null && typeof inObj === 'object') {
Object.freeze(inObj);
Object.getOwnPropertyNames(inObj)?.forEach(function (prop) {
@@ -40,12 +40,12 @@ export function defaultMemoization(previousValue: any, currentValue: any): boole
/**
* @export
* @class UniqueBehaviorSubject
* @class DeepState
* @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*/
export class UniqueBehaviorSubject<T> extends BehaviorSubject<T> {
export class DeepState<T> extends BehaviorSubject<T> {
constructor(initialData: T) {
super(deepFreeze(initialData));
}

View File

@@ -1,7 +1,9 @@
export * from './observer.controller';
export * from './observer';
export * from './unique-behavior-subject';
export * from './unique-array-behavior-subject';
export * from './unique-object-behavior-subject';
export * from './number-state';
export * from './string-state';
export * from './deep-state';
export * from './array-state';
export * from './object-state';
export * from './create-observable-part.method'
export * from './append-to-frozen-array.method'

View File

@@ -0,0 +1,14 @@
import { BasicState } from "./basic-state";
/**
* @export
* @class NumberState
* @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value.
*/
export class NumberState<T> extends BasicState<T | number> {
constructor(initialData: T | number) {
super(initialData);
}
}

View File

@@ -1,17 +1,17 @@
import { expect } from '@open-wc/testing';
import { UniqueObjectBehaviorSubject } from './unique-object-behavior-subject';
import { ObjectState } from './object-state';
import { createObservablePart } from '@umbraco-cms/observable-api';
describe('UniqueObjectBehaviorSubject', () => {
describe('ObjectState', () => {
type ObjectType = {key: string, another: string};
let subject: UniqueObjectBehaviorSubject<ObjectType>;
let subject: ObjectState<ObjectType>;
let initialData: ObjectType;
beforeEach(() => {
initialData = {key: 'some', another: 'myValue'};
subject = new UniqueObjectBehaviorSubject(initialData);
subject = new ObjectState(initialData);
});

View File

@@ -1,15 +1,15 @@
import { UniqueBehaviorSubject } from "./unique-behavior-subject";
import { DeepState } from "./deep-state";
/**
* @export
* @class UniqueObjectBehaviorSubject
* @extends {UniqueBehaviorSubject<T>}
* @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* @class ObjectState
* @extends {DeepState<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*
* The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object.
* The ObjectState provides methods to append data when the data is an Object.
*/
export class UniqueObjectBehaviorSubject<T> extends UniqueBehaviorSubject<T> {
export class ObjectState<T> extends DeepState<T> {
/**
* @method append
@@ -17,7 +17,7 @@ export class UniqueObjectBehaviorSubject<T> extends UniqueBehaviorSubject<T> {
* @description - Append some new data to this Object.
* @example <caption>Example append some data.</caption>
* const data = {key: 'myKey', value: 'myInitialValue'};
* const mySubject = new UniqueObjectBehaviorSubject(data)
* const mySubject = new ObjectState(data)
* mySubject.append({value: 'myNewValue'})
*/
update(partialData: Partial<T>) {

View File

@@ -2,7 +2,7 @@ import { Observable } from 'rxjs';
import { UmbObserver } from './observer';
import { UmbControllerInterface, UmbControllerHostInterface } from '@umbraco-cms/controller';
export class UmbObserverController<T> extends UmbObserver<T> implements UmbControllerInterface {
export class UmbObserverController<T = unknown> extends UmbObserver<T> implements UmbControllerInterface {
_alias?: string;
public get unique() {
return this._alias;

View File

@@ -0,0 +1,13 @@
import { BasicState } from "./basic-state";
/**
* @export
* @class StringState
* @extends {BasicState<T>}
* @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value.
*/
export class StringState<T> extends BasicState<T | string> {
constructor(initialData: T | string) {
super(initialData);
}
}

View File

@@ -2,8 +2,6 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
import { UmbLitElement } from '@umbraco-cms/element';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../core/modal';
import { UmbUserStore } from './users/users/user.store';
@@ -14,6 +12,7 @@ import {
UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN,
} from './users/current-user/current-user-history.store';
import { UmbBackofficeContext, UMB_BACKOFFICE_CONTEXT_TOKEN } from './shared/components/backoffice-frame/backoffice.context';
import {UmbDocumentTypeDetailStore} from './documents/document-types/document-type.detail.store';
import {UmbDocumentTypeTreeStore} from './documents/document-types/document-type.tree.store';
import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store';
@@ -30,9 +29,9 @@ import { UmbDictionaryTreeStore } from './translation/dictionary/dictionary.tree
import { UmbDocumentBlueprintDetailStore } from './documents/document-blueprints/document-blueprint.detail.store';
import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/document-blueprint.tree.store';
import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from './shared/components/section/section.store';
import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store';
import { UmbDataTypeTreeStore } from './settings/data-types/data-type.tree.store';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
// Domains
@@ -45,6 +44,7 @@ import './users';
import './packages';
import './search';
import './shared';
import { UmbLitElement } from '@umbraco-cms/element';
@defineElement('umb-backoffice')
export class UmbBackofficeElement extends UmbLitElement {
@@ -92,7 +92,7 @@ export class UmbBackofficeElement extends UmbLitElement {
new UmbDocumentBlueprintDetailStore(this);
new UmbDocumentBlueprintTreeStore(this);
this.provideContext(UMB_SECTION_STORE_CONTEXT_TOKEN, new UmbSectionStore());
this.provideContext(UMB_BACKOFFICE_CONTEXT_TOKEN, new UmbBackofficeContext());
this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore());
}

View File

@@ -1,6 +1,6 @@
import type { DocumentBlueprintDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export class UmbDocumentBlueprintDetailStore extends UmbStoreBase {
// TODO: use the right type:
#data = new UniqueArrayBehaviorSubject<DocumentBlueprintDetails>([], (x) => x.key);
#data = new ArrayState<DocumentBlueprintDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
@@ -33,7 +33,7 @@ export class UmbDocumentBlueprintDetailStore extends UmbStoreBase {
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/document-blueprint/${key}`)
fetch(`/umbraco/management/api/v1/document-blueprint/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);

View File

@@ -1,7 +1,7 @@
import { DocumentBlueprintResource, DocumentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export const UMB_DocumentBlueprint_TREE_STORE_CONTEXT_TOKEN = new UmbContextToke
export class UmbDocumentBlueprintTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DocumentTreeItem>([], (x) => x.key);
#data = new ArrayState<DocumentTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,6 +1,6 @@
import type { DocumentTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -17,7 +17,7 @@ export const UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<
export class UmbDocumentTypeDetailStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DocumentTypeDetails>([], (x) => x.key);
#data = new ArrayState<DocumentTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
@@ -32,7 +32,7 @@ export class UmbDocumentTypeDetailStore extends UmbStoreBase {
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/document-type/${key}`)
fetch(`/umbraco/management/api/v1/document-type/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);

View File

@@ -1,7 +1,7 @@
import { DocumentTypeResource, DocumentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<Um
export class UmbDocumentTypeTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DocumentTreeItem>([], (x) => x.key);
#data = new ArrayState<DocumentTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,6 +1,6 @@
import type { DocumentDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase, UmbContentStore } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -17,7 +17,7 @@ export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDo
export class UmbDocumentDetailStore extends UmbStoreBase implements UmbContentStore<DocumentDetails> {
private _data = new UniqueArrayBehaviorSubject<DocumentDetails>([], (x) => x.key);
private _data = new ArrayState<DocumentDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
@@ -25,6 +25,7 @@ export class UmbDocumentDetailStore extends UmbStoreBase implements UmbContentSt
}
getByKey(key: string) {
console.log("document getByKey", key)
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/details/${key}`)
.then((res) => res.json())

View File

@@ -1,7 +1,7 @@
import { DocumentResource, DocumentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocu
export class UmbDocumentTreeStore extends UmbStoreBase {
private _data = new UniqueArrayBehaviorSubject<DocumentTreeItem>([], (x) => x.key);
private _data = new ArrayState<DocumentTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,4 +1,4 @@
import { UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store';
import { UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from '../document.tree.store';
import type { ManifestTree, ManifestTreeItemAction } from '@umbraco-cms/models';
const treeAlias = 'Umb.Tree.Documents';
@@ -8,7 +8,7 @@ const tree: ManifestTree = {
alias: treeAlias,
name: 'Documents Tree',
meta: {
storeAlias: UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString(),
storeAlias: UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN.toString(),
},
};

View File

@@ -37,6 +37,8 @@ const DefaultDocumentData = {
export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext<DocumentDetails, UmbDocumentDetailStore> {
constructor(host: UmbControllerHostInterface) {
super(host, DefaultDocumentData, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'document');
console.log("UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN", UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString())
}
public setPropertyValue(alias: string, value: unknown) {

View File

@@ -1,6 +1,6 @@
import type { DataTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -17,7 +17,7 @@ export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<Umb
export class UmbMediaTypeDetailStore extends UmbStoreBase {
private _data = new UniqueArrayBehaviorSubject<DataTypeDetails>([], (x) => x.key);
private _data = new ArrayState<DataTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,7 +1,7 @@
import { FolderTreeItem, MediaTypeResource } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -17,7 +17,7 @@ export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMed
export class UmbMediaTypeTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<FolderTreeItem>([], (x) => x.key);
#data = new ArrayState<FolderTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,23 +1,23 @@
import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase, UmbContentStore } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaDetailStore>('UmbDocumentDetailStore');
export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaDetailStore>('UmbMediaDetailStore');
/**
* @export
* @class UmbMediaStore
* @class UmbMediaDetailStore
* @extends {UmbStoreBase}
* @description - Data Store for Media
*/
export class UmbMediaDetailStore extends UmbStoreBase implements UmbContentStore<MediaDetails> {
#data = new UniqueArrayBehaviorSubject<DocumentDetails>([], (x) => x.key);
#data = new ArrayState<DocumentDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -2,7 +2,7 @@ import type { Observable } from 'rxjs';
import { MediaResource, ContentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -10,7 +10,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTreeStore>('UmbMediaTreeStore');
// TODO: Stop using ContentTreeItem
type MediaTreeItem = ContentTreeItem;
export type MediaTreeItem = ContentTreeItem;
/**
* @export
@@ -22,7 +22,7 @@ export class UmbMediaTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<MediaTreeItem>([], (x) => x.key);
#data = new ArrayState<MediaTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,10 +1,10 @@
import { UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from '../media.tree.store';
import type {
ManifestWorkspace,
ManifestWorkspaceAction,
ManifestWorkspaceView,
ManifestWorkspaceViewCollection,
} from '@umbraco-cms/models';
import { UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN } from '../media.detail.store';
const workspace: ManifestWorkspace = {
type: 'workspace',
@@ -59,7 +59,7 @@ const workspaceViewCollections: Array<ManifestWorkspaceViewCollection> = [
pathname: 'collection',
icon: 'umb:grid',
entityType: 'media',
storeAlias: UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString(),
storeAlias: UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN.toString(),
},
},
];

View File

@@ -1,4 +1,4 @@
import { UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN } from './media/media.detail.store';
import { UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from './media/media.tree.store';
import type { ManifestDashboardCollection, ManifestSection } from '@umbraco-cms/models';
const sectionAlias = 'Umb.Section.Media';
@@ -25,7 +25,7 @@ const dashboards: Array<ManifestDashboardCollection> = [
sections: [sectionAlias],
pathname: 'media-management',
entityType: 'media',
storeAlias: UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString(),
storeAlias: UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN.toString(),
},
},
];

View File

@@ -1,7 +1,7 @@
import { Observable } from 'rxjs';
import type { MemberGroupDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbStoreBase } from '@umbraco-cms/store';
@@ -16,7 +16,7 @@ export const UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMembe
export class UmbMemberGroupStore extends UmbStoreBase {
#groups = new UniqueArrayBehaviorSubject<MemberGroupDetails>([], x => x.key);
#groups = new ArrayState<MemberGroupDetails>([], x => x.key);
public groups = this.#groups.asObservable();

View File

@@ -1,6 +1,6 @@
import type { MemberTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -17,7 +17,7 @@ export const UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<Um
export class UmbMemberTypeDetailStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<MemberTypeDetails>([], (x) => x.key);
#data = new ArrayState<MemberTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,7 +1,7 @@
import { EntityTreeItem, MemberTypeResource, } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -19,7 +19,7 @@ export class UmbMemberTypeTreeStore extends UmbStoreBase {
// TODO: use the right type here:
#data = new UniqueArrayBehaviorSubject<EntityTreeItem>([], (x) => x.key);
#data = new ArrayState<EntityTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,8 +1,10 @@
import type { DataTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { DataTypeResource } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeDetailStore>('UmbDataTypeDetailStore');
@@ -17,7 +19,7 @@ export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbD
export class UmbDataTypeDetailStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DataTypeDetails>([], (x) => x.key);
#data = new ArrayState<DataTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
@@ -32,12 +34,13 @@ export class UmbDataTypeDetailStore extends UmbStoreBase {
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/data-type/${key}`)
fetch(`/umbraco/backoffice/data-type/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);
});
return createObservablePart(this.#data, (documents) =>
documents.find((document) => document.key === key)
);

View File

@@ -1,7 +1,7 @@
import { DataTypeResource, DocumentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDat
export class UmbDataTypeTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DocumentTreeItem>([], (x) => x.key);
#data = new ArrayState<DocumentTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -2,7 +2,7 @@ import { ContentTreeItem } from '@umbraco-cms/backend-api';
import { UmbTreeStore } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/context-api';
import { UniqueBehaviorSubject, UmbObserverController } from '@umbraco-cms/observable-api';
import { DeepState, UmbObserverController } from '@umbraco-cms/observable-api';
export class UmbCollectionContext<
DataType extends ContentTreeItem,
StoreType extends UmbTreeStore<DataType> = UmbTreeStore<DataType>
@@ -13,15 +13,15 @@ export class UmbCollectionContext<
private _store?: StoreType;
protected _dataObserver?: UmbObserverController<DataType[]>;
#data = new UniqueBehaviorSubject(<Array<DataType>>[]);
#data = new DeepState(<Array<DataType>>[]);
public readonly data = this.#data.asObservable();
#selection = new UniqueBehaviorSubject(<Array<string>>[]);
#selection = new DeepState(<Array<string>>[]);
public readonly selection = this.#selection.asObservable();
/*
TODO:
private _search = new UniqueBehaviorSubject('');
private _search = new StringState('');
public readonly search = this._search.asObservable();
*/

View File

@@ -3,7 +3,7 @@ import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { UmbCollectionContext, UMB_COLLECTION_CONTEXT_TOKEN } from '../collection.context';
import type { MediaDetails } from '@umbraco-cms/models';
import type { MediaTreeItem } from '../../../media/media/media.tree.store';
import { UmbLitElement } from '@umbraco-cms/element';
@customElement('umb-collection-view-media-grid')
@@ -65,12 +65,12 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement {
];
@state()
private _mediaItems?: Array<MediaDetails>;
private _mediaItems?: Array<MediaTreeItem>;
@state()
private _selection?: Array<string>;
private _selection: Array<string> = [];
private _collectionContext?: UmbCollectionContext<MediaDetails>;
private _collectionContext?: UmbCollectionContext<MediaTreeItem>;
constructor() {
super();
@@ -115,24 +115,31 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement {
});
}
private _handleOpenItem(mediaItem: MediaDetails) {
private _handleOpenItem(mediaItem: MediaTreeItem) {
//TODO: Fix when we have dynamic routing
history.pushState(null, '', 'section/media/media/' + mediaItem.key);
}
private _handleSelect(mediaItem: MediaDetails) {
this._collectionContext?.select(mediaItem.key);
private _handleSelect(mediaItem: MediaTreeItem) {
if(mediaItem.key) {
this._collectionContext?.select(mediaItem.key);
}
}
private _handleDeselect(mediaItem: MediaDetails) {
this._collectionContext?.deselect(mediaItem.key);
private _handleDeselect(mediaItem: MediaTreeItem) {
if(mediaItem.key) {
this._collectionContext?.deselect(mediaItem.key);
}
}
private _isSelected(mediaItem: MediaDetails) {
return this._selection?.includes(mediaItem.key);
private _isSelected(mediaItem: MediaTreeItem) {
if(mediaItem.key) {
return this._selection.includes(mediaItem.key);
}
return false;
}
private _renderMediaItem(item: MediaDetails) {
private _renderMediaItem(item: MediaTreeItem) {
const name = item.name || '';
//TODO: fix the file extension when media items have a file extension.
return html`<uui-card-media
@@ -159,7 +166,7 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement {
${this._mediaItems
? repeat(
this._mediaItems,
(file, index) => file.key + index,
(file, index) => (file.key || '') + index,
(file) => this._renderMediaItem(file)
)
: ''}

View File

@@ -2,7 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, CSSResultGroup, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { when } from 'lit/directives/when.js';
import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from '../section/section.store';
import { UMB_BACKOFFICE_CONTEXT_TOKEN } from './backoffice.context';
import type { UmbBackofficeContext } from './backoffice.context';
import type { ManifestSection } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
@@ -49,13 +50,13 @@ export class UmbBackofficeHeaderSections extends UmbLitElement {
@state()
private _currentSectionAlias = '';
private _sectionStore?: UmbSectionStore;
private _backofficeContext?: UmbBackofficeContext;
constructor() {
super();
this.consumeContext(UMB_SECTION_STORE_CONTEXT_TOKEN, (sectionStore) => {
this._sectionStore = sectionStore;
this.consumeContext(UMB_BACKOFFICE_CONTEXT_TOKEN, (backofficeContext) => {
this._backofficeContext = backofficeContext;
this._observeSections();
this._observeCurrentSection();
});
@@ -66,15 +67,8 @@ export class UmbBackofficeHeaderSections extends UmbLitElement {
this._open = !this._open;
}
private _handleTabClick(e: PointerEvent) {
const tab = e.currentTarget as HTMLElement;
// TODO: we need to be able to prevent the tab from setting the active state
if (tab.id === 'moreTab') return;
if (!tab.dataset.alias) return;
this._sectionStore?.setCurrent(tab.dataset.alias);
private _handleSectionTabClick(alias: string) {
this._backofficeContext?.setActiveSectionAlias(alias);
}
private _handleLabelClick() {
@@ -85,19 +79,19 @@ export class UmbBackofficeHeaderSections extends UmbLitElement {
}
private _observeSections() {
if (!this._sectionStore) return;
if (!this._backofficeContext) return;
this.observe(this._sectionStore.getAllowed(), (allowedSections) => {
this.observe(this._backofficeContext.getAllowedSections(), (allowedSections) => {
this._sections = allowedSections;
this._visibleSections = this._sections;
});
}
private _observeCurrentSection() {
if (!this._sectionStore) return;
if (!this._backofficeContext) return;
this.observe(this._sectionStore.currentAlias, (currentSectionAlias) => {
this._currentSectionAlias = currentSectionAlias;
this.observe(this._backofficeContext.activeSectionAlias, (currentSectionAlias) => {
this._currentSectionAlias = currentSectionAlias || '';
});
}
@@ -107,11 +101,11 @@ export class UmbBackofficeHeaderSections extends UmbLitElement {
${this._visibleSections.map(
(section: ManifestSection) => html`
<uui-tab
@click="${this._handleTabClick}"
@click="${() => this._handleSectionTabClick(section.alias)}"
?active="${this._currentSectionAlias === section.alias}"
href="${`section/${section.meta.pathname}`}"
label="${section.meta.label || section.name}"
data-alias="${section.alias}"></uui-tab>
></uui-tab>
`
)}
${this._renderExtraSections()}
@@ -123,7 +117,7 @@ export class UmbBackofficeHeaderSections extends UmbLitElement {
return when(
this._extraSections.length > 0,
() => html`
<uui-tab id="moreTab" @click="${this._handleTabClick}">
<uui-tab id="moreTab">
<uui-popover .open=${this._open} placement="bottom-start" @close="${() => (this._open = false)}">
<uui-button slot="trigger" look="primary" label="More" @click="${this._handleMore}" compact>
<uui-symbol-more></uui-symbol-more>

View File

@@ -3,9 +3,9 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { state } from 'lit/decorators.js';
import { IRoutingInfo } from 'router-slot';
import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from '../section/section.store';
import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section/section.context';
import { UmbSectionElement } from '../section/section.element';
import { UmbBackofficeContext, UMB_BACKOFFICE_CONTEXT_TOKEN } from './backoffice.context';
import { createExtensionElement } from '@umbraco-cms/extensions-api';
import type { ManifestSection } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
@@ -35,29 +35,31 @@ export class UmbBackofficeMain extends UmbLitElement {
private _sections: Array<ManifestSection> = [];
private _routePrefix = 'section/';
private _backofficeContext?: UmbBackofficeContext;
private _sectionContext?: UmbSectionContext;
private _sectionStore?: UmbSectionStore;
constructor() {
super();
this.consumeContext(UMB_SECTION_STORE_CONTEXT_TOKEN, (_instance) => {
this._sectionStore = _instance;
this._observeSections();
this.consumeContext(UMB_BACKOFFICE_CONTEXT_TOKEN, (_instance) => {
this._backofficeContext = _instance;
this._observeBackoffice();
});
}
private async _observeSections() {
if (!this._sectionStore) return;
this.observe(this._sectionStore.getAllowed(), (sections) => {
this._sections = sections;
if (!sections) return;
this._createRoutes();
});
private async _observeBackoffice() {
if(this._backofficeContext) {
this.observe(this._backofficeContext.getAllowedSections(), (sections) => {
this._sections = sections;
this._createRoutes();
}, 'observeAllowedSections');
}
}
private _createRoutes() {
if (!this._sections) return;
this._routes = [];
this._routes = this._sections.map((section) => {
return {
@@ -86,7 +88,7 @@ export class UmbBackofficeMain extends UmbLitElement {
const currentPath = info.match.route.path;
const section = this._sections.find((s) => this._routePrefix + s.meta.pathname === currentPath);
if (!section) return;
this._sectionStore?.setCurrent(section.alias);
this._backofficeContext?.setActiveSectionAlias(section.alias);
this._provideSectionContext(section);
};

View File

@@ -0,0 +1,28 @@
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { StringState } from '@umbraco-cms/observable-api';
export class UmbBackofficeContext {
#activeSectionAlias = new StringState(undefined);
public readonly activeSectionAlias = this.#activeSectionAlias.asObservable();
public getAllowedSections() {
// TODO: implemented allowed filtering based on user, maybe this will be a general need and solved else where so this might not be needed in the end.
/*
const { data } = await getUserSections({});
this._allowedSection = data.sections;
*/
return umbExtensionsRegistry.extensionsOfType('section');
}
public setActiveSectionAlias(alias: string) {
this.#activeSectionAlias.next(alias);
}
}
export const UMB_BACKOFFICE_CONTEXT_TOKEN = new UmbContextToken<UmbBackofficeContext>('UmbBackofficeContext');

View File

@@ -8,7 +8,6 @@ import { createExtensionElement } from '@umbraco-cms/extensions-api';
import type {
ManifestDashboard,
ManifestDashboardCollection,
ManifestSection,
ManifestWithMeta,
} from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
@@ -58,7 +57,7 @@ export class UmbSectionDashboardsElement extends UmbLitElement {
@state()
private _currentSectionPathname = '';
private _currentSectionAlias = '';
private _currentSectionAlias?: string;
private _sectionContext?: UmbSectionContext;
constructor() {
@@ -73,12 +72,12 @@ export class UmbSectionDashboardsElement extends UmbLitElement {
private _observeSectionContext() {
if (!this._sectionContext) return;
this.observe(this._sectionContext.manifest.pipe(first()), (section) => {
if (section) {
this._currentSectionAlias = section.alias;
this._currentSectionPathname = section.meta.pathname;
this._observeDashboards();
}
this.observe(this._sectionContext.alias.pipe(first()), (alias) => {
this._currentSectionAlias = alias;
this._observeDashboards();
});
this.observe(this._sectionContext.pathname.pipe(first()), (pathname) => {
this._currentSectionPathname = pathname || '';
});
}

View File

@@ -28,8 +28,9 @@ export class UmbSectionSidebarMenuElement extends UmbLitElement {
private _observeCurrentSection() {
if (!this._sectionContext) return;
this.observe(this._sectionContext?.manifest, (section) => {
this._currentSectionAlias = section.alias;
this.observe(this._sectionContext.alias, (alias) => {
console.log("this._sectionContext?.alias", alias)
this._currentSectionAlias = alias;
});
}

View File

@@ -2,7 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context';
import type { ManifestSection } from '@umbraco-cms/models';
import '../../tree/context-menu/tree-context-menu.service';
import { UmbLitElement } from '@umbraco-cms/element';
@@ -48,9 +47,11 @@ export class UmbSectionSidebarElement extends UmbLitElement {
private _observeSectionContext() {
if (!this._sectionContext) return;
this.observe(this._sectionContext.manifest, (section) => {
this._sectionLabel = section.meta.label || section.name;
this._sectionPathname = section.meta.pathname;
this.observe(this._sectionContext.pathname, (pathname) => {
this._sectionPathname = pathname || '';
});
this.observe(this._sectionContext.label, (label) => {
this._sectionLabel = label || '';
});
}

View File

@@ -1,11 +1,12 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { EMPTY, map, of, Subscription, switchMap } from 'rxjs';
import { map, of } from 'rxjs';
import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context';
import type { ManifestSectionView } from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbLitElement } from '@umbraco-cms/element';
import { UmbObserverController } from '@umbraco-cms/observable-api';
@customElement('umb-section-views')
export class UmbSectionViewsElement extends UmbLitElement {
@@ -35,11 +36,10 @@ export class UmbSectionViewsElement extends UmbLitElement {
private _routerFolder = '';
@state()
private _activeView?: ManifestSectionView;
private _activeViewPathname?: string;
private _sectionContext?: UmbSectionContext;
private _viewsSubscription?: Subscription;
private _activeViewSubscription?: Subscription;
private _extensionsObserver?: UmbObserverController;
constructor() {
super();
@@ -60,43 +60,35 @@ export class UmbSectionViewsElement extends UmbLitElement {
private _observeViews() {
if (!this._sectionContext) return;
this._viewsSubscription?.unsubscribe();
this._viewsSubscription = this._sectionContext?.manifest
.pipe(
switchMap((section) => {
if (!section) return EMPTY;
return (
umbExtensionsRegistry
?.extensionsOfType('sectionView')
.pipe(
map((views) =>
views
.filter((view) => view.meta.sections.includes(section.alias))
.sort((a, b) => b.meta.weight - a.meta.weight)
)
) ?? of([])
);
})
)
.subscribe((views) => {
this._views = views;
});
this.observe(this._sectionContext.alias, (sectionAlias) => {
this._observeExtensions(sectionAlias);
}, 'viewsObserver')
}
private _observeExtensions(sectionAlias?: string) {
this._extensionsObserver?.destroy();
if(sectionAlias) {
this._extensionsObserver = this.observe(
umbExtensionsRegistry?.extensionsOfType('sectionView').pipe(
map((views) =>
views
.filter((view) => view.meta.sections.includes(sectionAlias))
.sort((a, b) => b.meta.weight - a.meta.weight)
)
) ?? of([])
,
(views) => {
this._views = views;
}
);
}
}
private _observeActiveView() {
this._activeViewSubscription?.unsubscribe();
this._activeViewSubscription = this._sectionContext?.activeView.subscribe((view) => {
this._activeView = view;
});
}
disconnectedCallback(): void {
super.disconnectedCallback();
this._viewsSubscription?.unsubscribe();
this._activeViewSubscription?.unsubscribe();
if(this._sectionContext) {
this.observe(this._sectionContext?.activeViewPathname, (pathname) => {
this._activeViewPathname = pathname;
}, 'activeViewPathnameObserver');
}
}
render() {
@@ -113,7 +105,7 @@ export class UmbSectionViewsElement extends UmbLitElement {
<uui-tab
.label="${view.meta.label || view.name}"
href="${this._routerFolder}/view/${view.meta.pathname}"
?active="${this._activeView?.meta?.pathname.includes(view.meta.pathname)}">
?active="${this._activeViewPathname?.includes(view.meta.pathname)}">
<uui-icon slot="icon" name=${view.meta.icon}></uui-icon>
${view.meta.label || view.name}
</uui-tab>

View File

@@ -1,47 +1,55 @@
import { BehaviorSubject } from 'rxjs';
import type { Entity, ManifestSection, ManifestSectionView, ManifestTree } from '@umbraco-cms/models';
import { UniqueObjectBehaviorSubject } from '@umbraco-cms/observable-api';
import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models';
import { ObjectState, StringState } from '@umbraco-cms/observable-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
export class UmbSectionContext {
#manifest;
public readonly manifest;
export type ActiveTreeItemType = Entity | undefined;
// TODO: what is the best context to put this in?
export class UmbSectionContext {
#manifestAlias = new StringState<string | undefined>(undefined);
#manifestPathname = new StringState<string | undefined>(undefined);
#manifestLabel = new StringState<string | undefined>(undefined);
public readonly alias = this.#manifestAlias.asObservable();
public readonly pathname = this.#manifestPathname.asObservable();
public readonly label = this.#manifestLabel.asObservable();
/*
This was not used anywhere
private _activeTree = new BehaviorSubject<ManifestTree | undefined>(undefined);
public readonly activeTree = this._activeTree.asObservable();
*/
// TODO: what is the best context to put this in?
private _activeTreeItem = new UniqueObjectBehaviorSubject<Entity | undefined>(undefined);
public readonly activeTreeItem = this._activeTreeItem.asObservable();
#activeTreeItem = new ObjectState<ActiveTreeItemType | undefined>(undefined);
public readonly activeTreeItem = this.#activeTreeItem.asObservable();
// TODO: what is the best context to put this in?
private _activeView = new BehaviorSubject<ManifestSectionView | undefined>(undefined);
public readonly activeView = this._activeView.asObservable();
#activeViewPathname = new StringState(undefined);
public readonly activeViewPathname = this.#activeViewPathname.asObservable();
constructor(sectionManifest: ManifestSection) {
this.#manifest = new BehaviorSubject<ManifestSection>(sectionManifest);
this.manifest = this.#manifest.asObservable();
constructor(manifest: ManifestSection) {
this.setManifest(manifest);
}
public setManifest(data: ManifestSection) {
this.#manifest.next({ ...data });
}
public getData() {
return this.#manifest.getValue();
public setManifest(manifest?: ManifestSection) {
this.#manifestAlias.next(manifest?.alias);
this.#manifestPathname.next(manifest?.meta?.pathname);
this.#manifestLabel.next(manifest ? (manifest.meta?.label || manifest.name) : undefined);
}
/*
This was not used anywhere
public setActiveTree(tree: ManifestTree) {
this._activeTree.next(tree);
}
*/
public setActiveTreeItem(item: Entity) {
this._activeTreeItem.next(item);
public setActiveTreeItem(item?: ActiveTreeItemType) {
this.#activeTreeItem.next(item);
}
public setActiveView(view: ManifestSectionView) {
this._activeView.next(view);
public setActiveView(view?: ManifestSectionView) {
this.#activeViewPathname.next(view?.meta.pathname);
}
}

View File

@@ -44,6 +44,7 @@ export class UmbSectionElement extends UmbLitElement {
private _views?: Array<ManifestSectionView>;
private _sectionContext?: UmbSectionContext;
private _sectionAlias?: string;
constructor() {
super();
@@ -53,15 +54,15 @@ export class UmbSectionElement extends UmbLitElement {
// TODO: currently they don't corporate, as they overwrite each other...
this._observeMenuItems();
this._observeViews();
this._observeSection();
});
}
private _observeMenuItems() {
if (!this._sectionContext) return;
this.observe(this._sectionContext?.manifest, (section) => {
this._observeSidebarMenuItem(section?.alias);
this.observe(this._sectionContext?.alias, (alias) => {
this._observeSidebarMenuItem(alias);
});
this.observe(umbExtensionsRegistry.extensionsOfType('workspace'), (workspaceExtensions) => {
@@ -88,6 +89,8 @@ export class UmbSectionElement extends UmbLitElement {
}
private _createMenuRoutes() {
console.log("_createMenuRoutes")
// TODO: find a way to make this reuseable across:
const workspaceRoutes = this._workspaces?.map((workspace: ManifestWorkspace) => {
return [
@@ -138,30 +141,27 @@ export class UmbSectionElement extends UmbLitElement {
];
}
private _observeViews() {
private _observeSection() {
if (!this._sectionContext) return;
this.observe(
this._sectionContext.manifest.pipe(
switchMap((section) => {
if (!section) return EMPTY;
this._sectionContext.alias, (alias) => {
this._sectionAlias = alias;
this._observeViews();
}
);
}
return (
umbExtensionsRegistry
?.extensionsOfType('sectionView')
.pipe(
map((views) =>
views
.filter((view) => view.meta.sections.includes(section.alias))
.sort((a, b) => b.meta.weight - a.meta.weight)
)
) ?? of([])
);
})
),
(views) => {
if (views.length > 0) {
this._views = views;
private _observeViews() {
this.observe(umbExtensionsRegistry?.extensionsOfType('sectionView'), (views) => {
const sectionViews = views.filter((view) => {
return this._sectionAlias ? view.meta.sections.includes(this._sectionAlias) : false
}).sort((a, b) => b.meta.weight - a.meta.weight);
if(sectionViews.length > 0) {
this._views = sectionViews;
this._createViewRoutes();
}
}
@@ -169,6 +169,9 @@ export class UmbSectionElement extends UmbLitElement {
}
private _createViewRoutes() {
console.log("_createViewRoutes")
this._routes =
this._views?.map((view) => {
return {

View File

@@ -1,25 +0,0 @@
import { Observable, ReplaySubject } from 'rxjs';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbContextToken } from '@umbraco-cms/context-api';
// TODO: maybe this should be named something else than store?
export class UmbSectionStore {
private _currentAlias: ReplaySubject<string> = new ReplaySubject(1);
public readonly currentAlias: Observable<string> = this._currentAlias.asObservable();
public getAllowed() {
// TODO: implemented allowed filtering
/*
const { data } = await getUserSections({});
this._allowedSection = data.sections;
*/
return umbExtensionsRegistry.extensionsOfType('section');
}
public setCurrent(alias: string) {
this._currentAlias.next(alias);
}
}
export const UMB_SECTION_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbSectionStore>(UmbSectionStore.name);

View File

@@ -1,5 +1,5 @@
import { customElement, property, state } from 'lit/decorators.js';
import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context';
import { ActiveTreeItemType, UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context';
import {
UmbTreeContextMenuPageService,
UMB_TREE_CONTEXT_MENU_PAGE_SERVICE_CONTEXT_TOKEN,
@@ -8,7 +8,7 @@ import {
UmbTreeContextMenuService,
UMB_TREE_CONTEXT_MENU_SERVICE_CONTEXT_TOKEN,
} from '../context-menu/tree-context-menu.service';
import type { Entity, ManifestTreeItemAction, ManifestTree } from '@umbraco-cms/models';
import type { ManifestTreeItemAction } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
export type ActionPageEntity = {
@@ -24,8 +24,7 @@ export default class UmbTreeItemActionElement extends UmbLitElement {
@state()
protected _entity: ActionPageEntity = { name: '', key: '' };
protected _activeTree?: ManifestTree;
protected _activeTreeItem?: Entity;
protected _activeTreeItem?: ActiveTreeItemType;
protected _sectionContext?: UmbSectionContext;
protected _treeContextMenuService?: UmbTreeContextMenuService;
@@ -36,7 +35,6 @@ export default class UmbTreeItemActionElement extends UmbLitElement {
this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionContext) => {
this._sectionContext = sectionContext;
this._observeActiveTree();
this._observeActiveTreeItem();
});
@@ -58,14 +56,6 @@ export default class UmbTreeItemActionElement extends UmbLitElement {
});
}
private _observeActiveTree() {
if (!this._sectionContext) return;
this.observe(this._sectionContext.activeTree, (tree) => {
this._activeTree = tree;
});
}
private _observeActiveTreeItem() {
if (!this._sectionContext) return;

View File

@@ -3,7 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
import { map } from 'rxjs';
import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context';
import type { Entity, ManifestTreeItemAction, ManifestTree } from '@umbraco-cms/models';
import type { Entity, ManifestTreeItemAction } from '@umbraco-cms/models';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry';
import { UmbLitElement } from '@umbraco-cms/element';
@@ -30,9 +30,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement {
@state()
private _actions?: Array<ManifestTreeItemAction>;
@state()
private _activeTree?: ManifestTree;
@state()
private _activeTreeItem?: Entity;
@@ -43,7 +40,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement {
this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionContext) => {
this._sectionContext = sectionContext;
this._observeActiveTree();
this._observeActiveTreeItem();
this._observeTreeItemActions();
});
@@ -62,14 +58,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement {
);
}
private _observeActiveTree() {
if (!this._sectionContext) return;
this.observe(this._sectionContext.activeTree, (tree) => {
this._activeTree = tree || undefined;
});
}
private _observeActiveTreeItem() {
if (!this._sectionContext) return;

View File

@@ -4,7 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js';
import UmbTreeItemActionElement, { ActionPageEntity } from '../action/tree-item-action.element';
import { UmbTreeContextMenuService } from './tree-context-menu.service';
import { UmbLitElement } from '@umbraco-cms/element';
import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api';
import { DeepState } from '@umbraco-cms/observable-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
// TODO: Refactor this, its not a service and the data should be handled by a context api.
@@ -15,7 +15,7 @@ export class UmbTreeContextMenuPageService extends UmbLitElement {
@property({ type: Object })
public actionEntity: ActionPageEntity = { key: '', name: '' };
#entity = new UniqueBehaviorSubject({ key: '', name: '' } as ActionPageEntity);
#entity = new DeepState({ key: '', name: '' } as ActionPageEntity);
public readonly entity = this.#entity.asObservable();
@state()

View File

@@ -116,8 +116,8 @@ export class UmbTreeItem extends UmbLitElement {
private _observeSection() {
if (!this._sectionContext) return;
this.observe(this._sectionContext?.manifest, (section) => {
this._href = this._constructPath(section?.meta.pathname || '', this.entityType, this.key);
this.observe(this._sectionContext?.pathname, (pathname) => {
this._href = this._constructPath(pathname || '', this.entityType, this.key);
});
}
@@ -184,7 +184,8 @@ export class UmbTreeItem extends UmbLitElement {
private _openActions() {
if (!this._treeContext || !this._sectionContext) return;
this._sectionContext?.setActiveTree(this._treeContext?.tree);
// This is out-commented as it was not used. only kept if someone need this later:
//this._sectionContext?.setActiveTree(this._treeContext?.tree);
this._sectionContext?.setActiveTreeItem({
key: this.key,

View File

@@ -1,6 +1,6 @@
import type { Observable } from 'rxjs';
import type { ManifestTree } from '@umbraco-cms/models';
import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api';
import { DeepState } from '@umbraco-cms/observable-api';
export interface UmbTreeContext {
tree: ManifestTree;
@@ -14,10 +14,10 @@ export interface UmbTreeContext {
export class UmbTreeContextBase implements UmbTreeContext {
public tree: ManifestTree;
#selectable = new UniqueBehaviorSubject(false);
#selectable = new DeepState(false);
public readonly selectable = this.#selectable.asObservable();
#selection = new UniqueBehaviorSubject(<Array<string>>[]);
#selection = new DeepState(<Array<string>>[]);
public readonly selection = this.#selection.asObservable();
constructor(tree: ManifestTree) {

View File

@@ -1,7 +1,7 @@
import { UmbWorkspaceContentContext } from '../workspace/workspace-content/workspace-content.context';
import type { DataTypeDetails } from '@umbraco-cms/models';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { createObservablePart, UniqueObjectBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ObjectState } from '@umbraco-cms/observable-api';
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api';
// If we get this from the server then we can consider using TypeScripts Partial<> around the model from the Management-API.
@@ -16,7 +16,7 @@ export type WorkspacePropertyData<ValueType> = {
export class UmbWorkspacePropertyContext<ValueType = unknown> {
private _providerController: UmbContextProviderController;
private _data = new UniqueObjectBehaviorSubject<WorkspacePropertyData<ValueType>>({});
private _data = new ObjectState<WorkspacePropertyData<ValueType>>({});
public readonly alias = createObservablePart(this._data, (data) => data.alias);
public readonly label = createObservablePart(this._data, (data) => data.label);
@@ -45,7 +45,7 @@ export class UmbWorkspacePropertyContext<ValueType = unknown> {
this._data.update({ description: description });
}
public setValue(value: WorkspacePropertyData<ValueType>['value']) {
// Note: Do not try to compare new / old value, as it can of any type. We trust the UniqueBehaviorSubject in doing such.
// Note: Do not try to compare new / old value, as it can of any type. We trust the ObjectState in doing such.
this._data.update({ value: value });

View File

@@ -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 { UniqueBehaviorSubject, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api';
import { DeepState, 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 UniqueBehaviorSubject<ContentTypeType>(defaultData);
this._data = new DeepState<ContentTypeType>(defaultData);
this.data = this._data.asObservable();
this.name = createObservablePart(this._data, (data) => data.name);

View File

@@ -1,9 +1,9 @@
import { UmbContextProviderController } from '@umbraco-cms/context-api';
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api';
import { DeepState } from '@umbraco-cms/observable-api';
export class UmbPropertyActionMenuContext {
#isOpen = new UniqueBehaviorSubject(false);
#isOpen = new DeepState(false);
public readonly isOpen = this.#isOpen.asObservable();
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,6 +1,6 @@
import type { DictionaryDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { EntityTreeItem } from '@umbraco-cms/backend-api';
@@ -19,7 +19,7 @@ export class UmbDictionaryDetailStore extends UmbStoreBase {
// TODO: use the right type:
#data = new UniqueArrayBehaviorSubject<EntityTreeItem>([], (x) => x.key);
#data = new ArrayState<EntityTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
@@ -34,7 +34,7 @@ export class UmbDictionaryDetailStore extends UmbStoreBase {
*/
getByKey(key: string) {
// TODO: use backend cli when available.
fetch(`/umbraco/management/api/v1/document/dictionary/${key}`)
fetch(`/umbraco/management/api/v1/dictionary/details/${key}`)
.then((res) => res.json())
.then((data) => {
this.#data.append(data);

View File

@@ -1,7 +1,7 @@
import { DictionaryResource, DocumentTreeItem } from '@umbraco-cms/backend-api';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,7 +18,7 @@ export const UMB_DICTIONARY_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDi
export class UmbDictionaryTreeStore extends UmbStoreBase {
#data = new UniqueArrayBehaviorSubject<DocumentTreeItem>([], (x) => x.key);
#data = new ArrayState<DocumentTreeItem>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {

View File

@@ -1,5 +1,5 @@
import { UmbContextToken } from '@umbraco-cms/context-api';
import { createObservablePart, UniqueBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, DeepState } from '@umbraco-cms/observable-api';
export type UmbModelType = 'dialog' | 'sidebar';
@@ -10,7 +10,7 @@ export type UmbCurrentUserHistoryItem = {
};
export class UmbCurrentUserHistoryStore {
#history = new UniqueBehaviorSubject(<Array<UmbCurrentUserHistoryItem>>[]);
#history = new DeepState(<Array<UmbCurrentUserHistoryItem>>[]);
public readonly history = this.#history.asObservable();
public readonly latestHistory = createObservablePart(this.#history, (historyItems) => historyItems.slice(-10));

View File

@@ -1,12 +1,12 @@
import { BehaviorSubject, Observable } from 'rxjs';
import { umbUsersData } from '../../../core/mocks/data/users.data';
import type { UserDetails } from '@umbraco-cms/models';
import { umbracoPath } from '@umbraco-cms/utils';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ObjectState } from '@umbraco-cms/observable-api';
export class UmbCurrentUserStore {
private _currentUser = new BehaviorSubject<UserDetails>(umbUsersData.getAll()[0]); //TODO: Temp solution to set the first user as the current logged in user
public readonly currentUser: Observable<UserDetails> = this._currentUser.asObservable();
private _currentUser = new ObjectState<UserDetails | undefined>(undefined);
public readonly currentUser = this._currentUser.asObservable();
/**
* logs out the user
@@ -24,7 +24,8 @@ export class UmbCurrentUserStore {
public get isAdmin(): boolean {
//TODO: Find a way to figure out if current user is in the admin group
const adminUserGroupKey = 'c630d49e-4e7b-42ea-b2bc-edc0edacb6b1';
return this._currentUser.getValue()?.userGroups.includes(adminUserGroupKey);
const currentUser = this._currentUser.getValue();
return currentUser ? currentUser.userGroups.includes(adminUserGroupKey) : false;
}
}

View File

@@ -1,7 +1,7 @@
import type { UserGroupDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
// TODO: get rid of this type addition & { ... }:
@@ -18,7 +18,7 @@ export const UMB_USER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbUserGro
export class UmbUserGroupStore extends UmbStoreBase {
#groups = new UniqueArrayBehaviorSubject<UmbUserGroupStoreItemType>([], x => x.key);
#groups = new ArrayState<UmbUserGroupStoreItemType>([], x => x.key);
public groups = this.#groups.asObservable();

View File

@@ -12,7 +12,7 @@ import type { ManifestWorkspace, UserDetails } from '@umbraco-cms/models';
import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from 'src/backoffice/users/users/user.store';
import { createExtensionElement } from '@umbraco-cms/extensions-api';
import { UmbLitElement } from '@umbraco-cms/element';
import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api';
import { DeepState } from '@umbraco-cms/observable-api';
@customElement('umb-section-view-users')
export class UmbSectionViewUsersElement extends UmbLitElement {
@@ -33,13 +33,13 @@ export class UmbSectionViewUsersElement extends UmbLitElement {
// TODO: This must be turned into context api: Maybe its a Collection View (SectionView Collection View)?
private _userStore?: UmbUserStore;
#selection = new UniqueBehaviorSubject(<Array<string>>[]);
#selection = new DeepState(<Array<string>>[]);
public readonly selection = this.#selection.asObservable();
#users = new UniqueBehaviorSubject(<Array<UserDetails>>[]);
#users = new DeepState(<Array<UserDetails>>[]);
public readonly users = this.#users.asObservable();
#search = new UniqueBehaviorSubject('');
#search = new DeepState('');
public readonly search = this.#search.asObservable();
constructor() {

View File

@@ -1,6 +1,5 @@
import { BehaviorSubject } from 'rxjs';
import type { UserDetails } from '@umbraco-cms/models';
import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api';
import { createObservablePart, ArrayState, NumberState } from '@umbraco-cms/observable-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
@@ -18,10 +17,10 @@ export const UMB_USER_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbUserStore>('U
export class UmbUserStore extends UmbStoreBase {
#users = new UniqueArrayBehaviorSubject<UserDetails>([], x => x.key);
#users = new ArrayState<UserDetails>([], x => x.key);
public users = this.#users.asObservable();
#totalUsers = new BehaviorSubject(0);
#totalUsers = new NumberState(0);
public readonly totalUsers = this.#totalUsers.asObservable();

View File

@@ -5,7 +5,6 @@ import './layouts/property-editor-ui-picker/modal-layout-property-editor-ui-pick
import './layouts/modal-layout-current-user.element';
import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar';
import { BehaviorSubject } from 'rxjs';
import { UmbModalChangePasswordData } from './layouts/modal-layout-change-password.element';
import type { UmbModalIconPickerData } from './layouts/icon-picker/modal-layout-icon-picker.element';
@@ -16,6 +15,7 @@ import type { UmbModalContentPickerData } from './layouts/content-picker/modal-l
import type { UmbModalPropertyEditorUIPickerData } from './layouts/property-editor-ui-picker/modal-layout-property-editor-ui-picker.element';
import { UmbModalHandler } from './modal-handler';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
export type UmbModalType = 'dialog' | 'sidebar';
@@ -27,7 +27,8 @@ export interface UmbModalOptions<UmbModalData> {
// TODO: Should this be called UmbModalContext ? as we don't have 'services' as a term.
export class UmbModalService {
#modals = new BehaviorSubject(<Array<UmbModalHandler>>[]);
#modals = new ArrayState(<Array<UmbModalHandler>>[]);
public readonly modals = this.#modals.asObservable();
/**

View File

@@ -66,7 +66,7 @@ export class UmbInstallerConsentElement extends UmbLitElement {
if (!this._installerContext) return;
this.observe(this._installerContext.settings, (settings) => {
this._telemetryLevels = settings.user?.consentLevels ?? [];
this._telemetryLevels = settings?.user?.consentLevels ?? [];
});
}

View File

@@ -1,7 +1,8 @@
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';
import { Observable } from 'rxjs';
import { Install, InstallResource, InstallSettings, ProblemDetails, TelemetryLevel } from '@umbraco-cms/backend-api';
import { tryExecute } from '@umbraco-cms/resources';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ObjectState, NumberState } from '@umbraco-cms/observable-api';
/**
* Context API for the installer
@@ -9,20 +10,20 @@ import { UmbContextToken } from '@umbraco-cms/context-api';
* @class UmbInstallerContext
*/
export class UmbInstallerContext {
private _data = new BehaviorSubject<Install>({
private _data = new ObjectState<Install>({
user: { name: '', email: '', password: '', subscribeToNewsletter: false },
database: { id: '', providerName: '' },
telemetryLevel: TelemetryLevel.BASIC,
});
public readonly data = this._data.asObservable();
private _currentStep = new BehaviorSubject<number>(1);
private _currentStep = new NumberState<number>(1);
public readonly currentStep = this._currentStep.asObservable();
private _settings = new ReplaySubject<InstallSettings>();
private _settings = new ObjectState<InstallSettings | undefined>(undefined);
public readonly settings = this._settings.asObservable();
private _installStatus = new ReplaySubject<ProblemDetails | null>(1);
private _installStatus = new ObjectState<ProblemDetails | null>(null);
public readonly installStatus = this._installStatus.asObservable();
constructor() {

View File

@@ -13,7 +13,7 @@ Generally a Store will be holding one or more RxJS Subjects, each Subject is mad
````
class MyProductStore {
protected _products = new UniqueBehaviorSubject(<Array<T>>[]);
protected _products = new ArrayState(<Array<T>>[]);
public readonly products = this._items.asObservable();
protected host: UmbControllerHostInterface;