Removable footer, compact styling for particle creator
This commit is contained in:
parent
d1ff7b3f88
commit
d9b60d8a94
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
<footer>
|
||||
@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;">
|
||||
|
|
@ -41,4 +45,5 @@
|
|||
<p class="copyright">Copyright © 2015-{{ getCurrentYear() }} Altitude. All rights Reserved. Not affiliated with
|
||||
Mojang AB or Microsoft.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</footer>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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,21 +45,20 @@ import {ParticlesService} from '@api';
|
|||
PropertiesComponent,
|
||||
ParticleComponent,
|
||||
FramesComponent,
|
||||
RenderContainerComponent
|
||||
],
|
||||
RenderContainerComponent,
|
||||
MatTooltipModule,
|
||||
FullSizeComponent
|
||||
],
|
||||
templateUrl: './particles.component.html',
|
||||
styleUrl: './particles.component.scss'
|
||||
})
|
||||
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
|
||||
|
|
|
|||
10
frontend/src/app/services/footer.service.ts
Normal file
10
frontend/src/app/services/footer.service.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {Injectable, signal} from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FooterService {
|
||||
|
||||
public hideFooter = signal<boolean>(false);
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user