diff --git a/angular.json b/angular.json index f056620..45f07c3 100644 --- a/angular.json +++ b/angular.json @@ -40,6 +40,14 @@ }, "configurations": { "production": { + "optimization": { + "scripts": true, + "styles": { + "minify": true, + "inlineCritical": false + }, + "fonts": true + }, "budgets": [ { "type": "initial", @@ -49,7 +57,7 @@ { "type": "anyComponentStyle", "maximumWarning": "2kB", - "maximumError": "4kB" + "maximumError": "10kB" } ], "outputHashing": "all" @@ -100,4 +108,4 @@ } } } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index deba0cf..cf0a823 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", "express": "^4.18.2", - "prismjs": "^1.29.0", + "prism-code-editor": "^4.0.0-beta.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.10" @@ -33,7 +33,6 @@ "@types/express": "^4.17.17", "@types/jasmine": "~5.1.0", "@types/node": "^18.18.0", - "@types/prismjs": "^1.26.4", "jasmine-core": "~5.2.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", @@ -4577,12 +4576,6 @@ "@types/node": "*" } }, - "node_modules/@types/prismjs": { - "version": "1.26.4", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", - "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==", - "dev": true - }, "node_modules/@types/qs": { "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", @@ -11263,14 +11256,11 @@ "dev": true, "license": "MIT" }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } + "node_modules/prism-code-editor": { + "version": "4.0.0-beta.1", + "resolved": "https://registry.npmjs.org/prism-code-editor/-/prism-code-editor-4.0.0-beta.1.tgz", + "integrity": "sha512-4coLI0t7P5+ICyoF5IUqatNbN0N5zj2trwpwSDHsg/q9/D3K5GKh3RxbM7U5lehFQiZqyfXE/RZTKacPZrUSlA==", + "license": "MIT" }, "node_modules/proc-log": { "version": "4.2.0", diff --git a/package.json b/package.json index 5286b2f..7214979 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", "express": "^4.18.2", - "prismjs": "^1.29.0", + "prism-code-editor": "^4.0.0-beta.1", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.10" @@ -37,7 +37,6 @@ "@types/express": "^4.17.17", "@types/jasmine": "~5.1.0", "@types/node": "^18.18.0", - "@types/prismjs": "^1.26.4", "jasmine-core": "~5.2.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.2.0", diff --git a/src/app/code-area/code-area.component.css b/src/app/code-area/code-area.component.css index 575206d..38fefc6 100644 --- a/src/app/code-area/code-area.component.css +++ b/src/app/code-area/code-area.component.css @@ -1,6 +1,4 @@ -@import "../../../node_modules/prismjs/themes/prism-dark.min.css"; - -:host.dark { - background: #333; - color: #FFF; -} \ No newline at end of file +@import '../../../node_modules/prism-code-editor/dist/layout.css'; +@import '../../../node_modules/prism-code-editor/dist/themes/prism.css'; +@import '../../../node_modules/prism-code-editor/dist/themes/github-dark.css'; +@import '../../../node_modules/prism-code-editor/dist/copy.css'; diff --git a/src/app/code-area/code-area.component.ts b/src/app/code-area/code-area.component.ts index a98bd86..bf76a66 100644 --- a/src/app/code-area/code-area.component.ts +++ b/src/app/code-area/code-area.component.ts @@ -1,44 +1,94 @@ -import { AfterViewInit, Component, ElementRef, HostListener, input, Input } from '@angular/core'; -import Prism from 'prismjs'; -import { debounceTime, Subject, takeLast, tap } from 'rxjs'; +import { + AfterViewInit, + Component, + ElementRef, + Inject, + Input, + input, + model, + OnChanges, + PLATFORM_ID, + SimpleChanges, + ViewChild, + ViewEncapsulation, +} from "@angular/core"; +import { isPlatformBrowser } from "@angular/common"; +import { createEditor, PrismEditor } from "prism-code-editor"; +import { matchBrackets } from "prism-code-editor/match-brackets"; +import { highlightBracketPairs } from "prism-code-editor/highlight-brackets"; +import { editHistory } from "prism-code-editor/commands"; +import { copyButton } from "prism-code-editor/copy-button"; +import "prism-code-editor/prism/languages/json"; +import "prism-code-editor/prism/languages/yaml"; +import "prism-code-editor/prism/languages/toml"; +import "prism-code-editor/prism/languages/java"; +import "prism-code-editor/prism/languages/xml"; +import "prism-code-editor/prism/languages/csharp"; +import "prism-code-editor/prism/languages/typescript"; @Component({ - selector: 'code-area, [code-area]', + selector: 'code-area', standalone: true, imports: [], - template: '', - styleUrl: './code-area.component.css' + template: '
', + styleUrl: './code-area.component.css', + encapsulation: ViewEncapsulation.ShadowDom }) -export class CodeAreaComponent implements AfterViewInit { - @Input() - code!: string; +export class CodeAreaComponent implements AfterViewInit, OnChanges { + code = model(""); + language = input("typescript"); + readonly = input(false, { + transform: (value: string) => value == "true", + }); + @Input() innerStyle!: string; + @Input() innerClass!: string; + editor: PrismEditor | undefined = undefined; - @Input() - language = 'javascript'; + @ViewChild("editorContainer") editorContainer!: ElementRef; - constructor(private el: ElementRef) { } + isBrowser = false; + constructor(@Inject(PLATFORM_ID) private platformId: any) { + this.isBrowser = isPlatformBrowser(this.platformId); + } - ngAfterViewInit() { - this.highlight(this.code || this.el.nativeElement.innerText) + ngAfterViewInit(): void { + if (!this.isBrowser) + return; - this.inputDebouncer.pipe( - debounceTime(500), - tap(value => this.highlight(value)), - takeLast(1), - ).subscribe(); + if (this.editorContainer.nativeElement) { + this.editor = this.initEditor(); + } } - - private inputDebouncer = new Subject(); - - @HostListener("input") - protected onInput(){ - this.inputDebouncer.next(this.el.nativeElement.innerText); + + ngOnChanges(changes: SimpleChanges): void { + if (changes["code"].previousValue !== changes["code"].currentValue) { + this.editor?.setOptions({ value: changes["code"].currentValue }); + } } - private highlight(code: string){ - const grammar = Prism.languages[this.language]; - const html = Prism.highlight(code, grammar, this.language); - this.el.nativeElement.innerHTML = html; - console.log(code); + initEditor(): PrismEditor { + const editor = createEditor(this.editorContainer.nativeElement, { + value: this.code(), + language: this.language(), + lineNumbers: true, + wordWrap: false, + readOnly: this.readonly(), + + onUpdate: (code: string) => { + this.code.set(code); + }, + }); + editor.addExtensions( + copyButton(), + matchBrackets(true), + highlightBracketPairs(), + editHistory(), + ); + + this.code.subscribe(() => { + this.editor?.update(); + }); + + return editor; } } \ No newline at end of file diff --git a/src/app/cs2ts/cs2ts.component.html b/src/app/cs2ts/cs2ts.component.html index dfd8805..646ccac 100644 --- a/src/app/cs2ts/cs2ts.component.html +++ b/src/app/cs2ts/cs2ts.component.html @@ -7,17 +7,17 @@

C# to TS Converter

- - - +
+ +

- - - +
+ +

diff --git a/src/app/cs2ts/cs2ts.component.ts b/src/app/cs2ts/cs2ts.component.ts index 88e378f..ce02c1a 100644 --- a/src/app/cs2ts/cs2ts.component.ts +++ b/src/app/cs2ts/cs2ts.component.ts @@ -12,21 +12,20 @@ import { Meta } from '@angular/platform-browser'; templateUrl: './cs2ts.component.html', animations: [ trigger('valueChangeAnim', [ - state('*', style({ "border-width": "3px" })), transition('* <=> *', [ - animate('0.1s ease-out', style({ "border-color": "var(--bs-success)" })), - animate('0.1s ease-in', style({ "border-color": "none" })) + animate('0.07s ease-out', style({ "border-color": "limegreen" })), + animate('0.07s ease-in', style({ "border-color": "var(--bs-border-color)" })) ]), ]) - ] + ], + styles: ` + .code-border{ + border: 3px solid var(--bs-border-color); + } + ` }) export class Cs2tsComponent implements AfterContentInit { - @ViewChild("csCode", { read: ElementRef }) - protected csCode!: ElementRef; - @ViewChild("tsCode", { read: ElementRef }) - protected tsCode!: ElementRef; - - protected placeholder1: string = `public class a : b { + protected csCode: string = `public class a : b { public int x1 { get; set; } public float? x2 { get; set; } public string x3 { get; set; } @@ -34,10 +33,7 @@ export class Cs2tsComponent implements AfterContentInit { public long[] x9 { get; set; } public IEnumerable x10 { get; set; } }`; - protected placeholder2!: string; - - protected csModel: string = ""; - protected tsModel !: string; + protected tsCode !: string; protected status: boolean = false; protected inputDebouncer = new Subject(); @@ -47,8 +43,6 @@ export class Cs2tsComponent implements AfterContentInit { { name: "description", content: "Converts C# model to TS model, converts fields, types, arrays and generics." }, { name: "keywords", content: "C#, TS, CSharp, TypeScript, script, type, generic, array, converter, model, DTO, DataTransferObject, POCO, fields, string, number, int, class, code, language, long, float, boolean, bool" }, ]); - - this.placeholder2 = Cs2tsComponent.convert(this.placeholder1); } ngAfterContentInit(): void { @@ -58,20 +52,12 @@ export class Cs2tsComponent implements AfterContentInit { takeLast(1), ).subscribe(); - this.convert(this.csModel); - } - - protected onInput() { - this.inputDebouncer.next(this.csModel); - this.csCode.nativeElement.style.height = "auto"; - this.csCode.nativeElement.style.height = `${this.csCode.nativeElement.scrollHeight + 5}px`; - this.tsCode.nativeElement.style.height = "auto"; - this.tsCode.nativeElement.style.height = `${this.csCode.nativeElement.scrollHeight + 5}px`; + this.convert(this.csCode); } protected convert(csCode?: string) { - let code = csCode ? csCode : this.csModel; - this.tsModel = Cs2tsComponent.convert(code); + let code = csCode ? csCode : this.csCode; + this.tsCode = Cs2tsComponent.convert(code); this.status = !this.status; } diff --git a/src/app/header/header.component.css b/src/app/header/header.component.css index be8e7f5..fdc98bd 100644 --- a/src/app/header/header.component.css +++ b/src/app/header/header.component.css @@ -7,6 +7,7 @@ height: var(--height); background-color: #212529b0; backdrop-filter: blur(2px); + z-index: 2; } img { diff --git a/src/app/not-found/not-found.component.spec.ts b/src/app/not-found/not-found.component.spec.ts index 5b65d9e..ab0f495 100644 --- a/src/app/not-found/not-found.component.spec.ts +++ b/src/app/not-found/not-found.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NotFoundComponent } from './not-found.component'; +import { ActivatedRoute } from '@angular/router'; describe('NotFoundComponent', () => { let component: NotFoundComponent; @@ -8,7 +9,17 @@ describe('NotFoundComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [NotFoundComponent] + imports: [NotFoundComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + url: ["404"] + } + } + }, + ] }) .compileComponents();