From 650aafc96e02943f4daf92c374b015e3dc72c6b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= <crazyserver@gmail.com>
Date: Mon, 26 Apr 2021 14:14:15 +0200
Subject: [PATCH] MOBILE-3734 core: Style ion fab not direct children of ion
 content

---
 .../index/addon-mod-data-index.html           | 197 +++++++++---------
 .../index/addon-mod-wiki-index.html           |  88 ++++----
 src/core/directives/fab.ts                    |  14 +-
 src/theme/theme.base.scss                     |   8 +
 4 files changed, 155 insertions(+), 152 deletions(-)

diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html
index 26c2e3ec2..bf89fd47c 100644
--- a/src/addons/mod/data/components/index/addon-mod-data-index.html
+++ b/src/addons/mod/data/components/index/addon-mod-data-index.html
@@ -37,118 +37,115 @@
 </core-navbar-buttons>
 
 <!-- Content. -->
+<core-loading [hideUntil]="loaded" class="core-loading-center">
 
-<ion-content>
-    <core-loading [hideUntil]="loaded" class="core-loading-center">
+    <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
+        contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
+    </core-course-module-description>
 
-        <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
-            contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
-        </core-course-module-description>
+    <!-- Data done in offline but not synchronized -->
+    <ion-card class="core-warning-card" *ngIf="hasOffline || hasOfflineRatings">
+        <ion-item>
+            <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
+            <ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
+        </ion-item>
+    </ion-card>
 
-        <!-- Data done in offline but not synchronized -->
-        <ion-card class="core-warning-card" *ngIf="hasOffline || hasOfflineRatings">
-            <ion-item>
-                <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
-                <ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
-            </ion-item>
-        </ion-card>
+    <ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
+        <ion-label id="addon-data-groupslabel">
+            <ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
+            <ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
+        </ion-label>
+        <ion-select [(ngModel)]="selectedGroup" (ionChange)="setGroup(selectedGroup)" aria-labelledby="addon-data-groupslabel"
+            interface="action-sheet">
+            <ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
+                {{groupOpt.name}}
+            </ion-select-option>
+        </ion-select>
+    </ion-item>
 
-        <ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
-            <ion-label id="addon-data-groupslabel">
-                <ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
-                <ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
+    <ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableFrom">
+        <ion-item>
+            <ion-icon name="fas-info-circle" slot="start"></ion-icon>
+            <ion-label>{{ 'addon.mod_data.notopenyet' | translate:{$a: timeAvailableFromReadable} }}</ion-label>
+        </ion-item>
+    </ion-card>
+
+    <ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableTo">
+        <ion-item>
+            <ion-icon name="fas-info-circle" slot="start"></ion-icon>
+            <ion-label>{{ 'addon.mod_data.expired' | translate:{$a: timeAvailableToReadable} }}</ion-label>
+        </ion-item>
+    </ion-card>
+
+    <ion-card class="core-info-card" *ngIf="access && access.entrieslefttoview">>
+        <ion-item>
+            <ion-icon name="fas-info-circle" slot="start"></ion-icon>
+            <ion-label>
+                {{ 'addon.mod_data.entrieslefttoaddtoview' | translate:{$a: {entrieslefttoview: access.entrieslefttoview} } }}
+            </ion-label>
+        </ion-item>
+    </ion-card>
+
+    <ion-card class="core-info-card" *ngIf="access && access.entrieslefttoadd">>
+        <ion-item>
+            <ion-icon name="fas-info-circle" slot="start"></ion-icon>
+            <ion-label>
+                {{ 'addon.mod_data.entrieslefttoadd' | translate:{$a: {entriesleft: access.entrieslefttoadd} } }}
+            </ion-label>
+        </ion-item>
+    </ion-card>
+
+    <!-- Reset search. -->
+    <ng-container *ngIf="search.searching && !isEmpty">
+        <ion-item *ngIf="!foundRecordsTranslationData">
+            <ion-label>
+                <a (click)="searchReset()">{{ 'addon.mod_data.resetsettings' | translate}}</a>
             </ion-label>
-            <ion-select [(ngModel)]="selectedGroup" (ionChange)="setGroup(selectedGroup)" aria-labelledby="addon-data-groupslabel"
-                interface="action-sheet">
-                <ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
-                    {{groupOpt.name}}
-                </ion-select-option>
-            </ion-select>
         </ion-item>
 
-        <ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableFrom">
-            <ion-item>
-                <ion-icon name="fas-info-circle" slot="start"></ion-icon>
-                <ion-label>{{ 'addon.mod_data.notopenyet' | translate:{$a: timeAvailableFromReadable} }}</ion-label>
-            </ion-item>
+        <ion-card class="core-success-card" *ngIf="foundRecordsTranslationData" (click)="searchReset()">
+            <ion-item><ion-label>
+            <p [innerHTML]="'addon.mod_data.foundrecords' | translate:{$a: foundRecordsTranslationData}"></p>
+            </ion-label></ion-item>
         </ion-card>
+    </ng-container>
 
-        <ion-card class="core-info-card" *ngIf="!access?.timeavailable && timeAvailableTo">
-            <ion-item>
-                <ion-icon name="fas-info-circle" slot="start"></ion-icon>
-                <ion-label>{{ 'addon.mod_data.expired' | translate:{$a: timeAvailableToReadable} }}</ion-label>
-            </ion-item>
-        </ion-card>
+    <div class="addon-data-contents addon-data-entries-{{database.id}} ion-padding-horizontal" *ngIf="!isEmpty && database">
+        <core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}"></core-style>
 
-        <ion-card class="core-info-card" *ngIf="access && access.entrieslefttoview">>
-            <ion-item>
-                <ion-icon name="fas-info-circle" slot="start"></ion-icon>
-                <ion-label>
-                    {{ 'addon.mod_data.entrieslefttoaddtoview' | translate:{$a: {entrieslefttoview: access.entrieslefttoview} } }}
-                </ion-label>
-            </ion-item>
-        </ion-card>
+        <core-compile-html [text]="entriesRendered" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
+    </div>
 
-        <ion-card class="core-info-card" *ngIf="access && access.entrieslefttoadd">>
-            <ion-item>
-                <ion-icon name="fas-info-circle" slot="start"></ion-icon>
-                <ion-label>
-                    {{ 'addon.mod_data.entrieslefttoadd' | translate:{$a: {entriesleft: access.entrieslefttoadd} } }}
-                </ion-label>
-            </ion-item>
-        </ion-card>
+    <ion-grid *ngIf="search.page > 0 || hasNextPage">
+        <ion-row class="ion-align-items-center">
+            <ion-col *ngIf="search.page > 0">
+                <ion-button expand="block" fill="outline" (click)="searchEntries(search.page - 1)">
+                    <ion-icon name="fas-chevron-left" slot="start"></ion-icon>
+                    {{ 'core.previous' | translate }}
+                </ion-button>
+            </ion-col>
+            <ion-col *ngIf="hasNextPage">
+                <ion-button expand="block" (click)="searchEntries(search.page + 1)">
+                    {{ 'core.next' | translate }}
+                    <ion-icon name="fas-chevron-right" slot="end"></ion-icon>
+                </ion-button>
+            </ion-col>
+        </ion-row>
+    </ion-grid>
 
-        <!-- Reset search. -->
-        <ng-container *ngIf="search.searching && !isEmpty">
-            <ion-item *ngIf="!foundRecordsTranslationData">
-                <ion-label>
-                    <a (click)="searchReset()">{{ 'addon.mod_data.resetsettings' | translate}}</a>
-                </ion-label>
-            </ion-item>
+    <core-empty-box *ngIf="isEmpty && !search.searching" icon="fas-database" [message]="'addon.mod_data.norecords' | translate">
+    </core-empty-box>
 
-            <ion-card class="core-success-card" *ngIf="foundRecordsTranslationData" (click)="searchReset()">
-                <ion-item><ion-label>
-                <p [innerHTML]="'addon.mod_data.foundrecords' | translate:{$a: foundRecordsTranslationData}"></p>
-                </ion-label></ion-item>
-            </ion-card>
-        </ng-container>
+    <core-empty-box *ngIf="isEmpty && search.searching" icon="fas-database" [message]="'addon.mod_data.nomatch' | translate"
+        class="core-empty-box-clickable">
+        <a (click)="searchReset()">{{ 'addon.mod_data.resetsettings' | translate}}</a>
+    </core-empty-box>
 
-        <div class="addon-data-contents addon-data-entries-{{database.id}} ion-padding-horizontal" *ngIf="!isEmpty && database">
-            <core-style [css]="database.csstemplate" prefix=".addon-data-entries-{{database.id}}"></core-style>
+</core-loading>
 
-            <core-compile-html [text]="entriesRendered" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
-        </div>
-
-        <ion-grid *ngIf="search.page > 0 || hasNextPage">
-            <ion-row class="ion-align-items-center">
-                <ion-col *ngIf="search.page > 0">
-                    <ion-button expand="block" fill="outline" (click)="searchEntries(search.page - 1)">
-                        <ion-icon name="fas-chevron-left" slot="start"></ion-icon>
-                        {{ 'core.previous' | translate }}
-                    </ion-button>
-                </ion-col>
-                <ion-col *ngIf="hasNextPage">
-                    <ion-button expand="block" (click)="searchEntries(search.page + 1)">
-                        {{ 'core.next' | translate }}
-                        <ion-icon name="fas-chevron-right" slot="end"></ion-icon>
-                    </ion-button>
-                </ion-col>
-            </ion-row>
-        </ion-grid>
-
-        <core-empty-box *ngIf="isEmpty && !search.searching" icon="fas-database" [message]="'addon.mod_data.norecords' | translate">
-        </core-empty-box>
-
-        <core-empty-box *ngIf="isEmpty && search.searching" icon="fas-database" [message]="'addon.mod_data.nomatch' | translate"
-            class="core-empty-box-clickable">
-            <a (click)="searchReset()">{{ 'addon.mod_data.resetsettings' | translate}}</a>
-        </core-empty-box>
-
-    </core-loading>
-
-    <ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAdd">
-        <ion-fab-button (click)="gotoAddEntries()" [attr.aria-label]="'addon.mod_data.addentries' | translate">
-            <ion-icon name="fas-plus"></ion-icon>
-        </ion-fab-button>
-    </ion-fab>
-</ion-content>
+<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAdd">
+    <ion-fab-button (click)="gotoAddEntries()" [attr.aria-label]="'addon.mod_data.addentries' | translate">
+        <ion-icon name="fas-plus"></ion-icon>
+    </ion-fab-button>
+</ion-fab>
diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html
index 4885fe49c..3466dfb92 100644
--- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html
+++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html
@@ -46,52 +46,50 @@
 </core-navbar-buttons>
 
 <!-- Content. -->
-<ion-content>
-    <core-loading [hideUntil]="loaded" class="core-loading-center">
-        <div *ngIf="description || pageIsOffline || hasOffline || pageWarning">
-            <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
+<core-loading [hideUntil]="loaded" class="core-loading-center">
+    <div *ngIf="description || pageIsOffline || hasOffline || pageWarning">
+        <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
+            contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
+        </core-course-module-description>
+
+        <!-- Wiki has something offline. -->
+        <ion-card class="core-warning-card" *ngIf="pageIsOffline || hasOffline">
+            <ion-item>
+                <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
+                <ion-label>
+                    <span *ngIf="pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: pageStr} }}</span>
+                    <span *ngIf="!pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: moduleName} }}</span>
+                </ion-label>
+            </ion-item>
+        </ion-card>
+
+        <!-- Page warning. -->
+        <ion-card class="core-warning-card" *ngIf="pageWarning">
+            <ion-item>
+                <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
+                <ion-label>{{ pageWarning }}</ion-label>
+            </ion-item>
+        </ion-card>
+    </div>
+    <div class="ion-padding addon-mod_wiki-page-content">
+        <article [ngClass]="{'addon-mod_wiki-noedit': !canEdit}">
+            <core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"
                 contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
-            </core-course-module-description>
+            </core-format-text>
+            <core-empty-box *ngIf="!pageContent" icon="fas-file-alt" [message]="'addon.mod_wiki.nocontent' | translate"
+                [inline]="true">
+            </core-empty-box>
+        </article>
 
-            <!-- Wiki has something offline. -->
-            <ion-card class="core-warning-card" *ngIf="pageIsOffline || hasOffline">
-                <ion-item>
-                    <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
-                    <ion-label>
-                        <span *ngIf="pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: pageStr} }}</span>
-                        <span *ngIf="!pageIsOffline">{{ 'core.hasdatatosync' | translate:{$a: moduleName} }}</span>
-                    </ion-label>
-                </ion-item>
-            </ion-card>
-
-            <!-- Page warning. -->
-            <ion-card class="core-warning-card" *ngIf="pageWarning">
-                <ion-item>
-                    <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
-                    <ion-label>{{ pageWarning }}</ion-label>
-                </ion-item>
-            </ion-card>
+        <div class="ion-margin-top" *ngIf="tagsEnabled && tags.length > 0">
+            <b>{{ 'core.tag.tags' | translate }}:</b>
+            <core-tag-list [tags]="tags"></core-tag-list>
         </div>
-        <div class="ion-padding addon-mod_wiki-page-content">
-            <article [ngClass]="{'addon-mod_wiki-noedit': !canEdit}">
-                <core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"
-                    contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
-                </core-format-text>
-                <core-empty-box *ngIf="!pageContent" icon="fas-file-alt" [message]="'addon.mod_wiki.nocontent' | translate"
-                    [inline]="true">
-                </core-empty-box>
-            </article>
+    </div>
+</core-loading>
 
-            <div class="ion-margin-top" *ngIf="tagsEnabled && tags.length > 0">
-                <b>{{ 'core.tag.tags' | translate }}:</b>
-                <core-tag-list [tags]="tags"></core-tag-list>
-            </div>
-        </div>
-    </core-loading>
-
-    <ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canEdit">
-        <ion-fab-button (click)="goToNewPage()" [attr.aria-label]="'addon.mod_wiki.createpage' | translate">
-            <ion-icon name="fas-plus"></ion-icon>
-        </ion-fab-button>
-    </ion-fab>
-</ion-content>
+<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canEdit">
+    <ion-fab-button (click)="goToNewPage()" [attr.aria-label]="'addon.mod_wiki.createpage' | translate">
+        <ion-icon name="fas-plus"></ion-icon>
+    </ion-fab-button>
+</ion-fab>
diff --git a/src/core/directives/fab.ts b/src/core/directives/fab.ts
index 433d16bf5..838c0b9c2 100644
--- a/src/core/directives/fab.ts
+++ b/src/core/directives/fab.ts
@@ -29,7 +29,7 @@ export class CoreFabDirective implements OnDestroy {
 
     protected static readonly PADDINGBOTTOM = 56;
 
-    protected element?: HTMLElement;
+    protected scrollElement?: HTMLElement;
     protected done = false;
 
     constructor(protected content: IonContent) {
@@ -41,10 +41,10 @@ export class CoreFabDirective implements OnDestroy {
      */
     async asyncInit(): Promise<void> {
         if (this.content) {
-            this.element = await this.content.getScrollElement();
+            this.scrollElement = await this.content.getScrollElement();
             if (!this.done) {
-                const bottom = parseInt(this.element.style.paddingBottom, 10) ||  0;
-                this.element.style.paddingBottom = (bottom + CoreFabDirective.PADDINGBOTTOM) + 'px';
+                const bottom = parseInt(this.scrollElement.style.paddingBottom, 10) ||  0;
+                this.scrollElement.style.paddingBottom = (bottom + CoreFabDirective.PADDINGBOTTOM) + 'px';
                 this.done = true;
             }
         }
@@ -54,9 +54,9 @@ export class CoreFabDirective implements OnDestroy {
      * Destroy component.
      */
     ngOnDestroy(): void {
-        if (this.done && this.element) {
-            const bottom = parseInt(this.element.style.paddingBottom, 10) ||  0;
-            this.element.style.paddingBottom = (bottom + CoreFabDirective.PADDINGBOTTOM) + 'px';
+        if (this.done && this.scrollElement) {
+            const bottom = parseInt(this.scrollElement.style.paddingBottom, 10) ||  0;
+            this.scrollElement.style.paddingBottom = (bottom - CoreFabDirective.PADDINGBOTTOM) + 'px';
         }
     }
 
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 17809b66b..e83caa979 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -496,3 +496,11 @@ ion-button.core-button-select {
 textarea:not([core-auto-rows]) {
     height: 200px;
 }
+
+ion-fab[core-fab] {
+    position: fixed;
+}
+
+ion-content > ion-fab[core-fab] {
+    position: absolute;
+}