forked from EVOgeek/Vmeda.Online
		
	MOBILE-4470 rte: Fix focus and blur
This commit is contained in:
		
							parent
							
								
									dc698724ac
								
							
						
					
					
						commit
						0fedfee2e4
					
				
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 27 KiB | 
| @ -1,12 +1,12 @@ | |||||||
| <div class="core-rte-editor-container" (click)="focusRTE()" [class.toolbar-hidden]="toolbarHidden"> | <div class="core-rte-editor-container" (click)="focusRTE($event)" [class.toolbar-hidden]="toolbarHidden"> | ||||||
|     <div [hidden]="!rteEnabled" #editor class="core-rte-editor" role="textbox" contenteditable="true" [class.empty]="isEmpty" |     <div [hidden]="!rteEnabled" #editor class="core-rte-editor" role="textbox" contenteditable="true" [class.empty]="isEmpty" | ||||||
|         [attr.aria-labelledby]="ariaLabelledBy" [attr.data-placeholder-text]="placeholder" (focus)="showToolbar($event)" |         [attr.aria-labelledby]="ariaLabelledBy" [attr.data-placeholder-text]="placeholder" (focus)="focusRTE($event)" | ||||||
|         (blur)="hideToolbar($event)" (keydown)="onKeyDown($event)"> |         (blur)="blurRTE($event)" (keydown)="onKeyDown($event)"> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|     <ion-textarea [hidden]="rteEnabled" #textarea class="core-textarea" role="textbox" [attr.name]="name" ngControl="control" |     <ion-textarea [hidden]="rteEnabled" #textarea class="core-textarea" role="textbox" [attr.name]="name" ngControl="control" | ||||||
|         [placeholder]="placeholder" [attr.aria-labelledby]="ariaLabelledBy" (ionChange)="onChange()" (ionFocus)="showToolbar($event)" |         [placeholder]="placeholder" [attr.aria-labelledby]="ariaLabelledBy" (ionChange)="onChange()" (ionFocus)="focusRTE($event)" | ||||||
|         (ionBlur)="hideToolbar($event)" /> |         (ionBlur)="blurRTE($event)" /> | ||||||
| 
 | 
 | ||||||
|     <div class="core-rte-info-message" *ngIf="infoMessage"> |     <div class="core-rte-info-message" *ngIf="infoMessage"> | ||||||
|         <ion-icon name="fas-circle-info" aria-hidden="true" /> |         <ion-icon name="fas-circle-info" aria-hidden="true" /> | ||||||
| @ -110,12 +110,6 @@ | |||||||
|                 <ion-icon name="fas-code" aria-hidden="true" /> |                 <ion-icon name="fas-code" aria-hidden="true" /> | ||||||
|             </button> |             </button> | ||||||
|         </swiper-slide> |         </swiper-slide> | ||||||
|         <swiper-slide *ngIf="isPhone"> |  | ||||||
|             <button [title]="'core.editor.hidetoolbar' | translate" (click)="hideToolbar($event, true)" (keyup)="hideToolbar($event, true)" |  | ||||||
|                 (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0"> |  | ||||||
|                 <ion-icon name="fas-xmark" aria-hidden="true" /> |  | ||||||
|             </button> |  | ||||||
|         </swiper-slide> |  | ||||||
|     </swiper-container> |     </swiper-container> | ||||||
|     <button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarNextHidden ? 'true' : null" |     <button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarNextHidden ? 'true' : null" | ||||||
|         [attr.aria-label]="'core.next' | translate" (click)="toolbarNext($event)" (keyup)="toolbarNext($event)" |         [attr.aria-label]="'core.next' | translate" (click)="toolbarNext($event)" (keyup)="toolbarNext($event)" | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ | |||||||
|     border: 1px solid var(--stroke); |     border: 1px solid var(--stroke); | ||||||
|     border-radius: var(--mdl-shape-borderRadius-md); |     border-radius: var(--mdl-shape-borderRadius-md); | ||||||
| 
 | 
 | ||||||
|     &:focus-within { |     &.has-focus { | ||||||
|         border-color: var(--a11y-focus-color); |         border-color: var(--a11y-focus-color); | ||||||
|         border-width: 2px; |         border-width: 2px; | ||||||
|         outline: none !important; |         outline: none !important; | ||||||
|  | |||||||
| @ -78,9 +78,13 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|     @Input() draftExtraParams?: Record<string, unknown>; // Extra params to identify the draft.
 |     @Input() draftExtraParams?: Record<string, unknown>; // Extra params to identify the draft.
 | ||||||
|     @Output() contentChanged: EventEmitter<string | undefined | null>; |     @Output() contentChanged: EventEmitter<string | undefined | null>; | ||||||
| 
 | 
 | ||||||
|     @ViewChild('editor') editor?: ElementRef; // WYSIWYG editor.
 |     protected editorElement?: HTMLDivElement; // WYSIWYG editor.
 | ||||||
|  |     @ViewChild('editor') editor?: ElementRef<HTMLDivElement>; | ||||||
|  | 
 | ||||||
|  |     @ViewChild('toolbar') toolbar?: ElementRef<HTMLDivElement>; | ||||||
|  | 
 | ||||||
|  |     protected textareaElement?: HTMLTextAreaElement; | ||||||
|     @ViewChild('textarea') textarea?: IonTextarea; // Textarea editor.
 |     @ViewChild('textarea') textarea?: IonTextarea; // Textarea editor.
 | ||||||
|     @ViewChild('toolbar') toolbar?: ElementRef; |  | ||||||
| 
 | 
 | ||||||
|     protected toolbarSlides?: Swiper; |     protected toolbarSlides?: Swiper; | ||||||
|     @ViewChild('swiperRef') set swiperRef(swiperRef: ElementRef) { |     @ViewChild('swiperRef') set swiperRef(swiperRef: ElementRef) { | ||||||
| @ -88,7 +92,9 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|          * This setTimeout waits for Ionic's async initialization to complete. |          * This setTimeout waits for Ionic's async initialization to complete. | ||||||
|          * Otherwise, an outdated swiper reference will be used. |          * Otherwise, an outdated swiper reference will be used. | ||||||
|          */ |          */ | ||||||
|         setTimeout(() => { |         setTimeout(async () => { | ||||||
|  |             await this.waitLoadingsDone(); | ||||||
|  | 
 | ||||||
|             const swiper = CoreSwiper.initSwiperIfAvailable(this.toolbarSlides, swiperRef, this.swiperOpts); |             const swiper = CoreSwiper.initSwiperIfAvailable(this.toolbarSlides, swiperRef, this.swiperOpts); | ||||||
|             if (!swiper) { |             if (!swiper) { | ||||||
|                 return; |                 return; | ||||||
| @ -102,8 +108,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|     protected readonly RESTORE_MESSAGE_CLEAR_TIME = 6000; |     protected readonly RESTORE_MESSAGE_CLEAR_TIME = 6000; | ||||||
|     protected readonly SAVE_MESSAGE_CLEAR_TIME = 2000; |     protected readonly SAVE_MESSAGE_CLEAR_TIME = 2000; | ||||||
| 
 | 
 | ||||||
|     protected element: HTMLDivElement; |     protected element: HTMLElement; | ||||||
|     protected editorElement?: HTMLDivElement; |  | ||||||
|     protected minHeight = 200; // Minimum height of the editor.
 |     protected minHeight = 200; // Minimum height of the editor.
 | ||||||
| 
 | 
 | ||||||
|     protected valueChangeSubscription?: Subscription; |     protected valueChangeSubscription?: Subscription; | ||||||
| @ -127,6 +132,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|     protected domPromise?: CoreCancellablePromise<void>; |     protected domPromise?: CoreCancellablePromise<void>; | ||||||
|     protected buttonsDomPromise?: CoreCancellablePromise<void>; |     protected buttonsDomPromise?: CoreCancellablePromise<void>; | ||||||
|     protected shortcutCommands?: Record<string, EditorCommand>; |     protected shortcutCommands?: Record<string, EditorCommand>; | ||||||
|  |     protected blurTimeout?: number; | ||||||
| 
 | 
 | ||||||
|     rteEnabled = false; |     rteEnabled = false; | ||||||
|     isPhone = false; |     isPhone = false; | ||||||
| @ -165,7 +171,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|         elementRef: ElementRef, |         elementRef: ElementRef, | ||||||
|     ) { |     ) { | ||||||
|         this.contentChanged = new EventEmitter<string>(); |         this.contentChanged = new EventEmitter<string>(); | ||||||
|         this.element = elementRef.nativeElement as HTMLDivElement; |         this.element = elementRef.nativeElement; | ||||||
|         this.pageInstance = 'app_' + Date.now(); // Generate a "unique" ID based on timestamp.
 |         this.pageInstance = 'app_' + Date.now(); // Generate a "unique" ID based on timestamp.
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -188,6 +194,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
| 
 | 
 | ||||||
|         // Setup the editor.
 |         // Setup the editor.
 | ||||||
|         this.editorElement = this.editor?.nativeElement as HTMLDivElement; |         this.editorElement = this.editor?.nativeElement as HTMLDivElement; | ||||||
|  |         this.textareaElement = await this.textarea?.getInputElement(); | ||||||
|         this.setContent(this.control?.value); |         this.setContent(this.control?.value); | ||||||
|         this.originalContent = this.control?.value ?? undefined; |         this.originalContent = this.control?.value ?? undefined; | ||||||
|         this.lastDraft = this.control?.value ?? ''; |         this.lastDraft = this.control?.value ?? ''; | ||||||
| @ -508,17 +515,18 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
| 
 | 
 | ||||||
|         // Set focus and cursor at the end.
 |         // Set focus and cursor at the end.
 | ||||||
|         // Modify the DOM directly so the keyboard stays open.
 |         // Modify the DOM directly so the keyboard stays open.
 | ||||||
|  | 
 | ||||||
|         if (this.rteEnabled) { |         if (this.rteEnabled) { | ||||||
|             this.editorElement?.removeAttribute('hidden'); |             this.editorElement?.removeAttribute('hidden'); | ||||||
|             const textareaInputElement = await this.textarea?.getInputElement(); |             this.textareaElement?.setAttribute('hidden', ''); | ||||||
|             textareaInputElement?.setAttribute('hidden', ''); |  | ||||||
|             this.editorElement?.focus(); |  | ||||||
|         } else { |         } else { | ||||||
|             this.editorElement?.setAttribute('hidden', ''); |             this.editorElement?.setAttribute('hidden', ''); | ||||||
|             const textareaInputElement = await this.textarea?.getInputElement(); |             this.textareaElement?.removeAttribute('hidden'); | ||||||
|             textareaInputElement?.removeAttribute('hidden'); |  | ||||||
|             this.textarea?.setFocus(); |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         await CoreUtils.nextTick(); | ||||||
|  | 
 | ||||||
|  |         this.focusRTE(event); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -707,40 +715,28 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Focus editor when click the area. |      * Blur and hide the toolbar in phone mode. | ||||||
|      */ |  | ||||||
|     focusRTE(): void { |  | ||||||
|         if (this.rteEnabled) { |  | ||||||
|             this.editorElement?.focus(); |  | ||||||
|         } else { |  | ||||||
|             this.textarea?.setFocus(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Hide the toolbar in phone mode. |  | ||||||
|      * |      * | ||||||
|      * @param event Event. |      * @param event Event. | ||||||
|      * @param force If true it will not check the target of the event. |  | ||||||
|      */ |      */ | ||||||
|     hideToolbar(event: FocusEvent | KeyboardEvent | MouseEvent, force = false): void { |     blurRTE(event: FocusEvent): void { | ||||||
|         if (!force && event.target && this.element.contains(event.target as HTMLElement)) { |         const doBlur = (event: FocusEvent) => { | ||||||
|             // Do not hide if clicked inside the editor area, except forced.
 |             if (this.element.contains(document.activeElement)) { | ||||||
|  |                 // Do not hide if clicked inside the editor area, except hideButton.
 | ||||||
| 
 | 
 | ||||||
|             return; |                 return; | ||||||
|         } |             } | ||||||
| 
 | 
 | ||||||
|         if (event.type === 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) { |             this.element.classList.remove('has-focus'); | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         this.element.classList.remove('has-focus'); |             this.stopBubble(event); | ||||||
| 
 | 
 | ||||||
|         this.stopBubble(event); |             if (this.isPhone) { | ||||||
|  |                 this.toolbarHidden = true; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|         if (this.isPhone) { |         this.blurTimeout = window.setTimeout(() => doBlur(event),300); | ||||||
|             this.toolbarHidden = true; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -754,19 +750,28 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Show the toolbar. |      * Focus editor when click the area and show toolbar. | ||||||
|  |      * | ||||||
|  |      * @param event Event. | ||||||
|      */ |      */ | ||||||
|     showToolbar(event: FocusEvent): void { |     focusRTE(event: Event): void { | ||||||
|         this.updateToolbarButtons(); |         clearTimeout(this.blurTimeout); | ||||||
|  | 
 | ||||||
|  |         if (this.rteEnabled) { | ||||||
|  |             this.editorElement?.focus(); | ||||||
|  |         } else { | ||||||
|  |             this.textarea?.setFocus(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         this.element.classList.add('ion-touched'); |         this.element.classList.add('ion-touched'); | ||||||
|         this.element.classList.remove('ion-untouched'); |         this.element.classList.remove('ion-untouched'); | ||||||
|         this.element.classList.add('has-focus'); |         this.element.classList.add('has-focus'); | ||||||
| 
 | 
 | ||||||
|         this.stopBubble(event); |         event && this.stopBubble(event); | ||||||
| 
 | 
 | ||||||
|         this.editorElement?.focus(); |  | ||||||
|         this.toolbarHidden = false; |         this.toolbarHidden = false; | ||||||
|  |         this.updateToolbarButtons(); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -839,7 +844,8 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|      * Update the number of toolbar buttons displayed. |      * Update the number of toolbar buttons displayed. | ||||||
|      */ |      */ | ||||||
|     async updateToolbarButtons(): Promise<void> { |     async updateToolbarButtons(): Promise<void> { | ||||||
|         if (!this.isCurrentView || !this.toolbar || !this.toolbarSlides || this.element.offsetParent === null) { |         if (!this.isCurrentView || !this.toolbar || !this.toolbarSlides || | ||||||
|  |             this.toolbarHidden || this.element.offsetParent === null) { | ||||||
|             // Don't calculate if component isn't in current view, the calculations are wrong.
 |             // Don't calculate if component isn't in current view, the calculations are wrong.
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| @ -848,10 +854,10 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
| 
 | 
 | ||||||
|         // Cancel previous one, if any.
 |         // Cancel previous one, if any.
 | ||||||
|         this.buttonsDomPromise?.cancel(); |         this.buttonsDomPromise?.cancel(); | ||||||
|         this.buttonsDomPromise = CoreDom.waitToBeInDOM(this.toolbar.nativeElement); |         this.buttonsDomPromise = CoreDom.waitToBeInDOM(this.toolbar?.nativeElement); | ||||||
|         await this.buttonsDomPromise; |         await this.buttonsDomPromise; | ||||||
| 
 | 
 | ||||||
|         const width = this.toolbar.nativeElement.getBoundingClientRect().width; |         const width = this.toolbar?.nativeElement.getBoundingClientRect().width; | ||||||
| 
 | 
 | ||||||
|         if (length > 0 && width > length * this.toolbarButtonWidth) { |         if (length > 0 && width > length * this.toolbarButtonWidth) { | ||||||
|             this.swiperOpts.slidesPerView = length; |             this.swiperOpts.slidesPerView = length; | ||||||
| @ -1069,7 +1075,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit, | |||||||
|         const text = await CoreUtils.scanQR(); |         const text = await CoreUtils.scanQR(); | ||||||
| 
 | 
 | ||||||
|         if (text) { |         if (text) { | ||||||
|             this.editorElement?.focus(); // Make sure the editor is focused.
 |             this.focusRTE(event); // Make sure the editor is focused.
 | ||||||
|             // eslint-disable-next-line deprecation/deprecation
 |             // eslint-disable-next-line deprecation/deprecation
 | ||||||
|             document.execCommand('insertText', false, text); |             document.execCommand('insertText', false, text); | ||||||
|         } |         } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user