Add new database column type - link to other page
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
0378522e9a
commit
7cb5745dc4
16334
package-lock.json
generated
Normal file
16334
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,7 @@
|
|||||||
"angular-resize-event": "^2.0.1",
|
"angular-resize-event": "^2.0.1",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
"dexie": "^3.0.2",
|
"dexie": "^3.0.2",
|
||||||
|
"ionic-selectable": "^4.7.1",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"ng-connection-service": "^1.0.4",
|
"ng-connection-service": "^1.0.4",
|
||||||
"ngx-markdown": "^10.1.1",
|
"ngx-markdown": "^10.1.1",
|
||||||
|
@ -514,6 +514,11 @@ export class AppComponent implements OnInit {
|
|||||||
this.onMenuRefresh(quiet);
|
this.onMenuRefresh(quiet);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.navService.navigationRequest$.subscribe(pageId => {
|
||||||
|
this.currentPageId = pageId;
|
||||||
|
this.router.navigate(['/editor', { id: pageId }]);
|
||||||
|
});
|
||||||
|
|
||||||
debug('Reloading menu items...');
|
debug('Reloading menu items...');
|
||||||
this.reloadMenuItems().subscribe(() => {
|
this.reloadMenuItems().subscribe(() => {
|
||||||
debug('Reloaded menu items. Displaying interface.');
|
debug('Reloaded menu items. Displaying interface.');
|
||||||
|
@ -19,6 +19,7 @@ import {ConnectionServiceModule} from 'ng-connection-service';
|
|||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { AngularResizedEventModule } from 'angular-resize-event';
|
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`.
|
* 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,
|
ConnectionServiceModule,
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||||
AngularResizedEventModule,
|
AngularResizedEventModule,
|
||||||
|
IonicSelectableModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
StatusBar,
|
StatusBar,
|
||||||
|
@ -9,6 +9,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
|||||||
import {ContenteditableModule} from '@ng-stack/contenteditable';
|
import {ContenteditableModule} from '@ng-stack/contenteditable';
|
||||||
import {CodeComponent} from './editor/code/code.component';
|
import {CodeComponent} from './editor/code/code.component';
|
||||||
import {MonacoEditorModule} from 'ngx-monaco-editor';
|
import {MonacoEditorModule} from 'ngx-monaco-editor';
|
||||||
|
import {IonicSelectableModule} from 'ionic-selectable';
|
||||||
import {FilesComponent} from './editor/files/files.component';
|
import {FilesComponent} from './editor/files/files.component';
|
||||||
import {OptionPickerComponent} from './option-picker/option-picker.component';
|
import {OptionPickerComponent} from './option-picker/option-picker.component';
|
||||||
import {HostOptionsComponent} from './editor/host-options/host-options.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 {AngularResizedEventModule} from 'angular-resize-event';
|
||||||
import {DateTimeFilterComponent} from './editor/database/filters/date-time.filter';
|
import {DateTimeFilterComponent} from './editor/database/filters/date-time.filter';
|
||||||
import {DatabasePageComponent} from './editor/database/database-page.component';
|
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({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -72,6 +75,8 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
|
|||||||
WysiwygModalComponent,
|
WysiwygModalComponent,
|
||||||
DateTimeFilterComponent,
|
DateTimeFilterComponent,
|
||||||
DatabasePageComponent,
|
DatabasePageComponent,
|
||||||
|
PageLinkRendererComponent,
|
||||||
|
PageLinkEditorComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -85,6 +90,7 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
|
|||||||
MarkdownModule,
|
MarkdownModule,
|
||||||
EditorPageRoutingModule,
|
EditorPageRoutingModule,
|
||||||
AngularResizedEventModule,
|
AngularResizedEventModule,
|
||||||
|
IonicSelectableModule,
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -117,6 +123,8 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
|
|||||||
WysiwygModalComponent,
|
WysiwygModalComponent,
|
||||||
DateTimeFilterComponent,
|
DateTimeFilterComponent,
|
||||||
DatabasePageComponent,
|
DatabasePageComponent,
|
||||||
|
PageLinkRendererComponent,
|
||||||
|
PageLinkEditorComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NodePickerComponent,
|
NodePickerComponent,
|
||||||
@ -149,6 +157,8 @@ import {DatabasePageComponent} from './editor/database/database-page.component';
|
|||||||
WysiwygModalComponent,
|
WysiwygModalComponent,
|
||||||
DateTimeFilterComponent,
|
DateTimeFilterComponent,
|
||||||
DatabasePageComponent,
|
DatabasePageComponent,
|
||||||
|
PageLinkRendererComponent,
|
||||||
|
PageLinkEditorComponent,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ComponentsModule {}
|
export class ComponentsModule {}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<ion-select-option value="datetime">Date-Time</ion-select-option>
|
<ion-select-option value="datetime">Date-Time</ion-select-option>
|
||||||
<ion-select-option value="currency">Currency</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="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="person">Person</ion-select-option>-->
|
||||||
<!-- <ion-select-option value="url">URL</ion-select-option>-->
|
<!-- <ion-select-option value="url">URL</ion-select-option>-->
|
||||||
<!-- <ion-select-option value="email">E-Mail</ion-select-option>-->
|
<!-- <ion-select-option value="email">E-Mail</ion-select-option>-->
|
||||||
|
@ -18,6 +18,8 @@ import {WysiwygEditorComponent} from './editors/wysiwyg/wysiwyg-editor.component
|
|||||||
import {debounce, debug} from '../../../utility';
|
import {debounce, debug} from '../../../utility';
|
||||||
import {DateTimeFilterComponent} from './filters/date-time.filter';
|
import {DateTimeFilterComponent} from './filters/date-time.filter';
|
||||||
import {DatabasePageComponent} from './database-page.component';
|
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({
|
@Component({
|
||||||
selector: 'editor-database',
|
selector: 'editor-database',
|
||||||
@ -40,6 +42,7 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
|
|||||||
public lastClickRow = -1;
|
public lastClickRow = -1;
|
||||||
public dbName = '';
|
public dbName = '';
|
||||||
public notAvailableOffline = false;
|
public notAvailableOffline = false;
|
||||||
|
public pages = [];
|
||||||
protected dbId!: string;
|
protected dbId!: string;
|
||||||
protected isInitialLoad = false;
|
protected isInitialLoad = false;
|
||||||
protected triggerSaveDebounce = debounce(() => {
|
protected triggerSaveDebounce = debounce(() => {
|
||||||
@ -86,13 +89,29 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
|
|||||||
this.node.Value.Mode = 'database';
|
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 = this.editorService.getEditor(this.editorUUID);
|
||||||
this.editorService.registerNodeEditor(this.nodeId, this).then(() => {
|
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) {
|
onGridReady($event) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -265,6 +284,21 @@ export class DatabaseComponent extends EditorNodeContract implements OnInit {
|
|||||||
} else if ( x.Type === 'wysiwyg' ) {
|
} else if ( x.Type === 'wysiwyg' ) {
|
||||||
x.cellEditorFramework = WysiwygEditorComponent;
|
x.cellEditorFramework = WysiwygEditorComponent;
|
||||||
x.filter = 'agTextColumnFilter';
|
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;
|
return x;
|
||||||
|
@ -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>
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -57,14 +57,13 @@ export class WysiwygComponent implements OnInit {
|
|||||||
ngOnInit() { }
|
ngOnInit() { }
|
||||||
|
|
||||||
onFocusIn(event: MouseEvent) {
|
onFocusIn(event: MouseEvent) {
|
||||||
console.log('on focus in', event);
|
|
||||||
this.isFocused = !this.readonly;
|
this.isFocused = !this.readonly;
|
||||||
this.editingContents = this.currentContents;
|
this.editingContents = this.currentContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:keyup.escape', ['$event'])
|
@HostListener('document:keyup.escape', ['$event'])
|
||||||
onFocusOut(event) {
|
onFocusOut(event) {
|
||||||
if ( this.isEditOnly ) {
|
if ( this.isEditOnly || !this.isFocused ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,27 +530,29 @@ export class ApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMenuItems(): Promise<any[]> {
|
public getMenuItems(pageOnly: boolean = false): Promise<any[]> {
|
||||||
return new Promise(async (res, rej) => {
|
return new Promise(async (res, rej) => {
|
||||||
await this.db.createSchemata();
|
await this.db.createSchemata();
|
||||||
|
|
||||||
// If offline, fetch the menu from the database
|
// If offline, fetch the menu from the database
|
||||||
if ( this.isOffline ) {
|
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[]);
|
const nodes = MenuItem.inflateTree(items as MenuItem[]);
|
||||||
return res(nodes);
|
return res(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download the latest menu items
|
// Download the latest menu items
|
||||||
const tree: any[] = await new Promise(res2 => {
|
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 => {
|
next: async result => {
|
||||||
const nodes = result.data as any[];
|
const nodes = result.data as any[];
|
||||||
const items = MenuItem.deflateTree(nodes);
|
const items = MenuItem.deflateTree(nodes);
|
||||||
|
|
||||||
// Update the locally stored nodes
|
// Update the locally stored nodes
|
||||||
|
if ( !pageOnly ) {
|
||||||
await this.db.menuItems.clear();
|
await this.db.menuItems.clear();
|
||||||
await Promise.all(items.map(item => item.save()));
|
await Promise.all(items.map(item => item.save()));
|
||||||
|
}
|
||||||
|
|
||||||
res2(nodes);
|
res2(nodes);
|
||||||
},
|
},
|
||||||
|
@ -7,9 +7,14 @@ import {BehaviorSubject} from 'rxjs';
|
|||||||
export class NavigationService {
|
export class NavigationService {
|
||||||
protected refreshCount = 0;
|
protected refreshCount = 0;
|
||||||
public readonly sidebarRefresh$: BehaviorSubject<[number, boolean]> = new BehaviorSubject<[number, boolean]>([this.refreshCount, true]);
|
public readonly sidebarRefresh$: BehaviorSubject<[number, boolean]> = new BehaviorSubject<[number, boolean]>([this.refreshCount, true]);
|
||||||
|
public readonly navigationRequest$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||||
|
|
||||||
requestSidebarRefresh({ quiet = false }) {
|
requestSidebarRefresh({ quiet = false }) {
|
||||||
this.refreshCount += 1;
|
this.refreshCount += 1;
|
||||||
this.sidebarRefresh$.next([this.refreshCount, quiet]);
|
this.sidebarRefresh$.next([this.refreshCount, quiet]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestNavigation(pageId: string) {
|
||||||
|
this.navigationRequest$.next(pageId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user