Removable footer, compact styling for particle creator

This commit is contained in:
akastijn 2025-12-27 21:04:55 +01:00
parent d1ff7b3f88
commit d9b60d8a94
13 changed files with 175 additions and 195 deletions

View File

@ -1,29 +0,0 @@
import {TestBed} from '@angular/core/testing';
import {AppComponent} from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have the 'frontend' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('frontend');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, frontend');
});
});

View File

@ -1,7 +1,7 @@
import {Component, OnInit} from '@angular/core';
import {Component, inject, OnInit} from '@angular/core';
import {Meta, Title} from '@angular/platform-browser';
import {ALTITUDE_VERSION} from '@custom-types/constant';
import {Router, RouterOutlet} from '@angular/router';
import {RouterOutlet} from '@angular/router';
import {FooterComponent} from '@pages/footer/footer/footer.component';
@Component({
@ -27,9 +27,8 @@ export class AppComponent implements OnInit {
ALTITUDE_VERSION + ',altitude,alttd,play,join,find,friends,friendly,simple,private,whitelist,whitelisted,creative,' +
'worldedit'
constructor(private titleService: Title, private metaService: Meta, private router: Router) {
}
private readonly titleService = inject(Title);
private readonly metaService = inject(Meta);
ngOnInit(): void {
this.titleService.setTitle(this.title)

View File

@ -1,9 +1,13 @@
@if (hideFooter()) {
} @else {
<footer>
<div class="footer">
<div class="footerInner">
<div class="footerText">
<h2>ABOUT US</h2>
<p>Altitude is a community-centered {{ ALTITUDE_VERSION }} survival server. We're one of those servers you come
<p>Altitude is a community-centered {{ ALTITUDE_VERSION }} survival server. We're one of those servers you
come
to call "home". We are your place to get together with friends and play survival, with a few extra features
suggested by our community!</p>
<div class="followUs" style="height: 35px; display: flex; align-items: flex-end;">
@ -42,3 +46,4 @@
Mojang AB or Microsoft.</p>
</div>
</footer>
}

View File

@ -1,19 +1,21 @@
import {Component} from '@angular/core';
import {Component, computed, inject} from '@angular/core';
import {ALTITUDE_VERSION} from '@custom-types/constant';
import { NgOptimizedImage } from '@angular/common';
import {FooterService} from '@services/footer.service';
import {RouterLink} from '@angular/router';
@Component({
selector: 'app-footer',
standalone: true,
imports: [
RouterLink,
NgOptimizedImage
RouterLink
],
templateUrl: './footer.component.html',
styleUrl: './footer.component.scss'
})
export class FooterComponent {
private readonly footerService: FooterService = inject(FooterService);
hideFooter = computed(() => (this.footerService.hideFooter()));
public getCurrentYear() {
return new Date().getFullYear();
}

View File

@ -1,7 +1,22 @@
<div class="card-div">
<mat-card>
<mat-card-header>
<mat-card-title>Frames</mat-card-title>
<mat-card-header class="frame-header-fullwidth">
<div class="frame-header-row" mat-card-title>
<span class="frame-title-text">Frames</span>
<div>
<ng-content></ng-content>
<button mat-icon-button color="warn" (click)="removeFrame(currentFrame)"
matTooltip="Delete {{currentFrame}}"
[disabled]="frames.length <= 1">
<mat-icon [class.can-delete]="frames.length > 1" [class.can-not-delete]="frames.length <= 1">
delete
</mat-icon>
</button>
<button mat-icon-button (click)="addFrame()" matTooltip="Add Frame">
<mat-icon class="add-button">add</mat-icon>
</button>
</div>
</div>
</mat-card-header>
<mat-card-content>
<div class="frames-container">
@ -33,21 +48,10 @@
</div>
}
</div>
<div class="frame-actions">
<button mat-raised-button color="warn" (click)="removeFrame(frameId)"
[disabled]="frames.length <= 1">
Remove Frame
</button>
</div>
</div>
</mat-tab>
}
</mat-tab-group>
<div class="add-frame">
<button mat-raised-button color="primary" (click)="addFrame()">
Add New Frame
</button>
</div>
</div>
</mat-card-content>
</mat-card>

View File

@ -6,6 +6,36 @@
padding: 15px;
}
:host ::ng-deep .mat-mdc-card-header-text {
width: 90%;
text-align: center;
}
.frame-header-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.frame-title-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.add-button {
color: green
}
.can-delete {
color: red;
}
.can-not-delete {
color: gray;
}
.particles-list {
height: 550px;
overflow-y: auto;

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FramesComponent } from './frames.component';
describe('FramesComponent', () => {
let component: FramesComponent;
let fixture: ComponentFixture<FramesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FramesComponent]
})
.compileComponents();
fixture = TestBed.createComponent(FramesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,5 +1,5 @@
import {Component} from '@angular/core';
import {MatButton, MatIconButton} from "@angular/material/button";
import {Component, inject} from '@angular/core';
import {MatIconButton} from "@angular/material/button";
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
import {MatTab, MatTabGroup} from "@angular/material/tabs";
@ -7,11 +7,11 @@ import {ParticleData} from '../../models/particle.model';
import {MatIcon} from '@angular/material/icon';
import {ParticleManagerService} from '../../services/particle-manager.service';
import {FrameManagerService} from '../../services/frame-manager.service';
import {MatTooltipModule} from '@angular/material/tooltip';
@Component({
selector: 'app-frames',
imports: [
MatButton,
MatCard,
MatCardContent,
MatCardHeader,
@ -19,17 +19,15 @@ import {FrameManagerService} from '../../services/frame-manager.service';
MatIcon,
MatIconButton,
MatTab,
MatTabGroup
MatTabGroup,
MatTooltipModule,
],
templateUrl: './frames.component.html',
styleUrl: './frames.component.scss'
})
export class FramesComponent {
constructor(
private particleManagerService: ParticleManagerService,
private frameManagerService: FrameManagerService) {
}
private particleManagerService = inject(ParticleManagerService);
private frameManagerService = inject(FrameManagerService);
/**
* Get the particle data

View File

@ -6,7 +6,8 @@
</app-header>
<main>
<section class="darkmodeSection">
<app-full-size [hideFooter]="true">
<section class="darkmodeSection full-height">
<section class="column">
<div class="renderer-section column">
<div class="flex row">
@ -25,16 +26,15 @@
</div>
<div class="flex side-column">
<app-particle></app-particle>
<app-frames></app-frames>
<div>
<button mat-fab extended (click)="copyJson()">
<app-frames>
<button mat-icon-button matTooltip="Copy JSON to clipboard" (click)="copyJson()">
<mat-icon>content_copy</mat-icon>
Copy JSON to clipboard
</button>
</div>
</app-frames>
</div>
</div>
</div>
</section>
</section>
</app-full-size>
</main>

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ParticlesComponent } from './particles.component';
describe('ParticlesComponent', () => {
let component: ParticlesComponent;
let fixture: ComponentFixture<ParticlesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ParticlesComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ParticlesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,4 +1,4 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {Component, ElementRef, inject, ViewChild} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatButtonModule} from '@angular/material/button';
@ -23,6 +23,8 @@ import {FramesComponent} from './components/frames/frames.component';
import {MatSnackBar} from '@angular/material/snack-bar';
import {RenderContainerComponent} from './components/render-container/render-container.component';
import {ParticlesService} from '@api';
import {MatTooltipModule} from '@angular/material/tooltip';
import {FullSizeComponent} from '@shared-components/full-size/full-size.component';
@Component({
selector: 'app-particles',
@ -43,7 +45,9 @@ import {ParticlesService} from '@api';
PropertiesComponent,
ParticleComponent,
FramesComponent,
RenderContainerComponent
RenderContainerComponent,
MatTooltipModule,
FullSizeComponent
],
templateUrl: './particles.component.html',
styleUrl: './particles.component.scss'
@ -51,13 +55,10 @@ import {ParticlesService} from '@api';
export class ParticlesComponent {
@ViewChild('planeSlider') planeSlider!: ElementRef;
constructor(
private intersectionPlaneService: IntersectionPlaneService,
private particleManagerService: ParticleManagerService,
private matSnackBar: MatSnackBar,
private particlesService: ParticlesService,
) {
}
private readonly intersectionPlaneService = inject(IntersectionPlaneService);
private readonly particleManagerService = inject(ParticleManagerService);
private readonly matSnackBar = inject(MatSnackBar);
private readonly particlesService = inject(ParticlesService);
/**
* Update plane position based on slider

View File

@ -0,0 +1,10 @@
import {Injectable, signal} from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class FooterService {
public hideFooter = signal<boolean>(false);
}

View File

@ -1,4 +1,5 @@
import {AfterViewInit, Component, ElementRef, Input, OnDestroy, Renderer2} from '@angular/core';
import {AfterViewInit, Component, ElementRef, inject, input, OnDestroy, OnInit, Renderer2} from '@angular/core';
import {FooterService} from '@services/footer.service';
@Component({
selector: 'app-full-size',
@ -7,17 +8,20 @@ import {AfterViewInit, Component, ElementRef, Input, OnDestroy, Renderer2} from
templateUrl: './full-size.component.html',
styleUrl: './full-size.component.scss'
})
export class FullSizeComponent implements AfterViewInit, OnDestroy {
export class FullSizeComponent implements OnInit, AfterViewInit, OnDestroy {
private resizeObserver: ResizeObserver | null = null;
private boundHandleResize: any;
// Optional extra offset in pixels to subtract from available height
@Input() extraOffset: number = 0;
extraOffset = input<number>(0);
hideFooter = input<boolean>(false);
constructor(
private elementRef: ElementRef,
private renderer: Renderer2
) {
private readonly elementRef = inject(ElementRef);
private readonly renderer = inject(Renderer2);
private readonly footerService = inject(FooterService);
ngOnInit(): void {
this.footerService.hideFooter.set(this.hideFooter());
}
ngAfterViewInit(): void {
@ -40,6 +44,8 @@ export class FullSizeComponent implements AfterViewInit, OnDestroy {
if (this.boundHandleResize) {
window.removeEventListener('resize', this.boundHandleResize);
}
this.footerService.hideFooter.set(false);
}
private handleResize() {
@ -70,9 +76,9 @@ export class FullSizeComponent implements AfterViewInit, OnDestroy {
if (container) {
const headerHeight = headerElement ? headerElement.getBoundingClientRect().height : 0;
const footerHeight = footerElement ? footerElement.getBoundingClientRect().height : 0;
const footerHeight = this.hideFooter() ? 0 : (footerElement ? footerElement.getBoundingClientRect().height : 0);
const totalOffset = headerHeight + footerHeight + (this.extraOffset || 0);
const totalOffset = headerHeight + footerHeight + (this.extraOffset() || 0);
const calculatedHeight = `calc(100vh - ${totalOffset}px)`;
this.renderer.setStyle(container, 'height', calculatedHeight);
}