Merge branch 'main' into manual-json-schema

This commit is contained in:
Jacob Overgaard
2023-04-20 13:44:59 +02:00
131 changed files with 1832 additions and 1481 deletions

View File

@@ -44,6 +44,7 @@
"local-rules/enforce-element-suffix-on-element-class-name": "error", "local-rules/enforce-element-suffix-on-element-class-name": "error",
"local-rules/prefer-umbraco-cms-imports": "error", "local-rules/prefer-umbraco-cms-imports": "error",
"local-rules/no-external-imports": "error", "local-rules/no-external-imports": "error",
"local-rules/umb-class-prefix": "error",
"@typescript-eslint/no-non-null-assertion": "off" "@typescript-eslint/no-non-null-assertion": "off"
}, },
"settings": { "settings": {

View File

@@ -205,4 +205,31 @@ module.exports = {
}; };
}, },
}, },
/** @type {import('eslint').Rule.RuleModule} */
'umb-class-prefix': {
meta: {
type: 'problem',
docs: {
description: 'Ensure that all class declarations are prefixed with "Umb"',
category: 'Best Practices',
recommended: true,
},
schema: [],
},
create: function (context) {
function checkClassName(node) {
if (node.id && node.id.name && !node.id.name.startsWith('Umb')) {
context.report({
node: node.id,
message: 'Class declaration should be prefixed with "Umb"',
});
}
}
return {
ClassDeclaration: checkClassName,
};
},
},
}; };

View File

@@ -5,7 +5,7 @@ import { UmbContextRequestEventImplementation, umbContextRequestEventType } from
const testContextAlias = 'my-test-context'; const testContextAlias = 'my-test-context';
class MyClass { class UmbTestContextConsumerClass {
prop = 'value from provider'; prop = 'value from provider';
} }
@@ -39,16 +39,20 @@ describe('UmbContextConsumer', () => {
}); });
it('works with UmbContextProvider', (done) => { it('works with UmbContextProvider', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new MyClass()); const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected(); provider.hostConnected();
const element = document.createElement('div'); const element = document.createElement('div');
document.body.appendChild(element); document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, testContextAlias, (_instance: MyClass) => { const localConsumer = new UmbContextConsumer(
expect(_instance.prop).to.eq('value from provider'); element,
done(); testContextAlias,
}); (_instance: UmbTestContextConsumerClass) => {
expect(_instance.prop).to.eq('value from provider');
done();
}
);
localConsumer.hostConnected(); localConsumer.hostConnected();
provider.hostDisconnected(); provider.hostDisconnected();

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-case-declarations */
/** /**
* Change the collection of Contexts into a simplified array of data * Change the collection of Contexts into a simplified array of data
* *
@@ -39,10 +40,36 @@ function contextItemData(contextInstance:any):DebugContextItemData {
} }
const value = contextInstance[key]; const value = contextInstance[key];
if (typeof value === 'string' || typeof value === 'boolean') { const valueType = typeof value;
props.push({ key: key, value: value, type: typeof value });
} else { switch (valueType) {
props.push({ key: key, type: typeof value }); case 'string':
case 'boolean':
case 'number':
props.push({ key: key, value: value, type: typeof value });
break;
case 'object':
// Check if the object is an observable (by checking if it has a subscribe method/function)
const isSubscribeLike = 'subscribe' in value && typeof value['subscribe'] === 'function';
const isWebComponent = value instanceof HTMLElement;
let valueToDisplay = "Complex Object";
if(isWebComponent){
const tagName = value.tagName.toLowerCase();
valueToDisplay = `Web Component <${tagName}>`;
} else if(isSubscribeLike){
valueToDisplay = "Subscribable";
}
props.push({ key: key, type: typeof value, value: valueToDisplay });
break;
default:
props.push({ key: key, type: typeof value });
break;
} }
} }

View File

@@ -3,21 +3,21 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbContextConsumer } from '../consume/context-consumer'; import { UmbContextConsumer } from '../consume/context-consumer';
import { UmbContextProviderController } from './context-provider.controller'; import { UmbContextProviderController } from './context-provider.controller';
class MyClass { class UmbTestContextProviderControllerClass {
prop = 'value from provider'; prop = 'value from provider';
} }
class ControllerHostElement extends UmbLitElement {} class UmbTestControllerHostElement extends UmbLitElement {}
const controllerHostElement = defineCE(ControllerHostElement); const controllerHostElement = defineCE(UmbTestControllerHostElement);
describe('UmbContextProviderController', () => { describe('UmbContextProviderController', () => {
let instance: MyClass; let instance: UmbTestContextProviderControllerClass;
let provider: UmbContextProviderController; let provider: UmbContextProviderController;
let element: UmbLitElement; let element: UmbLitElement;
beforeEach(async () => { beforeEach(async () => {
element = await fixture(`<${controllerHostElement}></${controllerHostElement}>`); element = await fixture(`<${controllerHostElement}></${controllerHostElement}>`);
instance = new MyClass(); instance = new UmbTestContextProviderControllerClass();
provider = new UmbContextProviderController(element, 'my-test-context', instance); provider = new UmbContextProviderController(element, 'my-test-context', instance);
}); });
@@ -39,11 +39,15 @@ describe('UmbContextProviderController', () => {
}); });
it('works with UmbContextConsumer', (done) => { it('works with UmbContextConsumer', (done) => {
const localConsumer = new UmbContextConsumer(element, 'my-test-context', (_instance: MyClass) => { const localConsumer = new UmbContextConsumer(
expect(_instance.prop).to.eq('value from provider'); element,
done(); 'my-test-context',
localConsumer.hostDisconnected(); (_instance: UmbTestContextProviderControllerClass) => {
}); expect(_instance.prop).to.eq('value from provider');
done();
localConsumer.hostDisconnected();
}
);
localConsumer.hostConnected(); localConsumer.hostConnected();
}); });

View File

@@ -3,16 +3,16 @@ import { UmbContextConsumer } from '../consume/context-consumer';
import { UmbContextRequestEventImplementation } from '../consume/context-request.event'; import { UmbContextRequestEventImplementation } from '../consume/context-request.event';
import { UmbContextProvider } from './context-provider'; import { UmbContextProvider } from './context-provider';
class MyClass { class UmbTestContextProviderClass {
prop = 'value from provider'; prop = 'value from provider';
} }
describe('UmbContextProvider', () => { describe('UmbContextProvider', () => {
let instance: MyClass; let instance: UmbTestContextProviderClass;
let provider: UmbContextProvider; let provider: UmbContextProvider;
beforeEach(() => { beforeEach(() => {
instance = new MyClass(); instance = new UmbTestContextProviderClass();
provider = new UmbContextProvider(document.body, 'my-test-context', instance); provider = new UmbContextProvider(document.body, 'my-test-context', instance);
provider.hostConnected(); provider.hostConnected();
}); });
@@ -40,10 +40,13 @@ describe('UmbContextProvider', () => {
}); });
it('handles context request events', (done) => { it('handles context request events', (done) => {
const event = new UmbContextRequestEventImplementation('my-test-context', (_instance: MyClass) => { const event = new UmbContextRequestEventImplementation(
expect(_instance.prop).to.eq('value from provider'); 'my-test-context',
done(); (_instance: UmbTestContextProviderClass) => {
}); expect(_instance.prop).to.eq('value from provider');
done();
}
);
document.body.dispatchEvent(event); document.body.dispatchEvent(event);
}); });
@@ -52,11 +55,15 @@ describe('UmbContextProvider', () => {
const element = document.createElement('div'); const element = document.createElement('div');
document.body.appendChild(element); document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, 'my-test-context', (_instance: MyClass) => { const localConsumer = new UmbContextConsumer(
expect(_instance.prop).to.eq('value from provider'); element,
done(); 'my-test-context',
localConsumer.hostDisconnected(); (_instance: UmbTestContextProviderClass) => {
}); expect(_instance.prop).to.eq('value from provider');
done();
localConsumer.hostDisconnected();
}
);
localConsumer.hostConnected(); localConsumer.hostConnected();
}); });
}); });

View File

@@ -5,13 +5,13 @@ import { UmbContextToken } from './context-token';
const testContextAlias = 'my-test-context'; const testContextAlias = 'my-test-context';
class MyClass { class UmbTestContextTokenClass {
prop = 'value from provider'; prop = 'value from provider';
} }
describe('ContextAlias', () => { describe('ContextAlias', () => {
const contextAlias = new UmbContextToken<MyClass>(testContextAlias); const contextAlias = new UmbContextToken<UmbTestContextTokenClass>(testContextAlias);
const typedProvider = new UmbContextProvider(document.body, contextAlias, new MyClass()); const typedProvider = new UmbContextProvider(document.body, contextAlias, new UmbTestContextTokenClass());
typedProvider.hostConnected(); typedProvider.hostConnected();
after(() => { after(() => {
@@ -27,7 +27,7 @@ describe('ContextAlias', () => {
document.body.appendChild(element); document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, contextAlias, (_instance) => { const localConsumer = new UmbContextConsumer(element, contextAlias, (_instance) => {
expect(_instance).to.be.instanceOf(MyClass); expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance.prop).to.eq('value from provider'); expect(_instance.prop).to.eq('value from provider');
done(); done();
}); });
@@ -39,8 +39,8 @@ describe('ContextAlias', () => {
const element = document.createElement('div'); const element = document.createElement('div');
document.body.appendChild(element); document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, testContextAlias, (_instance: MyClass) => { const localConsumer = new UmbContextConsumer(element, testContextAlias, (_instance: UmbTestContextTokenClass) => {
expect(_instance).to.be.instanceOf(MyClass); expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance.prop).to.eq('value from provider'); expect(_instance.prop).to.eq('value from provider');
done(); done();
}); });

View File

@@ -3,18 +3,18 @@ import { customElement } from 'lit/decorators.js';
import { UmbControllerHostElement, UmbControllerHostMixin } from './controller-host.mixin'; import { UmbControllerHostElement, UmbControllerHostMixin } from './controller-host.mixin';
import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
class MyClass { class UmbTestContext {
prop = 'value from provider'; prop = 'value from provider';
} }
@customElement('test-my-controller-host') @customElement('test-my-controller-host')
export class MyHostElement extends UmbControllerHostMixin(HTMLElement) {} export class UmbTestControllerHostElement extends UmbControllerHostMixin(HTMLElement) {}
describe('UmbContextProvider', () => { describe('UmbContextProvider', () => {
type NewType = UmbControllerHostElement; type NewType = UmbControllerHostElement;
let hostElement: NewType; let hostElement: NewType;
const contextInstance = new MyClass(); const contextInstance = new UmbTestContext();
beforeEach(() => { beforeEach(() => {
hostElement = document.createElement('test-my-controller-host') as UmbControllerHostElement; hostElement = document.createElement('test-my-controller-host') as UmbControllerHostElement;
@@ -35,7 +35,7 @@ describe('UmbContextProvider', () => {
describe('Unique controllers replace each other', () => { describe('Unique controllers replace each other', () => {
it('has a host property', () => { it('has a host property', () => {
const firstCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance); const firstCtrl = new UmbContextProviderController(hostElement, 'my-test-context', contextInstance);
const secondCtrl = new UmbContextProviderController(hostElement, 'my-test-context', new MyClass()); const secondCtrl = new UmbContextProviderController(hostElement, 'my-test-context', new UmbTestContext());
expect(hostElement.hasController(firstCtrl)).to.be.false; expect(hostElement.hasController(firstCtrl)).to.be.false;
expect(hostElement.hasController(secondCtrl)).to.be.true; expect(hostElement.hasController(secondCtrl)).to.be.true;

View File

@@ -5,7 +5,7 @@
* @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return. * @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. * @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. * @description - Creates a RxJS Observable from RxJS Subject.
* @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> * @example <caption>Example append new entry for a ArrayState or a part of UmbDeepState/UmbObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing.</caption>
* const entry = {id: 'myKey', value: 'myValue'}; * const entry = {id: 'myKey', value: 'myValue'};
* const newDataSet = appendToFrozenArray(mySubject.getValue(), entry, x => x.id === id); * const newDataSet = appendToFrozenArray(mySubject.getValue(), entry, x => x.id === id);
* mySubject.next(newDataSet); * mySubject.next(newDataSet);

View File

@@ -1,11 +1,11 @@
import { expect } from '@open-wc/testing'; import { expect } from '@open-wc/testing';
import { ArrayState } from './array-state'; import { UmbArrayState } from './array-state';
describe('ArrayState', () => { describe('ArrayState', () => {
type ObjectType = { key: string; another: string }; type ObjectType = { key: string; another: string };
type ArrayType = ObjectType[]; type ArrayType = ObjectType[];
let subject: ArrayState<ObjectType>; let subject: UmbArrayState<ObjectType>;
let initialData: ArrayType; let initialData: ArrayType;
beforeEach(() => { beforeEach(() => {
@@ -14,7 +14,7 @@ describe('ArrayState', () => {
{ key: '2', another: 'myValue2' }, { key: '2', another: 'myValue2' },
{ key: '3', another: 'myValue3' }, { key: '3', another: 'myValue3' },
]; ];
subject = new ArrayState(initialData, (x) => x.key); subject = new UmbArrayState(initialData, (x) => x.key);
}); });
it('replays latests, no matter the amount of subscriptions.', (done) => { it('replays latests, no matter the amount of subscriptions.', (done) => {

View File

@@ -1,17 +1,17 @@
import { DeepState } from './deep-state'; import { UmbDeepState } from './deep-state';
import { partialUpdateFrozenArray } from './partial-update-frozen-array.function'; import { partialUpdateFrozenArray } from './partial-update-frozen-array.function';
import { pushToUniqueArray } from './push-to-unique-array.function'; import { pushToUniqueArray } from './push-to-unique-array.function';
/** /**
* @export * @export
* @class ArrayState * @class UmbArrayState
* @extends {DeepState<T>} * @extends {UmbDeepState<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. * @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. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
* *
* The ArrayState 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 ArrayState<T> extends DeepState<T[]> { export class UmbArrayState<T> extends UmbDeepState<T[]> {
#getUnique?: (entry: T) => unknown; #getUnique?: (entry: T) => unknown;
#sortMethod?: (a: T, b: T) => number; #sortMethod?: (a: T, b: T) => number;
@@ -29,7 +29,7 @@ export class ArrayState<T> extends DeepState<T[]> {
* { key: 1, value: 'foo'}, * { key: 1, value: 'foo'},
* { key: 2, value: 'bar'} * { key: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data, (x) => x.key); * const myState = new UmbArrayState(data, (x) => x.key);
* myState.sortBy((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0)); * myState.sortBy((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
*/ */
sortBy(sortMethod?: (a: T, b: T) => number) { sortBy(sortMethod?: (a: T, b: T) => number) {
@@ -48,14 +48,14 @@ export class ArrayState<T> extends DeepState<T[]> {
/** /**
* @method remove * @method remove
* @param {unknown[]} uniques - The unique values to remove. * @param {unknown[]} uniques - The unique values to remove.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Remove some new data of this Subject. * @description - Remove some new data of this Subject.
* @example <caption>Example remove entry with id '1' and '2'</caption> * @example <caption>Example remove entry with id '1' and '2'</caption>
* const data = [ * const data = [
* { id: 1, value: 'foo'}, * { id: 1, value: 'foo'},
* { id: 2, value: 'bar'} * { id: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data, (x) => x.id); * const myState = new UmbArrayState(data, (x) => x.id);
* myState.remove([1, 2]); * myState.remove([1, 2]);
*/ */
remove(uniques: unknown[]) { remove(uniques: unknown[]) {
@@ -77,14 +77,14 @@ export class ArrayState<T> extends DeepState<T[]> {
/** /**
* @method removeOne * @method removeOne
* @param {unknown} unique - The unique value to remove. * @param {unknown} unique - The unique value to remove.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Remove some new data of this Subject. * @description - Remove some new data of this Subject.
* @example <caption>Example remove entry with id '1'</caption> * @example <caption>Example remove entry with id '1'</caption>
* const data = [ * const data = [
* { id: 1, value: 'foo'}, * { id: 1, value: 'foo'},
* { id: 2, value: 'bar'} * { id: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data, (x) => x.id); * const myState = new UmbArrayState(data, (x) => x.id);
* myState.removeOne(1); * myState.removeOne(1);
*/ */
removeOne(unique: unknown) { removeOne(unique: unknown) {
@@ -104,7 +104,7 @@ export class ArrayState<T> extends DeepState<T[]> {
/** /**
* @method filter * @method filter
* @param {unknown} filterMethod - The unique value to remove. * @param {unknown} filterMethod - The unique value to remove.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Remove some new data of this Subject. * @description - Remove some new data of this Subject.
* @example <caption>Example remove entry with key '1'</caption> * @example <caption>Example remove entry with key '1'</caption>
* const data = [ * const data = [
@@ -112,7 +112,7 @@ export class ArrayState<T> extends DeepState<T[]> {
* { key: 2, value: 'bar'}, * { key: 2, value: 'bar'},
* { key: 3, value: 'poo'} * { key: 3, value: 'poo'}
* ]; * ];
* const myState = new ArrayState(data, (x) => x.key); * const myState = new UmbArrayState(data, (x) => x.key);
* myState.filter((entry) => entry.key !== 1); * myState.filter((entry) => entry.key !== 1);
* *
* Result: * Result:
@@ -130,14 +130,14 @@ export class ArrayState<T> extends DeepState<T[]> {
/** /**
* @method appendOne * @method appendOne
* @param {T} entry - new data to be added in this Subject. * @param {T} entry - new data to be added in this Subject.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Append some new data to this Subject. * @description - Append some new data to this Subject.
* @example <caption>Example append some data.</caption> * @example <caption>Example append some data.</caption>
* const data = [ * const data = [
* { key: 1, value: 'foo'}, * { key: 1, value: 'foo'},
* { key: 2, value: 'bar'} * { key: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data); * const myState = new UmbArrayState(data);
* myState.append({ key: 1, value: 'replaced-foo'}); * myState.append({ key: 1, value: 'replaced-foo'});
*/ */
appendOne(entry: T) { appendOne(entry: T) {
@@ -154,14 +154,14 @@ export class ArrayState<T> extends DeepState<T[]> {
/** /**
* @method append * @method append
* @param {T[]} entries - A array of new data to be added in this Subject. * @param {T[]} entries - A array of new data to be added in this Subject.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Append some new data to this Subject, if it compares to existing data it will replace it. * @description - Append some new data to this Subject, if it compares to existing data it will replace it.
* @example <caption>Example append some data.</caption> * @example <caption>Example append some data.</caption>
* const data = [ * const data = [
* { key: 1, value: 'foo'}, * { key: 1, value: 'foo'},
* { key: 2, value: 'bar'} * { key: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data); * const myState = new UmbArrayState(data);
* myState.append([ * myState.append([
* { key: 1, value: 'replaced-foo'}, * { key: 1, value: 'replaced-foo'},
* { key: 3, value: 'another-bla'} * { key: 3, value: 'another-bla'}
@@ -184,14 +184,14 @@ export class ArrayState<T> extends DeepState<T[]> {
* @method updateOne * @method updateOne
* @param {unknown} unique - Unique value to find entry to update. * @param {unknown} unique - Unique value to find entry to update.
* @param {Partial<T>} entry - new data to be added in this Subject. * @param {Partial<T>} entry - new data to be added in this Subject.
* @return {ArrayState<T>} Reference to it self. * @return {UmbArrayState<T>} Reference to it self.
* @description - Update a item with some new data, requires the ArrayState to be constructed with a getUnique method. * @description - Update a item with some new data, requires the ArrayState to be constructed with a getUnique method.
* @example <caption>Example append some data.</caption> * @example <caption>Example append some data.</caption>
* const data = [ * const data = [
* { key: 1, value: 'foo'}, * { key: 1, value: 'foo'},
* { key: 2, value: 'bar'} * { key: 2, value: 'bar'}
* ]; * ];
* const myState = new ArrayState(data, (x) => x.key); * const myState = new UmbArrayState(data, (x) => x.key);
* myState.updateOne(2, {value: 'updated-bar'}); * myState.updateOne(2, {value: 'updated-bar'});
*/ */
updateOne(unique: unknown, entry: Partial<T>) { updateOne(unique: unknown, entry: Partial<T>) {

View File

@@ -2,11 +2,11 @@ import { BehaviorSubject } from 'rxjs';
/** /**
* @export * @export
* @class BasicState * @class UmbBasicState
* @extends {BehaviorSubject<T>} * @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. * @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> { export class UmbBasicState<T> extends BehaviorSubject<T> {
constructor(initialData: T) { constructor(initialData: T) {
super(initialData); super(initialData);
} }

View File

@@ -1,12 +1,12 @@
import { BasicState } from './basic-state'; import { UmbBasicState } from './basic-state';
/** /**
* @export * @export
* @class BooleanState * @class UmbBooleanState
* @extends {BehaviorSubject<T>} * @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. * @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 BooleanState<T> extends BasicState<T | boolean> { export class UmbBooleanState<T> extends UmbBasicState<T | boolean> {
constructor(initialData: T | boolean) { constructor(initialData: T | boolean) {
super(initialData); super(initialData);
} }

View File

@@ -1,16 +1,16 @@
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
interface ClassStateData { interface UmbClassStateData {
equal(otherClass: ClassStateData): boolean; equal(otherClass: UmbClassStateData): boolean;
} }
/** /**
* @export * @export
* @class ClassState * @class UmbClassState
* @extends {BehaviorSubject<T>} * @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject which can hold class instance which has a equal method to compare in coming instances for changes. * @description - A RxJS BehaviorSubject which can hold class instance which has a equal method to compare in coming instances for changes.
*/ */
export class ClassState<T extends ClassStateData | undefined | null> extends BehaviorSubject<T> { export class UmbClassState<T extends UmbClassStateData | undefined | null> extends BehaviorSubject<T> {
constructor(initialData: T) { constructor(initialData: T) {
super(initialData); super(initialData);
} }

View File

@@ -1,30 +1,27 @@
import { expect } from '@open-wc/testing'; import { expect } from '@open-wc/testing';
import { DeepState } from './deep-state'; import { UmbDeepState } from './deep-state';
describe('DeepState', () => { describe('UmbDeepState', () => {
type ObjectType = { key: string; another: string };
type ObjectType = {key: string, another: string}; let subject: UmbDeepState<ObjectType>;
let subject: DeepState<ObjectType>;
let initialData: ObjectType; let initialData: ObjectType;
beforeEach(() => { beforeEach(() => {
initialData = {key: 'some', another: 'myValue'}; initialData = { key: 'some', another: 'myValue' };
subject = new DeepState(initialData); subject = new UmbDeepState(initialData);
}); });
it('getValue gives the initial data', () => { it('getValue gives the initial data', () => {
expect(subject.value.another).to.be.equal(initialData.another); expect(subject.value.another).to.be.equal(initialData.another);
}); });
it('update via next', () => { it('update via next', () => {
subject.next({key: 'some', another: 'myNewValue'}); subject.next({ key: 'some', another: 'myNewValue' });
expect(subject.value.another).to.be.equal('myNewValue'); expect(subject.value.another).to.be.equal('myNewValue');
}); });
it('replays latests, no matter the amount of subscriptions.', (done) => { it('replays latests, no matter the amount of subscriptions.', (done) => {
const observer = subject.asObservable(); const observer = subject.asObservable();
observer.subscribe((value) => { observer.subscribe((value) => {
expect(value).to.be.equal(initialData); expect(value).to.be.equal(initialData);
@@ -33,28 +30,24 @@ describe('DeepState', () => {
expect(value).to.be.equal(initialData); expect(value).to.be.equal(initialData);
done(); done();
}); });
}); });
it('use gObservablePart, updates on its specific change.', (done) => { it('use gObservablePart, updates on its specific change.', (done) => {
let amountOfCallbacks = 0; let amountOfCallbacks = 0;
const subObserver = subject.getObservablePart(data => data.another); const subObserver = subject.getObservablePart((data) => data.another);
subObserver.subscribe((value) => { subObserver.subscribe((value) => {
amountOfCallbacks++; amountOfCallbacks++;
if(amountOfCallbacks === 1) { if (amountOfCallbacks === 1) {
expect(value).to.be.equal('myValue'); expect(value).to.be.equal('myValue');
} }
if(amountOfCallbacks === 2) { if (amountOfCallbacks === 2) {
expect(value).to.be.equal('myNewValue'); expect(value).to.be.equal('myNewValue');
done(); done();
} }
}); });
subject.next({key: 'change_this_first_should_not_trigger_update', another: 'myValue'}); subject.next({ key: 'change_this_first_should_not_trigger_update', another: 'myValue' });
subject.next({key: 'some', another: 'myNewValue'}); subject.next({ key: 'some', another: 'myNewValue' });
}); });
}); });

View File

@@ -7,12 +7,12 @@ import { naiveObjectComparison } from './naive-object-comparison';
/** /**
* @export * @export
* @class DeepState * @class UmbDeepState
* @extends {BehaviorSubject<T>} * @extends {BehaviorSubject<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations. * @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. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*/ */
export class DeepState<T> extends BehaviorSubject<T> { export class UmbDeepState<T> extends BehaviorSubject<T> {
constructor(initialData: T) { constructor(initialData: T) {
super(deepFreeze(initialData)); super(deepFreeze(initialData));
} }

View File

@@ -1,12 +1,12 @@
import { BasicState } from './basic-state'; import { UmbBasicState } from './basic-state';
/** /**
* @export * @export
* @class NumberState * @class UmbNumberState
* @extends {BehaviorSubject<T>} * @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. * @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> { export class UmbNumberState<T> extends UmbBasicState<T | number> {
constructor(initialData: T | number) { constructor(initialData: T | number) {
super(initialData); super(initialData);
} }

View File

@@ -1,21 +1,18 @@
import { expect } from '@open-wc/testing'; import { expect } from '@open-wc/testing';
import { ObjectState } from './object-state'; import { UmbObjectState } from './object-state';
describe('ObjectState', () => { describe('UmbObjectState', () => {
type ObjectType = { key: string; another: string };
type ObjectType = {key: string, another: string}; let subject: UmbObjectState<ObjectType>;
let subject: ObjectState<ObjectType>;
let initialData: ObjectType; let initialData: ObjectType;
beforeEach(() => { beforeEach(() => {
initialData = {key: 'some', another: 'myValue'}; initialData = { key: 'some', another: 'myValue' };
subject = new ObjectState(initialData); subject = new UmbObjectState(initialData);
}); });
it('replays latests, no matter the amount of subscriptions.', (done) => { it('replays latests, no matter the amount of subscriptions.', (done) => {
const observer = subject.asObservable(); const observer = subject.asObservable();
observer.subscribe((value) => { observer.subscribe((value) => {
expect(value).to.be.equal(initialData); expect(value).to.be.equal(initialData);
@@ -24,28 +21,24 @@ describe('ObjectState', () => {
expect(value).to.be.equal(initialData); expect(value).to.be.equal(initialData);
done(); done();
}); });
}); });
it('use getObservablePart, updates on its specific change.', (done) => { it('use getObservablePart, updates on its specific change.', (done) => {
let amountOfCallbacks = 0; let amountOfCallbacks = 0;
const subObserver = subject.getObservablePart(data => data.another); const subObserver = subject.getObservablePart((data) => data.another);
subObserver.subscribe((value) => { subObserver.subscribe((value) => {
amountOfCallbacks++; amountOfCallbacks++;
if(amountOfCallbacks === 1) { if (amountOfCallbacks === 1) {
expect(value).to.be.equal('myValue'); expect(value).to.be.equal('myValue');
} }
if(amountOfCallbacks === 2) { if (amountOfCallbacks === 2) {
expect(value).to.be.equal('myNewValue'); expect(value).to.be.equal('myNewValue');
done(); done();
} }
}); });
subject.update({key: 'change_this_first_should_not_trigger_update'}); subject.update({ key: 'change_this_first_should_not_trigger_update' });
subject.update({another: 'myNewValue'}); subject.update({ another: 'myNewValue' });
}); });
}); });

View File

@@ -1,23 +1,23 @@
import { DeepState } from './deep-state'; import { UmbDeepState } from './deep-state';
/** /**
* @export * @export
* @class ObjectState * @class UmbObjectState
* @extends {DeepState<T>} * @extends {UmbDeepState<T>}
* @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. * @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. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
* *
* The ObjectState provides methods to append data when the data is an Object. * The UmbObjectState provides methods to append data when the data is an Object.
*/ */
export class ObjectState<T> extends DeepState<T> { export class UmbObjectState<T> extends UmbDeepState<T> {
/** /**
* @method update * @method update
* @param {Partial<T>} partialData - A object containing some of the data to update in this Subject. * @param {Partial<T>} partialData - A object containing some of the data to update in this Subject.
* @description - Append some new data to this Object. * @description - Append some new data to this Object.
* @return {ObjectState<T>} Reference to it self. * @return {UmbObjectState<T>} Reference to it self.
* @example <caption>Example append some data.</caption> * @example <caption>Example append some data.</caption>
* const data = {key: 'myKey', value: 'myInitialValue'}; * const data = {key: 'myKey', value: 'myInitialValue'};
* const myState = new ObjectState(data); * const myState = new UmbObjectState(data);
* myState.update({value: 'myNewValue'}); * myState.update({value: 'myNewValue'});
*/ */
update(partialData: Partial<T>) { update(partialData: Partial<T>) {

View File

@@ -5,7 +5,7 @@
* @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return. * @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. * @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. * @description - Creates a RxJS Observable from RxJS Subject.
* @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> * @example <caption>Example append new entry for a ArrayState or a part of UmbDeepState/UmbObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing.</caption>
* const partialEntry = {value: 'myValue'}; * const partialEntry = {value: 'myValue'};
* const newDataSet = partialUpdateFrozenArray(mySubject.getValue(), partialEntry, x => x.key === 'myKey'); * const newDataSet = partialUpdateFrozenArray(mySubject.getValue(), partialEntry, x => x.key === 'myKey');
* mySubject.next(newDataSet); * mySubject.next(newDataSet);

View File

@@ -1,12 +1,12 @@
import { BasicState } from './basic-state'; import { UmbBasicState } from './basic-state';
/** /**
* @export * @export
* @class StringState * @class UmbStringState
* @extends {BasicState<T>} * @extends {UmbBasicState<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. * @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> { export class UmbStringState<T> extends UmbBasicState<T | string> {
constructor(initialData: T | string) { constructor(initialData: T | string) {
super(initialData); super(initialData);
} }

View File

@@ -1,5 +1,5 @@
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
/** /**
@@ -9,7 +9,7 @@ import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
* @description - General Tree Data Store * @description - General Tree Data Store
*/ */
export class UmbEntityTreeStore extends UmbStoreBase implements UmbTreeStore<EntityTreeItemResponseModel> { export class UmbEntityTreeStore extends UmbStoreBase implements UmbTreeStore<EntityTreeItemResponseModel> {
#data = new ArrayState<EntityTreeItemResponseModel>([], (x) => x.id); #data = new UmbArrayState<EntityTreeItemResponseModel>([], (x) => x.id);
/** /**
* Appends items to the store * Appends items to the store

View File

@@ -1,5 +1,5 @@
import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api';
import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
/** /**
@@ -9,7 +9,7 @@ import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
* @description - General Tree Data Store * @description - General Tree Data Store
*/ */
export class UmbFileSystemTreeStore extends UmbStoreBase implements UmbTreeStore<FileSystemTreeItemPresentationModel> { export class UmbFileSystemTreeStore extends UmbStoreBase implements UmbTreeStore<FileSystemTreeItemPresentationModel> {
#data = new ArrayState<FileSystemTreeItemPresentationModel>([], (x) => x.path); #data = new UmbArrayState<FileSystemTreeItemPresentationModel>([], (x) => x.path);
/** /**
* Appends items to the store * Appends items to the store

File diff suppressed because it is too large Load Diff

View File

@@ -61,8 +61,8 @@
"npm": ">=9.5 < 10" "npm": ">=9.5 < 10"
}, },
"dependencies": { "dependencies": {
"@umbraco-ui/uui": "^1.2.0-rc.2", "@umbraco-ui/uui": "^1.2.0-rc.3",
"@umbraco-ui/uui-css": "^1.2.0-rc.2", "@umbraco-ui/uui-css": "^1.2.0-rc.3",
"element-internals-polyfill": "^1.1.19", "element-internals-polyfill": "^1.1.19",
"lit": "^2.7.0", "lit": "^2.7.0",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",

View File

@@ -46,26 +46,23 @@ export class UmbAppElement extends UmbLitElement {
{ {
path: 'upgrade', path: 'upgrade',
component: () => import('./upgrader/upgrader.element'), component: () => import('./upgrader/upgrader.element'),
guards: [this._isAuthorizedGuard('/upgrade')], guards: [this.#isAuthorizedGuard('/upgrade')],
}, },
{ {
path: '**', path: '**',
component: () => import('./backoffice/backoffice.element'), component: () => import('./backoffice/backoffice.element'),
guards: [this._isAuthorizedGuard()], guards: [this.#isAuthorizedGuard()],
}, },
]; ];
private _umbIconRegistry = new UmbIconStore(); #umbIconRegistry = new UmbIconStore();
#uuiIconRegistry = new UUIIconRegistryEssential();
private _iconRegistry = new UUIIconRegistryEssential(); #runtimeLevel = RuntimeLevelModel.UNKNOWN;
private _runtimeLevel = RuntimeLevelModel.UNKNOWN;
constructor() { constructor() {
super(); super();
this.#umbIconRegistry.attach(this);
this._umbIconRegistry.attach(this); this.#uuiIconRegistry.attach(this);
this._setup();
} }
connectedCallback() { connectedCallback() {
@@ -78,8 +75,7 @@ export class UmbAppElement extends UmbLitElement {
OpenAPI.WITH_CREDENTIALS = true; OpenAPI.WITH_CREDENTIALS = true;
this.provideContext('UMBRACOBASE', OpenAPI.BASE); this.provideContext('UMBRACOBASE', OpenAPI.BASE);
this.#setInitStatus();
this._setInitStatus();
// Listen for the debug event from the <umb-debug> component // Listen for the debug event from the <umb-debug> component
this.addEventListener(umbDebugContextEventType, (event: any) => { this.addEventListener(umbDebugContextEventType, (event: any) => {
@@ -104,19 +100,14 @@ export class UmbAppElement extends UmbLitElement {
}); });
} }
private async _setup() { async #setInitStatus() {
await this._setInitStatus();
this._iconRegistry.attach(this);
}
private async _setInitStatus() {
const { data } = await tryExecuteAndNotify(this, ServerResource.getServerStatus()); const { data } = await tryExecuteAndNotify(this, ServerResource.getServerStatus());
this._runtimeLevel = data?.serverStatus ?? RuntimeLevelModel.UNKNOWN; this.#runtimeLevel = data?.serverStatus ?? RuntimeLevelModel.UNKNOWN;
this._redirect(); this.#redirect();
} }
private _redirect() { #redirect() {
switch (this._runtimeLevel) { switch (this.#runtimeLevel) {
case RuntimeLevelModel.INSTALL: case RuntimeLevelModel.INSTALL:
history.replaceState(null, '', 'install'); history.replaceState(null, '', 'install');
break; break;
@@ -137,18 +128,18 @@ export class UmbAppElement extends UmbLitElement {
} }
default: default:
throw new Error(`Unsupported runtime level: ${this._runtimeLevel}`); throw new Error(`Unsupported runtime level: ${this.#runtimeLevel}`);
} }
} }
private _isAuthorized(): boolean { #isAuthorized(): boolean {
return true; // TODO: Return true for now, until new login page is up and running return true; // TODO: Return true for now, until new login page is up and running
//return sessionStorage.getItem('is-authenticated') === 'true'; //return sessionStorage.getItem('is-authenticated') === 'true';
} }
private _isAuthorizedGuard(redirectTo?: string): Guard { #isAuthorizedGuard(redirectTo?: string): Guard {
return () => { return () => {
if (this._isAuthorized()) { if (this.#isAuthorized()) {
return true; return true;
} }

View File

@@ -1,6 +1,6 @@
import type { DocumentBlueprintDetails } from '@umbraco-cms/backoffice/models'; import type { DocumentBlueprintDetails } from '@umbraco-cms/backoffice/models';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -12,7 +12,7 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
*/ */
export class UmbDocumentBlueprintStore extends UmbStoreBase { export class UmbDocumentBlueprintStore extends UmbStoreBase {
// TODO: use the right type: // TODO: use the right type:
#data = new ArrayState<DocumentBlueprintDetails>([], (x) => x.id); #data = new UmbArrayState<DocumentBlueprintDetails>([], (x) => x.id);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host, UMB_DOCUMENT_BLUEPRINT_STORE_CONTEXT_TOKEN.toString()); super(host, UMB_DOCUMENT_BLUEPRINT_STORE_CONTEXT_TOKEN.toString());

View File

@@ -1,4 +1,4 @@
import { DocumentTypeTreeServerDataSource } from './sources/document-type.tree.server.data'; import { UmbDocumentTypeTreeServerDataSource } from './sources/document-type.tree.server.data';
import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data'; import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data';
import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store'; import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store';
import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store'; import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store';
@@ -27,7 +27,7 @@ export class UmbDocumentTypeRepository implements UmbTreeRepository<ItemType>, U
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new DocumentTypeTreeServerDataSource(this.#host); this.#treeSource = new UmbDocumentTypeTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbDocumentTypeServerDataSource(this.#host); this.#detailDataSource = new UmbDocumentTypeServerDataSource(this.#host);
this.#init = Promise.all([ this.#init = Promise.all([

View File

@@ -1,6 +1,6 @@
import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -11,7 +11,7 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
* @description - Data Store for Document Types * @description - Data Store for Document Types
*/ */
export class UmbDocumentTypeStore extends UmbStoreBase { export class UmbDocumentTypeStore extends UmbStoreBase {
#data = new ArrayState<DocumentTypeResponseModel>([], (x) => x.id); #data = new UmbArrayState<DocumentTypeResponseModel>([], (x) => x.id);
/** /**
* Creates an instance of UmbDocumentTypeStore. * Creates an instance of UmbDocumentTypeStore.

View File

@@ -6,10 +6,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the Document tree that fetches data from the server * A data source for the Document tree that fetches data from the server
* @export * @export
* @class DocumentTreeServerDataSource * @class UmbDocumentTypeTreeServerDataSource
* @implements {DocumentTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource { export class UmbDocumentTypeTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
// TODO: how do we handle trashed items? // TODO: how do we handle trashed items?
@@ -42,9 +42,9 @@ export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource {
} }
/** /**
* Creates an instance of DocumentTreeServerDataSource. * Creates an instance of UmbDocumentTypeTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTypeTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -53,7 +53,7 @@ export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTypeTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, DocumentTypeResource.getTreeDocumentTypeRoot({})); return tryExecuteAndNotify(this.#host, DocumentTypeResource.getTreeDocumentTypeRoot({}));
@@ -63,7 +63,7 @@ export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTypeTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
if (!parentId) { if (!parentId) {
@@ -83,7 +83,7 @@ export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTypeTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (ids) { if (ids) {

View File

@@ -1,7 +1,7 @@
import { UmbDocumentServerDataSource } from './sources/document.server.data'; import { UmbDocumentServerDataSource } from './sources/document.server.data';
import { UmbDocumentStore, UMB_DOCUMENT_STORE_CONTEXT_TOKEN } from './document.store'; import { UmbDocumentStore, UMB_DOCUMENT_STORE_CONTEXT_TOKEN } from './document.store';
import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store'; import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store';
import { DocumentTreeServerDataSource } from './sources/document.tree.server.data'; import { UmbDocumentTreeServerDataSource } from './sources/document.tree.server.data';
import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
@@ -32,7 +32,7 @@ export class UmbDocumentRepository implements UmbTreeRepository<ItemType>, UmbDe
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new DocumentTreeServerDataSource(this.#host); this.#treeSource = new UmbDocumentTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbDocumentServerDataSource(this.#host); this.#detailDataSource = new UmbDocumentServerDataSource(this.#host);
this.#init = Promise.all([ this.#init = Promise.all([

View File

@@ -1,6 +1,6 @@
import { DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -11,7 +11,7 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
* @description - Data Store for Template Details * @description - Data Store for Template Details
*/ */
export class UmbDocumentStore extends UmbStoreBase { export class UmbDocumentStore extends UmbStoreBase {
#data = new ArrayState<DocumentResponseModel>([], (x) => x.id); #data = new UmbArrayState<DocumentResponseModel>([], (x) => x.id);
/** /**
* Creates an instance of UmbDocumentDetailStore. * Creates an instance of UmbDocumentDetailStore.

View File

@@ -6,10 +6,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the Document tree that fetches data from the server * A data source for the Document tree that fetches data from the server
* @export * @export
* @class DocumentTreeServerDataSource * @class UmbDocumentTreeServerDataSource
* @implements {DocumentTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class DocumentTreeServerDataSource implements UmbTreeDataSource { export class UmbDocumentTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
// TODO: how do we handle trashed items? // TODO: how do we handle trashed items?
@@ -42,9 +42,9 @@ export class DocumentTreeServerDataSource implements UmbTreeDataSource {
} }
/** /**
* Creates an instance of DocumentTreeServerDataSource. * Creates an instance of UmbDocumentTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -53,7 +53,7 @@ export class DocumentTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, DocumentResource.getTreeDocumentRoot({})); return tryExecuteAndNotify(this.#host, DocumentResource.getTreeDocumentRoot({}));
@@ -63,7 +63,7 @@ export class DocumentTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
if (!parentId) { if (!parentId) {
@@ -83,7 +83,7 @@ export class DocumentTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof DocumentTreeServerDataSource * @memberof UmbDocumentTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (!ids) { if (!ids) {

View File

@@ -6,7 +6,11 @@ import { UmbVariantId } from '../../../shared/variants/variant-id.class';
import { UmbWorkspacePropertyStructureManager } from '../../../shared/components/workspace/workspace-context/workspace-structure-manager.class'; import { UmbWorkspacePropertyStructureManager } from '../../../shared/components/workspace/workspace-context/workspace-structure-manager.class';
import { UmbWorkspaceSplitViewManager } from '../../../shared/components/workspace/workspace-context/workspace-split-view-manager.class'; import { UmbWorkspaceSplitViewManager } from '../../../shared/components/workspace/workspace-context/workspace-split-view-manager.class';
import type { CreateDocumentRequestModel, DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { CreateDocumentRequestModel, DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { partialUpdateFrozenArray, ObjectState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import {
partialUpdateFrozenArray,
UmbObjectState,
UmbObserverController,
} from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
// TODO: should this context be called DocumentDraft instead of workspace? or should the draft be part of this? // TODO: should this context be called DocumentDraft instead of workspace? or should the draft be part of this?
@@ -22,12 +26,12 @@ export class UmbDocumentWorkspaceContext
* For now lets not share this publicly as it can become confusing. * For now lets not share this publicly as it can become confusing.
* TODO: Use this to compare, for variants with changes. * TODO: Use this to compare, for variants with changes.
*/ */
#document = new ObjectState<EntityType | undefined>(undefined); #document = new UmbObjectState<EntityType | undefined>(undefined);
/** /**
* The document is the current state/draft version of the document. * The document is the current state/draft version of the document.
*/ */
#draft = new ObjectState<EntityType | undefined>(undefined); #draft = new UmbObjectState<EntityType | undefined>(undefined);
readonly unique = this.#draft.getObservablePart((data) => data?.id); readonly unique = this.#draft.getObservablePart((data) => data?.id);
readonly documentTypeKey = this.#draft.getObservablePart((data) => data?.contentTypeId); readonly documentTypeKey = this.#draft.getObservablePart((data) => data?.contentTypeId);

View File

@@ -1,7 +1,7 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models'; import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models';
/** /**
@@ -11,7 +11,7 @@ import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models';
* @description - Details Data Store for Media Types * @description - Details Data Store for Media Types
*/ */
export class UmbMediaTypeStore extends UmbStoreBase { export class UmbMediaTypeStore extends UmbStoreBase {
#data = new ArrayState<MediaTypeDetails>([], (x) => x.id); #data = new UmbArrayState<MediaTypeDetails>([], (x) => x.id);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN.toString()); super(host, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN.toString());

View File

@@ -1,7 +1,7 @@
import { UmbMediaTypeTreeStore, UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './media-type.tree.store'; import { UmbMediaTypeTreeStore, UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './media-type.tree.store';
import { UmbMediaTypeDetailServerDataSource } from './sources/media-type.detail.server.data'; import { UmbMediaTypeDetailServerDataSource } from './sources/media-type.detail.server.data';
import { UmbMediaTypeStore, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN } from './media-type.detail.store'; import { UmbMediaTypeStore, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN } from './media-type.detail.store';
import { MediaTypeTreeServerDataSource } from './sources/media-type.tree.server.data'; import { UmbMediaTypeTreeServerDataSource } from './sources/media-type.tree.server.data';
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api'; import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -26,7 +26,7 @@ export class UmbMediaTypeRepository implements UmbTreeRepository {
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new MediaTypeTreeServerDataSource(this.#host); this.#treeSource = new UmbMediaTypeTreeServerDataSource(this.#host);
this.#detailSource = new UmbMediaTypeDetailServerDataSource(this.#host); this.#detailSource = new UmbMediaTypeDetailServerDataSource(this.#host);
this.#init = Promise.all([ this.#init = Promise.all([

View File

@@ -6,10 +6,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the MediaType tree that fetches data from the server * A data source for the MediaType tree that fetches data from the server
* @export * @export
* @class MediaTypeTreeServerDataSource * @class UmbMediaTypeTreeServerDataSource
* @implements {MediaTypeTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class MediaTypeTreeServerDataSource implements UmbTreeDataSource { export class UmbMediaTypeTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
/** /**
@@ -24,7 +24,7 @@ export class MediaTypeTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof MediaTypeTreeServerDataSource * @memberof UmbMediaTypeTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, MediaTypeResource.getTreeMediaTypeRoot({})); return tryExecuteAndNotify(this.#host, MediaTypeResource.getTreeMediaTypeRoot({}));
@@ -34,7 +34,7 @@ export class MediaTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof MediaTypeTreeServerDataSource * @memberof UmbMediaTypeTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
if (!parentId) { if (!parentId) {
@@ -54,7 +54,7 @@ export class MediaTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof MediaTypeTreeServerDataSource * @memberof UmbMediaTypeTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (!ids || ids.length === 0) { if (!ids || ids.length === 0) {

View File

@@ -2,7 +2,7 @@ import { UmbWorkspaceContext } from '../../../shared/components/workspace/worksp
import { UmbMediaTypeRepository } from '../repository/media-type.repository'; import { UmbMediaTypeRepository } from '../repository/media-type.repository';
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models'; import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models';
type EntityType = MediaTypeDetails; type EntityType = MediaTypeDetails;
@@ -10,7 +10,7 @@ export class UmbWorkspaceMediaTypeContext
extends UmbWorkspaceContext<UmbMediaTypeRepository, EntityType> extends UmbWorkspaceContext<UmbMediaTypeRepository, EntityType>
implements UmbEntityWorkspaceContextInterface<EntityType | undefined> implements UmbEntityWorkspaceContextInterface<EntityType | undefined>
{ {
#data = new ObjectState<MediaTypeDetails | undefined>(undefined); #data = new UmbObjectState<MediaTypeDetails | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);

View File

@@ -1,5 +1,5 @@
import type { MediaDetails } from '../'; import type { MediaDetails } from '../';
import { MediaTreeServerDataSource } from './sources/media.tree.server.data'; import { UmbMediaTreeServerDataSource } from './sources/media.tree.server.data';
import { UmbMediaTreeStore, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from './media.tree.store'; import { UmbMediaTreeStore, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from './media.tree.store';
import { UmbMediaStore, UMB_MEDIA_STORE_CONTEXT_TOKEN } from './media.store'; import { UmbMediaStore, UMB_MEDIA_STORE_CONTEXT_TOKEN } from './media.store';
import { UmbMediaDetailServerDataSource } from './sources/media.detail.server.data'; import { UmbMediaDetailServerDataSource } from './sources/media.detail.server.data';
@@ -36,7 +36,7 @@ export class UmbMediaRepository
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new MediaTreeServerDataSource(this.#host); this.#treeSource = new UmbMediaTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbMediaDetailServerDataSource(this.#host); this.#detailDataSource = new UmbMediaDetailServerDataSource(this.#host);
new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => { new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => {

View File

@@ -1,6 +1,6 @@
import type { MediaDetails } from '../'; import type { MediaDetails } from '../';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -11,7 +11,7 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
* @description - Data Store for Template Details * @description - Data Store for Template Details
*/ */
export class UmbMediaStore extends UmbStoreBase { export class UmbMediaStore extends UmbStoreBase {
#data = new ArrayState<MediaDetails>([], (x) => x.id); #data = new UmbArrayState<MediaDetails>([], (x) => x.id);
/** /**
* Creates an instance of UmbMediaStore. * Creates an instance of UmbMediaStore.

View File

@@ -1,6 +1,6 @@
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store'; import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -13,7 +13,7 @@ export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTr
* @description - Tree Data Store for Media * @description - Tree Data Store for Media
*/ */
export class UmbMediaTreeStore extends UmbEntityTreeStore { export class UmbMediaTreeStore extends UmbEntityTreeStore {
#data = new ArrayState<EntityTreeItemResponseModel>([], (x) => x.id); #data = new UmbArrayState<EntityTreeItemResponseModel>([], (x) => x.id);
/** /**
* Creates an instance of UmbMediaTreeStore. * Creates an instance of UmbMediaTreeStore.

View File

@@ -6,10 +6,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the Media tree that fetches data from the server * A data source for the Media tree that fetches data from the server
* @export * @export
* @class MediaTreeServerDataSource * @class UmbMediaTreeServerDataSource
* @implements {MediaTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class MediaTreeServerDataSource implements UmbTreeDataSource { export class UmbMediaTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
// TODO: how do we handle trashed items? // TODO: how do we handle trashed items?
@@ -42,9 +42,9 @@ export class MediaTreeServerDataSource implements UmbTreeDataSource {
} }
/** /**
* Creates an instance of MediaTreeServerDataSource. * Creates an instance of UmbMediaTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof MediaTreeServerDataSource * @memberof UmbMediaTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -53,7 +53,7 @@ export class MediaTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof MediaTreeServerDataSource * @memberof UmbMediaTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, MediaResource.getTreeMediaRoot({})); return tryExecuteAndNotify(this.#host, MediaResource.getTreeMediaRoot({}));
@@ -63,7 +63,7 @@ export class MediaTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof MediaTreeServerDataSource * @memberof UmbMediaTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
if (!parentId) { if (!parentId) {
@@ -83,7 +83,7 @@ export class MediaTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof MediaTreeServerDataSource * @memberof UmbMediaTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (!ids) { if (!ids) {

View File

@@ -2,7 +2,7 @@ import { UmbWorkspaceContext } from '../../../shared/components/workspace/worksp
import { UmbMediaRepository } from '../repository/media.repository'; import { UmbMediaRepository } from '../repository/media.repository';
import type { MediaDetails } from '../'; import type { MediaDetails } from '../';
import type { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import type { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import { appendToFrozenArray, ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { appendToFrozenArray, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
type EntityType = MediaDetails; type EntityType = MediaDetails;
@@ -10,7 +10,7 @@ export class UmbMediaWorkspaceContext
extends UmbWorkspaceContext<UmbMediaRepository, EntityType> extends UmbWorkspaceContext<UmbMediaRepository, EntityType>
implements UmbEntityWorkspaceContextInterface<EntityType | undefined> implements UmbEntityWorkspaceContextInterface<EntityType | undefined>
{ {
#data = new ObjectState<EntityType | undefined>(undefined); #data = new UmbObjectState<EntityType | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);

View File

@@ -1,7 +1,7 @@
import { UmbMemberGroupTreeStore, UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN } from './member-group.tree.store'; import { UmbMemberGroupTreeStore, UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN } from './member-group.tree.store';
import { UmbMemberGroupDetailServerDataSource } from './sources/member-group.detail.server.data'; import { UmbMemberGroupDetailServerDataSource } from './sources/member-group.detail.server.data';
import { UmbMemberGroupStore, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN } from './member-group.store'; import { UmbMemberGroupStore, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN } from './member-group.store';
import { MemberGroupTreeServerDataSource } from './sources/member-group.tree.server.data'; import { UmbMemberGroupTreeServerDataSource } from './sources/member-group.tree.server.data';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
@@ -26,7 +26,7 @@ export class UmbMemberGroupRepository implements UmbTreeRepository, UmbDetailRep
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new MemberGroupTreeServerDataSource(this.#host); this.#treeSource = new UmbMemberGroupTreeServerDataSource(this.#host);
this.#detailSource = new UmbMemberGroupDetailServerDataSource(this.#host); this.#detailSource = new UmbMemberGroupDetailServerDataSource(this.#host);
new UmbContextConsumerController(this.#host, UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN, (instance) => { new UmbContextConsumerController(this.#host, UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN, (instance) => {

View File

@@ -1,6 +1,6 @@
import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models'; import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
@@ -11,7 +11,7 @@ import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
* @description - Data Store for Member Groups * @description - Data Store for Member Groups
*/ */
export class UmbMemberGroupStore extends UmbStoreBase { export class UmbMemberGroupStore extends UmbStoreBase {
#data = new ArrayState<MemberGroupDetails>([], (x) => x.id); #data = new UmbArrayState<MemberGroupDetails>([], (x) => x.id);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString()); super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString());

View File

@@ -6,16 +6,16 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the Member Group tree that fetches data from the server * A data source for the Member Group tree that fetches data from the server
* @export * @export
* @class MemberGroupTreeServerDataSource * @class UmbMemberGroupTreeServerDataSource
* @implements {MemberGroupTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class MemberGroupTreeServerDataSource implements UmbTreeDataSource { export class UmbMemberGroupTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
/** /**
* Creates an instance of MemberGroupTreeServerDataSource. * Creates an instance of UmbMemberGroupTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof MemberGroupTreeServerDataSource * @memberof UmbMemberGroupTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -24,7 +24,7 @@ export class MemberGroupTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof MemberGroupTreeServerDataSource * @memberof UmbMemberGroupTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, MemberGroupResource.getTreeMemberGroupRoot({})); return tryExecuteAndNotify(this.#host, MemberGroupResource.getTreeMemberGroupRoot({}));
@@ -34,7 +34,7 @@ export class MemberGroupTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof MemberGroupTreeServerDataSource * @memberof UmbMemberGroupTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
// Not implemented for this tree // Not implemented for this tree
@@ -45,7 +45,7 @@ export class MemberGroupTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof MemberGroupTreeServerDataSource * @memberof UmbMemberGroupTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (!ids || ids.length === 0) { if (!ids || ids.length === 0) {

View File

@@ -3,14 +3,14 @@ import { UmbMemberGroupRepository } from '../repository/member-group.repository'
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models'; import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
type EntityType = MemberGroupDetails; type EntityType = MemberGroupDetails;
export class UmbWorkspaceMemberGroupContext export class UmbWorkspaceMemberGroupContext
extends UmbWorkspaceContext<UmbMemberGroupRepository, EntityType> extends UmbWorkspaceContext<UmbMemberGroupRepository, EntityType>
implements UmbEntityWorkspaceContextInterface<EntityType | undefined> implements UmbEntityWorkspaceContextInterface<EntityType | undefined>
{ {
#data = new ObjectState<EntityType | undefined>(undefined); #data = new UmbObjectState<EntityType | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);

View File

@@ -1,4 +1,4 @@
import { MemberTypeTreeServerDataSource } from './sources/member-type.tree.server.data'; import { UmbMemberTypeTreeServerDataSource } from './sources/member-type.tree.server.data';
import { UmbMemberTypeTreeStore, UMB_MEMBER_TYPE_TREE_STORE_CONTEXT_TOKEN } from './member-type.tree.store'; import { UmbMemberTypeTreeStore, UMB_MEMBER_TYPE_TREE_STORE_CONTEXT_TOKEN } from './member-type.tree.store';
import { UmbMemberTypeStore, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN } from './member-type.store'; import { UmbMemberTypeStore, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN } from './member-type.store';
import { UmbMemberTypeDetailServerDataSource } from './sources/member-type.detail.server.data'; import { UmbMemberTypeDetailServerDataSource } from './sources/member-type.detail.server.data';
@@ -30,7 +30,7 @@ export class UmbMemberTypeRepository implements UmbTreeRepository<TreeItemType>,
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new MemberTypeTreeServerDataSource(this.#host); this.#treeSource = new UmbMemberTypeTreeServerDataSource(this.#host);
this.#detailSource = new UmbMemberTypeDetailServerDataSource(this.#host); this.#detailSource = new UmbMemberTypeDetailServerDataSource(this.#host);
this.#init = Promise.all([ this.#init = Promise.all([

View File

@@ -1,7 +1,7 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models'; import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models';
/** /**
@@ -11,7 +11,7 @@ import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models';
* @description - Data Store for Member Types * @description - Data Store for Member Types
*/ */
export class UmbMemberTypeStore extends UmbStoreBase { export class UmbMemberTypeStore extends UmbStoreBase {
#data = new ArrayState<MemberTypeDetails>([], (x) => x.id); #data = new UmbArrayState<MemberTypeDetails>([], (x) => x.id);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN.toString()); super(host, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN.toString());

View File

@@ -6,10 +6,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the MemberType tree that fetches data from the server * A data source for the MemberType tree that fetches data from the server
* @export * @export
* @class MemberTypeTreeServerDataSource * @class UmbMemberTypeTreeServerDataSource
* @implements {MemberTypeTreeDataSource} * @implements {UmbTreeDataSource}
*/ */
export class MemberTypeTreeServerDataSource implements UmbTreeDataSource { export class UmbMemberTypeTreeServerDataSource implements UmbTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
/** /**
@@ -24,7 +24,7 @@ export class MemberTypeTreeServerDataSource implements UmbTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof MemberTypeTreeServerDataSource * @memberof UmbMemberTypeTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, MemberTypeResource.getTreeMemberTypeRoot({})); return tryExecuteAndNotify(this.#host, MemberTypeResource.getTreeMemberTypeRoot({}));
@@ -34,7 +34,7 @@ export class MemberTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the children of a given parent id from the server * Fetches the children of a given parent id from the server
* @param {(string | null)} parentId * @param {(string | null)} parentId
* @return {*} * @return {*}
* @memberof MemberTypeTreeServerDataSource * @memberof UmbMemberTypeTreeServerDataSource
*/ */
async getChildrenOf(parentId: string | null) { async getChildrenOf(parentId: string | null) {
const error: ProblemDetailsModel = { title: 'Not implemented for Member Type' }; const error: ProblemDetailsModel = { title: 'Not implemented for Member Type' };
@@ -45,7 +45,7 @@ export class MemberTypeTreeServerDataSource implements UmbTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof MemberTypeTreeServerDataSource * @memberof UmbMemberTypeTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (!ids || ids.length === 0) { if (!ids || ids.length === 0) {

View File

@@ -1,7 +1,7 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbMemberTypeRepository } from '../repository/member-type.repository'; import { UmbMemberTypeRepository } from '../repository/member-type.repository';
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import { ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
// TODO => use correct tpye // TODO => use correct tpye
@@ -11,7 +11,7 @@ export class UmbMemberTypeWorkspaceContext
extends UmbWorkspaceContext<UmbMemberTypeRepository, EntityType> extends UmbWorkspaceContext<UmbMemberTypeRepository, EntityType>
implements UmbEntityWorkspaceContextInterface<EntityType | undefined> implements UmbEntityWorkspaceContextInterface<EntityType | undefined>
{ {
#data = new ObjectState<EntityType | undefined>(undefined); #data = new UmbObjectState<EntityType | undefined>(undefined);
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {

View File

@@ -2,7 +2,7 @@ import { Observable } from 'rxjs';
import { umbMemberData } from '../../../core/mocks/data/member.data'; import { umbMemberData } from '../../../core/mocks/data/member.data';
import type { MemberDetails, MemberGroupDetails } from '@umbraco-cms/backoffice/models'; import type { MemberDetails, MemberGroupDetails } from '@umbraco-cms/backoffice/models';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState, createObservablePart } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState, createObservablePart } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/backoffice/store';
@@ -13,7 +13,7 @@ import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/backoffice/stor
* @description - Data Store for Members * @description - Data Store for Members
*/ */
export class UmbMemberStore extends UmbStoreBase implements UmbEntityDetailStore<MemberDetails> { export class UmbMemberStore extends UmbStoreBase implements UmbEntityDetailStore<MemberDetails> {
#data = new ArrayState<MemberDetails>([], (x) => x.id); #data = new UmbArrayState<MemberDetails>([], (x) => x.id);
public groups = this.#data.asObservable(); public groups = this.#data.asObservable();
constructor(private host: UmbControllerHostElement) { constructor(private host: UmbControllerHostElement) {

View File

@@ -1,5 +1,5 @@
import { UmbMemberTreeStore, UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN } from './member.tree.store'; import { UmbMemberTreeStore, UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN } from './member.tree.store';
import { MemberTreeServerDataSource } from './sources/member.tree.server.data'; import { UmbMemberTreeServerDataSource } from './sources/member.tree.server.data';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
@@ -8,7 +8,7 @@ import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
export class UmbMemberRepository implements UmbTreeRepository { export class UmbMemberRepository implements UmbTreeRepository {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
#dataSource: MemberTreeServerDataSource; #dataSource: UmbMemberTreeServerDataSource;
#treeStore?: UmbMemberTreeStore; #treeStore?: UmbMemberTreeStore;
#notificationContext?: UmbNotificationContext; #notificationContext?: UmbNotificationContext;
#initResolver?: () => void; #initResolver?: () => void;
@@ -17,7 +17,7 @@ export class UmbMemberRepository implements UmbTreeRepository {
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#dataSource = new MemberTreeServerDataSource(this.#host); this.#dataSource = new UmbMemberTreeServerDataSource(this.#host);
new UmbContextConsumerController(this.#host, UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN, (instance) => { new UmbContextConsumerController(this.#host, UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this.#treeStore = instance; this.#treeStore = instance;

View File

@@ -1,7 +1,7 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import type { MemberDetails } from '@umbraco-cms/backoffice/models'; import type { MemberDetails } from '@umbraco-cms/backoffice/models';
/** /**
@@ -11,7 +11,7 @@ import type { MemberDetails } from '@umbraco-cms/backoffice/models';
* @description - Data Store for Members * @description - Data Store for Members
*/ */
export class UmbMemberStore extends UmbStoreBase { export class UmbMemberStore extends UmbStoreBase {
#data = new ArrayState<MemberDetails>([], (x) => x.id); #data = new UmbArrayState<MemberDetails>([], (x) => x.id);
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host, UMB_MEMBER_STORE_CONTEXT_TOKEN.toString()); super(host, UMB_MEMBER_STORE_CONTEXT_TOKEN.toString());

View File

@@ -4,16 +4,16 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
/** /**
* A data source for the Member tree that fetches data from the server * A data source for the Member tree that fetches data from the server
* @export * @export
* @class MemberTreeServerDataSource * @class UmbMemberTreeServerDataSource
* @implements {MemberTreeDataSource} * @implements {MemberTreeDataSource}
*/ */
export class MemberTreeServerDataSource implements MemberTreeDataSource { export class UmbMemberTreeServerDataSource implements MemberTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
/** /**
* Creates an instance of MemberTreeServerDataSource. * Creates an instance of UmbMemberTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof MemberTreeServerDataSource * @memberof UmbMemberTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -22,7 +22,7 @@ export class MemberTreeServerDataSource implements MemberTreeDataSource {
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof MemberTreeServerDataSource * @memberof UmbMemberTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
const response = await fetch('/umbraco/management/api/v1/tree/member/root'); const response = await fetch('/umbraco/management/api/v1/tree/member/root');
@@ -36,7 +36,7 @@ export class MemberTreeServerDataSource implements MemberTreeDataSource {
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof MemberTreeServerDataSource * @memberof UmbMemberTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
const response = await fetch('/umbraco/management/api/v1/tree/member/item'); const response = await fetch('/umbraco/management/api/v1/tree/member/item');

View File

@@ -5,7 +5,7 @@ import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import type { UmbPackage } from '@umbraco-cms/backoffice/models'; import type { UmbPackage } from '@umbraco-cms/backoffice/models';
import type { PackageMigrationStatusResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { PackageMigrationStatusResponseModel } from '@umbraco-cms/backoffice/backend-api';
import type { ManifestBase } from '@umbraco-cms/backoffice/extensions-registry'; import type { ManifestBase } from '@umbraco-cms/backoffice/extensions-registry';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken<UmbPackageStore>('UmbPackageStore'); export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken<UmbPackageStore>('UmbPackageStore');
@@ -21,9 +21,9 @@ export class UmbPackageStore extends UmbStoreBase {
*/ */
#packages = new ReplaySubject<Array<UmbPackage>>(1); #packages = new ReplaySubject<Array<UmbPackage>>(1);
#extensions = new ArrayState<ManifestBase>([], (e) => e.alias); #extensions = new UmbArrayState<ManifestBase>([], (e) => e.alias);
#migrations = new ArrayState<PackageMigrationStatusResponseModel>([], (e) => e.packageName); #migrations = new UmbArrayState<PackageMigrationStatusResponseModel>([], (e) => e.packageName);
/** /**
* Observable of packages with extensions * Observable of packages with extensions

View File

@@ -135,7 +135,7 @@ export class UmbDashboardModelsBuilderElement extends UmbLitElement {
</p> </p>
${this._modelsBuilder?.lastError ${this._modelsBuilder?.lastError
? html`<p class="error">Last generation failed with the following error:</p> ? html`<p class="error">Last generation failed with the following error:</p>
<uui-code-block>${this._modelsBuilder.lastError}</uui-code-block>` <umb-code-block>${this._modelsBuilder.lastError}</umb-code-block>`
: nothing} : nothing}
</uui-box> </uui-box>
`; `;

View File

@@ -1,6 +1,6 @@
import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -13,7 +13,7 @@ export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataType
* @description - Data Store for Template Details * @description - Data Store for Template Details
*/ */
export class UmbDataTypeStore extends UmbStoreBase { export class UmbDataTypeStore extends UmbStoreBase {
#data = new ArrayState<DataTypeResponseModel>([], (x) => x.id); #data = new UmbArrayState<DataTypeResponseModel>([], (x) => x.id);
/** /**
* Creates an instance of UmbDataTypeStore. * Creates an instance of UmbDataTypeStore.

View File

@@ -2,7 +2,7 @@ import { UmbWorkspaceContext } from '../../../shared/components/workspace/worksp
import { UmbDataTypeRepository } from '../repository/data-type.repository'; import { UmbDataTypeRepository } from '../repository/data-type.repository';
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import type { CreateDataTypeRequestModel, DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { CreateDataTypeRequestModel, DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { appendToFrozenArray, ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { appendToFrozenArray, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
export class UmbDataTypeWorkspaceContext export class UmbDataTypeWorkspaceContext
@@ -10,7 +10,7 @@ export class UmbDataTypeWorkspaceContext
implements UmbEntityWorkspaceContextInterface<DataTypeResponseModel | undefined> implements UmbEntityWorkspaceContextInterface<DataTypeResponseModel | undefined>
{ {
// TODO: revisit. temp solution because the create and response models are different. // TODO: revisit. temp solution because the create and response models are different.
#data = new ObjectState<DataTypeResponseModel | undefined>(undefined); #data = new UmbObjectState<DataTypeResponseModel | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);

View File

@@ -1,5 +1,5 @@
import { UmbLanguageRepository } from '../repository/language.repository'; import { UmbLanguageRepository } from '../repository/language.repository';
import { ObjectState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
@@ -10,7 +10,7 @@ export class UmbAppLanguageContext {
#languages: Array<LanguageResponseModel> = []; #languages: Array<LanguageResponseModel> = [];
#appLanguage = new ObjectState<LanguageResponseModel | undefined>(undefined); #appLanguage = new UmbObjectState<LanguageResponseModel | undefined>(undefined);
appLanguage = this.#appLanguage.asObservable(); appLanguage = this.#appLanguage.asObservable();
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {

View File

@@ -1,7 +1,7 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbLanguageStore>('UmbLanguageStore'); export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbLanguageStore>('UmbLanguageStore');
@@ -13,7 +13,7 @@ export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbLanguageS
* @description - Details Data Store for Languages * @description - Details Data Store for Languages
*/ */
export class UmbLanguageStore extends UmbStoreBase { export class UmbLanguageStore extends UmbStoreBase {
#data = new ArrayState<LanguageResponseModel>([], (x) => x.isoCode); #data = new UmbArrayState<LanguageResponseModel>([], (x) => x.isoCode);
data = this.#data.asObservable(); data = this.#data.asObservable();
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {

View File

@@ -2,18 +2,18 @@ import { UmbLanguageRepository } from '../../repository/language.repository';
import { UmbWorkspaceContext } from '../../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbWorkspaceContext } from '../../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import type { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
export class UmbLanguageWorkspaceContext export class UmbLanguageWorkspaceContext
extends UmbWorkspaceContext<UmbLanguageRepository, LanguageResponseModel> extends UmbWorkspaceContext<UmbLanguageRepository, LanguageResponseModel>
implements UmbEntityWorkspaceContextInterface implements UmbEntityWorkspaceContextInterface
{ {
#data = new ObjectState<LanguageResponseModel | undefined>(undefined); #data = new UmbObjectState<LanguageResponseModel | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
// TODO: this is a temp solution to bubble validation errors to the UI // TODO: this is a temp solution to bubble validation errors to the UI
#validationErrors = new ObjectState<any | undefined>(undefined); #validationErrors = new UmbObjectState<any | undefined>(undefined);
validationErrors = this.#validationErrors.asObservable(); validationErrors = this.#validationErrors.asObservable();
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {

View File

@@ -2,7 +2,7 @@ import { UmbLogMessagesServerDataSource, UmbLogSearchesServerDataSource } from '
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
import { DirectionModel, LogLevelModel } from '@umbraco-cms/backoffice/backend-api'; import { DirectionModel, LogLevelModel, SavedLogSearchPresenationBaseModel } from '@umbraco-cms/backoffice/backend-api';
// Move to documentation / JSdoc // Move to documentation / JSdoc
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */ /* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
@@ -47,10 +47,30 @@ export class UmbLogViewerRepository {
return this.#searchDataSource.getAllSavedSearches({ skip, take }); return this.#searchDataSource.getAllSavedSearches({ skip, take });
} }
async getMessageTemplates({ skip, take }: { skip: number; take: number }) { async saveSearch({ name, query }: SavedLogSearchPresenationBaseModel) {
await this.#init();
this.#searchDataSource.postLogViewerSavedSearch({ name, query });
}
async removeSearch({ name }: { name: string }) {
await this.#init();
this.#searchDataSource.deleteSavedSearchByName({ name });
}
async getMessageTemplates({
skip,
take,
startDate,
endDate,
}: {
skip: number;
take: number;
startDate?: string;
endDate?: string;
}) {
await this.#init(); await this.#init();
return this.#messagesDataSource.getLogViewerMessageTemplate({ skip, take }); return this.#messagesDataSource.getLogViewerMessageTemplate({ skip, take, startDate, endDate });
} }
async getLogCount({ startDate, endDate }: { startDate?: string; endDate?: string }) { async getLogCount({ startDate, endDate }: { startDate?: string; endDate?: string }) {

View File

@@ -20,11 +20,7 @@ export interface LogSearchDataSource {
}): Promise<DataSourceResponse<PagedSavedLogSearchResponseModel>>; }): Promise<DataSourceResponse<PagedSavedLogSearchResponseModel>>;
getSavedSearchByName({ name }: { name: string }): Promise<DataSourceResponse<SavedLogSearchResponseModel>>; getSavedSearchByName({ name }: { name: string }): Promise<DataSourceResponse<SavedLogSearchResponseModel>>;
deleteSavedSearchByName({ name }: { name: string }): Promise<DataSourceResponse<unknown>>; deleteSavedSearchByName({ name }: { name: string }): Promise<DataSourceResponse<unknown>>;
postLogViewerSavedSearch({ postLogViewerSavedSearch({ name, query }: SavedLogSearchResponseModel): Promise<DataSourceResponse<unknown>>;
requestBody,
}: {
requestBody?: SavedLogSearchResponseModel;
}): Promise<DataSourceResponse<unknown>>;
} }
export interface LogMessagesDataSource { export interface LogMessagesDataSource {

View File

@@ -54,8 +54,11 @@ export class UmbLogSearchesServerDataSource implements LogSearchDataSource {
* @return {*} * @return {*}
* @memberof UmbLogSearchesServerDataSource * @memberof UmbLogSearchesServerDataSource
*/ */
async postLogViewerSavedSearch({ requestBody }: { requestBody?: SavedLogSearchResponseModel }) { async postLogViewerSavedSearch({ name, query }: SavedLogSearchResponseModel) {
return await tryExecuteAndNotify(this.#host, LogViewerResource.postLogViewerSavedSearch({ requestBody })); return await tryExecuteAndNotify(
this.#host,
LogViewerResource.postLogViewerSavedSearch({ requestBody: { name, query } })
);
} }
/** /**
* Remove a log viewer saved search by name from the server * Remove a log viewer saved search by name from the server

View File

@@ -7,6 +7,7 @@ import {
UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN,
} from '../../logviewer.context'; } from '../../logviewer.context';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { query as getQuery, path, toQueryString } from '@umbraco-cms/backoffice/router';
@customElement('umb-log-viewer-date-range-selector') @customElement('umb-log-viewer-date-range-selector')
export class UmbLogViewerDateRangeSelectorElement extends UmbLitElement { export class UmbLogViewerDateRangeSelectorElement extends UmbLitElement {
@@ -86,8 +87,15 @@ export class UmbLogViewerDateRangeSelectorElement extends UmbLitElement {
this._endDate = input.value; this._endDate = input.value;
} }
}); });
const newDateRange: LogViewerDateRange = { startDate: this._startDate, endDate: this._endDate };
this.#logViewerContext?.setDateRange(newDateRange); const query = getQuery();
const qs = toQueryString({
...query,
startDate: this._startDate,
endDate: this._endDate,
});
window.history.pushState({}, '', `${path()}?${qs}`);
} }
render() { render() {

View File

@@ -27,7 +27,10 @@ export class UmbLogViewerLevelTagElement extends LitElement {
Information: { look: 'primary', color: 'positive' }, Information: { look: 'primary', color: 'positive' },
Warning: { look: 'primary', color: 'warning' }, Warning: { look: 'primary', color: 'warning' },
Error: { look: 'primary', color: 'danger' }, Error: { look: 'primary', color: 'danger' },
Fatal: { look: 'primary' }, Fatal: {
look: 'primary',
style: 'background-color: var(--umb-log-viewer-fatal-color); color: var(--uui-color-surface)',
},
}; };
render() { render() {

View File

@@ -1,6 +1,6 @@
import './components'; import './components';
import { map } from 'rxjs'; import { map } from 'rxjs';
import { css, html, nothing } from 'lit'; import { PropertyValueMap, css, html, nothing } from 'lit';
import { customElement, state } from 'lit/decorators.js'; import { customElement, state } from 'lit/decorators.js';
import { UUITextStyles } from '@umbraco-ui/uui-css'; import { UUITextStyles } from '@umbraco-ui/uui-css';
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js';
@@ -8,7 +8,7 @@ import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { umbExtensionsRegistry, createExtensionElement } from '@umbraco-cms/backoffice/extensions-api'; import { umbExtensionsRegistry, createExtensionElement } from '@umbraco-cms/backoffice/extensions-api';
import { ManifestWorkspaceView, ManifestWorkspaceViewCollection } from '@umbraco-cms/backoffice/extensions-registry'; import { ManifestWorkspaceView, ManifestWorkspaceViewCollection } from '@umbraco-cms/backoffice/extensions-registry';
import { UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/internal/router'; import type { UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/internal/router';
import type { IRoute } from '@umbraco-cms/backoffice/router'; import type { IRoute } from '@umbraco-cms/backoffice/router';
//TODO make uui-input accept min and max values //TODO make uui-input accept min and max values
@@ -26,7 +26,7 @@ export class UmbLogViewerWorkspaceElement extends UmbLitElement {
--umb-log-viewer-information-color: var(--uui-color-positive); --umb-log-viewer-information-color: var(--uui-color-positive);
--umb-log-viewer-warning-color: var(--uui-color-warning); --umb-log-viewer-warning-color: var(--uui-color-warning);
--umb-log-viewer-error-color: var(--uui-color-danger); --umb-log-viewer-error-color: var(--uui-color-danger);
--umb-log-viewer-fatal-color: var(--uui-color-default); --umb-log-viewer-fatal-color: var(--uui-palette-black);
--umb-log-viewer-verbose-color: var(--uui-color-current); --umb-log-viewer-verbose-color: var(--uui-color-current);
} }
@@ -64,12 +64,24 @@ export class UmbLogViewerWorkspaceElement extends UmbLitElement {
constructor() { constructor() {
super(); super();
this.#logViewerContext.init(); this.#logViewerContext.init();
this.provideContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, this.#logViewerContext);
}
firstUpdated(props: PropertyValueMap<unknown>) {
super.firstUpdated(props);
window.addEventListener('changestate', this.#logViewerContext.onChangeState);
this.#logViewerContext.onChangeState();
} }
connectedCallback() { connectedCallback() {
super.connectedCallback(); super.connectedCallback();
this._observeWorkspaceViews(); this._observeWorkspaceViews();
this.provideContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, this.#logViewerContext); }
disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener('changestate', this.#logViewerContext.onChangeState);
} }
load(): void { load(): void {
@@ -101,9 +113,7 @@ export class UmbLogViewerWorkspaceElement extends UmbLitElement {
this._routes = this._workspaceViews.map((view) => { this._routes = this._workspaceViews.map((view) => {
return { return {
path: `${view.meta.pathname}`, path: `${view.meta.pathname}`,
component: () => { component: () => createExtensionElement(view),
return createExtensionElement(view);
},
setup: (component) => { setup: (component) => {
(component as any).manifest = view; (component as any).manifest = view;
}, },

View File

@@ -1,4 +1,5 @@
import type { import type {
ManifestModal,
ManifestWorkspace, ManifestWorkspace,
ManifestWorkspaceAction, ManifestWorkspaceAction,
ManifestWorkspaceView, ManifestWorkspaceView,
@@ -51,4 +52,13 @@ const workspaceViews: Array<ManifestWorkspaceView> = [
const workspaceActions: Array<ManifestWorkspaceAction> = []; const workspaceActions: Array<ManifestWorkspaceAction> = [];
export const manifests = [workspace, ...workspaceViews, ...workspaceActions]; const modals: Array<ManifestModal> = [
{
type: 'modal',
alias: 'Umb.Modal.LogViewer.SaveSearch',
name: 'Saved Searches Modal',
loader: () => import('../views/search/components/log-viewer-search-input-modal.element'),
},
];
export const manifests = [workspace, ...workspaceViews, ...workspaceActions, ...modals];

View File

@@ -1,11 +1,11 @@
import { UmbLogViewerRepository } from '../repository/log-viewer.repository'; import { UmbLogViewerRepository } from '../repository/log-viewer.repository';
import { import {
BasicState, UmbBasicState,
ArrayState, UmbArrayState,
createObservablePart, createObservablePart,
DeepState, UmbDeepState,
ObjectState, UmbObjectState,
StringState, UmbStringState,
} from '@umbraco-cms/backoffice/observable-api'; } from '@umbraco-cms/backoffice/observable-api';
import { import {
DirectionModel, DirectionModel,
@@ -15,9 +15,11 @@ import {
PagedLogMessageResponseModel, PagedLogMessageResponseModel,
PagedLogTemplateResponseModel, PagedLogTemplateResponseModel,
PagedSavedLogSearchResponseModel, PagedSavedLogSearchResponseModel,
SavedLogSearchPresenationBaseModel,
} from '@umbraco-cms/backoffice/backend-api'; } from '@umbraco-cms/backoffice/backend-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { query } from '@umbraco-cms/backoffice/router';
export type PoolingInterval = 0 | 2000 | 5000 | 10000 | 20000 | 30000; export type PoolingInterval = 0 | 2000 | 5000 | 10000 | 20000 | 30000;
export interface PoolingCOnfig { export interface PoolingCOnfig {
@@ -56,38 +58,38 @@ export class UmbLogViewerWorkspaceContext {
endDate: this.today, endDate: this.today,
}; };
#savedSearches = new DeepState<PagedSavedLogSearchResponseModel | undefined>(undefined); #savedSearches = new UmbObjectState<PagedSavedLogSearchResponseModel | undefined>(undefined);
savedSearches = createObservablePart(this.#savedSearches, (data) => data?.items); savedSearches = createObservablePart(this.#savedSearches, (data) => data?.items);
#logCount = new DeepState<LogLevelCountsReponseModel | null>(null); #logCount = new UmbDeepState<LogLevelCountsReponseModel | null>(null);
logCount = createObservablePart(this.#logCount, (data) => data); logCount = createObservablePart(this.#logCount, (data) => data);
#dateRange = new DeepState<LogViewerDateRange>(this.defaultDateRange); #dateRange = new UmbDeepState<LogViewerDateRange>(this.defaultDateRange);
dateRange = createObservablePart(this.#dateRange, (data) => data); dateRange = createObservablePart(this.#dateRange, (data) => data);
#loggers = new DeepState<PagedLoggerResponseModel | null>(null); #loggers = new UmbDeepState<PagedLoggerResponseModel | null>(null);
loggers = createObservablePart(this.#loggers, (data) => data?.items); loggers = createObservablePart(this.#loggers, (data) => data?.items);
#canShowLogs = new BasicState<boolean | null>(null); #canShowLogs = new UmbBasicState<boolean | null>(null);
canShowLogs = createObservablePart(this.#canShowLogs, (data) => data); canShowLogs = createObservablePart(this.#canShowLogs, (data) => data);
#filterExpression = new StringState<string>(''); #filterExpression = new UmbStringState<string>('');
filterExpression = createObservablePart(this.#filterExpression, (data) => data); filterExpression = createObservablePart(this.#filterExpression, (data) => data);
#messageTemplates = new DeepState<PagedLogTemplateResponseModel | null>(null); #messageTemplates = new UmbDeepState<PagedLogTemplateResponseModel | null>(null);
messageTemplates = createObservablePart(this.#messageTemplates, (data) => data); messageTemplates = createObservablePart(this.#messageTemplates, (data) => data);
#logLevelsFilter = new ArrayState<LogLevelModel>([]); #logLevelsFilter = new UmbArrayState<LogLevelModel>([]);
logLevelsFilter = createObservablePart(this.#logLevelsFilter, (data) => data); logLevelsFilter = createObservablePart(this.#logLevelsFilter, (data) => data);
#logs = new DeepState<PagedLogMessageResponseModel | null>(null); #logs = new UmbDeepState<PagedLogMessageResponseModel | null>(null);
logs = createObservablePart(this.#logs, (data) => data?.items); logs = createObservablePart(this.#logs, (data) => data?.items);
logsTotal = createObservablePart(this.#logs, (data) => data?.total); logsTotal = createObservablePart(this.#logs, (data) => data?.total);
#polling = new ObjectState<PoolingCOnfig>({ enabled: false, interval: 2000 }); #polling = new UmbObjectState<PoolingCOnfig>({ enabled: false, interval: 2000 });
polling = createObservablePart(this.#polling, (data) => data); polling = createObservablePart(this.#polling, (data) => data);
#sortingDirection = new BasicState<DirectionModel>(DirectionModel.ASCENDING); #sortingDirection = new UmbBasicState<DirectionModel>(DirectionModel.ASCENDING);
sortingDirection = createObservablePart(this.#sortingDirection, (data) => data); sortingDirection = createObservablePart(this.#sortingDirection, (data) => data);
#intervalID: number | null = null; #intervalID: number | null = null;
@@ -103,8 +105,54 @@ export class UmbLogViewerWorkspaceContext {
this.validateLogSize(); this.validateLogSize();
} }
setDateRange(dateRange: LogViewerDateRange) { reset() {
const { startDate, endDate } = dateRange; this.#logs.next({ items: [], total: 0 });
this.setCurrentPage(1);
}
onChangeState = () => {
this.reset();
const searchQuery = query();
let sanitizedQuery = '';
if (searchQuery.lq) {
sanitizedQuery = decodeURIComponent(searchQuery.lq);
}
this.setFilterExpression(sanitizedQuery);
let validLogLevels: LogLevelModel[] = [];
if (searchQuery.loglevels) {
const loglevels = searchQuery.loglevels.split(',') as LogLevelModel[];
// Filter out invalid log levels that do not exist in LogLevelModel
validLogLevels = loglevels.filter((loglevel) => {
return Object.values(LogLevelModel).includes(loglevel);
});
}
this.setLogLevelsFilter(validLogLevels);
const dateRange: Partial<LogViewerDateRange> = {};
if (searchQuery.startDate) {
dateRange.startDate = searchQuery.startDate;
}
if (searchQuery.endDate) {
dateRange.endDate = searchQuery.endDate;
}
this.setDateRange(dateRange);
this.setCurrentPage(searchQuery.page ? Number(searchQuery.page) : 1);
this.getLogs();
};
setDateRange(dateRange: Partial<LogViewerDateRange>) {
let { startDate, endDate } = dateRange;
if (!startDate) startDate = this.defaultDateRange.startDate;
if (!endDate) endDate = this.defaultDateRange.endDate;
const isAnyDateInTheFuture = new Date(startDate) > new Date() || new Date(endDate) > new Date(); const isAnyDateInTheFuture = new Date(startDate) > new Date() || new Date(endDate) > new Date();
const isStartDateBiggerThenEndDate = new Date(startDate) > new Date(endDate); const isStartDateBiggerThenEndDate = new Date(startDate) > new Date(endDate);
@@ -112,9 +160,10 @@ export class UmbLogViewerWorkspaceContext {
return; return;
} }
this.#dateRange.next(dateRange); this.#dateRange.next({ startDate, endDate });
this.validateLogSize(); this.validateLogSize();
this.getLogCount(); this.getLogCount();
this.getMessageTemplates(0, 10);
} }
async getSavedSearches() { async getSavedSearches() {
@@ -122,7 +171,7 @@ export class UmbLogViewerWorkspaceContext {
if (data) { if (data) {
this.#savedSearches.next(data); this.#savedSearches.next(data);
} else { } else {
//falback to some default searches like in the old backoffice //falback to some default searches resembling Umbraco <= 12
this.#savedSearches.next({ this.#savedSearches.next({
items: [ items: [
{ {
@@ -155,6 +204,26 @@ export class UmbLogViewerWorkspaceContext {
} }
} }
async saveSearch({ name, query }: SavedLogSearchPresenationBaseModel) {
const previousSavedSearches = this.#savedSearches.getValue()?.items ?? [];
try {
this.#savedSearches.update({ items: [...previousSavedSearches, { name, query }] });
await this.#repository.saveSearch({ name, query });
} catch (err) {
this.#savedSearches.update({ items: previousSavedSearches });
}
}
async removeSearch({ name }: { name: string }) {
const previousSavedSearches = this.#savedSearches.getValue()?.items ?? [];
try {
this.#savedSearches.update({ items: previousSavedSearches.filter((search) => search.name !== name) });
await this.#repository.removeSearch({ name });
} catch (err) {
this.#savedSearches.update({ items: previousSavedSearches });
}
}
async getLogCount() { async getLogCount() {
const { data } = await this.#repository.getLogCount({ ...this.#dateRange.getValue() }); const { data } = await this.#repository.getLogCount({ ...this.#dateRange.getValue() });
@@ -164,7 +233,7 @@ export class UmbLogViewerWorkspaceContext {
} }
async getMessageTemplates(skip: number, take: number) { async getMessageTemplates(skip: number, take: number) {
const { data } = await this.#repository.getMessageTemplates({ skip, take }); const { data } = await this.#repository.getMessageTemplates({ skip, take, ...this.#dateRange.getValue() });
if (data) { if (data) {
this.#messageTemplates.next(data); this.#messageTemplates.next(data);
@@ -180,14 +249,12 @@ export class UmbLogViewerWorkspaceContext {
} }
async validateLogSize() { async validateLogSize() {
const { data, error } = await this.#repository.getLogViewerValidateLogsSize({ ...this.#dateRange.getValue() }); const { error } = await this.#repository.getLogViewerValidateLogsSize({ ...this.#dateRange.getValue() });
if (error) { if (error) {
this.#canShowLogs.next(false); this.#canShowLogs.next(false);
console.info('LogViewer: ', error);
return; return;
} }
this.#canShowLogs.next(true); this.#canShowLogs.next(true);
console.info('LogViewer:showinfg logs');
} }
setCurrentPage(page: number) { setCurrentPage(page: number) {
@@ -244,7 +311,7 @@ export class UmbLogViewerWorkspaceContext {
} }
setPollingInterval(interval: PoolingInterval) { setPollingInterval(interval: PoolingInterval) {
this.#polling.update({ interval, enabled: true }); this.#polling.update({ interval });
} }
toggleSortOrder() { toggleSortOrder() {

View File

@@ -1,4 +1,4 @@
import { html } from 'lit'; import { html, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context'; import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@@ -37,7 +37,7 @@ export class UmbLogViewerLogLevelOverviewElement extends UmbLitElement {
render() { render() {
return html`${this._loggers.length > 0 return html`${this._loggers.length > 0
? this._loggers.find((logger) => logger.name === this.loggerName)?.level ? this._loggers.find((logger) => logger.name === this.loggerName)?.level
: ''}`; : nothing}`;
} }
} }

View File

@@ -66,14 +66,11 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement {
#renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => { #renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => {
return html` <li> return html` <li>
<uui-button <uui-button
@click=${() => {
this.#setCurrentQuery(searchListItem.query ?? '');
}}
label="${searchListItem.name ?? ''}" label="${searchListItem.name ?? ''}"
title="${searchListItem.name ?? ''}" title="${searchListItem.name ?? ''}"
href=${'/section/settings/logviewer/search?lq=' + searchListItem.query} href=${`section/settings/workspace/logviewer/search/?lq=${searchListItem.query}`}>
><uui-icon name="umb:search"></uui-icon>${searchListItem.name}</uui-button <uui-icon name="umb:search"></uui-icon>${searchListItem.name}
> </uui-button>
</li>`; </li>`;
}; };
@@ -90,13 +87,12 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement {
${this._messageTemplates ${this._messageTemplates
? this._messageTemplates.items.map( ? this._messageTemplates.items.map(
(template) => (template) =>
html`<uui-table-row html`<uui-table-row>
><uui-table-cell> <uui-table-cell>
<a <a
@click=${() => { href=${`section/settings/workspace/logviewer/search/?lq=${encodeURIComponent(
this.#setCurrentQuery(`@MessageTemplate='${template.messageTemplate}'` ?? ''); `@MessageTemplate='${template.messageTemplate}'`
}} )}`}>
href=${'/section/settings/logviewer/search?lg=@MessageTemplate%3D' + template.messageTemplate}>
<span>${template.messageTemplate}</span> <span>${template.count}</span> <span>${template.messageTemplate}</span> <span>${template.count}</span>
</a> </a>
</uui-table-cell> </uui-table-cell>
@@ -109,9 +105,9 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement {
id="show-more-templates-btn" id="show-more-templates-btn"
look="primary" look="primary"
@click=${this.#getMessageTemplates} @click=${this.#getMessageTemplates}
label="Show more templates" label="Show more templates">
>Show more</uui-button Show more
> </uui-button>
</uui-box> </uui-box>
`; `;
} }

View File

@@ -40,6 +40,7 @@ export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement {
private _savedSearches: SavedLogSearchResponseModel[] = []; private _savedSearches: SavedLogSearchResponseModel[] = [];
#logViewerContext?: UmbLogViewerWorkspaceContext; #logViewerContext?: UmbLogViewerWorkspaceContext;
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => { this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => {
@@ -56,39 +57,21 @@ export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement {
}); });
} }
#setCurrentQuery(query: string) {
this.#logViewerContext?.setFilterExpression(query);
}
#renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => { #renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => {
return html` <li> return html` <li>
<uui-button <uui-button
@click=${() => {
this.#setCurrentQuery(searchListItem.query ?? '');
}}
label="${searchListItem.name ?? ''}" label="${searchListItem.name ?? ''}"
title="${searchListItem.name ?? ''}" title="${searchListItem.name ?? ''}"
href=${'/section/settings/logviewer/search?lq=' + searchListItem.query} href=${`section/settings/workspace/logviewer/search/?lq=${encodeURIComponent(searchListItem.query ?? '')}`}>
><uui-icon name="umb:search"></uui-icon>${searchListItem.name}</uui-button <uui-icon name="umb:search"></uui-icon>${searchListItem.name}
> </uui-button>
</li>`; </li>`;
}; };
render() { render() {
return html` <uui-box id="saved-searches" headline="Saved searches"> return html` <uui-box id="saved-searches" headline="Saved searches">
<ul> <ul>
<li> ${this.#renderSearchItem({ name: 'All logs', query: '' })} ${this._savedSearches.map(this.#renderSearchItem)}
<uui-button
@click=${() => {
this.#setCurrentQuery('');
}}
label="All logs"
title="All logs"
href="/section/settings/logviewer/search"
><uui-icon name="umb:search"></uui-icon>All logs</uui-button
>
</li>
${this._savedSearches.map(this.#renderSearchItem)}
</ul> </ul>
</uui-box>`; </uui-box>`;
} }

View File

@@ -107,8 +107,8 @@ export class UmbLogViewerOverviewViewElement extends UmbLitElement {
#observeErrorCount() { #observeErrorCount() {
if (!this.#logViewerContext) return; if (!this.#logViewerContext) return;
this.observe(this.#logViewerContext.logCount, () => { this.observe(this.#logViewerContext.logCount, (logLevelCount) => {
this._errorCount = this._logLevelCount?.error ?? 0; this._errorCount = logLevelCount?.error ?? 0;
}); });
} }
@@ -128,7 +128,13 @@ export class UmbLogViewerOverviewViewElement extends UmbLitElement {
</uui-box> </uui-box>
<uui-box id="errors" headline="Number of Errors"> <uui-box id="errors" headline="Number of Errors">
<h1 id="error-count">${this._errorCount}</h1> <uui-button
label="Show error logs"
href=${`section/settings/workspace/logviewer/search/?lq=${encodeURIComponent(
`@Level='Fatal' or @Level='Error' or Has(@Exception)`
)}`}>
<h1 id="error-count">${this._errorCount}</h1></uui-button
>
</uui-box> </uui-box>
<uui-box id="level" headline="Log level"> <uui-box id="level" headline="Log level">

View File

@@ -2,10 +2,11 @@ import { UUICheckboxElement } from '@umbraco-ui/uui';
import { UUITextStyles } from '@umbraco-ui/uui-css'; import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { customElement, queryAll, state } from 'lit/decorators.js'; import { customElement, queryAll, state } from 'lit/decorators.js';
import _ from 'lodash'; import { debounce } from 'lodash-es';
import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context'; import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context';
import { LogLevelModel } from '@umbraco-cms/backoffice/backend-api'; import { LogLevelModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { path, query, toQueryString } from '@umbraco-cms/backoffice/router';
@customElement('umb-log-viewer-log-level-filter-menu') @customElement('umb-log-viewer-log-level-filter-menu')
export class UmbLogViewerLogLevelFilterMenuElement extends UmbLitElement { export class UmbLogViewerLogLevelFilterMenuElement extends UmbLitElement {
@@ -58,16 +59,23 @@ export class UmbLogViewerLogLevelFilterMenuElement extends UmbLitElement {
#setLogLevel() { #setLogLevel() {
if (!this.#logViewerContext) return; if (!this.#logViewerContext) return;
this.#logViewerContext?.setCurrentPage(1);
const logLevels = Array.from(this._logLevelSelectorCheckboxes) const logLevels = Array.from(this._logLevelSelectorCheckboxes)
.filter((checkbox) => checkbox.checked) .filter((checkbox) => checkbox.checked)
.map((checkbox) => checkbox.value as LogLevelModel); .map((checkbox) => checkbox.value as LogLevelModel);
this.#logViewerContext?.setLogLevelsFilter(logLevels);
this.#logViewerContext.getLogs(); let q = query();
if (logLevels.length) {
q = { ...q, loglevels: logLevels.join(',') };
} else {
delete q.loglevels;
}
window.history.pushState({}, '', `${path()}?${toQueryString(q)}`);
} }
setLogLevelDebounce = _.debounce(this.#setLogLevel, 300); setLogLevelDebounce = debounce(this.#setLogLevel, 300);
#selectAllLogLevels() { #selectAllLogLevels() {
this._logLevelSelectorCheckboxes.forEach((checkbox) => (checkbox.checked = true)); this._logLevelSelectorCheckboxes.forEach((checkbox) => (checkbox.checked = true));
@@ -84,9 +92,13 @@ export class UmbLogViewerLogLevelFilterMenuElement extends UmbLitElement {
<div slot="dropdown" id="log-level-selector" @change=${this.setLogLevelDebounce}> <div slot="dropdown" id="log-level-selector" @change=${this.setLogLevelDebounce}>
${Object.values(LogLevelModel).map( ${Object.values(LogLevelModel).map(
(logLevel) => (logLevel) =>
html`<uui-checkbox class="log-level-menu-item" .value=${logLevel} label="${logLevel}" html`<uui-checkbox
><umb-log-viewer-level-tag .level=${logLevel}></umb-log-viewer-level-tag class="log-level-menu-item"
></uui-checkbox>` .checked=${this._logLevelFilter.includes(logLevel)}
.value=${logLevel}
label="${logLevel}">
<umb-log-viewer-level-tag .level=${logLevel}></umb-log-viewer-level-tag>
</uui-checkbox>`
)} )}
<uui-button class="log-level-menu-item" @click=${this.#selectAllLogLevels} label="Select all" <uui-button class="log-level-menu-item" @click=${this.#selectAllLogLevels} label="Select all"
>Select all</uui-button >Select all</uui-button
@@ -100,8 +112,8 @@ export class UmbLogViewerLogLevelFilterMenuElement extends UmbLitElement {
render() { render() {
return html` return html`
<umb-button-with-dropdown label="Select log levels" <umb-button-with-dropdown label="Select log levels">
>Log Level: Log Level:
${this._logLevelFilter.length > 0 ${this._logLevelFilter.length > 0
? this._logLevelFilter.map((level) => html`<span class="log-level-button-indicator">${level}</span>`) ? this._logLevelFilter.map((level) => html`<span class="log-level-button-indicator">${level}</span>`)
: 'All'} : 'All'}

View File

@@ -4,6 +4,7 @@ import { customElement, property, query, state } from 'lit/decorators.js';
import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context'; import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context';
import { LogLevelModel, LogMessagePropertyPresentationModel } from '@umbraco-cms/backoffice/backend-api'; import { LogLevelModel, LogMessagePropertyPresentationModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { query as getQuery, toQueryString } from '@umbraco-cms/backoffice/router';
//TODO: check how to display EventId field in the message properties //TODO: check how to display EventId field in the message properties
@customElement('umb-log-viewer-message') @customElement('umb-log-viewer-message')
@@ -211,18 +212,23 @@ export class UmbLogViewerMessageElement extends UmbLitElement {
private _propertiesWithSearchMenu: Array<string> = ['HttpRequestNumber', 'SourceContext', 'MachineName']; private _propertiesWithSearchMenu: Array<string> = ['HttpRequestNumber', 'SourceContext', 'MachineName'];
private _findLogsWithProperty({ name, value }: LogMessagePropertyPresentationModel) { private _findLogsWithProperty({ name, value }: LogMessagePropertyPresentationModel) {
let queryString = ''; if (!name) return '';
let query = getQuery();
let sanitizedValue = value ?? '';
if (isNaN(+(value ?? ''))) { if (isNaN(+(value ?? ''))) {
queryString = name + "='" + value + "'"; sanitizedValue = "'" + value + "'";
} else {
queryString = name + '=' + value;
} }
this.#logViewerContext?.setFilterExpression(queryString); query = {
this.#logViewerContext?.setCurrentPage(1); ...query,
this.details.removeAttribute('open'); lq: encodeURIComponent(`${name}=${sanitizedValue}`),
this.#logViewerContext?.getLogs(); };
const queryString = toQueryString(query);
return queryString;
} }
#setOpen(event: Event) { #setOpen(event: Event) {
@@ -259,14 +265,12 @@ export class UmbLogViewerMessageElement extends UmbLitElement {
${this._propertiesWithSearchMenu.includes(property.name ?? '') ${this._propertiesWithSearchMenu.includes(property.name ?? '')
? html`<uui-button ? html`<uui-button
compact compact
@click=${() => {
this._findLogsWithProperty(property);
}}
look="secondary" look="secondary"
label="Find logs with ${property.name}" label="Find logs with ${property.name}"
title="Find logs with ${property.name}" title="Find logs with ${property.name}"
><uui-icon name="umb:search"></uui-icon href=${`section/settings/workspace/logviewer/search/?${this._findLogsWithProperty(property)}`}>
></uui-button>` <uui-icon name="umb:search"></uui-icon>
</uui-button>`
: ''} : ''}
</div> </div>
</li>` </li>`

View File

@@ -11,6 +11,9 @@ export class UmbLogViewerMessagesListElement extends UmbLitElement {
static styles = [ static styles = [
UUITextStyles, UUITextStyles,
css` css`
:host {
display: block;
}
#message-list-header { #message-list-header {
display: flex; display: flex;
font-weight: 600; font-weight: 600;
@@ -129,23 +132,21 @@ export class UmbLogViewerMessagesListElement extends UmbLitElement {
<div id="machine">Machine name</div> <div id="machine">Machine name</div>
<div id="message">Message</div> <div id="message">Message</div>
</div> </div>
<uui-scroll-container id="logs-scroll-container" style="max-height: calc(100vh - 490px)"> ${this._logs.length > 0
${this._logs.length > 0 ? html` ${this._logs.map(
? html` ${this._logs.map( (log) => html`<umb-log-viewer-message
(log) => html`<umb-log-viewer-message .timestamp=${log.timestamp ?? ''}
.timestamp=${log.timestamp ?? ''} .level=${log.level ?? ''}
.level=${log.level ?? ''} .renderedMessage=${log.renderedMessage ?? ''}
.renderedMessage=${log.renderedMessage ?? ''} .properties=${log.properties ?? []}
.properties=${log.properties ?? []} .exception=${log.exception ?? ''}
.exception=${log.exception ?? ''} .messageTemplate=${log.messageTemplate ?? ''}></umb-log-viewer-message>`
.messageTemplate=${log.messageTemplate ?? ''}></umb-log-viewer-message>` )}`
)}` : html`<umb-empty-state size="small"
: html`<umb-empty-state size="small" ><span id="empty">
><span id="empty"> <uui-icon name="umb:search"></uui-icon>Sorry, we cannot find what you are looking for.
<uui-icon name="umb:search"></uui-icon>Sorry, we cannot find what you are looking for. </span></umb-empty-state
</span></umb-empty-state >`}
>`}
</uui-scroll-container>
${this._renderPagination()} ${this._renderPagination()}
</uui-box>`; </uui-box>`;
} }

View File

@@ -90,10 +90,11 @@ export class UmbLogViewerPollingButtonElement extends UmbLitElement {
this.#logViewerContext?.togglePolling(); this.#logViewerContext?.togglePolling();
} }
#setPolingInterval(interval: PoolingInterval) { #setPolingInterval = (interval: PoolingInterval) => {
this.#logViewerContext?.setPollingInterval(interval); this.#logViewerContext?.setPollingInterval(interval);
this.#closePoolingPopover(); this.#closePoolingPopover();
} };
#openPoolingPopover() { #openPoolingPopover() {
this._pollingPopover.open = true; this._pollingPopover.open = true;
@@ -103,6 +104,7 @@ export class UmbLogViewerPollingButtonElement extends UmbLitElement {
#closePoolingPopover() { #closePoolingPopover() {
this._pollingPopover.open = false; this._pollingPopover.open = false;
this._polingExpandSymbol.open = false; this._polingExpandSymbol.open = false;
this.#togglePolling();
} }
render() { render() {
@@ -111,7 +113,7 @@ export class UmbLogViewerPollingButtonElement extends UmbLitElement {
>${this._poolingConfig.enabled >${this._poolingConfig.enabled
? html`<uui-icon name="umb:axis-rotation" id="polling-enabled-icon"></uui-icon>Polling ? html`<uui-icon name="umb:axis-rotation" id="polling-enabled-icon"></uui-icon>Polling
${this._poolingConfig.interval / 1000} seconds` ${this._poolingConfig.interval / 1000} seconds`
: 'Pooling'}</uui-button : 'Polling'}</uui-button
> >
<uui-popover placement="bottom-end" id="polling-popover" @close=${() => (this._polingExpandSymbol.open = false)}> <uui-popover placement="bottom-end" id="polling-popover" @close=${() => (this._polingExpandSymbol.open = false)}>
<uui-button slot="trigger" compact label="Choose pooling time" @click=${this.#openPoolingPopover}> <uui-button slot="trigger" compact label="Choose pooling time" @click=${this.#openPoolingPopover}>

View File

@@ -0,0 +1,73 @@
import { html, css } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, query, state } from 'lit/decorators.js';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
import { SavedLogSearchPresenationBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UUIInputElement } from '@umbraco-ui/uui';
@customElement('umb-log-viewer-save-search-modal')
export default class UmbLogViewerSaveSearchModalElement extends UmbModalBaseElement<
{ query: string },
SavedLogSearchPresenationBaseModel
> {
static styles = [
UUITextStyles,
css`
uui-input {
width: 100%;
}
`,
];
@query('uui-input')
private _input!: UUIInputElement;
private _handleClose() {
this.modalHandler?.reject();
}
private _handleSubmit() {
this.modalHandler?.submit({ name: this._input.value as string, query: this.data?.query });
}
@state()
private _hasValue = false;
#validate(event: Event) {
const target = event.target as UUIInputElement;
this._hasValue = (target.value as string).length > 0;
}
render() {
return html`
<uui-dialog-layout headline="Save Search">
<span>Enter a friendly name for your search query</span>
<uui-form-layout-item>
<uui-label slot="label">Query:</uui-label>
<span>${this.data?.query}</span>
</uui-form-layout-item>
<uui-form-layout-item>
<uui-label slot="label" for="input">Name:</uui-label>
<uui-input label="Search name" id="input" @input=${this.#validate}></uui-input>
</uui-form-layout-item>
<uui-button slot="actions" @click="${this._handleClose}" label="Close dialog">Close</uui-button>
<uui-button
.disabled=${!this._hasValue}
slot="actions"
look="primary"
color="positive"
label="Save search"
@click="${this._handleSubmit}"
>Save</uui-button
>
</uui-dialog-layout>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-log-viewer-save-search-modal': UmbLogViewerSaveSearchModalElement;
}
}

View File

@@ -1,10 +1,31 @@
import { UUIInputElement, UUIPopoverElement, UUISymbolExpandElement } from '@umbraco-ui/uui'; import { UUIButtonElement, UUIInputElement, UUIPopoverElement, UUISymbolExpandElement } from '@umbraco-ui/uui';
import { UUITextStyles } from '@umbraco-ui/uui-css'; import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { customElement, query, state } from 'lit/decorators.js'; import { customElement, query, state } from 'lit/decorators.js';
import { Subject, debounceTime, tap } from 'rxjs';
import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context'; import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../../logviewer.context';
import { SavedLogSearchResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { SavedLogSearchResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { query as getQuery, path, toQueryString } from '@umbraco-cms/backoffice/router';
import {
UMB_MODAL_CONTEXT_TOKEN,
UmbModalContext,
UmbModalHandler,
UmbModalToken,
} from '@umbraco-cms/backoffice/modal';
import './log-viewer-search-input-modal.element';
export interface UmbContextSaveSearchModalData {
query: string;
}
export const UMB_LOG_VIEWER_SAVE_SEARCH_MODAL = new UmbModalToken<UmbContextSaveSearchModalData>(
'Umb.Modal.LogViewer.SaveSearch',
{
type: 'dialog',
size: 'small',
}
);
@customElement('umb-log-viewer-search-input') @customElement('umb-log-viewer-search-input')
export class UmbLogViewerSearchInputElement extends UmbLitElement { export class UmbLogViewerSearchInputElement extends UmbLitElement {
@@ -37,6 +58,13 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement {
box-shadow: var(--uui-shadow-depth-1); box-shadow: var(--uui-shadow-depth-1);
} }
#loader-container {
display: flex;
justify-content: center;
align-items: center;
margin: 0 var(--uui-size-space-4);
}
.saved-search-item { .saved-search-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -92,15 +120,38 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement {
@state() @state()
private _inputQuery = ''; private _inputQuery = '';
@state()
private _showLoader = false;
private inputQuery$ = new Subject<string>();
#logViewerContext?: UmbLogViewerWorkspaceContext; #logViewerContext?: UmbLogViewerWorkspaceContext;
private _modalContext?: UmbModalContext;
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => { this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => {
this.#logViewerContext = instance; this.#logViewerContext = instance;
this.#observeStuff(); this.#observeStuff();
this.#logViewerContext?.getSavedSearches();
this.#logViewerContext.getLogs(); this.#logViewerContext.getLogs();
}); });
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
});
this.inputQuery$
.pipe(
tap(() => (this._showLoader = true)),
debounceTime(250)
)
.subscribe((query) => {
this.#logViewerContext?.setFilterExpression(query);
this.#persist(query);
this._showLoader = false;
});
} }
#observeStuff() { #observeStuff() {
@@ -129,36 +180,54 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement {
#setQuery(event: Event) { #setQuery(event: Event) {
const target = event.target as UUIInputElement; const target = event.target as UUIInputElement;
this._inputQuery = target.value as string; this.inputQuery$.next(target.value as string);
this.#logViewerContext?.setFilterExpression(this._inputQuery);
} }
#setQueryFromSavedSearch(query: string) { #setQueryFromSavedSearch(query: string) {
this._inputQuery = query; this.inputQuery$.next(query);
this.#logViewerContext?.setFilterExpression(query);
this.#logViewerContext?.setCurrentPage(1);
this.#logViewerContext?.getLogs();
this._savedSearchesPopover.open = false; this._savedSearchesPopover.open = false;
} }
#persist(filter: string) {
let q = getQuery();
q = {
...q,
lq: filter,
};
window.history.pushState({}, '', `${path()}?${toQueryString(q)}`);
}
#clearQuery() { #clearQuery() {
this._inputQuery = ''; this.inputQuery$.next('');
this.#logViewerContext?.setFilterExpression(''); this.#logViewerContext?.setFilterExpression('');
this.#logViewerContext?.getLogs(); this.#logViewerContext?.getLogs();
} }
#search() { #modalHandler?: UmbModalHandler;
this.#logViewerContext?.setCurrentPage(1);
this.#logViewerContext?.getLogs(); #saveSearch(savedSearch: SavedLogSearchResponseModel) {
this.#logViewerContext?.saveSearch(savedSearch);
}
#removeSearch(event: Event) {
const target = event.target as UUIButtonElement;
this.#logViewerContext?.removeSearch({ name: target.id });
}
#openSaveSearchDialog() {
this.#modalHandler = this._modalContext?.open(UMB_LOG_VIEWER_SAVE_SEARCH_MODAL, { query: this._inputQuery });
this.#modalHandler?.onSubmit().then((savedSearch) => {
if (savedSearch) {
this.#saveSearch(savedSearch);
}
});
} }
render() { render() {
return html` <uui-popover return html`
placement="bottom-start" <uui-popover placement="bottom-start" id="saved-searches-popover" @close=${this.#toggleSavedSearchesExpandSymbol}>
id="saved-searches-popover"
@close=${this.#toggleSavedSearchesExpandSymbol}>
<uui-input <uui-input
id="search-input" id="search-input"
label="Search logs" label="Search logs"
@@ -166,8 +235,13 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement {
slot="trigger" slot="trigger"
@input=${this.#setQuery} @input=${this.#setQuery}
.value=${this._inputQuery}> .value=${this._inputQuery}>
${this._showLoader
? html`<div id="loader-container" slot="append">
<uui-loader-circle></uui-loader-circle>
</div>`
: ''}
${this._inputQuery ${this._inputQuery
? html`<uui-button compact slot="append" label="Save search" ? html`<uui-button compact slot="append" label="Save search" @click=${this.#openSaveSearchDialog}
><uui-icon name="umb:favorite"></uui-icon></uui-button ><uui-icon name="umb:favorite"></uui-icon></uui-button
><uui-button compact slot="append" label="Clear" @click=${this.#clearQuery} ><uui-button compact slot="append" label="Clear" @click=${this.#clearQuery}
><uui-icon name="umb:delete"></uui-icon ><uui-icon name="umb:delete"></uui-icon
@@ -193,14 +267,14 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement {
@click=${() => this.#setQueryFromSavedSearch(search.query ?? '')}> @click=${() => this.#setQueryFromSavedSearch(search.query ?? '')}>
<span class="saved-search-item-name">${search.name}</span> <span class="saved-search-item-name">${search.name}</span>
<span class="saved-search-item-query">${search.query}</span></button <span class="saved-search-item-query">${search.query}</span></button
><uui-button label="Remove saved search" color="danger" ><uui-button label="Remove saved search" id="${search.name}" color="danger" @click=${this.#removeSearch}
><uui-icon name="umb:trash"></uui-icon ><uui-icon name="umb:trash"></uui-icon
></uui-button> ></uui-button>
</li>` </li>`
)} )}
</uui-scroll-container> </uui-scroll-container>
</uui-popover> </uui-popover>
<uui-button look="primary" @click=${this.#search} label="Search">Search</uui-button>`; `;
} }
} }

View File

@@ -3,6 +3,7 @@ import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js'; import { customElement, state } from 'lit/decorators.js';
import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../logviewer.context'; import { UmbLogViewerWorkspaceContext, UMB_APP_LOG_VIEWER_CONTEXT_TOKEN } from '../../logviewer.context';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@customElement('umb-log-viewer-search-view') @customElement('umb-log-viewer-search-view')
export class UmbLogViewerSearchViewElement extends UmbLitElement { export class UmbLogViewerSearchViewElement extends UmbLitElement {
@@ -11,6 +12,7 @@ export class UmbLogViewerSearchViewElement extends UmbLitElement {
css` css`
#layout { #layout {
margin: 20px; margin: 20px;
padding-bottom: 20px;
} }
#levels-container, #levels-container,
#input-container { #input-container {
@@ -44,6 +46,9 @@ export class UmbLogViewerSearchViewElement extends UmbLitElement {
private _canShowLogs = false; private _canShowLogs = false;
#logViewerContext?: UmbLogViewerWorkspaceContext; #logViewerContext?: UmbLogViewerWorkspaceContext;
#canShowLogsObserver?: UmbObserverController<boolean | null>;
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => { this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT_TOKEN, (instance) => {
@@ -53,8 +58,10 @@ export class UmbLogViewerSearchViewElement extends UmbLitElement {
} }
#observeCanShowLogs() { #observeCanShowLogs() {
if (this.#canShowLogsObserver) this.#canShowLogsObserver.destroy();
if (!this.#logViewerContext) return; if (!this.#logViewerContext) return;
this.observe(this.#logViewerContext.canShowLogs, (canShowLogs) => {
this.#canShowLogsObserver = this.observe(this.#logViewerContext.canShowLogs, (canShowLogs) => {
this._canShowLogs = canShowLogs ?? false; this._canShowLogs = canShowLogs ?? false;
}); });
} }

View File

@@ -1,8 +1,8 @@
import { UmbRelationTypeTreeStore, UMB_RELATION_TYPE_TREE_STORE_CONTEXT_TOKEN } from './relation-type.tree.store'; import { UmbRelationTypeTreeStore, UMB_RELATION_TYPE_TREE_STORE_CONTEXT_TOKEN } from './relation-type.tree.store';
import { UmbRelationTypeServerDataSource } from './sources/relation-type.server.data'; import { UmbRelationTypeServerDataSource } from './sources/relation-type.server.data';
import { UmbRelationTypeStore, UMB_RELATION_TYPE_STORE_CONTEXT_TOKEN } from './relation-type.store'; import { UmbRelationTypeStore, UMB_RELATION_TYPE_STORE_CONTEXT_TOKEN } from './relation-type.store';
import { RelationTypeTreeServerDataSource } from './sources/relation-type.tree.server.data'; import { UmbRelationTypeTreeServerDataSource } from './sources/relation-type.tree.server.data';
import { RelationTypeTreeDataSource } from './sources'; import { UmbRelationTypeTreeDataSource } from './sources';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { import {
@@ -23,7 +23,7 @@ export class UmbRelationTypeRepository
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
#treeSource: RelationTypeTreeDataSource; #treeSource: UmbRelationTypeTreeDataSource;
#treeStore?: UmbRelationTypeTreeStore; #treeStore?: UmbRelationTypeTreeStore;
#detailDataSource: UmbRelationTypeServerDataSource; #detailDataSource: UmbRelationTypeServerDataSource;
@@ -35,7 +35,7 @@ export class UmbRelationTypeRepository
this.#host = host; this.#host = host;
// TODO: figure out how spin up get the correct data source // TODO: figure out how spin up get the correct data source
this.#treeSource = new RelationTypeTreeServerDataSource(this.#host); this.#treeSource = new UmbRelationTypeTreeServerDataSource(this.#host);
this.#detailDataSource = new UmbRelationTypeServerDataSource(this.#host); this.#detailDataSource = new UmbRelationTypeServerDataSource(this.#host);
this.#init = Promise.all([ this.#init = Promise.all([

View File

@@ -1,6 +1,6 @@
import type { RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -13,7 +13,7 @@ export const UMB_RELATION_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbRela
* @description - Data Store for Template Details * @description - Data Store for Template Details
*/ */
export class UmbRelationTypeStore extends UmbStoreBase { export class UmbRelationTypeStore extends UmbStoreBase {
#data = new ArrayState<RelationTypeResponseModel>([], (x) => x.id); #data = new UmbArrayState<RelationTypeResponseModel>([], (x) => x.id);
/** /**
* Creates an instance of UmbRelationTypeStore. * Creates an instance of UmbRelationTypeStore.

View File

@@ -1,7 +1,7 @@
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository'; import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
import { ItemResponseModelBaseModel, PagedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { ItemResponseModelBaseModel, PagedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
export interface RelationTypeTreeDataSource { export interface UmbRelationTypeTreeDataSource {
getRootItems(): Promise<DataSourceResponse<PagedEntityTreeItemResponseModel>>; getRootItems(): Promise<DataSourceResponse<PagedEntityTreeItemResponseModel>>;
getItems(ids: Array<string>): Promise<DataSourceResponse<ItemResponseModelBaseModel[]>>; getItems(ids: Array<string>): Promise<DataSourceResponse<ItemResponseModelBaseModel[]>>;
} }

View File

@@ -1,4 +1,4 @@
import { RelationTypeTreeDataSource } from '.'; import { UmbRelationTypeTreeDataSource } from '.';
import { ProblemDetailsModel, RelationTypeResource } from '@umbraco-cms/backoffice/backend-api'; import { ProblemDetailsModel, RelationTypeResource } from '@umbraco-cms/backoffice/backend-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
@@ -7,10 +7,10 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
/** /**
* A data source for the RelationType tree that fetches data from the server * A data source for the RelationType tree that fetches data from the server
* @export * @export
* @class RelationTypeTreeServerDataSource * @class UmbRelationTypeTreeServerDataSource
* @implements {RelationTypeTreeDataSource} * @implements {UmbRelationTypeTreeDataSource}
*/ */
export class RelationTypeTreeServerDataSource implements RelationTypeTreeDataSource { export class UmbRelationTypeTreeServerDataSource implements UmbRelationTypeTreeDataSource {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
// TODO: how do we handle trashed items? // TODO: how do we handle trashed items?
@@ -47,9 +47,9 @@ export class RelationTypeTreeServerDataSource implements RelationTypeTreeDataSou
} }
/** /**
* Creates an instance of RelationTypeTreeServerDataSource. * Creates an instance of UmbRelationTypeTreeServerDataSource.
* @param {UmbControllerHostElement} host * @param {UmbControllerHostElement} host
* @memberof RelationTypeTreeServerDataSource * @memberof UmbRelationTypeTreeServerDataSource
*/ */
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
this.#host = host; this.#host = host;
@@ -58,7 +58,7 @@ export class RelationTypeTreeServerDataSource implements RelationTypeTreeDataSou
/** /**
* Fetches the root items for the tree from the server * Fetches the root items for the tree from the server
* @return {*} * @return {*}
* @memberof RelationTypeTreeServerDataSource * @memberof UmbRelationTypeTreeServerDataSource
*/ */
async getRootItems() { async getRootItems() {
return tryExecuteAndNotify(this.#host, RelationTypeResource.getTreeRelationTypeRoot({})); return tryExecuteAndNotify(this.#host, RelationTypeResource.getTreeRelationTypeRoot({}));
@@ -68,7 +68,7 @@ export class RelationTypeTreeServerDataSource implements RelationTypeTreeDataSou
* Fetches the items for the given ids from the server * Fetches the items for the given ids from the server
* @param {Array<string>} ids * @param {Array<string>} ids
* @return {*} * @return {*}
* @memberof RelationTypeTreeServerDataSource * @memberof UmbRelationTypeTreeServerDataSource
*/ */
async getItems(ids: Array<string>) { async getItems(ids: Array<string>) {
if (ids) { if (ids) {

View File

@@ -3,14 +3,14 @@ import { UmbRelationTypeRepository } from '../repository/relation-type.repositor
import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
import type { RelationTypeBaseModel, RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { RelationTypeBaseModel, RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { ObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
export class UmbRelationTypeWorkspaceContext export class UmbRelationTypeWorkspaceContext
extends UmbWorkspaceContext<UmbRelationTypeRepository, RelationTypeResponseModel> extends UmbWorkspaceContext<UmbRelationTypeRepository, RelationTypeResponseModel>
implements UmbEntityWorkspaceContextInterface<RelationTypeResponseModel | undefined> implements UmbEntityWorkspaceContextInterface<RelationTypeResponseModel | undefined>
{ {
#data = new ObjectState<RelationTypeResponseModel | undefined>(undefined); #data = new UmbObjectState<RelationTypeResponseModel | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = this.#data.getObservablePart((data) => data?.name); name = this.#data.getObservablePart((data) => data?.name);
id = this.#data.getObservablePart((data) => data?.id); id = this.#data.getObservablePart((data) => data?.id);

View File

@@ -2,7 +2,7 @@ import { Observable } from 'rxjs';
import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { ArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { umbExtensionsRegistry, createExtensionClass } from '@umbraco-cms/backoffice/extensions-api'; import { umbExtensionsRegistry, createExtensionClass } from '@umbraco-cms/backoffice/extensions-api';
import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
@@ -17,15 +17,15 @@ export class UmbCollectionContext<DataType extends EntityTreeItemResponseModel =
private _store?: any; private _store?: any;
protected _dataObserver?: UmbObserverController<DataType[]>; protected _dataObserver?: UmbObserverController<DataType[]>;
#data = new ArrayState(<Array<DataType>>[]); #data = new UmbArrayState(<Array<DataType>>[]);
public readonly data = this.#data.asObservable(); public readonly data = this.#data.asObservable();
#selection = new ArrayState(<Array<string>>[]); #selection = new UmbArrayState(<Array<string>>[]);
public readonly selection = this.#selection.asObservable(); public readonly selection = this.#selection.asObservable();
/* /*
TODO: TODO:
private _search = new StringState(''); private _search = new UmbStringState('');
public readonly search = this._search.asObservable(); public readonly search = this._search.asObservable();
*/ */

View File

@@ -1,9 +1,9 @@
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { StringState } from '@umbraco-cms/backoffice/observable-api'; import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
export class UmbBackofficeContext { export class UmbBackofficeContext {
#activeSectionAlias = new StringState(undefined); #activeSectionAlias = new UmbStringState(undefined);
public readonly activeSectionAlias = this.#activeSectionAlias.asObservable(); public readonly activeSectionAlias = this.#activeSectionAlias.asObservable();
public getAllowedSections() { public getAllowedSections() {

View File

@@ -7,8 +7,8 @@ import { customElement } from 'lit/decorators.js';
* @slot the full message * @slot the full message
* *
*/ */
@customElement('uui-code-block') @customElement('umb-code-block')
export class UUICodeBlockElement extends LitElement { export class UmbCodeBlockElement extends LitElement {
static styles = [ static styles = [
UUITextStyles, UUITextStyles,
css` css`
@@ -54,6 +54,6 @@ export class UUICodeBlockElement extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'uui-code-block': UUICodeBlockElement; 'umb-code-block': UmbCodeBlockElement;
} }
} }

View File

@@ -1,15 +1,15 @@
import { Meta, StoryObj } from '@storybook/web-components'; import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit'; import { html } from 'lit';
import './code-block.element'; import './code-block.element';
import type { UUICodeBlockElement } from './code-block.element'; import type { UmbCodeBlockElement } from './code-block.element';
const meta: Meta<UUICodeBlockElement> = { const meta: Meta<UmbCodeBlockElement> = {
title: 'Components/Code Block', title: 'Components/Code Block',
component: 'uui-code-block', component: 'umb-code-block',
}; };
export default meta; export default meta;
type Story = StoryObj<UUICodeBlockElement>; type Story = StoryObj<UmbCodeBlockElement>;
export const Overview: Story = { export const Overview: Story = {
args: {}, args: {},
@@ -17,5 +17,5 @@ export const Overview: Story = {
export const WithCode: Story = { export const WithCode: Story = {
decorators: [], decorators: [],
render: () => html` <uui-code-block> // Lets write some javascript alert("Hello World"); </uui-code-block>`, render: () => html` <umb-code-block> // Lets write some javascript alert("Hello World"); </umb-code-block>`,
}; };

View File

@@ -111,12 +111,12 @@ const codeSnippets: Record<CodeEditorLanguage, string> = {
"Smartypants, double quotes" and 'single quotes'`, "Smartypants, double quotes" and 'single quotes'`,
typescript: `import { UmbTemplateRepository } from '../repository/template.repository'; typescript: `import { UmbTemplateRepository } from '../repository/template.repository';
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { createObservablePart, DeepState } from '@umbraco-cms/observable-api'; import { createObservablePart, UmbDeepState } from '@umbraco-cms/observable-api';
import { TemplateModel } from '@umbraco-cms/backend-api'; import { TemplateModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostElement } from '@umbraco-cms/controller'; import { UmbControllerHostElement } from '@umbraco-cms/controller';
export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext<UmbTemplateRepository, TemplateModel> { export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext<UmbTemplateRepository, TemplateModel> {
#data = new DeepState<TemplateModel | undefined>(undefined); #data = new UmbDeepState<TemplateModel | undefined>(undefined);
data = this.#data.asObservable(); data = this.#data.asObservable();
name = createObservablePart(this.#data, (data) => data?.name); name = createObservablePart(this.#data, (data) => data?.name);
content = createObservablePart(this.#data, (data) => data?.content); content = createObservablePart(this.#data, (data) => data?.content);

View File

@@ -180,10 +180,17 @@ export class UmbDebugElement extends UmbLitElement {
const props: TemplateResult[] = []; const props: TemplateResult[] = [];
instance.properties?.forEach((property) => { instance.properties?.forEach((property) => {
if (property.type === 'string') { switch(property.type){
props.push(html`<li>${property.key} = ${property.value}</li>`); case 'string':
} else { case 'number':
props.push(html`<li>${property.key} <em>(${property.type})</em></li>`); case 'boolean':
case 'object':
props.push(html`<li>${property.key} <em>(${property.type})</em> = ${property.value}</li>`);
break;
default:
props.push(html`<li>${property.key} <em>(${property.type})</em></li>`);
break;
} }
}); });

View File

@@ -9,6 +9,7 @@ export interface Circle {
name: string; name: string;
percent: number; percent: number;
kind: string; kind: string;
number: number;
} }
interface CircleWithCommands extends Circle { interface CircleWithCommands extends Circle {
@@ -195,6 +196,7 @@ export class UmbDonutChartElement extends LitElement {
this._slices.map((slice) => { this._slices.map((slice) => {
return { return {
percent: this.#calculatePercentage(slice.amount), percent: this.#calculatePercentage(slice.amount),
number: slice.amount,
color: slice.color, color: slice.color,
name: slice.name, name: slice.name,
kind: slice.kind, kind: slice.kind,
@@ -249,7 +251,7 @@ export class UmbDonutChartElement extends LitElement {
const index = target.dataset.index as unknown as number; const index = target.dataset.index as unknown as number;
const circle = this.circles[index]; const circle = this.circles[index];
this._detailName = circle.name; this._detailName = circle.name;
this._detailAmount = circle.percent; this._detailAmount = circle.number;
this._detailColor = circle.color; this._detailColor = circle.color;
this._detailKind = circle.kind; this._detailKind = circle.kind;
} }

View File

@@ -3,10 +3,9 @@ import { expect, fixture, html } from '@open-wc/testing';
import { InitializedExtension, UmbExtensionSlotElement } from './extension-slot.element'; import { InitializedExtension, UmbExtensionSlotElement } from './extension-slot.element';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
import { ManifestDashboard } from '@umbraco-cms/backoffice/extensions-registry'; import { ManifestDashboard } from '@umbraco-cms/backoffice/extensions-registry';
import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
@customElement('test-extension-slot-manifest-element') @customElement('umb-test-extension-slot-manifest-element')
class MyExtensionSlotManifestElement extends HTMLElement {} class UmbTestExtensionSlotManifestElement extends HTMLElement {}
function sleep(ms: number) { function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
@@ -52,7 +51,7 @@ describe('UmbExtensionSlotElement', () => {
type: 'dashboard', type: 'dashboard',
alias: 'unit-test-ext-slot-element-manifest', alias: 'unit-test-ext-slot-element-manifest',
name: 'unit-test-extension', name: 'unit-test-extension',
elementName: 'test-extension-slot-manifest-element', elementName: 'umb-test-extension-slot-manifest-element',
meta: { meta: {
pathname: 'test/test', pathname: 'test/test',
}, },
@@ -75,7 +74,7 @@ describe('UmbExtensionSlotElement', () => {
await sleep(0); await sleep(0);
expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(MyExtensionSlotManifestElement); expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement);
}); });
it('use the render method', async () => { it('use the render method', async () => {
@@ -90,7 +89,9 @@ describe('UmbExtensionSlotElement', () => {
await sleep(0); await sleep(0);
expect(element.shadowRoot!.firstElementChild?.nodeName).to.be.equal('BLA'); expect(element.shadowRoot!.firstElementChild?.nodeName).to.be.equal('BLA');
expect(element.shadowRoot!.firstElementChild?.firstElementChild).to.be.instanceOf(MyExtensionSlotManifestElement); expect(element.shadowRoot!.firstElementChild?.firstElementChild).to.be.instanceOf(
UmbTestExtensionSlotManifestElement
);
}); });
}); });
}); });

View File

@@ -1,19 +1,19 @@
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { StringState, BooleanState } from '@umbraco-cms/backoffice/observable-api'; import { UmbStringState, UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
export class UmbSectionSidebarContext { export class UmbSectionSidebarContext {
#host: UmbControllerHostElement; #host: UmbControllerHostElement;
#contextMenuIsOpen = new BooleanState(false); #contextMenuIsOpen = new UmbBooleanState(false);
contextMenuIsOpen = this.#contextMenuIsOpen.asObservable(); contextMenuIsOpen = this.#contextMenuIsOpen.asObservable();
#entityType = new StringState<undefined>(undefined); #entityType = new UmbStringState<undefined>(undefined);
entityType = this.#entityType.asObservable(); entityType = this.#entityType.asObservable();
#unique = new StringState<undefined>(undefined); #unique = new UmbStringState<undefined>(undefined);
unique = this.#unique.asObservable(); unique = this.#unique.asObservable();
#headline = new StringState<undefined>(undefined); #headline = new UmbStringState<undefined>(undefined);
headline = this.#headline.asObservable(); headline = this.#headline.asObservable();
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {

View File

@@ -1,14 +1,14 @@
import type { ManifestSection } from '@umbraco-cms/backoffice/extensions-registry'; import type { ManifestSection } from '@umbraco-cms/backoffice/extensions-registry';
import type { Entity } from '@umbraco-cms/backoffice/models'; import type { Entity } from '@umbraco-cms/backoffice/models';
import { ObjectState, StringState } from '@umbraco-cms/backoffice/observable-api'; import { UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export type ActiveTreeItemType = Entity | undefined; export type ActiveTreeItemType = Entity | undefined;
export class UmbSectionContext { export class UmbSectionContext {
#manifestAlias = new StringState<string | undefined>(undefined); #manifestAlias = new UmbStringState<string | undefined>(undefined);
#manifestPathname = new StringState<string | undefined>(undefined); #manifestPathname = new UmbStringState<string | undefined>(undefined);
#manifestLabel = new StringState<string | undefined>(undefined); #manifestLabel = new UmbStringState<string | undefined>(undefined);
public readonly alias = this.#manifestAlias.asObservable(); public readonly alias = this.#manifestAlias.asObservable();
public readonly pathname = this.#manifestPathname.asObservable(); public readonly pathname = this.#manifestPathname.asObservable();
public readonly label = this.#manifestLabel.asObservable(); public readonly label = this.#manifestLabel.asObservable();

View File

@@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, nothing, PropertyValueMap } from 'lit'; import { css, nothing, PropertyValueMap } from 'lit';
import { customElement, property, state } from 'lit/decorators.js'; import { customElement, property, state } from 'lit/decorators.js';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { DeepState } from '@umbraco-cms/backoffice/observable-api'; import { UmbDeepState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
// TODO: Refactor this, its not a service and the data should be handled by a context api. // TODO: Refactor this, its not a service and the data should be handled by a context api.
@@ -13,7 +13,7 @@ export class UmbTreeContextMenuPageServiceElement extends UmbLitElement {
@property({ type: Object }) @property({ type: Object })
public actionEntity: any = { key: '', name: '' }; public actionEntity: any = { key: '', name: '' };
#entity = new DeepState({ key: '', name: '' } as any); #entity = new UmbDeepState({ key: '', name: '' } as any);
public readonly entity = this.#entity.asObservable(); public readonly entity = this.#entity.asObservable();
@state() @state()

View File

@@ -7,7 +7,12 @@ import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/sect
import { UmbTreeContextBase } from '../tree.context'; import { UmbTreeContextBase } from '../tree.context';
import { UmbTreeItemContext } from '../tree-item.context.interface'; import { UmbTreeItemContext } from '../tree-item.context.interface';
import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry'; import { ManifestEntityAction } from '@umbraco-cms/backoffice/extensions-registry';
import { BooleanState, DeepState, StringState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import {
UmbBooleanState,
UmbDeepState,
UmbStringState,
UmbObserverController,
} from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { import {
UmbContextConsumerController, UmbContextConsumerController,
@@ -27,28 +32,28 @@ export class UmbTreeItemContextBase<T extends TreeItemPresentationModel = TreeIt
public unique?: string; public unique?: string;
public type?: string; public type?: string;
#treeItem = new DeepState<T | undefined>(undefined); #treeItem = new UmbDeepState<T | undefined>(undefined);
treeItem = this.#treeItem.asObservable(); treeItem = this.#treeItem.asObservable();
#hasChildren = new BooleanState(false); #hasChildren = new UmbBooleanState(false);
hasChildren = this.#hasChildren.asObservable(); hasChildren = this.#hasChildren.asObservable();
#isLoading = new BooleanState(false); #isLoading = new UmbBooleanState(false);
isLoading = this.#isLoading.asObservable(); isLoading = this.#isLoading.asObservable();
#isSelectable = new BooleanState(false); #isSelectable = new UmbBooleanState(false);
isSelectable = this.#isSelectable.asObservable(); isSelectable = this.#isSelectable.asObservable();
#isSelected = new BooleanState(false); #isSelected = new UmbBooleanState(false);
isSelected = this.#isSelected.asObservable(); isSelected = this.#isSelected.asObservable();
#isActive = new BooleanState(false); #isActive = new UmbBooleanState(false);
isActive = this.#isActive.asObservable(); isActive = this.#isActive.asObservable();
#hasActions = new BooleanState(false); #hasActions = new UmbBooleanState(false);
hasActions = this.#hasActions.asObservable(); hasActions = this.#hasActions.asObservable();
#path = new StringState(''); #path = new UmbStringState('');
path = this.#path.asObservable(); path = this.#path.asObservable();
treeContext?: UmbTreeContextBase; treeContext?: UmbTreeContextBase;

View File

@@ -1,7 +1,7 @@
import type { Observable } from 'rxjs'; import type { Observable } from 'rxjs';
import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry'; import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
import { DeepState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbDeepState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { createExtensionClass, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { createExtensionClass, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
@@ -18,10 +18,10 @@ export class UmbTreeContextBase implements UmbTreeContext {
host: UmbControllerHostElement; host: UmbControllerHostElement;
public tree: ManifestTree; public tree: ManifestTree;
#selectable = new DeepState(false); #selectable = new UmbDeepState(false);
public readonly selectable = this.#selectable.asObservable(); public readonly selectable = this.#selectable.asObservable();
#selection = new DeepState(<Array<string>>[]); #selection = new UmbDeepState(<Array<string>>[]);
public readonly selection = this.#selection.asObservable(); public readonly selection = this.#selection.asObservable();
repository?: UmbTreeRepository; repository?: UmbTreeRepository;

View File

@@ -3,7 +3,12 @@ import { UmbWorkspaceVariableEntityContextInterface } from '../workspace/workspa
import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../workspace/workspace-variant/workspace-variant.context'; import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../workspace/workspace-variant/workspace-variant.context';
import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { ClassState, ObjectState, StringState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import {
UmbClassState,
UmbObjectState,
UmbStringState,
UmbObserverController,
} from '@umbraco-cms/backoffice/observable-api';
import { import {
UmbContextConsumerController, UmbContextConsumerController,
UmbContextProviderController, UmbContextProviderController,
@@ -25,7 +30,7 @@ export class UmbWorkspacePropertyContext<ValueType = any> {
private _providerController: UmbContextProviderController; private _providerController: UmbContextProviderController;
private _data = new ObjectState<WorkspacePropertyData<ValueType>>({}); private _data = new UmbObjectState<WorkspacePropertyData<ValueType>>({});
public readonly alias = this._data.getObservablePart((data) => data.alias); public readonly alias = this._data.getObservablePart((data) => data.alias);
public readonly label = this._data.getObservablePart((data) => data.label); public readonly label = this._data.getObservablePart((data) => data.label);
@@ -35,10 +40,10 @@ export class UmbWorkspacePropertyContext<ValueType = any> {
#workspaceVariantId?: UmbVariantId; #workspaceVariantId?: UmbVariantId;
#variantId = new ClassState<UmbVariantId | undefined>(undefined); #variantId = new UmbClassState<UmbVariantId | undefined>(undefined);
public readonly variantId = this.#variantId.asObservable(); public readonly variantId = this.#variantId.asObservable();
private _variantDifference = new StringState(undefined); private _variantDifference = new UmbStringState(undefined);
public readonly variantDifference = this._variantDifference.asObservable(); public readonly variantDifference = this._variantDifference.asObservable();
private _workspaceContext?: UmbWorkspaceVariableEntityContextInterface; private _workspaceContext?: UmbWorkspaceVariableEntityContextInterface;
@@ -88,7 +93,7 @@ export class UmbWorkspacePropertyContext<ValueType = any> {
this._data.update({ description }); this._data.update({ description });
} }
public setValue(value: WorkspacePropertyData<ValueType>['value']) { public setValue(value: WorkspacePropertyData<ValueType>['value']) {
// Note: Do not try to compare new / old value, as it can of any type. We trust the ObjectState in doing such. // Note: Do not try to compare new / old value, as it can of any type. We trust the UmbObjectState in doing such.
this._data.update({ value }); this._data.update({ value });
} }
public changeValue(value: WorkspacePropertyData<ValueType>['value']) { public changeValue(value: WorkspacePropertyData<ValueType>['value']) {

Some files were not shown because too many files have changed in this diff Show More