mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-21 09:03:02 +00:00
feat: dashboard search rework
This commit is contained in:
committed by
Rainer Killinger
parent
dff4a95acc
commit
8c30a47706
@@ -43,7 +43,7 @@
|
||||
</div>
|
||||
|
||||
<ion-content fullscreen="true" #ionContent>
|
||||
<stapps-search-section #search (focusin)="onSearchBarFocus($event)"></stapps-search-section>
|
||||
<stapps-search-section></stapps-search-section>
|
||||
<stapps-news-section></stapps-news-section>
|
||||
<stapps-mensa-section></stapps-mensa-section>
|
||||
<stapps-favorites-section></stapps-favorites-section>
|
||||
|
||||
@@ -43,8 +43,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
|
||||
|
||||
@ViewChild('search', {read: ElementRef}) searchRef: ElementRef;
|
||||
|
||||
@ViewChild('ionContent') ionContentRef: IonContent;
|
||||
|
||||
collapseAnimation: DashboardCollapse;
|
||||
@@ -145,8 +143,4 @@ export class DashboardComponent implements OnInit, OnDestroy {
|
||||
this._eventUuidSubscription.unsubscribe();
|
||||
this.collapseAnimation.destroy();
|
||||
}
|
||||
|
||||
async onSearchBarFocus(_event: Event) {
|
||||
this.ionContentRef.scrollToTop(100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import {DataModule} from '../data/data.module';
|
||||
import {SettingsProvider} from '../settings/settings.provider';
|
||||
import {DashboardComponent} from './dashboard.component';
|
||||
import {EditModalComponent} from './edit-modal/edit-modal.component';
|
||||
import {SectionComponent} from './section/section.component';
|
||||
import {NavigationSectionComponent} from './sections/navigation-section/navigation-section.component';
|
||||
import {SearchSectionComponent} from './sections/search-section/search-section.component';
|
||||
import {NewsSectionComponent} from './sections/news-section/news-section.component';
|
||||
import {MensaSectionComponent} from './sections/mensa-section/mensa-section.component';
|
||||
@@ -48,9 +46,7 @@ const catalogRoutes: Routes = [
|
||||
*/
|
||||
@NgModule({
|
||||
declarations: [
|
||||
SectionComponent,
|
||||
EditModalComponent,
|
||||
NavigationSectionComponent,
|
||||
SearchSectionComponent,
|
||||
NewsSectionComponent,
|
||||
MensaSectionComponent,
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
[title]="'dashboard.favorites.title' | translate"
|
||||
[isEditable]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<stapps-section [title]="'dashboard.favorites.title' | translate">
|
||||
<ion-button slot="button-end" fill="clear" color="medium" [routerLink]="['/favorites']">
|
||||
<ion-icon slot="icon-only" name="search" size="24"></ion-icon>
|
||||
</ion-button>
|
||||
<simple-swiper *ngIf="(items | async)?.length; else noItems">
|
||||
<stapps-data-list-item
|
||||
*ngFor="let item of items | async"
|
||||
@@ -31,7 +30,7 @@
|
||||
<ion-item class="nothing-selected" lines="none">
|
||||
<ion-label class="ion-text-wrap">
|
||||
{{ 'dashboard.favorites.no_favorite_prefix' | translate }}
|
||||
<a (click)="onSectionEdit()">{{ 'dashboard.favorites.no_favorite_link' | translate }}</a>
|
||||
<a [routerLink]="'/search'">{{ 'dashboard.favorites.no_favorite_link' | translate }}</a>
|
||||
{{ 'dashboard.favorites.no_favorite_suffix' | translate }}
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -127,11 +127,4 @@ export class FavoritesSectionComponent extends SearchPageComponent implements On
|
||||
notifySelect(item: SCThings) {
|
||||
this.dataRoutingService.emitChildEvent(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action when user clicked edit to this section
|
||||
*/
|
||||
onSectionEdit() {
|
||||
void this.router.navigate(['/search']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,24 +16,17 @@
|
||||
<ng-container *ngIf="items | async as items">
|
||||
<ng-container *ngIf="items.length !== 0; else nothingSelected">
|
||||
<ng-container *ngFor="let item of items">
|
||||
<stapps-section
|
||||
@fade
|
||||
[item]="item"
|
||||
[title]="'name' | thingTranslate: item"
|
||||
[isEditable]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<stapps-section @fade [item]="item" [title]="'name' | thingTranslate: item">
|
||||
<ion-button slot="button-end" fill="clear" color="medium" (click)="favoritesService.delete(item)">
|
||||
<ion-icon slot="icon-only" name="delete" size="24"></ion-icon>
|
||||
</ion-button>
|
||||
<stapps-opening-hours slot="subtitle" [openingHours]="item.openingHours"></stapps-opening-hours>
|
||||
<stapps-mensa-section-content [item]="item"></stapps-mensa-section-content>
|
||||
</stapps-section>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-template #nothingSelected>
|
||||
<stapps-section
|
||||
[title]="'dashboard.canteens.title' | translate"
|
||||
[isEditable]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<stapps-section [title]="'dashboard.canteens.title' | translate">
|
||||
<ion-item class="nothing-selected" lines="none">
|
||||
<ion-label class="ion-text-wrap">
|
||||
{{ 'dashboard.canteens.no_favorite_prefix' | translate }}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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/>.
|
||||
*/
|
||||
|
||||
export interface MenuItemInterface {
|
||||
icon: string;
|
||||
label: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export enum MenuItemKey {
|
||||
CATALOG = 'catalog',
|
||||
CANTEEN = 'canteen',
|
||||
MAP = 'map',
|
||||
SETTINGS = 'settings',
|
||||
SEARCH = 'search',
|
||||
}
|
||||
|
||||
export type MenuItemConfig = Record<MenuItemKey, boolean>;
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 {MenuItemInterface, MenuItemKey} from './menu-item.interface';
|
||||
import {SCIcon} from '../../../../util/ion-icon/icon';
|
||||
|
||||
export const MENU_ITEMS: Record<MenuItemKey, MenuItemInterface> = {
|
||||
catalog: {
|
||||
icon: SCIcon`book`,
|
||||
label: 'dashboard.navigation.item.catalog',
|
||||
link: '/catalog',
|
||||
},
|
||||
canteen: {
|
||||
icon: SCIcon`local_cafe`,
|
||||
label: 'dashboard.navigation.item.canteen',
|
||||
link: '/canteen',
|
||||
},
|
||||
map: {
|
||||
icon: SCIcon`map`,
|
||||
label: 'dashboard.navigation.item.map',
|
||||
link: '/map',
|
||||
},
|
||||
settings: {
|
||||
icon: SCIcon`settings`,
|
||||
label: 'dashboard.navigation.item.settings',
|
||||
link: '/settings',
|
||||
},
|
||||
search: {
|
||||
icon: SCIcon`search`,
|
||||
label: 'dashboard.navigation.item.search',
|
||||
link: '/search',
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_ACTIVE_MENU_ITEMS: MenuItemKey[] = [
|
||||
MenuItemKey.CATALOG,
|
||||
MenuItemKey.CANTEEN,
|
||||
MenuItemKey.MAP,
|
||||
MenuItemKey.SETTINGS,
|
||||
MenuItemKey.SEARCH,
|
||||
];
|
||||
@@ -1,30 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
[title]="'dashboard.navigation.title' | translate"
|
||||
[isEditable]="true"
|
||||
[isSectionExtended]="true"
|
||||
(onEdit)="onSectionEdit()"
|
||||
>
|
||||
<swiper [config]="sliderOptions" slidesPerView="auto" class="navigation-swiper card-swiper">
|
||||
<ng-template swiperSlide *ngFor="let item of activeMenuItems">
|
||||
<a [routerLink]="menuItems[item].link" class="card">
|
||||
<ion-icon size="40" [name]="menuItems[item].icon"></ion-icon>
|
||||
<ion-label>{{ menuItems[item].label | translate }}</ion-label>
|
||||
</a>
|
||||
</ng-template>
|
||||
</swiper>
|
||||
</stapps-section>
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 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/>.
|
||||
*/
|
||||
|
||||
.navigation-swiper.swiper {
|
||||
.swiper-slide {
|
||||
a {
|
||||
font-family: var(--ion-font-family);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semi-bold);
|
||||
text-align: center;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 {Component, OnInit, ViewEncapsulation} from '@angular/core';
|
||||
import {ModalController} from '@ionic/angular';
|
||||
import {EditModalComponent} from '../../edit-modal/edit-modal.component';
|
||||
import {DEFAULT_ACTIVE_MENU_ITEMS, MENU_ITEMS} from './menu-items.config';
|
||||
import {MenuItemKey} from './menu-item.interface';
|
||||
import {EditModalItem, EditModalTypeEnum} from '../../edit-modal/edit-modal-type.enum';
|
||||
import {StorageProvider} from '../../../storage/storage.provider';
|
||||
import {TranslatePipe} from '@ngx-translate/core';
|
||||
|
||||
const DASHBOARD_NAVIGATION = 'stapps.dashboard.navigation';
|
||||
|
||||
/**
|
||||
* Shows a horizontal list of navigation items
|
||||
*/
|
||||
@Component({
|
||||
selector: 'stapps-navigation-section',
|
||||
templateUrl: 'navigation-section.component.html',
|
||||
styleUrls: ['navigation-section.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class NavigationSectionComponent implements OnInit {
|
||||
/**
|
||||
* Slider options
|
||||
*/
|
||||
sliderOptions = {
|
||||
spaceBetween: 12,
|
||||
freeMode: {
|
||||
enabled: true,
|
||||
sticky: true,
|
||||
},
|
||||
width: 120,
|
||||
};
|
||||
|
||||
menuItems = MENU_ITEMS;
|
||||
|
||||
activeMenuItems: MenuItemKey[] = DEFAULT_ACTIVE_MENU_ITEMS;
|
||||
|
||||
constructor(
|
||||
public modalController: ModalController,
|
||||
private storageProvider: StorageProvider,
|
||||
private translatePipe: TranslatePipe,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
void this.getItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current order of items
|
||||
*/
|
||||
async getItems() {
|
||||
if (await this.storageProvider.has(DASHBOARD_NAVIGATION)) {
|
||||
const storedMenuItems: string = await this.storageProvider.get(DASHBOARD_NAVIGATION);
|
||||
if (storedMenuItems) {
|
||||
const parsedMenuItems = JSON.parse(storedMenuItems);
|
||||
if (Array.isArray(parsedMenuItems) && parsedMenuItems.every(it => typeof it === 'string')) {
|
||||
this.activeMenuItems = parsedMenuItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save updated order of items
|
||||
*/
|
||||
updateActiveItems(items: MenuItemKey[]) {
|
||||
this.activeMenuItems = items;
|
||||
void this.storageProvider.put<string>(DASHBOARD_NAVIGATION, JSON.stringify(this.activeMenuItems));
|
||||
}
|
||||
|
||||
/**
|
||||
* Action when user clicked edit to this section
|
||||
*/
|
||||
async onSectionEdit() {
|
||||
const modal = await this.modalController.create({
|
||||
component: EditModalComponent,
|
||||
canDismiss: true,
|
||||
componentProps: {
|
||||
items: Object.entries(this.menuItems).map(([id, item]) => ({
|
||||
id,
|
||||
active: this.activeMenuItems.includes(id as MenuItemKey),
|
||||
labelLocalized: this.translatePipe.transform(item.label),
|
||||
})),
|
||||
type: EditModalTypeEnum.CHECKBOXES,
|
||||
},
|
||||
});
|
||||
await modal.present();
|
||||
|
||||
modal.onDidDismiss().then(result => {
|
||||
if (result.data?.items) {
|
||||
this.updateActiveItems(
|
||||
result.data.items.filter((it: EditModalItem) => it.active).map((it: EditModalItem) => it.id),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,11 @@
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section
|
||||
[title]="'dashboard.news.title' | translate"
|
||||
[isEditable]="false"
|
||||
[customIcon]="'read_more'"
|
||||
class="is-editable"
|
||||
(onEdit)="onMoreNewsClicked()"
|
||||
>
|
||||
<simple-swiper class="news-swiper card-swiper" *ngIf="news" @fade>
|
||||
<stapps-section [title]="'dashboard.news.title' | translate">
|
||||
<ion-button size="small" slot="button-end" fill="clear" color="medium" [routerLink]="['/news']">
|
||||
<ion-icon slot="icon-only" name="read_more"></ion-icon>
|
||||
</ion-button>
|
||||
<simple-swiper class="news-swiper card-swiper" *ngIf="news | async as news" @fade>
|
||||
<stapps-news-item *ngFor="let newsItem of news" [item]="newsItem"> </stapps-news-item>
|
||||
<ion-item [routerLink]="['/news']" class="more-news" lines="none">
|
||||
<ion-label>{{ 'dashboard.news.moreNews' | translate | titlecase }}</ion-label>
|
||||
|
||||
@@ -12,15 +12,11 @@
|
||||
* 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, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {NewsPageComponent} from '../../../news/page/news-page.component';
|
||||
import {Component} from '@angular/core';
|
||||
import {NewsProvider} from '../../../news/news.provider';
|
||||
import {SettingsProvider} from '../../../settings/settings.provider';
|
||||
import {newsFilterSettingsFieldsMapping, NewsFilterSettingsNames} from '../../../news/news-filter-settings';
|
||||
import {DataProvider} from '../../../data/data.provider';
|
||||
import {SCSearchValueFilter} from '@openstapps/core';
|
||||
import {SCMessage} from '@openstapps/core';
|
||||
import {animate, style, transition, trigger} from '@angular/animations';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
/**
|
||||
* Shows a section with news
|
||||
@@ -38,40 +34,12 @@ import {animate, style, transition, trigger} from '@angular/animations';
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class NewsSectionComponent extends NewsPageComponent implements OnInit {
|
||||
pageSize = 5;
|
||||
export class NewsSectionComponent {
|
||||
news: Promise<SCMessage[]>;
|
||||
|
||||
/**
|
||||
* A map of the filters where the keys are settings names
|
||||
*/
|
||||
filtersMap = new Map<NewsFilterSettingsNames, SCSearchValueFilter>();
|
||||
|
||||
constructor(newsProvider: NewsProvider, settingsProvider: SettingsProvider, private router: Router) {
|
||||
super(newsProvider, settingsProvider);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await super.ngOnInit();
|
||||
for (const setting of this.settings) {
|
||||
this.filtersMap.set(
|
||||
setting.name as NewsFilterSettingsNames,
|
||||
DataProvider.createValueFilter(
|
||||
newsFilterSettingsFieldsMapping[setting.name as NewsFilterSettingsNames],
|
||||
setting.value as string,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
this.filters = [...this.filtersMap.values()];
|
||||
|
||||
try {
|
||||
await this.fetchNews();
|
||||
} catch {
|
||||
this.news = [];
|
||||
}
|
||||
}
|
||||
|
||||
onMoreNewsClicked() {
|
||||
void this.router.navigate(['/news']);
|
||||
constructor(readonly newsProvider: NewsProvider, readonly router: Router) {
|
||||
this.news = this.newsProvider
|
||||
.getCurrentFilters()
|
||||
.then(filters => this.newsProvider.getList(5, 0, filters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 {AnimationController} from '@ionic/angular';
|
||||
import {AnimationOptions} from '@ionic/angular/providers/nav-controller';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function homePageSearchTransition(animationController: AnimationController) {
|
||||
let scheduleTransform: WebKitCSSMatrix;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (_baseElement: HTMLElement, options: AnimationOptions | any) => {
|
||||
const back = options.direction === 'back';
|
||||
const searchPage = back ? options.leavingEl : options.enteringEl;
|
||||
const homePage = back ? options.enteringEl : options.leavingEl;
|
||||
const rootTransition = animationController
|
||||
.create()
|
||||
.duration(options.duration ?? 350)
|
||||
.easing(
|
||||
// quintic in / out
|
||||
back ? 'cubic-bezier(0.64, 0, 0.78, 0)' : 'cubic-bezier(0.22, 1, 0.36, 1)',
|
||||
);
|
||||
|
||||
const homePageContent = homePage.querySelector('ion-content').shadowRoot.querySelector('.inner-scroll');
|
||||
const leavingSearchbar = homePage.querySelector('ion-searchbar');
|
||||
const enteringSearchbar = searchPage.querySelector('ion-searchbar');
|
||||
const searchPageHeader = searchPage.querySelector('ion-header');
|
||||
const homePageSchedule = homePage.querySelector('.schedule');
|
||||
|
||||
if (!back) {
|
||||
scheduleTransform = new WebKitCSSMatrix(window.getComputedStyle(homePageSchedule).transform);
|
||||
}
|
||||
const enteringSearchbarTop = enteringSearchbar.getBoundingClientRect().top;
|
||||
const leavingSearchbarTop = leavingSearchbar.getBoundingClientRect().top;
|
||||
const searchbarDelta = leavingSearchbarTop - enteringSearchbarTop;
|
||||
const searchHeaderHeight = searchPageHeader.getBoundingClientRect().bottom;
|
||||
const homeHeaderHeight = homePageSchedule.getBoundingClientRect().bottom;
|
||||
const homePageSlideAmount = -50;
|
||||
const headerDelta = homeHeaderHeight - searchHeaderHeight;
|
||||
|
||||
const enterTransition = animationController.create().fromTo('opacity', '0', '1').addElement(searchPage);
|
||||
const exitTransition = animationController.create().fromTo('opacity', '1', '1').addElement(homePage);
|
||||
|
||||
const homePageSlide = animationController
|
||||
.create()
|
||||
.fromTo('transform', `translateY(0px)`, `translateY(${homePageSlideAmount}px)`)
|
||||
.addElement(homePageContent);
|
||||
|
||||
const toolbarExit = animationController
|
||||
.create()
|
||||
.fromTo(
|
||||
'transform',
|
||||
scheduleTransform.toString(),
|
||||
scheduleTransform.translate(undefined, -headerDelta).toString(),
|
||||
)
|
||||
.addElement(homePageSchedule);
|
||||
const headerSlide = animationController
|
||||
.create()
|
||||
.fromTo('transform', `translateY(${headerDelta}px)`, 'translateY(0px)')
|
||||
.addElement(searchPageHeader);
|
||||
|
||||
const searchbarSlideIn = animationController
|
||||
.create()
|
||||
.fromTo('transform', `translateY(${searchbarDelta - headerDelta}px)`, 'translateY(0px)')
|
||||
.beforeStyles({
|
||||
'z-index': 1000,
|
||||
})
|
||||
.afterClearStyles(['z-index'])
|
||||
.addElement(searchPage.querySelector('.toolbar-searchbar'));
|
||||
const searchbarSlideOut = animationController
|
||||
.create()
|
||||
.fromTo('transform', 'translateY(0px)', `translateY(-${searchbarDelta + homePageSlideAmount}px)`)
|
||||
.addElement(homePage.querySelector('stapps-search-section > stapps-section'));
|
||||
|
||||
rootTransition
|
||||
.addAnimation([
|
||||
enterTransition,
|
||||
exitTransition,
|
||||
toolbarExit,
|
||||
homePageSlide,
|
||||
headerSlide,
|
||||
searchbarSlideIn,
|
||||
searchbarSlideOut,
|
||||
])
|
||||
.direction(back ? 'reverse' : 'normal');
|
||||
return rootTransition;
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ Copyright (C) 2023 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.
|
||||
@@ -13,17 +13,12 @@
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<stapps-section title="{{ 'dashboard.navigation.item.search' | translate }}" [isEditable]="false">
|
||||
<div class="searchbar">
|
||||
<ion-input
|
||||
type="search"
|
||||
enterkeyhint="search"
|
||||
placeholder="{{ 'search.search_bar.placeholder' | translate }}"
|
||||
(submit)="onSubmitSearch()"
|
||||
(keyup.enter)="onSubmitSearch()"
|
||||
(search)="onSubmitSearch()"
|
||||
[(ngModel)]="searchTerm"
|
||||
></ion-input>
|
||||
<ion-icon size="35" weight="300" name="search" (click)="onSubmitSearch()" class="clickable"></ion-icon>
|
||||
</div>
|
||||
<stapps-section title="{{ 'dashboard.navigation.item.search' | translate }}">
|
||||
<ion-searchbar
|
||||
[routerLink]="'/search'"
|
||||
[routerAnimation]="routeTransition"
|
||||
class="stapps-searchbar ion-activatable ripple-parent"
|
||||
>
|
||||
<ion-ripple-effect></ion-ripple-effect>
|
||||
</ion-searchbar>
|
||||
</stapps-section>
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 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.
|
||||
* Copyright (C) 2023 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.
|
||||
* 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/>.
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.searchbar {
|
||||
position: relative;
|
||||
max-width: 700px;
|
||||
ion-ripple-effect {
|
||||
z-index: 1000;
|
||||
border-radius: var(--spacing-sm);
|
||||
}
|
||||
|
||||
ion-input {
|
||||
background: var(--ion-color-field-bg);
|
||||
border-radius: var(--border-radius-default);
|
||||
--padding-start: var(--spacing-md);
|
||||
--padding-end: var(--spacing-md);
|
||||
--padding-top: var(--spacing-md);
|
||||
--padding-bottom: var(--spacing-md);
|
||||
box-shadow: var(--shadow-default);
|
||||
ion-searchbar {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
ion-searchbar ::ng-deep .searchbar-input-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
ion-searchbar.ios {
|
||||
ion-ripple-effect {
|
||||
display: none;
|
||||
}
|
||||
ion-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: var(--spacing-md);
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
|
||||
transition: opacity 150ms ease;
|
||||
&:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 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.
|
||||
@@ -16,6 +16,8 @@ import {Component} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {Capacitor} from '@capacitor/core';
|
||||
import {Keyboard} from '@capacitor/keyboard';
|
||||
import {AnimationBuilder, AnimationController} from '@ionic/angular';
|
||||
import {homePageSearchTransition} from './search-route-transition';
|
||||
|
||||
/**
|
||||
* Shows a search input field
|
||||
@@ -28,7 +30,11 @@ import {Keyboard} from '@capacitor/keyboard';
|
||||
export class SearchSectionComponent {
|
||||
searchTerm = '';
|
||||
|
||||
constructor(private router: Router) {}
|
||||
routeTransition: AnimationBuilder;
|
||||
|
||||
constructor(private router: Router, private animationController: AnimationController) {
|
||||
this.routeTransition = homePageSearchTransition(this.animationController);
|
||||
}
|
||||
|
||||
/**
|
||||
* User submits search
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 StApps
|
||||
~ Copyright (C) 2023 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.
|
||||
@@ -33,6 +33,7 @@
|
||||
type="search"
|
||||
enterkeyhint="search"
|
||||
class="filterable"
|
||||
autofocus
|
||||
>
|
||||
<ion-menu-button menu="context" auto-hide="false">
|
||||
<ion-icon name="tune"></ion-icon>
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*!
|
||||
* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -35,3 +50,11 @@ ion-content {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-header {
|
||||
background: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
ion-toolbar {
|
||||
--ion-color-base: none !important;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ ion-card-title {
|
||||
overflow: hidden;
|
||||
font-size: var(--font-size-xl);
|
||||
--color: var(--ion-color-dark-contrast);
|
||||
max-lines: 3;
|
||||
}
|
||||
|
||||
ion-card-subtitle {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 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.
|
||||
@@ -19,8 +19,17 @@ import {
|
||||
SCSearchBooleanFilter,
|
||||
SCSearchFilter,
|
||||
SCSearchQuery,
|
||||
SCSearchValueFilter,
|
||||
SCSetting,
|
||||
} from '@openstapps/core';
|
||||
import {DataProvider} from '../data/data.provider';
|
||||
import {
|
||||
newsFilterSettingsCategory,
|
||||
newsFilterSettingsFieldsMapping,
|
||||
NewsFilterSettingsNames,
|
||||
} from './news-filter-settings';
|
||||
import {SettingsProvider} from '../settings/settings.provider';
|
||||
|
||||
/**
|
||||
* Service for providing news messages
|
||||
*/
|
||||
@@ -28,7 +37,31 @@ import {DataProvider} from '../data/data.provider';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NewsProvider {
|
||||
constructor(private dataProvider: DataProvider) {}
|
||||
constructor(private dataProvider: DataProvider, private settingsProvider: SettingsProvider) {}
|
||||
|
||||
async getCurrentSettings(): Promise<SCSetting[]> {
|
||||
const settings: SCSetting[] = [];
|
||||
for (const settingName of Object.keys(newsFilterSettingsFieldsMapping) as NewsFilterSettingsNames[]) {
|
||||
settings.push(await this.settingsProvider.getSetting(newsFilterSettingsCategory, settingName));
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
async getCurrentFilters(): Promise<SCSearchFilter[]> {
|
||||
const settings = await this.getCurrentSettings();
|
||||
const filtersMap = new Map<NewsFilterSettingsNames, SCSearchValueFilter>();
|
||||
for (const setting of settings) {
|
||||
filtersMap.set(
|
||||
setting.name as NewsFilterSettingsNames,
|
||||
DataProvider.createValueFilter(
|
||||
newsFilterSettingsFieldsMapping[setting.name as NewsFilterSettingsNames],
|
||||
setting.value as string,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return [...filtersMap.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get news messages
|
||||
|
||||
@@ -15,12 +15,6 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {IonRefresher} from '@ionic/angular';
|
||||
import {SCMessage, SCSearchFilter, SCSearchValueFilter, SCSetting} from '@openstapps/core';
|
||||
import {SettingsProvider} from '../../settings/settings.provider';
|
||||
import {
|
||||
newsFilterSettingsCategory,
|
||||
newsFilterSettingsFieldsMapping,
|
||||
NewsFilterSettingsNames,
|
||||
} from '../news-filter-settings';
|
||||
import {NewsProvider} from '../news.provider';
|
||||
import {SplashScreen} from '@capacitor/splash-screen';
|
||||
|
||||
@@ -68,7 +62,7 @@ export class NewsPageComponent implements OnInit {
|
||||
*/
|
||||
filters: SCSearchFilter[];
|
||||
|
||||
constructor(private newsProvider: NewsProvider, private settingsProvider: SettingsProvider) {}
|
||||
constructor(private newsProvider: NewsProvider) {}
|
||||
|
||||
/**
|
||||
* Fetch news from the backend
|
||||
@@ -109,18 +103,7 @@ export class NewsPageComponent implements OnInit {
|
||||
* Initialize the local variables on component initialization
|
||||
*/
|
||||
async ngOnInit() {
|
||||
// Helper method to provide the relevant settings
|
||||
const getNewsSettings = async (settingNames: NewsFilterSettingsNames[]) => {
|
||||
const settings = [];
|
||||
for (const settingName of settingNames) {
|
||||
settings.push(await this.settingsProvider.getSetting(newsFilterSettingsCategory, settingName));
|
||||
}
|
||||
return settings;
|
||||
};
|
||||
|
||||
this.settings = await getNewsSettings(
|
||||
Object.keys(newsFilterSettingsFieldsMapping) as NewsFilterSettingsNames[],
|
||||
);
|
||||
this.settings = await this.newsProvider.getCurrentSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,58 +13,26 @@
|
||||
~ this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<ion-label class="section-headline">
|
||||
<!-- TODO: move this to thing translate -->
|
||||
{{ 'name' | translateSimple: item }}
|
||||
</ion-label>
|
||||
<ion-button
|
||||
*ngIf="item.authProvider"
|
||||
fill="clear"
|
||||
color="dark"
|
||||
(click)="toggleLogIn()"
|
||||
style="grid-area: login"
|
||||
>
|
||||
<ion-icon *ngIf="isLoggedIn; else loginIcon" slot="end" name="logout"></ion-icon>
|
||||
<ng-template #loginIcon>
|
||||
<ion-icon slot="end" name="login"></ion-icon>
|
||||
</ng-template>
|
||||
<ion-label>{{ 'profile.buttons.default.log_' + (isLoggedIn ? 'out' : 'in') | translate }}</ion-label>
|
||||
</ion-button>
|
||||
<ion-button
|
||||
[disabled]="isBeginning"
|
||||
(click)="swiper.swiperRef.slidePrev()"
|
||||
fill="clear"
|
||||
color="dark"
|
||||
class="navigation"
|
||||
[class.hidden]="slidesFillScreen"
|
||||
style="grid-area: prev"
|
||||
><ion-icon slot="icon-only" name="chevron_left"></ion-icon
|
||||
></ion-button>
|
||||
<ion-button
|
||||
[disabled]="isEnd"
|
||||
(click)="swiper.swiperRef.slideNext()"
|
||||
fill="clear"
|
||||
color="dark"
|
||||
class="navigation"
|
||||
[class.hidden]="slidesFillScreen"
|
||||
style="grid-area: next"
|
||||
><ion-icon slot="icon-only" name="chevron_right"></ion-icon
|
||||
></ion-button>
|
||||
<swiper
|
||||
#swiper
|
||||
(elementSizeChange)="resizeSwiper($event, swiper.swiperRef)"
|
||||
[elementSizeChangeDebounce]="100"
|
||||
(toEdge)="activeIndexChange($event[0])"
|
||||
(fromEdge)="activeIndexChange($event[0])"
|
||||
[cssMode]="true"
|
||||
[spaceBetween]="0"
|
||||
[slidesPerView]="slidesPerView"
|
||||
class="card-swiper"
|
||||
>
|
||||
<ng-template swiperSlide *ngFor="let link of item.links">
|
||||
<stapps-section-link-card
|
||||
<stapps-section [title]="'name' | translateSimple: item">
|
||||
<ion-button slot="button-end" *ngIf="item.authProvider" fill="clear" color="dark" (click)="toggleLogIn()">
|
||||
<ion-icon *ngIf="isLoggedIn; else loginIcon" slot="end" name="logout"></ion-icon>
|
||||
<ng-template #loginIcon>
|
||||
<ion-icon slot="end" name="login"></ion-icon>
|
||||
</ng-template>
|
||||
<ion-label>{{ 'profile.buttons.default.log_' + (isLoggedIn ? 'out' : 'in') | translate }}</ion-label>
|
||||
</ion-button>
|
||||
|
||||
<simple-swiper>
|
||||
<ion-item
|
||||
lines="none"
|
||||
*ngFor="let link of item.links"
|
||||
[routerLink]="link.link"
|
||||
[disabled]="link.needsAuth && !isLoggedIn"
|
||||
[item]="link"
|
||||
></stapps-section-link-card>
|
||||
</ng-template>
|
||||
</swiper>
|
||||
>
|
||||
<div>
|
||||
<ion-icon [name]="link.icon" size="36" color="dark"></ion-icon>
|
||||
<ion-label>{{ 'name' | translateSimple: link }}</ion-label>
|
||||
</div>
|
||||
</ion-item>
|
||||
</simple-swiper>
|
||||
</stapps-section>
|
||||
|
||||
@@ -12,9 +12,50 @@
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
$width: 108px;
|
||||
|
||||
.section-headline {
|
||||
padding-inline-start: var(--spacing-md);
|
||||
simple-swiper {
|
||||
container-type: inline-size;
|
||||
--swiper-slide-width: #{$width};
|
||||
|
||||
ion-item {
|
||||
@each $i in 7, 6, 5, 4, 3, 2, 1 {
|
||||
$max: #{($width + 8px) * $i};
|
||||
@container (inline-size < #{$max}) {
|
||||
--swiper-slide-width: #{100cqi / $i};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-item {
|
||||
height: 96px;
|
||||
--border-radius: var(--border-radius-default);
|
||||
--inner-padding-start: unset;
|
||||
--inner-padding-end: unset;
|
||||
--padding-start: unset;
|
||||
--padding-end: unset;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
div,
|
||||
ion-label {
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
&::part(native) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.log-in-hint {
|
||||
@@ -23,47 +64,3 @@
|
||||
box-shadow: none;
|
||||
background: var(--ion-color-light-tint);
|
||||
}
|
||||
|
||||
.navigation::part(native) {
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
swiper {
|
||||
//noinspection CssInvalidFunction
|
||||
transform: translateY(calc(-1 * var(--spacing-sm)));
|
||||
}
|
||||
|
||||
stapps-section-link-card {
|
||||
// required spacing for box shadow
|
||||
margin-block: var(--spacing-sm);
|
||||
}
|
||||
|
||||
:host {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto auto;
|
||||
grid-template-rows: 42px 1fr;
|
||||
grid-template-areas:
|
||||
'title prev next login'
|
||||
'swiper swiper swiper swiper';
|
||||
align-items: center;
|
||||
|
||||
> ion-label {
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
> swiper {
|
||||
grid-area: swiper;
|
||||
width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ion-button.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
ion-button.navigation {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2022 StApps
|
||||
* Copyright (C) 2023 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.
|
||||
@@ -25,7 +25,6 @@ import {UtilModule} from '../../util/util.module';
|
||||
import {IonIconModule} from '../../util/ion-icon/ion-icon.module';
|
||||
import {ProfilePageSectionComponent} from './page/profile-page-section.component';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {SectionModule} from '../../util/section/section.module';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -46,7 +45,6 @@ const routes: Routes = [
|
||||
SwiperModule,
|
||||
UtilModule,
|
||||
ThingTranslateModule,
|
||||
SectionModule,
|
||||
],
|
||||
})
|
||||
export class ProfilePageModule {}
|
||||
|
||||
39
src/app/util/searchbar-autofocus.directive.ts
Normal file
39
src/app/util/searchbar-autofocus.directive.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 {AfterViewInit, Directive, ElementRef} from '@angular/core';
|
||||
import {IonSearchbar} from '@ionic/angular';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-searchbar[autofocus]',
|
||||
})
|
||||
export class SearchbarAutofocusDirective implements AfterViewInit {
|
||||
constructor(private element: ElementRef) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const label = `focus`;
|
||||
console.time(label);
|
||||
const interval = setInterval(() => {
|
||||
const searchbar = this.element.nativeElement as IonSearchbar;
|
||||
searchbar.setFocus();
|
||||
});
|
||||
const onFocus = () => {
|
||||
console.timeEnd(label);
|
||||
clearInterval(interval);
|
||||
this.element.nativeElement.removeEventListener('ionFocus', onFocus);
|
||||
};
|
||||
this.element.nativeElement.addEventListener('ionFocus', onFocus);
|
||||
}
|
||||
}
|
||||
@@ -37,20 +37,14 @@
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ng-container>
|
||||
|
||||
<ion-col size="auto" *ngIf="isEditable">
|
||||
<ion-button fill="clear" color="medium" (click)="onEditClick()">
|
||||
<ion-icon size="24" slot="icon-only" name="edit_square"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
<ion-col size="auto" *ngIf="customIcon">
|
||||
<ion-button fill="clear" color="medium" (click)="onEditClick()">
|
||||
<ion-icon slot="icon-only" size="24" [name]="customIcon"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-col size="auto">
|
||||
<div>
|
||||
<ng-content select="[slot=button-end]"></ng-content>
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row>
|
||||
<ion-col #content>
|
||||
<ion-col>
|
||||
<ng-content></ng-content>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
@@ -13,7 +13,7 @@
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import '../../../../theme/util/mixins';
|
||||
@import 'src/theme/util/mixins';
|
||||
|
||||
a {
|
||||
display: contents;
|
||||
@@ -44,8 +44,8 @@ ion-col {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ion-button::part(native) {
|
||||
padding-inline: var(--spacing-sm);
|
||||
:host ::ng-deep ion-button::part(native) {
|
||||
padding-inline: var(--spacing-xs);
|
||||
}
|
||||
|
||||
@media (hover: none) {
|
||||
@@ -12,17 +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 {
|
||||
AfterContentInit,
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
Input,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewContainerRef,
|
||||
} from '@angular/core';
|
||||
import {AfterContentInit, Component, Input, OnDestroy, ViewContainerRef} from '@angular/core';
|
||||
import {SCThings} from '@openstapps/core';
|
||||
|
||||
/**
|
||||
@@ -33,35 +23,17 @@ import {SCThings} from '@openstapps/core';
|
||||
templateUrl: 'section.component.html',
|
||||
styleUrls: ['section.component.scss'],
|
||||
})
|
||||
export class SectionComponent implements OnInit, AfterContentInit, OnDestroy {
|
||||
@HostBinding('class.is-extended') isExtendedClass = false;
|
||||
|
||||
@HostBinding('class.is-editable') isEditableClass = false;
|
||||
|
||||
export class SectionComponent implements AfterContentInit, OnDestroy {
|
||||
@Input() title = '';
|
||||
|
||||
@Input() isSectionExtended = false;
|
||||
|
||||
@Input() isEditable = false;
|
||||
|
||||
@Input() customIcon?: string = undefined;
|
||||
|
||||
@Input() item?: SCThings;
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/no-output-on-prefix
|
||||
@Output() onEdit = new EventEmitter<void>();
|
||||
|
||||
mutationObserver: MutationObserver;
|
||||
|
||||
swiper?: HTMLElement;
|
||||
|
||||
constructor(readonly viewContainerRef: ViewContainerRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.isExtendedClass = this.isSectionExtended;
|
||||
this.isEditableClass = this.isEditable;
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.mutationObserver = new MutationObserver(() => {
|
||||
const simpleSwiper = this.viewContainerRef.element.nativeElement.querySelector('simple-swiper');
|
||||
@@ -93,13 +65,6 @@ export class SectionComponent implements OnInit, AfterContentInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action when edit is clicked
|
||||
*/
|
||||
onEditClick() {
|
||||
this.onEdit.emit();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.mutationObserver.disconnect();
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 {Component, Input} from '@angular/core';
|
||||
import {SCSectionLink} from '../../modules/profile/page/sections';
|
||||
|
||||
@Component({
|
||||
selector: 'stapps-section-link-card',
|
||||
templateUrl: 'section-link-card.html',
|
||||
styleUrls: ['section-link-card.scss'],
|
||||
})
|
||||
export class SectionLinkCardComponent {
|
||||
@Input() item: SCSectionLink;
|
||||
|
||||
@Input() disabled: boolean;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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/>.
|
||||
-->
|
||||
<ion-card [routerLink]="item.link" class="card" [class.disabled]="disabled">
|
||||
<ion-card-header mode="md">
|
||||
<ion-card-title>
|
||||
<ion-icon [name]="item.icon" size="40"></ion-icon>
|
||||
<!-- TODO: move this to thing translate -->
|
||||
{{ 'name' | translateSimple: item }}
|
||||
</ion-card-title>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
@@ -1,49 +0,0 @@
|
||||
/*!
|
||||
* Copyright (C) 2023 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/>.
|
||||
*/
|
||||
|
||||
:host,
|
||||
ion-card,
|
||||
ion-card-header,
|
||||
ion-card::part(native) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ion-card {
|
||||
padding: 0;
|
||||
margin-block: 0;
|
||||
margin-inline: var(--spacing-sm);
|
||||
}
|
||||
|
||||
ion-card-header {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
}
|
||||
|
||||
ion-card-title {
|
||||
padding: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
font-family: var(--ion-font-family);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-semi-bold);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'stapps-section-tail-prompt-card',
|
||||
templateUrl: 'section-tail-prompt-card.html',
|
||||
styleUrls: ['section-tail-prompt-card.scss', 'section-link-card.scss'],
|
||||
})
|
||||
export class SectionTailPromptCardComponent {}
|
||||
@@ -1,22 +0,0 @@
|
||||
<!--
|
||||
~ Copyright (C) 2022 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/>.
|
||||
-->
|
||||
|
||||
<ion-card [button]="true" class="card">
|
||||
<ion-card-header>
|
||||
<ion-card-title>
|
||||
<ng-content></ng-content>
|
||||
</ion-card-title>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
@@ -1,19 +0,0 @@
|
||||
/*!
|
||||
* Copyright (C) 2022 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/>.
|
||||
*/
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 {NgModule} from '@angular/core';
|
||||
import {SectionLinkCardComponent} from './section-link-card.component';
|
||||
import {IonicModule} from '@ionic/angular';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {TranslateModule} from '@ngx-translate/core';
|
||||
import {IonIconModule} from '../ion-icon/ion-icon.module';
|
||||
import {ThingTranslateModule} from '../../translation/thing-translate.module';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {SectionTailPromptCardComponent} from './section-tail-prompt-card.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, IonicModule, TranslateModule, IonIconModule, ThingTranslateModule, RouterModule],
|
||||
declarations: [SectionLinkCardComponent, SectionTailPromptCardComponent],
|
||||
exports: [SectionLinkCardComponent, SectionTailPromptCardComponent],
|
||||
})
|
||||
export class SectionModule {}
|
||||
@@ -28,6 +28,8 @@ import {ElementSizeChangeDirective} from './element-size-change.directive';
|
||||
import {OpeningHoursComponent} from './opening-hours.component';
|
||||
import {ThingTranslateModule} from '../translation/thing-translate.module';
|
||||
import {SimpleSwiperComponent} from './simple-swiper.component';
|
||||
import {SearchbarAutofocusDirective} from './searchbar-autofocus.directive';
|
||||
import {SectionComponent} from './section.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule, IonicModule, TranslateModule, ThingTranslateModule.forChild()],
|
||||
@@ -37,12 +39,14 @@ import {SimpleSwiperComponent} from './simple-swiper.component';
|
||||
DateIsThisPipe,
|
||||
NullishCoalescingPipe,
|
||||
LazyPipe,
|
||||
SectionComponent,
|
||||
DateFromIndexPipe,
|
||||
DaytimeKeyPipe,
|
||||
NextDateInListPipe,
|
||||
EditModalComponent,
|
||||
OpeningHoursComponent,
|
||||
SimpleSwiperComponent,
|
||||
SearchbarAutofocusDirective,
|
||||
],
|
||||
exports: [
|
||||
ElementSizeChangeDirective,
|
||||
@@ -52,10 +56,12 @@ import {SimpleSwiperComponent} from './simple-swiper.component';
|
||||
LazyPipe,
|
||||
DateFromIndexPipe,
|
||||
DaytimeKeyPipe,
|
||||
SectionComponent,
|
||||
NextDateInListPipe,
|
||||
EditModalComponent,
|
||||
OpeningHoursComponent,
|
||||
SimpleSwiperComponent,
|
||||
SearchbarAutofocusDirective,
|
||||
],
|
||||
})
|
||||
export class UtilModule {}
|
||||
|
||||
Reference in New Issue
Block a user