feat: Theme engine
big changes
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
computed,
|
||||
inject,
|
||||
input,
|
||||
output,
|
||||
viewChild
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ThemeGridEditorItem,
|
||||
ThemeGridRect,
|
||||
ThemeLayoutContainerDefinition
|
||||
} from '../../domain/theme.models';
|
||||
|
||||
type DragMode = 'move' | 'resize';
|
||||
|
||||
interface DragState {
|
||||
key: string;
|
||||
mode: DragMode;
|
||||
startClientX: number;
|
||||
startClientY: number;
|
||||
startGrid: ThemeGridRect;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-theme-grid-editor',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './theme-grid-editor.component.html',
|
||||
styleUrl: './theme-grid-editor.component.scss'
|
||||
})
|
||||
export class ThemeGridEditorComponent {
|
||||
readonly container = input.required<ThemeLayoutContainerDefinition>();
|
||||
readonly items = input.required<ThemeGridEditorItem[]>();
|
||||
readonly selectedKey = input<string | null>(null);
|
||||
readonly disabled = input(false);
|
||||
|
||||
readonly itemChanged = output<{ key: string; grid: ThemeGridRect }>();
|
||||
readonly itemSelected = output<string>();
|
||||
|
||||
private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);
|
||||
private dragState: DragState | null = null;
|
||||
|
||||
readonly canvasRef = viewChild.required<ElementRef<HTMLElement>>('canvasRef');
|
||||
readonly frameStyle = computed(() => ({
|
||||
'--theme-grid-columns': `${this.container().columns}`,
|
||||
'--theme-grid-rows': `${this.container().rows}`
|
||||
}));
|
||||
|
||||
itemStyle(item: ThemeGridEditorItem): Record<string, string> {
|
||||
const { columns, rows } = this.container();
|
||||
|
||||
return {
|
||||
left: `${(item.grid.x / columns) * 100}%`,
|
||||
top: `${(item.grid.y / rows) * 100}%`,
|
||||
width: `${(item.grid.w / columns) * 100}%`,
|
||||
height: `${(item.grid.h / rows) * 100}%`
|
||||
};
|
||||
}
|
||||
|
||||
selectItem(key: string): void {
|
||||
this.itemSelected.emit(key);
|
||||
}
|
||||
|
||||
startMove(event: PointerEvent, item: ThemeGridEditorItem): void {
|
||||
this.startDrag(event, item, 'move');
|
||||
}
|
||||
|
||||
startResize(event: PointerEvent, item: ThemeGridEditorItem): void {
|
||||
this.startDrag(event, item, 'resize');
|
||||
}
|
||||
|
||||
@HostListener('document:pointermove', ['$event'])
|
||||
onPointerMove(event: PointerEvent): void {
|
||||
if (!this.dragState || this.disabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvasRect = this.canvasRef().nativeElement.getBoundingClientRect();
|
||||
const columnWidth = canvasRect.width / this.container().columns;
|
||||
const rowHeight = canvasRect.height / this.container().rows;
|
||||
const deltaColumns = Math.round((event.clientX - this.dragState.startClientX) / columnWidth);
|
||||
const deltaRows = Math.round((event.clientY - this.dragState.startClientY) / rowHeight);
|
||||
const nextGrid = { ...this.dragState.startGrid };
|
||||
|
||||
if (this.dragState.mode === 'move') {
|
||||
nextGrid.x = this.clamp(deltaColumns + this.dragState.startGrid.x, 0, this.container().columns - nextGrid.w);
|
||||
nextGrid.y = this.clamp(deltaRows + this.dragState.startGrid.y, 0, this.container().rows - nextGrid.h);
|
||||
} else {
|
||||
nextGrid.w = this.clamp(deltaColumns + this.dragState.startGrid.w, 1, this.container().columns - nextGrid.x);
|
||||
nextGrid.h = this.clamp(deltaRows + this.dragState.startGrid.h, 1, this.container().rows - nextGrid.y);
|
||||
}
|
||||
|
||||
this.itemChanged.emit({
|
||||
key: this.dragState.key,
|
||||
grid: nextGrid
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('document:pointerup')
|
||||
@HostListener('document:pointercancel')
|
||||
onPointerUp(): void {
|
||||
this.dragState = null;
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape')
|
||||
onEscape(): void {
|
||||
this.dragState = null;
|
||||
}
|
||||
|
||||
private startDrag(event: PointerEvent, item: ThemeGridEditorItem, mode: DragMode): void {
|
||||
if (this.disabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.itemSelected.emit(item.key);
|
||||
this.dragState = {
|
||||
key: item.key,
|
||||
mode,
|
||||
startClientX: event.clientX,
|
||||
startClientY: event.clientY,
|
||||
startGrid: { ...item.grid }
|
||||
};
|
||||
}
|
||||
|
||||
private clamp(value: number, minimum: number, maximum: number): number {
|
||||
return Math.min(Math.max(value, minimum), maximum);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user