Merge remote-tracking branch 'origin/main' into feature/file-upload-temp

This commit is contained in:
Lone Iversen
2023-11-22 11:27:57 +01:00
203 changed files with 4118 additions and 1702 deletions

View File

@@ -51,6 +51,11 @@
"exceptions": ["@umbraco-cms", "@open-wc/testing", "@storybook", "msw", "."]
}
],
"local-rules/exported-string-constant-naming": ["error",
{
"excludedFileNames": ["umbraco-package", "input-tiny-mce.defaults"] // TODO: what to do about the tiny mce defaults?
}
],
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn"

View File

@@ -13,7 +13,7 @@ import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext } from '../src/
import { UmbDataTypeTreeStore } from '../src/packages/core/data-type/tree/data-type.tree.store';
import { UmbDocumentStore } from '../src/packages/documents/documents/repository/document.store';
import { UmbDocumentTreeStore } from '../src/packages/documents/documents/repository/document.tree.store';
import { UmbDocumentTypeStore } from '../src/packages/documents/document-types/repository/document-type.store';
import { UmbDocumentTypeDetailStore } from '../src/packages/documents/document-types/repository/detail/document-type-detail.store';
import { umbExtensionsRegistry } from '../src/packages/core/extension-registry';
import { UmbIconRegistry } from '../src/shared/icon-registry/icon.registry';
import { UmbLitElement } from '../src/shared/lit-element';

View File

@@ -0,0 +1,39 @@
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Ensure all exported string constants should be in uppercase with words separated by underscores and prefixed with UMB_',
},
},
create: function (context) {
const excludedFileNames = context.options[0]?.excludedFileNames || [];
return {
ExportNamedDeclaration(node) {
const fileName = context.getFilename();
if (excludedFileNames.some((excludedFileName) => fileName.includes(excludedFileName))) {
// Skip the rule check for files in the excluded list
return;
}
if (node.declaration && node.declaration.type === 'VariableDeclaration') {
const declaration = node.declaration.declarations[0];
const { id, init } = declaration;
if (id && id.type === 'Identifier' && init && init.type === 'Literal' && typeof init.value === 'string') {
const isValidName = /^[A-Z]+(_[A-Z]+)*$/.test(id.name);
if (!isValidName || !id.name.startsWith('UMB_')) {
context.report({
node: id,
message:
'Exported string constant should be in uppercase with words separated by underscores and prefixed with UMB_',
});
}
}
}
},
};
},
};

View File

@@ -18,4 +18,5 @@ module.exports = {
'prefer-import-aliases': preferImportAliasesRule,
'prefer-static-styles-last': preferStaticStylesLastRule,
'umb-class-prefix': umbClassPrefixRule,
'exported-string-constant-naming': require('./devops/eslint/rules/exported-string-constant-naming.cjs'),
};

View File

@@ -21,7 +21,7 @@
"marked": "^9.1.0",
"monaco-editor": "^0.41.0",
"rxjs": "^7.8.1",
"tinymce": "^6.7.1",
"tinymce": "^6.7.3",
"tinymce-i18n": "^23.8.7",
"uuid": "^9.0.0"
},
@@ -20560,9 +20560,9 @@
"dev": true
},
"node_modules/tinymce": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.7.1.tgz",
"integrity": "sha512-SIGJgWk2d/X59VbO+i81QfNx2EP1P5t+sza2/1So3OLGtmMBhEJMag7sN/Mo8sq4s0niwb65Z51yLju32jP11g=="
"version": "6.7.3",
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.7.3.tgz",
"integrity": "sha512-J7WmYIi/gt1RvZ6Ap2oQiUjzAoiS9pfV+d4GnKuZuPu8agmlAEAInNmMvMjfCNBzHv4JnZXY7qlHUAI0IuYQVA=="
},
"node_modules/tinymce-i18n": {
"version": "23.8.7",

View File

@@ -139,7 +139,7 @@
"marked": "^9.1.0",
"monaco-editor": "^0.41.0",
"rxjs": "^7.8.1",
"tinymce": "^6.7.1",
"tinymce": "^6.7.3",
"tinymce-i18n": "^23.8.7",
"uuid": "^9.0.0"
},

View File

@@ -2,23 +2,25 @@ import { expect, oneEvent } from '@open-wc/testing';
import { UmbContextProvider } from '../provide/context-provider.js';
import { UmbContextToken } from '../token/context-token.js';
import { UmbContextConsumer } from './context-consumer.js';
import { UmbContextRequestEventImplementation, umbContextRequestEventType } from './context-request.event.js';
import { UmbContextRequestEventImplementation, UMB_CONTENT_REQUEST_EVENT_TYPE } from './context-request.event.js';
const testContextAlias = 'my-test-context';
const testContextAliasAndApiAlias = 'my-test-context#testApi';
const testContextAliasAndNotExstingApiAlias = 'my-test-context#notExistingTestApi';
class UmbTestContextConsumerClass {
public prop: string = 'value from provider';
}
describe('UmbContextConsumer', () => {
let consumer: UmbContextConsumer;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
consumer = new UmbContextConsumer(document.body, testContextAlias, () => {});
});
describe('Public API', () => {
let consumer: UmbContextConsumer;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
consumer = new UmbContextConsumer(document.body, testContextAlias, () => {});
});
describe('properties', () => {
it('has a instance property', () => {
expect(consumer).to.have.property('instance').that.is.undefined;
@@ -32,143 +34,191 @@ describe('UmbContextConsumer', () => {
describe('events', () => {
it('dispatches context request event when constructed', async () => {
const listener = oneEvent(window, umbContextRequestEventType);
const listener = oneEvent(window, UMB_CONTENT_REQUEST_EVENT_TYPE);
consumer.hostConnected();
const event = (await listener) as unknown as UmbContextRequestEventImplementation;
expect(event).to.exist;
expect(event.type).to.eq(umbContextRequestEventType);
expect(event.type).to.eq(UMB_CONTENT_REQUEST_EVENT_TYPE);
expect(event.contextAlias).to.eq(testContextAlias);
consumer.hostDisconnected();
});
});
});
it('works with UmbContextProvider', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
describe('Simple implementation', () => {
it('works with UmbContextProvider', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextConsumerClass | undefined) => {
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextConsumerClass | undefined) => {
if (_instance) {
expect(_instance.prop).to.eq('value from provider');
done();
localConsumer.hostDisconnected();
provider.hostDisconnected();
}
},
);
localConsumer.hostConnected();
});
/*
Unprovided feature is out commented currently. I'm not sure there is a use case. So lets leave the code around until we know for sure.
it('acts to Context API disconnected', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
let callbackNum = 0;
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextConsumerClass | undefined) => {
callbackNum++;
if (callbackNum === 1) {
expect(_instance?.prop).to.eq('value from provider');
// unregister.
provider.hostDisconnected();
} else if (callbackNum === 2) {
expect(_instance?.prop).to.be.undefined;
done();
}
}
);
localConsumer.hostConnected();
});
*/
});
describe('Implementation with Api Alias', () => {
it('responds when api alias matches', (done) => {
const provider = new UmbContextProvider(
document.body,
testContextAliasAndApiAlias,
new UmbTestContextConsumerClass(),
);
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, testContextAliasAndApiAlias, (_instance) => {
if (_instance) {
expect((_instance as UmbTestContextConsumerClass).prop).to.eq('value from provider');
localConsumer.hostDisconnected();
provider.hostDisconnected();
done();
}
});
localConsumer.hostConnected();
});
it('does not respond to a non existing api alias', (done) => {
const provider = new UmbContextProvider(
document.body,
testContextAliasAndApiAlias,
new UmbTestContextConsumerClass(),
);
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(element, testContextAliasAndNotExstingApiAlias, () => {
expect(false).to.be.true;
});
localConsumer.hostConnected();
// Delayed check to make sure the callback is not called.
Promise.resolve().then(() => {
localConsumer.hostDisconnected();
provider.hostDisconnected();
done();
});
});
});
describe('Implementation with discriminator method', () => {
type A = { prop: string };
function discriminator(instance: unknown): instance is A {
return typeof (instance as any).prop === 'string';
}
function badDiscriminator(instance: unknown): instance is A {
return typeof (instance as any).notExistingProp === 'string';
}
it('discriminator determines the instance type', async () => {
const localConsumer = new UmbContextConsumer(
document.body,
new UmbContextToken(testContextAlias, undefined, discriminator),
(instance: A) => {
console.log(instance);
},
);
localConsumer.hostConnected();
// This bit of code is not really a test but it serves as a TypeScript type test, making sure the given type is matches the one given from the Discriminator method.
type TestType = Exclude<typeof localConsumer.instance, undefined> extends A ? true : never;
const test: TestType = true;
expect(test).to.be.true;
localConsumer.destroy();
});
it('approving discriminator still fires callback', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
new UmbContextToken(testContextAlias, undefined, discriminator),
(_instance) => {
expect(_instance.prop).to.eq('value from provider');
done();
localConsumer.hostDisconnected();
provider.hostDisconnected();
}
}
);
localConsumer.hostConnected();
});
},
);
localConsumer.hostConnected();
});
/*
Unprovided feature is out commented currently. I'm not sure there is a use case. So lets leave the code around until we know for sure.
it('acts to Context API disconnected', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
it('disapproving discriminator does not fire callback', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const element = document.createElement('div');
document.body.appendChild(element);
let callbackNum = 0;
const localConsumer = new UmbContextConsumer(
element,
new UmbContextToken(testContextAlias, undefined, badDiscriminator),
(_instance) => {
expect(_instance.prop).to.eq('this must not happen!');
},
);
localConsumer.hostConnected();
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextConsumerClass | undefined) => {
callbackNum++;
if (callbackNum === 1) {
expect(_instance?.prop).to.eq('value from provider');
// unregister.
provider.hostDisconnected();
} else if (callbackNum === 2) {
expect(_instance?.prop).to.be.undefined;
done();
}
}
);
localConsumer.hostConnected();
});
*/
});
describe('UmbContextConsumer with discriminator test', () => {
type A = { prop: string };
function discriminator(instance: unknown): instance is A {
return typeof (instance as any).prop === 'string';
}
function badDiscriminator(instance: unknown): instance is A {
return typeof (instance as any).notExistingProp === 'string';
}
it('discriminator determines the instance type', async () => {
const localConsumer = new UmbContextConsumer(
document.body,
new UmbContextToken(testContextAlias, discriminator),
(instance: A) => { console.log(instance)}
);
localConsumer.hostConnected();
// This bit of code is not really a test but it serves as a TypeScript type test, making sure the given type is matches the one given from the Discriminator method.
type TestType = Exclude<(typeof localConsumer.instance), undefined> extends A ? true : never;
const test: TestType = true;
expect(test).to.be.true;
localConsumer.destroy();
});
it('approving discriminator still fires callback', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
new UmbContextToken(testContextAlias, discriminator),
(_instance) => {
expect(_instance.prop).to.eq('value from provider');
Promise.resolve().then(() => {
done();
localConsumer.hostDisconnected();
provider.hostDisconnected();
}
);
localConsumer.hostConnected();
});
it('disapproving discriminator does not fire callback', (done) => {
const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass());
provider.hostConnected();
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
new UmbContextToken(testContextAlias, badDiscriminator),
(_instance) => {
expect(_instance.prop).to.eq('this must not happen!');
}
);
localConsumer.hostConnected();
Promise.resolve().then(() => {
done();
localConsumer.hostDisconnected();
provider.hostDisconnected();
});
});
});
});

View File

@@ -2,7 +2,7 @@ import { UmbContextDiscriminator, UmbContextToken } from '../token/context-token
import {
isUmbContextProvideEventType,
//isUmbContextUnprovidedEventType,
umbContextProvideEventType,
UMB_CONTEXT_PROVIDE_EVENT_TYPE,
//umbContextUnprovidedEventType,
} from '../provide/context-provide.event.js';
import { UmbContextRequestEventImplementation, UmbContextCallback } from './context-request.event.js';
@@ -11,9 +11,7 @@ import { UmbContextRequestEventImplementation, UmbContextCallback } from './cont
* @export
* @class UmbContextConsumer
*/
export class UmbContextConsumer<
BaseType = unknown,
ResultType extends BaseType = BaseType> {
export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType = BaseType> {
#callback?: UmbContextCallback<ResultType>;
#promise?: Promise<ResultType>;
#promiseResolver?: (instance: ResultType) => void;
@@ -24,33 +22,36 @@ ResultType extends BaseType = BaseType> {
}
#contextAlias: string;
#apiAlias: string;
#discriminator?: UmbContextDiscriminator<BaseType, ResultType>;
/**
* Creates an instance of UmbContextConsumer.
* @param {EventTarget} hostElement
* @param {string} contextAlias
* @param {string} contextIdentifier
* @param {UmbContextCallback} callback
* @memberof UmbContextConsumer
*/
constructor(
protected hostElement: EventTarget,
contextAlias: string | UmbContextToken<BaseType, ResultType>,
callback?: UmbContextCallback<ResultType>
contextIdentifier: string | UmbContextToken<BaseType, ResultType>,
callback?: UmbContextCallback<ResultType>,
) {
this.#contextAlias = contextAlias.toString();
const idSplit = contextIdentifier.toString().split('#');
this.#contextAlias = idSplit[0];
this.#apiAlias = idSplit[1] ?? 'default';
this.#callback = callback;
this.#discriminator = (contextAlias as UmbContextToken<BaseType, ResultType>).getDiscriminator?.();
this.#discriminator = (contextIdentifier as UmbContextToken<BaseType, ResultType>).getDiscriminator?.();
}
protected _onResponse = (instance: BaseType): boolean => {
if (this.#instance === instance) {
return false;
return true;
}
if(this.#discriminator) {
if (this.#discriminator) {
// Notice if discriminator returns false, we do not want to setInstance.
if(this.#discriminator(instance)) {
if (this.#discriminator(instance)) {
this.setInstance(instance as unknown as ResultType);
return true;
}
@@ -70,6 +71,11 @@ ResultType extends BaseType = BaseType> {
}
}
/**
* @public
* @memberof UmbContextConsumer
* @description Get the context as a promise.
*/
public asPromise() {
return (
this.#promise ??
@@ -80,23 +86,25 @@ ResultType extends BaseType = BaseType> {
}
/**
* @public
* @memberof UmbContextConsumer
* @description Request the context from the host element.
*/
public request() {
const event = new UmbContextRequestEventImplementation(this.#contextAlias, this._onResponse);
const event = new UmbContextRequestEventImplementation(this.#contextAlias, this.#apiAlias, this._onResponse);
this.hostElement.dispatchEvent(event);
}
public hostConnected() {
// TODO: We need to use closets application element. We need this in order to have separate Backoffice running within or next to each other.
window.addEventListener(umbContextProvideEventType, this.#handleNewProvider);
window.addEventListener(UMB_CONTEXT_PROVIDE_EVENT_TYPE, this.#handleNewProvider);
//window.addEventListener(umbContextUnprovidedEventType, this.#handleRemovedProvider);
this.request();
}
public hostDisconnected() {
// TODO: We need to use closets application element. We need this in order to have separate Backoffice running within or next to each other.
window.removeEventListener(umbContextProvideEventType, this.#handleNewProvider);
window.removeEventListener(UMB_CONTEXT_PROVIDE_EVENT_TYPE, this.#handleNewProvider);
//window.removeEventListener(umbContextUnprovidedEventType, this.#handleRemovedProvider);
}
@@ -128,7 +136,6 @@ ResultType extends BaseType = BaseType> {
}
*/
// TODO: Test destroy scenarios:
public destroy() {
this.hostDisconnected();
this.#callback = undefined;

View File

@@ -9,13 +9,18 @@ describe('UmbContextRequestEvent', () => {
const event: UmbContextRequestEvent = new UmbContextRequestEventImplementation(
'my-test-context-alias',
contextRequestCallback
'my-test-api-alias',
contextRequestCallback,
);
it('has context', () => {
it('has context alias', () => {
expect(event.contextAlias).to.eq('my-test-context-alias');
});
it('has api alias', () => {
expect(event.apiAlias).to.eq('my-test-api-alias');
});
it('has a callback', () => {
expect(event.callback).to.eq(contextRequestCallback);
});

View File

@@ -1,7 +1,5 @@
import { UmbContextToken } from '../token/context-token.js';
export const umbContextRequestEventType = 'umb:context-request';
export const umbDebugContextEventType = 'umb:debug-contexts';
export const UMB_CONTENT_REQUEST_EVENT_TYPE = 'umb:context-request';
export const UMB_DEBUG_CONTEXT_EVENT_TYPE = 'umb:debug-contexts';
export type UmbContextCallback<T> = (instance: T) => void;
@@ -10,7 +8,8 @@ export type UmbContextCallback<T> = (instance: T) => void;
* @interface UmbContextRequestEvent
*/
export interface UmbContextRequestEvent<ResultType = unknown> extends Event {
readonly contextAlias: string | UmbContextToken<unknown, ResultType>;
readonly contextAlias: string;
readonly apiAlias: string;
readonly callback: (context: ResultType) => boolean;
}
@@ -20,17 +19,21 @@ export interface UmbContextRequestEvent<ResultType = unknown> extends Event {
* @extends {Event}
* @implements {UmbContextRequestEvent}
*/
export class UmbContextRequestEventImplementation<ResultType = unknown> extends Event implements UmbContextRequestEvent<ResultType> {
export class UmbContextRequestEventImplementation<ResultType = unknown>
extends Event
implements UmbContextRequestEvent<ResultType>
{
public constructor(
public readonly contextAlias: string | UmbContextToken<any, ResultType>,
public readonly callback: (context: ResultType) => boolean
public readonly contextAlias: string,
public readonly apiAlias: string,
public readonly callback: (context: ResultType) => boolean,
) {
super(umbContextRequestEventType, { bubbles: true, composed: true, cancelable: true });
super(UMB_CONTENT_REQUEST_EVENT_TYPE, { bubbles: true, composed: true, cancelable: true });
}
}
export class UmbContextDebugRequest extends Event {
public constructor(public readonly callback: any) {
super(umbDebugContextEventType, { bubbles: true, composed: true, cancelable: false });
super(UMB_DEBUG_CONTEXT_EVENT_TYPE, { bubbles: true, composed: true, cancelable: false });
}
}

View File

@@ -1,6 +1,6 @@
import { UmbContextToken } from '../token/context-token.js';
export const umbContextProvideEventType = 'umb:context-provide';
export const UMB_CONTEXT_PROVIDE_EVENT_TYPE = 'umb:context-provide';
/**
* @export
@@ -18,15 +18,15 @@ export interface UmbContextProvideEvent extends Event {
*/
export class UmbContextProvideEventImplementation extends Event implements UmbContextProvideEvent {
public constructor(public readonly contextAlias: string | UmbContextToken) {
super(umbContextProvideEventType, { bubbles: true, composed: true });
super(UMB_CONTEXT_PROVIDE_EVENT_TYPE, { bubbles: true, composed: true });
}
}
export const isUmbContextProvideEventType = (event: Event): event is UmbContextProvideEventImplementation => {
return event.type === umbContextProvideEventType;
return event.type === UMB_CONTEXT_PROVIDE_EVENT_TYPE;
};
export const umbContextUnprovidedEventType = 'umb:context-unprovided';
export const UMB_CONTEXT_UNPROVIDED_EVENT_TYPE = 'umb:context-unprovided';
/**
* @export
@@ -44,11 +44,14 @@ export interface UmbContextUnprovidedEvent extends Event {
* @implements {UmbContextUnprovidedEvent}
*/
export class UmbContextUnprovidedEventImplementation extends Event implements UmbContextUnprovidedEvent {
public constructor(public readonly contextAlias: string | UmbContextToken, public readonly instance: unknown) {
super(umbContextUnprovidedEventType, { bubbles: true, composed: true });
public constructor(
public readonly contextAlias: string | UmbContextToken,
public readonly instance: unknown,
) {
super(UMB_CONTEXT_UNPROVIDED_EVENT_TYPE, { bubbles: true, composed: true });
}
}
export const isUmbContextUnprovidedEventType = (event: Event): event is UmbContextUnprovidedEventImplementation => {
return event.type === umbContextUnprovidedEventType;
return event.type === UMB_CONTEXT_UNPROVIDED_EVENT_TYPE;
};

View File

@@ -42,11 +42,12 @@ describe('UmbContextProvider', () => {
it('handles context request events', (done) => {
const event = new UmbContextRequestEventImplementation(
'my-test-context',
'default',
(_instance: UmbTestContextProviderClass) => {
expect(_instance.prop).to.eq('value from provider');
done();
return true;
}
},
);
document.body.dispatchEvent(event);
@@ -63,7 +64,7 @@ describe('UmbContextProvider', () => {
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.hostDisconnected();
}
},
);
localConsumer.hostConnected();
});

View File

@@ -1,7 +1,7 @@
import {
UmbContextRequestEvent,
umbContextRequestEventType,
umbDebugContextEventType,
UMB_CONTENT_REQUEST_EVENT_TYPE,
UMB_DEBUG_CONTEXT_EVENT_TYPE,
} from '../consume/context-request.event.js';
import { UmbContextToken } from '../token/context-token.js';
import {
@@ -17,6 +17,7 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
protected hostElement: EventTarget;
protected _contextAlias: string;
protected _apiAlias: string;
#instance: unknown;
/**
@@ -31,13 +32,20 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
/**
* Creates an instance of UmbContextProvider.
* @param {EventTarget} host
* @param {string} contextAlias
* @param {string | UmbContextToken} contextIdentifier
* @param {*} instance
* @memberof UmbContextProvider
*/
constructor(hostElement: EventTarget, contextAlias: string | UmbContextToken<BaseType, ResultType>, instance: ResultType) {
constructor(
hostElement: EventTarget,
contextIdentifier: string | UmbContextToken<BaseType, ResultType>,
instance: ResultType,
) {
this.hostElement = hostElement;
this._contextAlias = contextAlias.toString();
const idSplit = contextIdentifier.toString().split('#');
this._contextAlias = idSplit[0];
this._apiAlias = idSplit[1] ?? 'default';
this.#instance = instance;
}
@@ -52,7 +60,8 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
// Since the alias matches, we will stop it from bubbling further up. But we still allow it to ask the other Contexts of the element. Hence not calling `event.stopImmediatePropagation();`
event.stopPropagation();
if(event.callback(this.#instance)) {
// First and importantly, check that the apiAlias matches and then call the callback. If that returns true then we can stop the event completely.
if (this._apiAlias === event.apiAlias && event.callback(this.#instance)) {
// Make sure the event not hits any more Contexts as we have found a match.
event.stopImmediatePropagation();
}
@@ -62,23 +71,23 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
* @memberof UmbContextProvider
*/
public hostConnected() {
this.hostElement.addEventListener(umbContextRequestEventType, this.#handleContextRequest);
this.hostElement.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
this.hostElement.dispatchEvent(new UmbContextProvideEventImplementation(this._contextAlias));
// Listen to our debug event 'umb:debug-contexts'
this.hostElement.addEventListener(umbDebugContextEventType, this._handleDebugContextRequest);
this.hostElement.addEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this._handleDebugContextRequest);
}
/**
* @memberof UmbContextProvider
*/
public hostDisconnected() {
this.hostElement.removeEventListener(umbContextRequestEventType, this.#handleContextRequest);
this.hostElement.removeEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
// Out-commented for now, but kept if we like to reintroduce this:
//window.dispatchEvent(new UmbContextUnprovidedEventImplementation(this._contextAlias, this.#instance));
// Stop listen to our debug event 'umb:debug-contexts'
this.hostElement.removeEventListener(umbDebugContextEventType, this._handleDebugContextRequest);
this.hostElement.removeEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this._handleDebugContextRequest);
}
private _handleDebugContextRequest = (event: any) => {

View File

@@ -4,57 +4,164 @@ import { UmbContextProvider } from '../provide/context-provider.js';
import { UmbContextToken } from './context-token.js';
const testContextAlias = 'my-test-context';
const testApiAlias = 'my-test-api';
class UmbTestContextTokenClass {
prop = 'value from provider';
}
describe('UmbContextToken', () => {
const contextToken = new UmbContextToken<UmbTestContextTokenClass>(testContextAlias);
const typedProvider = new UmbContextProvider(document.body, contextToken, new UmbTestContextTokenClass());
typedProvider.hostConnected();
describe('Simple context token', () => {
const contextToken = new UmbContextToken<UmbTestContextTokenClass>(testContextAlias);
const typedProvider = new UmbContextProvider(document.body, contextToken, new UmbTestContextTokenClass());
typedProvider.hostConnected();
after(() => {
typedProvider.hostDisconnected();
after(() => {
typedProvider.hostDisconnected();
});
it('toString returns the alias', () => {
expect(contextToken.toString()).to.eq(testContextAlias + '#' + 'default');
});
it('can be used to consume a context API', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
contextToken,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
it('gives the same result when using the string alias', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
});
it('toString returns the alias', () => {
expect(contextToken.toString()).to.eq(testContextAlias);
describe('Context Token with alternative api alias', () => {
const contextToken = new UmbContextToken<UmbTestContextTokenClass>(testContextAlias, testApiAlias);
const typedProvider = new UmbContextProvider(document.body, contextToken, new UmbTestContextTokenClass());
typedProvider.hostConnected();
after(() => {
typedProvider.hostDisconnected();
});
it('toString returns the alias', () => {
expect(contextToken.toString()).to.eq(testContextAlias + '#' + testApiAlias);
});
it('can be used to consume a context API', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
contextToken,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
it('gives the same result when using the string alias', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
});
it('can be used to consume a context API', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
contextToken,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
}
);
localConsumer.hostConnected();
});
it('gives the same result when using the string alias', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
describe('Context Token with discriminator method', () => {
const contextToken = new UmbContextToken<UmbTestContextTokenClass>(
testContextAlias,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
}
undefined,
(instance): instance is UmbTestContextTokenClass => instance.prop === 'value from provider',
);
const typedProvider = new UmbContextProvider(document.body, contextToken, new UmbTestContextTokenClass());
typedProvider.hostConnected();
localConsumer.hostConnected();
after(() => {
typedProvider.hostDisconnected();
});
it('toString returns the alias', () => {
expect(contextToken.toString()).to.eq(testContextAlias + '#' + 'default');
});
it('can be used to consume a context API', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
contextToken,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
it('gives the same result when using the string alias', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
const localConsumer = new UmbContextConsumer(
element,
testContextAlias,
(_instance: UmbTestContextTokenClass | undefined) => {
expect(_instance).to.be.instanceOf(UmbTestContextTokenClass);
expect(_instance?.prop).to.eq('value from provider');
done();
localConsumer.destroy(); // We do not want to react to when the provider is disconnected.
},
);
localConsumer.hostConnected();
});
});
});

View File

@@ -1,10 +1,14 @@
export type UmbContextDiscriminator<BaseType, DiscriminatorResult extends BaseType> = (
instance: BaseType,
) => instance is DiscriminatorResult;
export type UmbContextDiscriminator<BaseType, DiscriminatorResult extends BaseType> = (instance: BaseType) => instance is DiscriminatorResult;
export class UmbContextToken<
BaseType = unknown,
ResultType extends BaseType = BaseType> {
/**
* @export
* @class UmbContextToken
* @template BaseType - A generic type of the API before decimated.
* @template ResultType - A concrete type of the API after decimation, use this when you apply a discriminator method. Note this is optional and defaults to the BaseType.
*/
export class UmbContextToken<BaseType = unknown, ResultType extends BaseType = BaseType> {
#discriminator: UmbContextDiscriminator<BaseType, ResultType> | undefined;
/**
* Get the type of the token
@@ -18,12 +22,23 @@ ResultType extends BaseType = BaseType> {
readonly TYPE: ResultType = undefined as never;
/**
* @param alias Unique identifier for the token
* @param contextAlias Unique identifier for the context
* @param apiAlias Unique identifier for the api
* @param discriminator A discriminator that will be used to discriminate the API — testing if the API lives up to a certain requirement. If the API does not meet the requirement then the consumer will not receive this API.
*/
constructor(protected alias: string, discriminator?: UmbContextDiscriminator<BaseType, ResultType>) {
constructor(
protected contextAlias: string,
protected apiAlias: string = 'default',
discriminator?: UmbContextDiscriminator<BaseType, ResultType>,
) {
this.#discriminator = discriminator;
}
/**
* Get the discriminator method for the token
*
* @returns the discriminator method
*/
getDiscriminator(): UmbContextDiscriminator<BaseType, ResultType> | undefined {
return this.#discriminator;
}
@@ -35,8 +50,6 @@ ResultType extends BaseType = BaseType> {
* @returns the unique alias of the token
*/
toString(): string {
return this.alias;
return this.contextAlias + '#' + this.apiAlias;
}
}

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const copyHandlers = [
rest.post(umbracoPath(`${slug}/:id/copy`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}/:id/copy`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const detailHandlers = [
rest.post(umbracoPath(`${slug}`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -13,7 +13,7 @@ export const detailHandlers = [
return res(ctx.status(200));
}),
rest.get(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -22,7 +22,7 @@ export const detailHandlers = [
return res(ctx.status(200), ctx.json(dataType));
}),
rest.put(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -33,7 +33,7 @@ export const detailHandlers = [
return res(ctx.status(200), ctx.json(saved));
}),
rest.delete<string>(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.delete<string>(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import { ProblemDetails } from '@umbraco-cms/backoffice/backend-api';
export const folderHandlers = [
rest.post(umbracoPath(`${slug}/folder`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}/folder`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -14,7 +14,7 @@ export const folderHandlers = [
return res(ctx.status(200));
}),
rest.get(umbracoPath(`${slug}/folder/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/folder/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -23,7 +23,7 @@ export const folderHandlers = [
return res(ctx.status(200), ctx.json(dataType));
}),
rest.put(umbracoPath(`${slug}/folder/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/folder/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -34,7 +34,7 @@ export const folderHandlers = [
return res(ctx.status(200));
}),
rest.delete(umbracoPath(`${slug}/folder/:id`), async (req, res, ctx) => {
rest.delete(umbracoPath(`${UMB_SLUG}/folder/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -48,7 +48,7 @@ export const folderHandlers = [
status: 404,
type: 'error',
detail: 'Not Found',
})
}),
);
}
}),

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const itemHandlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbDataTypeData.getItems(ids);

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const moveHandlers = [
rest.post(umbracoPath(`${slug}/:id/move`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}/:id/move`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1 +1 @@
export const slug = '/data-type';
export const UMB_SLUG = '/data-type';

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDataTypeData } from '../../data/data-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const treeHandlers = [
rest.get(umbracoPath(`/tree${slug}/root`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => {
const rootItems = umbDataTypeData.getTreeRoot();
const response = {
total: rootItems.length,
@@ -13,7 +13,7 @@ export const treeHandlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get(umbracoPath(`/tree${slug}/children`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => {
const parentId = req.url.searchParams.get('parentId');
if (!parentId) return;

View File

@@ -1,24 +1,24 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const document = umbDocumentTypeData.getById(id);
return res(ctx.status(200), ctx.json(document));
}),
rest.delete(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.delete(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const document = umbDocumentTypeData.getById(id);
return res(ctx.status(200), ctx.json(document));
}),
rest.put(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -27,7 +27,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(saved));
}),
rest.post(umbracoPath(`${slug}`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
umbDocumentTypeData.insert(data);

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbDocumentTypeData.getItems(ids);

View File

@@ -1 +1 @@
export const slug = '/document-type';
export const UMB_SLUG = '/document-type';

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentTypeData } from '../../data/document-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`/tree${slug}/root`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => {
const rootItems = umbDocumentTypeData.getTreeRoot();
const response = {
total: rootItems.length,
@@ -13,7 +13,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get(umbracoPath(`/tree${slug}/children`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => {
const parentId = req.url.searchParams.get('parentId');
if (!parentId) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentData } from '../../data/document.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbDocumentData.getItems(ids);

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbDocumentData } from '../../data/document.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
// TODO: temp handlers until we have a real API
export const handlers = [
rest.get(umbracoPath(`${slug}/:id/permissions`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id/permissions`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const response = umbDocumentData.getUserPermissionsForDocument(id);

View File

@@ -1 +1 @@
export const slug = '/document';
export const UMB_SLUG = '/document';

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbMediaTypeData } from '../../data/media-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const detailHandlers = [
rest.post(umbracoPath(`${slug}`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -13,7 +13,7 @@ export const detailHandlers = [
return res(ctx.status(200));
}),
rest.get(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -22,7 +22,7 @@ export const detailHandlers = [
return res(ctx.status(200), ctx.json(data));
}),
rest.put(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -33,7 +33,7 @@ export const detailHandlers = [
return res(ctx.status(200), ctx.json(saved));
}),
rest.delete<string>(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.delete<string>(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1,17 +1,17 @@
const { rest } = window.MockServiceWorker;
import { umbMediaTypeData } from '../../data/media-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const itemHandlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbMediaTypeData.getItems(ids);
return res(ctx.status(200), ctx.json(items));
}),
rest.get(umbracoPath(`/tree${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbMediaTypeData.getTreeItems(ids);

View File

@@ -1 +1 @@
export const slug = '/media-type';
export const UMB_SLUG = '/media-type';

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbMediaTypeData } from '../../data/media-type.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const treeHandlers = [
rest.get(umbracoPath(`/tree${slug}/root`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/root`), (req, res, ctx) => {
const rootItems = umbMediaTypeData.getTreeRoot();
const response = {
total: rootItems.length,
@@ -13,7 +13,7 @@ export const treeHandlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get(umbracoPath(`/tree${slug}/children`), (req, res, ctx) => {
rest.get(umbracoPath(`/tree${UMB_SLUG}/children`), (req, res, ctx) => {
const parentId = req.url.searchParams.get('parentId');
if (!parentId) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUserGroupData } from '../../data/user-group.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const collectionHandlers = [
rest.get(umbracoPath(`${slug}`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}`), (req, res, ctx) => {
const response = umbUserGroupData.getAll();
return res(ctx.status(200), ctx.json(response));
}),

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUserGroupData } from '../../data/user-group.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const detailHandlers = [
rest.post(umbracoPath(`${slug}`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -13,7 +13,7 @@ export const detailHandlers = [
return res(ctx.status(200));
}),
rest.get(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -22,7 +22,7 @@ export const detailHandlers = [
return res(ctx.status(200), ctx.json(dataType));
}),
rest.put(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -33,7 +33,7 @@ export const detailHandlers = [
return res(ctx.status(200));
}),
rest.delete<string>(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.delete<string>(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUserGroupData } from '../../data/user-group.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const itemHandlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbUserGroupData.getItems(ids);

View File

@@ -1 +1 @@
export const slug = '/user-group';
export const UMB_SLUG = '/user-group';

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { ChangePasswordUserRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post<ChangePasswordUserRequestModel>(umbracoPath(`${slug}/change-password/:id`), async (req, res, ctx) => {
rest.post<ChangePasswordUserRequestModel>(umbracoPath(`${UMB_SLUG}/change-password/:id`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
if (!data.newPassword) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/current`), (_req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/current`), (_req, res, ctx) => {
const loggedInUser = umbUsersData.getCurrentUser();
return res(ctx.status(200), ctx.json(loggedInUser));
}),

View File

@@ -1,15 +1,15 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}`), (req, res, ctx) => {
const response = umbUsersData.getAll();
return res(ctx.status(200), ctx.json(response));
}),
rest.post(umbracoPath(`${slug}`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -18,7 +18,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get(umbracoPath(`${slug}/:id`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/:id`), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -27,7 +27,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(item));
}),
rest.put(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.put(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
const data = await req.json();
@@ -38,7 +38,7 @@ export const handlers = [
return res(ctx.status(200));
}),
rest.delete<string>(umbracoPath(`${slug}/:id`), async (req, res, ctx) => {
rest.delete<string>(umbracoPath(`${UMB_SLUG}/:id`), async (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { DisableUserRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post<DisableUserRequestModel>(umbracoPath(`${slug}/disable`), async (req, res, ctx) => {
rest.post<DisableUserRequestModel>(umbracoPath(`${UMB_SLUG}/disable`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
if (!data.userIds) return;

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { EnableUserRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post<EnableUserRequestModel>(umbracoPath(`${slug}/enable`), async (req, res, ctx) => {
rest.post<EnableUserRequestModel>(umbracoPath(`${UMB_SLUG}/enable`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
if (!data.userIds) return;

View File

@@ -1,30 +1,29 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/filter`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/filter`), (req, res, ctx) => {
const skip = Number(req.url.searchParams.get('skip'));
const take = Number(req.url.searchParams.get('take'));
const orderBy = req.url.searchParams.get('orderBy');
const orderDirection = req.url.searchParams.get('orderDirection');
const userGroupIds = req.url.searchParams.getAll('userGroupIds');
const userStates = req.url.searchParams.getAll('userStates');
const filter = req.url.searchParams.get('filter');
const skip = Number(req.url.searchParams.get('skip'));
const take = Number(req.url.searchParams.get('take'));
const orderBy = req.url.searchParams.get('orderBy');
const orderDirection = req.url.searchParams.get('orderDirection');
const userGroupIds = req.url.searchParams.getAll('userGroupIds');
const userStates = req.url.searchParams.getAll('userStates');
const filter = req.url.searchParams.get('filter');
const options = {
skip: skip || undefined,
take: take || undefined,
orderBy: orderBy || undefined,
orderDirection: orderDirection || undefined,
userGroupIds: userGroupIds.length > 0 ? userGroupIds : undefined,
userStates: userStates.length > 0 ? userStates : undefined,
filter: filter || undefined,
};
const options = {
skip: skip || undefined,
take: take || undefined,
orderBy: orderBy || undefined,
orderDirection: orderDirection || undefined,
userGroupIds: userGroupIds.length > 0 ? userGroupIds : undefined,
userStates: userStates.length > 0 ? userStates : undefined,
filter: filter || undefined,
};
const response = umbUsersData.filter(options);
return res(ctx.status(200), ctx.json(response));
}),
const response = umbUsersData.filter(options);
return res(ctx.status(200), ctx.json(response));
}),
];

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { InviteUserRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post<InviteUserRequestModel>(umbracoPath(`${slug}/invite`), async (req, res, ctx) => {
rest.post<InviteUserRequestModel>(umbracoPath(`${UMB_SLUG}/invite`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -14,7 +14,7 @@ export const handlers = [
return res(ctx.status(200));
}),
rest.post<any>(umbracoPath(`${slug}/invite/resend`), async (req, res, ctx) => {
rest.post<any>(umbracoPath(`${UMB_SLUG}/invite/resend`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.get(umbracoPath(`${slug}/item`), (req, res, ctx) => {
rest.get(umbracoPath(`${UMB_SLUG}/item`), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
const items = umbUsersData.getItems(ids);

View File

@@ -1,10 +1,10 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post(umbracoPath(`${slug}/set-user-groups`), async (req, res, ctx) => {
rest.post(umbracoPath(`${UMB_SLUG}/set-user-groups`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;

View File

@@ -1 +1 @@
export const slug = '/user';
export const UMB_SLUG = '/user';

View File

@@ -1,11 +1,11 @@
const { rest } = window.MockServiceWorker;
import { umbUsersData } from '../../data/user.data.js';
import { slug } from './slug.js';
import { UMB_SLUG } from './slug.js';
import { UnlockUsersRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
export const handlers = [
rest.post<UnlockUsersRequestModel>(umbracoPath(`${slug}/unlock`), async (req, res, ctx) => {
rest.post<UnlockUsersRequestModel>(umbracoPath(`${UMB_SLUG}/unlock`), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
if (!data.userIds) return;

View File

@@ -12,9 +12,6 @@ export class UmbCollectionViewBundleElement extends UmbLitElement {
@state()
_currentView?: ManifestCollectionView;
@state()
private _isOpen = false;
@state()
private _collectionRootPathname = '';
@@ -33,56 +30,65 @@ export class UmbCollectionViewBundleElement extends UmbLitElement {
}
#observeCurrentView() {
this.observe(this.#collectionContext!.currentView, (view) => {
this._currentView = view;
}, 'umbCurrentCollectionViewObserver');
this.observe(
this.#collectionContext!.currentView,
(view) => {
//TODO: This is not called when the view is changed
this._currentView = view;
},
'umbCurrentCollectionViewObserver',
);
}
#observeViews() {
this.observe(this.#collectionContext!.views, (views) => {
this._views = views;
}, 'umbCollectionViewsObserver');
this.observe(
this.#collectionContext!.views,
(views) => {
this._views = views;
},
'umbCollectionViewsObserver',
);
}
#toggleDropdown() {
this._isOpen = !this._isOpen;
}
#closeDropdown() {
this._isOpen = false;
}
render() {
return html`${this.#renderLayoutButton()}`;
}
#renderLayoutButton() {
if (!this._currentView) return nothing;
return html` <umb-dropdown .open="${this._isOpen}" @close=${this.#closeDropdown}>
<uui-button slot="trigger" label="status" @click=${this.#toggleDropdown}
>${this.#renderItemDisplay(this._currentView)}</uui-button
>
<div slot="dropdown" class="filter-dropdown">${this._views.map((view) => this.#renderItem(view))}</div>
</umb-dropdown>`;
return html`
<uui-button compact popovertarget="collection-view-bundle-popover" label="status">
${this.#renderItemDisplay(this._currentView)}
</uui-button>
<uui-popover-container id="collection-view-bundle-popover" popover placement="bottom">
<umb-popover-layout>
<div class="filter-dropdown">${this._views.map((view) => this.#renderItem(view))}</div>
</umb-popover-layout>
</uui-popover-container>
`;
}
#renderItem(view: ManifestCollectionView) {
return html`<a href="${this._collectionRootPathname}/${view.meta.pathName}">${this.#renderItemDisplay(view)}</a>`;
return html`
<uui-button compact href="${this._collectionRootPathname}/${view.meta.pathName}">
${this.#renderItemDisplay(view)} <span class="label">${view.meta.label}</span>
</uui-button>
`;
}
#renderItemDisplay(view: ManifestCollectionView) {
return html`<span class="item"><uui-icon name=${view.meta.icon}></uui-icon> ${view.meta.label}</span>`;
return html`<uui-icon name=${view.meta.icon}></uui-icon>`;
}
static styles = [
UmbTextStyles,
css`
.item {
:host {
--uui-button-content-align: left;
}
a {
display: block;
.label {
margin-left: var(--uui-size-space-1);
}
.filter-dropdown {
display: flex;
gap: var(--uui-size-space-3);
flex-direction: column;
}
`,
];

View File

@@ -33,3 +33,4 @@ export * from './ref-property-editor-ui/index.js';
export * from './table/index.js';
export * from './tooltip-menu/index.js';
export * from './variant-selector/index.js';
export * from './popover-layout/index.js';

View File

@@ -0,0 +1,3 @@
import './popover-layout.element.js';
export * from './popover-layout.element.js';

View File

@@ -0,0 +1,33 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
// TODO: maybe move this to UI Library.
// TODO: add overwrites for customization.
@customElement('umb-popover-layout')
export class UmbPopoverLayoutElement extends UmbLitElement {
render() {
return html`<slot></slot>`;
}
static styles = [
UmbTextStyles,
css`
:host {
background-color: var(--uui-color-surface);
display: block;
border: 1px solid var(--uui-color-border);
border-radius: var(--uui-border-radius);
box-shadow: var(--uui-shadow-depth-3);
padding: var(--uui-size-space-3);
overflow: clip;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
'umb-popover-layout': UmbPopoverLayoutElement;
}
}

View File

@@ -1,11 +1,11 @@
import { UmbCultureRepository } from './culture.repository.js';
import { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const CULTURE_REPOSITORY_ALIAS = 'Umb.Repository.Culture';
export const UMB_CULTURE_REPOSITORY_ALIAS = 'Umb.Repository.Culture';
const repository: ManifestRepository = {
type: 'repository',
alias: CULTURE_REPOSITORY_ALIAS,
alias: UMB_CULTURE_REPOSITORY_ALIAS,
name: 'Cultures Repository',
api: UmbCultureRepository,
};

View File

@@ -1,5 +1,5 @@
import { DATA_TYPE_ENTITY_TYPE } from '../../entity.js';
import { COPY_DATA_TYPE_REPOSITORY_ALIAS } from '../../repository/copy/manifests.js';
import { UMB_DATA_TYPE_ENTITY_TYPE } from '../../entity.js';
import { UMB_COPY_DATA_TYPE_REPOSITORY_ALIAS } from '../../repository/copy/manifests.js';
import { UmbCopyDataTypeEntityAction } from './copy.action.js';
import { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
@@ -13,8 +13,8 @@ const entityActions: Array<ManifestTypes> = [
meta: {
icon: 'icon-documents',
label: 'Copy to...',
repositoryAlias: COPY_DATA_TYPE_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE],
repositoryAlias: UMB_COPY_DATA_TYPE_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE],
},
},
];

View File

@@ -1,5 +1,9 @@
import { DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE, DATA_TYPE_ROOT_ENTITY_TYPE } from '../../entity.js';
import { DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from '../../repository/detail/manifests.js';
import {
UMB_DATA_TYPE_ENTITY_TYPE,
UMB_DATA_TYPE_FOLDER_ENTITY_TYPE,
UMB_DATA_TYPE_ROOT_ENTITY_TYPE,
} from '../../entity.js';
import { UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from '../../repository/detail/manifests.js';
import { UmbCreateDataTypeEntityAction } from './create.action.js';
import { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
@@ -13,8 +17,8 @@ const entityActions: Array<ManifestTypes> = [
meta: {
icon: 'icon-add',
label: 'Create...',
repositoryAlias: DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_ROOT_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE],
repositoryAlias: UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE, UMB_DATA_TYPE_ROOT_ENTITY_TYPE, UMB_DATA_TYPE_FOLDER_ENTITY_TYPE],
},
},
{

View File

@@ -1,4 +1,4 @@
import { DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from '../../../repository/folder/manifests.js';
import { UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from '../../../repository/folder/manifests.js';
import { UmbDataTypeCreateOptionsModalData } from './index.js';
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
@@ -30,7 +30,7 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbLitElement {
#onClick(event: PointerEvent) {
event.stopPropagation();
const folderModalHandler = this.#modalContext?.open(UMB_FOLDER_MODAL, {
repositoryAlias: DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
repositoryAlias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
});
folderModalHandler?.onSubmit().then(() => this.modalContext?.submit());
}

View File

@@ -1,5 +1,5 @@
import { DATA_TYPE_FOLDER_ENTITY_TYPE, DATA_TYPE_ENTITY_TYPE } from '../entity.js';
import { DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from '../repository/detail/manifests.js';
import { UMB_DATA_TYPE_FOLDER_ENTITY_TYPE, UMB_DATA_TYPE_ENTITY_TYPE } from '../entity.js';
import { UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from '../repository/detail/manifests.js';
import { manifests as createManifests } from './create/manifests.js';
import { manifests as moveManifests } from './move/manifests.js';
import { manifests as copyManifests } from './copy/manifests.js';
@@ -10,7 +10,7 @@ import {
UmbFolderUpdateEntityAction,
} from '@umbraco-cms/backoffice/entity-action';
import { ManifestEntityAction } from '@umbraco-cms/backoffice/extension-registry';
import { DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from '../repository/folder/manifests.js';
import { UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from '../repository/folder/manifests.js';
const entityActions: Array<ManifestEntityAction> = [
{
@@ -22,8 +22,8 @@ const entityActions: Array<ManifestEntityAction> = [
meta: {
icon: 'icon-trash',
label: 'Delete...',
repositoryAlias: DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE],
repositoryAlias: UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE],
},
},
{
@@ -35,8 +35,8 @@ const entityActions: Array<ManifestEntityAction> = [
meta: {
icon: 'icon-trash',
label: 'Delete Folder...',
repositoryAlias: DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE],
repositoryAlias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE, UMB_DATA_TYPE_FOLDER_ENTITY_TYPE],
},
},
{
@@ -48,8 +48,8 @@ const entityActions: Array<ManifestEntityAction> = [
meta: {
icon: 'icon-edit',
label: 'Rename Folder...',
repositoryAlias: DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE],
repositoryAlias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE, UMB_DATA_TYPE_FOLDER_ENTITY_TYPE],
},
},
];

View File

@@ -1,5 +1,5 @@
import { DATA_TYPE_ENTITY_TYPE } from '../../entity.js';
import { MOVE_DATA_TYPE_REPOSITORY_ALIAS } from '../../repository/move/manifests.js';
import { UMB_DATA_TYPE_ENTITY_TYPE } from '../../entity.js';
import { UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS } from '../../repository/move/manifests.js';
import { UmbMoveDataTypeEntityAction } from './move.action.js';
import { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
@@ -13,8 +13,8 @@ const entityActions: Array<ManifestTypes> = [
meta: {
icon: 'icon-enter',
label: 'Move to...',
repositoryAlias: MOVE_DATA_TYPE_REPOSITORY_ALIAS,
entityTypes: [DATA_TYPE_ENTITY_TYPE],
repositoryAlias: UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS,
entityTypes: [UMB_DATA_TYPE_ENTITY_TYPE],
},
},
];

View File

@@ -1,3 +1,3 @@
export const DATA_TYPE_ROOT_ENTITY_TYPE = 'data-type-root';
export const DATA_TYPE_ENTITY_TYPE = 'data-type';
export const DATA_TYPE_FOLDER_ENTITY_TYPE = 'data-type-folder';
export const UMB_DATA_TYPE_ROOT_ENTITY_TYPE = 'data-type-root';
export const UMB_DATA_TYPE_ENTITY_TYPE = 'data-type';
export const UMB_DATA_TYPE_FOLDER_ENTITY_TYPE = 'data-type-folder';

View File

@@ -1,2 +1,2 @@
export { UmbCopyDataTypeRepository } from './data-type-copy.repository.js';
export { COPY_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js';
export { UMB_COPY_DATA_TYPE_REPOSITORY_ALIAS as COPY_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js';

View File

@@ -1,11 +1,11 @@
import { UmbCopyDataTypeRepository } from './data-type-copy.repository.js';
import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const COPY_DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Copy';
export const UMB_COPY_DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Copy';
const copyRepository: ManifestRepository = {
type: 'repository',
alias: COPY_DATA_TYPE_REPOSITORY_ALIAS,
alias: UMB_COPY_DATA_TYPE_REPOSITORY_ALIAS,
name: 'Copy Data Type Repository',
api: UmbCopyDataTypeRepository,
};

View File

@@ -1,2 +1,2 @@
export { UmbDataTypeDetailRepository } from './data-type-detail.repository.js';
export { DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from './manifests.js';
export { UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS as DATA_TYPE_DETAIL_REPOSITORY_ALIAS } from './manifests.js';

View File

@@ -2,20 +2,20 @@ import { UmbDataTypeDetailRepository } from './data-type-detail.repository.js';
import { UmbDataTypeDetailStore } from './data-type-detail.store.js';
import { ManifestRepository, ManifestStore } from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Detail';
export const UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Detail';
const repository: ManifestRepository = {
type: 'repository',
alias: DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
alias: UMB_DATA_TYPE_DETAIL_REPOSITORY_ALIAS,
name: 'Data Type Detail Repository',
api: UmbDataTypeDetailRepository,
};
export const DATA_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.DataType.Detail';
export const UMB_DATA_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.DataType.Detail';
const store: ManifestStore = {
type: 'store',
alias: DATA_TYPE_DETAIL_STORE_ALIAS,
alias: UMB_DATA_TYPE_DETAIL_STORE_ALIAS,
name: 'Data Type Detail Store',
api: UmbDataTypeDetailStore,
};

View File

@@ -1,2 +1,2 @@
export { UmbDataTypeFolderRepository } from './data-type-folder.repository.js';
export { DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from './manifests.js';
export { UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS as DATA_TYPE_FOLDER_REPOSITORY_ALIAS } from './manifests.js';

View File

@@ -1,11 +1,11 @@
import { UmbDataTypeFolderRepository } from './data-type-folder.repository.js';
import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_FOLDER_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Folder';
export const UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Folder';
const folderRepository: ManifestRepository = {
type: 'repository',
alias: DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
alias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
name: 'Data Type Folder Repository',
api: UmbDataTypeFolderRepository,
};

View File

@@ -1,2 +1,2 @@
export { UmbDataTypeItemRepository } from './data-type-item.repository.js';
export { DATA_TYPE_ITEM_REPOSITORY_ALIAS } from './manifests.js';
export { UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS as DATA_TYPE_ITEM_REPOSITORY_ALIAS } from './manifests.js';

View File

@@ -2,19 +2,19 @@ import { UmbDataTypeItemStore } from './data-type-item.store.js';
import { UmbDataTypeItemRepository } from './data-type-item.repository.js';
import type { ManifestRepository, ManifestItemStore } from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DataTypeItem';
export const DATA_TYPE_STORE_ALIAS = 'Umb.Store.DataTypeItem';
export const UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DataTypeItem';
export const UMB_DATA_TYPE_STORE_ALIAS = 'Umb.Store.DataTypeItem';
const itemRepository: ManifestRepository = {
type: 'repository',
alias: DATA_TYPE_ITEM_REPOSITORY_ALIAS,
alias: UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS,
name: 'Data Type Item Repository',
api: UmbDataTypeItemRepository,
};
const itemStore: ManifestItemStore = {
type: 'itemStore',
alias: DATA_TYPE_STORE_ALIAS,
alias: UMB_DATA_TYPE_STORE_ALIAS,
name: 'Data Type Item Store',
api: UmbDataTypeItemStore,
};

View File

@@ -1,2 +1,2 @@
export { UmbMoveDataTypeRepository } from './data-type-move.repository.js';
export { MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js';
export { UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS as MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js';

View File

@@ -1,11 +1,11 @@
import { UmbMoveDataTypeRepository } from './data-type-move.repository.js';
import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const MOVE_DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Move';
export const UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Move';
const moveRepository: ManifestRepository = {
type: 'repository',
alias: MOVE_DATA_TYPE_REPOSITORY_ALIAS,
alias: UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS,
name: 'Move Data Type Repository',
api: UmbMoveDataTypeRepository,
};

View File

@@ -1,5 +1,5 @@
import { UmbTreeRepositoryBase } from '../../tree/tree-repository-base.js';
import { DATA_TYPE_ROOT_ENTITY_TYPE } from '../entity.js';
import { UMB_DATA_TYPE_ROOT_ENTITY_TYPE } from '../entity.js';
import { UmbDataTypeTreeServerDataSource } from './data-type.tree.server.data.js';
import { UMB_DATA_TYPE_TREE_STORE_CONTEXT } from './data-type.tree.store.js';
import { UmbDataTypeTreeItemModel, UmbDataTypeTreeRootModel } from './types.js';
@@ -17,7 +17,7 @@ export class UmbDataTypeTreeRepository
async requestTreeRoot() {
const data = {
id: null,
type: DATA_TYPE_ROOT_ENTITY_TYPE,
type: UMB_DATA_TYPE_ROOT_ENTITY_TYPE,
name: 'Data Types',
icon: 'icon-folder',
hasChildren: true,

View File

@@ -7,19 +7,19 @@ import type {
ManifestTreeStore,
} from '@umbraco-cms/backoffice/extension-registry';
export const DATA_TYPE_TREE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Tree';
export const DATA_TYPE_TREE_STORE_ALIAS = 'Umb.Store.DataType.Tree';
export const UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Tree';
export const UMB_DATA_TYPE_TREE_STORE_ALIAS = 'Umb.Store.DataType.Tree';
const treeRepository: ManifestRepository = {
type: 'repository',
alias: DATA_TYPE_TREE_REPOSITORY_ALIAS,
alias: UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS,
name: 'Data Type Tree Repository',
api: UmbDataTypeTreeRepository,
};
const treeStore: ManifestTreeStore = {
type: 'treeStore',
alias: DATA_TYPE_TREE_STORE_ALIAS,
alias: UMB_DATA_TYPE_TREE_STORE_ALIAS,
name: 'Data Type Tree Store',
api: UmbDataTypeTreeStore,
};
@@ -29,7 +29,7 @@ const tree: ManifestTree = {
alias: 'Umb.Tree.DataTypes',
name: 'Data Types Tree',
meta: {
repositoryAlias: DATA_TYPE_TREE_REPOSITORY_ALIAS,
repositoryAlias: UMB_DATA_TYPE_TREE_REPOSITORY_ALIAS,
},
};

View File

@@ -1,8 +1,12 @@
import type { UmbDataTypeVariantContext } from "./data-type-variant-context.js";
import { UmbVariantContext } from "@umbraco-cms/backoffice/workspace";
import { UmbContextToken } from "@umbraco-cms/backoffice/context-api";
import type { UmbDataTypeVariantContext } from './data-type-variant-context.js';
import { UmbVariantContext } from '@umbraco-cms/backoffice/workspace';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const isDataTypeVariantContext = (context: UmbVariantContext): context is UmbDataTypeVariantContext => ('properties' in context && context.getType() === 'data-type');
export const isDataTypeVariantContext = (context: UmbVariantContext): context is UmbDataTypeVariantContext =>
'properties' in context && context.getType() === 'data-type';
export const UMB_DATA_TYPE_VARIANT_CONTEXT = new UmbContextToken<UmbVariantContext, UmbDataTypeVariantContext>(
"UmbVariantContext", isDataTypeVariantContext);
'UmbVariantContext',
undefined,
isDataTypeVariantContext,
);

View File

@@ -28,17 +28,17 @@ export class UmbDataTypeWorkspaceContext
{
// TODO: revisit. temp solution because the create and response models are different.
#data = new UmbObjectState<DataTypeResponseModel | undefined>(undefined);
data = this.#data.asObservable();
readonly data = this.#data.asObservable();
#getDataPromise?: Promise<any>;
name = this.#data.asObservablePart((data) => data?.name);
id = this.#data.asObservablePart((data) => data?.id);
readonly name = this.#data.asObservablePart((data) => data?.name);
readonly id = this.#data.asObservablePart((data) => data?.id);
propertyEditorUiAlias = this.#data.asObservablePart((data) => data?.propertyEditorUiAlias);
propertyEditorSchemaAlias = this.#data.asObservablePart((data) => data?.propertyEditorAlias);
readonly propertyEditorUiAlias = this.#data.asObservablePart((data) => data?.propertyEditorUiAlias);
readonly propertyEditorSchemaAlias = this.#data.asObservablePart((data) => data?.propertyEditorAlias);
#properties = new UmbObjectState<Array<PropertyEditorConfigProperty> | undefined>(undefined);
properties: Observable<Array<PropertyEditorConfigProperty> | undefined> = this.#properties.asObservable();
readonly properties: Observable<Array<PropertyEditorConfigProperty> | undefined> = this.#properties.asObservable();
private _propertyEditorSchemaConfigDefaultData: Array<PropertyEditorConfigDefaultData> = [];
private _propertyEditorUISettingsDefaultData: Array<PropertyEditorConfigDefaultData> = [];
@@ -53,13 +53,13 @@ export class UmbDataTypeWorkspaceContext
private _propertyEditorUISettingsSchemaAlias?: string;
#defaults = new UmbArrayState<PropertyEditorConfigDefaultData>([], (entry) => entry.alias);
defaults = this.#defaults.asObservable();
readonly defaults = this.#defaults.asObservable();
#propertyEditorUiIcon = new UmbStringState<string | null>(null);
propertyEditorUiIcon = this.#propertyEditorUiIcon.asObservable();
readonly propertyEditorUiIcon = this.#propertyEditorUiIcon.asObservable();
#propertyEditorUiName = new UmbStringState<string | null>(null);
propertyEditorUiName = this.#propertyEditorUiName.asObservable();
readonly propertyEditorUiName = this.#propertyEditorUiName.asObservable();
constructor(host: UmbControllerHostElement) {
super(host, 'Umb.Workspace.DataType', new UmbDataTypeDetailRepository(host));
@@ -265,5 +265,6 @@ export const UMB_DATA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
UmbDataTypeWorkspaceContext
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbDataTypeWorkspaceContext => context.getEntityType?.() === 'data-type',
);

View File

@@ -1,4 +1,4 @@
import { contextData, umbDebugContextEventType } from '@umbraco-cms/backoffice/context-api';
import { contextData, UMB_DEBUG_CONTEXT_EVENT_TYPE } from '@umbraco-cms/backoffice/context-api';
import { UmbBaseController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
// Temp controller to get the code away from the app.element.ts
@@ -11,7 +11,10 @@ export class UmbContextDebugController extends UmbBaseController {
super.hostConnected();
// Maybe this could be part of the context-api? When we create a new root, we could attach the debugger to it?
// Listen for the debug event from the <umb-debug> component
this.getHostElement().addEventListener(umbDebugContextEventType, this.#onContextDebug as unknown as EventListener);
this.getHostElement().addEventListener(
UMB_DEBUG_CONTEXT_EVENT_TYPE,
this.#onContextDebug as unknown as EventListener,
);
}
#onContextDebug = (event: any) => {
@@ -38,7 +41,7 @@ export class UmbContextDebugController extends UmbBaseController {
hostDisconnected(): void {
super.hostDisconnected();
this.getHostElement().removeEventListener(
umbDebugContextEventType,
UMB_DEBUG_CONTEXT_EVENT_TYPE,
this.#onContextDebug as unknown as EventListener,
);
}

View File

@@ -5,7 +5,7 @@ export interface UmbImportDictionaryModalData {
}
export interface UmbImportDictionaryModalValue {
temporaryFileId?: string;
temporaryFileId: string;
parentId?: string;
}

View File

@@ -19,6 +19,7 @@ export * from './invite-user-modal.token.js';
export * from './language-picker-modal.token.js';
export * from './link-picker-modal.token.js';
export * from './media-tree-picker-modal.token.js';
export * from './media-type-picker-modal.token.js';
export * from './property-editor-ui-picker-modal.token.js';
export * from './property-settings-modal.token.js';
export * from './search-modal.token.js';

View File

@@ -0,0 +1,16 @@
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbModalToken, UmbPickerModalValue, UmbTreePickerModalData } from '@umbraco-cms/backoffice/modal';
export type UmbMediaTypePickerModalData = UmbTreePickerModalData<EntityTreeItemResponseModel>;
export type UmbMediaTypePickerModalValue = UmbPickerModalValue;
export const UMB_MEDIA_TYPE_PICKER_MODAL = new UmbModalToken<UmbMediaTypePickerModalData, UmbMediaTypePickerModalValue>(
'Umb.Modal.TreePicker',
{
type: 'sidebar',
size: 'small',
},
{
treeAlias: 'Umb.Tree.MediaType',
},
);

View File

@@ -35,7 +35,12 @@ export class UmbFileSystemTreeStore
return this.rootItems;
}
return this._data.asObservablePart((items) => items.filter((item) => item.path?.startsWith(parentPath + '/')));
return this._data.asObservablePart((items) =>
items.filter((item) => {
const pathCut = item.path?.substring(0, item.path?.lastIndexOf('/'));
return parentPath === pathCut;
}),
);
}
/**

View File

@@ -1,6 +1,6 @@
export type variantObject = { culture?: string | null; segment?: string | null };
export const INVARIANT_CULTURE = 'invariant';
export const UMB_INVARIANT_CULTURE = 'invariant';
export class UmbVariantId {
public static Create(variantData: variantObject): UmbVariantId {
@@ -15,7 +15,7 @@ export class UmbVariantId {
public readonly segment: string | null = null;
constructor(variantData: variantObject) {
this.culture = (variantData.culture === INVARIANT_CULTURE ? null : variantData.culture) ?? null;
this.culture = (variantData.culture === UMB_INVARIANT_CULTURE ? null : variantData.culture) ?? null;
this.segment = variantData.segment ?? null;
}
@@ -28,15 +28,15 @@ export class UmbVariantId {
}
public toString(): string {
return (this.culture || INVARIANT_CULTURE) + (this.segment ? `_${this.segment}` : '');
return (this.culture || UMB_INVARIANT_CULTURE) + (this.segment ? `_${this.segment}` : '');
}
public toCultureString(): string {
return (this.culture || INVARIANT_CULTURE);
return this.culture || UMB_INVARIANT_CULTURE;
}
public toSegmentString(): string {
return (this.segment || '');
return this.segment || '';
}
public isInvariant(): boolean {

View File

@@ -1,9 +1,12 @@
import { type UmbVariantContext } from "./variant-context.interface.js";
import { UmbNameableVariantContext } from "./nameable-variant-context.interface.js";
import { UmbContextToken } from "@umbraco-cms/backoffice/context-api";
import { type UmbVariantContext } from './variant-context.interface.js';
import { UmbNameableVariantContext } from './nameable-variant-context.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const isNameablePropertySetContext = (context: UmbVariantContext): context is UmbNameableVariantContext => 'setName' in context;
export const isNameablePropertySetContext = (context: UmbVariantContext): context is UmbNameableVariantContext =>
'setName' in context;
export const UMB_NAMEABLE_VARIANT_CONTEXT = new UmbContextToken<UmbVariantContext, UmbNameableVariantContext>(
"UmbVariantContext",
isNameablePropertySetContext);
'UmbVariantContext',
undefined,
isNameablePropertySetContext,
);

View File

@@ -1,4 +1,4 @@
import { type UmbVariantContext } from "./variant-context.interface.js";
import { UmbContextToken } from "@umbraco-cms/backoffice/context-api";
import { type UmbVariantContext } from './variant-context.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_VARIANT_CONTEXT = new UmbContextToken<UmbVariantContext>("UmbVariantContext");
export const UMB_VARIANT_CONTEXT = new UmbContextToken<UmbVariantContext>('UmbVariantContext');

View File

@@ -6,4 +6,8 @@ import type { UmbEntityBase } from '@umbraco-cms/backoffice/models';
export const UMB_VARIANT_WORKSPACE_CONTEXT_TOKEN = new UmbContextToken<
UmbWorkspaceContextInterface,
UmbVariantableWorkspaceContextInterface<UmbEntityBase>
>('UmbWorkspaceContext', (context): context is UmbVariantableWorkspaceContextInterface => 'variants' in context);
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbVariantableWorkspaceContextInterface => 'variants' in context,
);

View File

@@ -1,4 +1,6 @@
export interface UmbWorkspaceContextInterface {
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
export interface UmbWorkspaceContextInterface extends UmbApi {
readonly workspaceAlias: string;
// TODO: should we consider another name than entity type. File system files are not entities but still have this type.
getEntityType(): string;

View File

@@ -0,0 +1,10 @@
import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UMB_DICTIONARY_ITEM_PICKER_MODAL } from '@umbraco-cms/backoffice/modal';
import { DictionaryItemItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
export class UmbDictionaryItemPickerContext extends UmbPickerInputContext<DictionaryItemItemResponseModel> {
constructor(host: UmbControllerHostElement) {
super(host, 'Umb.Repository.Dictionary', UMB_DICTIONARY_ITEM_PICKER_MODAL);
}
}

View File

@@ -0,0 +1,149 @@
import { UmbDictionaryItemPickerContext } from './dictionary-item-input.context.js';
import { css, html, customElement, property, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit';
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { DictionaryItemItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
@customElement('umb-dictionary-item-input')
export class UmbDictionaryItemInputElement extends FormControlMixin(UmbLitElement) {
/**
* This is a minimum amount of selected items in this input.
* @type {number}
* @attr
* @default 0
*/
@property({ type: Number })
public get min(): number {
return this.#pickerContext.min;
}
public set min(value: number) {
this.#pickerContext.min = value;
}
/**
* Min validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
minMessage = 'This field need more items';
/**
* This is a maximum amount of selected items in this input.
* @type {number}
* @attr
* @default Infinity
*/
@property({ type: Number })
public get max(): number {
return this.#pickerContext.max;
}
public set max(value: number) {
this.#pickerContext.max = value;
}
/**
* Max validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
maxMessage = 'This field exceeds the allowed amount of items';
public get selectedIds(): Array<string> {
return this.#pickerContext.getSelection();
}
public set selectedIds(ids: Array<string>) {
this.#pickerContext.setSelection(ids);
}
@property()
public set value(idsString: string) {
// Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection.
this.selectedIds = idsString.split(/[ ,]+/);
}
@state()
private _items?: Array<DictionaryItemItemResponseModel>;
#pickerContext = new UmbDictionaryItemPickerContext(this);
constructor() {
super();
this.addValidator(
'rangeUnderflow',
() => this.minMessage,
() => !!this.min && this.#pickerContext.getSelection().length < this.min,
);
this.addValidator(
'rangeOverflow',
() => this.maxMessage,
() => !!this.max && this.#pickerContext.getSelection().length > this.max,
);
this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(',')));
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems));
}
protected getFormElement() {
return undefined;
}
render() {
return html`
${this._items
? html` <uui-ref-list
>${repeat(
this._items,
(item) => item.id,
(item) => this._renderItem(item),
)}
</uui-ref-list>`
: ''}
${this.#renderAddButton()}
`;
}
#renderAddButton() {
if (this.max > 0 && this.selectedIds.length >= this.max) return;
return html`<uui-button
id="add-button"
look="placeholder"
@click=${() => this.#pickerContext.openPicker()}
label=${this.localize.term('general_add')}></uui-button>`;
}
private _renderItem(item: DictionaryItemItemResponseModel) {
if (!item.id) return;
return html`
<uui-ref-node name=${ifDefined(item.name)} detail=${ifDefined(item.id)}>
<!-- TODO: implement is trashed <uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag> -->
<uui-action-bar slot="actions">
<uui-button
@click=${() => this.#pickerContext.requestRemoveItem(item.id!)}
label=${this.localize.term('actions_remove')}></uui-button>
</uui-action-bar>
</uui-ref-node>
`;
}
static styles = [
css`
#add-button {
width: 100%;
}
`,
];
}
export default UmbDictionaryItemInputElement;
declare global {
interface HTMLElementTagNameMap {
'umb-dictionary-item-input': UmbDictionaryItemInputElement;
}
}

View File

@@ -0,0 +1 @@
export * from './dictionary-item-input/dictionary-item-input.element.js';

View File

@@ -1,5 +1,5 @@
import { UmbDictionaryRepository } from '../../repository/dictionary.repository.js';
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import {
@@ -22,18 +22,30 @@ export default class UmbExportDictionaryEntityAction extends UmbEntityActionBase
}
async execute() {
// TODO: what to do if modal service is not available?
if (!this.#modalContext) return;
const modalContext = this.#modalContext?.open(UMB_EXPORT_DICTIONARY_MODAL, { unique: this.unique });
// TODO: get type from modal result
const { includeChildren } = await modalContext.onSubmit();
if (includeChildren === undefined) return;
// Export the file
const result = await this.repository?.export(this.unique, includeChildren);
const blobContent = await result?.data;
// TODO => get location header to route to new item
console.log(result);
if (!blobContent) return;
const blob = new Blob([blobContent], { type: 'text/plain' });
const a = document.createElement('a');
const url = window.URL.createObjectURL(blob);
// Download
a.href = url;
a.download = `${this.unique}.udt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Clean up
window.URL.revokeObjectURL(url);
}
}

View File

@@ -1,3 +1,5 @@
import '../../components/dictionary-item-input/dictionary-item-input.element.js';
import UmbDictionaryItemInputElement from '../../components/dictionary-item-input/dictionary-item-input.element.js';
import { UmbDictionaryRepository } from '../../repository/dictionary.repository.js';
import { css, html, customElement, query, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
@@ -7,167 +9,213 @@ import {
UmbModalBaseElement,
} from '@umbraco-cms/backoffice/modal';
import { ImportDictionaryRequestModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbId } from '@umbraco-cms/backoffice/id';
interface DictionaryItemPreview {
name: string;
children: Array<DictionaryItemPreview>;
}
@customElement('umb-import-dictionary-modal')
export class UmbImportDictionaryModalLayout extends UmbModalBaseElement<
UmbImportDictionaryModalData,
UmbImportDictionaryModalValue
> {
@state()
private _parentId?: string;
@state()
private _temporaryFileId?: string;
@query('#form')
private _form!: HTMLFormElement;
#fileReader;
#fileContent: Array<DictionaryItemPreview> = [];
#handleClose() {
this.modalContext?.reject();
}
#submit() {
// TODO: Gotta do a temp file upload before submitting, so that the server can use it
console.log('submit:', this._temporaryFileId, this._parentId);
//this.modalContext?.submit({ temporaryFileId: this._temporaryFileId, parentId: this._parentId });
}
constructor() {
super();
this.#fileReader = new FileReader();
this.#fileReader.onload = (e) => {
if (typeof e.target?.result === 'string') {
const fileContent = e.target.result;
this.#dictionaryItemBuilder(fileContent);
}
};
}
connectedCallback(): void {
super.connectedCallback();
this._parentId = this.data?.unique ?? undefined;
}
#dictionaryItemBuilder(htmlString: string) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlString, 'text/xml');
const elements = doc.childNodes;
this.#fileContent = this.#makeDictionaryItems(elements);
this.requestUpdate();
}
#makeDictionaryItems(nodeList: NodeListOf<ChildNode>): Array<DictionaryItemPreview> {
const items: Array<DictionaryItemPreview> = [];
const list: Array<Element> = [];
nodeList.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'DictionaryItem') {
list.push(node as Element);
}
});
list.forEach((item) => {
items.push({
name: item.getAttribute('Name') ?? '',
children: this.#makeDictionaryItems(item.childNodes) ?? undefined,
});
});
return items;
}
#onUpload(e: Event) {
e.preventDefault();
const formData = new FormData(this._form);
const file = formData.get('file') as Blob;
this.#fileReader.readAsText(file);
this._temporaryFileId = file ? UmbId.new() : undefined;
}
#onParentChange(event: CustomEvent) {
this._parentId = (event.target as UmbDictionaryItemInputElement).selectedIds[0] || undefined;
//console.log((event.target as UmbDictionaryItemInputElement).selectedIds[0] || undefined);
}
async #onFileInput() {
requestAnimationFrame(() => {
this._form.requestSubmit();
});
}
#onClear() {
this._temporaryFileId = '';
}
render() {
return html` <umb-body-layout headline=${this.localize.term('general_import')}>
<uui-box>
${when(
this._temporaryFileId,
() => this.#renderImportDestination(),
() => this.#renderUploadZone(),
)}
</uui-box>
<uui-button
slot="actions"
type="button"
label=${this.localize.term('general_cancel')}
@click=${this.#handleClose}></uui-button>
</umb-body-layout>`;
}
#renderFileContents(items: Array<DictionaryItemPreview>): any {
return html`${items.map((item: DictionaryItemPreview) => {
return html`${item.name}
<div>${this.#renderFileContents(item.children)}</div>`;
})}`;
}
#renderImportDestination() {
return html`
<div id="wrapper">
<div>
<strong><umb-localize key="visuallyHiddenTexts_dictionaryItems">Dictionary items</umb-localize>:</strong>
<div id="item-list">${this.#renderFileContents(this.#fileContent)}</div>
</div>
<div>
<strong><umb-localize key="actions_chooseWhereToImport">Choose where to import</umb-localize>:</strong>
Work in progress<br />
${
this._parentId
// TODO
// <umb-dictionary-item-input
// @change=${this.#onParentChange}
// .selectedIds=${this._parentId ? [this._parentId] : []}
// max="1">
// </umb-dictionary-item-input>
}
</div>
${this.#renderNavigate()}
</div>
`;
}
#renderNavigate() {
return html`<div id="nav">
<uui-button label=${this.localize.term('general_import')} look="secondary" @click=${this.#onClear}>
<uui-icon name="icon-arrow-left"></uui-icon>
${this.localize.term('general_back')}
</uui-button>
<uui-button
type="button"
label=${this.localize.term('general_import')}
look="primary"
@click=${this.#submit}></uui-button>
</div>`;
}
#renderUploadZone() {
return html`<umb-localize key="dictionary_importDictionaryItemHelp"></umb-localize>
<uui-form>
<form id="form" name="form" @submit=${this.#onUpload}>
<uui-form-layout-item>
<uui-label for="file" slot="label" required>${this.localize.term('formFileUpload_pickFile')}</uui-label>
<uui-input-file
accept=".udt"
name="file"
id="file"
@input=${this.#onFileInput}
required
required-message=${this.localize.term('formFileUpload_pickFile')}></uui-input-file>
</uui-form-layout-item>
</form>
</uui-form>`;
}
static styles = [
UmbTextStyles,
css`
uui-input {
width: 100%;
}
#item-list {
padding: var(--uui-size-3) var(--uui-size-4);
border: 1px solid var(--uui-color-border);
border-radius: var(--uui-border-radius);
}
#item-list div {
padding-left: 20px;
}
#wrapper {
display: flex;
flex-direction: column;
gap: var(--uui-size-3);
}
`,
];
@query('#form')
private _form!: HTMLFormElement;
@state()
private _uploadedDictionaryTempId?: string;
@state()
private _showUploadView = true;
@state()
private _showImportView = false;
@state()
private _showErrorView = false;
@state()
private _selection: Array<string> = [];
#detailRepo = new UmbDictionaryRepository(this);
async #importDictionary() {
if (!this._uploadedDictionaryTempId) return;
this.modalContext?.submit({
temporaryFileId: this._uploadedDictionaryTempId,
parentId: this._selection[0],
});
}
#handleClose() {
this.modalContext?.reject();
}
#submitForm() {
this._form?.requestSubmit();
}
async #handleSubmit(e: SubmitEvent) {
e.preventDefault();
if (!this._form.checkValidity()) return;
const formData = new FormData(this._form);
const uploadData: ImportDictionaryRequestModel = {
temporaryFileId: formData.get('file')?.toString() ?? '',
};
// TODO: fix this upload experience. We need to update our form so it gets temporary file id from the server:
const { data } = await this.#detailRepo.upload(uploadData);
if (!data) return;
this._uploadedDictionaryTempId = data;
// TODO: We need to find another way to gather the data of the uploaded dictionary, to represent the dictionaryItems? See further below.
//this._uploadedDictionary = data;
if (!this._uploadedDictionaryTempId) {
this._showErrorView = true;
this._showImportView = false;
return;
}
this._showErrorView = false;
this._showUploadView = false;
this._showImportView = true;
}
/*
#handleSelectionChange(e: CustomEvent) {
e.stopPropagation();
const element = e.target as UmbTreeElement;
this._selection = element.selection;
}
*/
#renderUploadView() {
return html`<p>
To import a dictionary item, find the ".udt" file on your computer by clicking the "Import" button (you'll be
asked for confirmation on the next screen)
</p>
<uui-form>
<form id="form" name="form" @submit=${this.#handleSubmit}>
<uui-form-layout-item>
<uui-label for="file" slot="label" required>File</uui-label>
<div>
<uui-input-file
accept=".udt"
name="file"
id="file"
required
required-message="File is required"></uui-input-file>
</div>
</uui-form-layout-item>
</form>
</uui-form>
<uui-button slot="actions" type="button" label="Cancel" @click=${this.#handleClose}></uui-button>
<uui-button slot="actions" type="button" label="Import" look="primary" @click=${this.#submitForm}></uui-button>`;
}
/// TODO => Tree view needs isolation and single-select option
#renderImportView() {
//TODO: gather this data in some other way, we cannot use the feedback from the server anymore. can we use info about the file directly? or is a change to the end point required?
/*
if (!this._uploadedDictionary?.dictionaryItems) return;
return html`
<b>Dictionary items</b>
<ul>
${repeat(
this._uploadedDictionary.dictionaryItems,
(item) => item.name,
(item) => html`<li>${item.name}</li>`
)}
</ul>
<hr />
<b>Choose where to import dictionary items (optional)</b>
<umb-tree
alias="Umb.Tree.Dictionary"
@selection-change=${this.#handleSelectionChange}
.selection=${this._selection}
selectable></umb-tree>
<uui-button slot="actions" type="button" label="Cancel" @click=${this.#handleClose}></uui-button>
<uui-button
slot="actions"
type="button"
label="Import"
look="primary"
@click=${this.#importDictionary}></uui-button>
`;
*/
}
// TODO => Determine what to display when dictionary import/upload fails
#renderErrorView() {
return html`Something went wrong`;
}
render() {
return html` <umb-body-layout headline="Import">
${when(this._showUploadView, () => this.#renderUploadView())}
${when(this._showImportView, () => this.#renderImportView())}
${when(this._showErrorView, () => this.#renderErrorView())}
</umb-body-layout>`;
}
}
export default UmbImportDictionaryModalLayout;

View File

@@ -1,5 +1,5 @@
import { UmbDictionaryRepository } from '../../repository/dictionary.repository.js';
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import {
@@ -22,18 +22,12 @@ export default class UmbImportDictionaryEntityAction extends UmbEntityActionBase
}
async execute() {
// TODO: what to do if modal service is not available?
if (!this.#modalContext) return;
const modalContext = this.#modalContext?.open(UMB_IMPORT_DICTIONARY_MODAL, { unique: this.unique });
// TODO: get type from modal result
const { temporaryFileId, parentId } = await modalContext.onSubmit();
if (!temporaryFileId) return;
const { parentId, temporaryFileId } = await modalContext.onSubmit();
const result = await this.repository?.import(temporaryFileId, parentId);
// TODO => get location header to route to new item
console.log(result);
await this.repository?.import(temporaryFileId, parentId);
}
}

View File

@@ -1,2 +1,3 @@
export * from './repository/index.js';
export * from './tree/index.js';
export * from './components/index.js';

View File

@@ -1,6 +1,9 @@
import { UmbDictionaryRepository } from '../repository/dictionary.repository.js';
import { UmbSaveableWorkspaceContextInterface, UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import {
type UmbSaveableWorkspaceContextInterface,
UmbEditableWorkspaceContextBase,
} from '@umbraco-cms/backoffice/workspace';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { DictionaryItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
@@ -10,10 +13,10 @@ export class UmbDictionaryWorkspaceContext
implements UmbSaveableWorkspaceContextInterface<DictionaryItemResponseModel | undefined>
{
#data = new UmbObjectState<DictionaryItemResponseModel | undefined>(undefined);
data = this.#data.asObservable();
readonly data = this.#data.asObservable();
name = this.#data.asObservablePart((data) => data?.name);
dictionary = this.#data.asObservablePart((data) => data);
readonly name = this.#data.asObservablePart((data) => data?.name);
readonly dictionary = this.#data.asObservablePart((data) => data);
constructor(host: UmbControllerHostElement) {
super(host, 'Umb.Workspace.Dictionary', new UmbDictionaryRepository(host));
@@ -97,5 +100,6 @@ export const UMB_DICTIONARY_WORKSPACE_CONTEXT = new UmbContextToken<
UmbDictionaryWorkspaceContext
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbDictionaryWorkspaceContext => context.getEntityType?.() === 'dictionary-item',
);

View File

@@ -4,19 +4,19 @@ import { manifests as menuItemManifests } from './menu-item/manifests.js';
import { manifests as workspaceManifests } from './workspace/manifests.js';
import type { ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extension-registry';
export const DOCUMENT_BLUEPRINT_STORE_ALIAS = 'Umb.Store.DocumentBlueprint';
export const DOCUMENT_BLUEPRINT_TREE_STORE_ALIAS = 'Umb.Store.DocumentBlueprintTree';
export const UMB_DOCUMENT_BLUEPRINT_STORE_ALIAS = 'Umb.Store.DocumentBlueprint';
export const UMB_DOCUMENT_BLUEPRINT_TREE_STORE_ALIAS = 'Umb.Store.DocumentBlueprintTree';
const store: ManifestStore = {
type: 'store',
alias: DOCUMENT_BLUEPRINT_STORE_ALIAS,
alias: UMB_DOCUMENT_BLUEPRINT_STORE_ALIAS,
name: 'Document Blueprint Store',
api: UmbDocumentBlueprintStore,
};
const treeStore: ManifestTreeStore = {
type: 'treeStore',
alias: DOCUMENT_BLUEPRINT_TREE_STORE_ALIAS,
alias: UMB_DOCUMENT_BLUEPRINT_TREE_STORE_ALIAS,
name: 'Document Blueprint Tree Store',
api: UmbDocumentBlueprintTreeStore,
};

View File

@@ -1,7 +1,7 @@
import {
DOCUMENT_TYPE_ENTITY_TYPE,
DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
UMB_DOCUMENT_TYPE_ENTITY_TYPE,
UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
} from '../../index.js';
import { DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS } from '../../repository/index.js';
import { UmbCreateDataTypeEntityAction } from './create.action.js';
@@ -18,7 +18,11 @@ const entityActions: Array<ManifestTypes> = [
icon: 'icon-add',
label: 'Create...',
repositoryAlias: DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
entityTypes: [DOCUMENT_TYPE_ENTITY_TYPE, DOCUMENT_TYPE_ROOT_ENTITY_TYPE, DOCUMENT_TYPE_FOLDER_ENTITY_TYPE],
entityTypes: [
UMB_DOCUMENT_TYPE_ENTITY_TYPE,
UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE,
],
},
},
{

View File

@@ -2,6 +2,6 @@ import './components/index.js';
export * from './repository/index.js';
export const DOCUMENT_TYPE_ROOT_ENTITY_TYPE = 'document-type-root';
export const DOCUMENT_TYPE_ENTITY_TYPE = 'document-type';
export const DOCUMENT_TYPE_FOLDER_ENTITY_TYPE = 'document-type-folder';
export const UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE = 'document-type-root';
export const UMB_DOCUMENT_TYPE_ENTITY_TYPE = 'document-type';
export const UMB_DOCUMENT_TYPE_FOLDER_ENTITY_TYPE = 'document-type-folder';

View File

@@ -1,4 +1,4 @@
import { DOCUMENT_TYPE_TREE_ALIAS } from '../tree/manifests.js';
import { UMB_DOCUMENT_TYPE_TREE_ALIAS } from '../tree/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
const menuItem: ManifestTypes = {
@@ -8,7 +8,7 @@ const menuItem: ManifestTypes = {
name: 'Document Types Menu Item',
weight: 900,
meta: {
treeAlias: DOCUMENT_TYPE_TREE_ALIAS,
treeAlias: UMB_DOCUMENT_TYPE_TREE_ALIAS,
label: 'Document Types',
icon: 'icon-folder',
menus: ['Umb.Menu.Settings'],

View File

@@ -1,3 +1,6 @@
export { UmbDocumentTypeDetailRepository } from './document-type-detail.repository.js';
export { DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS, DOCUMENT_TYPE_DETAIL_STORE_ALIAS } from './manifests.js';
export {
UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS as DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
UMB_DOCUMENT_TYPE_DETAIL_STORE_ALIAS as DOCUMENT_TYPE_DETAIL_STORE_ALIAS,
} from './manifests.js';
export { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT } from './document-type-detail.store.js';

View File

@@ -2,19 +2,19 @@ import { UmbDocumentTypeDetailRepository } from './document-type-detail.reposito
import { UmbDocumentTypeDetailStore } from './document-type-detail.store.js';
import { ManifestRepository, ManifestStore } from '@umbraco-cms/backoffice/extension-registry';
export const DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Detail';
export const DOCUMENT_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.DocumentType.Detail';
export const UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Detail';
export const UMB_DOCUMENT_TYPE_DETAIL_STORE_ALIAS = 'Umb.Store.DocumentType.Detail';
const detailRepository: ManifestRepository = {
type: 'repository',
alias: DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
alias: UMB_DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
name: 'Document Types Repository',
api: UmbDocumentTypeDetailRepository,
};
const detailStore: ManifestStore = {
type: 'store',
alias: DOCUMENT_TYPE_DETAIL_STORE_ALIAS,
alias: UMB_DOCUMENT_TYPE_DETAIL_STORE_ALIAS,
name: 'Document Type Store',
api: UmbDocumentTypeDetailStore,
};

View File

@@ -1,3 +1,6 @@
export { UmbDocumentTypeItemRepository } from './document-type-item.repository.js';
export { DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS, DOCUMENT_TYPE_ITEM_STORE_ALIAS } from './manifests.js';
export {
UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS as DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
UMB_DOCUMENT_TYPE_ITEM_STORE_ALIAS as DOCUMENT_TYPE_ITEM_STORE_ALIAS,
} from './manifests.js';
export * from './types.js';

View File

@@ -2,19 +2,19 @@ import { UmbDocumentTypeItemRepository } from './document-type-item.repository.j
import { UmbDocumentTypeItemStore } from './document-type-item.store.js';
import { ManifestItemStore, ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
export const DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Item';
export const DOCUMENT_TYPE_ITEM_STORE_ALIAS = 'Umb.Store.DocumentType.Item';
export const UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.DocumentType.Item';
export const UMB_DOCUMENT_TYPE_ITEM_STORE_ALIAS = 'Umb.Store.DocumentType.Item';
const itemRepository: ManifestRepository = {
type: 'repository',
alias: DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
alias: UMB_DOCUMENT_TYPE_ITEM_REPOSITORY_ALIAS,
name: 'Document Type Item Repository',
api: UmbDocumentTypeItemRepository,
};
const itemStore: ManifestItemStore = {
type: 'itemStore',
alias: DOCUMENT_TYPE_ITEM_STORE_ALIAS,
alias: UMB_DOCUMENT_TYPE_ITEM_STORE_ALIAS,
name: 'Document Type Item Store',
api: UmbDocumentTypeItemStore,
};

View File

@@ -1,4 +1,4 @@
import { DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from '../index.js';
import { UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from '../index.js';
import { UmbDocumentTypeTreeServerDataSource } from './document-type.tree.server.data-source.js';
import { UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT } from './document-type.tree.store.js';
import { UmbDocumentTypeTreeItemModel, UmbDocumentTypeTreeRootModel } from './types.js';
@@ -17,7 +17,7 @@ export class UmbDocumentTypeTreeRepository
async requestTreeRoot() {
const data = {
id: null,
type: DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
type: UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE,
name: 'Document Types',
icon: 'icon-folder',
hasChildren: true,

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