Carousel improvements, ability to customize number of visible zones and offset.

merge-notes
isark 2 years ago
parent 70cdb6b3b0
commit d010d6dde7

@ -1,6 +1,6 @@
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
import { NgStyle } from '@angular/common';
import { AfterViewInit, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, TemplateRef, ViewChild, ViewChildren } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AngularResizeEventModule, ResizedEvent } from 'angular-resize-event';
import { Subject, debounceTime } from 'rxjs';
@ -33,10 +33,9 @@ import { Subject, debounceTime } from 'rxjs';
])
]
})
export class CarouselComponent<T> implements OnInit, AfterViewInit {
export class CarouselComponent<T> implements OnInit, AfterViewInit, OnChanges {
@Output() afterInitSelf: EventEmitter<CarouselComponent<T>> = new EventEmitter<CarouselComponent<T>>();
@Output() changedIndex: EventEmitter<number> = new EventEmitter<number>();
@Input() slides?: T[];
@ContentChild(TemplateRef) template?: TemplateRef<any>;
@ViewChild('carouselWindow') window!: ElementRef;
@ -45,6 +44,7 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
@Input() initIndex?: number;
current: number = 0;
intersectionObserver?: IntersectionObserver;
visibleSlides?: IntersectingSlide[];
increasing: boolean = true;
@ -53,12 +53,10 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
animation: string = 'vertical';
directionTime: number = 0;
@Input() numVisible: number = 1;
@Input() offset: number = 0;
containerDirectionLength: number = 0;
private debouncedOnchange: Subject<void> = new Subject<void>();
constructor(private cdr: ChangeDetectorRef) {
this.visibleSlides = [];
this.debouncedOnchange.pipe(debounceTime(500)).subscribe(() => this.realOnChange());
@ -67,9 +65,9 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
}
}
ngOnInit(): void {
this.afterInitSelf.next(this);
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
const runIntersectionHandling = () => {
@ -77,38 +75,35 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
if (!entryIndex && entryIndex != 0) {
return;
}
const entryIntersectingSlide = this.visibleSlides?.find((s) => {
return s.index == entryIndex;
});
const entryIntersectingSlide = this.visibleSlides?.find(s => s.index == entryIndex);
if (!entryIntersectingSlide) {
return;
}
entryIntersectingSlide.currentlyIntersecting = entry.isIntersecting;
if (entryIntersectingSlide.hasBeenVisible && !entry.isIntersecting) {
this.visibleSlides = this.visibleSlides?.filter(e => e.index != entryIndex);
this.intersectionObserver?.unobserve(entry.target);
}
};
runIntersectionHandling();
this.onChange();
})
})
}
numExtraNext() {
return Math.floor((this.numVisible - 1) / 2);
return this.numVisible + this.offset - 1;
}
numExtraPrev() {
return Math.ceil((this.numVisible - 1) / 2);
return 0 + this.offset;
}
ngAfterViewInit(): void {
ngOnChanges(changes: SimpleChanges): void {
if(changes['numVisible'] || changes['offset']) {
this.reinitializeVisibleSlides();
}
}
ngAfterViewInit(): void {
this.slideElements.changes.subscribe((comps: QueryList<ElementRef>) => {
comps.forEach((comp) => this.handleNewDomSlide(comp))
});
@ -123,7 +118,6 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
for (let i = 0; i <= this.numExtraNext() && i < this.slides!.length; i++) {
this.visibleSlides?.push({
index: i,
hasBeenVisible: false,
currentlyIntersecting: false,
})
}
@ -135,20 +129,19 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
setIndex(slideIndex: number) {
this.current = slideIndex;
console.log("setIndex", slideIndex);
this.visibleSlides!.length = 0;
console.log("length", this.slides!.length);
this.reinitializeVisibleSlides();
this.onChange();
}
for (let i = -this.numExtraPrev(); i <= this.numExtraNext(); i++) {
console.log("i, ", i);
if (this.current + i >= 0 && this.current + i < this.slides!.length) {
console.log("pushing", this.current + i);
reinitializeVisibleSlides() {
this.visibleSlides!.length = 0;
const start = Math.max(0, this.current - this.numExtraPrev());
const end = Math.min(this.current + this.numExtraNext(), this.slides!.length - 1);
for (let i = start; i <= end; i++) {
this.visibleSlides?.push({
index: this.current + i,
hasBeenVisible: false,
index: i,
currentlyIntersecting: false,
});
}
}
this.onChange();
@ -158,12 +151,10 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
this.increasing = true;
if (this.slides && this.current + 1 < this.slides?.length) {
this.current += 1;
this.changedIndex.emit(this.current);
if (this.current + this.numExtraNext() < this.slides.length) {
if (!this.visibleSlides?.find(e => e.index == this.current + this.numExtraNext())) {
this.visibleSlides?.push({
index: this.current + this.numExtraNext(),
hasBeenVisible: false,
currentlyIntersecting: false,
});
}
@ -176,13 +167,10 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
this.increasing = false;
if (this.current - 1 >= 0) {
this.current -= 1;
this.changedIndex.emit(this.current);
if (this.current - this.numExtraPrev() >= 0) {
if (!this.visibleSlides?.find(e => e.index == this.current - this.numExtraPrev())) {
this.visibleSlides?.push({
index: this.current - this.numExtraPrev(),
hasBeenVisible: false,
currentlyIntersecting: false,
});
}
@ -196,47 +184,36 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
}
realOnChange() {
const safetyFactor = this.numVisible == 1 ? 1 : 2;
{
const intersecting = this.visibleSlides?.filter(e => e.currentlyIntersecting).sort((e1, e2) => e1.index - e2.index);
if (intersecting && intersecting.length > 0) {
const lowestIntersecting = intersecting![0];
this.visibleSlides = this.visibleSlides?.filter(e => e.index + safetyFactor >= lowestIntersecting!.index && e.index >= this.current - this.numExtraPrev());
}
}
{
const safetyFactor = (this.numVisible == 1 ? 1 : 2);
const intersecting = this.visibleSlides?.filter(e => e.currentlyIntersecting).sort((e1, e2) => e1.index - e2.index);
if (intersecting && intersecting.length > 0) {
const lowestIntersecting = intersecting![0];
const highestIntersecting = intersecting![intersecting!.length - 1];
const intersecting = this.visibleSlides?.filter(e => e.currentlyIntersecting).sort((e1, e2) => e1.index - e2.index).reverse();
if (intersecting && intersecting.length > 0) {
const highestIntersecting = intersecting![0]
this.visibleSlides = this.visibleSlides?.filter(e => e.index - safetyFactor <= highestIntersecting!.index && e.index <= this.current + this.numExtraNext());
}
const min = Math.min(lowestIntersecting.index - safetyFactor, this.current - this.numExtraPrev());
const max = Math.max(highestIntersecting.index + safetyFactor, this.current + this.numExtraNext());
this.visibleSlides = this.visibleSlides?.filter(e => e.index >= min && e.index <= max);
}
}
translation() {
let num = (this.current - this.numExtraNext() - (this.numVisible % 2 == 0 ? 1 : 0)) * (-1 / this.numVisible) * 100;
const step = this.containerDirectionLength / this.numVisible;
const pos = (this.current - this.numExtraNext() - (this.numVisible % 2 == 0 ? 1 : 0));
const offset = pos * -step;
const translation = -((this.current - this.offset) * step);
if (this.vertical) {
return `0 ${offset}px`
return `0 ${translation}px`
} else {
return `${offset}px 0`
return `${translation}px 0`
}
}
templateValue() {
const len = this.slides?.length;
return `repeat(${len}, ${this.containerDirectionLength / this.numVisible}px)`;
}
style() {
let style: any = {
};
let style: any = {};
if (this.vertical) {
style['grid-template-rows'] = this.templateValue();
@ -281,11 +258,9 @@ export class CarouselComponent<T> implements OnInit, AfterViewInit {
this.containerDirectionLength = event.newRect.width;
}
}
}
interface IntersectingSlide {
index: number;
hasBeenVisible: boolean;
currentlyIntersecting: boolean;
}

@ -1,13 +1,15 @@
<ng-container *ngIf="init">
<div #globalTopLeft style="position: fixed; top: 0; left: 0; z-index: -1;"></div>
<ng-container *ngIf="rect && planService.currentPlan">
<div class="target waypoint trial" [style.background-color]="backgroundColor ? backgroundColor : 'rgba(0, 0, 0, 0.1)'"
<div class="target waypoint trial"
[style.background-color]="backgroundColor ? backgroundColor : 'rgba(0, 0, 0, 0.1)'"
[style.transform]="transform()" [style.width]="rect.width + 'px'" [style.height]="rect.height + 'px'"
[class]="specialClasses()" (wheel)="onScroll($event)" #targetRef>
<ng-container *ngIf="planService.currentPlan">
<span hidden>{{planService.currentPlan.current}}</span>
<carousel [initIndex]="planService.currentPlan.current" [numVisible]="3"
[slides]="planService.currentPlan.plan" (afterInitSelf)="registerZoneSlides($event)">
<carousel [initIndex]="planService.currentPlan.current" [numVisible]="configService.config.numVisible"
[offset]="clampedOffset()" [slides]="planService.currentPlan.plan"
(afterInitSelf)="registerZoneSlides($event)">
<ng-template let-slide let-index="index">
<div class="zone-slide" [style.color]="configService.config.noteDefaultFg"
[style.border]="index == planService.currentPlan.current ? '1px white solid' : 'none'">
@ -35,6 +37,10 @@
<button mat-icon-button *ngIf="overlayService.interactable" class="settings-button"
(click)="settingsClick($event)" (mousedown)="$event.stopPropagation()"><img
src="assets/material-settings.svg"></button>
<!-- <div *ngIf="overlayService.interactable" #tooltip="matTooltip" matTooltip="(T) = Trial in zone<br> (W) = Waypoint in zone. <br> Border around plan window will light up in the same color"
[matTooltipPosition]="'below'" matTooltipHideDelay="1000" class="help-button">
<img src="assets/material-help.svg">
</div> -->
</div>
<ngx-moveable #moveable [target]="targetRef" [draggable]="draggable && overlayService.interactable"

@ -115,7 +115,12 @@ notes {
position: absolute;
top: 0;
right: 0;
}
.help-button {
position: absolute;
top: 0;
left: 0;
}

@ -1,21 +1,17 @@
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, NgZone, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, Input, NgZone, OnInit, ViewChild } from '@angular/core';
import { NgxMoveableComponent, OnDragEnd, OnResize, OnResizeEnd } from 'ngx-moveable';
import { OnDrag } from 'ngx-moveable';
import { ConfigService } from '../_services/config.service';
import { Rect } from '../_models/generated/Rect';
import { Color } from '../color-picker/color-picker.component';
import { ShortcutService } from '../_services/shortcut.service';
import { CarouselComponent } from '../carousel/carousel.component';
import { PlanService } from '../_services/plan.service';
import { Plan, PlanElement } from '../_models/plan';
import { WorldAreaService } from '../_services/world-area.service';
import { WorldArea } from '../_models/world-area';
import { NotesComponent } from '../editor/notes/notes.component';
import { ResizedEvent } from 'angular-resize-event';
import { from } from 'rxjs';
import { open } from '@tauri-apps/api/dialog';
import { OverlayRef } from '@angular/cdk/overlay';
import { OverlayService, StateEvent } from '../_services/overlay.service';
import { OverlayService } from '../_services/overlay.service';
import { appWindow } from '@tauri-apps/api/window';
import { EventsService } from '../_services/events.service';
@ -25,23 +21,12 @@ import { EventsService } from '../_services/events.service';
styleUrls: ['./plan-display.component.scss']
})
export class PlanDisplayComponent implements AfterViewInit, OnInit {
specialClasses() {
const waypoint = this.hasWaypoint() ? 'active' : '';
const trial = this.hasTrial() ? 'trial-active' : '';
return `${waypoint} ${trial}`;
}
@Input() backgroundColor?: String;
draggable: boolean = true;
rect?: Rect;
bounds: any = { "left": 0, "top": 0, "right": 0, "bottom": 0, "position": "css" };
@ViewChild("moveable") moveable?: NgxMoveableComponent;
// slides: PlanElement[] = [];
slideIndex: number = 0;
zoneSlides?: CarouselComponent<PlanElement>;
currentSlides?: CarouselComponent<PlanElement>;
@ -57,16 +42,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
})
});
// const test = this.events.listen<StateEvent>("OverlayStateChange").subscribe(event => {
// if (!this.hasAttachedOnce) {
// this.hasAttachedOnce = true;
// test.unsubscribe();
// if (!event.payload.Hidden)
// overlayService.setInteractable();
// }
// });
appWindow.listen("entered", (entered) => {
if (this.planService.currentPlan) {
const current = this.planService.currentPlan.current;
@ -175,6 +150,7 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
this.zoneSlides = carousel;
this.zoneSlides.setIndex(this.slideIndex);
}
registerCurrentSlides(carousel: CarouselComponent<PlanElement>) {
this.currentSlides = carousel;
this.currentSlides.setIndex(this.slideIndex);
@ -201,11 +177,9 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
this.slideIndex = index;
if (this.currentSlides) {
this.currentSlides.setIndex(index);
}
if (this.zoneSlides) {
this.zoneSlides.setIndex(index);
}
}
@ -215,31 +189,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
});
}
onResizeNote(noteSlide: NotesComponent) {
if (!noteSlide.ref) { return; }
let bounds = noteSlide.ref.nativeElement.getBoundingClientRect();
const children = noteSlide.ref.nativeElement.children;
let sumWidth = 0;
let sumHeight = 0;
for (let child of children) {
const c = child.getBoundingClientRect();
sumWidth += c.width;
sumHeight += c.height;
}
const scale = Math.min(
sumWidth / bounds.width,
sumHeight / bounds.height,
)
for (let child of children) {
child.style.transform = `scale(1, ${scale})`;
}
}
settingsClick(event: any) {
this.settingsOpen = !this.settingsOpen;
event.stopPropagation();
@ -277,10 +226,6 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
})
}
enumeratedPlans() {
}
onScroll(event: WheelEvent) {
if (event.deltaY < 0) {
this.prev();
@ -288,4 +233,14 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
this.next();
}
}
specialClasses() {
const waypoint = this.hasWaypoint() ? 'active' : '';
const trial = this.hasTrial() ? 'trial-active' : '';
return `${waypoint} ${trial}`;
}
clampedOffset(): number {
return Math.min(this.configService.config.numVisible - 1, this.configService.config.offset);
}
}

@ -12,6 +12,7 @@ import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatListModule } from '@angular/material/list';
import { ScalableComponent } from '../Scalable/scalable.component';
import {MatTooltipModule} from '@angular/material/tooltip';
@NgModule({
declarations: [
PlanDisplayComponent
@ -29,7 +30,8 @@ import { ScalableComponent } from '../Scalable/scalable.component';
MatButtonModule,
MatIconModule,
MatListModule,
ScalableComponent
ScalableComponent,
MatTooltipModule
],
exports: [
PlanDisplayComponent

@ -6,17 +6,22 @@
</h1>
</div>
<div class="row">
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.planBg" (color)="onPlanColorChange($event)">Plan window
background</color-picker>
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.planBg"
(color)="onPlanColorChange($event)">
Plan window background
</color-picker>
</div>
<div class="row">
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.backdropBg" (color)="onBackdropColorChange($event)">Overlay
backdrop color</color-picker>
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.backdropBg"
(color)="onBackdropColorChange($event)">
Overlay backdrop color
</color-picker>
</div>
<div class="row">
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.noteDefaultFg" (color)="onNoteColorChange($event)">Overlay
default
font color</color-picker>
<color-picker [placement]="{color: 'bottom'}" [setColor]="configService.config.noteDefaultFg"
(color)="onNoteColorChange($event)">
Overlay default font color
</color-picker>
</div>
<div class="d-flex flex-row justify-content-between">
@ -25,15 +30,26 @@
[(ngModel)]="configService.config.hideOnUnfocus"></mat-slide-toggle>
</div>
<div class="row">
<button mat-raised-button color="primary" (click)="rebindOverlayToggle()">Overlay toggle shortcut:
{{configService.config.toggleOverlay}}</button>
<button mat-raised-button color="primary" (click)="rebindOverlayToggle()">
Overlay toggle shortcut: {{configService.config.toggleOverlay}}
</button>
</div>
<div class="row">
<button mat-raised-button color="primary" (click)="rebindPreviousZone()">Previous zone shortcut:
{{configService.config.prev}}</button>
<button mat-raised-button color="primary" (click)="rebindPreviousZone()">
Previous zone shortcut: {{configService.config.prev}}
</button>
</div>
<div class="row">
<button mat-raised-button color="primary" (click)="rebindNextZone()">Next zone shortcut:
{{configService.config.next}}</button>
<button mat-raised-button color="primary" (click)="rebindNextZone()">
Next zone shortcut: {{configService.config.next}}
</button>
</div>
<!-- TODO: Better visuals :D -->
<div class="row">
<input type="number" [(ngModel)]="configService.config.numVisible" min="1" max="10" step="1">
</div>
<div class="row">
<input type="number" [(ngModel)]="configService.config.offset" min="0" [max]="configService.config.numVisible - 1" step="1">
</div>
</div>

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0z" fill="none"/><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>

After

Width:  |  Height:  |  Size: 400 B

Loading…
Cancel
Save