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",
|
||||
"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",
|
||||
|
@ -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.');
|
||||
|
@ -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,
|
||||
|
@ -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 {}
|
||||
|
@ -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>-->
|
||||
|
@ -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;
|
||||
|
@ -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() { }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user