Skip to content

Commit 5c6428b

Browse files
Merge pull request #2271 from IDEMSInternational/fix/ios-fullscreen-popups
Fix: iOS fullscreen popups extend outside safe area
2 parents 131865f + 9bdf853 commit 5c6428b

File tree

9 files changed

+152
-141
lines changed

9 files changed

+152
-141
lines changed

capacitor.config.template.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ const config: CapacitorConfig = {
2323
/**
2424
* NOTE - to support live-reload on external device (e.g. emulator)
2525
* 1) Uncomment url and replace with local ip to serve live-reload on local device
26-
* 2) Sync to capacitor `npx cap sync`
27-
* 3) Serve via `yarn ng serve --configuration=external`
28-
* 4) Run app from android studio `npx cap open android` and run
26+
* 2) Either edit capacitor.config.ts directly, or if editing capacitor.config.template.ts,
27+
* run `yarn workflow <platform> configure`, replacing `<platform>` with either ios or android
28+
* 3) Sync to capacitor `npx cap sync`
29+
* 4) Serve via `yarn ng serve --configuration=external`
30+
* 5) Run app from android studio or xcode: `npx cap open <platform>` and run
2931
* Local browser (localhost:4000), device app and device browser ([ip]:4200) should all be able to access served app
3032
**/
3133
// url: "http://192.168.50.67:4200",

src/app/shared/components/template/components/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { SquareIconButtonComponent } from "./square-icon-button/square-icon-butt
1919
import { TemplateBaseComponent } from "./base";
2020
import { TemplateDebuggerComponent } from "./debugger";
2121
import { TemplateHTMLComponent } from "./html/html.component";
22-
import { TemplatePopupComponent } from "./layout/popup";
22+
import { TemplatePopupComponent } from "./layout/popup/popup.component";
2323

2424
import { TmplAccordionComponent } from "./accordion/accordion.component";
2525
import { TmplAdvancedDashedBoxComponent } from "./layout/advanced-dashed-box/advanced-dashed-box.component";

src/app/shared/components/template/components/layout/popup.ts

-135
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div
2+
class="popup-backdrop"
3+
(click)="dismissOnBackdrop($event)"
4+
[attr.data-fullscreen]="props.fullscreen ? true : null"
5+
>
6+
<div class="popup-container">
7+
<div (click)="dismiss()" class="close-button" fill="clear" *ngIf="props.showCloseButton">
8+
<ion-icon slot="icon-only" name="close"></ion-icon>
9+
</div>
10+
<div class="popup-content" [attr.data-fullscreen]="props.fullscreen ? true : null">
11+
<plh-template-container
12+
class="template-container"
13+
[name]="props.name"
14+
[templatename]="props.templatename"
15+
[parent]="props.parent"
16+
[row]="props.row"
17+
(emittedValue)="handleEmittedValue($event)"
18+
></plh-template-container>
19+
</div>
20+
</div>
21+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
.popup-backdrop {
2+
height: 100vh;
3+
width: 100%;
4+
background: rgba(0, 0, 0, 0.6);
5+
display: flex;
6+
flex-direction: column;
7+
justify-content: center;
8+
.popup-container {
9+
position: relative;
10+
width: var(--content-max-width);
11+
padding: 0 2rem;
12+
margin: auto;
13+
margin-top: var(--ion-safe-area-top, 0);
14+
margin-bottom: var(--ion-safe-area-bottom, 0);
15+
}
16+
&[data-fullscreen] {
17+
background: white;
18+
.popup-container {
19+
height: var(--safe-area-height);
20+
}
21+
}
22+
.popup-content {
23+
margin: 30px auto;
24+
max-height: calc(var(--safe-area-height) - 60px);
25+
background: white;
26+
border-radius: 20px;
27+
padding: 20px;
28+
overflow: auto;
29+
}
30+
.popup-content[data-fullscreen] {
31+
width: 100%;
32+
height: 100%;
33+
max-height: 100vh;
34+
border-radius: 0;
35+
margin: 0;
36+
}
37+
.popup-content::-webkit-scrollbar {
38+
display: none;
39+
}
40+
.close-button {
41+
position: absolute;
42+
top: 16px;
43+
right: 22px;
44+
background: white;
45+
width: 40px;
46+
height: 40px;
47+
border-radius: 100%;
48+
display: flex;
49+
flex-direction: column;
50+
align-items: center;
51+
justify-content: center;
52+
border: 1px solid var(--ion-color-primary);
53+
font-size: 24px;
54+
z-index: 1;
55+
box-shadow: var(--ion-default-box-shadow);
56+
color: var(--ion-color-primary);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Component, Input } from "@angular/core";
2+
import { ModalController } from "@ionic/angular";
3+
import { FlowTypes, ITemplateContainerProps } from "../../../models";
4+
import { TemplateContainerComponent } from "../../../template-container.component";
5+
6+
@Component({
7+
templateUrl: "./popup.component.html",
8+
styleUrl: "./popup.component.scss",
9+
})
10+
/**
11+
* When opening a template as a popup, provide a minimal interface and load
12+
* the template directly as a regular template-container element
13+
*/
14+
export class TemplatePopupComponent {
15+
@Input() props: ITemplatePopupComponentProps;
16+
17+
constructor(private modalCtrl: ModalController) {}
18+
19+
/**
20+
* When templates emit completed/uncompleted value from standalone popup close the popup
21+
* NOTE - we do not want to respond to non-standalone templates as this is done through template nav-actions
22+
* */
23+
handleEmittedValue(value: { emit_value: string; emit_data: any }) {
24+
const { emit_value } = value;
25+
if (this.props.dismissOnEmit) {
26+
if (["completed", "uncompleted"].includes(emit_value)) {
27+
this.dismiss(value);
28+
}
29+
}
30+
}
31+
32+
dismissOnBackdrop(e: MouseEvent) {
33+
const el = e.target as HTMLElement;
34+
if (el.classList.contains("popup-backdrop")) {
35+
this.dismiss();
36+
}
37+
}
38+
39+
dismiss(value?: { emit_value: string; emit_data: any }) {
40+
this.modalCtrl.dismiss(value);
41+
}
42+
}
43+
44+
export interface ITemplatePopupComponentProps extends ITemplateContainerProps {
45+
name: string;
46+
templatename: string;
47+
parent?: TemplateContainerComponent;
48+
row?: FlowTypes.TemplateRow;
49+
showCloseButton?: boolean;
50+
/** Dismiss popup when completed or uncompleted is emitted from child template */
51+
dismissOnEmit?: boolean;
52+
/** Wait for template to be self-dismissed before returning (default: True) */
53+
waitForDismiss?: boolean;
54+
/** Display fullscreen overlayed on top of all other app content */
55+
fullscreen?: boolean;
56+
}

src/app/shared/components/template/services/template-nav.service.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { first } from "rxjs/operators";
66
import { FlowTypes } from "src/app/shared/model";
77
import { SyncServiceBase } from "src/app/shared/services/syncService.base";
88
import { arrayToHashmapArray, parseBoolean } from "src/app/shared/utils";
9-
import { ITemplatePopupComponentProps, TemplatePopupComponent } from "../components/layout/popup";
9+
import {
10+
ITemplatePopupComponentProps,
11+
TemplatePopupComponent,
12+
} from "../components/layout/popup/popup.component";
1013
import { ITemplateContainerProps } from "../models";
1114
import { TemplateContainerComponent } from "../template-container.component";
1215

src/app/shared/components/template/services/template.service.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { AppDataService } from "src/app/shared/services/data/app-data.service";
44
import { DbService } from "src/app/shared/services/db/db.service";
55
import { FlowTypes } from "src/app/shared/model";
66
import { ModalController } from "@ionic/angular";
7-
import { ITemplatePopupComponentProps, TemplatePopupComponent } from "../components/layout/popup";
7+
import {
8+
ITemplatePopupComponentProps,
9+
TemplatePopupComponent,
10+
} from "../components/layout/popup/popup.component";
811
import { TemplateTranslateService } from "./template-translate.service";
912
import { IFlowEvent } from "data-models";
1013
import { TemplateVariablesService } from "./template-variables.service";

src/theme/variables.scss

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
/** Ionic CSS Variables **/
1717
:root {
18+
// The total height of the central safe area of the device viewport
19+
--safe-area-height: calc(100vh - var(--ion-safe-area-top, 0) - var(--ion-safe-area-bottom, 0));
20+
1821
// Margins //
1922
--tiny-margin: 5px;
2023
--small-margin: 10px;

0 commit comments

Comments
 (0)