diff --git a/src/app/modules/schedule/page/grid/range-overlap.spec.ts b/src/app/modules/schedule/page/grid/range-overlap.spec.ts
new file mode 100644
index 00000000..4ca97633
--- /dev/null
+++ b/src/app/modules/schedule/page/grid/range-overlap.spec.ts
@@ -0,0 +1,153 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/*
+ * 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 .
+ */
+import {groupRangeOverlaps} from './range-overlap';
+import {shuffle} from 'lodash-es';
+
+interface SimpleRange {
+ starty: number;
+ endy: number;
+}
+
+const from = (range: SimpleRange) => range.starty;
+const till = (range: SimpleRange) => range.endy;
+
+describe('RangeOverlaps', () => {
+ it('should handle empty ranges', () => {
+ expect(groupRangeOverlaps([], from, till)).toEqual([]);
+ });
+
+ it('should handle single range', () => {
+ expect(
+ groupRangeOverlaps([{starty: 0, endy: 1}], from, till),
+ ).toEqual([{start: 0, end: 1, elements: [{starty: 0, endy: 1}]}]);
+ });
+
+ it('should handle two non-overlapping ranges', () => {
+ expect(
+ groupRangeOverlaps(
+ shuffle([
+ {starty: 0, endy: 1},
+ {starty: 2, endy: 3},
+ ]),
+ from,
+ till,
+ ),
+ ).toEqual([
+ {start: 0, end: 1, elements: [{starty: 0, endy: 1}]},
+ {start: 2, end: 3, elements: [{starty: 2, endy: 3}]},
+ ]);
+ });
+
+ it('should not overlap two directly adjacent ranges', () => {
+ expect(
+ groupRangeOverlaps(
+ shuffle([
+ {starty: 0, endy: 1},
+ {starty: 1, endy: 2},
+ ]),
+ from,
+ till,
+ ),
+ ).toEqual([
+ {start: 0, end: 1, elements: [{starty: 0, endy: 1}]},
+ {start: 1, end: 2, elements: [{starty: 1, endy: 2}]},
+ ]);
+ });
+
+ it('should handle two overlapping ranges', () => {
+ expect(
+ groupRangeOverlaps(
+ shuffle([
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ ]),
+ from,
+ till,
+ ),
+ ).toEqual([
+ {
+ start: 0,
+ end: 3,
+ elements: [
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ ],
+ },
+ ]);
+ });
+
+ it('should handle multiple overlapping ranges', () => {
+ expect(
+ groupRangeOverlaps(
+ shuffle([
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ {starty: 2, endy: 4},
+ {starty: 3, endy: 5},
+ ]),
+ from,
+ till,
+ ),
+ ).toEqual([
+ {
+ start: 0,
+ end: 5,
+ elements: [
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ {starty: 2, endy: 4},
+ {starty: 3, endy: 5},
+ ],
+ },
+ ]);
+ });
+
+ it('should handle two groups of three overlapping ranges each', () => {
+ expect(
+ groupRangeOverlaps(
+ shuffle([
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ {starty: 2, endy: 4},
+ {starty: 5, endy: 7},
+ {starty: 6, endy: 8},
+ {starty: 7, endy: 9},
+ ]),
+ from,
+ till,
+ ),
+ ).toEqual([
+ {
+ start: 0,
+ end: 4,
+ elements: [
+ {starty: 0, endy: 2},
+ {starty: 1, endy: 3},
+ {starty: 2, endy: 4},
+ ],
+ },
+ {
+ start: 5,
+ end: 9,
+ elements: [
+ {starty: 5, endy: 7},
+ {starty: 6, endy: 8},
+ {starty: 7, endy: 9},
+ ],
+ },
+ ]);
+ });
+});
diff --git a/src/app/modules/schedule/page/grid/range-overlap.ts b/src/app/modules/schedule/page/grid/range-overlap.ts
new file mode 100644
index 00000000..464d76a7
--- /dev/null
+++ b/src/app/modules/schedule/page/grid/range-overlap.ts
@@ -0,0 +1,86 @@
+/*
+ * 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 .
+ */
+import {flatMap, max, min, partition} from 'lodash-es';
+
+export interface RangeInfo {
+ elements: T[];
+ start: number;
+ end: number;
+}
+
+/**
+ * Takes a list of ranges and groups by overlaps.
+ */
+export function groupRangeOverlaps(
+ ranges: T[],
+ start: (range: T) => number,
+ end: (range: T) => number,
+): RangeInfo[] {
+ return internalGroupRangeOverlaps(
+ ranges
+ .sort((a, b) => start(a) - start(b))
+ .map(range => ({
+ elements: [range],
+ start: start(range),
+ end: end(range),
+ })),
+ )
+ .map(range => ({
+ ...range,
+ elements: range.elements.sort((a, b) => start(a) - start(b)),
+ }))
+ .sort((a, b) => a.start - b.start);
+}
+
+/**
+ *
+ */
+function within(a: number, b: number, c: number): boolean {
+ return a > b && a < c;
+}
+
+/**
+ *
+ */
+function hasOverlap(a1: number, b1: number, a2: number, b2: number): boolean {
+ return within(a1, a2, b2) || within(b1, a2, b2);
+}
+
+/**
+ * Takes a list of ranges and groups by overlaps.
+ */
+function internalGroupRangeOverlaps(input: RangeInfo[]): RangeInfo[] {
+ const result: RangeInfo[] = [];
+ let ranges = [...input];
+ let cumulativeReorders = 0;
+ while (ranges.length > 0) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const range = ranges.pop()!;
+ const [overlaps, rest] = partition(ranges, r =>
+ hasOverlap(range.start, range.end, r.start, r.end),
+ );
+ cumulativeReorders += overlaps.length;
+ ranges = rest;
+ const elements = [range, ...overlaps];
+ result.push({
+ elements: flatMap(elements, 'elements'),
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ start: min(elements.map(it => it.start))!,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ end: max(elements.map(it => it.end))!,
+ });
+ }
+ return cumulativeReorders === 0 ? result : internalGroupRangeOverlaps(result);
+}
diff --git a/src/app/modules/schedule/page/grid/schedule-card.scss b/src/app/modules/schedule/page/grid/schedule-card.scss
index 149599fe..3eb7c5a9 100644
--- a/src/app/modules/schedule/page/grid/schedule-card.scss
+++ b/src/app/modules/schedule/page/grid/schedule-card.scss
@@ -1,25 +1,12 @@
ion-card {
+ width: inherit;
z-index: 2;
- ion-grid {
- padding: 0;
- margin: 0;
-
- ion-row {
- ion-col {
- height: 5px;
- padding: 0;
- margin: 0;
- }
- }
- }
-
ion-card-header {
height: available;
- width: 100%;
ion-card-title {
- width: 100%;
+ overflow-wrap: break-word;
}
}
diff --git a/src/app/modules/schedule/page/grid/schedule-day.component.ts b/src/app/modules/schedule/page/grid/schedule-day.component.ts
index 77b8e736..f4c0d190 100644
--- a/src/app/modules/schedule/page/grid/schedule-day.component.ts
+++ b/src/app/modules/schedule/page/grid/schedule-day.component.ts
@@ -18,6 +18,7 @@ import {Range, ScheduleEvent} from '../schema/schema';
import {ScheduleProvider} from '../../../calendar/schedule.provider';
import {SCISO8601Duration, SCUuid} from '@openstapps/core';
import {materialFade} from '../../../../animation/material-motion';
+import {groupRangeOverlaps} from './range-overlap';
@Component({
selector: 'schedule-day',
@@ -36,14 +37,20 @@ export class ScheduleDayComponent {
@Input() frequencies?: SCISO8601Duration[];
- @Input() dateSeries?: Record;
+ dateSeriesGroups?: ScheduleEvent[][];
+
+ @Input() set dateSeries(value: Record) {
+ if (!value) return;
+
+ this.dateSeriesGroups = groupRangeOverlaps(
+ Object.values(value),
+ it => it.time.start,
+ it => it.time.start + moment.duration(it.time.duration).asHours(),
+ ).map(it => it.elements);
+ }
constructor(protected readonly scheduleProvider: ScheduleProvider) {}
- // ngOnInit() {
- // this.dateSeries = this.fetchDateSeries();
- // }
-
// TODO: backend bug results in the wrong date series being returned
/* async fetchDateSeries(): Promise {
const dateSeries = await this.scheduleProvider.getDateSeries(
diff --git a/src/app/modules/schedule/page/grid/schedule-day.html b/src/app/modules/schedule/page/grid/schedule-day.html
index 7d78a1f2..cd97ad40 100644
--- a/src/app/modules/schedule/page/grid/schedule-day.html
+++ b/src/app/modules/schedule/page/grid/schedule-day.html
@@ -13,6 +13,18 @@
~ this program. If not, see .
-->
diff --git a/src/app/modules/schedule/page/grid/schedule-day.scss b/src/app/modules/schedule/page/grid/schedule-day.scss
index 7a7cd89a..40640931 100644
--- a/src/app/modules/schedule/page/grid/schedule-day.scss
+++ b/src/app/modules/schedule/page/grid/schedule-day.scss
@@ -14,12 +14,7 @@
*/
.schedule-card {
- position: absolute;
- top: 13px;
- left: 0;
- z-index: 4;
-
- width: 100%;
+ overflow: hidden;
}
div {
@@ -27,6 +22,22 @@ div {
width: 100%;
}
+.horizontal-group {
+ position: absolute;
+ top: 13px;
+ left: 0;
+ grid-column: 1;
+ grid-row: 1;
+ width: 100%;
+
+ box-sizing: border-box;
+ max-width: inherit;
+
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+}
+
.vertical-line {
position: absolute;
top: 0;