diff --git a/src/app/components/components.module.ts b/src/app/components/components.module.ts index b79e22a..89a0bbc 100644 --- a/src/app/components/components.module.ts +++ b/src/app/components/components.module.ts @@ -26,6 +26,8 @@ import {CurrencyRendererComponent} from './editor/database/renderers/currency-re import {BooleanRendererComponent} from './editor/database/renderers/boolean-renderer.component'; import {SearchComponent} from './search/Search.component'; +import {NormComponent} from './nodes/norm/norm.component'; + @NgModule({ declarations: [ HostComponent, @@ -49,6 +51,8 @@ import {SearchComponent} from './search/Search.component'; CurrencyRendererComponent, BooleanRendererComponent, SearchComponent, + + NormComponent, ], imports: [ CommonModule, @@ -79,6 +83,8 @@ import {SearchComponent} from './search/Search.component'; CurrencyRendererComponent, BooleanRendererComponent, SearchComponent, + + NormComponent, ], exports: [ HostComponent, @@ -102,6 +108,8 @@ import {SearchComponent} from './search/Search.component'; CurrencyRendererComponent, BooleanRendererComponent, SearchComponent, + + NormComponent, ] }) export class ComponentsModule {} diff --git a/src/app/components/nodes/EditorNode.contract.ts b/src/app/components/nodes/EditorNode.contract.ts new file mode 100644 index 0000000..7aeb9c0 --- /dev/null +++ b/src/app/components/nodes/EditorNode.contract.ts @@ -0,0 +1,32 @@ +import PageRecord from '../../structures/PageRecord'; + +export abstract class EditorNodeContract { + protected pageRec!: PageRecord; + protected nodeRec!: any; // TODO + + get page() { + return this.pageRec; + } + + set page(page: PageRecord) { + this.pageRec = page; + } + + get identifier() { + return this.nodeRec.UUID; + } + + public abstract isDirty(): boolean | Promise; + + public needsSave(): boolean | Promise { + return false; + } + + public needsLoad(): boolean | Promise { + return false; + } + + public performSave(): void | Promise {} + + public performLoad(): void | Promise {} +} diff --git a/src/app/components/nodes/norm/norm.component.html b/src/app/components/nodes/norm/norm.component.html new file mode 100644 index 0000000..17ad38c --- /dev/null +++ b/src/app/components/nodes/norm/norm.component.html @@ -0,0 +1,66 @@ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+ + +
+
+ + +
+
+ +
+ +
+ +
+ +
+
+
+ Content editable! +
+
\ No newline at end of file diff --git a/src/app/components/nodes/norm/norm.component.scss b/src/app/components/nodes/norm/norm.component.scss new file mode 100644 index 0000000..eea858f --- /dev/null +++ b/src/app/components/nodes/norm/norm.component.scss @@ -0,0 +1,33 @@ +.editable-base { + padding: 20px; + background: aliceblue; // TODO temporary +} + +.toolbar-base { + height: 40px; + border: 1px solid lightgray; + border-radius: 5px; + display: flex; + flex-direction: row; + + .toolbar-button { + height: calc(100% - 6px); + min-width: 30px; + display: flex; + justify-content: center; + align-items: center; + margin: 3px; + + &:hover { + background: lightgrey; + cursor: pointer; + } + } + + .toolbar-sep { + height: 100%; + width: 1px; + border: 1px solid lightgrey; + margin: 0 5px; + } +} diff --git a/src/app/components/nodes/norm/norm.component.ts b/src/app/components/nodes/norm/norm.component.ts new file mode 100644 index 0000000..218c564 --- /dev/null +++ b/src/app/components/nodes/norm/norm.component.ts @@ -0,0 +1,23 @@ +import {Component} from '@angular/core'; +import {EditorNodeContract} from '../EditorNode.contract'; + +@Component({ + selector: 'editor-norm', + templateUrl: './norm.component.html', + styleUrls: ['./norm.component.scss'], +}) +export class NormComponent extends EditorNodeContract { + public isFocused = false; + + public isDirty(): boolean | Promise { + return false; // TODO implement + } + + onFocusIn(event: MouseEvent) { + this.isFocused = true; + } + + onFocusOut(event: MouseEvent) { + this.isFocused = false; + } +} diff --git a/src/app/pages/editor/editor.page.html b/src/app/pages/editor/editor.page.html index 0f19bb3..fdea70a 100644 --- a/src/app/pages/editor/editor.page.html +++ b/src/app/pages/editor/editor.page.html @@ -1,47 +1,61 @@ - + -
{{ pageRecord.Name }}
+
- +
-
- - - - - +
+
-
- Add Node - Save -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/pages/editor/editor.page.scss b/src/app/pages/editor/editor.page.scss index 5092988..eb93e16 100644 --- a/src/app/pages/editor/editor.page.scss +++ b/src/app/pages/editor/editor.page.scss @@ -5,3 +5,7 @@ ion-icon { ion-icon.invisible { opacity: 0; } + +.title-input { + +} diff --git a/src/app/pages/editor/editor.page.ts b/src/app/pages/editor/editor.page.ts index 82577a3..25c9d1b 100644 --- a/src/app/pages/editor/editor.page.ts +++ b/src/app/pages/editor/editor.page.ts @@ -1,4 +1,4 @@ -import {Component, Host, OnInit, ViewChild, ViewChildren} from '@angular/core'; +import {Component, Host, Input, OnInit, ViewChild, ViewChildren} from '@angular/core'; import HostRecord from '../../structures/HostRecord'; import PageRecord from '../../structures/PageRecord'; import {PageService} from '../../service/page.service'; @@ -13,19 +13,15 @@ import {HostOptionsComponent} from '../../components/editor/host-options/host-op styleUrls: ['./editor.page.scss'], }) export class EditorPage implements OnInit { - public hostRecords: Array = [new HostRecord('Click to edit page...')]; - public pageRecord: PageRecord = new PageRecord(); - public pageId: string; - public visibleButtons: Array = []; + // @ViewChildren('editorHosts') editorHosts; + // @ViewChild('titleBar') titleBar; - @ViewChildren('editorHosts') editorHosts; - @ViewChild('titleBar') titleBar; + @Input() pageId: string; + public pageName = ''; constructor( - protected pages: PageService, protected route: ActivatedRoute, protected router: Router, - protected popover: PopoverController, protected loader: LoadingController, ) { this.route.params.subscribe(params => { @@ -35,215 +31,215 @@ export class EditorPage implements OnInit { ngOnInit() {} - buttonIsVisible(index) { - return this.visibleButtons.includes(index); - } - - makeVisible(index) { - if ( !this.buttonIsVisible(index) ) { - this.visibleButtons.push(index); - } - } - - makeInvisible(index) { - this.visibleButtons = this.visibleButtons.filter(x => x !== index); - } - - ionViewDidEnter() { - if ( this.pageId ) { - this.pages.load(this.pageId).subscribe(pageRecord => { - this.pageRecord = pageRecord; - this.pages.get_nodes(pageRecord).subscribe((hosts: Array) => { - this.hostRecords = hosts; - if ( !pageRecord.isViewOnly() ) { - this.onSaveClick(); - } - }); - }); - } else { - this.router.navigate(['/home']); - } - } - - onHostRecordChange($event, i) { - if ( !this.pageRecord.isViewOnly() ) { - this.hostRecords[i] = $event; - } - } - - async onAddClick($event) { - if ( this.pageRecord.isViewOnly() ) { - return; - } - - const popover = await this.popover.create({ - component: NodePickerComponent, - event: $event, - }); - - popover.onDidDismiss().then(arg => { - const defValue = this.getDefaultValue(arg.data); - const hostRec = new HostRecord(defValue); - hostRec.type = arg.data; - hostRec.PageId = this.pageRecord.UUID; - - if ( hostRec.type === 'ul' ) { - hostRec.value = JSON.stringify([{value: '', indentationLevel: 0}]); - } - - this.hostRecords.push(hostRec); - if ( hostRec.isNorm() ) { - setTimeout(() => { - this.editorHosts.toArray().reverse()[0].takeFocus(); - }, 0); - } else { - this.onSaveClick(); - } - }); - - await popover.present(); - } - - getDefaultValue(type: string) { - if ( type === 'paragraph' ) { - return ''; - } else if ( type === 'header1' ) { - return '# '; - } else if ( type === 'header2' ) { - return '## '; - } else if ( type === 'header3' ) { - return '### '; - } else if ( type === 'header4' ) { - return '#### '; - } else if ( type === 'block_code' ) { - return '```'; - } else if ( type === 'click_link' ) { - return 'https://'; - } else if ( type === 'page_sep' ) { - return '==='; - } else { - return ''; - } - } - - onNewHostRequested($event) { - if ( this.pageRecord.isViewOnly() ) { - return; - } - - const insertAfter = this.getIndexFromRecord($event.record); - const record = new HostRecord(''); - const newHosts = [] - this.hostRecords.forEach((rec, i) => { - newHosts.push(rec); - if ( i === insertAfter ) { - newHosts.push(record); - } - }) - - this.hostRecords = newHosts; - - setTimeout(() => { - this.editorHosts.toArray()[insertAfter + 1].takeFocus(); - }, 0); - } - - onDestroyHostRequested($event) { - if ( this.pageRecord.isViewOnly() ) { - return; - } - - let removedIndex = 0; - const newHostRecords = this.editorHosts.filter((host, i) => { - if ( $event.record === host.record ) { - removedIndex = i; - } - return host.record !== $event.record; - }); - - const removedHost = this.editorHosts[removedIndex]; - - const hostRecords = newHostRecords.map(host => host.record); - this.hostRecords = hostRecords; - - setTimeout(() => { - let focusIndex; - if ( removedIndex === 0 && this.editorHosts.toArray().length ) { - focusIndex = 0; - } else if ( removedIndex !== 0 ) { - focusIndex = removedIndex - 1; - } - - if ( focusIndex >= 0 ) { - this.editorHosts.toArray()[focusIndex].takeFocus(false); - } - }, 0); - } - - protected getIndexFromRecord(record) { - let index; - this.editorHosts.toArray().forEach((host, i) => { - if ( host.record === record ) { - index = i; - } - }); - return index; - } - - onSaveClick() { - if ( this.pageRecord.isViewOnly() ) { - return; - } - - this.loader.create({message: 'Saving changes...'}).then(loader => { - loader.present().then(() => { - this.pageRecord.Name = this.titleBar.el.innerText.trim(); - - // First, save the page record itself - this.pages.save(this.pageRecord).subscribe(pageRecord => { - this.pageRecord = pageRecord; - - // Now, save the nodes - this.pages.save_nodes(pageRecord, this.hostRecords).subscribe(result => { - this.hostRecords = result; - loader.dismiss(); - }); - }); - }); - }); - } - - async onOptionsClick($event, i) { - if ( this.pageRecord.isViewOnly() ) { - return; - } - - const popover = await this.popover.create({ - component: HostOptionsComponent, - event: $event, - componentProps: { - editor: this, - index: i, - event: $event, - hostRecord: this.hostRecords[i], - } - }); - - popover.onDidDismiss().then((result) => { - if ( result.data === 'delete_node' ) { - $event.record = this.hostRecords[i]; - this.onDestroyHostRequested($event); - } - }) - - await popover.present(); - } - - onEditorKeydown($event) { - if ( $event.key === 's' && $event.ctrlKey ) { - $event.preventDefault(); - this.onSaveClick(); - } - } + // buttonIsVisible(index) { + // return this.visibleButtons.includes(index); + // } + // + // makeVisible(index) { + // if ( !this.buttonIsVisible(index) ) { + // this.visibleButtons.push(index); + // } + // } + // + // makeInvisible(index) { + // this.visibleButtons = this.visibleButtons.filter(x => x !== index); + // } + // + // ionViewDidEnter() { + // if ( this.pageId ) { + // this.pages.load(this.pageId).subscribe(pageRecord => { + // this.pageRecord = pageRecord; + // this.pages.get_nodes(pageRecord).subscribe((hosts: Array) => { + // this.hostRecords = hosts; + // if ( !pageRecord.isViewOnly() ) { + // this.onSaveClick(); + // } + // }); + // }); + // } else { + // this.router.navigate(['/home']); + // } + // } + // + // onHostRecordChange($event, i) { + // if ( !this.pageRecord.isViewOnly() ) { + // this.hostRecords[i] = $event; + // } + // } + // + // async onAddClick($event) { + // if ( this.pageRecord.isViewOnly() ) { + // return; + // } + // + // const popover = await this.popover.create({ + // component: NodePickerComponent, + // event: $event, + // }); + // + // popover.onDidDismiss().then(arg => { + // const defValue = this.getDefaultValue(arg.data); + // const hostRec = new HostRecord(defValue); + // hostRec.type = arg.data; + // hostRec.PageId = this.pageRecord.UUID; + // + // if ( hostRec.type === 'ul' ) { + // hostRec.value = JSON.stringify([{value: '', indentationLevel: 0}]); + // } + // + // this.hostRecords.push(hostRec); + // if ( hostRec.isNorm() ) { + // setTimeout(() => { + // this.editorHosts.toArray().reverse()[0].takeFocus(); + // }, 0); + // } else { + // this.onSaveClick(); + // } + // }); + // + // await popover.present(); + // } + // + // getDefaultValue(type: string) { + // if ( type === 'paragraph' ) { + // return ''; + // } else if ( type === 'header1' ) { + // return '# '; + // } else if ( type === 'header2' ) { + // return '## '; + // } else if ( type === 'header3' ) { + // return '### '; + // } else if ( type === 'header4' ) { + // return '#### '; + // } else if ( type === 'block_code' ) { + // return '```'; + // } else if ( type === 'click_link' ) { + // return 'https://'; + // } else if ( type === 'page_sep' ) { + // return '==='; + // } else { + // return ''; + // } + // } + // + // onNewHostRequested($event) { + // if ( this.pageRecord.isViewOnly() ) { + // return; + // } + // + // const insertAfter = this.getIndexFromRecord($event.record); + // const record = new HostRecord(''); + // const newHosts = [] + // this.hostRecords.forEach((rec, i) => { + // newHosts.push(rec); + // if ( i === insertAfter ) { + // newHosts.push(record); + // } + // }) + // + // this.hostRecords = newHosts; + // + // setTimeout(() => { + // this.editorHosts.toArray()[insertAfter + 1].takeFocus(); + // }, 0); + // } + // + // onDestroyHostRequested($event) { + // if ( this.pageRecord.isViewOnly() ) { + // return; + // } + // + // let removedIndex = 0; + // const newHostRecords = this.editorHosts.filter((host, i) => { + // if ( $event.record === host.record ) { + // removedIndex = i; + // } + // return host.record !== $event.record; + // }); + // + // const removedHost = this.editorHosts[removedIndex]; + // + // const hostRecords = newHostRecords.map(host => host.record); + // this.hostRecords = hostRecords; + // + // setTimeout(() => { + // let focusIndex; + // if ( removedIndex === 0 && this.editorHosts.toArray().length ) { + // focusIndex = 0; + // } else if ( removedIndex !== 0 ) { + // focusIndex = removedIndex - 1; + // } + // + // if ( focusIndex >= 0 ) { + // this.editorHosts.toArray()[focusIndex].takeFocus(false); + // } + // }, 0); + // } + // + // protected getIndexFromRecord(record) { + // let index; + // this.editorHosts.toArray().forEach((host, i) => { + // if ( host.record === record ) { + // index = i; + // } + // }); + // return index; + // } + // + // onSaveClick() { + // if ( this.pageRecord.isViewOnly() ) { + // return; + // } + // + // this.loader.create({message: 'Saving changes...'}).then(loader => { + // loader.present().then(() => { + // this.pageRecord.Name = this.titleBar.el.innerText.trim(); + // + // // First, save the page record itself + // this.pages.save(this.pageRecord).subscribe(pageRecord => { + // this.pageRecord = pageRecord; + // + // // Now, save the nodes + // this.pages.save_nodes(pageRecord, this.hostRecords).subscribe(result => { + // this.hostRecords = result; + // loader.dismiss(); + // }); + // }); + // }); + // }); + // } + // + // async onOptionsClick($event, i) { + // if ( this.pageRecord.isViewOnly() ) { + // return; + // } + // + // const popover = await this.popover.create({ + // component: HostOptionsComponent, + // event: $event, + // componentProps: { + // editor: this, + // index: i, + // event: $event, + // hostRecord: this.hostRecords[i], + // } + // }); + // + // popover.onDidDismiss().then((result) => { + // if ( result.data === 'delete_node' ) { + // $event.record = this.hostRecords[i]; + // this.onDestroyHostRequested($event); + // } + // }) + // + // await popover.present(); + // } + // + // onEditorKeydown($event) { + // if ( $event.key === 's' && $event.ctrlKey ) { + // $event.preventDefault(); + // this.onSaveClick(); + // } + // } }