editor-refactor #18
@ -29,4 +29,10 @@
|
|||||||
color: var(--noded-background-code);
|
color: var(--noded-background-code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.files {
|
||||||
|
.tree-node-icon {
|
||||||
|
color: var(--noded-background-files);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { HostComponent } from './editor/host/host.component';
|
|
||||||
import {NodePickerComponent} from './editor/node-picker/node-picker.component';
|
import {NodePickerComponent} from './editor/node-picker/node-picker.component';
|
||||||
import {IonicModule} from '@ionic/angular';
|
import {IonicModule} from '@ionic/angular';
|
||||||
import {DatabaseComponent} from './editor/database/database.component';
|
import {DatabaseComponent} from './editor/database/database.component';
|
||||||
@ -32,7 +31,6 @@ import {DirectivesModule} from '../directives/directives.module';
|
|||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
HostComponent,
|
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
DatabaseComponent,
|
DatabaseComponent,
|
||||||
ColumnsComponent,
|
ColumnsComponent,
|
||||||
@ -67,7 +65,6 @@ import {DirectivesModule} from '../directives/directives.module';
|
|||||||
DirectivesModule,
|
DirectivesModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
HostComponent,
|
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
DatabaseComponent,
|
DatabaseComponent,
|
||||||
ColumnsComponent,
|
ColumnsComponent,
|
||||||
@ -92,7 +89,6 @@ import {DirectivesModule} from '../directives/directives.module';
|
|||||||
NormComponent,
|
NormComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
HostComponent,
|
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
DatabaseComponent,
|
DatabaseComponent,
|
||||||
ColumnsComponent,
|
ColumnsComponent,
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
<ng-container>
|
|
||||||
<div
|
|
||||||
*ngIf="!page.isViewOnly() && ( record.type === 'paragraph'
|
|
||||||
|| record.type === 'header1'
|
|
||||||
|| record.type === 'header2'
|
|
||||||
|| record.type === 'header3'
|
|
||||||
|| record.type === 'header4'
|
|
||||||
|| record.type === 'block_code'
|
|
||||||
|| record.type === 'click_link' )"
|
|
||||||
class="host-host ion-padding"
|
|
||||||
contenteditable="true"
|
|
||||||
(keyup)="onKeyUp($event)"
|
|
||||||
(blur)="record.value=hostContainer.innerHTML"
|
|
||||||
(dblclick)="onHostDblClick()"
|
|
||||||
#hostContainer
|
|
||||||
[ngClass]="{'paragraph': record.type === 'paragraph', 'header1': record.type === 'header1', 'header2': record.type === 'header2', 'header3': record.type === 'header3', 'header4': record.type === 'header4', 'block_code': record.type === 'block_code', 'click_link': record.type === 'click_link'}"
|
|
||||||
[innerHTML]="record.value.replace('\n', '<br>')"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
*ngIf="page.isViewOnly() && ( record.type === 'paragraph'
|
|
||||||
|| record.type === 'header1'
|
|
||||||
|| record.type === 'header2'
|
|
||||||
|| record.type === 'header3'
|
|
||||||
|| record.type === 'header4'
|
|
||||||
|| record.type === 'block_code'
|
|
||||||
|| record.type === 'click_link' )"
|
|
||||||
(click)="onHostDblClick()"
|
|
||||||
class="host-host ion-padding"
|
|
||||||
#hostContainer
|
|
||||||
[ngClass]="{'paragraph': record.type === 'paragraph', 'header1': record.type === 'header1', 'header2': record.type === 'header2', 'header3': record.type === 'header3', 'header4': record.type === 'header4', 'block_code': record.type === 'block_code', 'click_link': record.type === 'click_link'}"
|
|
||||||
[innerHTML]="record.value.replace('\n', '<br>')"
|
|
||||||
></div>
|
|
||||||
<ul
|
|
||||||
*ngIf="record.type === 'ul' && !page.isViewOnly()"
|
|
||||||
class="host-host ion-padding"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
#liItems
|
|
||||||
contenteditable="true"
|
|
||||||
(keyup)="onUlKeyUp($event, i)"
|
|
||||||
(keydown)="onUlKeyDown($event, i)"
|
|
||||||
*ngFor="let line of listLines; let i = index"
|
|
||||||
[innerHTML]="listLines[i]"
|
|
||||||
></li>
|
|
||||||
</ul>
|
|
||||||
<ul
|
|
||||||
*ngIf="record.type === 'ul' && page.isViewOnly()"
|
|
||||||
class="host-host ion-padding"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
#liItems
|
|
||||||
*ngFor="let line of listLines; let i = index"
|
|
||||||
[innerHTML]="listLines[i]"
|
|
||||||
></li>
|
|
||||||
</ul>
|
|
||||||
<div *ngIf="record.type === 'page_sep'" class="hr-wrapper">
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngIf="record.type === 'database_ref'"
|
|
||||||
class="db-wrapper"
|
|
||||||
>
|
|
||||||
<editor-database
|
|
||||||
[readonly]="page.isViewOnly()"
|
|
||||||
[hostRecord]="record"
|
|
||||||
(hostRecordChange)="onRecordChange($event)"
|
|
||||||
(requestParentSave)="onRequestParentSave($event)"
|
|
||||||
(requestParentDelete)="onRequestDelete($event)"
|
|
||||||
></editor-database>
|
|
||||||
</div>
|
|
||||||
<div class="code-wrapper" *ngIf="record.type === 'code_ref'">
|
|
||||||
<editor-code
|
|
||||||
[readonly]="page.isViewOnly()"
|
|
||||||
[hostRecord]="record"
|
|
||||||
(hostRecordChange)="onRecordChange($event)"
|
|
||||||
(requestParentSave)="onRequestParentSave($event)"
|
|
||||||
(requestParentDelete)="onRequestDelete($event)"
|
|
||||||
></editor-code>
|
|
||||||
</div>
|
|
||||||
<div class="files-wrapper" *ngIf="record.type === 'file_ref'">
|
|
||||||
<editor-files
|
|
||||||
[readonly]="page.isViewOnly()"
|
|
||||||
[hostRecord]="record"
|
|
||||||
(hostRecordChange)="onRecordChange($event)"
|
|
||||||
(requestParentSave)="onRequestParentSave($event)"
|
|
||||||
(requestParentDelete)="onRequestDelete($event)"
|
|
||||||
></editor-files>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
@ -1,64 +0,0 @@
|
|||||||
.host-host.header1 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 24pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-host.header2 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 21pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-host.header3 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-host.header4 {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-host.block_code {
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 12pt;
|
|
||||||
background-color: #ddd;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.host-host.click_link {
|
|
||||||
color: #0141b0;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hr-wrapper {
|
|
||||||
margin: 50px 100px;
|
|
||||||
|
|
||||||
& hr {
|
|
||||||
background: #ccc;
|
|
||||||
height: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-indentation-level-num-1 {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-indentation-level-num-2 {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-indentation-level-num-3 {
|
|
||||||
margin-left: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-indentation-level-num-4 {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-indentation-level-num-5 {
|
|
||||||
margin-left: 75px;
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
import {Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, ViewChildren} from '@angular/core';
|
|
||||||
import HostRecord from '../../../structures/HostRecord';
|
|
||||||
import PageRecord from '../../../structures/PageRecord';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'editor-host',
|
|
||||||
templateUrl: './host.component.html',
|
|
||||||
styleUrls: ['./host.component.scss'],
|
|
||||||
})
|
|
||||||
export class HostComponent implements OnInit {
|
|
||||||
@Input() page: PageRecord;
|
|
||||||
@Input() record: HostRecord;
|
|
||||||
@Output() recordChange = new EventEmitter<HostRecord>();
|
|
||||||
@Output() newHostRequested = new EventEmitter<HostComponent>();
|
|
||||||
@Output() destroyHostRequested = new EventEmitter<HostComponent>();
|
|
||||||
@Output() saveHostRequested = new EventEmitter<HostComponent>();
|
|
||||||
@ViewChild('hostContainer') hostContainer: ElementRef;
|
|
||||||
@ViewChildren('liItems') liItems;
|
|
||||||
|
|
||||||
public listLines: Array<string> = [];
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
onRecordChange($event) {
|
|
||||||
this.recordChange.emit($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyUp($event) {
|
|
||||||
}
|
|
||||||
|
|
||||||
onUlKeyDown($event, index) {
|
|
||||||
if ( $event.code === 'Tab' ) {
|
|
||||||
$event.preventDefault();
|
|
||||||
const elem = this.liItems.toArray()[index];
|
|
||||||
let currentLevel = 0;
|
|
||||||
|
|
||||||
elem.nativeElement.className.split(' ').some(x => {
|
|
||||||
if ( x.startsWith('node-indentation-level-num-') ) {
|
|
||||||
currentLevel = Number(x.replace('node-indentation-level-num-', ''));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const newLevel = $event.shiftKey ? currentLevel - 1 : currentLevel + 1;
|
|
||||||
if ( newLevel <= 5 && newLevel >= 0 ) {
|
|
||||||
const existing = elem.nativeElement.className.split(' ').filter(x => !x.startsWith('node-indentation-level-num-'));
|
|
||||||
existing.push(`node-indentation-level-num-${newLevel}`);
|
|
||||||
|
|
||||||
elem.nativeElement.className = existing.join(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onUlKeyUp($event, i) {
|
|
||||||
if ( $event.code === 'Enter' && !$event.shiftKey ) {
|
|
||||||
const e = this.liItems.toArray()[i].nativeElement;
|
|
||||||
e.innerText = e.innerText.trim();
|
|
||||||
if ( this.liItems.toArray()[i].nativeElement.innerText.trim() === '' ) {
|
|
||||||
this.newHostRequested.emit(this);
|
|
||||||
} else {
|
|
||||||
this.listLines.push('');
|
|
||||||
setTimeout(() => {
|
|
||||||
this.focusStart(this.liItems.toArray()[i + 1].nativeElement);
|
|
||||||
|
|
||||||
let newLevel = 0;
|
|
||||||
this.liItems.toArray()[i].nativeElement.className.split(' ').some(x => {
|
|
||||||
if ( x.startsWith('node-indentation-level-num-') ) {
|
|
||||||
newLevel = Number(x.replace('node-indentation-level-num-', ''));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const classes = this.liItems.toArray()[i + 1].nativeElement.className
|
|
||||||
.split(' ')
|
|
||||||
.filter(x => !x.startsWith('node-indentation-level-num-'));
|
|
||||||
classes.push(`node-indentation-level-num-${newLevel}`);
|
|
||||||
this.liItems.toArray()[i + 1].nativeElement.className = classes.join(' ');
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
} else if ( $event.code === 'Backspace' && this.liItems.toArray()[i].nativeElement.innerText.trim() === '' ) {
|
|
||||||
const newLines = [];
|
|
||||||
this.liItems.toArray().forEach((elem, index) => {
|
|
||||||
if ( index !== i ) {
|
|
||||||
newLines.push(elem.nativeElement.innerText ? elem.nativeElement.innerText.trim() : '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.listLines = newLines;
|
|
||||||
|
|
||||||
if ( i === 0 && this.listLines.length === 0 ) {
|
|
||||||
this.destroyHostRequested.emit(this);
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.focusEnd(this.liItems.toArray()[i - 1].nativeElement);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
} else if ( $event.code === 'ArrowDown' ) {
|
|
||||||
const liArr = this.liItems.toArray();
|
|
||||||
if ( liArr.length > i + 1 ) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.focusStart(this.liItems.toArray()[i + 1].nativeElement);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
} else if ( $event.code === 'ArrowUp' ) {
|
|
||||||
if ( i !== 0 ) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.focusStart(this.liItems.toArray()[i - 1].nativeElement);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const recordValue = this.liItems.toArray().map(item => {
|
|
||||||
const elem = item.nativeElement;
|
|
||||||
const value = elem.innerText.trim();
|
|
||||||
let indentationLevel = 0;
|
|
||||||
elem.className.split(' ').some(x => {
|
|
||||||
if ( x.startsWith('node-indentation-level-num-') ) {
|
|
||||||
indentationLevel = x.replace('node-indentation-level-num-', '');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {value, indentationLevel};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.record.value = JSON.stringify(recordValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRequestDelete($event) {
|
|
||||||
this.destroyHostRequested.emit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
onRequestParentSave($event) {
|
|
||||||
this.saveHostRequested.emit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
onHostDblClick() {
|
|
||||||
}
|
|
||||||
|
|
||||||
takeFocus(fromTop = true) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO return an observable here, probably
|
|
||||||
focusEnd(item) {
|
|
||||||
const s = window.getSelection();
|
|
||||||
const r = document.createRange();
|
|
||||||
r.setStart(item, 0);
|
|
||||||
r.setEnd(item, 0);
|
|
||||||
s.removeAllRanges();
|
|
||||||
s.addRange(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO return an observable here, probably
|
|
||||||
focusStart(item) {
|
|
||||||
const s = window.getSelection();
|
|
||||||
const r = document.createRange();
|
|
||||||
r.setStart(item, 0);
|
|
||||||
r.setEnd(item, 0);
|
|
||||||
s.removeAllRanges();
|
|
||||||
s.addRange(r);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const r2 = document.createRange();
|
|
||||||
r2.selectNodeContents(item);
|
|
||||||
r2.collapse(false);
|
|
||||||
const s2 = window.getSelection();
|
|
||||||
s2.removeAllRanges();
|
|
||||||
s2.addRange(r2);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@
|
|||||||
<ion-label>Code Editor</ion-label>
|
<ion-label>Code Editor</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item button (click)="onSelect('file_ref')" class="files">
|
<ion-item button (click)="onSelect('file_ref')" class="files">
|
||||||
<i class="fa" slot="start" [ngClass]="typeIcons.node"></i>
|
<i class="fa" slot="start" [ngClass]="typeIcons.files"></i>
|
||||||
<ion-label>Upload Files</ion-label>
|
<ion-label>Upload Files</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
@ -19,3 +19,9 @@ i {
|
|||||||
color: var(--noded-background-code);
|
color: var(--noded-background-code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.files {
|
||||||
|
i {
|
||||||
|
color: var(--noded-background-files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,6 +36,12 @@
|
|||||||
color: var(--noded-background-code);
|
color: var(--noded-background-code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.files {
|
||||||
|
.search-icon {
|
||||||
|
color: var(--noded-background-files);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-assoc {
|
.search-assoc {
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
*ngFor="let node of editorService.immutableNodes"
|
*ngFor="let node of editorService.immutableNodes"
|
||||||
>
|
>
|
||||||
<div class="host-icons">
|
<div class="host-icons">
|
||||||
<i class="type-icon fa" [ngClass]="typeIcons[(node.isNorm() ? 'node' : node.type)] + ' ' + (node.isNorm() ? 'node' : node.type)" title="WYSIWYG Node"></i>
|
<i class="type-icon fa" [ngClass]="typeIcons[(node.isNorm() ? 'node' : node.type)] + ' ' + (node.isNorm() ? 'node' : node.type)"></i>
|
||||||
<button (click)="onOptionsClick($event, node)">
|
<button (click)="onOptionsClick($event, node)">
|
||||||
<i class="fa fa-ellipsis-v" title="Node options"></i>
|
<i class="fa fa-ellipsis-v" title="Node options"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -28,6 +28,10 @@ ion-icon.invisible {
|
|||||||
&.database_ref {
|
&.database_ref {
|
||||||
color: var(--noded-background-db);
|
color: var(--noded-background-db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.file_ref {
|
||||||
|
color: var(--noded-background-files);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.host-add-button {
|
.host-add-button {
|
||||||
|
@ -97,6 +97,10 @@ export class EditorPage implements OnInit {
|
|||||||
|
|
||||||
popover.onDidDismiss().then(result => {
|
popover.onDidDismiss().then(result => {
|
||||||
console.log('adding node', result.data);
|
console.log('adding node', result.data);
|
||||||
|
if ( !result.data ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.editorService.addNode(result.data, position, positionNodeId);
|
this.editorService.addNode(result.data, position, positionNodeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,4 +7,6 @@ export const NodeTypeIcons = {
|
|||||||
database_ref: 'fa fa-database',
|
database_ref: 'fa fa-database',
|
||||||
code: 'fa fa-code',
|
code: 'fa fa-code',
|
||||||
code_ref: 'fa fa-code',
|
code_ref: 'fa fa-code',
|
||||||
|
file_ref: 'fa fa-archive',
|
||||||
|
files: 'fa fa-archive',
|
||||||
};
|
};
|
||||||
|
@ -46,6 +46,10 @@
|
|||||||
--noded-background-code: #FF006E;
|
--noded-background-code: #FF006E;
|
||||||
--noded-color-code: white;
|
--noded-color-code: white;
|
||||||
--noded-background-code-hover: #FF5CA3;
|
--noded-background-code-hover: #FF5CA3;
|
||||||
|
|
||||||
|
--noded-background-files: #0E7B81;
|
||||||
|
--noded-color-files: white;
|
||||||
|
--noded-background-files-hover: #14AFB8;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.picker-wrapper {
|
div.picker-wrapper {
|
||||||
|
Loading…
Reference in New Issue
Block a user