feat: expandable accordion in grades module

This commit is contained in:
Thea Schöbl
2022-06-01 15:26:23 +00:00
parent 51bb8e3b9c
commit 3f81afda82
27 changed files with 857 additions and 276 deletions

View File

@@ -34,6 +34,7 @@ import {AssessmentsDetailComponent} from './detail/assessments-detail.component'
import {AssessmentsProvider} from './assessments.provider';
import {AssessmentsSimpleDataListComponent} from './list/assessments-simple-data-list.component';
import {ProtectedRoutes} from '../auth/protected.routes';
import {AssessmentsTreeListComponent} from './list/assessments-tree-list.component';
const routes: ProtectedRoutes = [
{
@@ -56,6 +57,7 @@ const routes: ProtectedRoutes = [
AssessmentBaseInfoComponent,
AssessmentDetailComponent,
AssessmentsListItemComponent,
AssessmentsTreeListComponent,
CourseOfStudyAssessmentComponent,
AssessmentsPageComponent,
AssessmentsDataListComponent,

View File

@@ -13,29 +13,60 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, ViewChild} from '@angular/core';
import {Component, Input, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {AssessmentsProvider} from '../assessments.provider';
import {
DataDetailComponent,
ExternalDataLoadEvent,
} from '../../data/detail/data-detail.component';
import {ViewWillEnter} from '@ionic/angular';
import {NavController, ViewWillEnter} from '@ionic/angular';
import {Subscription} from 'rxjs';
import {DataRoutingService} from '../../data/data-routing.service';
@Component({
selector: 'assessments-detail',
templateUrl: 'assessments-detail.html',
styleUrls: ['assessments-detail.scss'],
})
export class AssessmentsDetailComponent implements ViewWillEnter {
export class AssessmentsDetailComponent
implements ViewWillEnter, OnInit, OnDestroy
{
constructor(
readonly route: ActivatedRoute,
readonly assessmentsProvider: AssessmentsProvider,
readonly dataRoutingService: DataRoutingService,
readonly navController: NavController,
readonly activatedRoute: ActivatedRoute,
) {}
subscriptions: Subscription[] = [];
@Input() dataPathAutoRouting = true;
@ViewChild(DataDetailComponent)
detailComponent: DataDetailComponent;
ngOnInit() {
if (!this.dataPathAutoRouting) return;
this.subscriptions.push(
this.dataRoutingService.pathSelectListener().subscribe(item => {
void this.navController.navigateBack(
['assessments', 'detail', item.uid],
{
queryParams: {
token: this.activatedRoute.snapshot.queryParamMap.get('token'),
},
},
);
}),
);
}
ngOnDestroy() {
for (const sub of this.subscriptions) sub.unsubscribe();
}
getItem(event: ExternalDataLoadEvent) {
this.assessmentsProvider
.getAssessments(

View File

@@ -22,6 +22,7 @@
</ion-toolbar>
</ion-header>
<stapps-data-detail
[autoRouteDataPath]="false"
[externalData]="true"
(loadItem)="getItem($event)"
[defaultHeader]="false"

View File

@@ -0,0 +1,30 @@
/*
* 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 {SCThings} from '@openstapps/core';
@Component({
selector: 'assessments-tree-list',
templateUrl: 'assessments-tree-list.html',
styleUrls: ['assessments-tree-list.scss'],
})
export class AssessmentsTreeListComponent {
@Input() items?: Promise<SCThings[] | undefined>;
@Input() singleType = false;
@Input() groupingKey: string;
}

View File

@@ -0,0 +1,27 @@
<!--
~ 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/>.
-->
<tree-list
[items]="items"
[singleType]="singleType"
[groupingKey]="groupingKey"
>
<ng-template let-item>
<assessments-list-item
[item]="item"
[hideThumbnail]="singleType"
></assessments-list-item>
</ng-template>
</tree-list>

View File

@@ -0,0 +1,15 @@
/*!
* 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/>.
*/

View File

@@ -1,3 +1,19 @@
/*!
* 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/>.
*/
.content {
height: 100%;
padding-inline: 8px;
}

View File

@@ -1,18 +1,29 @@
<ion-note *ngIf="item.courseOfStudy as courseOfStudy">
{{ $any('courseOfStudy' | propertyNameTranslate: item) | titlecase }}:
{{ 'name' | thingTranslate: $any(courseOfStudy) }}
({{ 'academicDegree' | thingTranslate: $any(courseOfStudy) }})
</ion-note>
<!--
~ 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>
<ion-card-content>
<ion-note *ngIf="item.courseOfStudy as courseOfStudy">
{{ $any('courseOfStudy' | propertyNameTranslate: item) | titlecase }}:
{{ 'name' | thingTranslate: $any(courseOfStudy) }}
({{ 'academicDegree' | thingTranslate: $any(courseOfStudy) }})
</ion-note>
</ion-card-content>
</ion-card>
<ion-list class="container">
<ion-item lines="none">
<assessment-base-info [item]="item"></assessment-base-info>
</ion-item>
<h2 *ngIf="item.superAssessments">
{{ $any('superAssessments' | propertyNameTranslate: item) | titlecase }}
</h2>
<assessments-simple-data-list
*ngIf="item.superAssessments"
[items]="$any(item.superAssessments)"
[singleType]="true"
></assessments-simple-data-list>
</ion-list>

View File

@@ -1,5 +1,22 @@
<h2 class="name">
{{ 'name' | thingTranslate: item }}
{{ item.date ? (item.date | amDateFormat) : '' }}
</h2>
<assessment-base-info [item]="item"></assessment-base-info>
<!--
~ 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/>.
-->
<div class="container">
<h2 class="name">
{{ 'name' | thingTranslate: item }}
{{ item.date ? (item.date | amDateFormat) : '' }}
</h2>
<assessment-base-info [item]="item"></assessment-base-info>
</div>

View File

@@ -0,0 +1,41 @@
/*!
* 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 Licens 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/>.
*/
.column {
display: flex;
flex-direction: column;
}
.item {
height: 72px;
}
.tree-indicator {
width: 16px;
margin-right: 4px;
padding-left: 1px;
}
.super-assessments-list {
// prevent the list from hijacking hover overlays
z-index: -1;
:last-child {
.tree-indicator-after {
display: none;
}
}
}

View File

@@ -25,21 +25,23 @@ import {sum, sumBy} from '../../../../_helpers/collections/sum';
export class CourseOfStudyAssessmentComponent {
@Input() courseOfStudy: SCCourseOfStudyWithoutReferences | null;
_assessments: SCAssessment[];
_assessments: Promise<SCAssessment[]>;
grade = 0;
ects = 0;
@Input() set assessments(value: SCAssessment[]) {
this._assessments = value;
const assessments = value;
const grades = this._assessments
const grades = assessments
// TODO: find out if this is correct
.filter(assessment => assessment.status === 'bestanden')
.map(assessment => Number(assessment.grade))
.filter(grade => !Number.isNaN(grade));
this.grade = grades.length > 0 ? sum(grades) / grades.length : 0;
this.ects = sumBy(this._assessments, it => it.ects);
this.ects = sumBy(assessments, it => it.ects);
this._assessments = Promise.resolve(assessments);
}
}

View File

@@ -13,25 +13,25 @@
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<assessments-data-list
[singleType]="true"
[items]="_assessments"
[loading]="false"
>
<div header>
<section>
<h3>{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}</h3>
<p>
{{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}:
{{
grade
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1'
}}
</p>
<p>{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}</p>
</section>
<h3>
{{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }}
</h3>
</div>
</assessments-data-list>
<section>
<h3>{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}</h3>
<p>
{{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}:
{{
grade | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1'
}}
</p>
<p>{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}</p>
</section>
<section>
<h3>
{{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }}
</h3>
<assessments-tree-list
[items]="_assessments"
[singleType]="true"
[groupingKey]="'superAssessments'"
>
</assessments-tree-list>
</section>

View File

@@ -1,19 +1,19 @@
/*
* Copyright (C) 2021 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) 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.
* 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/>.
*/
import {Injectable} from '@angular/core';
import {SCThings} from '@openstapps/core';
import {SCThings, SCThingWithoutReferences} from '@openstapps/core';
import {Subject} from 'rxjs';
/**
@@ -28,6 +28,8 @@ export class DataRoutingService {
*/
private childSelectedEvent = new Subject<SCThings>();
private pathSelectedEvent = new Subject<SCThingWithoutReferences>();
/**
* Provides the thing that was selected
*
@@ -37,10 +39,18 @@ export class DataRoutingService {
this.childSelectedEvent.next(thing);
}
emitPathEvent(thing: SCThingWithoutReferences) {
this.pathSelectedEvent.next(thing);
}
/**
* Provides a listener for the event
*/
itemSelectListener() {
return this.childSelectedEvent.asObservable();
}
pathSelectListener() {
return this.pathSelectedEvent.asObservable();
}
}

View File

@@ -86,6 +86,8 @@ import {RoutingStackService} from '../../util/routing-stack.service';
import {DataPathComponent} from './detail/data-path.component';
import {EventRoutePathComponent} from './types/event/event-route-path.component';
import {UtilModule} from '../../util/util.module';
import {TreeListComponent} from './list/tree-list.component';
import {TreeListFragmentComponent} from './list/tree-list-fragment.component';
/**
* Module for handling data
@@ -140,6 +142,8 @@ import {UtilModule} from '../../util/util.module';
SkeletonListItemComponent,
SkeletonSegmentComponent,
SkeletonSimpleCardComponent,
TreeListComponent,
TreeListFragmentComponent,
VideoDetailContentComponent,
VideoListItemComponent,
SimpleDataListComponent,
@@ -192,6 +196,7 @@ import {UtilModule} from '../../util/util.module';
ArticleDetailContentComponent,
OriginDetailComponent,
FavoriteButtonComponent,
TreeListComponent,
],
})
export class DataModule {}

View File

@@ -61,6 +61,8 @@ export class DataDetailComponent implements ViewWillEnter {
@Input() isModal = false;
@Input() autoRouteDataPath = true;
/**
* The language of the item
*/

View File

@@ -46,7 +46,7 @@
<div [ngSwitch]="true">
<ng-container *ngSwitchCase="!item && (isDisconnected | async)">
<div class="centeredMessageContainer">
<ion-icon name="no-connection"> </ion-icon>
<ion-icon name="no-connection"></ion-icon>
<ion-label>
{{ 'data.detail.COULD_NOT_CONNECT' | translate }}
</ion-label>
@@ -54,7 +54,7 @@
</ng-container>
<ng-container *ngSwitchCase="item === null">
<div class="centeredMessageContainer">
<ion-icon name="broken-link"> </ion-icon>
<ion-icon name="broken-link"></ion-icon>
<ion-label>
{{ 'data.detail.NOT_FOUND' | translate }}
</ion-label>
@@ -65,7 +65,10 @@
<stapps-skeleton-simple-card></stapps-skeleton-simple-card>
</ng-container>
<ng-container *ngSwitchDefault>
<stapps-data-path [item]="item"></stapps-data-path>
<stapps-data-path
[item]="item"
[autoRouting]="autoRouteDataPath"
></stapps-data-path>
<stapps-data-detail-content
[item]="item"
[contentTemplateRef]="contentTemplateRef"

View File

@@ -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, OnInit} from '@angular/core';
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {RoutingStackService} from '../../../util/routing-stack.service';
import {
SCCatalog,
@@ -21,24 +21,24 @@ import {
SCThingWithoutReferences,
} from '@openstapps/core';
import {DataProvider, DataScope} from '../data.provider';
import {fromEvent, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {fromEvent, Observable, Subscription} from 'rxjs';
import {map, startWith} from 'rxjs/operators';
import {DataRoutingService} from '../data-routing.service';
import {NavController} from '@ionic/angular';
@Component({
selector: 'stapps-data-path',
templateUrl: './data-path.html',
styleUrls: ['./data-path.scss'],
})
export class DataPathComponent implements OnInit {
export class DataPathComponent implements OnInit, OnDestroy {
path: Promise<SCThingWithoutReferences[]>;
$width: Observable<number>;
ngOnInit() {
this.$width = fromEvent(window, 'resize').pipe(
map(() => window.innerWidth),
);
}
subscriptions: Subscription[] = [];
@Input() autoRouting = true;
@Input() set item(item: SCThings) {
// eslint-disable-next-line unicorn/prefer-ternary
@@ -47,6 +47,11 @@ export class DataPathComponent implements OnInit {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
resolve([...item.superCatalogs!, item]),
);
} else if (item.type === SCThingType.Assessment && item.superAssessments) {
this.path = new Promise(resolve =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
resolve([...item.superAssessments!, item]),
);
} else if (
item.type === SCThingType.AcademicEvent &&
item.catalogs &&
@@ -76,7 +81,27 @@ export class DataPathComponent implements OnInit {
}
constructor(
readonly dataRoutingService: DataRoutingService,
readonly navController: NavController,
readonly routeStack: RoutingStackService,
readonly dataProvider: DataProvider,
) {}
ngOnInit() {
this.$width = fromEvent(window, 'resize').pipe(
map(() => window.innerWidth),
startWith(window.innerWidth),
);
if (!this.autoRouting) return;
this.subscriptions.push(
this.dataRoutingService.pathSelectListener().subscribe(item => {
void this.navController.navigateBack(['data-detail', item.uid]);
}),
);
}
ngOnDestroy() {
for (const sub of this.subscriptions) sub.unsubscribe();
}
}

View File

@@ -22,8 +22,7 @@
>
<ion-breadcrumb *ngFor="let fragment of stack">
<ion-label
[routerLink]="['/data-detail', fragment.uid]"
[routerDirection]="'back'"
(click)="dataRoutingService.emitPathEvent(fragment)"
[style.max-width]="
stack.length === 1
? '100%'
@@ -42,10 +41,11 @@
<ng-template>
<ion-list>
<ion-item
button
*ngFor="let fragment of stack"
(click)="popover.dismiss()"
[routerLink]="['/data-detail', fragment.uid]"
[routerDirection]="'back'"
(click)="
dataRoutingService.emitPathEvent(fragment); popover.dismiss()
"
>{{ 'name' | thingTranslate: $any(fragment) }}</ion-item
>
</ion-list>

View File

@@ -0,0 +1,44 @@
/*
* 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, TemplateRef} from '@angular/core';
import {SCThings, SCThingWithoutReferences, SCUuid} from '@openstapps/core';
import {Tree} from '../../../_helpers/collections/tree-group';
import {DataListContext} from './data-list.component';
@Component({
selector: 'tree-list-fragment',
templateUrl: 'tree-list-fragment.html',
styleUrls: ['tree-list-fragment.scss'],
})
export class TreeListFragmentComponent {
entries?: [string, Tree<SCThings>][];
@Input() set items(items: Tree<SCThings> | undefined) {
if (!items) {
delete this.entries;
return;
}
const temporary = items._;
delete items._;
this.entries = Object.entries(items) as [string, Tree<SCThings>][];
items._ = temporary;
}
@Input() groupMap: Record<SCUuid, SCThingWithoutReferences>;
@Input() singleType = false;
@Input() listItemTemplateRef: TemplateRef<DataListContext<SCThings>>;
}

View File

@@ -0,0 +1,45 @@
<!--
~ 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-accordion-group *ngIf="entries as entries" [value]="entries[0]?.[0]">
<ion-accordion *ngFor="let entry of entries" [value]="entry[0]">
<ion-item *ngIf="groupMap[entry[0]] as header" slot="header">
<ion-label>{{ header.name }}</ion-label>
</ion-item>
<ion-list slot="content">
<ng-container *ngFor="let item of entry[1]._ || []">
<ng-container
*ngTemplateOutlet="
listItemTemplateRef || defaultListItem;
context: {$implicit: item}
"
></ng-container>
</ng-container>
<tree-list-fragment
[groupMap]="groupMap"
[items]="entry[1]"
[singleType]="singleType"
[listItemTemplateRef]="listItemTemplateRef"
></tree-list-fragment>
</ion-list>
</ion-accordion>
</ion-accordion-group>
<ng-template let-item #defaultListItem>
<stapps-data-list-item
[item]="item"
[hideThumbnail]="singleType"
></stapps-data-list-item>
</ng-template>

View File

@@ -0,0 +1,18 @@
/*!
* 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-list {
margin-left: 16px;
}

View File

@@ -0,0 +1,76 @@
/*
* 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, ContentChild, Input, TemplateRef} from '@angular/core';
import {DataListContext} from './data-list.component';
import {SCThings, SCThingWithoutReferences, SCUuid} from '@openstapps/core';
import {Tree, treeGroupBy} from '../../../_helpers/collections/tree-group';
@Component({
selector: 'tree-list',
templateUrl: 'tree-list.html',
styleUrls: ['tree-list.scss'],
})
export class TreeListComponent {
_items?: Promise<SCThings[] | undefined>;
_groupingKey?: string;
_groups?: Promise<Tree<SCThings> | undefined>;
_groupItems?: Record<SCUuid, SCThingWithoutReferences>;
@Input() set groupingKey(value: keyof SCThings | string | undefined) {
this._groupingKey = value;
this.groupItems();
}
@Input() set items(items: Promise<SCThings[] | undefined> | undefined) {
this._items = items;
this.groupItems();
}
@Input() singleType = false;
@ContentChild(TemplateRef) listItemTemplateRef: TemplateRef<
DataListContext<SCThings>
>;
groupItems() {
if (!this._items || !this._groupingKey) return;
this._groups = this._items.then(items => {
if (!items || !this._groupingKey) return;
this._groupItems = {};
for (const item of items) {
const path = (
item as unknown as Record<string, SCThingWithoutReferences[]>
)[this._groupingKey];
for (const pathFragment of path) {
this._groupItems[pathFragment.uid] = pathFragment;
}
}
const tree = treeGroupBy(items, item =>
(item as unknown as Record<string, SCThingWithoutReferences[]>)[
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this._groupingKey!
].map(thing => thing.uid),
);
return tree;
});
}
}

View File

@@ -0,0 +1,22 @@
<!--
~ 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/>.
-->
<tree-list-fragment
*ngIf="_groups | async as groups"
[items]="groups"
[groupMap]="_groupItems"
[singleType]="singleType"
[listItemTemplateRef]="listItemTemplateRef"
></tree-list-fragment>

View File

@@ -0,0 +1,15 @@
/*!
* 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/>.
*/