-
+
+
+ (dataChanged)="handleData($event)" [language]="language">
-
-
-
+
diff --git a/src/app/project/list/list-item-form/list-item-form.component.scss b/src/app/project/list/list-item-form/list-item-form.component.scss
index ea0945de93..05a38548df 100644
--- a/src/app/project/list/list-item-form/list-item-form.component.scss
+++ b/src/app/project/list/list-item-form/list-item-form.component.scss
@@ -13,12 +13,69 @@
}
.add-node-btn,
.edit-node-btn,
-.progress-indicator {
+.progress-indicator
+.update-success-btn {
margin: 0 0 0 8px;
}
+.update-success-btn {
+ color: green !important;
+ cursor: default;
+}
+
.new-list-item {
display: inline-flex;
margin: 8px 0 0 40px;
width: 640px;
}
+
+.list-item {
+ position: relative;
+
+ .action-bubble {
+ position: absolute;
+ right: 5px;
+ top: 0px;
+ border: 1px solid #e4e4e4;
+ border-radius: 10px;
+ padding: 0 1px;
+ background-color: #e4e4e4;
+ z-index: 2;
+ box-shadow: #949494 1px 4px 5px 0px;
+
+ .button-container {
+
+ button {
+ cursor: pointer;
+ border: none;
+ padding: 2px;
+ outline: none;
+ background-color: transparent;
+ color: #000000;
+ margin: 0 2px;
+ border-radius: 10px;
+ transition: background-color ease-out 0.5s;
+ min-width: inherit;
+ line-height: normal;
+
+ .material-icons {
+ font-size: 18px;
+ }
+
+ .mat-icon {
+ width: 18px;
+ height: 18px;
+ vertical-align: middle;
+ }
+ }
+
+ button.info {
+ cursor: default;
+ }
+
+ button:hover {
+ background-color: #c7c7c7;
+ }
+ }
+ }
+}
diff --git a/src/app/project/list/list-item-form/list-item-form.component.ts b/src/app/project/list/list-item-form/list-item-form.component.ts
index 8d43a90f6c..dcca6ec66d 100644
--- a/src/app/project/list/list-item-form/list-item-form.component.ts
+++ b/src/app/project/list/list-item-form/list-item-form.component.ts
@@ -1,21 +1,48 @@
+import { trigger, state, style, transition, animate } from '@angular/animations';
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms';
+import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
import {
ApiResponseData,
ApiResponseError,
+ ChildNodeInfo,
CreateChildNodeRequest,
KnoraApiConnection,
+ ListInfoResponse,
ListNodeInfo,
ListNodeInfoResponse,
StringLiteral
} from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken } from '@dasch-swiss/dsp-ui';
+import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorHandlerService } from 'src/app/main/error/error-handler.service';
@Component({
selector: 'app-list-item-form',
templateUrl: './list-item-form.component.html',
- styleUrls: ['./list-item-form.component.scss']
+ styleUrls: ['./list-item-form.component.scss'],
+ animations: [
+ // the fade-in/fade-out animation.
+ // https://www.kdechant.com/blog/angular-animations-fade-in-and-fade-out
+ trigger('simpleFadeAnimation', [
+
+ // the "in" style determines the "resting" state of the element when it is visible.
+ state('in', style({opacity: 1})),
+
+ // fade in when created.
+ transition(':enter', [
+ // the styles start from this point when the element appears
+ style({opacity: 0}),
+ // and animate toward the "in" state above
+ animate(150)
+ ]),
+
+ // fade out when destroyed.
+ transition(':leave',
+ // fading out uses a different syntax, with the "style" being passed into animate()
+ animate(150, style({opacity: 0})))
+ ])
+ ]
})
export class ListItemFormComponent implements OnInit {
@@ -52,16 +79,12 @@ export class ListItemFormComponent implements OnInit {
placeholder: string = 'Append item to ';
- /**
- * form group for the form controller
- */
- form: FormGroup;
-
- updateData: boolean = false;
+ showActionBubble: boolean = false;
constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
- private _errorHandler: ErrorHandlerService
+ private _errorHandler: ErrorHandlerService,
+ private _dialog: MatDialog
) { }
ngOnInit() {
@@ -75,8 +98,13 @@ export class ListItemFormComponent implements OnInit {
// it can be used in the input placeholder
if (this.parentIri) {
this._dspApiConnection.admin.listsEndpoint.getListNodeInfo(this.parentIri).subscribe(
- (response: ApiResponseData
) => {
- this.placeholder += response.body.nodeinfo.labels[0].value;
+ (response: ApiResponseData) => {
+ if (response.body instanceof ListInfoResponse) { // root node
+ this.placeholder += response.body.listinfo.labels[0].value;
+ } else { // child node
+ this.placeholder += response.body.nodeinfo.labels[0].value;
+ }
+
this.initComponent = false;
},
(error: ApiResponseError) => {
@@ -86,7 +114,11 @@ export class ListItemFormComponent implements OnInit {
}
}
- submitData() {
+ /**
+ * Called from the template when the plus button is clicked.
+ * Sends the info to make a new child node to DSP-API and refreshes the UI to show the newly added node at the end of the list.
+ */
+ createChildNode() {
if (!this.labels.length) {
return;
@@ -94,45 +126,40 @@ export class ListItemFormComponent implements OnInit {
this.loading = true;
- if (this.iri && this.updateData) {
- // edit mode
- // TODO: update node method not yet implemented; Waiting for Knora API
+ // generate the data payload
+ const listItem: CreateChildNodeRequest = new CreateChildNodeRequest();
+ listItem.parentNodeIri = this.parentIri;
+ listItem.projectIri = this.projectIri;
+ listItem.name = this.projectcode + '-' + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
+
+ // initialize labels
+ let i = 0;
+ for (const l of this.labels) {
+ listItem.labels[i] = new StringLiteral();
+ listItem.labels[i].language = l.language;
+ listItem.labels[i].value = l.value;
+ i++;
+ }
+ listItem.comments = []; // TODO: comments are not yet implemented in the template
- // TODO: remove setTimeout after testing position of progress indicator
- setTimeout(() => {
+ // send payload to dsp-api's api
+ this._dspApiConnection.admin.listsEndpoint.createChildNode(listItem).subscribe(
+ (response: ApiResponseData) => {
+ this.refreshParent.emit(response.body.nodeinfo);
this.loading = false;
- }, 500);
-
- } else {
- // generate the data payload
- const listItem: CreateChildNodeRequest = new CreateChildNodeRequest();
- listItem.parentNodeIri = this.parentIri;
- listItem.projectIri = this.projectIri;
- listItem.name = this.projectcode + '-' + Math.random().toString(36).substr(2) + Math.random().toString(36).substr(2);
-
- // initialize labels
- let i = 0;
- for (const l of this.labels) {
- listItem.labels[i] = new StringLiteral();
- listItem.labels[i].language = l.language;
- listItem.labels[i].value = l.value;
- i++;
+ },
+ (error: ApiResponseError) => {
+ this._errorHandler.showMessage(error);
}
- listItem.comments = []; // TODO: comments are not yet implemented in the template
-
- // send payload to dsp-api's api
- this._dspApiConnection.admin.listsEndpoint.createChildNode(listItem).subscribe(
- (response: ApiResponseData) => {
- this.refreshParent.emit(response.body.nodeinfo);
- this.loading = false;
- },
- (error: ApiResponseError) => {
- this._errorHandler.showMessage(error);
- }
- );
- }
+ );
}
+ /**
+ * Called from the template any time the label changes.
+ * Currently only implemented for labels because entering comments is not yet supported.
+ *
+ * @param data the data that was changed.
+ */
handleData(data: StringLiteral[]) {
// this shouldn't run on the init...
if (!this.initComponent) {
@@ -140,7 +167,49 @@ export class ListItemFormComponent implements OnInit {
}
}
- toggleBtn(show: boolean) {
- this.updateData = show;
+ /**
+ * Show action bubble with various CRUD buttons when hovered over.
+ */
+ mouseEnter() {
+ this.showActionBubble = true;
+ }
+
+ /**
+ * Hide action bubble with various CRUD buttons when not hovered over.
+ */
+ mouseLeave() {
+ this.showActionBubble = false;
+ }
+
+ /**
+ * Called when the 'edit' button is clicked.
+ *
+ * @param mode mode to tell DialogComponent which part of the template to show.
+ * @param name label of the node; for now this is always the first label in the array.
+ * @param iri iri of the node.
+ */
+ openDialog(mode: string, name: string, iri?: string): void {
+ const dialogConfig: MatDialogConfig = {
+ width: '640px',
+ position: {
+ top: '112px'
+ },
+ data: { mode: mode, title: name, id: iri, project: this.projectIri }
+ };
+
+ // open the dialog box
+ const dialogRef = this._dialog.open(
+ DialogComponent,
+ dialogConfig
+ );
+
+ dialogRef.afterClosed().subscribe((data: ChildNodeInfo) => {
+ // update the view if data was passed back
+ // data is only passed back when clicking the 'update' button
+ if (data) {
+ this.refreshParent.emit(data as ListNodeInfo);
+ this.labels = data.labels;
+ }
+ });
}
}
diff --git a/src/app/project/list/list-item/list-item.component.html b/src/app/project/list/list-item/list-item.component.html
index f3449d82d3..0a9153a4f1 100644
--- a/src/app/project/list/list-item/list-item.component.html
+++ b/src/app/project/list/list-item/list-item.component.html
@@ -8,8 +8,7 @@
-
+
diff --git a/src/app/project/list/list-item/list-item.component.ts b/src/app/project/list/list-item/list-item.component.ts
index cca137e290..850d97f6d2 100644
--- a/src/app/project/list/list-item/list-item.component.ts
+++ b/src/app/project/list/list-item/list-item.component.ts
@@ -2,6 +2,7 @@ import { Component, Inject, Input, OnInit } from '@angular/core';
import {
ApiResponseData,
ApiResponseError,
+ ChildNodeInfo,
KnoraApiConnection,
ListNode,
ListResponse
@@ -30,37 +31,40 @@ export class ListItemComponent implements OnInit {
expandedNode: string;
- loading: boolean;
-
constructor(
@Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection,
private _errorHandler: ErrorHandlerService
) { }
ngOnInit() {
- this.loading = true;
- // in case of child node: do not run the following request
+ // in case of parent node: do not run the following request
if (!this.childNode) {
this._dspApiConnection.admin.listsEndpoint.getList(this.parentIri).subscribe(
(result: ApiResponseData) => {
this.list = result.body.list.children;
this.language = result.body.list.listinfo.labels[0].language;
-
- this.loading = false;
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);
}
-
}
+ /**
+ * Checks if parent node should show its children.
+ * @param id id of parent node.
+ */
showChildren(id: string): boolean {
return (id === this.expandedNode);
}
+ /**
+ * Called from template when the 'expand' button is clicked.
+ *
+ * @param id id of parent node for which the 'expand' button was clicked.
+ */
toggleChildren(id: string) {
if (this.showChildren(id)) {
@@ -71,20 +75,30 @@ export class ListItemComponent implements OnInit {
}
+ /**
+ * Called when the 'refreshParent' event from ListItemFormComponent is triggered.
+ *
+ * @param data info about the node; can be a root node or child node.
+ * @param firstNode states whether or not the node is a new child node; defaults to false.
+ */
updateView(data: ListNode, firstNode: boolean = false) {
- this.loading = true;
- // update the view by updating the existing list
- if (firstNode) {
- // in case of new child node, we have to use the children from list
- const index: number = this.list.findIndex(item => item.id === this.expandedNode);
- this.list[index].children.push(data);
-
+ if (data instanceof ChildNodeInfo) {
+ this.list[data.position].labels = data.labels;
+ this.list[data.position].comments = data.comments;
} else {
- this.list.push(data);
- }
+ // update the view by updating the existing list
+ if (firstNode) {
+ // in case of new child node, we have to use the children from list
+ const index: number = this.list.findIndex(item => item.id === this.expandedNode);
+ this.list[index].children.push(data);
+
+ } else {
+ this.list.push(data);
+ }
- data.children = [];
+ data.children = [];
+ }
}