Compare commits
42 Commits
basic_edit
...
Brokenbutt
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b023caac6 | |||
| 343518d551 | |||
|
|
b3be216de9 | ||
| 3444dd2774 | |||
| 116db0d092 | |||
|
|
732dfa4256 | ||
|
|
e90b375d02 | ||
| 63fa78684c | |||
|
|
868262bb18 | ||
| 38445f3e70 | |||
| 448dc46cca | |||
| dbc9713710 | |||
| a7a4fe441e | |||
|
|
64ade5a5c4 | ||
|
|
7a504bb8de | ||
| f5e9b075ed | |||
| ddf90e1789 | |||
| a73bff6a7e | |||
| c6c515567c | |||
| 2488871e25 | |||
| bf1be4d3d6 | |||
| badf90e5b3 | |||
|
|
3861c1e72f | ||
| 8a53bc2888 | |||
|
|
d474f9ef83 | ||
|
|
98a95a60f1 | ||
| 629d105262 | |||
| d11df9e9f0 | |||
| 23743bc49c | |||
| 9fa44a3b3d | |||
| 4720536428 | |||
| 9a383d7429 | |||
| 70c4a4dd33 | |||
| d3d992fa3c | |||
| f9b55c7f42 | |||
| e046468bb2 | |||
| cd6f331be1 | |||
| a17207427b | |||
| f888173447 | |||
| bb8c085d64 | |||
| 930d847f9c | |||
| 3a38f8d77f |
@@ -16,8 +16,45 @@ steps:
|
|||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
# npm install -g @angular/cli
|
|
||||||
npm install -g @ionic/cli
|
npm install -g @ionic/cli
|
||||||
|
npm update
|
||||||
npm install
|
npm install
|
||||||
ionic build --prod
|
ionic build --prod
|
||||||
displayName: 'npm install and build'
|
displayName: 'npm install and build'
|
||||||
|
|
||||||
|
- task: CmdLine@2
|
||||||
|
inputs:
|
||||||
|
script: |
|
||||||
|
echo $(Build.Repository.LocalPath)
|
||||||
|
ls -la $(Build.Repository.LocalPath)
|
||||||
|
|
||||||
|
# # Archive files
|
||||||
|
- task: ArchiveFiles@2
|
||||||
|
inputs:
|
||||||
|
rootFolderOrFile: '$(Build.Repository.LocalPath)/www'
|
||||||
|
includeRootFolder: true
|
||||||
|
archiveType: 'tar'
|
||||||
|
archiveFile: '$(Build.ArtifactStagingDirectory)/target-www.tar.gz'
|
||||||
|
replaceExistingArchive: true
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
- task: PublishPipelineArtifact@1
|
||||||
|
inputs:
|
||||||
|
# targetPath: '$(Pipeline.Workspace)'
|
||||||
|
targetPath: '$(Build.ArtifactStagingDirectory)/target-www.tar.gz'
|
||||||
|
artifact: 'FrontEnd'
|
||||||
|
publishLocation: 'pipeline'
|
||||||
|
|
||||||
|
- task: CopyFilesOverSSH@0
|
||||||
|
displayName: 'Securely copy files to the remote machine'
|
||||||
|
inputs:
|
||||||
|
sshEndpoint: GoogleServer
|
||||||
|
sourceFolder: '$(Build.ArtifactStagingDirectory)'
|
||||||
|
targetFolder: /var/lib/app/pipeline/
|
||||||
|
failOnEmptySource: true
|
||||||
|
|
||||||
|
- task: SSH@0
|
||||||
|
displayName: 'Run shell commands on remote machine'
|
||||||
|
inputs:
|
||||||
|
sshEndpoint: GoogleServer
|
||||||
|
commands: '/var/lib/app/pipeline/frontend-deploy.sh'
|
||||||
|
|||||||
29
package-lock.json
generated
29
package-lock.json
generated
@@ -1728,6 +1728,17 @@
|
|||||||
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"angular-tree-component": {
|
||||||
|
"version": "8.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-tree-component/-/angular-tree-component-8.5.2.tgz",
|
||||||
|
"integrity": "sha512-3NwMB+vLq1+WHz2UVgsZA73E1LmIIWJlrrasCKXbLJ3S7NmY9O/wKcolji3Vp2W//5KQ33RXu1jiPXCOQdRzVA==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"mobx": "^5.14.2",
|
||||||
|
"mobx-angular": "3.0.3",
|
||||||
|
"opencollective-postinstall": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-colors": {
|
"ansi-colors": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
|
||||||
@@ -6514,8 +6525,7 @@
|
|||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.15",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.clonedeep": {
|
"lodash.clonedeep": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
@@ -6989,6 +6999,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mobx": {
|
||||||
|
"version": "5.15.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.4.tgz",
|
||||||
|
"integrity": "sha512-xRFJxSU2Im3nrGCdjSuOTFmxVDGeqOHL+TyADCGbT0k4HHqGmx5u2yaHNryvoORpI4DfbzjJ5jPmuv+d7sioFw=="
|
||||||
|
},
|
||||||
|
"mobx-angular": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mobx-angular/-/mobx-angular-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-mZuuose70V+Sd0hMWDElpRe3mA6GhYjSQN3mHzqk2XWXRJ+eWQa/f3Lqhw+Me/Xd2etWsGR1hnRa1BfQ2ZDtpw=="
|
||||||
|
},
|
||||||
"move-concurrently": {
|
"move-concurrently": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||||
@@ -7461,6 +7481,11 @@
|
|||||||
"is-wsl": "^1.1.0"
|
"is-wsl": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"opencollective-postinstall": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
|
||||||
|
},
|
||||||
"opn": {
|
"opn": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"@ionic-native/splash-screen": "^5.0.0",
|
"@ionic-native/splash-screen": "^5.0.0",
|
||||||
"@ionic-native/status-bar": "^5.0.0",
|
"@ionic-native/status-bar": "^5.0.0",
|
||||||
"@ionic/angular": "^4.7.1",
|
"@ionic/angular": "^4.7.1",
|
||||||
|
"angular-tree-component": "^8.5.2",
|
||||||
"core-js": "^2.5.4",
|
"core-js": "^2.5.4",
|
||||||
"rxjs": "~6.5.1",
|
"rxjs": "~6.5.1",
|
||||||
"tslib": "^1.9.0",
|
"tslib": "^1.9.0",
|
||||||
|
|||||||
@@ -1,24 +1,48 @@
|
|||||||
<ion-app>
|
<ion-app class="dark">
|
||||||
<ion-split-pane contentId="main-content">
|
<ion-split-pane when="sm">
|
||||||
<ion-menu contentId="main-content" type="overlay">
|
<ion-menu class="sidebar">
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-toolbar>
|
<ion-toolbar color="primary">
|
||||||
<ion-title>Menu</ion-title>
|
<ion-title>Menu</ion-title>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages">
|
<ion-list-header>
|
||||||
<ion-item [routerDirection]="'root'" [routerLink]="[p.url]">
|
Navigate
|
||||||
<ion-icon slot="start" [name]="p.icon"></ion-icon>
|
<ion-buttons class="ion-padding-end">
|
||||||
<ion-label>
|
<ion-button fill="outline" color="light" (click)="onTopLevelCreate()">
|
||||||
{{p.title}}
|
<ion-icon color="primary" name="add-circle"></ion-icon>
|
||||||
</ion-label>
|
</ion-button>
|
||||||
</ion-item>
|
<ion-button fill="outline" color="light" (click)="onChildCreate()" [disabled]="!addChildTarget">
|
||||||
</ion-menu-toggle>
|
<ion-icon color="primary" name="add-circle"></ion-icon> <span class="button-text">Child</span>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button fill="outline" color="light" (click)="onDeleteClick()" [disabled]="!deleteTarget">
|
||||||
|
<ion-icon color="danger" name="trash"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-list-header>
|
||||||
|
|
||||||
|
<tree-root [nodes]="nodes" [options]="options"></tree-root>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
<ion-footer>
|
||||||
|
<ion-item slot="end" lines="full">
|
||||||
|
<ion-icon slot="start" name="moon"></ion-icon>
|
||||||
|
<ion-label>
|
||||||
|
Dark mode
|
||||||
|
</ion-label>
|
||||||
|
<ion-toggle (ionChange)="toggleDark()" id="themeToggle" slot="end"></ion-toggle>
|
||||||
|
</ion-item>
|
||||||
|
</ion-footer>
|
||||||
</ion-menu>
|
</ion-menu>
|
||||||
<ion-router-outlet id="main-content"></ion-router-outlet>
|
|
||||||
|
<div class="ion-page" main>
|
||||||
|
<ion-header> </ion-header>
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
<ion-router-outlet id="main-content"></ion-router-outlet>
|
||||||
|
</ion-content>
|
||||||
|
</div>
|
||||||
</ion-split-pane>
|
</ion-split-pane>
|
||||||
</ion-app>
|
</ion-app>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.sidebar {
|
||||||
|
max-width: 20em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
//text portion of buttons
|
||||||
|
.button-text {
|
||||||
|
color: var(--ion-color-medium-shade);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,45 +1,185 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
import { Platform } from '@ionic/angular';
|
import { AlertController, Platform } from '@ionic/angular';
|
||||||
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
|
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
|
||||||
import { StatusBar } from '@ionic-native/status-bar/ngx';
|
import { StatusBar } from '@ionic-native/status-bar/ngx';
|
||||||
|
import { ApiService } from './service/api.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { TREE_ACTIONS } from 'angular-tree-component';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: 'app.component.html',
|
templateUrl: 'app.component.html',
|
||||||
styleUrls: ['app.component.scss']
|
styleUrls: ['app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit {
|
||||||
public appPages = [
|
public addChildTarget: any = false;
|
||||||
{
|
public deleteTarget: any = false;
|
||||||
title: 'Home',
|
public lastClickEvent: Array<any> = [];
|
||||||
url: '/home',
|
|
||||||
icon: 'home'
|
public nodes = [];
|
||||||
},
|
public options = {
|
||||||
{
|
actionMapping: {
|
||||||
title: 'List',
|
mouse: {
|
||||||
url: '/list',
|
dblClick: (tree, node, $event) => {
|
||||||
icon: 'list'
|
console.log({ tree, node, $event });
|
||||||
},
|
const id = node.data.id;
|
||||||
{
|
this.router.navigate(['/editor', { id }]);
|
||||||
title: 'Editor',
|
},
|
||||||
url: '/editor',
|
click: (tree, node, $event) => {
|
||||||
icon: 'edit'
|
console.log('click', { tree, node, $event });
|
||||||
|
TREE_ACTIONS.FOCUS(tree, node, $event);
|
||||||
|
this.addChildTarget = node;
|
||||||
|
this.deleteTarget = node;
|
||||||
|
this.lastClickEvent = [tree, node, $event];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
};
|
||||||
|
|
||||||
|
public darkMode = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private platform: Platform,
|
private platform: Platform,
|
||||||
private splashScreen: SplashScreen,
|
private splashScreen: SplashScreen,
|
||||||
private statusBar: StatusBar
|
private statusBar: StatusBar,
|
||||||
|
private api: ApiService,
|
||||||
|
protected router: Router,
|
||||||
|
protected alerts: AlertController
|
||||||
) {
|
) {
|
||||||
this.initializeApp();
|
this.initializeApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.reloadMenuItems().subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onTopLevelCreate() {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: 'Create Page',
|
||||||
|
message: 'Please enter a new name for the page:',
|
||||||
|
cssClass: 'page-prompt',
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'My Awesome Page'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
cssClass: 'secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Create',
|
||||||
|
handler: async args => {
|
||||||
|
this.api.post('/page/create', args).subscribe(res => {
|
||||||
|
this.router.navigate(['/editor', { id: res.data.UUID }]);
|
||||||
|
this.reloadMenuItems().subscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onChildCreate() {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: 'Create Sub-Page',
|
||||||
|
message: 'Please enter a new name for the page:',
|
||||||
|
cssClass: 'page-prompt',
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
placeholder: 'My Awesome Page'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Cancel',
|
||||||
|
role: 'cancel',
|
||||||
|
cssClass: 'secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Create',
|
||||||
|
handler: async args => {
|
||||||
|
args = {
|
||||||
|
name: args.name,
|
||||||
|
parentId: this.addChildTarget.data.id
|
||||||
|
};
|
||||||
|
this.api.post('/page/create-child', args).subscribe(res => {
|
||||||
|
this.reloadMenuItems().subscribe(() => {
|
||||||
|
TREE_ACTIONS.EXPAND(
|
||||||
|
this.lastClickEvent[0],
|
||||||
|
this.lastClickEvent[1],
|
||||||
|
this.lastClickEvent[2]
|
||||||
|
);
|
||||||
|
this.router.navigate(['/editor', { id: res.data.UUID }]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onDeleteClick() {
|
||||||
|
const alert = await this.alerts.create({
|
||||||
|
header: 'Delete page?',
|
||||||
|
message:
|
||||||
|
'Deleting this page will make its contents and all of its children inaccessible. Are you sure you want to continue?',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: 'Keep It',
|
||||||
|
role: 'cancel'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Delete It',
|
||||||
|
handler: async () => {
|
||||||
|
this.api
|
||||||
|
.post(`/page/delete/${this.deleteTarget.data.id}`)
|
||||||
|
.subscribe(res => {
|
||||||
|
this.reloadMenuItems().subscribe();
|
||||||
|
this.deleteTarget = false;
|
||||||
|
this.addChildTarget = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadMenuItems() {
|
||||||
|
return new Observable(sub => {
|
||||||
|
this.api.get('/menu/items').subscribe(result => {
|
||||||
|
this.nodes = result.data;
|
||||||
|
sub.next();
|
||||||
|
sub.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
initializeApp() {
|
initializeApp() {
|
||||||
this.platform.ready().then(() => {
|
this.platform.ready().then(() => {
|
||||||
this.statusBar.styleDefault();
|
this.statusBar.styleDefault();
|
||||||
this.splashScreen.hide();
|
this.splashScreen.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleDark() {
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
this.darkMode = !this.darkMode;
|
||||||
|
console.log('toggel Dark mode');
|
||||||
|
document.body.classList.toggle('dark', this.darkMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from "@angular/platform-browser";
|
||||||
import { RouteReuseStrategy } from '@angular/router';
|
import { RouteReuseStrategy } from "@angular/router";
|
||||||
|
|
||||||
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
|
import { IonicModule, IonicRouteStrategy } from "@ionic/angular";
|
||||||
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
|
import { SplashScreen } from "@ionic-native/splash-screen/ngx";
|
||||||
import { StatusBar } from '@ionic-native/status-bar/ngx';
|
import { StatusBar } from "@ionic-native/status-bar/ngx";
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from "./app.component";
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import {HttpClientModule} from '@angular/common/http';
|
import { HttpClientModule } from "@angular/common/http";
|
||||||
import {ComponentsModule} from './components/components.module';
|
import { ComponentsModule } from "./components/components.module";
|
||||||
|
import { TreeModule } from "angular-tree-component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent],
|
||||||
@@ -20,6 +21,7 @@ import {ComponentsModule} from './components/components.module';
|
|||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
ComponentsModule,
|
ComponentsModule,
|
||||||
|
TreeModule.forRoot()
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
StatusBar,
|
StatusBar,
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
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 { HostComponent } from './editor/host/host.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [HostComponent],
|
declarations: [HostComponent],
|
||||||
imports: [
|
imports: [CommonModule],
|
||||||
CommonModule
|
|
||||||
],
|
|
||||||
entryComponents: [HostComponent],
|
entryComponents: [HostComponent],
|
||||||
exports: [
|
exports: [HostComponent]
|
||||||
HostComponent
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
export class ComponentsModule { }
|
export class ComponentsModule {}
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
<ng-container>
|
<ng-container>
|
||||||
<div
|
<div
|
||||||
*ngIf="record.type === 'paragraph' || record.type === 'header1' || record.type === 'header2' || record.type === 'header3' || record.type === 'header4' || record.type === 'block_code'"
|
*ngIf="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"
|
class="host-host ion-padding"
|
||||||
contenteditable="true"
|
contenteditable="true"
|
||||||
(keyup)="onKeyUp($event)"
|
(keyup)="onKeyUp($event)"
|
||||||
(blur)="record.value=hostContainer.innerText"
|
(blur)="record.value=hostContainer.innerHTML"
|
||||||
|
(dblclick)="onHostDblClick()"
|
||||||
#hostContainer
|
#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'}"
|
[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'"
|
||||||
|
class="host-host ion-padding"
|
||||||
>
|
>
|
||||||
{{ record.value }}
|
<li
|
||||||
</div>
|
contenteditable="true"
|
||||||
|
*ngFor="let line of listLines; let i = index"
|
||||||
|
#liItems
|
||||||
|
(keyup)="onLIKeyUp($event, i)"
|
||||||
|
(blur)="listLines[i]=liItems.innerHTML"
|
||||||
|
[innerHTML]="listLines[i]"
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -24,3 +24,12 @@
|
|||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.host-host.click_link {
|
||||||
|
color: #0141b0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, ViewChildren} from '@angular/core';
|
||||||
import HostRecord from '../../../structures/HostRecord';
|
import HostRecord from '../../../structures/HostRecord';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,6 +12,9 @@ export class HostComponent implements OnInit {
|
|||||||
@Output() newHostRequested = new EventEmitter<HostComponent>();
|
@Output() newHostRequested = new EventEmitter<HostComponent>();
|
||||||
@Output() destroyHostRequested = new EventEmitter<HostComponent>();
|
@Output() destroyHostRequested = new EventEmitter<HostComponent>();
|
||||||
@ViewChild('hostContainer', {static: false}) hostContainer: ElementRef;
|
@ViewChild('hostContainer', {static: false}) hostContainer: ElementRef;
|
||||||
|
@ViewChildren('liItems') liItems;
|
||||||
|
|
||||||
|
public listLines: Array<string> = [];
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
@@ -40,9 +43,65 @@ export class HostComponent implements OnInit {
|
|||||||
this.record.type = 'header4';
|
this.record.type = 'header4';
|
||||||
} else if ( innerText.startsWith('```') ) {
|
} else if ( innerText.startsWith('```') ) {
|
||||||
this.record.type = 'block_code';
|
this.record.type = 'block_code';
|
||||||
|
} else if ( innerText.startsWith('http') ) {
|
||||||
|
this.record.type = 'click_link';
|
||||||
|
} else if ( innerText.startsWith('-') || innerText.startsWith(' -') ) {
|
||||||
|
this.record.type = 'ul';
|
||||||
|
this.listLines = [this.record.value];
|
||||||
|
setTimeout(() => {
|
||||||
|
const item = this.liItems.toArray()[0].nativeElement;
|
||||||
|
const s = window.getSelection();
|
||||||
|
const r = document.createRange();
|
||||||
|
r.setStart(item, 0);
|
||||||
|
r.setEnd(item, 0);
|
||||||
|
s.removeAllRanges();
|
||||||
|
s.addRange(r);
|
||||||
|
}, 0);
|
||||||
} else {
|
} else {
|
||||||
this.record.type = 'paragraph';
|
this.record.type = 'paragraph';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLIKeyUp($event, i) {
|
||||||
|
console.log({$event});
|
||||||
|
if ( $event.code === 'Enter' ) {
|
||||||
|
const newListLines = [];
|
||||||
|
this.liItems.forEach((li, index) => {
|
||||||
|
newListLines.push(li.nativeElement.innerText.trim());
|
||||||
|
if ( index === i ) {
|
||||||
|
newListLines.push('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.listLines = newListLines;
|
||||||
|
// this.listLines[i] = this.liItems[i].innerText.trim()
|
||||||
|
// const newLines = []
|
||||||
|
// this.listLines.forEach((rec, x) => {
|
||||||
|
// newLines.push(rec.trim());
|
||||||
|
// if ( i === x ) {
|
||||||
|
// newLines.push('');
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
|
// this.listLines = newLines;
|
||||||
|
|
||||||
|
// setTimeout(() => {
|
||||||
|
// const item = this.liItems.toArray()[i + 1].nativeElement;
|
||||||
|
// const s = window.getSelection();
|
||||||
|
// const r = document.createRange();
|
||||||
|
// r.setStart(item, 0);
|
||||||
|
// r.setEnd(item, 0);
|
||||||
|
// s.removeAllRanges();
|
||||||
|
// s.addRange(r);
|
||||||
|
// }, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onHostDblClick() {
|
||||||
|
if ( this.record.type === 'click_link' ) {
|
||||||
|
window.open(this.record.value.trim(), '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<p>
|
|
||||||
paragraph works!
|
|
||||||
</p>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { IonicModule } from '@ionic/angular';
|
|
||||||
|
|
||||||
import { ParagraphComponent } from './paragraph.component';
|
|
||||||
|
|
||||||
describe('ParagraphComponent', () => {
|
|
||||||
let component: ParagraphComponent;
|
|
||||||
let fixture: ComponentFixture<ParagraphComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [ ParagraphComponent ],
|
|
||||||
imports: [IonicModule.forRoot()]
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ParagraphComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-paragraph',
|
|
||||||
templateUrl: './paragraph.component.html',
|
|
||||||
styleUrls: ['./paragraph.component.scss'],
|
|
||||||
})
|
|
||||||
export class ParagraphComponent implements OnInit {
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit() {}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,48 +1,16 @@
|
|||||||
<ion-header>
|
<ion-toolbar>
|
||||||
<ion-toolbar>
|
<ion-buttons slot="start">
|
||||||
<ion-buttons slot="start">
|
<ion-menu-toggle>
|
||||||
<ion-menu-button></ion-menu-button>
|
<ion-button>
|
||||||
</ion-buttons>
|
<ion-icon slot="icon-only" name="menu"></ion-icon>
|
||||||
<ion-title>
|
</ion-button>
|
||||||
Home
|
</ion-menu-toggle>
|
||||||
</ion-title>
|
</ion-buttons>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
|
||||||
|
|
||||||
<ion-content>
|
|
||||||
<ion-card class="welcome-card">
|
|
||||||
<img src="/assets/shapes.svg" alt=""/>
|
|
||||||
<ion-card-header>
|
|
||||||
<ion-card-subtitle>Get Started</ion-card-subtitle>
|
|
||||||
<ion-card-title>Welcome to Ionic</ion-card-title>
|
|
||||||
</ion-card-header>
|
|
||||||
<ion-card-content>
|
|
||||||
<p>
|
|
||||||
Now that your app has been created, you'll want to start building out features
|
|
||||||
and components. Check out some of the resources below for next steps.
|
|
||||||
</p>
|
|
||||||
</ion-card-content>
|
|
||||||
</ion-card>
|
|
||||||
|
|
||||||
<ion-list lines="none">
|
<ion-grid style="height: 100%; justify-content: center; display: flex; font-size: 24pt; color: #ccc;">
|
||||||
<ion-list-header>
|
<ion-row align-items-center>
|
||||||
<ion-label>Resources</ion-label>
|
<ion-col>Hi, there! Select or create a page to get started.</ion-col>
|
||||||
</ion-list-header>
|
</ion-row>
|
||||||
<ion-item href="https://ionicframework.com/docs/">
|
</ion-grid>
|
||||||
<ion-icon slot="start" color="medium" name="book"></ion-icon>
|
|
||||||
<ion-label>Ionic Documentation</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item href="https://ionicframework.com/docs/building/scaffolding">
|
|
||||||
<ion-icon slot="start" color="medium" name="build"></ion-icon>
|
|
||||||
<ion-label>Scaffold Out Your App</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item href="https://ionicframework.com/docs/layout/structure">
|
|
||||||
<ion-icon slot="start" color="medium" name="grid"></ion-icon>
|
|
||||||
<ion-label>Change Your App Layout</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
<ion-item href="https://ionicframework.com/docs/theming/basics">
|
|
||||||
<ion-icon slot="start" color="medium" name="color-fill"></ion-icon>
|
|
||||||
<ion-label>Theme Your App</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-list>
|
|
||||||
</ion-content>
|
|
||||||
@@ -24,4 +24,4 @@
|
|||||||
You navigated here from <b>{{selectedItem.title }}</b>
|
You navigated here from <b>{{selectedItem.title }}</b>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
</ion-content>
|
</ion-content>
|
||||||
@@ -1,26 +1,30 @@
|
|||||||
<ion-header>
|
<ng-container>
|
||||||
<ion-toolbar>
|
<ion-header>
|
||||||
<ion-title>Note Editor</ion-title>
|
<ion-toolbar>
|
||||||
</ion-toolbar>
|
<ion-buttons slot="start">
|
||||||
</ion-header>
|
<ion-menu-toggle>
|
||||||
|
<ion-button>
|
||||||
|
<ion-icon slot="icon-only" name="menu"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-menu-toggle>
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title contenteditable="true" #titleBar>{{ pageRecord.Name }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ng-container>
|
<ng-container>
|
||||||
<div class="editor-buttons">
|
<div class="editor-root ion-padding">
|
||||||
<ion-button class="ion-padding ion-margin">Save</ion-button>
|
<div class="host-container ion-padding">
|
||||||
</div>
|
<editor-host #editorHosts *ngFor="let record of hostRecords; let i = index" [(record)]="hostRecords[i]"
|
||||||
<div class="editor-root ion-padding">
|
(newHostRequested)="onNewHostRequested($event)" (destroyHostRequested)="onDestroyHostRequested($event)">
|
||||||
Hello, world!
|
</editor-host>
|
||||||
|
</div>
|
||||||
<div class="host-container ion-padding">
|
|
||||||
<editor-host
|
|
||||||
#editorHosts
|
|
||||||
*ngFor="let record of hostRecords"
|
|
||||||
[(record)]="record"
|
|
||||||
(newHostRequested)="onNewHostRequested($event)"
|
|
||||||
(destroyHostRequested)="onDestroyHostRequested($event)"
|
|
||||||
></editor-host>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="editor-buttons">
|
||||||
</ng-container>
|
<ion-button (click)="onAddClick()" class="ion-padding ion-margin">Add Node</ion-button>
|
||||||
</ion-content>
|
<ion-button (click)="onSaveClick()" class="ion-padding ion-margin">Save</ion-button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ion-content>
|
||||||
|
</ng-container>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import {Component, OnInit, ViewChildren} from '@angular/core';
|
import {Component, Host, OnInit, ViewChild, ViewChildren} from '@angular/core';
|
||||||
import HostRecord from '../../structures/HostRecord';
|
import HostRecord from '../../structures/HostRecord';
|
||||||
|
import PageRecord from '../../structures/PageRecord';
|
||||||
|
import {PageService} from '../../service/page.service';
|
||||||
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-editor',
|
selector: 'app-editor',
|
||||||
@@ -7,14 +10,49 @@ import HostRecord from '../../structures/HostRecord';
|
|||||||
styleUrls: ['./editor.page.scss'],
|
styleUrls: ['./editor.page.scss'],
|
||||||
})
|
})
|
||||||
export class EditorPage implements OnInit {
|
export class EditorPage implements OnInit {
|
||||||
public hostRecords: Array<HostRecord> = [new HostRecord('I am a record.')];
|
public hostRecords: Array<HostRecord> = [new HostRecord('Click to edit page...')];
|
||||||
|
public pageRecord: PageRecord = new PageRecord();
|
||||||
|
public pageId: string;
|
||||||
|
|
||||||
@ViewChildren('editorHosts') editorHosts;
|
@ViewChildren('editorHosts') editorHosts;
|
||||||
|
@ViewChild('titleBar', {static: false}) titleBar;
|
||||||
|
|
||||||
constructor() { }
|
constructor(
|
||||||
|
protected pages: PageService,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected router: Router,
|
||||||
|
) {
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.pageId = params.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {}
|
||||||
console.log('Editor component: ', this);
|
|
||||||
|
ionViewDidEnter() {
|
||||||
|
if ( this.pageId ) {
|
||||||
|
this.pages.load(this.pageId).subscribe(pageRecord => {
|
||||||
|
this.pageRecord = pageRecord;
|
||||||
|
this.pages.get_nodes(pageRecord).subscribe((hosts: Array<HostRecord>) => {
|
||||||
|
this.hostRecords = hosts;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddClick() {
|
||||||
|
this.hostRecords.push(new HostRecord(''));
|
||||||
|
setTimeout(() => {
|
||||||
|
const host = this.editorHosts.toArray().reverse()[0].hostContainer.nativeElement;
|
||||||
|
const s = window.getSelection();
|
||||||
|
const r = document.createRange();
|
||||||
|
r.setStart(host, 0);
|
||||||
|
r.setEnd(host, 0);
|
||||||
|
s.removeAllRanges();
|
||||||
|
s.addRange(r);
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewHostRequested($event) {
|
onNewHostRequested($event) {
|
||||||
@@ -87,4 +125,18 @@ export class EditorPage implements OnInit {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSaveClick() {
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ApiService } from './api.service';
|
|
||||||
|
|
||||||
describe('ApiService', () => {
|
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
const service: ApiService = TestBed.get(ApiService);
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -24,11 +24,11 @@ export class ApiService {
|
|||||||
|
|
||||||
public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
public request(endpoint, params = {}, method: 'get'|'post' = 'get'): Observable<ApiResponse> {
|
||||||
return new Observable<ApiResponse>(sub => {
|
return new Observable<ApiResponse>(sub => {
|
||||||
const data: any = {}
|
let data: any = {}
|
||||||
if ( method === 'get' ) {
|
if ( method === 'get' ) {
|
||||||
data.params = params;
|
data.params = params;
|
||||||
} else {
|
} else {
|
||||||
data.body = params;
|
data = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http[method](this._build_url(endpoint), data).subscribe({
|
this.http[method](this._build_url(endpoint), data).subscribe({
|
||||||
|
|||||||
74
src/app/service/page.service.ts
Normal file
74
src/app/service/page.service.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import {Host, Injectable} from '@angular/core';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import PageRecord from '../structures/PageRecord';
|
||||||
|
import {ApiService} from './api.service';
|
||||||
|
import HostRecord from '../structures/HostRecord';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class PageService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected api: ApiService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
load(id: string): Observable<PageRecord> {
|
||||||
|
return new Observable<PageRecord>(sub => {
|
||||||
|
this.api.get(`/page/${id}`).subscribe(response => {
|
||||||
|
if ( response.status === 200 ) {
|
||||||
|
sub.next(new PageRecord(response.data));
|
||||||
|
sub.complete();
|
||||||
|
} else {
|
||||||
|
throw new Error(response.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save(page: PageRecord): Observable<PageRecord> {
|
||||||
|
return new Observable<PageRecord>(sub => {
|
||||||
|
this.api.post(`/page/${page.UUID}/save`, page.toSave()).subscribe(res => {
|
||||||
|
sub.next(new PageRecord(res.data));
|
||||||
|
sub.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get_nodes(page: PageRecord): Observable<Array<HostRecord>> {
|
||||||
|
return new Observable<Array<HostRecord>>(sub => {
|
||||||
|
this.api.get(`/page/${page.UUID}/nodes`).subscribe(res => {
|
||||||
|
const returns = [];
|
||||||
|
res.data.forEach(rec => {
|
||||||
|
const host = new HostRecord(rec.Value.Value);
|
||||||
|
host.load(rec);
|
||||||
|
returns.push(host);
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.next(returns);
|
||||||
|
sub.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save_nodes(page: PageRecord, nodes: Array<HostRecord>): Observable<Array<HostRecord>> {
|
||||||
|
return new Observable<Array<HostRecord>>(sub => {
|
||||||
|
nodes = nodes.map(x => {
|
||||||
|
x.PageId = page.UUID;
|
||||||
|
return x.toSave();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.post(`/page/${page.UUID}/nodes/save`, nodes).subscribe(res => {
|
||||||
|
const returns = [];
|
||||||
|
res.data.forEach(rec => {
|
||||||
|
const host = new HostRecord(rec.Value.Value);
|
||||||
|
host.load(rec);
|
||||||
|
returns.push(host);
|
||||||
|
});
|
||||||
|
|
||||||
|
sub.next(returns);
|
||||||
|
sub.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,54 @@
|
|||||||
export default class HostRecord {
|
export default class HostRecord {
|
||||||
public value = '';
|
public value = '';
|
||||||
|
public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code'|'click_link'|'ul' = 'paragraph';
|
||||||
|
|
||||||
public type: 'paragraph'|'header1'|'header2'|'header3'|'header4'|'block_code' = 'paragraph';
|
public CreatedAt: string;
|
||||||
|
public PageId: string;
|
||||||
|
public UUID: string;
|
||||||
|
public UpdatedAt: string;
|
||||||
|
public Value: any;
|
||||||
|
|
||||||
constructor(value = '') {
|
constructor(value = '') {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load(data: any) {
|
||||||
|
this.type = data.Type;
|
||||||
|
|
||||||
|
[
|
||||||
|
'CreatedAt',
|
||||||
|
'PageId',
|
||||||
|
'UUID',
|
||||||
|
'UpdatedAt',
|
||||||
|
'Value',
|
||||||
|
].forEach(field => {
|
||||||
|
if ( field in data ) {
|
||||||
|
this[field] = data[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toSave() {
|
||||||
|
const data: any = {
|
||||||
|
Type: this.type
|
||||||
|
};
|
||||||
|
|
||||||
|
[
|
||||||
|
'CreatedAt',
|
||||||
|
'PageId',
|
||||||
|
'UUID',
|
||||||
|
'UpdatedAt',
|
||||||
|
'Value',
|
||||||
|
].forEach(field => {
|
||||||
|
if ( field in this ) {
|
||||||
|
data[field] = this[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( !data.Value ) {
|
||||||
|
data.Value = {};
|
||||||
|
}
|
||||||
|
data.Value.Value = this.value;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/app/structures/PageRecord.ts
Normal file
58
src/app/structures/PageRecord.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
export default class PageRecord {
|
||||||
|
public UUID: string;
|
||||||
|
public Name: string;
|
||||||
|
public OrgUserId: string;
|
||||||
|
public IsPublic = true;
|
||||||
|
public IsVisibleInMenu = true;
|
||||||
|
public ParentId: string;
|
||||||
|
public NodeIds: Array<string>;
|
||||||
|
public CreatedAt: Date;
|
||||||
|
public UpdatedAt: Date;
|
||||||
|
public CreatedUserId: string;
|
||||||
|
public UpdateUserId: string;
|
||||||
|
public ChildPageIds: Array<string>;
|
||||||
|
|
||||||
|
constructor(data: any = {Name: 'Click to edit title...'}) {
|
||||||
|
[
|
||||||
|
'UUID',
|
||||||
|
'Name',
|
||||||
|
'OrgUserId',
|
||||||
|
'IsPublic',
|
||||||
|
'IsVisibleInMenu',
|
||||||
|
'ParentId',
|
||||||
|
'NodeIds',
|
||||||
|
'CreatedAt',
|
||||||
|
'UpdatedAt',
|
||||||
|
'CreatedUserId',
|
||||||
|
'UpdateUserId',
|
||||||
|
'ChildPageIds'
|
||||||
|
].forEach(field => {
|
||||||
|
if ( field in data ) {
|
||||||
|
this[field] = data[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toSave() {
|
||||||
|
const data = {};
|
||||||
|
[
|
||||||
|
'UUID',
|
||||||
|
'Name',
|
||||||
|
'OrgUserId',
|
||||||
|
'IsPublic',
|
||||||
|
'IsVisibleInMenu',
|
||||||
|
'ParentId',
|
||||||
|
'NodeIds',
|
||||||
|
'CreatedAt',
|
||||||
|
'UpdatedAt',
|
||||||
|
'CreatedUserId',
|
||||||
|
'UpdateUserId',
|
||||||
|
'ChildPageIds'
|
||||||
|
].forEach(field => {
|
||||||
|
if ( field in this ) {
|
||||||
|
data[field] = this[field];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
backendBase: '/',
|
backendBase: '/api/v1',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
@import "~@ionic/angular/css/normalize.css";
|
@import "~@ionic/angular/css/normalize.css";
|
||||||
@import "~@ionic/angular/css/structure.css";
|
@import "~@ionic/angular/css/structure.css";
|
||||||
@import "~@ionic/angular/css/typography.css";
|
@import "~@ionic/angular/css/typography.css";
|
||||||
@import '~@ionic/angular/css/display.css';
|
@import "~@ionic/angular/css/display.css";
|
||||||
|
|
||||||
/* Optional CSS utils that can be commented out */
|
/* Optional CSS utils that can be commented out */
|
||||||
@import "~@ionic/angular/css/padding.css";
|
@import "~@ionic/angular/css/padding.css";
|
||||||
@@ -24,3 +24,5 @@
|
|||||||
@import "~@ionic/angular/css/text-alignment.css";
|
@import "~@ionic/angular/css/text-alignment.css";
|
||||||
@import "~@ionic/angular/css/text-transformation.css";
|
@import "~@ionic/angular/css/text-transformation.css";
|
||||||
@import "~@ionic/angular/css/flex-utils.css";
|
@import "~@ionic/angular/css/flex-utils.css";
|
||||||
|
|
||||||
|
@import "~angular-tree-component/dist/angular-tree-component.css";
|
||||||
|
|||||||
@@ -75,3 +75,161 @@
|
|||||||
--ion-color-light-shade: #d7d8da;
|
--ion-color-light-shade: #d7d8da;
|
||||||
--ion-color-light-tint: #f5f6f9;
|
--ion-color-light-tint: #f5f6f9;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Dark Colors
|
||||||
|
* -------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
body.dark {
|
||||||
|
--ion-color-primary: #428cff;
|
||||||
|
--ion-color-primary-rgb: 66, 140, 255;
|
||||||
|
--ion-color-primary-contrast: #ffffff;
|
||||||
|
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-primary-shade: #3a7be0;
|
||||||
|
--ion-color-primary-tint: #5598ff;
|
||||||
|
|
||||||
|
--ion-color-secondary: #50c8ff;
|
||||||
|
--ion-color-secondary-rgb: 80, 200, 255;
|
||||||
|
--ion-color-secondary-contrast: #ffffff;
|
||||||
|
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-secondary-shade: #46b0e0;
|
||||||
|
--ion-color-secondary-tint: #62ceff;
|
||||||
|
|
||||||
|
--ion-color-tertiary: #6a64ff;
|
||||||
|
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||||
|
--ion-color-tertiary-contrast: #ffffff;
|
||||||
|
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-tertiary-shade: #5d58e0;
|
||||||
|
--ion-color-tertiary-tint: #7974ff;
|
||||||
|
|
||||||
|
--ion-color-success: #2fdf75;
|
||||||
|
--ion-color-success-rgb: 47, 223, 117;
|
||||||
|
--ion-color-success-contrast: #000000;
|
||||||
|
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-success-shade: #29c467;
|
||||||
|
--ion-color-success-tint: #44e283;
|
||||||
|
|
||||||
|
--ion-color-warning: #ffd534;
|
||||||
|
--ion-color-warning-rgb: 255, 213, 52;
|
||||||
|
--ion-color-warning-contrast: #000000;
|
||||||
|
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-warning-shade: #e0bb2e;
|
||||||
|
--ion-color-warning-tint: #ffd948;
|
||||||
|
|
||||||
|
--ion-color-danger: #ff4961;
|
||||||
|
--ion-color-danger-rgb: 255, 73, 97;
|
||||||
|
--ion-color-danger-contrast: #ffffff;
|
||||||
|
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-danger-shade: #e04055;
|
||||||
|
--ion-color-danger-tint: #ff5b71;
|
||||||
|
|
||||||
|
--ion-color-dark: #f4f5f8;
|
||||||
|
--ion-color-dark-rgb: 244, 245, 248;
|
||||||
|
--ion-color-dark-contrast: #000000;
|
||||||
|
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-dark-shade: #d7d8da;
|
||||||
|
--ion-color-dark-tint: #f5f6f9;
|
||||||
|
|
||||||
|
--ion-color-medium: #989aa2;
|
||||||
|
--ion-color-medium-rgb: 152, 154, 162;
|
||||||
|
--ion-color-medium-contrast: #000000;
|
||||||
|
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||||
|
--ion-color-medium-shade: #86888f;
|
||||||
|
--ion-color-medium-tint: #a2a4ab;
|
||||||
|
|
||||||
|
--ion-color-light: #222428;
|
||||||
|
--ion-color-light-rgb: 34, 36, 40;
|
||||||
|
--ion-color-light-contrast: #ffffff;
|
||||||
|
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||||
|
--ion-color-light-shade: #1e2023;
|
||||||
|
--ion-color-light-tint: #383a3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iOS Dark Theme
|
||||||
|
* -------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ios body.dark {
|
||||||
|
--ion-background-color: #000000;
|
||||||
|
--ion-background-color-rgb: 0, 0, 0;
|
||||||
|
|
||||||
|
--ion-text-color: #ffffff;
|
||||||
|
--ion-text-color-rgb: 255, 255, 255;
|
||||||
|
|
||||||
|
--ion-color-step-50: #0d0d0d;
|
||||||
|
--ion-color-step-100: #1a1a1a;
|
||||||
|
--ion-color-step-150: #262626;
|
||||||
|
--ion-color-step-200: #333333;
|
||||||
|
--ion-color-step-250: #404040;
|
||||||
|
--ion-color-step-300: #4d4d4d;
|
||||||
|
--ion-color-step-350: #595959;
|
||||||
|
--ion-color-step-400: #666666;
|
||||||
|
--ion-color-step-450: #737373;
|
||||||
|
--ion-color-step-500: #808080;
|
||||||
|
--ion-color-step-550: #8c8c8c;
|
||||||
|
--ion-color-step-600: #999999;
|
||||||
|
--ion-color-step-650: #a6a6a6;
|
||||||
|
--ion-color-step-700: #b3b3b3;
|
||||||
|
--ion-color-step-750: #bfbfbf;
|
||||||
|
--ion-color-step-800: #cccccc;
|
||||||
|
--ion-color-step-850: #d9d9d9;
|
||||||
|
--ion-color-step-900: #e6e6e6;
|
||||||
|
--ion-color-step-950: #f2f2f2;
|
||||||
|
|
||||||
|
--ion-toolbar-background: #0d0d0d;
|
||||||
|
|
||||||
|
--ion-item-background: #1c1c1c;
|
||||||
|
--ion-item-background-activated: #313131;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Material Design Dark Theme
|
||||||
|
* -------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.md body.dark {
|
||||||
|
--ion-background-color: #121212;
|
||||||
|
--ion-background-color-rgb: 18, 18, 18;
|
||||||
|
|
||||||
|
--ion-text-color: #ffffff;
|
||||||
|
--ion-text-color-rgb: 255, 255, 255;
|
||||||
|
|
||||||
|
--ion-border-color: #222222;
|
||||||
|
|
||||||
|
--ion-color-step-50: #1e1e1e;
|
||||||
|
--ion-color-step-100: #2a2a2a;
|
||||||
|
--ion-color-step-150: #363636;
|
||||||
|
--ion-color-step-200: #414141;
|
||||||
|
--ion-color-step-250: #4d4d4d;
|
||||||
|
--ion-color-step-300: #595959;
|
||||||
|
--ion-color-step-350: #656565;
|
||||||
|
--ion-color-step-400: #717171;
|
||||||
|
--ion-color-step-450: #7d7d7d;
|
||||||
|
--ion-color-step-500: #898989;
|
||||||
|
--ion-color-step-550: #949494;
|
||||||
|
--ion-color-step-600: #a0a0a0;
|
||||||
|
--ion-color-step-650: #acacac;
|
||||||
|
--ion-color-step-700: #b8b8b8;
|
||||||
|
--ion-color-step-750: #c4c4c4;
|
||||||
|
--ion-color-step-800: #d0d0d0;
|
||||||
|
--ion-color-step-850: #dbdbdb;
|
||||||
|
--ion-color-step-900: #e7e7e7;
|
||||||
|
--ion-color-step-950: #f3f3f3;
|
||||||
|
|
||||||
|
--ion-item-background: #1a1b1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--color-toolbar-background: #2a2a2a;
|
||||||
|
--color-toolbar-text: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--color-toolbar-background: white;
|
||||||
|
--color-toolbar-text: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user