Refactor Nickname Generator component with Angular Material, update logic for fields and commands, and improve styling.
This commit is contained in:
parent
423d5e4a4c
commit
2be79c180a
|
|
@ -154,6 +154,6 @@ export const routes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'nickgenerator',
|
path: 'nickgenerator',
|
||||||
loadComponent: () => import('./pages/reference/nickgenerator/nickgenerator.component').then(m => m.NickgeneratorComponent)
|
loadComponent: () => import('@pages/reference/nickgenerator/nick-generator.component').then(m => m.NickGeneratorComponent)
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
<ng-container>
|
||||||
|
<app-header [current_page]="'nickgenerator'" height="460px" background_image="/public/img/backgrounds/trees.jpg"
|
||||||
|
[overlay_gradient]="0.5">
|
||||||
|
<div class="title" header-content>
|
||||||
|
<h1>Nickname Generator</h1>
|
||||||
|
<h2>Customize your in-game nickname</h2>
|
||||||
|
<h3 style="font-family: 'minecraft-text', sans-serif; font-size: 0.8rem; margin-top: 10px;">Made by TheParm</h3>
|
||||||
|
<!--TODO remove this message when everything works-->
|
||||||
|
<p style="font-weight: bolder; color: red">NOTICE: This page is in the process of being updated to work on the new
|
||||||
|
site.<br> This version is functional, but only barely. Expect updates in the coming days</p>
|
||||||
|
</div>
|
||||||
|
</app-header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="containerNick">
|
||||||
|
<div class="controls">
|
||||||
|
|
||||||
|
@for (part of parts; track $index; let i = $index) {
|
||||||
|
<div class="part">
|
||||||
|
<div class="row">
|
||||||
|
<mat-form-field class="textField" appearance="outline">
|
||||||
|
<mat-label>Text</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[value]="part.text"
|
||||||
|
(input)="part.text = ($any($event.target).value || ''); onInputChanged()"
|
||||||
|
maxlength="16"
|
||||||
|
/>
|
||||||
|
<mat-hint align="end">{{ part.text.length }} / 16</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox
|
||||||
|
class="checkbox"
|
||||||
|
[(ngModel)]="part.gradient"
|
||||||
|
(change)="onGradientToggle(i)"
|
||||||
|
>Gradient
|
||||||
|
</mat-checkbox
|
||||||
|
>
|
||||||
|
|
||||||
|
<mat-form-field
|
||||||
|
class="colorField"
|
||||||
|
appearance="outline"
|
||||||
|
[style.visibility]="(part.continuation && i>0 && parts[i-1]?.gradient && part.gradient) ? 'hidden' : 'visible'">
|
||||||
|
<mat-label>Color A</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
type="color"
|
||||||
|
[value]="part.colorA"
|
||||||
|
(input)="part.colorA = $any($event.target).value; onInputChanged()"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field
|
||||||
|
class="colorField"
|
||||||
|
appearance="outline"
|
||||||
|
[style.visibility]="part.gradient ? 'visible' : 'hidden'">
|
||||||
|
<mat-label>Color B</mat-label>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
type="color"
|
||||||
|
[value]="part.colorB"
|
||||||
|
(input)="part.colorB = $any($event.target).value; onInputChanged()"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox
|
||||||
|
class="checkbox"
|
||||||
|
[(ngModel)]="part.continuation"
|
||||||
|
(change)="onContinuationToggle(i)"
|
||||||
|
[disabled]="i===0 || !part.gradient || !parts[i-1]?.gradient"
|
||||||
|
>Continuation
|
||||||
|
</mat-checkbox
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (part.invalid) {
|
||||||
|
<div class="invalid">(min 1 – max 16 chars{{ part.gradient ? '' : ' for non-empty text' }})</div>
|
||||||
|
}
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<button mat-raised-button (click)="addPart()">Add Part</button>
|
||||||
|
<button mat-raised-button (click)="deletePart()">Remove Part</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (showCommands) {
|
||||||
|
<div class="commands">
|
||||||
|
<div class="commandRow">
|
||||||
|
<div class="command">{{ tryCmd }}</div>
|
||||||
|
<button mat-stroked-button (click)="copy(tryCmd, 'try')">{{ tryCommandButtonContent }}</button>
|
||||||
|
</div>
|
||||||
|
<div class="commandRow">
|
||||||
|
<div class="command">{{ requestCmd }}</div>
|
||||||
|
<button mat-stroked-button (click)="copy(requestCmd, 'request')">{{ requestCommandButtonContent }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (showPreview) {
|
||||||
|
<div class="preview" [innerHTML]="previewHtml"></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</ng-container>
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* nick-generator.component.css */
|
||||||
|
.containerNick {
|
||||||
|
background-color: #292828;
|
||||||
|
padding: 40px 5%;
|
||||||
|
max-width: 1220px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part {
|
||||||
|
padding: 8px 0 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textField {
|
||||||
|
flex: 1 1 260px;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorField {
|
||||||
|
width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid {
|
||||||
|
color: #dd0000;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 20px 0 32px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commandRow {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command {
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: monospace;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
background: #1e1e1e;
|
||||||
|
padding: 14px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: 'minecraft-text', monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
||||||
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||||
|
import {MatInputModule} from '@angular/material/input';
|
||||||
|
import {HeaderComponent} from '@header/header.component';
|
||||||
|
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||||
|
import {FormsModule} from '@angular/forms';
|
||||||
|
import {MatDividerModule} from '@angular/material/divider';
|
||||||
|
import {MatButtonModule} from '@angular/material/button';
|
||||||
|
|
||||||
|
interface Part {
|
||||||
|
text: string;
|
||||||
|
gradient: boolean;
|
||||||
|
colorA: string;
|
||||||
|
colorB: string;
|
||||||
|
continuation: boolean;
|
||||||
|
invalid?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-nick-generator',
|
||||||
|
templateUrl: './nick-generator.component.html',
|
||||||
|
styleUrls: ['./nick-generator.component.scss'],
|
||||||
|
imports: [
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
HeaderComponent,
|
||||||
|
MatCheckboxModule,
|
||||||
|
FormsModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatButtonModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class NickGeneratorComponent {
|
||||||
|
parts: Part[] = [
|
||||||
|
{text: '', gradient: false, colorA: '#ffffff', colorB: '#ffffff', continuation: false}
|
||||||
|
];
|
||||||
|
|
||||||
|
tryCmd = '';
|
||||||
|
requestCmd = '';
|
||||||
|
previewHtml: SafeHtml = '';
|
||||||
|
showPreview = false;
|
||||||
|
showCommands = false;
|
||||||
|
|
||||||
|
constructor(private sanitizer: DomSanitizer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
addPart(): void {
|
||||||
|
this.parts.push({text: '', gradient: false, colorA: '#ffffff', colorB: '#ffffff', continuation: false});
|
||||||
|
this.onInputChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePart(): void {
|
||||||
|
if (this.parts.length > 1) {
|
||||||
|
this.parts.pop();
|
||||||
|
// If last part was a gradient, unset continuation on new last part
|
||||||
|
if (this.parts.length > 0) this.parts[this.parts.length - 1].continuation = false;
|
||||||
|
this.onInputChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onGradientToggle(i: number): void {
|
||||||
|
// Toggling gradient affects availability of continuation for this & next part
|
||||||
|
if (!this.parts[i].gradient) {
|
||||||
|
// If gradient turned off, force continuation off for this index (not visible anymore)
|
||||||
|
this.parts[i].continuation = false;
|
||||||
|
}
|
||||||
|
if (i + 1 < this.parts.length && !this.parts[i + 1].gradient) {
|
||||||
|
this.parts[i + 1].continuation = false;
|
||||||
|
}
|
||||||
|
this.onInputChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
onContinuationToggle(_: number): void {
|
||||||
|
this.onInputChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChanged(): void {
|
||||||
|
let result = '';
|
||||||
|
let preview = '';
|
||||||
|
let valid = true;
|
||||||
|
let nickLen = 0;
|
||||||
|
let prevColorB = '#ffffff';
|
||||||
|
|
||||||
|
for (let i = 0; i < this.parts.length; i++) {
|
||||||
|
const p = this.parts[i];
|
||||||
|
const len = p.text.length;
|
||||||
|
nickLen += len;
|
||||||
|
|
||||||
|
const partValid =
|
||||||
|
(p.gradient && len >= 1 && len <= 16) ||
|
||||||
|
(!p.gradient && len > 0);
|
||||||
|
|
||||||
|
p.invalid = !partValid;
|
||||||
|
if (!partValid) {
|
||||||
|
valid = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.gradient) {
|
||||||
|
// Continuation allowed only if previous & current are gradient
|
||||||
|
const contAllowed = i > 0 && this.parts[i - 1].gradient;
|
||||||
|
const cont = p.continuation && contAllowed;
|
||||||
|
|
||||||
|
if (cont) {
|
||||||
|
result += p.text;
|
||||||
|
preview += this.generateGradient(p.text, prevColorB, p.colorB);
|
||||||
|
} else {
|
||||||
|
result += `{${p.colorA}>}${p.text}`;
|
||||||
|
preview += this.generateGradient(p.text, p.colorA, p.colorB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add closing/continuation marker
|
||||||
|
const nextContinuation = (i + 1 < this.parts.length) && this.parts[i + 1].continuation;
|
||||||
|
if (i < this.parts.length - 1) {
|
||||||
|
result += `{${p.colorB}<>}`;
|
||||||
|
} else {
|
||||||
|
result += `{${p.colorB}<}`;
|
||||||
|
}
|
||||||
|
prevColorB = p.colorB;
|
||||||
|
} else {
|
||||||
|
// Solid
|
||||||
|
result += `{${p.colorA}}${p.text}`;
|
||||||
|
preview += this.generateSolidColor(p.text, p.colorA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tryCmd = '';
|
||||||
|
this.requestCmd = '';
|
||||||
|
this.showPreview = false;
|
||||||
|
this.showCommands = false;
|
||||||
|
|
||||||
|
if (valid && result.length > 0 && nickLen >= 3 && nickLen <= 16) {
|
||||||
|
this.tryCmd = `/nick try ${result}`;
|
||||||
|
this.requestCmd = `/nick request ${result}`;
|
||||||
|
this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
|
||||||
|
this.generateSolidColor('Nickname preview: ', '#ffffff') + preview
|
||||||
|
);
|
||||||
|
this.showPreview = true;
|
||||||
|
this.showCommands = true;
|
||||||
|
} else {
|
||||||
|
if (!valid && (this.parts.length > 1 || nickLen > 0)) {
|
||||||
|
this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
|
||||||
|
this.generateSolidColor('Invalid part(s) length', '#dd0000')
|
||||||
|
);
|
||||||
|
} else if (valid && (nickLen < 3 || nickLen > 16)) {
|
||||||
|
this.previewHtml = this.sanitizer.bypassSecurityTrustHtml(
|
||||||
|
this.generateSolidColor('Nickname needs to be 3–16 chars', '#dd0000')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.previewHtml = this.sanitizer.bypassSecurityTrustHtml('');
|
||||||
|
}
|
||||||
|
this.showPreview = nickLen > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tryCommandButtonContent = 'Copy';
|
||||||
|
requestCommandButtonContent = 'Copy';
|
||||||
|
|
||||||
|
copy(text: string, button: 'try' | 'request'): void {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
if (button === 'try') {
|
||||||
|
this.tryCommandButtonContent = 'Copied!';
|
||||||
|
} else if (button === 'request') {
|
||||||
|
this.requestCommandButtonContent = 'Copied!';
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
if (button === 'try') {
|
||||||
|
this.tryCommandButtonContent = 'Copy';
|
||||||
|
} else if (button === 'request') {
|
||||||
|
this.requestCommandButtonContent = 'Copy';
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSolidColor(text: string, color: string): string {
|
||||||
|
return `<span style="color:${color}">${this.escape(text)}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateGradient(text: string, colorA: string, colorB: string): string {
|
||||||
|
const len = text.length;
|
||||||
|
if (len === 0) return '';
|
||||||
|
const a = this.hexToRgb(colorA);
|
||||||
|
const b = this.hexToRgb(colorB);
|
||||||
|
if (!a || !b) return this.generateSolidColor(text, colorA);
|
||||||
|
|
||||||
|
const stepR = len > 1 ? (b.r - a.r) / (len - 1) : 0;
|
||||||
|
const stepG = len > 1 ? (b.g - a.g) / (len - 1) : 0;
|
||||||
|
const stepB = len > 1 ? (b.b - a.b) / (len - 1) : 0;
|
||||||
|
|
||||||
|
let res = '';
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const r = a.r + stepR * i;
|
||||||
|
const g = a.g + stepG * i;
|
||||||
|
const bl = a.b + stepB * i;
|
||||||
|
res += this.generateSolidColor(text[i], this.rgbToHex(r, g, bl));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||||||
|
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return m
|
||||||
|
? {r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16)}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentToHex(c: number): string {
|
||||||
|
const x = Math.round(c);
|
||||||
|
const h = x.toString(16);
|
||||||
|
return h.length === 1 ? '0' + h : h;
|
||||||
|
}
|
||||||
|
|
||||||
|
rgbToHex(r: number, g: number, b: number): string {
|
||||||
|
return (
|
||||||
|
'#' + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
escape(s: string): string {
|
||||||
|
return s
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<ng-container>
|
|
||||||
<app-header [current_page]="'nickgenerator'" height="460px" background_image="/public/img/backgrounds/trees.jpg"
|
|
||||||
[overlay_gradient]="0.5">
|
|
||||||
<div class="title" header-content>
|
|
||||||
<h1>Nickname Generator</h1>
|
|
||||||
<h2>Customize your in-game nickname</h2>
|
|
||||||
<h3 style="font-family: 'minecraft-text', sans-serif; font-size: 0.8rem; margin-top: 10px;">Made by TheParm</h3>
|
|
||||||
</div>
|
|
||||||
</app-header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<!-- <section class="darkmodeSection">
|
|
||||||
<div class="container containerNick">
|
|
||||||
<div style="padding: 0 5% 0 5%;">
|
|
||||||
<div id="parts" class="previewNickDiv">
|
|
||||||
</div>
|
|
||||||
<div class="previewNickDiv">
|
|
||||||
<input type="button" class="button" value="Add Part" onclick="addPart()"/>
|
|
||||||
<input type="button" class="button" value="Remove Part" onclick="deletePart()"/>
|
|
||||||
</div>
|
|
||||||
<br><br><br><br>
|
|
||||||
<div id="commandTry" class="previewNickDiv">
|
|
||||||
<div id="try" class="command darkBg"></div>
|
|
||||||
<input type="button" class="button copy" value="Copy" onclick="copy(this)"/>
|
|
||||||
</div>
|
|
||||||
<div id="commandRequest" class="previewNickDiv">
|
|
||||||
<div id="request" class="command darkBg"></div>
|
|
||||||
<input type="button" class="button copy" value="Copy" onclick="copy(this)"/>
|
|
||||||
</div>
|
|
||||||
<div id="preview" class="preview darkBg previewNickDiv">
|
|
||||||
</div>
|
|
||||||
<div id="template" class='part' style="display: none">
|
|
||||||
<p style="font-family: 'minecraft-text', sans-serif">
|
|
||||||
Text: <input type="text" id="text" class="textPart" size=18 oninput="inputChanged()"/>
|
|
||||||
Gradient: <input type="checkbox" id="grad" class="gradPart" oninput="onGradient(this)"/>
|
|
||||||
<input id="colorA" type="text" class="coloris colorAPart color" value="#ffffff" oninput="inputChanged()"/>
|
|
||||||
<input id="colorB" type="text" class="coloris colorBPart color" value="#ffffff" oninput="inputChanged()"/>
|
|
||||||
Continuation: <input type="checkbox" id="cont" class="contPart" disabled oninput="onContinuation(this)"/>
|
|
||||||
<span id="invalid" class="invalidPart" style="display: none">(min 1 - max 16 chars)</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 20px; text-align: center;">
|
|
||||||
<p style="font-family: 'minecraft-text', sans-serif">
|
|
||||||
Usage: Add as many parts as you wish, then apply the color and/or gradient, and copy/paste the command
|
|
||||||
into the minecraft chat. The total length of the nickname should be between 3 and 16 characters. Use the
|
|
||||||
continuation checkbox to continue the gradient from the last gradient color.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section> -->
|
|
||||||
</main>
|
|
||||||
</ng-container>
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { NickgeneratorComponent } from './nickgenerator.component';
|
|
||||||
|
|
||||||
describe('NickgeneratorComponent', () => {
|
|
||||||
let component: NickgeneratorComponent;
|
|
||||||
let fixture: ComponentFixture<NickgeneratorComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [NickgeneratorComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(NickgeneratorComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import {Component} from '@angular/core';
|
|
||||||
import {HeaderComponent} from "@header/header.component";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-nickgenerator',
|
|
||||||
imports: [
|
|
||||||
HeaderComponent
|
|
||||||
],
|
|
||||||
templateUrl: './nickgenerator.component.html',
|
|
||||||
styleUrl: './nickgenerator.component.scss'
|
|
||||||
})
|
|
||||||
export class NickgeneratorComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user