handle data type create and delete

This commit is contained in:
Mads Rasmussen
2022-09-08 14:37:01 +02:00
parent b651dd08fd
commit fdccc3ed11
14 changed files with 199 additions and 71 deletions

View File

@@ -1,6 +1,7 @@
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import type { ManifestSection, ManifestTree } from '../../core/models';
import { Entity } from '../../mocks/data/entity.data';
export class UmbSectionContext {
// TODO: figure out how fine grained we want to make our observables.
@@ -21,6 +22,10 @@ export class UmbSectionContext {
private _activeTree = new ReplaySubject<ManifestTree>(1);
public readonly activeTree = this._activeTree.asObservable();
// TODO: what is the best context to put this in?
private _activeTreeItem = new ReplaySubject<Entity>(1);
public readonly activeTreeItem = this._activeTreeItem.asObservable();
constructor(section: ManifestSection) {
if (!section) return;
this._data.next(section);
@@ -38,4 +43,8 @@ export class UmbSectionContext {
public setActiveTree(tree: ManifestTree) {
this._activeTree.next(tree);
}
public setActiveTreeItem(treeItem: Entity) {
this._activeTreeItem.next(treeItem);
}
}

View File

@@ -7,6 +7,7 @@ import { UmbExtensionRegistry } from '../../../core/extension';
import { UmbContextConsumerMixin } from '../../../core/context';
import { map, Subscription } from 'rxjs';
import { UmbSectionContext } from '../../sections/section.context';
import { Entity } from '../../../mocks/data/entity.data';
@customElement('umb-action-list-page')
export class UmbActionListPageElement extends UmbContextConsumerMixin(UmbActionElement) {
@@ -34,11 +35,15 @@ export class UmbActionListPageElement extends UmbContextConsumerMixin(UmbActionE
@state()
private _activeTree?: ManifestTree;
@state()
private _activeTreeItem?: Entity;
private _extensionRegistry?: UmbExtensionRegistry;
private _sectionContext?: UmbSectionContext;
private _treeItemActionsSubscription?: Subscription;
private _activeTreeSubscription?: Subscription;
private _activeTreeItemSubscription?: Subscription;
connectedCallback() {
super.connectedCallback();
@@ -51,6 +56,7 @@ export class UmbActionListPageElement extends UmbContextConsumerMixin(UmbActionE
this.consumeContext('umbSectionContext', (sectionContext) => {
this._sectionContext = sectionContext;
this._observeActiveTree();
this._observeActiveTreeItem();
this._observeTreeItemActions();
});
}
@@ -76,6 +82,14 @@ export class UmbActionListPageElement extends UmbContextConsumerMixin(UmbActionE
});
}
private _observeActiveTreeItem() {
this._activeTreeItemSubscription?.unsubscribe();
this._activeTreeItemSubscription = this._sectionContext?.activeTreeItem.subscribe((treeItem) => {
this._activeTreeItem = treeItem;
});
}
private _renderActions() {
return this._actions
.sort((a, b) => a.meta.weight - b.meta.weight)
@@ -93,7 +107,7 @@ export class UmbActionListPageElement extends UmbContextConsumerMixin(UmbActionE
render() {
return html`
<div id="title">
<h3>${this._entity.name}</h3>
<h3>${this._activeTreeItem?.name}</h3>
</div>
<div id="action-list">${this._renderActions()}</div>
`;

View File

@@ -2,6 +2,9 @@ import { LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { Subscription } from 'rxjs';
import { UmbContextConsumerMixin } from '../../../core/context';
import { ManifestTree } from '../../../core/models';
import { Entity } from '../../../mocks/data/entity.data';
import { UmbSectionContext } from '../../sections/section.context';
import { UmbActionPageService } from './action-page.service';
import { UmbActionService } from './actions.service';
@@ -15,13 +18,26 @@ export default class UmbActionElement extends UmbContextConsumerMixin(LitElement
@state()
protected _entity: ActionPageEntity = { name: '', key: '' };
protected _activeTree?: ManifestTree;
protected _activeTreeItem?: Entity;
protected _sectionContext?: UmbSectionContext;
protected _actionService?: UmbActionService;
protected _actionPageService?: UmbActionPageService;
private _actionPageSubscription?: Subscription;
protected _actionPageSubscription?: Subscription;
protected _activeTreeSubscription?: Subscription;
protected _activeTreeItemSubscription?: Subscription;
connectedCallback() {
super.connectedCallback();
this.consumeContext('umbSectionContext', (sectionContext) => {
this._sectionContext = sectionContext;
this._observeActiveTree();
this._observeActiveTreeItem();
});
this.consumeContext('umbActionService', (actionService: UmbActionService) => {
this._actionService = actionService;
});
@@ -36,9 +52,27 @@ export default class UmbActionElement extends UmbContextConsumerMixin(LitElement
});
}
private _observeActiveTree() {
this._activeTreeSubscription?.unsubscribe();
this._activeTreeSubscription = this._sectionContext?.activeTree.subscribe((tree) => {
this._activeTree = tree;
});
}
private _observeActiveTreeItem() {
this._activeTreeItemSubscription?.unsubscribe();
this._activeTreeItemSubscription = this._sectionContext?.activeTreeItem.subscribe((treeItem) => {
this._activeTreeItem = treeItem;
});
}
disconnectCallback() {
super.disconnectedCallback();
this._actionPageSubscription?.unsubscribe();
this._activeTreeSubscription?.unsubscribe();
this._activeTreeItemSubscription?.unsubscribe();
}
}

View File

@@ -1,17 +1,27 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, LitElement } from 'lit';
import { css, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import type { ManifestEntityAction } from '../../../../core/models';
import UmbActionElement from '../../actions/action.element';
@customElement('umb-tree-action-data-type-create')
export default class UmbTreeActionDataTypeCreateElement extends LitElement {
export default class UmbTreeActionDataTypeCreateElement extends UmbActionElement {
static styles = [UUITextStyles, css``];
@property({ attribute: false })
public treeAction?: ManifestEntityAction;
// TODO: how do we handle the href?
private _constructUrl() {
return `/section/settings/${this._activeTreeItem?.type}/${this._activeTreeItem?.key}/view/edit?create=true`;
}
// TODO: change to href. This is a temporary solution to get the link to work. For some reason query params gets removed when using href.
private _handleLabelClick() {
console.log('create new treee item');
if (!this._actionService) return;
const href = this._constructUrl();
history.pushState(null, '', href);
this._actionService.close();
}
render() {

View File

@@ -1,17 +1,48 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html, LitElement } from 'lit';
import { css, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { UmbContextConsumerMixin } from '../../../../core/context';
import type { ManifestEntityAction } from '../../../../core/models';
import { UmbModalService } from '../../../../core/services/modal';
import { UmbDataTypeStore } from '../../../../core/stores/data-type.store';
import UmbActionElement from '../../actions/action.element';
@customElement('umb-tree-action-data-type-delete')
export default class UmbTreeActionDataTypeDeleteElement extends LitElement {
export default class UmbTreeActionDataTypeDeleteElement extends UmbContextConsumerMixin(UmbActionElement) {
static styles = [UUITextStyles, css``];
@property({ attribute: false })
public treeAction?: ManifestEntityAction;
private _modalService?: UmbModalService;
private _dataTypeStore?: UmbDataTypeStore;
connectedCallback(): void {
super.connectedCallback();
this.consumeContext('umbModalService', (modalService: UmbModalService) => {
this._modalService = modalService;
});
this.consumeContext('umbDataTypeStore', (dataTypeStore: UmbDataTypeStore) => {
this._dataTypeStore = dataTypeStore;
});
}
private _handleLabelClick() {
console.log('delete this tree item');
const modalHandler = this._modalService?.confirm({
headline: `Delete ${this._activeTreeItem?.name ?? 'item'}`,
content: 'Are you sure you want to delete this item?',
color: 'danger',
confirmLabel: 'Delete',
});
modalHandler?.onClose.then(({ confirmed }: any) => {
if (confirmed && this._actionService && this._dataTypeStore && this._activeTreeItem) {
this._dataTypeStore?.trash(this._activeTreeItem.key);
this._actionService.close();
}
});
}
render() {

View File

@@ -62,7 +62,6 @@ export class UmbTreeDocumentElement extends UmbContextProviderMixin(UmbContextCo
this._selectionSubscription?.unsubscribe();
this._selectionSubscription = this._treeContext?.selection.subscribe((selection) => {
this._selection = selection;
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
});
}

View File

@@ -8,25 +8,14 @@ import { UmbSectionContext } from '../../sections/section.context';
import { map, Subscription } from 'rxjs';
import { Entity } from '../../../mocks/data/entity.data';
import { UmbActionService } from '../actions/actions.service';
import { repeat } from 'lit/directives/repeat.js';
@customElement('umb-tree-item')
export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
static styles = [UUITextStyles, css``];
@property()
itemKey = '';
@property()
itemType = '';
@property({ type: String })
label = '';
@property({ type: String })
icon = '';
@property({ type: Boolean })
hasChildren = false;
@property({ type: Object, attribute: false })
treeItem!: Entity;
@state()
private _childItems: Entity[] = [];
@@ -43,13 +32,18 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
@state()
private _selected = false;
@state()
private _isActive = false;
private _treeContext?: UmbTreeContextBase;
private _sectionContext?: UmbSectionContext;
private _actionService?: UmbActionService;
private _sectionSubscription?: Subscription;
private _childrenSubscription?: Subscription;
private _actionService?: UmbActionService;
private _selectableSubscription?: Subscription;
private _selectionSubscription?: Subscription;
private _activeTreeItemSubscription?: Subscription;
constructor() {
super();
@@ -62,7 +56,8 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
this.consumeContext('umbSectionContext', (sectionContext: UmbSectionContext) => {
this._sectionContext = sectionContext;
this._useSection();
this._observeSection();
this._observeActiveTreeItem();
});
this.consumeContext('umbActionService', (actionService: UmbActionService) => {
@@ -74,15 +69,15 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
super.connectedCallback();
this.addEventListener('selected', (e) => {
e.stopPropagation();
this._treeContext?.select(this.itemKey);
this._treeContext?.select(this.treeItem.key);
});
}
private _useSection() {
private _observeSection() {
this._sectionSubscription?.unsubscribe();
this._sectionSubscription = this._sectionContext?.data.subscribe((section) => {
this._href = this._constructPath(section.meta.pathname, this.itemType, this.itemKey);
this._href = this._constructPath(section.meta.pathname, this.treeItem.type, this.treeItem.key);
});
}
@@ -96,12 +91,20 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
private _observeSelection() {
this._selectionSubscription?.unsubscribe();
this._selectionSubscription = this._treeContext?.selection
.pipe(map((keys) => keys.includes(this.itemKey)))
.pipe(map((keys) => keys.includes(this.treeItem.key)))
.subscribe((isSelected) => {
this._selected = isSelected;
});
}
private _observeActiveTreeItem() {
this._activeTreeItemSubscription?.unsubscribe();
this._activeTreeItemSubscription = this._sectionContext?.activeTreeItem.subscribe((treeItem) => {
this._isActive = treeItem.key === this.treeItem.key;
});
}
// TODO: how do we handle this?
private _constructPath(sectionPathname: string, type: string, key: string) {
return `/section/${sectionPathname}/${type}/${key}`;
@@ -115,7 +118,7 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
this._childrenSubscription?.unsubscribe();
this._childrenSubscription = this._treeContext?.childrenChanges(this.itemKey).subscribe((items) => {
this._childrenSubscription = this._treeContext?.childrenChanges(this.treeItem.key).subscribe((items) => {
if (items?.length === 0) return;
this._childItems = items;
this._loading = false;
@@ -128,39 +131,40 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
this._childrenSubscription?.unsubscribe();
this._selectableSubscription?.unsubscribe();
this._selectionSubscription?.unsubscribe();
this._activeTreeItemSubscription?.unsubscribe();
}
private _renderChildItems() {
return this._childItems.map((item) => {
return html`<umb-tree-item
.label=${item.name}
.hasChildren=${item.hasChildren}
.itemKey=${item.key}
.itemType=${item.type}
.icon="${item.icon}">
</umb-tree-item>`;
});
return html`
${repeat(
this._childItems,
(item) => item.key,
(item) => html`<umb-tree-item .treeItem=${item}></umb-tree-item>`
)}
`;
}
private _openActions() {
if (!this._treeContext || !this._sectionContext) return;
if (!this._treeContext || !this._sectionContext || !this.treeItem) return;
this._sectionContext?.setActiveTree(this._treeContext?.tree);
this._actionService?.open({ name: this.label, key: this.itemKey });
this._sectionContext?.setActiveTreeItem(this.treeItem);
this._actionService?.open({ name: this.treeItem.name, key: this.treeItem.key });
}
render() {
return html`
<uui-menu-item
@show-children=${this._onShowChildren}
?selectable=${this._selectable}
?selected=${this._selected}
@show-children=${this._onShowChildren}
.loading=${this._loading}
.hasChildren=${this.hasChildren}
label="${this.label}"
href="${this._href}">
.hasChildren=${this.treeItem.hasChildren}
label="${this.treeItem.name}"
href="${this._href}"
?active=${this._isActive}>
${this._renderChildItems()}
<uui-icon slot="icon" name="${this.icon}"></uui-icon>
<uui-icon slot="icon" name="${this.treeItem.icon}"></uui-icon>
<uui-action-bar slot="actions">
<uui-button @click=${this._openActions} label="Open actions menu">
<uui-symbol-more></uui-symbol-more>

View File

@@ -51,13 +51,7 @@ export class UmbTreeNavigator extends UmbContextConsumerMixin(LitElement) {
${repeat(
this._items,
(item) => item.key,
(item) => html`<umb-tree-item
.itemKey=${item.key}
.itemType=${item.type}
.label=${item.name}
.icon="${item.icon}"
?hasChildren=${item.hasChildren}
.loading=${this._loading}></umb-tree-item>`
(item) => html`<umb-tree-item .treeItem=${item} .loading=${this._loading}></umb-tree-item>`
)}
`;
}

View File

@@ -42,4 +42,19 @@ export class UmbDataTypeStore extends UmbDataStoreBase<DataTypeEntity> {
console.error('Save Data Type error', error);
}
}
trash(key: string) {
return fetch('/umbraco/backoffice/data-type/trash', {
method: 'POST',
body: key,
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((data: Array<DataTypeEntity>) => {
this.update(data);
this._entityStore.update(data);
});
}
}

View File

@@ -1,12 +1,7 @@
import { UmbData } from './data';
import { Entity } from './entity.data';
export interface DataTypeEntity {
key: string;
name: string;
type: string;
parentKey: string;
isTrashed: boolean;
hasChildren: boolean;
export interface DataTypeEntity extends Entity {
propertyEditorUIAlias: string;
//configUI: any; // this is the prevalues...
}
@@ -19,6 +14,7 @@ export const data: Array<DataTypeEntity> = [
parentKey: '29d78e6c-c1bf-4c15-b820-d511c237ffae',
isTrashed: false,
hasChildren: false,
icon: '',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Text',
},
{
@@ -28,6 +24,7 @@ export const data: Array<DataTypeEntity> = [
parentKey: '29d78e6c-c1bf-4c15-b820-d511c237ffae',
isTrashed: false,
hasChildren: false,
icon: '',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.Textarea',
},
{
@@ -37,6 +34,7 @@ export const data: Array<DataTypeEntity> = [
parentKey: '29d78e6c-c1bf-4c15-b820-d511c237ffae',
isTrashed: false,
hasChildren: false,
icon: '',
propertyEditorUIAlias: 'My.PropertyEditorUI.Custom',
},
{
@@ -46,6 +44,7 @@ export const data: Array<DataTypeEntity> = [
parentKey: '29d78e6c-c1bf-4c15-b820-d511c237ffae',
isTrashed: false,
hasChildren: false,
icon: '',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.ContextExample',
},
{
@@ -55,6 +54,7 @@ export const data: Array<DataTypeEntity> = [
parentKey: '29d78e6c-c1bf-4c15-b820-d511c237ffae',
isTrashed: false,
hasChildren: false,
icon: '',
propertyEditorUIAlias: 'Umb.PropertyEditorUI.ContentPicker',
},
];

View File

@@ -1,6 +1,7 @@
import { UmbData } from './data';
import { Entity } from './entity.data';
export interface DocumentTypeEntity {
export interface DocumentTypeEntity extends Entity {
key: string;
name: string;
alias: string;
@@ -20,6 +21,7 @@ export const data: Array<DocumentTypeEntity> = [
parentKey: 'f50eb86d-3ef2-4011-8c5d-c56c04eec0da',
isTrashed: false,
hasChildren: false,
icon: '',
properties: [],
},
{
@@ -30,6 +32,7 @@ export const data: Array<DocumentTypeEntity> = [
parentKey: 'f50eb86d-3ef2-4011-8c5d-c56c04eec0da',
isTrashed: false,
hasChildren: false,
icon: '',
properties: [],
},
];

View File

@@ -1,13 +1,7 @@
import { UmbData } from './data';
import { Entity } from './entity.data';
export interface NodeEntity {
key: string;
name: string;
icon: string; // TODO: should come from the doc type?
type: string;
isTrashed: boolean;
hasChildren: boolean;
parentKey: string;
export interface NodeEntity extends Entity {
properties: Array<NodeProperty>;
data: Array<NodePropertyData>;
variants: Array<any>; // TODO: define variant data

View File

@@ -30,4 +30,13 @@ export const handlers = [
return res(ctx.status(200), ctx.json(saved));
}),
rest.post<DataTypeEntity[]>('/umbraco/backoffice/data-type/trash', (req, res, ctx) => {
console.warn('Please move to schema');
const key = req.body as string;
const trashed = umbDataTypeData.trash(key);
return res(ctx.status(200), ctx.json([trashed]));
}),
];

View File

@@ -388,8 +388,8 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
},
{
type: 'treeItemAction',
alias: 'Umb.EntityAction.Create',
name: 'Data Type Create',
alias: 'Umb.TreeItemAction.Create',
name: 'Tree Item Action Create',
loader: () => import('./backoffice/trees/data-types/actions/action-data-type-create.element'),
meta: {
trees: ['Umb.Tree.DataTypes'],
@@ -398,4 +398,16 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
weight: 100,
},
},
{
type: 'treeItemAction',
alias: 'Umb.TreeItemAction.Delete',
name: 'Tree Item Action Delete',
loader: () => import('./backoffice/trees/data-types/actions/action-data-type-delete.element'),
meta: {
trees: ['Umb.Tree.DataTypes'],
label: 'Delete',
icon: 'delete',
weight: 100,
},
},
];