Add image service

This commit is contained in:
Myx
2024-10-12 16:51:25 +02:00
parent 49d1cb8a7a
commit 5bf3041bb0
68 changed files with 1069 additions and 108 deletions

View File

@@ -0,0 +1,4 @@
<app-header></app-header>
<div class="main-content">
<router-outlet></router-outlet>
</div>

View File

@@ -0,0 +1,9 @@
// .main-content {
// display: flex;
// flex-direction: row;
// }
// router-outlet {
// flex: 1;
// padding: 20px;
// }

View File

@@ -0,0 +1,29 @@
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 'tools' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('tools');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, tools');
});
});

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { HeaderComponent } from './header/header.component';
import { PrimeNGConfig } from 'primeng/api';
import { Lara } from 'primeng/themes/lara';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, HeaderComponent],
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'tools';
constructor(private config: PrimeNGConfig) {
this.config.theme.set({ preset: Lara });
}
}

View File

@@ -0,0 +1,18 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideNgIconsConfig } from '@ng-icons/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideAnimationsAsync("animations"),
provideNgIconsConfig({
size: '1.5em',
}),
provideHttpClient()
]
};

View File

@@ -0,0 +1,41 @@
import { Routes } from '@angular/router';
import { AsciiToTextComponent } from '../tools/ascii-to-text/ascii-to-text.component';
import { GuidComponent } from '../tools/guid/guid.component';
import { Base64ConverterComponent } from '../tools/base64-converter/base64-converter.component';
import { JwtToJsonComponent } from '../tools/jwt-to-json/jwt-to-json.component';
import { TextToCronComponent } from '../tools/text-to-cron/text-to-cron.component';
import { DdsToPngComponent } from '../tools/dds-to-png/dds-to-png.component';
export const routes: Routes = [
{
path: 'ascii-to-text',
pathMatch: 'full',
component: AsciiToTextComponent
},
{
path: 'guid',
pathMatch: 'full',
component: GuidComponent
},
{
path: 'base64-converter',
pathMatch: 'full',
component: Base64ConverterComponent
},
{
path: 'jwt-decoder',
pathMatch: 'full',
component: JwtToJsonComponent
},
{
path: 'text-to-cron',
pathMatch: 'full',
component: TextToCronComponent
},
{
path: 'dds-to-png',
pathMatch: 'full',
component: DdsToPngComponent
}
];

View File

@@ -0,0 +1 @@
<p-megamenu [model]="items" />

View File

@@ -0,0 +1,28 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,76 @@
import { Component, OnInit } from '@angular/core';
import { MegaMenuItem } from 'primeng/api';
import { MegaMenuModule } from 'primeng/megamenu';
import { ButtonModule } from 'primeng/button';
import { CommonModule } from '@angular/common';
import { AvatarModule } from 'primeng/avatar';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
standalone: true,
imports: [MegaMenuModule, ButtonModule, CommonModule, AvatarModule]
})
export class HeaderComponent implements OnInit {
items: MegaMenuItem[] | undefined;
ngOnInit() {
this.items = [
{
label: 'Text Tools',
icon: 'pi pi-box',
items: [
[
{
items: [
{
label: 'Ascii to text',
routerLink: 'ascii-to-text',
routerLinkActiveOptions: { exact: true }
},
{
label: 'Guid Generator',
routerLink: 'guid',
routerLinkActiveOptions: { exact: true }
},
{
label: 'Base64 Converter',
routerLink: 'base64-converter',
routerLinkActiveOptions: { exact: true }
},
{
label: 'Jwt decoder',
routerLink: 'jwt-decoder',
routerLinkActiveOptions: { exact: true }
},
{
label: 'Text to Cron Expression',
routerLink: 'text-to-cron',
routerLinkActiveOptions: { exact: true }
}
],
}
]
]
},
{
label: 'Conversion',
icon: 'pi pi-box',
items: [
[
{
items: [
{
label: 'DDS to PNG',
routerLink: 'dds-to-png',
routerLinkActiveOptions: { exact: true }
}
]
}
]
]
}
]
}
}

View File

@@ -0,0 +1,21 @@
<div class="card flex justify-center">
<p-panel [header]="title">
<textarea
(keyup)="onTopChange($event)"
[disabled]="topDisabled"
pTextarea
[value]="topValue"
[placeholder]="topPlaceholder">
</textarea>
<div>
<i class="pi pi-arrow-right-arrow-left" style="font-size: 1rem"></i>
</div>
<textarea
(keyup)="onBottomChange($event)"
[disabled]="bottomDisabled"
pTextarea
[value]="bottomValue"
[placeholder]="bottomPlaceholder">
</textarea>
</p-panel>
</div>

View File

@@ -0,0 +1,42 @@
:host {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 98vw;
.card {
display: flex;
flex-direction: column;
width: 1140px;
}
.wrapper {
display: flex;
flex-direction: column;
width: 1140px;
.conversion {
justify-content: center;
display: flex;
align-items: center;
height: 70vh;
p-floatlabel {
width: 30vw;
}
}
}
textarea {
width: 100%;
height: 175px;
padding: 12px 20px;
box-sizing: border-box;
border-radius: 4px;
font-size: 16px;
resize: none;
background-color: var(--primary-contrast);
color: var(--text-color)
}
}

View File

@@ -0,0 +1,42 @@
import { CommonModule } from '@angular/common';
import { Component, Input, Output, EventEmitter, input } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FloatLabelModule } from 'primeng/floatlabel';
import { InputTextareaModule } from 'primeng/inputtextarea';
import { PanelModule } from 'primeng/panel';
@Component({
selector: 'app-dual-textarea',
templateUrl: 'dual-textarea.component.html',
styleUrls: ['dual-textarea.component.scss'],
standalone: true,
imports: [
FloatLabelModule,
InputTextareaModule,
FormsModule,
PanelModule,
CommonModule
]
})
export class DualTextareaComponent {
@Input() topDisabled: boolean = false;
@Input() bottomDisabled: boolean = false;
@Input() title: string = 'Dual Textarea';
@Input() topPlaceholder: string = 'Left Textarea';
@Input() bottomPlaceholder: string = 'Right Textarea';
@Input() topValue: string = '';
@Input() bottomValue: string = '';
@Output() topChange = new EventEmitter<string>();
@Output() bottomChange = new EventEmitter<string>();
onTopChange(event: Event): void {
const input = (event.target as HTMLTextAreaElement).value;
this.topChange.emit(input);
}
onBottomChange(event: Event): void {
const input = (event.target as HTMLTextAreaElement).value;
this.bottomChange.emit(input);
}
}

View File

@@ -0,0 +1,29 @@
<div class="card flex justify-center">
<p-panel [header]="title">
<p-fileUpload
name="file"
url="./upload"
(onSelect)="onFileSelect($event)"
[auto]="true"
[accept]="accept"
[previewWidth]="isPreview ? '50px' : '0px'"
>
</p-fileUpload>
<p-table [value]="processedFiles" *ngIf="processedFiles.length != 0">
<ng-template pTemplate="header">
<tr>
<th>Name</th>
<th>Format</th>
<th>Download</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-file>
<tr>
<td>{{file.name}}</td>
<td>{{file.format}}</td>
<td><a [href]="file.link" download>{{file.name}}</a></td>
</tr>
</ng-template>
</p-table>
</p-panel>
</div>

View File

@@ -0,0 +1,20 @@
:host {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 98vw;
.card {
display: flex;
flex-direction: column;
width: 1140px;
}
.conversion {
display: flex;
justify-content: center;
margin-top: 1rem;
}
}

View File

@@ -0,0 +1,53 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FileSelectEvent, FileUploadModule } from 'primeng/fileupload';
import { ButtonModule } from 'primeng/button';
import { PanelModule } from 'primeng/panel';
import { TableModule } from 'primeng/table';
interface ProcessedFile {
name: string;
link: string;
format: string;
}
@Component({
selector: 'app-file-converter',
templateUrl: 'file-converter.component.html',
styleUrls: ['file-converter.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
FileUploadModule,
ButtonModule,
PanelModule,
TableModule
]
})
export class FileConverterComponent {
_fileFormats: string[] = [];
accept: string = '';
@Output() fileSelected = new EventEmitter<File[]>();
@Input() isPreview: boolean = true;
@Input () title: string = 'File Converter';
@Input() processedFiles: ProcessedFile[] = [];
@Input()
set fileFormats(formats: string[]) {
this._fileFormats = formats;
this.accept = formats.join(',');
}
get fileFormats(): string[] {
return this._fileFormats;
}
selectedFile: File[] | null = null;
onFileSelect(event: FileSelectEvent): void {
this.selectedFile = event.files;
this.fileSelected.emit(this.selectedFile!);
}
}