Add new database column type - link to other page
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Garrett Mills 2020-11-16 09:37:45 -06:00
parent 0378522e9a
commit 7cb5745dc4
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
14 changed files with 16515 additions and 19 deletions

16334
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@
"angular-resize-event": "^2.0.1",
"core-js": "^2.5.4",
"dexie": "^3.0.2",
"ionic-selectable": "^4.7.1",
"moment": "^2.24.0",
"ng-connection-service": "^1.0.4",
"ngx-markdown": "^10.1.1",

View File

@ -514,6 +514,11 @@ export class AppComponent implements OnInit {
this.onMenuRefresh(quiet);
});
this.navService.navigationRequest$.subscribe(pageId => {
this.currentPageId = pageId;
this.router.navigate(['/editor', { id: pageId }]);
});
debug('Reloading menu items...');
this.reloadMenuItems().subscribe(() => {
debug('Reloaded menu items. Displaying interface.');

View File

@ -19,6 +19,7 @@ import {ConnectionServiceModule} from 'ng-connection-service';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import { AngularResizedEventModule } from 'angular-resize-event';
import { IonicSelectableModule } from 'ionic-selectable';
/**
* This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
@ -51,6 +52,7 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
ConnectionServiceModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
AngularResizedEventModule,
IonicSelectableModule,
],
providers: [
StatusBar,

View File

@ -9,6 +9,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {ContenteditableModule} from '@ng-stack/contenteditable';
import {CodeComponent} from './editor/code/code.component';
import {MonacoEditorModule} from 'ngx-monaco-editor';
import {IonicSelectableModule} from 'ionic-selectable';
import {FilesComponent} from './editor/files/files.component';
import {OptionPickerComponent} from './option-picker/option-picker.component';
import {HostOptionsComponent} from './editor/host-options/host-options.component';
@ -39,6 +40,8 @@ import {WysiwygModalComponent} from './editor/database/editors/wysiwyg/wysiwyg-m
import {AngularResizedEventModule} from 'angular-resize-event';
import {DateTimeFilterComponent} from './editor/database/filters/date-time.filter';
import {DatabasePageComponent} from './editor/database/database-page.component';
import {PageLinkRendererComponent} from './editor/database/renderers/page-link-renderer.component';
import {PageLinkEditorComponent} from './editor/database/editors/page-link/page-link-editor.component';
@NgModule({
declarations: [
@ -72,19 +75,22 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
WysiwygModalComponent,
DateTimeFilterComponent,
DatabasePageComponent,
PageLinkRendererComponent,
PageLinkEditorComponent,
],
imports: [
CommonModule,
IonicModule,
AgGridModule,
FormsModule,
ReactiveFormsModule,
ContenteditableModule,
MonacoEditorModule,
DirectivesModule,
MarkdownModule,
EditorPageRoutingModule,
AngularResizedEventModule,
CommonModule,
IonicModule,
AgGridModule,
FormsModule,
ReactiveFormsModule,
ContenteditableModule,
MonacoEditorModule,
DirectivesModule,
MarkdownModule,
EditorPageRoutingModule,
AngularResizedEventModule,
IonicSelectableModule,
],
entryComponents: [
NodePickerComponent,
@ -117,6 +123,8 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
WysiwygModalComponent,
DateTimeFilterComponent,
DatabasePageComponent,
PageLinkRendererComponent,
PageLinkEditorComponent,
],
exports: [
NodePickerComponent,
@ -149,6 +157,8 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
WysiwygModalComponent,
DateTimeFilterComponent,
DatabasePageComponent,
PageLinkRendererComponent,
PageLinkEditorComponent,
]
})
export class ComponentsModule {}

View File

@ -35,6 +35,7 @@
<ion-select-option value="datetime">Date-Time</ion-select-option>
<ion-select-option value="currency">Currency</ion-select-option>
<ion-select-option value="index">Incrementing Index</ion-select-option>
<ion-select-option value="page_link">Link to Page</ion-select-option>
<!-- <ion-select-option value="person">Person</ion-select-option>-->
<!-- <ion-select-option value="url">URL</ion-select-option>-->
<!-- <ion-select-option value="email">E-Mail</ion-select-option>-->

View File

@ -18,6 +18,8 @@ import {WysiwygEditorComponent} from './editors/wysiwyg/wysiwyg-editor.component
import {debounce, debug} from '../../../utility';
import {DateTimeFilterComponent} from './filters/date-time.filter';
import {DatabasePageComponent} from './database-page.component';
import {PageLinkRendererComponent} from './renderers/page-link-renderer.component';
import {PageLinkEditorComponent} from './editors/page-link/page-link-editor.component';
@Component({
selector: 'editor-database',
@ -40,6 +42,7 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
public lastClickRow = -1;
public dbName = '';
public notAvailableOffline = false;
public pages = [];
protected dbId!: string;
protected isInitialLoad = false;
protected triggerSaveDebounce = debounce(() => {
@ -86,13 +89,29 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
this.node.Value.Mode = 'database';
}
ngOnInit() {
async ngOnInit() {
this.pages = this.flattenItems(await this.api.getMenuItems(true));
this.editorService = this.editorService.getEditor(this.editorUUID);
this.editorService.registerNodeEditor(this.nodeId, this).then(() => {
});
}
protected flattenItems(items: any[], level = 0) {
let newItems = [];
for ( const item of items ) {
item.level = level;
newItems.push(item);
if ( Array.isArray(item.children) ) {
newItems = [...newItems, ...this.flattenItems(item.children, level + 1)];
}
}
return newItems;
}
onGridReady($event) {
}
@ -265,6 +284,21 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
} else if ( x.Type === 'wysiwyg' ) {
x.cellEditorFramework = WysiwygEditorComponent;
x.filter = 'agTextColumnFilter';
} else if ( x.Type === 'page_link' ) {
x.cellRendererFramework = PageLinkRendererComponent;
x.cellEditorFramework = PageLinkEditorComponent;
x.filter = 'agTextColumnFilter';
if ( !x.cellEditorParams ) {
x.cellEditorParams = {} as any;
}
if ( !x.cellRendererParams ) {
x.cellRendererParams = {} as any;
}
x.cellEditorParams._pagesData = this.pages;
x.cellRendererParams._pagesData = this.pages;
}
return x;

View File

@ -0,0 +1,14 @@
<ionic-selectable
#selectable
[items]="pages"
itemTextField="name"
itemValueField="id"
[canSearch]="true"
[title]="'Select a page'"
[(ngModel)]="value"
(onClose)="finishEdit()"
>
<ng-template ionicSelectableItemTemplate let-port="item" let-isPortSelected="itemIsSelected">
<div [ngStyle]="{ marginLeft: (20 * port.level) + 'px' }">{{ port.name }}</div>
</ng-template>
</ionic-selectable>

View File

@ -0,0 +1,44 @@
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {ICellEditorParams} from 'ag-grid-community';
import {ApiService} from '../../../../../service/api.service';
import {IonicSelectableComponent} from "ionic-selectable";
@Component({
selector: 'editor-cel-page-link',
templateUrl: './page-link-editor.component.html',
styleUrls: ['./page-link-editor.component.scss'],
})
export class PageLinkEditorComponent implements OnInit {
@ViewChild('selectable') selectable: IonicSelectableComponent;
public params: ICellEditorParams;
public value: any;
public pages: any[] = [];
constructor(
public readonly api: ApiService,
) { }
agInit(params: ICellEditorParams): void {
this.params = params;
this.value = { id: this.params.value };
// @ts-ignore
this.pages = params._pagesData || [];
console.log('page link editor comp', this);
setTimeout(() => {
this.selectable.open();
});
}
getValue(): any {
return this.value.id;
}
async ngOnInit() {}
finishEdit() {
this.params.stopEditing();
}
}

View File

@ -0,0 +1,45 @@
import {ICellRendererAngularComp} from 'ag-grid-angular';
import {Component, HostListener} from '@angular/core';
import {ICellRendererParams} from 'ag-grid-community';
import {NavigationService} from '../../../../service/navigation.service';
@Component({
selector: 'editor-page-link-renderer',
template: `
<div title="Control-click to open this page" (click)="onClick($event)">
<i class="fa fa-sticky-note" style="margin-right: 5px; color: var(--noded-background-note);"></i> {{ pageTitle }}
</div>`,
})
export class PageLinkRendererComponent implements ICellRendererAngularComp {
public params: ICellRendererParams;
public pageId?: string;
public pageTitle?: string;
constructor(
protected readonly nav: NavigationService,
) { }
agInit(params: ICellRendererParams): void {
this.params = params;
this.pageId = params.value;
// @ts-ignore
const page = params._pagesData.find(x => x.id === this.pageId);
if ( page ) {
this.pageTitle = page.name;
}
}
// @HostListener('click', ['@event.target'])
onClick(event) {
if ( event.ctrlKey ) {
event.stopPropagation();
event.preventDefault();
this.nav.requestNavigation(this.pageId);
}
}
refresh(params: any): boolean {
return false;
}
}

View File

@ -57,14 +57,13 @@ export class WysiwygComponent implements OnInit {
ngOnInit() { }
onFocusIn(event: MouseEvent) {
console.log('on focus in', event);
this.isFocused = !this.readonly;
this.editingContents = this.currentContents;
}
@HostListener('document:keyup.escape', ['$event'])
onFocusOut(event) {
if ( this.isEditOnly ) {
if ( this.isEditOnly || !this.isFocused ) {
return;
}

View File

@ -530,27 +530,29 @@ export class ApiService {
});
}
public getMenuItems(): Promise<any[]> {
public getMenuItems(pageOnly: boolean = false): Promise<any[]> {
return new Promise(async (res, rej) => {
await this.db.createSchemata();
// If offline, fetch the menu from the database
if ( this.isOffline ) {
const items = await this.db.menuItems.toArray();
const items = pageOnly ? await this.db.menuItems.where({ type: 'page' }).toArray() : await this.db.menuItems.toArray();
const nodes = MenuItem.inflateTree(items as MenuItem[]);
return res(nodes);
}
// Download the latest menu items
const tree: any[] = await new Promise(res2 => {
this.get('/menu/items').subscribe({
this.get(pageOnly ? '/menu/items?type=page' : '/menu/items').subscribe({
next: async result => {
const nodes = result.data as any[];
const items = MenuItem.deflateTree(nodes);
// Update the locally stored nodes
await this.db.menuItems.clear();
await Promise.all(items.map(item => item.save()));
if ( !pageOnly ) {
await this.db.menuItems.clear();
await Promise.all(items.map(item => item.save()));
}
res2(nodes);
},

View File

@ -7,9 +7,14 @@ import {BehaviorSubject} from 'rxjs';
export class NavigationService {
protected refreshCount = 0;
public readonly sidebarRefresh$: BehaviorSubject<[number, boolean]> = new BehaviorSubject<[number, boolean]>([this.refreshCount, true]);
public readonly navigationRequest$: BehaviorSubject<string> = new BehaviorSubject<string>('');
requestSidebarRefresh({ quiet = false }) {
this.refreshCount += 1;
this.sidebarRefresh$.next([this.refreshCount, quiet]);
}
requestNavigation(pageId: string) {
this.navigationRequest$.next(pageId);
}
}