Replacing resizor with pane directive; replacing container-ref with tags
This commit is contained in:
parent
c99013df04
commit
45b0d04448
@ -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;
|
||||||
|
82
src/app/common/directives/pane-handle.directive.ts
Normal file
82
src/app/common/directives/pane-handle.directive.ts
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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>
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +1,3 @@
|
|||||||
.dropzone {
|
.dropzone {
|
||||||
height: 90vh;
|
height: 92vh;
|
||||||
}
|
}
|
@ -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>
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 );
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user