Replacing resizor with pane directive; replacing container-ref with tags

This commit is contained in:
itdominator 2025-06-30 00:50:32 -05:00
parent c99013df04
commit 45b0d04448
12 changed files with 146 additions and 169 deletions

View File

@ -22,6 +22,7 @@ export class DraggableDirective {
@HostListener('pointerdown', ['$event']) @HostListener('pointerdown', ['$event'])
onPointerDown(event: PointerEvent): void { onPointerDown(event: PointerEvent): void {
console.log("pointerdown"); console.log("pointerdown");
this.dragging = true;
this.dragStart.emit(event); this.dragStart.emit(event);
} }
@ -31,14 +32,12 @@ export class DraggableDirective {
if (!this.dragging) return; if (!this.dragging) return;
console.log("pointermove"); console.log("pointermove");
this.dragging = true;
this.dragMove.emit(event); this.dragMove.emit(event);
} }
@HostListener('document:pointerup', ['$event']) @HostListener('document:pointerup', ['$event'])
onPointerUp(event: PointerEvent): void { onPointerUp(event: PointerEvent): void {
if (!this.dragging) return; if (!this.dragging) return;
console.log("pointerup"); console.log("pointerup");
this.dragging = false; this.dragging = false;

View File

@ -0,0 +1,82 @@
import {
Directive,
Output,
EventEmitter,
HostListener
} from '@angular/core';
@Directive({
selector: '[pane-handle]'
})
export class PaneHandleDirective {
@Output() dragStart = new EventEmitter<PointerEvent>();
@Output() dragMove = new EventEmitter<PointerEvent>();
@Output() dragEnd = new EventEmitter<PointerEvent>();
private dragging: boolean = false;
private isHrPane: boolean = false;
private parentElm: any;
private leftSiblingElm: any;
private rightSiblingElm: any;
@HostListener('pointerdown', ['$event'])
onPointerDown(event: PointerEvent): void {
event.preventDefault();
let target = event.target as Element;
if (
!target.classList.contains("hr-pane-handle") &&
!target.classList.contains("vr-pane-handle")
) {
console.log("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!");
return;
}
target.requestPointerLock();
this.dragging = true;
this.isHrPane = ( target.classList.contains("hr-pane-handle") ) ? true : false ;
this.parentElm = target.parentElement as Element;
this.leftSiblingElm = target.previousElementSibling as Element;
this.rightSiblingElm = target.nextElementSibling as Element;
this.dragStart.emit(event);
}
@HostListener('pointermove', ['$event'])
onPointerMove(event: PointerEvent): void {
if (!this.dragging) return;
let x = event.movementX;
let y = event.movementY;
let parentBounds = this.parentElm.getBoundingClientRect();
let leftBounds = this.leftSiblingElm.getBoundingClientRect();
let rightBounds = this.rightSiblingElm.getBoundingClientRect();
if (this.isHrPane) {
this.leftSiblingElm.style.height = `${leftBounds.height + y}px`;
this.rightSiblingElm.style.height = `${rightBounds.height - y}px`;
} else {
this.leftSiblingElm.style.width = `${leftBounds.width + x}px`;
this.rightSiblingElm.style.width = `${rightBounds.width - x}px`;
}
this.dragMove.emit(event);
}
@HostListener('pointerup', ['$event'])
onPointerUp(event: PointerEvent): void {
if (!this.dragging) return;
this.dragging = false;
this.leftSiblingElm = null;
this.rightSiblingElm = null;
document.exitPointerLock();
this.dragEnd.emit(event);
}
}

View File

@ -1,4 +1,4 @@
import { ComponentRef, Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs'; import { ReplaySubject, Observable } from 'rxjs';
import { NewtonEditorComponent } from "../../../editor/newton-editor/newton-editor.component"; import { NewtonEditorComponent } from "../../../editor/newton-editor/newton-editor.component";
@ -16,7 +16,7 @@ import { NewtonFile } from '../../types/file.type';
export class EditorsService { export class EditorsService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1); private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
editors: Map<string, ComponentRef<NewtonEditorComponent>>; editors: Map<string, NewtonEditorComponent>;
editorSettings: typeof EditorSettings; editorSettings: typeof EditorSettings;
activeEditor!: string; activeEditor!: string;
@ -24,24 +24,42 @@ export class EditorsService {
constructor() { constructor() {
this.editorSettings = EditorSettings; this.editorSettings = EditorSettings;
this.editors = new Map<string, ComponentRef<NewtonEditorComponent>>(); this.editors = new Map<string, NewtonEditorComponent>();
} }
public getEditorsAsArray(): ComponentRef<NewtonEditorComponent>[] { public getEditorsAsArray(): NewtonEditorComponent[] {
return [...this.editors.values()]; return [...this.editors.values()];
} }
public get(uuid: string): NewtonEditorComponent { public get(uuid: string): NewtonEditorComponent {
return this.editors.get(uuid).instance; return this.editors.get(uuid);
} }
public set(uuid: string, component: ComponentRef<NewtonEditorComponent>) { public set(uuid: string, component: NewtonEditorComponent) {
this.editors.set(uuid, component); this.editors.set(uuid, component);
if (Object.keys(this.editors).length < 1) return;
let leftEditor = null;
let rightEditor = null;
let _editors = this.getEditorsAsArray();
for (let i = 0; i < _editors.length; i++) {
if (_editors[i].uuid !== uuid) continue;
leftEditor = _editors[i - 1];
rightEditor = _editors[i];
}
leftEditor.rightSiblingUUID = rightEditor.uuid;
rightEditor.leftSiblingUUID = leftEditor.uuid;
} }
public async setSession(file: NewtonFile | undefined | null) { public async setSession(file: NewtonFile | undefined | null) {
if ( !file ) return; if ( !file ) return;
let editorComponent = this.getActiveEditorComponent(); let editorComponent = this.getActiveEditorComponent();
let editor = editorComponent.editor; let editor = editorComponent.editor;

View File

@ -1,16 +0,0 @@
.editor-size-button {
text-align: center;
width: 4em;
color: rgba(225, 225, 225, 0.64);
border-style: solid;
border-width: thin;
border-color: rgba(124, 124, 124, 0.64);
margin-left: 0.2em;
margin-right: 0.2em;
float: left;
}
.editor-size-button:hover {
cursor: pointer;
border-color: rgba(0, 124, 0, 0.64);
}

View File

@ -1,28 +0,0 @@
<div class="col editor-left-size" (click)="setEditorSize($event)">
<div class="editor-size-button size-12">
100%
</div>
<div class="editor-size-button size-8">
75%
</div>
<div class="editor-size-button size-6">
50%
</div>
<div class="editor-size-button size-2">
25%
</div>
</div>
<div class="col editor-right-size" (click)="setEditorSize($event)">
<div class="editor-size-button size-12">
100%
</div>
<div class="editor-size-button size-8">
75%
</div>
<div class="editor-size-button size-6">
50%
</div>
<div class="editor-size-button size-2">
25%
</div>
</div>

View File

@ -1,84 +0,0 @@
import { Component, inject } from '@angular/core';
import { EditorsService } from '../../common/services/editor/editors.service';
@Component({
selector: 'editor-resize',
standalone: true,
imports: [
],
templateUrl: './editor-resize.component.html',
styleUrl: './editor-resize.component.css',
host: {
'class': 'row'
}
})
export class EditorResizeComponent {
private editorsService: EditorsService = inject(EditorsService);
constructor() {
}
// Note: Only really works with 2 editors and very brittle logic.
protected setEditorSize(event: any) {
let lEditorComponent = null;
let rEditorComponent = null;
let lSize = 6;
let rSize = 6;
if (
event.target.parentElement.classList.contains("editor-left-size")
) {
lSize = parseInt(
event.target.classList[1].split("-")[1]
);
rSize = 12 - lSize;
lEditorComponent = this.editorsService.getActiveEditorComponent();
if (lEditorComponent.leftSiblingUUID) {
rEditorComponent = lEditorComponent;
lEditorComponent = this.editorsService.get(lEditorComponent.leftSiblingUUID);
} else {
rEditorComponent = this.editorsService.get(lEditorComponent.rightSiblingUUID);
}
} else {
rSize = parseInt(
event.target.classList[1].split("-")[1]
);
lSize = 12 - rSize;
rEditorComponent = this.editorsService.getActiveEditorComponent();
if (rEditorComponent.rightSiblingUUID) {
lEditorComponent = rEditorComponent;
rEditorComponent = this.editorsService.get(rEditorComponent.rightSiblingUUID);
} else {
lEditorComponent = this.editorsService.get(rEditorComponent.leftSiblingUUID);
}
}
this.resizeAndFocus(lEditorComponent, lSize, rEditorComponent, rSize);
}
private resizeAndFocus(lEditorComponent: any, lSize: number, rEditorComponent: any, rSize: number) {
let lElm = lEditorComponent.editorElm.nativeElement.parentElement;
let rElm = rEditorComponent.editorElm.nativeElement.parentElement;
lElm.setAttribute(
'class',
(lSize == 0) ? "hidden" : `col col-${lSize}`
);
rElm.setAttribute(
'class',
(rSize == 0) ? "hidden" : `col col-${rSize}`
);
if (lSize == 0) rEditorComponent.editor.focus();
if (rSize == 0) lEditorComponent.editor.focus();
}
}

View File

@ -1,3 +1,3 @@
.dropzone { .dropzone {
height: 90vh; height: 92vh;
} }

View File

@ -1,10 +1,9 @@
<div class="col"> <div class="col">
<div class="row dropzone" #dropzone dropzone (fileDropped)="onFileDropped($event)"> <div class="row dropzone" #dropzone dropzone (fileDropped)="onFileDropped($event)">
<ng-container #containerRef> <newton-editor [isDefault]="true"></newton-editor>
</ng-container> <hr class="col vr-pane-handle" pane-handle />
<newton-editor></newton-editor>
</div> </div>
<editor-resize></editor-resize>
<div> <div>

View File

@ -1,14 +1,14 @@
import { Component, ViewChild, ViewContainerRef, inject } from '@angular/core'; import { Component, inject } from '@angular/core';
import { Subject, takeUntil } from 'rxjs'; import { Subject, takeUntil } from 'rxjs';
import { NewtonEditorComponent } from "./newton-editor/newton-editor.component";
import { EditorResizeComponent } from "./editor-resize/editor-resize.component";
import { EditorsService } from '../common/services/editor/editors.service'; import { EditorsService } from '../common/services/editor/editors.service';
import { TabsService } from '../common/services/editor/tabs/tabs.service'; import { TabsService } from '../common/services/editor/tabs/tabs.service';
import { FilesService } from '../common/services/editor/files.service'; import { FilesService } from '../common/services/editor/files.service';
import { NewtonEditorComponent } from "./newton-editor/newton-editor.component";
import { DndDirective } from '../common/directives/dnd.directive'; import { DndDirective } from '../common/directives/dnd.directive';
import { PaneHandleDirective } from '../common/directives/pane-handle.directive';
import { NewtonFile } from '../common/types/file.type'; import { NewtonFile } from '../common/types/file.type';
import { ServiceMessage } from '../common/types/service-message.type'; import { ServiceMessage } from '../common/types/service-message.type';
@ -19,7 +19,8 @@ import { ServiceMessage } from '../common/types/service-message.type';
standalone: true, standalone: true,
imports: [ imports: [
DndDirective, DndDirective,
EditorResizeComponent PaneHandleDirective,
NewtonEditorComponent
], ],
templateUrl: './editors.component.html', templateUrl: './editors.component.html',
styleUrl: './editors.component.css', styleUrl: './editors.component.css',
@ -34,8 +35,6 @@ export class EditorsComponent {
private tabsService: TabsService = inject(TabsService); private tabsService: TabsService = inject(TabsService);
private filesService: FilesService = inject(FilesService); private filesService: FilesService = inject(FilesService);
@ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef;
constructor() { constructor() {
} }
@ -44,14 +43,6 @@ export class EditorsComponent {
private ngAfterViewInit(): void { private ngAfterViewInit(): void {
this.loadSubscribers(); this.loadSubscribers();
this.loadMainSubscribers(); this.loadMainSubscribers();
let leftEditor = this.createEditor();
let rightEditor = this.createEditor();
this.editorsService.setActiveEditor(leftEditor.instance.uuid);
leftEditor.instance.isDefault = true;
leftEditor.instance.rightSiblingUUID = rightEditor.instance.uuid;
rightEditor.instance.leftSiblingUUID = leftEditor.instance.uuid;
} }
private ngOnDestroy() { private ngOnDestroy() {
@ -120,7 +111,7 @@ export class EditorsComponent {
let editors = this.editorsService.getEditorsAsArray(); let editors = this.editorsService.getEditorsAsArray();
for (let i = 0; i < editors.length; i++) { for (let i = 0; i < editors.length; i++) {
let editorComponent = editors[i].instance; let editorComponent = editors[i];
if (editorComponent.editor.session == file.session) { if (editorComponent.editor.session == file.session) {
editorComponent.newSession(); editorComponent.newSession();
} }
@ -225,14 +216,6 @@ export class EditorsComponent {
}); });
} }
private createEditor() {
const component = this.containerRef.createComponent(NewtonEditorComponent);
component.instance.editorSettings = this.editorsService.editorSettings;
this.editorsService.set(component.instance.uuid, component)
return component;
}
protected onFileDropped(files: any) { protected onFileDropped(files: any) {
this.filesService.loadFilesList(files).then((file: NewtonFile | undefined | null) => { this.filesService.loadFilesList(files).then((file: NewtonFile | undefined | null) => {
this.editorsService.setSession(file); this.editorsService.setSession(file);

View File

@ -16,8 +16,8 @@ import { ServiceMessage } from '../../common/types/service-message.type';
@Directive() @Directive()
export class NewtonEditorBase { export class NewtonEditorBase {
public uuid: string = uuid.v4();; public uuid: string = uuid.v4();
public isDefault: boolean = false; @Input() public isDefault: boolean = false;
public leftSiblingUUID!: string; public leftSiblingUUID!: string;
public rightSiblingUUID!: string; public rightSiblingUUID!: string;

View File

@ -26,7 +26,8 @@ import { ServiceMessage } from '../../common/types/service-message.type';
templateUrl: './newton-editor.component.html', templateUrl: './newton-editor.component.html',
styleUrl: './newton-editor.component.css', styleUrl: './newton-editor.component.css',
host: { host: {
'class': 'col col-6' // 'class': 'col col-6'
'style': 'width: 48%;'
} }
}) })
export class NewtonEditorComponent extends NewtonEditorBase { export class NewtonEditorComponent extends NewtonEditorBase {
@ -34,11 +35,14 @@ export class NewtonEditorComponent extends NewtonEditorBase {
constructor() { constructor() {
super(); super();
this.editorsService.set(this.uuid, this);
} }
private ngAfterViewInit(): void { private ngAfterViewInit(): void {
if (this.isDefault) { if (this.isDefault) {
this.editorsService.setActiveEditor(this.uuid);
this.addActiveStyling(); this.addActiveStyling();
} }
@ -52,6 +56,8 @@ export class NewtonEditorComponent extends NewtonEditorBase {
} }
private configAceAndBindToElement(): void { private configAceAndBindToElement(): void {
this.editorSettings = this.editorsService.editorSettings;
ace.config.set('basePath', this.editorSettings.BASE_PATH); ace.config.set('basePath', this.editorSettings.BASE_PATH);
this.editor = ace.edit( this.editorElm.nativeElement ); this.editor = ace.edit( this.editorElm.nativeElement );

View File

@ -36,6 +36,24 @@ body {
margin-right: 0.2em; margin-right: 0.2em;
} }
.hr-pane-handle,
.vr-pane-handle {
border: 2px dashed lightblue;
}
/*
.hr-pane-handle {
}
.vr-pane-handle {
transform: rotate(90deg);
}
*/
.vr-pane-handle {
max-width: 0.05em;
}