You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
frontend/src/app/pages/editor/editor.page.ts

263 lines
7.3 KiB

import {Component, ElementRef, HostListener, Input, OnInit, ViewChildren} from '@angular/core';
import HostRecord from '../../structures/HostRecord';
import {ActivatedRoute, Router} from '@angular/router';
import {AlertController, LoadingController, ModalController, PopoverController} from '@ionic/angular';
import {NodePickerComponent} from '../../components/editor/node-picker/node-picker.component';
import {HostOptionsComponent} from '../../components/editor/host-options/host-options.component';
import {EditorService} from '../../service/editor.service';
import {NodeTypeIcons} from '../../structures/node-types';
import {OptionMenuComponent} from '../../components/option-menu/option-menu.component';
import {VersionModalComponent} from '../../components/version-modal/version-modal.component';
import {debug} from '../../utility';
import {DatabaseLinkComponent} from '../../components/editor/forms/database-link.component';
@Component({
selector: 'app-editor',
templateUrl: './editor.page.html',
styleUrls: ['./editor.page.scss'],
})
export class EditorPage implements OnInit {
public typeIcons = NodeTypeIcons;
@Input() pageId: string;
@Input() baseId: string;
@Input() isFilloutMode = false;
@Input() hosted = false;
@Input() version?: number;
@ViewChildren('nodeContainer') nodeContainers;
@ViewChildren('nodeContainer', { read: ElementRef }) nodeElements;
public pageType?: string;
protected scrollToNodeId?: string;
@Input()
set readonly(val: boolean) {
this.editorService.forceReadonly = val;
}
get readonly() {
return this.editorService.forceReadonly;
}
constructor(
public readonly route: ActivatedRoute,
public readonly router: Router,
public readonly loader: LoadingController,
public readonly popover: PopoverController,
public readonly alerts: AlertController,
public readonly modals: ModalController,
public editorService: EditorService,
) {
debug('Constructed editor page.');
this.editorService = editorService.getEditor();
this.route.params.subscribe(params => {
this.baseId = params.id;
this.pageId = params.page || params.id;
debug('Got page ID from route:', this.pageId, params);
if ( params.node_id ) {
debug('Scroll to node ID:', params.node_id);
this.scrollToNodeId = params.node_id;
}
});
}
ngOnInit() {}
async ionViewDidEnter() {
debug('Ion view did enter editor page.');
if ( this.pageId ) {
await this.editorService.startEditing(this.pageId, this.version);
} else if ( !this.hosted ) {
await this.router.navigate(['/home']);
}
setTimeout(() => {
if ( this.scrollToNodeId ) {
debug('Scrolling to node.');
const nodes = this.nodeContainers.toArray();
const elements = this.nodeElements.toArray();
debug('Nodes', {nodes, elements});
nodes.forEach((node, i) => {
if ( node.nodeId === this.scrollToNodeId ) {
elements[i].nativeElement.scrollIntoView();
node.performUIActivation();
}
});
}
}, 150);
}
@HostListener('document:keydown.control.s', ['$event'])
onManualSave(event) {
event.preventDefault();
this.editorService.save();
}
async onOptionsClick(event: MouseEvent, node: HostRecord) {
if ( !this.editorService.canEdit() ) {
return;
}
const popover = await this.popover.create({
component: HostOptionsComponent,
event,
componentProps: {
editor: this,
index: this.editorService.immutableNodes.indexOf(node),
event,
hostRecord: node,
}
});
popover.onDidDismiss().then(result => {
const { event: dismissEvent , value } = result.data;
if ( value === 'delete_node' ) {
this.alerts.create({
header: 'Delete node?',
message: 'Are you sure you want to delete this node? Its contents will be unrecoverable.',
buttons: [
{
text: 'Keep It',
role: 'cancel',
},
{
text: 'Delete It',
role: 'ok',
handler: () => {
this.editorService.deleteNode(node.UUID);
}
}
],
}).then(alert => alert.present());
} else if ( value === 'move_up' ) {
this.editorService.moveNode(node, 'up');
} else if ( value === 'move_down' ) {
this.editorService.moveNode(node, 'down');
} else if ( value === 'add_before' ) {
this.onAddClick(event, 'before', node.UUID);
} else if ( value === 'add_after' ) {
this.onAddClick(event, 'after', node.UUID);
}
});
await popover.present();
}
async onAddClick(event: MouseEvent, position?: 'before' | 'after', positionNodeId?: string) {
if ( !this.editorService.canEdit() ) {
return;
}
const popover = await this.popover.create({
component: NodePickerComponent,
event,
componentProps: {
formMode: this.editorService.currentPageType === 'form',
},
});
popover.onDidDismiss().then(result => {
if ( !result.data ) {
return;
}
this.editorService.addNode(result.data, position, positionNodeId);
});
await popover.present();
}
async onPageMenuClick(event: MouseEvent) {
debug('Page type: ', this.pageType)
const popover = await this.popover.create({
event,
component: OptionMenuComponent,
componentProps: {
menuItems: [
{
name: 'Versions',
icon: 'fa fa-history',
value: 'view-versions',
title: 'View other versions of this page',
},
...(this.editorService.currentPageType === 'form' ? [
{
name: 'Link to Database',
icon: NodeTypeIcons.db,
value: 'link-to-db',
title: 'Configure the database where submissions of this form will be stored',
},
] : []),
],
},
});
popover.onDidDismiss().then(async value => {
const { data } = value;
if ( data === 'view-versions' ) {
await this.showVersionModal();
} else if ( data === 'link-to-db' ) {
await this.showFormDatabaseLinkModal();
}
});
await popover.present();
}
async showFormDatabaseLinkModal() {
const modal = await this.modals.create({
component: DatabaseLinkComponent,
componentProps: {
pageId: this.pageId,
editorUUID: this.editorService.instanceUUID,
},
cssClass: 'modal-med',
});
modal.onDidDismiss().then(data => {
debug('Database link closed', data);
});
const modalState = {
modal : true,
desc : 'Link form to database'
};
history.pushState(modalState, null);
await modal.present();
}
async showVersionModal() {
const modal = await this.modals.create({
component: VersionModalComponent,
componentProps: {
pageId: this.pageId,
},
cssClass: 'modal-big',
});
modal.onDidDismiss().then(data => {
if ( data.data ) {
this.editorService.startEditing(this.pageId);
}
});
const modalState = {
modal : true,
desc : 'Page versions'
};
history.pushState(modalState, null);
await modal.present();
}
}