merge
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"cssVariables.lookupFiles": ["node_modules/@umbraco-ui/uui-css/dist/custom-properties.css"],
|
||||
"cSpell.words": ["combobox", "variantable"],
|
||||
"cSpell.words": ["combobox", "templating", "variantable"],
|
||||
"exportall.config.folderListener": [],
|
||||
"exportall.config.relExclusion": []
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
import { UmbContextConsumer } from './context-consumer';
|
||||
import { UmbContextCallback } from './context-request.event';
|
||||
import type { UmbControllerHostInterface, UmbControllerInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
import { isUmbContextProvideEventType, umbContextProvideEventType } from '../provide/context-provide.event';
|
||||
import { UmbContextRequestEventImplementation, UmbContextCallback } from './context-request.event';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
|
||||
export const umbContextRequestEventType = 'umb:context-request';
|
||||
export const umbDebugContextEventType = 'umb:debug-contexts';
|
||||
@@ -33,9 +33,8 @@ export const isUmbContextRequestEvent = (event: Event): event is UmbContextReque
|
||||
return event.type === umbContextRequestEventType;
|
||||
};
|
||||
|
||||
|
||||
export class UmbContextDebugRequest extends Event {
|
||||
public constructor(public readonly callback:any) {
|
||||
public constructor(public readonly callback: any) {
|
||||
super(umbDebugContextEventType, { bubbles: true, composed: true, cancelable: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ export * from './consume/context-request.event';
|
||||
export * from './provide/context-provider.controller';
|
||||
export * from './provide/context-provider';
|
||||
export * from './provide/context-provide.event';
|
||||
export * from './context-token';
|
||||
export * from './token/context-token';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
|
||||
export const umbContextProvideEventType = 'umb:context-provide';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
import { UmbContextProvider } from './context-provider';
|
||||
import type { UmbControllerHostInterface, UmbControllerInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { umbContextRequestEventType, isUmbContextRequestEvent, umbDebugContextEventType } from '../consume/context-request.event';
|
||||
import { UmbContextToken } from '../context-token';
|
||||
import {
|
||||
umbContextRequestEventType,
|
||||
isUmbContextRequestEvent,
|
||||
umbDebugContextEventType,
|
||||
} from '../consume/context-request.event';
|
||||
import { UmbContextToken } from '../token/context-token';
|
||||
import { UmbContextProvideEventImplementation } from './context-provide.event';
|
||||
|
||||
/**
|
||||
@@ -68,14 +72,14 @@ export class UmbContextProvider<HostType extends EventTarget = EventTarget> {
|
||||
|
||||
private _handleDebugContextRequest = (event: any) => {
|
||||
// If the event doesn't have an instances property, create it.
|
||||
if(!event.instances){
|
||||
if (!event.instances) {
|
||||
event.instances = new Map();
|
||||
}
|
||||
|
||||
// If the event doesn't have an instance for this context, add it.
|
||||
// Nearest to the DOM element of <umb-debug> will be added first
|
||||
// as contexts can change/override deeper in the DOM
|
||||
if(!event.instances.has(this._contextAlias)){
|
||||
if (!event.instances.has(this._contextAlias)) {
|
||||
event.instances.set(this._contextAlias, this.#instance);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from '@open-wc/testing';
|
||||
import { UmbContextConsumer } from './consume/context-consumer';
|
||||
import { UmbContextConsumer } from '../consume/context-consumer';
|
||||
import { UmbContextProvider } from '../provide/context-provider';
|
||||
import { UmbContextToken } from './context-token';
|
||||
import { UmbContextProvider } from './provide/context-provider';
|
||||
|
||||
const testContextAlias = 'my-test-context';
|
||||
|
||||
@@ -47,7 +47,7 @@ export class UmbExtensionRegistry {
|
||||
const nextData = this._kinds
|
||||
.getValue()
|
||||
.filter(
|
||||
(k) => k.matchType !== (kind as ManifestKind).matchType && k.matchKind !== (kind as ManifestKind).matchKind
|
||||
(k) => !(k.matchType === (kind as ManifestKind).matchType && k.matchKind === (kind as ManifestKind).matchKind)
|
||||
);
|
||||
nextData.push(kind as ManifestKind);
|
||||
this._kinds.next(nextData);
|
||||
@@ -125,7 +125,10 @@ export class UmbExtensionRegistry {
|
||||
T extends ManifestBase = SpecificManifestTypeOrManifestBase<Key>
|
||||
>(type: Key, alias: string) {
|
||||
return combineLatest([
|
||||
this.extensions.pipe(map((exts) => exts.find((ext) => ext.type === type && ext.alias === alias))),
|
||||
this.extensions.pipe(
|
||||
map((exts) => exts.find((ext) => ext.type === type && ext.alias === alias)),
|
||||
distinctUntilChanged(extensionSingleMemoization)
|
||||
),
|
||||
this._kindsOfType(type),
|
||||
]).pipe(
|
||||
map(([ext, kinds]) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { ManifestMenu } from './menu.models';
|
||||
import type { ManifestMenuItem, ManifestMenuItemTreeKind } from './menu-item.models';
|
||||
import type { ManifestTheme } from './theme.models';
|
||||
import type { ManifestTree } from './tree.models';
|
||||
import type { ManifestTreeItem } from './tree-item.models';
|
||||
import type { ManifestUserDashboard } from './user-dashboard.models';
|
||||
import type { ManifestWorkspace } from './workspace.models';
|
||||
import type { ManifestWorkspaceAction } from './workspace-action.models';
|
||||
@@ -44,6 +45,7 @@ export * from './menu.models';
|
||||
export * from './menu-item.models';
|
||||
export * from './theme.models';
|
||||
export * from './tree.models';
|
||||
export * from './tree-item.models';
|
||||
export * from './user-dashboard.models';
|
||||
export * from './workspace-action.models';
|
||||
export * from './workspace-view-collection.models';
|
||||
@@ -78,6 +80,7 @@ export type ManifestTypes =
|
||||
| ManifestMenuItemTreeKind
|
||||
| ManifestTheme
|
||||
| ManifestTree
|
||||
| ManifestTreeItem
|
||||
| ManifestUserDashboard
|
||||
| ManifestWorkspace
|
||||
| ManifestWorkspaceAction
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { ManifestClass } from './models';
|
||||
import { UmbStoreBase, UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
export interface ManifestStore extends ManifestClass<UmbStoreBase> {
|
||||
type: 'store';
|
||||
}
|
||||
|
||||
export interface ManifestTreeStore extends ManifestClass<UmbTreeStoreBase> {
|
||||
export interface ManifestTreeStore extends ManifestClass<UmbTreeStore> {
|
||||
type: 'treeStore';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { ManifestElement } from './models';
|
||||
|
||||
export interface ManifestTreeItem extends ManifestElement {
|
||||
type: 'treeItem';
|
||||
conditions: ConditionsTreeItem;
|
||||
}
|
||||
|
||||
export interface ConditionsTreeItem {
|
||||
entityType: string;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { ManifestBase } from './models';
|
||||
import type { ClassConstructor } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
export interface ManifestTree extends ManifestBase {
|
||||
type: 'tree';
|
||||
@@ -7,6 +6,5 @@ export interface ManifestTree extends ManifestBase {
|
||||
}
|
||||
|
||||
export interface MetaTree {
|
||||
storeAlias?: string;
|
||||
repository?: ClassConstructor<unknown>;
|
||||
repositoryAlias: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface UmbDataSource<T> {
|
||||
createScaffold(parentKey: string | null): Promise<DataSourceResponse<T>>;
|
||||
get(key: string): Promise<DataSourceResponse<T>>;
|
||||
insert(data: T): Promise<DataSourceResponse<T>>;
|
||||
update(data: T): Promise<DataSourceResponse<T>>;
|
||||
delete(key: string): Promise<DataSourceResponse<T>>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './data-source-response.interface';
|
||||
export * from './data-source.interface';
|
||||
export * from './tree-data-source.interface';
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface UmbTreeDataSource<PagedItemsType = any, ItemsType = any> {
|
||||
getRootItems(): Promise<DataSourceResponse<PagedItemsType>>;
|
||||
getChildrenOf(parentUnique: string): Promise<DataSourceResponse<PagedItemsType>>;
|
||||
getItems(unique: Array<string>): Promise<DataSourceResponse<Array<ItemsType>>>;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
export * from './data-source-response.interface';
|
||||
export * from './data-source';
|
||||
export * from './detail-repository.interface';
|
||||
export * from './tree-repository.interface';
|
||||
export * from './repository-tree-data-source.interface';
|
||||
export * from './repository-detail-data-source.interface';
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface RepositoryDetailDataSource<DetailType> {
|
||||
createScaffold(parentKey: string | null): Promise<DataSourceResponse<DetailType>>;
|
||||
get(key: string): Promise<DataSourceResponse<DetailType>>;
|
||||
insert(data: DetailType): Promise<DataSourceResponse<DetailType>>;
|
||||
update(data: DetailType): Promise<DataSourceResponse<DetailType>>;
|
||||
delete(key: string): Promise<DataSourceResponse<DetailType>>;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { EntityTreeItemResponseModel, PagedEntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface RepositoryTreeDataSource {
|
||||
getRootItems(): Promise<DataSourceResponse<PagedEntityTreeItemResponseModel>>;
|
||||
getChildrenOf(parentKey: string): Promise<DataSourceResponse<PagedEntityTreeItemResponseModel>>;
|
||||
getItems(key: Array<string>): Promise<DataSourceResponse<EntityTreeItemResponseModel[]>>;
|
||||
}
|
||||
@@ -1,24 +1,29 @@
|
||||
import type { Observable } from 'rxjs';
|
||||
import { EntityTreeItemResponseModel, PagedEntityTreeItemResponseModel, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export interface UmbTreeRepository {
|
||||
requestRootTreeItems: () => Promise<{
|
||||
data: PagedEntityTreeItemResponseModel | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<EntityTreeItemResponseModel[]>;
|
||||
}>;
|
||||
requestTreeItemsOf: (parentKey: string | null) => Promise<{
|
||||
data: PagedEntityTreeItemResponseModel | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<EntityTreeItemResponseModel[]>;
|
||||
}>;
|
||||
requestTreeItems: (keys: string[]) => Promise<{
|
||||
data: Array<EntityTreeItemResponseModel> | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<EntityTreeItemResponseModel[]>;
|
||||
}>;
|
||||
|
||||
rootTreeItems: () => Promise<Observable<EntityTreeItemResponseModel[]>>;
|
||||
treeItemsOf: (parentKey: string | null) => Promise<Observable<EntityTreeItemResponseModel[]>>;
|
||||
treeItems: (keys: string[]) => Promise<Observable<EntityTreeItemResponseModel[]>>;
|
||||
export interface UmbPagedData<T> {
|
||||
total: number;
|
||||
items: Array<T>;
|
||||
}
|
||||
|
||||
export interface UmbTreeRepository<ItemType = any, PagedItemType = UmbPagedData<ItemType>> {
|
||||
requestRootTreeItems: () => Promise<{
|
||||
data: PagedItemType | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<ItemType[]>;
|
||||
}>;
|
||||
requestTreeItemsOf: (parentUnique: string | null) => Promise<{
|
||||
data: PagedItemType | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<ItemType[]>;
|
||||
}>;
|
||||
requestTreeItems: (uniques: string[]) => Promise<{
|
||||
data: Array<ItemType> | undefined;
|
||||
error: ProblemDetailsModel | undefined;
|
||||
asObservable?: () => Observable<ItemType[]>;
|
||||
}>;
|
||||
|
||||
rootTreeItems: () => Promise<Observable<ItemType[]>>;
|
||||
treeItemsOf: (parentUnique: string | null) => Promise<Observable<ItemType[]>>;
|
||||
treeItems: (uniques: string[]) => Promise<Observable<ItemType[]>>;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbTreeStoreBase
|
||||
* @class UmbEntityTreeStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - General Tree Data Store
|
||||
*/
|
||||
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
|
||||
export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
export class UmbEntityTreeStore extends UmbStoreBase implements UmbTreeStore<EntityTreeItemResponseModel> {
|
||||
#data = new ArrayState<EntityTreeItemResponseModel>([], (x) => x.key);
|
||||
|
||||
/**
|
||||
* Appends items to the store
|
||||
* @param {Array<EntityTreeItemModel>} items
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @param {Array<EntityTreeItemResponseModel>} items
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
appendItems(items: Array<EntityTreeItemResponseModel>) {
|
||||
this.#data.append(items);
|
||||
@@ -24,8 +23,8 @@ export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
/**
|
||||
* Updates an item in the store
|
||||
* @param {string} key
|
||||
* @param {Partial<EntityTreeItemModel>} data
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @param {Partial<EntityTreeItemResponseModel>} data
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
updateItem(key: string, data: Partial<EntityTreeItemResponseModel>) {
|
||||
this.#data.next(partialUpdateFrozenArray(this.#data.getValue(), data, (entry) => entry.key === key));
|
||||
@@ -34,7 +33,7 @@ export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
/**
|
||||
* Removes an item from the store
|
||||
* @param {string} key
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
removeItem(key: string) {
|
||||
this.#data.removeOne(key);
|
||||
@@ -42,7 +41,7 @@ export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
|
||||
/**
|
||||
* An observable to observe the root items
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
|
||||
|
||||
@@ -50,7 +49,7 @@ export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
* Returns an observable to observe the children of a given parent
|
||||
* @param {(string | null)} parentKey
|
||||
* @return {*}
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
childrenOf(parentKey: string | null) {
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === parentKey));
|
||||
@@ -60,7 +59,7 @@ export class UmbTreeStoreBase extends UmbStoreBase {
|
||||
* Returns an observable to observe the items with the given keys
|
||||
* @param {Array<string>} keys
|
||||
* @return {*}
|
||||
* @memberof UmbTreeStoreBase
|
||||
* @memberof UmbEntityTreeStore
|
||||
*/
|
||||
items(keys: Array<string>) {
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
|
||||
@@ -0,0 +1,67 @@
|
||||
import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbFileSystemTreeStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - General Tree Data Store
|
||||
*/
|
||||
export class UmbFileSystemTreeStore extends UmbStoreBase implements UmbTreeStore<FileSystemTreeItemPresentationModel> {
|
||||
#data = new ArrayState<FileSystemTreeItemPresentationModel>([], (x) => x.path);
|
||||
|
||||
/**
|
||||
* Appends items to the store
|
||||
* @param {Array<FileSystemTreeItemPresentationModel>} items
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
appendItems(items: Array<FileSystemTreeItemPresentationModel>) {
|
||||
this.#data.append(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an item in the store
|
||||
* @param {string} path
|
||||
* @param {Partial<FileSystemTreeItemPresentationModel>} data
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
updateItem(path: string, data: Partial<FileSystemTreeItemPresentationModel>) {
|
||||
this.#data.appendOne(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the store
|
||||
* @param {string} path
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
removeItem(path: string) {
|
||||
this.#data.removeOne(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* An observable to observe the root items
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.path?.includes('/') === false));
|
||||
|
||||
/**
|
||||
* Returns an observable to observe the children of a given parent
|
||||
* @param {(string | null)} parentPath
|
||||
* @return {*}
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
childrenOf(parentPath: string | null) {
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => item.path?.startsWith(parentPath + '/')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an observable to observe the items with the given keys
|
||||
* @param {Array<string>} paths
|
||||
* @return {*}
|
||||
* @memberof UmbFileSystemTreeStore
|
||||
*/
|
||||
items(paths: Array<string>) {
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => paths.includes(item.path ?? '')));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './store';
|
||||
export * from './store-base';
|
||||
export * from './tree-store-base';
|
||||
export * from './entity-tree-store';
|
||||
export * from './file-system-tree.store';
|
||||
export * from './tree-store.interface';
|
||||
|
||||
@@ -9,33 +9,7 @@ export interface UmbDataStore {
|
||||
readonly storeAlias: string;
|
||||
}
|
||||
|
||||
export interface UmbTreeStore<T> extends UmbDataStore {
|
||||
|
||||
getTreeRoot(): Observable<Array<T>>;
|
||||
|
||||
getTreeItemChildren(key: string): Observable<Array<T>>;
|
||||
|
||||
// Notice: this might not be right to put here as only some content items has ability to be trashed.
|
||||
/**
|
||||
* @description - Trash data.
|
||||
* @param {string[]} keys
|
||||
* @return {*} {(Promise<void>)}
|
||||
* @memberof UmbTreeStore
|
||||
*/
|
||||
trash(keys: string[]): Promise<void>;
|
||||
|
||||
// Notice: this might not be right to put here as only some content items has ability to be moved.
|
||||
/**
|
||||
* @description - Move data.
|
||||
* @param {string[]} keys
|
||||
* @return {*} {(Promise<void>)}
|
||||
* @memberof UmbTreeStore
|
||||
*/
|
||||
move(keys: string[], destination: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface UmbEntityDetailStore<T> extends UmbDataStore {
|
||||
|
||||
/**
|
||||
* @description - Request scaffold data by entityType and . The data is added to the store and is returned as an Observable.
|
||||
* @param {string} key
|
||||
@@ -61,10 +35,7 @@ export interface UmbEntityDetailStore<T> extends UmbDataStore {
|
||||
save(data: T[]): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
export interface UmbContentStore<T> extends UmbEntityDetailStore<T> {
|
||||
|
||||
// TODO: make something that is specific for UmbContentStore, or then we should get rid of it. But for now i kept it as we might want this for rollback or other things specific to Content types.
|
||||
save(data: T[]): Promise<void>;
|
||||
|
||||
}
|
||||
|
||||
12
src/Umbraco.Web.UI.Client/libs/store/tree-store.interface.ts
Normal file
12
src/Umbraco.Web.UI.Client/libs/store/tree-store.interface.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Observable } from 'rxjs';
|
||||
import { TreeItemPresentationModel } from '../backend-api';
|
||||
|
||||
export interface UmbTreeStore<T extends TreeItemPresentationModel = any> {
|
||||
appendItems: (items: Array<T>) => void;
|
||||
updateItem: (unique: string, item: Partial<T>) => void;
|
||||
removeItem: (unique: string) => void;
|
||||
|
||||
rootItems: Observable<Array<T>>;
|
||||
childrenOf: (parentUnique: string | null) => Observable<Array<T>>;
|
||||
items: (uniques: Array<string>) => Observable<Array<T>>;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UmbWorkspaceContextInterface } from '../../../../src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface';
|
||||
import { UmbWorkspaceContextInterface } from '../../context/workspace-context.interface';
|
||||
import { UmbWorkspaceActionBase } from '../workspace-action-base';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './workspace-context.interface';
|
||||
@@ -7,6 +7,7 @@ export interface UmbWorkspaceContextInterface<T = unknown> {
|
||||
isNew: Observable<boolean>;
|
||||
getIsNew(): boolean;
|
||||
setIsNew(value: boolean): void;
|
||||
// TODO: should we consider another name than entity type. File system files are not entities but still have this type.
|
||||
getEntityType(): string;
|
||||
getData(): T;
|
||||
destroy(): void;
|
||||
@@ -1 +1,2 @@
|
||||
export * from './actions';
|
||||
export * from './context';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export const UMB_DOCUMENT_BLUEPRINT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentBlueprintTreeStore>(
|
||||
@@ -12,7 +12,7 @@ export const UMB_DOCUMENT_BLUEPRINT_TREE_STORE_CONTEXT_TOKEN = new UmbContextTok
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Tree Data Store for Document Blueprints
|
||||
*/
|
||||
export class UmbDocumentBlueprintTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbDocumentBlueprintTreeStore extends UmbEntityTreeStore {
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UMB_DOCUMENT_BLUEPRINT_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ import { DocumentTypeTreeServerDataSource } from './sources/document-type.tree.s
|
||||
import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data';
|
||||
import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store';
|
||||
import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store';
|
||||
import type {
|
||||
RepositoryTreeDataSource,
|
||||
UmbTreeRepository,
|
||||
UmbDetailRepository,
|
||||
} from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ProblemDetailsModel, DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -14,12 +10,12 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco
|
||||
|
||||
type ItemType = DocumentTypeResponseModel;
|
||||
|
||||
export class UmbDocumentTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
|
||||
export class UmbDocumentTypeRepository implements UmbTreeRepository<ItemType>, UmbDetailRepository<ItemType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbDocumentTypeTreeStore;
|
||||
|
||||
#detailDataSource: UmbDocumentTypeServerDataSource;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
* @description - Tree Data Store for Document-Types
|
||||
*/
|
||||
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
|
||||
export class UmbDocumentTypeTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbDocumentTypeTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbDocumentTypeTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { DocumentTypeResource, ProblemDetailsModel, DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
DocumentTypeResource,
|
||||
ProblemDetailsModel,
|
||||
DocumentTypeResponseModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
@@ -9,7 +13,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class UmbDocumentTypeServerDataSource
|
||||
* @implements {RepositoryDetailDataSource}
|
||||
*/
|
||||
export class UmbDocumentTypeServerDataSource implements RepositoryDetailDataSource<DocumentTypeResponseModel> {
|
||||
export class UmbDocumentTypeServerDataSource implements UmbDataSource<DocumentTypeResponseModel> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel, DocumentTypeResource } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class DocumentTreeServerDataSource
|
||||
* @implements {DocumentTreeDataSource}
|
||||
*/
|
||||
export class DocumentTypeTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class DocumentTypeTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
// TODO: how do we handle trashed items?
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { UmbDocumentTypeRepository } from '../repository/document-type.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { DOCUMENT_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.DocumentTypes',
|
||||
name: 'Document Types Tree',
|
||||
meta: {
|
||||
repository: UmbDocumentTypeRepository,
|
||||
repositoryAlias: DOCUMENT_TYPE_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.DocumentType',
|
||||
name: 'Document Type Tree Item',
|
||||
conditions: {
|
||||
entityType: 'document-type',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -105,7 +105,18 @@ export class UmbDocumentTypeWorkspaceEditElement extends UmbLitElement {
|
||||
</uui-input>
|
||||
</div>
|
||||
|
||||
<div slot="footer">Keyboard Shortcuts</div>
|
||||
<div slot="footer">
|
||||
<uui-button label="Show keyboard shortcuts">
|
||||
Keyboard Shortcuts
|
||||
<uui-keyboard-shortcut>
|
||||
<uui-key>ALT</uui-key>
|
||||
+
|
||||
<uui-key>shift</uui-key>
|
||||
+
|
||||
<uui-key>k</uui-key>
|
||||
</uui-keyboard-shortcut>
|
||||
</uui-button>
|
||||
</div>
|
||||
</umb-workspace-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,52 @@ const workspaceViews: Array<ManifestWorkspaceView> = [
|
||||
meta: {
|
||||
label: 'Design',
|
||||
pathname: 'design',
|
||||
icon: 'edit',
|
||||
icon: 'umb:document-dashed-line',
|
||||
},
|
||||
conditions: {
|
||||
workspaces: ['Umb.Workspace.DocumentType'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'workspaceView',
|
||||
alias: 'Umb.WorkspaceView.DocumentType.ListView',
|
||||
name: 'Document Type Workspace List View',
|
||||
loader: () => import('./views/listview/workspace-view-document-type-listview.element'),
|
||||
weight: 100,
|
||||
meta: {
|
||||
label: 'Listview',
|
||||
pathname: 'listview',
|
||||
icon: 'umb:list',
|
||||
},
|
||||
conditions: {
|
||||
workspaces: ['Umb.Workspace.DocumentType'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'workspaceView',
|
||||
alias: 'Umb.WorkspaceView.DocumentType.Permissions',
|
||||
name: 'Document Type Workspace Permissions View',
|
||||
loader: () => import('./views/permissions/workspace-view-document-type-permissions.element'),
|
||||
weight: 100,
|
||||
meta: {
|
||||
label: 'Permissions',
|
||||
pathname: 'permissions',
|
||||
icon: 'umb:keychain',
|
||||
},
|
||||
conditions: {
|
||||
workspaces: ['Umb.Workspace.DocumentType'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'workspaceView',
|
||||
alias: 'Umb.WorkspaceView.DocumentType.Templates',
|
||||
name: 'Document Type Workspace Templates View',
|
||||
loader: () => import('./views/templates/workspace-view-document-type-templates.element'),
|
||||
weight: 100,
|
||||
meta: {
|
||||
label: 'Templates',
|
||||
pathname: 'templates',
|
||||
icon: 'umb:layout',
|
||||
},
|
||||
conditions: {
|
||||
workspaces: ['Umb.Workspace.DocumentType'],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { customElement, query, state } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -13,8 +14,66 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
/* TODO: This should be replaced with a general workspace bar — naming is hard */
|
||||
#workspace-tab-bar {
|
||||
padding: 0 var(--uui-size-layout-1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: var(--uui-color-surface);
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.tab-actions {
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-4);
|
||||
}
|
||||
.tab-actions uui-button uui-icon {
|
||||
padding-right: calc(-1 * var(--uui-size-space-4));
|
||||
}
|
||||
|
||||
uui-tab {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
uui-tab .trash {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
uui-tab uui-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
uui-input:not(:focus) {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
uui-input:not(:hover, :focus) .trash {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/** Property Group Wrapper */
|
||||
|
||||
#wrapper {
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
|
||||
#add-group {
|
||||
margin-top: var(--uui-size-layout-1);
|
||||
width: 100%;
|
||||
--uui-button-height: var(--uui-size-layout-4);
|
||||
}
|
||||
|
||||
.group-headline {
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-4);
|
||||
}
|
||||
.group-headline uui-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -23,6 +82,9 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
|
||||
|
||||
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
|
||||
|
||||
@state()
|
||||
private _tabs: any[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -42,12 +104,70 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` Design of ${this._documentType?.name}
|
||||
<uui-box headline=${this._documentType?.name ?? ''}>
|
||||
<div>
|
||||
<umb-property-creator></umb-property-creator>
|
||||
return html`
|
||||
<div id="workspace-tab-bar">
|
||||
${this.renderTabBar()}
|
||||
<div class="tab-actions">
|
||||
<uui-button label="Compositions" look="outline" compact>
|
||||
<uui-icon name="umb:merge"></uui-icon>
|
||||
Compositions
|
||||
</uui-button>
|
||||
<uui-button label="Recorder" look="outline" compact>
|
||||
<uui-icon name="umb:navigation"></uui-icon>
|
||||
Recorder
|
||||
</uui-button>
|
||||
</div>
|
||||
</uui-box>`;
|
||||
</div>
|
||||
<div id="wrapper">
|
||||
<uui-box class="group-wrapper">
|
||||
<div class="group-headline" slot="headline">
|
||||
<uui-input label="Group name" value="${this._documentType?.name ?? ''}" size="10">
|
||||
<uui-button slot="append" label="Delete group" compact><uui-icon name="umb:trash"></uui-icon></uui-button>
|
||||
</uui-input>
|
||||
</div>
|
||||
<umb-property-creator></umb-property-creator>
|
||||
</uui-box>
|
||||
<uui-button label="Add group" id="add-group" look="placeholder">Add group</uui-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#remove(index: number) {
|
||||
this._tabs.splice(index, 1);
|
||||
this.requestUpdate();
|
||||
}
|
||||
async #addTab() {
|
||||
this._tabs = [...this._tabs, { name: 'Test' }];
|
||||
}
|
||||
|
||||
renderTabBar() {
|
||||
return html`<uui-tab-group>
|
||||
${repeat(
|
||||
this._tabs,
|
||||
(tab) => tab.name,
|
||||
(tab, index) => {
|
||||
//TODO: Should these tabs be part of routing?
|
||||
return html`<uui-tab label=${tab.name!} .active="${false}">
|
||||
<div>
|
||||
<uui-input label="${tab.name}" look="placeholder" value="" placeholder="Enter a name">
|
||||
<uui-button
|
||||
label="Remove tab"
|
||||
class="trash"
|
||||
slot="append"
|
||||
@click="${() => this.#remove(index)}"
|
||||
compact>
|
||||
<uui-icon name="umb:trash"></uui-icon>
|
||||
</uui-button>
|
||||
</uui-input>
|
||||
</div>
|
||||
</uui-tab>`;
|
||||
}
|
||||
)}
|
||||
<uui-button id="add-tab" @click="${this.#addTab}" label="Add tab" compact>
|
||||
<uui-icon name="umb:add"></uui-icon>
|
||||
Add tab
|
||||
</uui-button>
|
||||
</uui-tab-group>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
|
||||
import type { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
@customElement('umb-workspace-view-document-type-listview')
|
||||
export class UmbWorkspaceViewDocumentTypeListviewElement extends UmbLitElement {
|
||||
static styles = [UUITextStyles, css``];
|
||||
|
||||
@state()
|
||||
_documentType?: DocumentTypeResponseModel;
|
||||
|
||||
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken
|
||||
this.consumeContext<UmbWorkspaceDocumentTypeContext>('umbWorkspaceContext', (documentTypeContext) => {
|
||||
this._workspaceContext = documentTypeContext;
|
||||
this._observeDocumentType();
|
||||
});
|
||||
}
|
||||
|
||||
private _observeDocumentType() {
|
||||
if (!this._workspaceContext) return;
|
||||
|
||||
this.observe(this._workspaceContext.data, (documentType) => {
|
||||
this._documentType = documentType;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` Listview `;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbWorkspaceViewDocumentTypeListviewElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-workspace-view-document-type-listview': UmbWorkspaceViewDocumentTypeListviewElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
@customElement('umb-workspace-view-document-type-permissions')
|
||||
export class UmbWorkspaceViewDocumentTypePermissionsElement extends UmbLitElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
uui-label,
|
||||
umb-property-editor-ui-number {
|
||||
display: block;
|
||||
}
|
||||
|
||||
uui-toggle {
|
||||
display: flex;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
_documentType?: DocumentTypeResponseModel;
|
||||
|
||||
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken
|
||||
this.consumeContext<UmbWorkspaceDocumentTypeContext>('umbWorkspaceContext', (documentTypeContext) => {
|
||||
this._workspaceContext = documentTypeContext;
|
||||
this._observeDocumentType();
|
||||
});
|
||||
}
|
||||
|
||||
private _observeDocumentType() {
|
||||
if (!this._workspaceContext) return;
|
||||
|
||||
this.observe(this._workspaceContext.data, (documentType) => {
|
||||
this._documentType = documentType;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-box headline="Permissions">
|
||||
<umb-workspace-property-layout alias="Root" label="Allow as Root">
|
||||
<div slot="description">Allow editors to create content of this type in the root of the content tree.</div>
|
||||
<div slot="editor"><uui-toggle label="Allow as root"></uui-toggle></div>
|
||||
</umb-workspace-property-layout>
|
||||
<umb-workspace-property-layout alias="ChildNodeType" label="Allowed child node types">
|
||||
<div slot="description">
|
||||
Allow content of the specified types to be created underneath content of this type.
|
||||
</div>
|
||||
<div slot="editor">
|
||||
<umb-input-document-type-picker
|
||||
.currentDocumentType="${this._documentType}"></umb-input-document-type-picker>
|
||||
</div>
|
||||
</umb-workspace-property-layout>
|
||||
<umb-workspace-property-layout alias="VaryByNature" label="Allow vary by culture">
|
||||
<div slot="description">Allow editors to create content of different languages.</div>
|
||||
<div slot="editor"><uui-toggle label="Vary by culture"></uui-toggle></div>
|
||||
</umb-workspace-property-layout>
|
||||
<umb-workspace-property-layout alias="ElementType" label="Is an Element Type">
|
||||
<div slot="description">
|
||||
An Element Type is meant to be used for instance in Nested Content, and not in the tree.<br />
|
||||
A Document Type cannot be changed to an Element Type once it has been used to create one or more content
|
||||
items.
|
||||
</div>
|
||||
<div slot="editor"><uui-toggle label="Element type"></uui-toggle></div>
|
||||
</umb-workspace-property-layout>
|
||||
<umb-workspace-property-layout alias="HistoryCleanup" label="History cleanup">
|
||||
<div slot="description">Allow overriding the global history cleanup settings.</div>
|
||||
<div slot="editor">
|
||||
<uui-toggle .checked="${true}" label="Auto cleanup"></uui-toggle>
|
||||
<uui-label for="versions-newer-than-days">Keep all versions newer than days</uui-label>
|
||||
<umb-property-editor-ui-number id="versions-newer-than-days"></umb-property-editor-ui-number>
|
||||
<uui-label for="latest-version-per-day-days">Keep latest version per day for days</uui-label>
|
||||
<umb-property-editor-ui-number id="latest-version-per-day-days"></umb-property-editor-ui-number>
|
||||
</div>
|
||||
</umb-workspace-property-layout>
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbWorkspaceViewDocumentTypePermissionsElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-workspace-view-document-type-permissions': UmbWorkspaceViewDocumentTypePermissionsElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
@customElement('umb-workspace-view-document-type-templates')
|
||||
export class UmbWorkspaceViewDocumentTypeTemplatesElement extends UmbLitElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
|
||||
#templates {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#template-card-wrapper {
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-4);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
umb-workspace-property-layout {
|
||||
border-top: 1px solid var(--uui-color-border);
|
||||
}
|
||||
umb-workspace-property-layout:first-child {
|
||||
padding-top: 0;
|
||||
border: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
_documentType?: DocumentTypeResponseModel;
|
||||
|
||||
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext<UmbWorkspaceDocumentTypeContext>('umbWorkspaceContext', (documentTypeContext) => {
|
||||
this._workspaceContext = documentTypeContext;
|
||||
this._observeDocumentType();
|
||||
});
|
||||
}
|
||||
|
||||
private _observeDocumentType() {
|
||||
if (!this._workspaceContext) return;
|
||||
|
||||
this.observe(this._workspaceContext.data, (documentType) => {
|
||||
this._documentType = documentType;
|
||||
});
|
||||
}
|
||||
|
||||
async #changeDefaultKey(e: CustomEvent) {
|
||||
// save new default key
|
||||
console.log('workspace: default template key', e);
|
||||
}
|
||||
|
||||
#changeAllowedKeys(e: CustomEvent) {
|
||||
// save new allowed keys
|
||||
console.log('workspace: allowed templates changed', e);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-box headline="Templates">
|
||||
<umb-workspace-property-layout alias="Templates" label="Allowed Templates">
|
||||
<div slot="description">Choose which templates editors are allowed to use on content of this type</div>
|
||||
<div id="templates" slot="editor">
|
||||
<umb-input-template-picker
|
||||
.defaultKey="${this._documentType?.defaultTemplateKey ?? ''}"
|
||||
.allowedKeys="${this._documentType?.allowedTemplateKeys ?? []}"
|
||||
@change-default="${this.#changeDefaultKey}"
|
||||
@change-allowed="${this.#changeAllowedKeys}"></umb-input-template-picker>
|
||||
</div>
|
||||
</umb-workspace-property-layout>
|
||||
</uui-box>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbWorkspaceViewDocumentTypeTemplatesElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-workspace-view-document-type-templates': UmbWorkspaceViewDocumentTypeTemplatesElement;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { manifests as collectionManifests } from './collection/manifests';
|
||||
import { manifests as menuItemManifests } from './sidebar-menu-item/manifests';
|
||||
import { manifests as menuItemManifests } from './menu-item/manifests';
|
||||
import { manifests as repositoryManifests } from './repository/manifests';
|
||||
import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
|
||||
@@ -2,17 +2,17 @@ import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
@customElement('umb-document-sidebar-menu-item')
|
||||
export class UmbDocumentSidebarMenuItemElement extends UmbLitElement {
|
||||
@customElement('umb-document-menu-item')
|
||||
export class UmbDocumentMenuItemElement extends UmbLitElement {
|
||||
render() {
|
||||
return html`<umb-tree alias="Umb.Tree.Documents"></umb-tree>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbDocumentSidebarMenuItemElement;
|
||||
export default UmbDocumentMenuItemElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-sidebar-menu-item': UmbDocumentSidebarMenuItemElement;
|
||||
'umb-document-menu-item': UmbDocumentMenuItemElement;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ const menuItem: ManifestMenuItem = {
|
||||
alias: 'Umb.MenuItem.Documents',
|
||||
name: 'Documents Menu Item',
|
||||
weight: 100,
|
||||
loader: () => import('./document-sidebar-menu-item.element'),
|
||||
loader: () => import('./document-menu-item.element'),
|
||||
meta: {
|
||||
label: 'Documents',
|
||||
icon: 'umb:folder',
|
||||
@@ -0,0 +1,103 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import type { UmbTreeElement } from '../../../../shared/components/tree/tree.element';
|
||||
import { UmbDocumentTypePickerModalData, UmbDocumentTypePickerModalResult } from '.';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
|
||||
|
||||
// TODO: make use of UmbPickerLayoutBase
|
||||
@customElement('umb-document-type-picker-modal')
|
||||
export class UmbDocumentTypePickerModalElement extends UmbModalBaseElement<
|
||||
UmbDocumentTypePickerModalData,
|
||||
UmbDocumentTypePickerModalResult
|
||||
> {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
h3 {
|
||||
margin-left: var(--uui-size-space-5);
|
||||
margin-right: var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--uui-color-divider);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
#content-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
.content-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content-item.selected {
|
||||
background-color: var(--uui-color-selected);
|
||||
color: var(--uui-color-selected-contrast);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
_selection: Array<string> = [];
|
||||
|
||||
@state()
|
||||
_multiple = true;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._selection = this.data?.selection ?? [];
|
||||
this._multiple = this.data?.multiple ?? true;
|
||||
}
|
||||
|
||||
private _handleSelectionChange(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const element = e.target as UmbTreeElement;
|
||||
//TODO: Should multiple property be implemented here or be passed down into umb-tree?
|
||||
this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]];
|
||||
}
|
||||
|
||||
private _submit() {
|
||||
this.modalHandler?.submit({ selection: this._selection });
|
||||
}
|
||||
|
||||
private _close() {
|
||||
this.modalHandler?.reject();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-layout headline="Select Content">
|
||||
<uui-box>
|
||||
<uui-input></uui-input>
|
||||
<hr />
|
||||
<umb-tree
|
||||
alias="Umb.Tree.DocumentTypes"
|
||||
@selected=${this._handleSelectionChange}
|
||||
.selection=${this._selection}
|
||||
selectable></umb-tree>
|
||||
</uui-box>
|
||||
<div slot="actions">
|
||||
<uui-button label="Close" @click=${this._close}></uui-button>
|
||||
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
|
||||
</div>
|
||||
</umb-workspace-layout>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbDocumentTypePickerModalElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-type-picker-modal': UmbDocumentTypePickerModalElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import '../../../../shared/components/body-layout/body-layout.element';
|
||||
import './document-type-picker-modal.element';
|
||||
|
||||
import { Meta, Story } from '@storybook/web-components';
|
||||
import { html } from 'lit';
|
||||
|
||||
import type { UmbDocumentTypePickerModalElement } from './document-type-picker-modal.element';
|
||||
import type { UmbDocumentTypePickerModalData } from './index';
|
||||
|
||||
export default {
|
||||
title: 'API/Modals/Layouts/Content Picker',
|
||||
component: 'umb-document-type-picker-modal',
|
||||
id: 'umb-document-type-picker-modal',
|
||||
} as Meta;
|
||||
|
||||
const data: UmbDocumentTypePickerModalData = {
|
||||
multiple: true,
|
||||
selection: [],
|
||||
};
|
||||
|
||||
export const Overview: Story<UmbDocumentTypePickerModalElement> = () => html`
|
||||
<!-- TODO: figure out if generics are allowed for properties:
|
||||
https://github.com/runem/lit-analyzer/issues/149
|
||||
https://github.com/runem/lit-analyzer/issues/163 -->
|
||||
<umb-document-picker-modal .data=${data as any}></umb-document-picker-modal>
|
||||
`;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbDocumentTypePickerModalData {
|
||||
multiple?: boolean;
|
||||
selection?: Array<string>;
|
||||
}
|
||||
|
||||
export interface UmbDocumentTypePickerModalResult {
|
||||
selection: Array<string>;
|
||||
}
|
||||
|
||||
export const UMB_DOCUMENT_TYPE_PICKER_MODAL_TOKEN = new UmbModalToken<
|
||||
UmbDocumentTypePickerModalData,
|
||||
UmbDocumentTypePickerModalResult
|
||||
>('Umb.Modal.DocumentTypePicker', {
|
||||
type: 'sidebar',
|
||||
size: 'small',
|
||||
});
|
||||
@@ -7,6 +7,12 @@ const modals: Array<ManifestModal> = [
|
||||
name: 'Document Picker Modal',
|
||||
loader: () => import('./document-picker/document-picker-modal.element'),
|
||||
},
|
||||
{
|
||||
type: 'modal',
|
||||
alias: 'Umb.Modal.DocumentTypePicker',
|
||||
name: 'Document Type Picker Modal',
|
||||
loader: () => import('./document-type-picker/document-type-picker-modal.element'),
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...modals];
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { DocumentTreeServerDataSource } from './sources/document.tree.server.data';
|
||||
import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store';
|
||||
import { UmbDocumentStore, UMB_DOCUMENT_STORE_CONTEXT_TOKEN } from './document.store';
|
||||
import { UmbDocumentServerDataSource } from './sources/document.server.data';
|
||||
import type {
|
||||
RepositoryTreeDataSource,
|
||||
UmbTreeRepository,
|
||||
UmbDetailRepository,
|
||||
} from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDocumentStore, UMB_DOCUMENT_STORE_CONTEXT_TOKEN } from './document.store';
|
||||
import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store';
|
||||
import { DocumentTreeServerDataSource } from './sources/document.tree.server.data';
|
||||
import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ProblemDetailsModel, DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -14,16 +10,12 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco
|
||||
|
||||
type ItemType = DocumentResponseModel;
|
||||
|
||||
// Move to documentation / JSdoc
|
||||
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
|
||||
// element -> context -> repository -> (store) -> data source
|
||||
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
|
||||
export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
|
||||
export class UmbDocumentRepository implements UmbTreeRepository<ItemType>, UmbDetailRepository<ItemType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbDocumentTreeStore;
|
||||
|
||||
#detailDataSource: UmbDocumentServerDataSource;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbDocumentTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @extends {UmbEntityTreeStore}
|
||||
* @description - Tree Data Store for Templates
|
||||
*/
|
||||
export class UmbDocumentTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbDocumentTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbDocumentTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
@@ -19,6 +19,4 @@ export class UmbDocumentTreeStore extends UmbTreeStoreBase {
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTreeStore>(
|
||||
'UmbDocumentTreeStore'
|
||||
);
|
||||
export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTreeStore>('UmbDocumentTreeStore');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
DocumentResource,
|
||||
ProblemDetailsModel,
|
||||
@@ -15,7 +15,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class UmbDocumentServerDataSource
|
||||
* @implements {RepositoryDetailDataSource}
|
||||
*/
|
||||
export class UmbDocumentServerDataSource implements RepositoryDetailDataSource<DocumentResponseModel> {
|
||||
export class UmbDocumentServerDataSource implements UmbDataSource<DocumentResponseModel> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel, DocumentResource } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class DocumentTreeServerDataSource
|
||||
* @implements {DocumentTreeDataSource}
|
||||
*/
|
||||
export class DocumentTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class DocumentTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
// TODO: how do we handle trashed items?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { DocumentResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { RepositoryDetailDataSource, DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource, DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface UmbDocumentDataSource extends RepositoryDetailDataSource<DocumentResponseModel> {
|
||||
export interface UmbDocumentDataSource extends UmbDataSource<DocumentResponseModel> {
|
||||
createScaffold(documentTypeKey: string): Promise<DataSourceResponse<DocumentResponseModel>>;
|
||||
trash(key: string): Promise<DataSourceResponse<DocumentResponseModel>>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbDocumentRepository } from '../repository/document.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { DOCUMENT_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const treeAlias = 'Umb.Tree.Documents';
|
||||
|
||||
@@ -8,8 +8,18 @@ const tree: ManifestTree = {
|
||||
alias: treeAlias,
|
||||
name: 'Documents Tree',
|
||||
meta: {
|
||||
repository: UmbDocumentRepository, // TODO: use alias instead of class
|
||||
repositoryAlias: DOCUMENT_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
alias: 'Umb.TreeItem.Document',
|
||||
name: 'Document Tree Item',
|
||||
loader: () => import('./tree-item/document-tree-item.element'),
|
||||
conditions: {
|
||||
entityType: 'document',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { UmbTreeItemContextBase } from '../../../../shared/components/tree/tree-item-base/tree-item-base.context';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { DocumentTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
// TODO get unique method from an document repository static method
|
||||
export class UmbDocumentTreeItemContext extends UmbTreeItemContextBase<DocumentTreeItemResponseModel> {
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, (x: DocumentTreeItemResponseModel) => x.key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { UmbDocumentTreeItemContext } from './document-tree-item.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { DocumentTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
@customElement('umb-document-tree-item')
|
||||
export class UmbDocumentTreeItemElement extends UmbLitElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
#icon-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#status-symbol {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: blue;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-radius: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
private _item?: DocumentTreeItemResponseModel;
|
||||
@property({ type: Object, attribute: false })
|
||||
public get item() {
|
||||
return this._item;
|
||||
}
|
||||
public set item(value: DocumentTreeItemResponseModel | undefined) {
|
||||
this._item = value;
|
||||
this.#context.setTreeItem(value);
|
||||
}
|
||||
|
||||
#context = new UmbDocumentTreeItemContext(this);
|
||||
|
||||
render() {
|
||||
if (!this.item) return nothing;
|
||||
return html`
|
||||
<umb-tree-item-base> ${this.#renderIconWithStatusSymbol()} ${this.#renderLabel()} </umb-tree-item-base>
|
||||
`;
|
||||
}
|
||||
|
||||
// TODO: implement correct status symbol
|
||||
#renderIconWithStatusSymbol() {
|
||||
return html`
|
||||
<span id="icon-container" slot="icon">
|
||||
${this.item?.icon
|
||||
? html`
|
||||
<uui-icon id="icon" slot="icon" name="${this.item.icon}"></uui-icon> <span id="status-symbol"></span>
|
||||
`
|
||||
: nothing}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// TODO: lower opacity if item is not published
|
||||
#renderLabel() {
|
||||
return html` <span id="label" slot="label">${this.item?.name}</span> `;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbDocumentTreeItemElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-tree-item': UmbDocumentTreeItemElement;
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,14 @@ import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-ap
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
import { UmbTreeRepository, RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeRepository, UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export class UmbMediaTypeRepository implements UmbTreeRepository {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbMediaTypeTreeStore;
|
||||
|
||||
#detailSource: UmbMediaTypeDetailServerDataSource;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTypeTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @extends {UmbEntityTreeStore}
|
||||
* @description - Tree Data Store for Media Types
|
||||
*/
|
||||
export class UmbMediaTypeTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbMediaTypeTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbMediaTypeTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MediaTypeResource, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class MediaTypeTreeServerDataSource
|
||||
* @implements {MediaTypeTreeDataSource}
|
||||
*/
|
||||
export class MediaTypeTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class MediaTypeTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { UmbMediaTypeRepository } from '../repository/media-type.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { MEDIA_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.MediaTypes',
|
||||
name: 'Media Types Tree',
|
||||
meta: {
|
||||
repository: UmbMediaTypeRepository,
|
||||
repositoryAlias: MEDIA_TYPE_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.MediaType',
|
||||
name: 'Media Type Tree Item',
|
||||
conditions: {
|
||||
entityType: 'media-type',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -3,21 +3,20 @@ import { MediaTreeServerDataSource } from './sources/media.tree.server.data';
|
||||
import { UmbMediaTreeStore, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from './media.tree.store';
|
||||
import { UmbMediaStore, UMB_MEDIA_STORE_CONTEXT_TOKEN } from './media.store';
|
||||
import { UmbMediaDetailServerDataSource } from './sources/media.detail.server.data';
|
||||
import type { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
|
||||
|
||||
type ItemDetailType = MediaDetails;
|
||||
|
||||
export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepository<ItemDetailType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbMediaTreeStore;
|
||||
|
||||
#detailDataSource: UmbMediaDetailServerDataSource;
|
||||
@@ -25,6 +24,9 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor
|
||||
|
||||
#notificationContext?: UmbNotificationContext;
|
||||
|
||||
#initResolver?: () => void;
|
||||
#initialized = false;
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
this.#host = host;
|
||||
|
||||
@@ -32,19 +34,32 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor
|
||||
this.#treeSource = new MediaTreeServerDataSource(this.#host);
|
||||
this.#detailDataSource = new UmbMediaDetailServerDataSource(this.#host);
|
||||
|
||||
this.#init = Promise.all([
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#treeStore = instance;
|
||||
}),
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#treeStore = instance;
|
||||
this.#checkIfInitialized();
|
||||
});
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#store = instance;
|
||||
}),
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#store = instance;
|
||||
this.#checkIfInitialized();
|
||||
});
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
|
||||
this.#notificationContext = instance;
|
||||
}),
|
||||
]);
|
||||
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
|
||||
this.#notificationContext = instance;
|
||||
this.#checkIfInitialized();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: make a generic way to wait for initialization
|
||||
#init = new Promise<void>((resolve) => {
|
||||
this.#initialized ? resolve() : (this.#initResolver = resolve);
|
||||
});
|
||||
|
||||
#checkIfInitialized() {
|
||||
if (this.#treeStore && this.#store && this.#notificationContext) {
|
||||
this.#initialized = true;
|
||||
this.#initResolver?.();
|
||||
}
|
||||
}
|
||||
|
||||
async requestRootTreeItems() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTreeStore>('UmbMediaTreeStore');
|
||||
@@ -9,10 +9,10 @@ export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTr
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @extends {UmbEntityTreeStore}
|
||||
* @description - Tree Data Store for Media
|
||||
*/
|
||||
export class UmbMediaTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbMediaTreeStore extends UmbEntityTreeStore {
|
||||
#data = new ArrayState<EntityTreeItemResponseModel>([], (x) => x.key);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MediaDetails } from '../../';
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
@@ -10,7 +10,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class UmbTemplateDetailServerDataSource
|
||||
* @implements {TemplateDetailDataSource}
|
||||
*/
|
||||
export class UmbMediaDetailServerDataSource implements RepositoryDetailDataSource<MediaDetails> {
|
||||
export class UmbMediaDetailServerDataSource implements UmbDataSource<MediaDetails> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel, MediaResource } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class MediaTreeServerDataSource
|
||||
* @implements {MediaTreeDataSource}
|
||||
*/
|
||||
export class MediaTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class MediaTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
// TODO: how do we handle trashed items?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbMediaRepository } from '../repository/media.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { MEDIA_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const treeAlias = 'Umb.Tree.Media';
|
||||
|
||||
@@ -8,8 +8,18 @@ const tree: ManifestTree = {
|
||||
alias: treeAlias,
|
||||
name: 'Media Tree',
|
||||
meta: {
|
||||
repository: UmbMediaRepository, // TODO: use alias instead of class
|
||||
repositoryAlias: MEDIA_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.Media',
|
||||
name: 'Media Tree Item',
|
||||
conditions: {
|
||||
entityType: 'media',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -7,11 +7,7 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import type {
|
||||
RepositoryTreeDataSource,
|
||||
UmbDetailRepository,
|
||||
UmbTreeRepository,
|
||||
} from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
// TODO => Update type when backend updated
|
||||
export class UmbMemberGroupRepository implements UmbTreeRepository, UmbDetailRepository<any> {
|
||||
@@ -19,7 +15,7 @@ export class UmbMemberGroupRepository implements UmbTreeRepository, UmbDetailRep
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbMemberGroupTreeStore;
|
||||
|
||||
#detailSource: UmbMemberGroupDetailServerDataSource;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMemberGroupTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @extends {UmbEntityTreeStore}
|
||||
* @description - Tree Data Store for Member Groups
|
||||
*/
|
||||
export class UmbMemberGroupTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbMemberGroupTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbMemberGroupTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
|
||||
@@ -2,7 +2,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models';
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
/**
|
||||
* @description - A data source for the MemberGroup detail that fetches data from the server
|
||||
@@ -11,7 +11,7 @@ import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
* @implements {MemberGroupDetailDataSource}
|
||||
*/
|
||||
// TODO => Provide type when it is available
|
||||
export class UmbMemberGroupDetailServerDataSource implements RepositoryDetailDataSource<any> {
|
||||
export class UmbMemberGroupDetailServerDataSource implements UmbDataSource<any> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MemberGroupResource, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class MemberGroupTreeServerDataSource
|
||||
* @implements {MemberGroupTreeDataSource}
|
||||
*/
|
||||
export class MemberGroupTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class MemberGroupTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
@@ -22,42 +22,42 @@ export class MemberGroupTreeServerDataSource implements RepositoryTreeDataSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the root items for the tree from the server
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getRootItems() {
|
||||
return tryExecuteAndNotify(this.#host, MemberGroupResource.getTreeMemberGroupRoot({}));
|
||||
}
|
||||
* Fetches the root items for the tree from the server
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getRootItems() {
|
||||
return tryExecuteAndNotify(this.#host, MemberGroupResource.getTreeMemberGroupRoot({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the children of a given parent key from the server
|
||||
* @param {(string | null)} parentKey
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getChildrenOf(parentKey: string | null) {
|
||||
// Not implemented for this tree
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* Fetches the children of a given parent key from the server
|
||||
* @param {(string | null)} parentKey
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getChildrenOf(parentKey: string | null) {
|
||||
// Not implemented for this tree
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the items for the given keys from the server
|
||||
* @param {Array<string>} keys
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getItems(keys: Array<string>) {
|
||||
if (!keys || keys.length === 0) {
|
||||
const error: ProblemDetailsModel = { title: 'Keys are missing' };
|
||||
return { error };
|
||||
}
|
||||
/**
|
||||
* Fetches the items for the given keys from the server
|
||||
* @param {Array<string>} keys
|
||||
* @return {*}
|
||||
* @memberof MemberGroupTreeServerDataSource
|
||||
*/
|
||||
async getItems(keys: Array<string>) {
|
||||
if (!keys || keys.length === 0) {
|
||||
const error: ProblemDetailsModel = { title: 'Keys are missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MemberGroupResource.getTreeMemberGroupItem({
|
||||
key: keys,
|
||||
})
|
||||
);
|
||||
}
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MemberGroupResource.getTreeMemberGroupItem({
|
||||
key: keys,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbMemberGroupRepository } from '../repository/member-group.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { MEMBER_GROUP_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const treeAlias = 'Umb.Tree.MemberGroups';
|
||||
|
||||
@@ -9,8 +9,18 @@ const tree: ManifestTree = {
|
||||
name: 'Member Groups Tree',
|
||||
weight: 100,
|
||||
meta: {
|
||||
repository: UmbMemberGroupRepository,
|
||||
repositoryAlias: MEMBER_GROUP_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.MemberGroup',
|
||||
name: 'Member Group Tree Item',
|
||||
conditions: {
|
||||
entityType: 'member-group',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -4,20 +4,21 @@ import { UmbMemberTypeStore, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN } from './membe
|
||||
import { UmbMemberTypeDetailServerDataSource } from './sources/member-type.detail.server.data';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { RepositoryTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
// TODO => use correct type when available
|
||||
type ItemType = any;
|
||||
type TreeItemType = any;
|
||||
|
||||
export class UmbMemberTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
|
||||
export class UmbMemberTypeRepository implements UmbTreeRepository<TreeItemType>, UmbDetailRepository<ItemType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbMemberTypeTreeStore;
|
||||
|
||||
#detailSource: UmbMemberTypeDetailServerDataSource;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
/**
|
||||
@@ -8,7 +8,7 @@ import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/control
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Tree Data Store for Member Types
|
||||
*/
|
||||
export class UmbMemberTypeTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbMemberTypeTreeStore extends UmbEntityTreeStore {
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UMB_MEMBER_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MemberTypeResource, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class MemberTypeTreeServerDataSource
|
||||
* @implements {MemberTypeTreeDataSource}
|
||||
*/
|
||||
export class MemberTypeTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class MemberTypeTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbMemberTypeRepository } from '../repository/member-type.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { MEMBER_TYPES_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const treeAlias = 'Umb.Tree.MemberTypes';
|
||||
|
||||
@@ -8,8 +8,18 @@ const tree: ManifestTree = {
|
||||
alias: treeAlias,
|
||||
name: 'Member Types Tree',
|
||||
meta: {
|
||||
repository: UmbMemberTypeRepository,
|
||||
repositoryAlias: MEMBER_TYPES_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.MemberType',
|
||||
name: 'Member Type Tree Item',
|
||||
conditions: {
|
||||
entityType: 'member-type',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
|
||||
export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTreeStore>('UmbMemberTreeStore');
|
||||
@@ -7,10 +7,10 @@ export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMember
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMemberTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @extends {UmbEntityTreeStore}
|
||||
* @description - Tree Data Store for Members
|
||||
*/
|
||||
export class UmbMemberTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbMemberTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbTemplateTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbMemberRepository } from '../repository/member.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { MEMBER_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
@@ -7,8 +7,18 @@ const tree: ManifestTree = {
|
||||
name: 'Members Tree',
|
||||
weight: 10,
|
||||
meta: {
|
||||
repository: UmbMemberRepository,
|
||||
repositoryAlias: MEMBER_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.Member',
|
||||
name: 'Member Tree Item',
|
||||
conditions: {
|
||||
entityType: 'member',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -2,28 +2,25 @@ import { UmbDataTypeTreeStore, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './
|
||||
import { UmbDataTypeServerDataSource } from './sources/data-type.server.data';
|
||||
import { UmbDataTypeStore, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN } from './data-type.store';
|
||||
import { DataTypeTreeServerDataSource } from './sources/data-type.tree.server.data';
|
||||
import type {
|
||||
RepositoryTreeDataSource,
|
||||
UmbTreeRepository,
|
||||
UmbDetailRepository,
|
||||
} from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ProblemDetailsModel, DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
|
||||
type ItemType = DataTypeResponseModel;
|
||||
type TreeItemType = any;
|
||||
|
||||
// Move to documentation / JSdoc
|
||||
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
|
||||
// element -> context -> repository -> (store) -> data source
|
||||
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
|
||||
export class UmbDataTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
|
||||
export class UmbDataTypeRepository implements UmbTreeRepository<TreeItemType>, UmbDetailRepository<ItemType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeSource: UmbTreeDataSource;
|
||||
#treeStore?: UmbDataTypeTreeStore;
|
||||
|
||||
#detailDataSource: UmbDataTypeServerDataSource;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
/**
|
||||
* @export
|
||||
@@ -9,7 +9,7 @@ import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
* @description - Tree Data Store for Data-Types
|
||||
*/
|
||||
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
|
||||
export class UmbDataTypeTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbDataTypeTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbDataTypeTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
@@ -20,6 +20,4 @@ export class UmbDataTypeTreeStore extends UmbTreeStoreBase {
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeTreeStore>(
|
||||
'UmbDataTypeTreeStore'
|
||||
);
|
||||
export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeTreeStore>('UmbDataTypeTreeStore');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
ProblemDetailsModel,
|
||||
DataTypeResource,
|
||||
@@ -15,7 +15,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class UmbDataTypeServerDataSource
|
||||
* @implements {RepositoryDetailDataSource}
|
||||
*/
|
||||
export class UmbDataTypeServerDataSource implements RepositoryDetailDataSource<DataTypeResponseModel> {
|
||||
export class UmbDataTypeServerDataSource implements UmbDataSource<DataTypeResponseModel> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RepositoryTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { ProblemDetailsModel, DataTypeResource } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
@@ -9,7 +9,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class DocumentTreeServerDataSource
|
||||
* @implements {DocumentTreeDataSource}
|
||||
*/
|
||||
export class DataTypeTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
export class DataTypeTreeServerDataSource implements UmbTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
// TODO: how do we handle trashed items?
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { UmbDataTypeRepository } from '../repository/data-type.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { DATA_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.DataTypes',
|
||||
name: 'Data Types Tree',
|
||||
meta: {
|
||||
repository: UmbDataTypeRepository,
|
||||
repositoryAlias: DATA_TYPE_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.DataType',
|
||||
name: 'Data Type Tree Item',
|
||||
conditions: {
|
||||
entityType: 'data-type',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LanguageResponseModel, PagedLanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { RepositoryDetailDataSource, DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource, DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
// TODO: This is a temporary solution until we have a proper paging interface
|
||||
type paging = {
|
||||
@@ -7,7 +7,7 @@ type paging = {
|
||||
take: number;
|
||||
};
|
||||
|
||||
export interface UmbLanguageDataSource extends RepositoryDetailDataSource<LanguageResponseModel> {
|
||||
export interface UmbLanguageDataSource extends UmbDataSource<LanguageResponseModel> {
|
||||
createScaffold(): Promise<DataSourceResponse<LanguageResponseModel>>;
|
||||
get(isoCode: string): Promise<DataSourceResponse<LanguageResponseModel>>;
|
||||
delete(isoCode: string): Promise<DataSourceResponse<LanguageResponseModel>>;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import type { ManifestMenuItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const menuItem: ManifestMenuItem = {
|
||||
const menuItem: ManifestTypes = {
|
||||
type: 'menuItem',
|
||||
kind: 'tree',
|
||||
alias: 'Umb.MenuItem.RelationTypes',
|
||||
name: 'Relation Types Menu Item',
|
||||
weight: 40,
|
||||
loader: () => import('./relation-types-menu-item.element'),
|
||||
meta: {
|
||||
treeAlias: 'Umb.Tree.RelationTypes',
|
||||
label: 'Relation Types',
|
||||
icon: 'umb:folder',
|
||||
entityType: 'relation-type',
|
||||
},
|
||||
conditions: {
|
||||
menus: ['Umb.Menu.Settings'],
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { html, nothing } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
@customElement('umb-relation-types-menu-item')
|
||||
export class UmbRelationTypesMenuItemElement extends UmbLitElement {
|
||||
@state()
|
||||
private _renderTree = false;
|
||||
|
||||
private _onShowChildren() {
|
||||
this._renderTree = true;
|
||||
}
|
||||
|
||||
private _onHideChildren() {
|
||||
this._renderTree = false;
|
||||
}
|
||||
|
||||
// TODO: check if root has children before settings the has-children attribute
|
||||
// TODO: how do we want to cache the tree? (do we want to rerender every time the user opens the tree)?
|
||||
// TODO: can we make this reusable?
|
||||
render() {
|
||||
return html`<umb-tree-item
|
||||
label="Relation Types"
|
||||
icon="umb:folder"
|
||||
entity-type="relation-type"
|
||||
@show-children=${this._onShowChildren}
|
||||
@hide-children=${this._onHideChildren}
|
||||
has-children>
|
||||
${this._renderTree ? html`<umb-tree alias="Umb.Tree.RelationTypes"></umb-tree>` : nothing}
|
||||
</umb-tree-item> `;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbRelationTypesMenuItemElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-relation-types-menu-item': UmbRelationTypesMenuItemElement;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
|
||||
|
||||
type ItemType = RelationTypeResponseModel;
|
||||
type TreeItemType = any;
|
||||
|
||||
// Move to documentation / JSdoc
|
||||
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
|
||||
// element -> context -> repository -> (store) -> data source
|
||||
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
|
||||
export class UmbRelationTypeRepository implements UmbTreeRepository, UmbDetailRepository<ItemType> {
|
||||
export class UmbRelationTypeRepository implements UmbTreeRepository<TreeItemType>, UmbDetailRepository<ItemType> {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import { UmbEntityTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
|
||||
/**
|
||||
* @export
|
||||
@@ -9,7 +9,7 @@ import { UmbTreeStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
* @description - Tree Data Store for relation-types
|
||||
*/
|
||||
// TODO: consider if tree store could be turned into a general EntityTreeStore class?
|
||||
export class UmbRelationTypeTreeStore extends UmbTreeStoreBase {
|
||||
export class UmbRelationTypeTreeStore extends UmbEntityTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbRelationTypeTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RepositoryDetailDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
ProblemDetailsModel,
|
||||
RelationTypeResource,
|
||||
@@ -15,7 +15,7 @@ import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
* @class UmbRelationTypeServerDataSource
|
||||
* @implements {RepositoryDetailDataSource}
|
||||
*/
|
||||
export class UmbRelationTypeServerDataSource implements RepositoryDetailDataSource<RelationTypeResponseModel> {
|
||||
export class UmbRelationTypeServerDataSource implements UmbDataSource<RelationTypeResponseModel> {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import { UmbRelationTypeRepository } from '../repository/relation-type.repository';
|
||||
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
import { RELATION_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.RelationTypes',
|
||||
name: 'Relation Types Tree',
|
||||
meta: {
|
||||
repository: UmbRelationTypeRepository,
|
||||
repositoryAlias: RELATION_TYPE_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree];
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'entity',
|
||||
alias: 'Umb.TreeItem.RelationType',
|
||||
name: 'Relation Type Tree Item',
|
||||
conditions: {
|
||||
entityType: 'relation-type',
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { UmbTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller';
|
||||
import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { ArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
@@ -8,17 +7,14 @@ import { umbExtensionsRegistry, createExtensionClass } from '@umbraco-cms/backof
|
||||
import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
// TODO: Clean up the need for store as Media has switched to use Repositories(repository).
|
||||
export class UmbCollectionContext<
|
||||
DataType extends EntityTreeItemResponseModel = EntityTreeItemResponseModel,
|
||||
StoreType extends UmbTreeStore<DataType> = UmbTreeStore<DataType>
|
||||
> {
|
||||
export class UmbCollectionContext<DataType extends EntityTreeItemResponseModel = EntityTreeItemResponseModel> {
|
||||
private _host: UmbControllerHostInterface;
|
||||
private _entityType: string | null;
|
||||
private _entityKey: string | null;
|
||||
|
||||
#repository?: UmbTreeRepository;
|
||||
#repository?: UmbTreeRepository<any>;
|
||||
|
||||
private _store?: StoreType;
|
||||
private _store?: any;
|
||||
protected _dataObserver?: UmbObserverController<DataType[]>;
|
||||
|
||||
#data = new ArrayState(<Array<DataType>>[]);
|
||||
@@ -45,7 +41,7 @@ export class UmbCollectionContext<
|
||||
this._entityKey = entityKey;
|
||||
|
||||
if (storeAlias) {
|
||||
new UmbContextConsumerController(this._host, storeAlias, (_instance: StoreType) => {
|
||||
new UmbContextConsumerController(this._host, storeAlias, (_instance) => {
|
||||
this._store = _instance;
|
||||
if (!this._store) {
|
||||
// TODO: if we keep the type assumption of _store existing, then we should here make sure to break the application in a good way.
|
||||
@@ -174,4 +170,4 @@ export class UmbCollectionContext<
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken<UmbCollectionContext<any, any>>('UmbCollectionContext');
|
||||
export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken<UmbCollectionContext<any>>('UmbCollectionContext');
|
||||
|
||||
@@ -25,7 +25,7 @@ export class UmbDashboardCollectionElement extends UmbLitElement {
|
||||
];
|
||||
|
||||
// TODO: Use the right type here:
|
||||
private _collectionContext?: UmbCollectionContext<FolderTreeItemResponseModel, any>;
|
||||
private _collectionContext?: UmbCollectionContext<FolderTreeItemResponseModel>;
|
||||
|
||||
public manifest!: ManifestDashboardCollection;
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, nothing, TemplateResult } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
|
||||
import { UmbContextDebugRequest } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN, UMB_CONTEXT_DEBUGGER_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalContext, UMB_CONTEXT_DEBUGGER_MODAL, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
@customElement('umb-debug')
|
||||
export class UmbDebug extends UmbLitElement {
|
||||
|
||||
@@ -4,7 +4,11 @@ import type { TemplateResult } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { map } from 'rxjs';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { createExtensionElement, isManifestElementableType, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
|
||||
import {
|
||||
createExtensionElement,
|
||||
isManifestElementableType,
|
||||
umbExtensionsRegistry,
|
||||
} from '@umbraco-cms/backoffice/extensions-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
export type InitializedExtension = { alias: string; weight: number; component: HTMLElement | null };
|
||||
@@ -34,6 +38,16 @@ export class UmbExtensionSlotElement extends UmbLitElement {
|
||||
@property({ type: Object, attribute: false })
|
||||
public filter: (manifest: any) => boolean = () => true;
|
||||
|
||||
private _props?: Record<string, any> = {};
|
||||
@property({ type: Object, attribute: false })
|
||||
get props() {
|
||||
return this._props;
|
||||
}
|
||||
set props(newVal) {
|
||||
this._props = newVal;
|
||||
this.#assignPropsToAllComponents();
|
||||
}
|
||||
|
||||
@property({ type: String, attribute: 'default-element' })
|
||||
public defaultElement = '';
|
||||
|
||||
@@ -77,6 +91,7 @@ export class UmbExtensionSlotElement extends UmbLitElement {
|
||||
// TODO: Lets make an console.error in this case?
|
||||
}
|
||||
if (component) {
|
||||
this.#assignProps(component);
|
||||
(component as any).manifest = extension;
|
||||
extensionObject.component = component;
|
||||
|
||||
@@ -95,6 +110,18 @@ export class UmbExtensionSlotElement extends UmbLitElement {
|
||||
);
|
||||
}
|
||||
|
||||
#assignPropsToAllComponents() {
|
||||
this._extensions.forEach((ext) => this.#assignProps(ext.component));
|
||||
}
|
||||
|
||||
#assignProps = (component: HTMLElement | null) => {
|
||||
if (!component || !this._props) return;
|
||||
|
||||
Object.keys(this._props).forEach((key) => {
|
||||
(component as any)[key] = this._props?.[key];
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
// TODO: check if we can use repeat directly.
|
||||
return repeat(
|
||||
|
||||
@@ -21,20 +21,26 @@ import './input-checkbox-list/input-checkbox-list.element';
|
||||
import './input-color-picker/input-color-picker.element';
|
||||
import './input-culture-select/input-culture-select.element';
|
||||
import './input-document-picker/input-document-picker.element';
|
||||
import './input-document-type-picker/input-document-type-picker.element';
|
||||
import './input-eye-dropper/input-eye-dropper.element';
|
||||
import './input-language-picker/input-language-picker.element';
|
||||
import './input-media-picker/input-media-picker.element';
|
||||
import './input-multi-url-picker/input-multi-url-picker.element';
|
||||
import './input-slider/input-slider.element';
|
||||
import './input-toggle/input-toggle.element';
|
||||
import './input-template-picker/input-template-picker.element';
|
||||
import './property-type-based-property/property-type-based-property.element';
|
||||
import './ref-property-editor-ui/ref-property-editor-ui.element';
|
||||
import './section/section-main/section-main.element';
|
||||
import './section/section-sidebar/section-sidebar.element';
|
||||
import './section/section.element';
|
||||
|
||||
import './table/table.element';
|
||||
|
||||
import './tree/tree.element';
|
||||
import './tree/entity-tree-item/entity-tree-item.element';
|
||||
import './tree/tree-menu-item/tree-menu-item.element';
|
||||
|
||||
import './variantable-property/variantable-property.element';
|
||||
import './workspace/workspace-action-menu/workspace-action-menu.element';
|
||||
|
||||
@@ -45,8 +51,9 @@ import './history/history-item.element';
|
||||
|
||||
import './workspace/workspace-action/workspace-action.element';
|
||||
import './workspace/workspace-layout/workspace-layout.element';
|
||||
import './code-editor';
|
||||
|
||||
import './workspace/workspace-footer-layout/workspace-footer-layout.element';
|
||||
|
||||
import './template-card/template-card.element';
|
||||
import './code-editor';
|
||||
|
||||
export const manifests = [...debugManifests];
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
||||
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
|
||||
import {
|
||||
UmbDocumentTypeTreeStore,
|
||||
UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN,
|
||||
} from '../../../documents/document-types/repository/document-type.tree.store';
|
||||
import { UMB_CONFIRM_MODAL_TOKEN } from '../../modals/confirm';
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL_TOKEN } from '../../../documents/documents/modals/document-type-picker';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { DocumentTypeResponseModel, EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
@customElement('umb-input-document-type-picker')
|
||||
export class UmbInputDocumentTypePickerElement extends FormControlMixin(UmbLitElement) {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
#add-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#current-node {
|
||||
background-color: var(--uui-color-surface-alt);
|
||||
}
|
||||
|
||||
#wrapper-nodes {
|
||||
margin-left: var(--uui-size-space-6);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
// TODO: do we need both selectedKeys and value? If we just use value we follow the same pattern as native form controls.
|
||||
private _selectedKeys: Array<string> = [];
|
||||
public get selectedKeys(): Array<string> {
|
||||
return this._selectedKeys;
|
||||
}
|
||||
public set selectedKeys(keys: Array<string>) {
|
||||
this._selectedKeys = keys;
|
||||
super.value = keys.join(',');
|
||||
this._observePickedDocuments();
|
||||
}
|
||||
|
||||
@property()
|
||||
public set value(keysString: string) {
|
||||
if (keysString !== this._value) {
|
||||
this.selectedKeys = keysString.split(/[ ,]+/);
|
||||
}
|
||||
}
|
||||
|
||||
@property()
|
||||
currentDocumentType?: DocumentTypeResponseModel;
|
||||
|
||||
@state()
|
||||
private _items?: Array<DocumentTypeResponseModel>;
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
private _documentTypeStore?: UmbDocumentTypeTreeStore;
|
||||
private _pickedItemsObserver?: UmbObserverController<EntityTreeItemResponseModel[]>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this._documentTypeStore = instance;
|
||||
this._observePickedDocuments();
|
||||
});
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _observePickedDocuments() {
|
||||
this._pickedItemsObserver?.destroy();
|
||||
|
||||
if (!this._documentTypeStore) return;
|
||||
|
||||
// TODO: consider changing this to the list data endpoint when it is available
|
||||
this._pickedItemsObserver = this.observe(this._documentTypeStore.items(this._selectedKeys), (items) => {
|
||||
this._items = items;
|
||||
});
|
||||
}
|
||||
|
||||
private _openPicker() {
|
||||
// We send a shallow copy(good enough as its just an array of keys) of our this._selectedKeys, as we don't want the modal to manipulate our data:
|
||||
const modalHandler = this._modalContext?.open(UMB_DOCUMENT_TYPE_PICKER_MODAL_TOKEN, {
|
||||
multiple: true,
|
||||
selection: [...this._selectedKeys],
|
||||
});
|
||||
|
||||
modalHandler?.onSubmit().then(({ selection }: any) => {
|
||||
this._setSelection(selection);
|
||||
});
|
||||
}
|
||||
|
||||
private async _removeItem(item: DocumentTypeResponseModel) {
|
||||
const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL_TOKEN, {
|
||||
color: 'danger',
|
||||
headline: `Remove ${item.name}?`,
|
||||
content: 'Are you sure you want to remove this item',
|
||||
confirmLabel: 'Remove',
|
||||
});
|
||||
|
||||
await modalHandler?.onSubmit();
|
||||
const newSelection = this._selectedKeys.filter((value) => value !== item.key);
|
||||
this._setSelection(newSelection);
|
||||
}
|
||||
|
||||
private _setSelection(newSelection: Array<string>) {
|
||||
this.selectedKeys = newSelection;
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-ref-node id="current-node" .name="${this.currentDocumentType?.name ?? ''} (current)">
|
||||
<uui-icon slot="icon" .name="${this.currentDocumentType?.icon ?? 'umb:document'}"></uui-icon>
|
||||
</uui-ref-node>
|
||||
<div id="wrapper-nodes">
|
||||
<uui-ref-list> ${this._items?.map((item) => this._renderItem(item))} </uui-ref-list>
|
||||
<uui-button id="add-button" look="placeholder" @click=${this._openPicker} label="open">Add</uui-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderItem(item: DocumentTypeResponseModel) {
|
||||
// TODO: remove when we have a way to handle trashed items
|
||||
const tempItem = item as DocumentTypeResponseModel & { isTrashed: boolean };
|
||||
|
||||
return html`
|
||||
<uui-ref-node name=${ifDefined(item.name === null ? undefined : item.name)} detail=${ifDefined(item.key)}>
|
||||
<uui-icon slot="icon" name="${ifDefined(item.icon)}"></uui-icon>
|
||||
${tempItem.isTrashed ? html` <uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag> ` : nothing}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button @click=${() => this._removeItem(item)} label="Remove document ${item.name}">Remove</uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbInputDocumentTypePickerElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-document-type-picker': UmbInputDocumentTypePickerElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
|
||||
import { UmbTemplateCardElement } from '../template-card/template-card.element';
|
||||
import { UMB_TEMPLATE_PICKER_MODAL_TOKEN } from '../../modals/template-picker';
|
||||
import { UMB_TEMPLATE_MODAL_TOKEN } from '../../modals/template';
|
||||
import { UmbTemplateRepository } from '../../../templating/templates/repository/template.repository';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
@customElement('umb-input-template-picker')
|
||||
export class UmbInputTemplatePickerElement extends FormControlMixin(UmbLitElement) {
|
||||
/**
|
||||
* This is a minimum amount of selected items in this input.
|
||||
* @type {number}
|
||||
* @attr
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Number })
|
||||
min?: number;
|
||||
|
||||
/**
|
||||
* 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 undefined
|
||||
*/
|
||||
@property({ type: Number })
|
||||
max?: number;
|
||||
|
||||
/**
|
||||
* Max validation message.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
_allowedKeys: Array<string> = [];
|
||||
@property({ type: Array<string> })
|
||||
public get allowedKeys() {
|
||||
return this._allowedKeys;
|
||||
}
|
||||
public set allowedKeys(newKeys: Array<string>) {
|
||||
this._allowedKeys = newKeys;
|
||||
this.#observePickedTemplates();
|
||||
}
|
||||
|
||||
_defaultKey = '';
|
||||
@property({ type: String })
|
||||
public get defaultKey(): string {
|
||||
return this._defaultKey;
|
||||
}
|
||||
public set defaultKey(newKey: string) {
|
||||
this._defaultKey = newKey;
|
||||
super.value = newKey;
|
||||
}
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
private _templateRepository: UmbTemplateRepository = new UmbTemplateRepository(this);
|
||||
|
||||
@state()
|
||||
_pickedTemplates: TemplateResponseModel[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
async #observePickedTemplates() {
|
||||
this.observe(
|
||||
await this._templateRepository.treeItems(this._allowedKeys),
|
||||
(data) => {
|
||||
this._pickedTemplates = data;
|
||||
},
|
||||
'_templateRepositoryTreeItems'
|
||||
);
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return this;
|
||||
}
|
||||
|
||||
#changeDefault(e: CustomEvent) {
|
||||
e.stopPropagation();
|
||||
const newKey = (e.target as UmbTemplateCardElement).value as string;
|
||||
this.defaultKey = newKey;
|
||||
this.dispatchEvent(new CustomEvent('change-default'));
|
||||
}
|
||||
|
||||
#openPicker() {
|
||||
const modalHandler = this._modalContext?.open(UMB_TEMPLATE_PICKER_MODAL_TOKEN, {
|
||||
multiple: true,
|
||||
selection: [...this.allowedKeys],
|
||||
});
|
||||
|
||||
modalHandler?.onSubmit().then((data) => {
|
||||
if (!data.selection) return;
|
||||
this.allowedKeys = data.selection;
|
||||
this.dispatchEvent(new CustomEvent('change-allowed'));
|
||||
});
|
||||
}
|
||||
|
||||
#removeTemplate(key: string) {
|
||||
/*
|
||||
TODO: We need to follow up on this experience.
|
||||
Could we test if this document type is in use, if so we should have a dialog notifying the user(Dialog, are you sure...) about that we might will break something?
|
||||
If thats the case, Im not why if a template will be removed from an actual document.
|
||||
If if its just the option that will go away.
|
||||
(Comment by Niels)
|
||||
In current backoffice we just prevent deleting a default when there are other templates. But if its the only one its okay. This is a weird experience, so we should make something that makes more sense.
|
||||
BTW. its weird cause the damage of removing the default template is equally bad when there is one or more templates.
|
||||
*/
|
||||
this.allowedKeys = this.allowedKeys.filter((x) => x !== key);
|
||||
}
|
||||
|
||||
#openTemplate(e: CustomEvent) {
|
||||
const key = (e.target as UmbTemplateCardElement).value;
|
||||
|
||||
this._modalContext?.open(UMB_TEMPLATE_MODAL_TOKEN, {
|
||||
key: key as string,
|
||||
language: 'razor',
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this._pickedTemplates.map(
|
||||
(template) => html`
|
||||
<umb-template-card
|
||||
class="template-card"
|
||||
.name="${template.name ?? ''}"
|
||||
.key="${template.key ?? ''}"
|
||||
@change-default="${this.#changeDefault}"
|
||||
@open="${this.#openTemplate}"
|
||||
?default="${template.key === this.defaultKey}">
|
||||
<uui-button
|
||||
slot="actions"
|
||||
label="Remove document ${template.name}"
|
||||
@click="${() => this.#removeTemplate(template.key ?? '')}"
|
||||
compact>
|
||||
<uui-icon name="umb:trash"> </uui-icon>
|
||||
</uui-button>
|
||||
</umb-template-card>
|
||||
`
|
||||
)}
|
||||
<uui-button id="add-button" look="placeholder" label="open" @click="${this.#openPicker}">Add</uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
#add-button {
|
||||
width: 100%;
|
||||
}
|
||||
:host {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-4);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
:host > * {
|
||||
max-width: 180px;
|
||||
min-width: 180px;
|
||||
min-height: 150px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputTemplatePickerElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-template-picker': UmbInputTemplatePickerElement;
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,6 @@ export class UmbSectionContext {
|
||||
public readonly pathname = this.#manifestPathname.asObservable();
|
||||
public readonly label = this.#manifestLabel.asObservable();
|
||||
|
||||
/*
|
||||
This was not used anywhere
|
||||
private _activeTree = new BehaviorSubject<ManifestTree | undefined>(undefined);
|
||||
public readonly activeTree = this._activeTree.asObservable();
|
||||
*/
|
||||
|
||||
// TODO: what is the best context to put this in?
|
||||
#activeTreeItem = new ObjectState<ActiveTreeItemType | undefined>(undefined);
|
||||
public readonly activeTreeItem = this.#activeTreeItem.asObservable();
|
||||
|
||||
constructor(manifest: ManifestSection) {
|
||||
this.setManifest(manifest);
|
||||
}
|
||||
@@ -32,17 +22,6 @@ export class UmbSectionContext {
|
||||
this.#manifestPathname.next(manifest?.meta?.pathname);
|
||||
this.#manifestLabel.next(manifest ? manifest.meta?.label || manifest.name : undefined);
|
||||
}
|
||||
|
||||
/*
|
||||
This was not used anywhere
|
||||
public setActiveTree(tree: ManifestTree) {
|
||||
this._activeTree.next(tree);
|
||||
}
|
||||
*/
|
||||
|
||||
public setActiveTreeItem(item?: ActiveTreeItemType) {
|
||||
this.#activeTreeItem.next(item);
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_SECTION_CONTEXT_TOKEN = new UmbContextToken<UmbSectionContext>('UmbSectionContext');
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { css, html } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
/**
|
||||
* @element umb-template-card
|
||||
* @slot actions
|
||||
* @fires open
|
||||
* @fires selected
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
@customElement('umb-template-card')
|
||||
export class UmbTemplateCardElement extends FormControlMixin(UmbLitElement) {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
box-sizing: border-box;
|
||||
display: contents;
|
||||
position: relative;
|
||||
|
||||
height: 100%;
|
||||
border: 1px solid red;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#card {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 180px;
|
||||
//width: 200px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border-radius: var(--uui-border-radius);
|
||||
border: 1px solid var(--uui-color-divider-emphasis);
|
||||
background-color: var(--uui-color-background);
|
||||
padding: var(--uui-size-4);
|
||||
}
|
||||
|
||||
:host([default]) #card {
|
||||
border: 1px solid var(--uui-color-selected);
|
||||
outline: 1px solid var(--uui-color-selected);
|
||||
}
|
||||
#card:has(uui-button:hover) {
|
||||
border: 1px solid var(--uui-color-selected);
|
||||
}
|
||||
|
||||
#bottom {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
slot[name='actions'] {
|
||||
position: absolute;
|
||||
top: var(--uui-size-4);
|
||||
right: var(--uui-size-4);
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
|
||||
opacity: 0;
|
||||
transition: opacity 120ms;
|
||||
}
|
||||
|
||||
:host(:focus) slot[name='actions'],
|
||||
:host(:focus-within) slot[name='actions'],
|
||||
:host(:hover) slot[name='actions'] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#open-part {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 700;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#open-part,
|
||||
#card {
|
||||
gap: var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
#open-part strong {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:host([disabled]) #open-part {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#open-part:focus-visible,
|
||||
#open-part:focus-visible uui-icon,
|
||||
#open-part:hover,
|
||||
#open-part:hover uui-icon {
|
||||
text-decoration: underline;
|
||||
color: var(--uui-color-interactive-emphasis);
|
||||
}
|
||||
|
||||
#open-part uui-icon {
|
||||
font-size: var(--uui-size-20);
|
||||
color: var(--uui-color-divider-emphasis);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@property({ type: String })
|
||||
name = '';
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
default = false;
|
||||
|
||||
_key = '';
|
||||
@property({ type: String })
|
||||
public set key(newKey: string) {
|
||||
this._key = newKey;
|
||||
super.value = newKey;
|
||||
}
|
||||
public get key() {
|
||||
return this._key;
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#setSelection(e: KeyboardEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
//this.selected = true;
|
||||
this.dispatchEvent(new CustomEvent('change-default', { bubbles: true, composed: true }));
|
||||
}
|
||||
#openTemplate(e: KeyboardEvent) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.dispatchEvent(new CustomEvent('open', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div id="card">
|
||||
<button id="open-part" aria-label="Open ${this.name}" @click="${this.#openTemplate}">
|
||||
<uui-icon class="logo" name="umb:layout"></uui-icon>
|
||||
<strong>${this.name.length ? this.name : 'Untitled template'}</strong>
|
||||
</button>
|
||||
<uui-button id="bottom" label="Default template" ?disabled="${this.default}" @click="${this.#setSelection}">
|
||||
${this.default ? '(Default template)' : 'Set default'}
|
||||
</uui-button>
|
||||
<slot name="actions"></slot>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbTemplateCardElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-template-card': UmbTemplateCardElement;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user