feat: apply new layout overhaul

This commit is contained in:
Andy Bastian
2022-08-08 11:01:00 +00:00
committed by Rainer Killinger
parent f16e5394cc
commit 7bbdba5c0b
228 changed files with 28387 additions and 1092 deletions

View File

@@ -4,9 +4,11 @@
contentId="{{ contentId }}"
side="end"
>
<ion-toolbar color="primary">
<ion-toolbar color="primary" mode="ios">
<ion-label class="ion-padding-horizontal">
<h1>{{ 'menu.context.title' | translate | titlecase }}</h1>
<h1 class="ion-padding-horizontal">
{{ 'menu.context.title' | translate | titlecase }}
</h1>
</ion-label>
</ion-toolbar>
<ion-content>
@@ -14,7 +16,7 @@
<ion-list>
<ion-radio-group class="context-sort" *ngIf="sortOption" [value]="0">
<ion-list-header>
<ion-icon name="swap-vertical-outline"></ion-icon>
<ion-icon name="arrows-down-up"></ion-icon>
<ion-title>{{
'menu.context.sort.title' | translate | titlecase
}}</ion-title>
@@ -29,12 +31,9 @@
<span *ngIf="sortOption.value === value.value && value.reversible">
<ion-icon
*ngIf="sortOption.reversed"
name="arrow-down-outline"
></ion-icon>
<ion-icon
*ngIf="!sortOption.reversed"
name="arrow-up-outline"
name="arrow-down"
></ion-icon>
<ion-icon *ngIf="!sortOption.reversed" name="arrow-up"></ion-icon>
</span>
</ion-label>
<ion-radio slot="end" [value]="i"> </ion-radio>
@@ -44,7 +43,7 @@
<!-- Filter Context -->
<div class="context-filter" *ngIf="filterOption">
<ion-list-header>
<ion-icon name="filter-outline"></ion-icon>
<ion-icon name="filter"></ion-icon>
<ion-title>{{
'menu.context.filter.title' | translate | titlecase
}}</ion-title>

View File

@@ -16,12 +16,14 @@ import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {RouterModule} from '@angular/router';
import {LayoutModule} from '@angular/cdk/layout';
import {IonicModule} from '@ionic/angular';
import {TranslateModule} from '@ngx-translate/core';
import {SettingsModule} from '../settings/settings.module';
import {ContextMenuComponent} from './context/context-menu.component';
import {ContextMenuService} from './context/context-menu.service';
import {NavigationComponent} from './navigation/navigation.component';
import {TabsModule} from './tabs/tabs.module';
/**
* Menu module
@@ -36,6 +38,8 @@ import {NavigationComponent} from './navigation/navigation.component';
RouterModule,
SettingsModule,
TranslateModule.forChild(),
TabsModule,
LayoutModule,
],
providers: [ContextMenuService],
})

View File

@@ -23,6 +23,7 @@ import {
import {NavigationService} from './navigation.service';
import config from 'capacitor.config';
import {SettingsProvider} from '../../settings/settings.provider';
import {BreakpointObserver} from '@angular/cdk/layout';
/**
* Generated class for the MenuPage page.
@@ -36,6 +37,8 @@ import {SettingsProvider} from '../../settings/settings.provider';
templateUrl: 'navigation.html',
})
export class NavigationComponent implements OnInit {
showTabbar = true;
/**
* Name of the app
*/
@@ -60,11 +63,16 @@ export class NavigationComponent implements OnInit {
public translateService: TranslateService,
private navigationService: NavigationService,
private settingsProvider: SettingsProvider,
private responsive: BreakpointObserver,
) {
translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language);
});
this.responsive.observe(['(min-width: 768px)']).subscribe(result => {
this.showTabbar = !result.matches;
});
}
async ngOnInit() {

View File

@@ -1,13 +1,20 @@
<ion-split-pane contentId="main">
<ion-menu menuId="main" contentId="main" type="overlay" side="start">
<ion-split-pane contentId="main" when="md">
<ion-menu
menuId="main"
contentId="main"
type="overlay"
side="start"
swipe-gesture="false"
>
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
</ion-buttons>
<ion-title>
<img src="assets/imgs/logo.png" alt="" />
<span class="text">{{ appName }}</span>
<ion-toolbar color="primary" mode="ios">
<ion-buttons slot="start"></ion-buttons>
<ion-title
class="clickable"
[routerLink]="['/']"
[routerDirection]="'root'"
>
<ion-img src="assets/imgs/logo.png" class="logo"></ion-img>
</ion-title>
</ion-toolbar>
</ion-header>
@@ -19,7 +26,10 @@
</ion-label>
</ion-list-header>
<ion-menu-toggle auto-hide="false" *ngFor="let item of category.items">
<ion-item [routerDirection]="'root'" [routerLink]="[item.route]">
<ion-item
[routerDirection]="'root'"
[routerLink]="['/' + item.route]"
>
<ion-icon slot="end" [name]="item.icon"></ion-icon>
<ion-label>
{{ item.translations[language].title | titlecase }}
@@ -31,3 +41,4 @@
</ion-menu>
<ion-router-outlet id="main"></ion-router-outlet>
</ion-split-pane>
<stapps-navigation-tabs *ngIf="showTabbar"></stapps-navigation-tabs>

View File

@@ -1,11 +1,34 @@
ion-title {
span.text {
vertical-align: middle;
font-size: 20px;
padding-left: 5px;
@import '../../../../theme/util/mixins';
:host {
ion-split-pane {
margin-bottom: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom));
@include phoneLandscape {
margin-bottom: 0;
}
@include ion-md-up {
margin-bottom: 0;
}
}
img {
height: 25px;
vertical-align: middle
ion-toolbar.in-toolbar {
padding-bottom: 0;
ion-title {
position: relative;
padding: var(--spacing-xl) var(--spacing-md);
.logo {
object-position: left;
height: 80px;
@include ion-md-up {
height: var(--tablet-top-bar-height);
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
const routes: Routes = [
{
path: '',
redirectTo: '/dashboard',
pathMatch: 'full',
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class TabsRoutingModule {}

View File

@@ -0,0 +1,39 @@
:host {
display: flex;
flex-direction: row;
background: var(--ion-color-primary-contrast);
justify-content: space-between;
position: absolute;
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid var(--ion-color-medium);
max-height: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom);
a {
display: flex;
flex-direction: column;
flex: 1 1 25%;
align-items: center;
padding: var(--spacing-xs);
color: var(--ion-color-medium);
background: var(--ion-color-primary-contrast);
text-decoration: none;
&:focus,
&.active{
color: var(--ion-color-medium-contrast);
}
ion-icon {
font-size: 28px;
}
ion-label {
text-transform: uppercase;
font-size: var(--font-size-xs);
font-weight: var(--font-weight-semi-bold);
}
}
}

View File

@@ -0,0 +1,63 @@
import {Component} from '@angular/core';
import {
SCAppConfigurationMenuCategory,
SCLanguage,
SCThingTranslator,
SCTranslations,
} from '@openstapps/core';
import {ConfigProvider} from '../../config/config.provider';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {NGXLogger} from 'ngx-logger';
@Component({
selector: 'stapps-navigation-tabs',
templateUrl: 'tabs.template.html',
styleUrls: ['./tabs.component.scss'],
})
export class TabsComponent {
/**
* Possible languages to be used for translation
*/
language: keyof SCTranslations<SCLanguage> = 'en';
/**
* Menu entries from config module
*/
menu: SCAppConfigurationMenuCategory;
/**
* Core translator
*/
translator: SCThingTranslator;
constructor(
private readonly configProvider: ConfigProvider,
public translateService: TranslateService,
private readonly logger: NGXLogger,
) {
void this.loadMenuEntries();
translateService.onLangChange?.subscribe((event: LangChangeEvent) => {
this.language = event.lang as keyof SCTranslations<SCLanguage>;
this.translator = new SCThingTranslator(this.language);
});
this.translator = new SCThingTranslator('en');
}
/**
* Loads menu entries from configProvider
*/
async loadMenuEntries() {
try {
const menus = (await this.configProvider.getValue(
'menus',
)) as SCAppConfigurationMenuCategory[];
const menu = menus.find(menu => menu.id === 'main');
if (menu) {
this.menu = menu;
}
} catch (error) {
this.logger.error(`error from loading menu entries: ${error}`);
}
}
}

View File

@@ -0,0 +1,15 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {RouterModule} from '@angular/router';
import {IonicModule} from '@ionic/angular';
import {TabsComponent} from './tabs.component';
import {TranslateModule} from '@ngx-translate/core';
@NgModule({
imports: [CommonModule, IonicModule, TranslateModule, RouterModule],
declarations: [TabsComponent],
exports: [TabsComponent],
})
export class TabsModule {}

View File

@@ -0,0 +1,101 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
* Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* 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 {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {TestBed, waitForAsync} from '@angular/core/testing';
import {TabsComponent} from './tabs.component';
import {ConfigProvider} from '../../config/config.provider';
import {sampleAuthConfiguration} from '../../../_helpers/data/sample-configuration';
import {TranslateModule, TranslateService} from '@ngx-translate/core';
import {NGXLogger} from 'ngx-logger';
import {Platform} from '@ionic/angular';
import {ThingTranslateService} from '../../../translation/thing-translate.service';
import {SettingsProvider} from '../../settings/settings.provider';
import {ScheduleSyncService} from '../../background/schedule/schedule-sync.service';
import {StorageProvider} from '../../storage/storage.provider';
describe('Tabs', () => {
let platformReadySpy: any;
let platformSpy: jasmine.SpyObj<Platform>;
let translateServiceSpy: jasmine.SpyObj<TranslateService>;
let thingTranslateServiceSpy: jasmine.SpyObj<ThingTranslateService>;
let settingsProvider: jasmine.SpyObj<SettingsProvider>;
let configProvider: jasmine.SpyObj<ConfigProvider>;
let ngxLogger: jasmine.SpyObj<NGXLogger>;
let scheduleSyncServiceSpy: jasmine.SpyObj<ScheduleSyncService>;
let platformIsSpy;
let storageProvider: jasmine.SpyObj<StorageProvider>;
beforeEach(waitForAsync(() => {
platformReadySpy = Promise.resolve();
platformIsSpy = Promise.resolve();
platformSpy = jasmine.createSpyObj('Platform', {
ready: platformReadySpy,
is: platformIsSpy,
});
translateServiceSpy = jasmine.createSpyObj('TranslateService', [
'setDefaultLang',
'use',
]);
thingTranslateServiceSpy = jasmine.createSpyObj('ThingTranslateService', [
'init',
]);
settingsProvider = jasmine.createSpyObj('SettingsProvider', [
'getSettingValue',
'provideSetting',
'setCategoriesOrder',
]);
scheduleSyncServiceSpy = jasmine.createSpyObj('ScheduleSyncService', [
'getDifferences',
'postDifferencesNotification',
]);
configProvider = jasmine.createSpyObj('ConfigProvider', [
'init',
'getAnyValue',
]);
configProvider.getAnyValue = jasmine.createSpy().and.callFake(function () {
return sampleAuthConfiguration;
});
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
storageProvider = jasmine.createSpyObj('StorageProvider', [
'init',
'get',
'has',
'put',
]);
TestBed.configureTestingModule({
declarations: [TabsComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [TranslateModule.forRoot()],
providers: [
{provide: Platform, useValue: platformSpy},
{provide: TranslateService, useValue: translateServiceSpy},
{provide: ThingTranslateService, useValue: thingTranslateServiceSpy},
{provide: ScheduleSyncService, useValue: scheduleSyncServiceSpy},
{provide: SettingsProvider, useValue: settingsProvider},
{provide: ConfigProvider, useValue: configProvider},
{provide: NGXLogger, useValue: ngxLogger},
{provide: StorageProvider, useValue: storageProvider},
],
}).compileComponents();
}));
it('should create the tabs page', () => {
const fixture = TestBed.createComponent(TabsComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});

View File

@@ -0,0 +1,36 @@
<a
[routerDirection]="'root'"
[routerLink]="['/dashboard']"
routerLinkActive="active"
>
<ion-icon [name]="'smart-home'"></ion-icon>
<ion-label>{{ 'tabs.home' | translate }}</ion-label>
</a>
<a
[routerDirection]="'root'"
[routerLink]="['/canteen']"
routerLinkActive="active"
>
<ion-icon [name]="'tools-kitchen-2'"></ion-icon>
<ion-label>{{ 'tabs.canteens' | translate }}</ion-label>
</a>
<a
[routerDirection]="'root'"
[routerLink]="['/schedule']"
routerLinkActive="active"
>
<ion-icon [name]="'school'"></ion-icon>
<ion-label>{{ 'tabs.schedule' | translate }}</ion-label>
</a>
<a [routerDirection]="'root'" [routerLink]="['/map']" routerLinkActive="active">
<ion-icon [name]="'map-search'"></ion-icon>
<ion-label>{{ 'tabs.map' | translate }}</ion-label>
</a>
<a
[routerDirection]="'root'"
[routerLink]="['/profile']"
routerLinkActive="active"
>
<ion-icon [name]="'user'"></ion-icon>
<ion-label>{{ 'tabs.profile' | translate }}</ion-label>
</a>