diff --git a/src/components/rich-text-editor/core-rich-text-editor.html b/src/components/rich-text-editor/core-rich-text-editor.html index 7a02610dd..40e06d3f4 100644 --- a/src/components/rich-text-editor/core-rich-text-editor.html +++ b/src/components/rich-text-editor/core-rich-text-editor.html @@ -1,33 +1,81 @@ -
-
-
- - -
-
- - - - - - - - - - - - -
-
+
-
- -
-
- -
-
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- - diff --git a/src/components/rich-text-editor/rich-text-editor.scss b/src/components/rich-text-editor/rich-text-editor.scss index 8bce984e8..0cf3a5960 100644 --- a/src/components/rich-text-editor/rich-text-editor.scss +++ b/src/components/rich-text-editor/rich-text-editor.scss @@ -4,27 +4,20 @@ ion-app.app-root core-rich-text-editor { min-height: 200px; /* Just in case vh is not supported */ min-height: 40vh; width: 100%; - position: relative; - display: block; + display: flex; + flex-direction: column; - > div { - position: absolute; - @include position(0, 0, 0, 0); - height: 100%; - width: 100%; - display: flex; - flex-direction: column; - } .core-rte-editor, .core-textarea { padding: 2px; margin: 2px; width: 100%; resize: none; background-color: $white; - flex-grow: 1; } .core-rte-editor { + flex-grow: 1; + flex-shrink: 1; -webkit-user-select: auto !important; word-wrap: break-word; overflow-x: hidden; @@ -48,6 +41,8 @@ ion-app.app-root core-rich-text-editor { } .core-textarea { + flex-grow: 1; + flex-shrink: 1; position: relative; textarea { @@ -64,33 +59,64 @@ ion-app.app-root core-rich-text-editor { } div.core-rte-toolbar { - background: $gray-darker; - @include margin(0px, 1px, 15px, 1px); - text-align: center; - flex-grow: 0; + display: flex; width: 100%; z-index: 1; + flex-grow: 0; + flex-shrink: 0; + background-color: $white; + @include padding(5px, null); + border-top: 1px solid $gray; - .core-rte-buttons { + ion-slides { + width: 240px; + flex-grow: 1; + flex-shrink: 1; + } + + button { display: flex; + justify-content: center; align-items: center; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-evenly; + width: 36px; + height: 36px; + margin: 0 auto; + font-size: 18px; + background-color: $white; + border-radius: 4px; + @include core-transition(background-color, 200ms); + color: $text-color; + cursor: pointer; - button { - background: $gray-darker; - color: $white; - font-size: 1.1em; - height: 35px; - min-width: 30px; - @include padding(null, 3px, null, 3px); - @include border-end(qpx, solid, $gray-dark); - border-bottom: 1px solid $gray-dark; - @include position(-6px, 0, null, null); - flex-grow: 1; - margin: 0; + &.toolbar-button-enable { + width: 100%; } + + &:active, &[aria-pressed="true"] { + background-color: $gray; + } + + &.toolbar-arrow { + width: 28px; + flex-grow: 0; + flex-shrink: 0; + opacity: 1; + @include core-transition(opacity, 200ms); + + &:active { + background-color: $white; + } + + &.toolbar-arrow-hidden { + opacity: 0; + } + } + } + + &.toolbar-hidden { + visibility: none; + height: 0; + border: none; } } diff --git a/src/components/rich-text-editor/rich-text-editor.ts b/src/components/rich-text-editor/rich-text-editor.ts index a1254bad9..f66f553f1 100644 --- a/src/components/rich-text-editor/rich-text-editor.ts +++ b/src/components/rich-text-editor/rich-text-editor.ts @@ -14,7 +14,7 @@ import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, AfterContentInit, OnDestroy, Optional } from '@angular/core'; -import { TextInput, Content, Platform } from 'ionic-angular'; +import { TextInput, Content, Platform, Slides } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; @@ -56,7 +56,6 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy @ViewChild('editor') editor: ElementRef; // WYSIWYG editor. @ViewChild('textarea') textarea: TextInput; // Textarea editor. - @ViewChild('decorate') decorate: ElementRef; // Buttons. protected element: HTMLDivElement; protected editorElement: HTMLDivElement; @@ -71,6 +70,20 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy rteEnabled = false; editorSupported = true; + // Toolbar. + @ViewChild('toolbar') toolbar: ElementRef; + @ViewChild(Slides) toolbarSlides: Slides; + isPhone = this.platform.is('mobile') && !this.platform.is('tablet'); + toolbarHidden = this.isPhone; + numToolbarButtons = 6; + toolbarArrows = false; + toolbarPrevHidden = true; + toolbarNextHidden = false; + + protected isCurrentView = true; + protected toolbarButtonWidth = 40; + protected toolbarArrowWidth = 28; + constructor(private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private sitesProvider: CoreSitesProvider, private filepoolProvider: CoreFilepoolProvider, @Optional() private content: Content, elementRef: ElementRef, private events: CoreEventsProvider, @@ -123,6 +136,8 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy this.kbHeight = kbHeight; this.maximizeEditorSize(); }); + + this.updateToolbarButtons(); } /** @@ -390,13 +405,16 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy this.rteEnabled = !this.rteEnabled; // Set focus and cursor at the end. - setTimeout(() => { - if (this.rteEnabled) { - this.editorElement.focus(); - } else { - this.textarea.setFocus(); - } - }); + // Modify the DOM directly so the keyboard stays open. + if (this.rteEnabled) { + this.editorElement.removeAttribute('hidden'); + this.textarea.getNativeElement().setAttribute('hidden', ''); + this.editorElement.focus(); + } else { + this.editorElement.setAttribute('hidden', ''); + this.textarea.getNativeElement().removeAttribute('hidden'); + this.textarea.setFocus(); + } } /** @@ -508,6 +526,7 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy protected buttonAction($event: any, command: string): void { $event.preventDefault(); $event.stopPropagation(); + this.editorElement.focus(); if (command) { if (command.includes('|')) { @@ -521,6 +540,99 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy } } + /** + * Hide the toolbar. + */ + hideToolbar(): void { + this.editorElement.focus(); + this.toolbarHidden = true; + } + + /** + * Show the toolbar. + */ + showToolbar(): void { + this.editorElement.focus(); + this.toolbarHidden = false; + } + + /** + * Method that shows the next toolbar buttons. + */ + toolbarNext(): void { + if (!this.toolbarNextHidden) { + const currentIndex = this.toolbarSlides.getActiveIndex() || 0; + this.toolbarSlides.slideTo(currentIndex + this.numToolbarButtons); + } + this.editorElement.focus(); + } + + /** + * Method that shows the previous toolbar buttons. + */ + toolbarPrev(): void { + if (!this.toolbarPrevHidden) { + const currentIndex = this.toolbarSlides.getActiveIndex() || 0; + this.toolbarSlides.slideTo(currentIndex - this.numToolbarButtons); + } + this.editorElement.focus(); + } + + /** + * Update the number of toolbar buttons displayed. + */ + updateToolbarButtons(): void { + if (!this.isCurrentView) { + // Don't calculate if component isn't in current view, the calculations are wrong. + return; + } + + if (!(this.toolbarSlides as any)._init) { + // Slides is not initialized yet, try later. + setTimeout(this.updateToolbarButtons.bind(this), 100); + + return; + } + + const width = this.domUtils.getElementWidth(this.toolbar.nativeElement); + if (width > this.toolbarSlides.length() * this.toolbarButtonWidth) { + this.numToolbarButtons = this.toolbarSlides.length(); + this.toolbarArrows = false; + } else { + this.numToolbarButtons = Math.floor((width - this.toolbarArrowWidth * 2) / this.toolbarButtonWidth); + this.toolbarArrows = true; + } + + this.toolbarSlides.update(); + + this.updateToolbarArrows(); + } + + /** + * Show or hide next/previous toolbar arrows. + */ + updateToolbarArrows(): void { + const currentIndex = this.toolbarSlides.getActiveIndex() || 0; + this.toolbarPrevHidden = currentIndex <= 0; + this.toolbarNextHidden = currentIndex + this.numToolbarButtons >= this.toolbarSlides.length(); + } + + /** + * User entered the page that contains the component. + */ + ionViewDidEnter(): void { + this.isCurrentView = true; + + this.updateToolbarButtons(); + } + + /** + * User left the page that contains the component. + */ + ionViewDidLeave(): void { + this.isCurrentView = false; + } + /** * Component being destroyed. */