mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-20 16:42:56 +00:00
feat: apply new layout overhaul
This commit is contained in:
committed by
Rainer Killinger
parent
f16e5394cc
commit
7bbdba5c0b
@@ -8,6 +8,7 @@
|
||||
trigger="show-more"
|
||||
[presentingElement]="routerOutlet.nativeEl"
|
||||
swipeToClose="true"
|
||||
class="modal-large"
|
||||
>
|
||||
<ng-template>
|
||||
<app-map-single-modal [item]="item" style="height: 100%">
|
||||
@@ -15,36 +16,35 @@
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
<stapps-skeleton-list-item *ngIf="!item"></stapps-skeleton-list-item>
|
||||
<ion-button
|
||||
size="small"
|
||||
fill="clear"
|
||||
class="close"
|
||||
(click)="onCloseClick()"
|
||||
>
|
||||
<ion-icon name="circle-x"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col size="7">
|
||||
<ion-note>
|
||||
<span *ngIf="item.address as address">
|
||||
<span *ngIf="$any(item).inPlace"
|
||||
>{{ $any(item).inPlace.name }},</span
|
||||
>
|
||||
{{ address.streetAddress }}, {{ address.addressLocality }}
|
||||
</span>
|
||||
</ion-note>
|
||||
</ion-col>
|
||||
<ion-col size="5">
|
||||
<ion-button size="small" id="show-more-button"
|
||||
>More <ion-icon name="information-circle"></ion-icon
|
||||
></ion-button>
|
||||
<ion-modal
|
||||
trigger="show-more-button"
|
||||
swipeToClose="true"
|
||||
[presentingElement]="routerOutlet.nativeEl"
|
||||
>
|
||||
<ng-template>
|
||||
<app-map-single-modal [item]="item" style="height: 100%">
|
||||
</app-map-single-modal>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<ion-note>
|
||||
<span *ngIf="item.address as address">
|
||||
<span *ngIf="$any(item).inPlace">{{ $any(item).inPlace.name }},</span>
|
||||
{{ address.streetAddress }}, {{ address.addressLocality }}
|
||||
</span>
|
||||
</ion-note>
|
||||
<ion-button size="small" id="show-more-button" fill="clear">{{
|
||||
'map.page.buttons.MORE' | translate
|
||||
}}</ion-button>
|
||||
<ion-modal
|
||||
trigger="show-more-button"
|
||||
swipeToClose="true"
|
||||
[presentingElement]="routerOutlet.nativeEl"
|
||||
class="modal-large"
|
||||
>
|
||||
<ng-template>
|
||||
<app-map-single-modal [item]="item" style="height: 100%">
|
||||
</app-map-single-modal>
|
||||
</ng-template>
|
||||
</ion-modal>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
|
||||
@@ -1,8 +1,57 @@
|
||||
ion-col:nth-child(2) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@import '../../../../theme/util/mixins';
|
||||
|
||||
ion-button {
|
||||
margin-top: auto;
|
||||
:host {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
ion-card {
|
||||
padding: 0;
|
||||
overflow: visible;
|
||||
|
||||
ion-card-header{
|
||||
padding: 0;
|
||||
border-bottom: var(--border-width-default) solid var(--border-color-default);
|
||||
|
||||
stapps-data-list-item {
|
||||
--ion-margin: 0;
|
||||
|
||||
&::ng-deep ion-item {
|
||||
--padding-start: 0;
|
||||
--padding-end: 0;
|
||||
|
||||
ion-label {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
right: -15px;
|
||||
z-index: 1;
|
||||
--padding-top: 0;
|
||||
--padding-bottom: 0;
|
||||
--padding-start: 0;
|
||||
--padding-end: 0;
|
||||
|
||||
ion-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-card-content {
|
||||
padding: var(--spacing-md);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
#show-more-button {
|
||||
text-transform: uppercase;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {Component, EventEmitter, Input, Output} from '@angular/core';
|
||||
import {SCPlace} from '@openstapps/core';
|
||||
import {IonRouterOutlet} from '@ionic/angular';
|
||||
|
||||
@@ -27,5 +27,15 @@ export class MapItemComponent {
|
||||
*/
|
||||
@Input() item: SCPlace;
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
|
||||
@Output() onClose = new EventEmitter<void>();
|
||||
|
||||
constructor(readonly routerOutlet: IonRouterOutlet) {}
|
||||
|
||||
/**
|
||||
* Action when edit is clicked
|
||||
*/
|
||||
onCloseClick() {
|
||||
this.onClose.emit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import {MapListModalComponent} from './page/modals/map-list-modal.component';
|
||||
import {MapSingleModalComponent} from './page/modals/map-single-modal.component';
|
||||
import {MapItemComponent} from './item/map-item.component';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {UtilModule} from '../../util/util.module';
|
||||
|
||||
/**
|
||||
* Initializes the default area to show in advance (before components are initialized)
|
||||
@@ -78,6 +79,7 @@ const mapRoutes: Routes = [
|
||||
DataModule,
|
||||
FormsModule,
|
||||
ThingTranslateModule,
|
||||
UtilModule,
|
||||
],
|
||||
providers: [
|
||||
Geolocation,
|
||||
|
||||
@@ -76,7 +76,7 @@ export class MapProvider {
|
||||
? `<ion-icon name="navigate-straight"
|
||||
style="transform-origin: center; transform: rotate(${position.heading}deg);">
|
||||
</ion-icon>`
|
||||
: '<ion-icon name="locate"></ion-icon>',
|
||||
: '<ion-icon name="current-location"></ion-icon>',
|
||||
iconSize: [iconSize, iconSize],
|
||||
}),
|
||||
zIndexOffset: 1000,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Location} from '@angular/common';
|
||||
import {trigger, style, animate, transition} from '@angular/animations';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
@@ -59,6 +60,24 @@ import {Capacitor} from '@capacitor/core';
|
||||
styleUrls: ['./map-page.scss'],
|
||||
templateUrl: './map-page.html',
|
||||
providers: [ContextMenuService],
|
||||
animations: [
|
||||
trigger('fadeInOut', [
|
||||
transition(':enter', [
|
||||
style({transform: 'translateY(200%)', opacity: 0}),
|
||||
animate(
|
||||
'500ms ease-in-out',
|
||||
style({transform: 'translateY(0%)', opacity: 1}),
|
||||
),
|
||||
]),
|
||||
transition(':leave', [
|
||||
style({transform: 'translateY(0%)', opacity: 1}),
|
||||
animate(
|
||||
'500ms ease-in-out',
|
||||
style({transform: 'translateY(200%)', opacity: 0}),
|
||||
),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class MapPageComponent {
|
||||
/**
|
||||
@@ -242,13 +261,17 @@ export class MapPageComponent {
|
||||
/**
|
||||
* Fetches items with set query configuration
|
||||
*
|
||||
* @param fetchAll Should fetch all items
|
||||
* @param animate Should the fly animation be used
|
||||
*/
|
||||
async fetchAndUpdateItems(animate?: boolean): Promise<void> {
|
||||
async fetchAndUpdateItems(
|
||||
fetchAll = false,
|
||||
animate?: boolean,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const result = await this.mapProvider.searchPlaces(
|
||||
this.filterQuery,
|
||||
this.queryText,
|
||||
fetchAll ? '' : this.queryText,
|
||||
);
|
||||
if (result.data.length === 0) {
|
||||
const alert = await this.alertController.create({
|
||||
@@ -353,7 +376,7 @@ export class MapPageComponent {
|
||||
this.subscriptions.push(
|
||||
this.contextMenuService.filterQueryChanged$.subscribe(query => {
|
||||
this.filterQuery = query;
|
||||
this.fetchAndUpdateItems(true);
|
||||
this.fetchAndUpdateItems(false, true);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -414,7 +437,7 @@ export class MapPageComponent {
|
||||
*/
|
||||
async resetView() {
|
||||
this.location.go('/map');
|
||||
await this.fetchAndUpdateItems();
|
||||
await this.fetchAndUpdateItems(this.items.length > 0);
|
||||
|
||||
this.ref.detectChanges();
|
||||
}
|
||||
@@ -437,7 +460,7 @@ export class MapPageComponent {
|
||||
*/
|
||||
searchStringChanged(queryText?: string) {
|
||||
this.queryText = queryText || '';
|
||||
void this.fetchAndUpdateItems(true);
|
||||
void this.fetchAndUpdateItems(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,7 +474,7 @@ export class MapPageComponent {
|
||||
this.distance = this.positionService.getDistance(this.items[0].geo.point);
|
||||
this.addToMap(this.items, true);
|
||||
this.ref.detectChanges();
|
||||
const url = this.router.createUrlTree([[], uid]).toString();
|
||||
const url = this.router.createUrlTree(['/map', uid]).toString();
|
||||
this.location.go(url);
|
||||
// center the selected place
|
||||
this.focus(geoJSON(this.items[0].geo.point).getBounds().getCenter());
|
||||
|
||||
@@ -16,31 +16,39 @@
|
||||
<stapps-context contentId="map"></stapps-context>
|
||||
|
||||
<ion-header class="ion-no-border" translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-toolbar color="primary" mode="ios">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button
|
||||
*ngIf="items.length !== 1"
|
||||
[defaultHref]="'..'"
|
||||
[text]="'back' | translate | titlecase"
|
||||
></ion-back-button>
|
||||
<ion-back-button
|
||||
*ngIf="items.length === 1"
|
||||
default-href="/"
|
||||
[defaultHref]="'..'"
|
||||
[text]="'back' | translate | titlecase"
|
||||
(click)="resetView()"
|
||||
></ion-back-button>
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>{{ 'map.page.TITLE' | translate }}</ion-title>
|
||||
</ion-toolbar>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-searchbar
|
||||
(keyup)="searchKeyUp($event)"
|
||||
(keyup.enter)="hideKeyboard()"
|
||||
[(ngModel)]="queryText"
|
||||
(ionClear)="searchStringChanged()"
|
||||
mode="md"
|
||||
placeholder="{{ 'map.page.search_bar.placeholder' | translate }}"
|
||||
showClearButton="always"
|
||||
type="search"
|
||||
enterkeyhint="search"
|
||||
class="filterable"
|
||||
>
|
||||
</ion-searchbar>
|
||||
<ion-buttons slot="end">
|
||||
<ion-menu-button menu="context" auto-hide="false">
|
||||
<ion-icon name="filter"></ion-icon>
|
||||
<ion-icon name="adjustments"></ion-icon>
|
||||
</ion-menu-button>
|
||||
</ion-buttons>
|
||||
</ion-searchbar>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -61,6 +69,7 @@
|
||||
<div class="map-buttons above">
|
||||
<ion-button
|
||||
*ngIf="items.length > 1"
|
||||
[@fadeInOut]
|
||||
color="light"
|
||||
shape="round"
|
||||
size="small"
|
||||
@@ -79,21 +88,24 @@
|
||||
>
|
||||
<ion-icon
|
||||
*ngIf="position !== null; else questionIcon"
|
||||
name="locate"
|
||||
name="current-location"
|
||||
></ion-icon>
|
||||
<ng-template #questionIcon>
|
||||
<ion-icon name="help-circle-outline"></ion-icon>
|
||||
<ion-icon name="help"></ion-icon>
|
||||
</ng-template>
|
||||
</ion-button>
|
||||
</div>
|
||||
<stapps-map-item
|
||||
*ngIf="items.length === 1"
|
||||
[@fadeInOut]
|
||||
[item]="items[0]"
|
||||
(onClose)="resetView()"
|
||||
></stapps-map-item>
|
||||
</div>
|
||||
<div class="map-buttons floating-buttons">
|
||||
<ion-button
|
||||
*ngIf="items.length > 1"
|
||||
[@fadeInOut]
|
||||
color="light"
|
||||
shape="round"
|
||||
size="small"
|
||||
@@ -112,10 +124,10 @@
|
||||
>
|
||||
<ion-icon
|
||||
*ngIf="position !== null; else questionIcon"
|
||||
name="locate"
|
||||
name="current-location"
|
||||
></ion-icon>
|
||||
<ng-template #questionIcon>
|
||||
<ion-icon name="help-circle-outline"></ion-icon>
|
||||
<ion-icon name="help"></ion-icon>
|
||||
</ng-template>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
ion-header {
|
||||
ion-toolbar {
|
||||
--background: transparent;
|
||||
--ion-color-base: transparent;
|
||||
|
||||
ion-buttons {
|
||||
ion-back-button::part(native), ion-menu-button::part(native) {
|
||||
box-shadow: var(--map-box-shadow);
|
||||
//padding: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
ion-searchbar {
|
||||
--background: white;
|
||||
// important for iOS
|
||||
--box-shadow: var(--map-box-shadow);
|
||||
}
|
||||
}
|
||||
|
||||
ion-content {
|
||||
// fixes the unexpected issue that the content is not fullscreen (behind the header)
|
||||
position: absolute;
|
||||
@@ -24,12 +5,13 @@ ion-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
& > div {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
ion-back-button, ion-menu-button {
|
||||
--background: white;
|
||||
--background-hover: whitesmoke;
|
||||
--background-focused: whitesmoke;
|
||||
ion-toolbar:first-of-type {
|
||||
padding: 0 var(--spacing-md) var(--spacing-xs);
|
||||
}
|
||||
|
||||
::ng-deep {
|
||||
@@ -50,14 +32,20 @@ ion-back-button, ion-menu-button {
|
||||
}
|
||||
|
||||
div.floating-content {
|
||||
display: grid;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
padding: 0 var(--spacing-md) 8vh;
|
||||
justify-content: center;
|
||||
|
||||
ion-card {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.map-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -67,7 +55,7 @@ ion-back-button, ion-menu-button {
|
||||
width: 550px;
|
||||
position: center;
|
||||
justify-self: center;
|
||||
margin: 2px;
|
||||
margin: var(--spacing-sm) auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<div class="container">
|
||||
<ion-header translucent>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-toolbar color="primary" mode="ios">
|
||||
<ion-title>{{ 'map.modals.list.TITLE' | translate }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="modalController.dismiss()">{{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ion-header translucent>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-toolbar color="primary" mode="ios">
|
||||
<ion-title>{{ 'map.modals.single.TITLE' | translate }}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="modalController.dismiss()">{{
|
||||
@@ -9,5 +9,8 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<stapps-data-detail-content [item]="$any(item)"></stapps-data-detail-content>
|
||||
<stapps-data-detail-content
|
||||
[item]="$any(item)"
|
||||
[openAsModal]="true"
|
||||
></stapps-data-detail-content>
|
||||
</ion-content>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {SCPlace} from '@openstapps/core';
|
||||
import {geoJSON, Map, MapOptions, tileLayer} from 'leaflet';
|
||||
import {MapProvider} from '../map.provider';
|
||||
@@ -46,6 +47,13 @@ export class MapWidgetComponent implements OnInit {
|
||||
*/
|
||||
@Input() place: SCPlace;
|
||||
|
||||
/**
|
||||
* Indicates if the expand button should be visible
|
||||
*/
|
||||
showExpandButton = true;
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Prepare the map
|
||||
*/
|
||||
@@ -69,6 +77,9 @@ export class MapWidgetComponent implements OnInit {
|
||||
zoom: 16,
|
||||
zoomControl: false,
|
||||
};
|
||||
if (this.router) {
|
||||
this.showExpandButton = !this.router.url.startsWith('/map');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
#mapContainer
|
||||
[leafletOptions]="options"
|
||||
></div>
|
||||
<div class="map-buttons">
|
||||
<div class="map-buttons" *ngIf="showExpandButton">
|
||||
<ion-button
|
||||
color="primary"
|
||||
shape="round"
|
||||
size="small"
|
||||
[routerLink]="['/map', place.uid]"
|
||||
>
|
||||
<ion-icon name="expand"></ion-icon>
|
||||
<ion-icon name="arrows-maximize"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user