-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
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.
*/