Explorar o código

feat(admin-ui): Create rich text editor

Relates to #27
Michael Bromley %!s(int64=7) %!d(string=hai) anos
pai
achega
8ecb9395cf

+ 5 - 2
admin-ui/angular.json

@@ -30,9 +30,12 @@
             "styles": [
               "./node_modules/@clr/icons/clr-icons.min.css",
               "./node_modules/@clr/ui/clr-ui.min.css",
-              "src/styles/styles.scss"
+              "src/styles/styles.scss",
+              "./node_modules/trix/dist/trix.css"
+            ],
+            "scripts": [
+              "./node_modules/trix/dist/trix-core.js"
             ],
-            "scripts": [],
             "stylePreprocessorOptions": {
               "includePaths": ["./src/styles"]
             },

+ 2 - 1
admin-ui/package.json

@@ -43,6 +43,7 @@
     "ngx-translate-messageformat-compiler": "^4.1.3",
     "rxjs": "^6.3.3",
     "rxjs-compat": "^6.2.1",
+    "trix": "^1.0.0",
     "zone.js": "^0.8.26"
   },
   "devDependencies": {
@@ -70,4 +71,4 @@
     "tslint": "^5.11.0",
     "typescript": "~3.1.3"
   }
-}
+}

+ 2 - 0
admin-ui/src/app/shared/components/rich-text-editor/rich-text-editor.component.html

@@ -0,0 +1,2 @@
+<label (click)="trixEditor.focus()">{{ label }}</label>
+<trix-editor #trixEditor></trix-editor>

+ 31 - 0
admin-ui/src/app/shared/components/rich-text-editor/rich-text-editor.component.scss

@@ -0,0 +1,31 @@
+@import "variables";
+
+:host {
+    display: block;
+    max-width: 710px;
+    margin-bottom: .5rem;
+}
+
+trix-editor {
+    background-color: white;
+    min-height: 128px;
+    border: 2px solid $color-grey-3;
+    transition: border-color 0.2s;
+
+    &:focus {
+        border-color: $color-focused;
+    }
+}
+
+::ng-deep trix-toolbar {
+    background-color: $color-grey-2;
+    padding: 12px 12px 0;
+    position: sticky;
+    top: -24px;
+}
+
+label {
+    color: #000;
+    font-size: .541667rem;
+    margin: 0 0 .5rem;
+}

+ 88 - 0
admin-ui/src/app/shared/components/rich-text-editor/rich-text-editor.component.ts

@@ -0,0 +1,88 @@
+import {
+    AfterViewInit,
+    ChangeDetectionStrategy,
+    ChangeDetectorRef,
+    Component,
+    ElementRef,
+    Input,
+    OnDestroy,
+    ViewChild,
+} from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+export interface Trix {
+    editor: any;
+}
+
+/**
+ * A rich text (HTML) editor based on Trix (https://github.com/basecamp/trix)
+ */
+@Component({
+    selector: 'vdr-rich-text-editor',
+    templateUrl: './rich-text-editor.component.html',
+    styleUrls: ['./rich-text-editor.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+    providers: [
+        {
+            provide: NG_VALUE_ACCESSOR,
+            useExisting: RichTextEditorComponent,
+            multi: true,
+        },
+    ],
+})
+export class RichTextEditorComponent implements ControlValueAccessor, AfterViewInit, OnDestroy {
+    @Input() label: string;
+
+    id = Math.random()
+        .toString(36)
+        .substr(3);
+    onChange: (val: any) => void;
+    onTouch: () => void;
+    disabled = false;
+
+    @ViewChild('trixEditor') private trixEditor: ElementRef;
+
+    constructor(private changeDetector: ChangeDetectorRef) {}
+
+    get trix(): HTMLElement & Trix {
+        return this.trixEditor ? this.trixEditor.nativeElement : {};
+    }
+
+    onTrixChangeHandler = () => {
+        this.onChange(this.trix.innerHTML);
+        this.changeDetector.markForCheck();
+    };
+
+    onTrixFocusHandler = () => {
+        this.onTouch();
+        this.changeDetector.markForCheck();
+    };
+
+    ngAfterViewInit() {
+        this.trix.addEventListener('trix-change', this.onTrixChangeHandler);
+        this.trix.addEventListener('trix-focus', this.onTrixFocusHandler);
+    }
+
+    ngOnDestroy() {
+        this.trix.removeEventListener('trix-change', this.onTrixChangeHandler);
+        this.trix.removeEventListener('trix-focus', this.onTrixFocusHandler);
+    }
+
+    registerOnChange(fn: any) {
+        this.onChange = fn;
+    }
+
+    registerOnTouched(fn: any) {
+        this.onTouch = fn;
+    }
+
+    setDisabledState(isDisabled: boolean) {
+        this.disabled = isDisabled;
+    }
+
+    writeValue(value: any) {
+        if (this.trix.innerHTML !== value) {
+            this.trix.editor.loadHTML(value);
+        }
+    }
+}

+ 4 - 1
admin-ui/src/app/shared/shared.module.ts

@@ -1,5 +1,5 @@
 import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { RouterModule } from '@angular/router';
 import { ClarityModule, ClrFormsNextModule } from '@clr/angular';
@@ -31,6 +31,7 @@ import { DialogComponentOutletComponent } from './components/modal-dialog/dialog
 import { DialogTitleDirective } from './components/modal-dialog/dialog-title.directive';
 import { ModalDialogComponent } from './components/modal-dialog/modal-dialog.component';
 import { PaginationControlsComponent } from './components/pagination-controls/pagination-controls.component';
+import { RichTextEditorComponent } from './components/rich-text-editor/rich-text-editor.component';
 import { SelectToggleComponent } from './components/select-toggle/select-toggle.component';
 import { TableRowActionComponent } from './components/table-row-action/table-row-action.component';
 import { BackgroundColorFromDirective } from './directives/background-color-from.directive';
@@ -76,6 +77,7 @@ const DECLARATIONS = [
     DialogTitleDirective,
     SelectToggleComponent,
     LanguageSelectorComponent,
+    RichTextEditorComponent,
 ];
 
 @NgModule({
@@ -90,5 +92,6 @@ const DECLARATIONS = [
         ModalService,
     ],
     entryComponents: [ModalDialogComponent],
+    schemas: [CUSTOM_ELEMENTS_SCHEMA],
 })
 export class SharedModule {}

+ 1 - 0
admin-ui/src/styles/_variables.scss

@@ -16,6 +16,7 @@ $color-grey-4: #9A9A9A;
 $color-grey-5: #565656;
 $color-grey-6: #313131;
 $color-grey-7: #111111;
+$color-focused: #0094d2;
 
 // breakpoints
 $breakpoint-small: 768px;

+ 4 - 0
admin-ui/yarn.lock

@@ -7406,6 +7406,10 @@ trim-right@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
 
+trix@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/trix/-/trix-1.0.0.tgz#e9cc98cf6030c908f8d54e317b5b072f927b0c6b"
+
 "true-case-path@^1.0.2":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62"