From 79bddf9f39f4258328742b507aca4276ca6e18be Mon Sep 17 00:00:00 2001 From: mastah Date: Fri, 30 Jan 2026 01:01:02 +0100 Subject: [PATCH] Added mobile phone generator and made selecting generators bettter, probably... --- src/app/app.module.ts | 6 +- src/app/components/app/app.component.html | 12 +++- src/app/components/app/app.component.scss | 70 ++++++++++++++----- src/app/components/app/app.component.ts | 26 ++++--- .../components/mobile/mobile.component.html | 19 +++++ .../components/mobile/mobile.component.scss | 0 src/app/components/mobile/mobile.component.ts | 35 ++++++++++ src/app/service/mobile.service.ts | 63 +++++++++++++++++ src/styles.scss | 3 +- 9 files changed, 199 insertions(+), 35 deletions(-) create mode 100644 src/app/components/mobile/mobile.component.html create mode 100644 src/app/components/mobile/mobile.component.scss create mode 100644 src/app/components/mobile/mobile.component.ts create mode 100644 src/app/service/mobile.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a5d2089..1a91a67 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppComponent } from './components/app/app.component'; import { MatInputModule } from '@angular/material/input'; import { MatIconModule } from '@angular/material/icon'; @@ -14,6 +13,7 @@ import { NipComponent } from './components/nip/nip.component'; import { RegonComponent } from './components/regon/regon.component'; import { IbanComponent } from './components/iban/iban.component'; import { IdentityCardComponent } from './components/identitycard/identitycard.component'; +import { MobileComponent } from './components/mobile/mobile.component'; import {NgOptimizedImage} from "@angular/common"; import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; @@ -26,11 +26,11 @@ import { environment } from '../environments/environment'; NipComponent, RegonComponent, IbanComponent, - IdentityCardComponent + IdentityCardComponent, + MobileComponent ], imports: [ BrowserModule, - BrowserAnimationsModule, FormsModule, MatInputModule, MatIconModule, diff --git a/src/app/components/app/app.component.html b/src/app/components/app/app.component.html index dcdb62c..6c578e3 100644 --- a/src/app/components/app/app.component.html +++ b/src/app/components/app/app.component.html @@ -1,12 +1,15 @@
-
+ @if (showControls) { -
- +
+ REGON NIP PESEL DOWÓD + MOBILNY IBAN
@@ -24,6 +27,9 @@ @if (showIdentityCard) { } + @if (showMobile) { + + } @if (showIban) { } diff --git a/src/app/components/app/app.component.scss b/src/app/components/app/app.component.scss index 9e4b78f..263d3a0 100644 --- a/src/app/components/app/app.component.scss +++ b/src/app/components/app/app.component.scss @@ -1,22 +1,55 @@ -.hover-zone { +.settings-button { position: fixed; - top: 0; - left: 0; - right: 0; - height: 10px; - z-index: 9; + top: 10px; + right: 10px; + z-index: 11; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + color: white; + cursor: pointer; + box-shadow: 0 4px 16px 0 rgba(0, 38, 135, 0.2); + transition: background-color 0.3s, transform 0.3s, opacity 0.5s ease-in-out; + padding: 0; + opacity: 0; + + &.initial-visible, &:hover, &.controls-open { + opacity: 1; + } + + &:hover { + background-color: rgba(255, 255, 255, 0.3); + transform: rotate(30deg); + } + + mat-icon { + font-size: 24px; + width: 24px; + height: 24px; + } } .controls { display: flex; - justify-content: center; - padding: 1em; + flex-direction: column; + align-items: flex-end; + padding: 0; position: fixed; - top: 0; - left: 0; - right: 0; + top: 60px; + right: 10px; z-index: 10; animation: slideIn 300ms ease-out forwards; + pointer-events: none; + + & > * { + pointer-events: auto; + } mat-button-toggle-group { background: rgba(255, 255, 255, 0.15); @@ -25,16 +58,19 @@ border-radius: 8px; box-shadow: 0 4px 16px 0 rgba(0, 38, 135, 0.2); overflow: hidden; + flex-direction: column; } mat-button-toggle { color: white; font-family: 'Comfortaa', sans-serif; - border-left: 1px solid rgba(255, 255, 255, 0.2) !important; - background-color: rgba(26, 35, 126, 0.4); + border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important; + border-left: none !important; + background-color: rgba(51, 98, 188, 0.4); + width: 100%; - &:first-child { - border-left: none !important; + &:last-child { + border-bottom: none !important; } &.mat-button-toggle-checked { @@ -49,7 +85,7 @@ } .generators { - padding-top: 2em; + padding-top: 10px; & > * { display: block; @@ -59,7 +95,7 @@ @keyframes slideIn { from { - transform: translateY(-100%); + transform: translateY(-20px); opacity: 0; } to { diff --git a/src/app/components/app/app.component.ts b/src/app/components/app/app.component.ts index a13f133..a92b09d 100644 --- a/src/app/components/app/app.component.ts +++ b/src/app/components/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, HostListener } from '@angular/core'; +import { ChangeDetectorRef, Component, ElementRef, HostListener, OnInit } from '@angular/core'; @Component({ selector: 'app-root', @@ -13,23 +13,29 @@ export class AppComponent implements OnInit { showPesel = true; showIdentityCard = true; showIban = true; + showMobile = true; showControls = false; + isInitialVisible = true; - constructor() { + constructor(private cdr: ChangeDetectorRef, private eRef: ElementRef) { } ngOnInit(): void { + setTimeout(() => { + this.isInitialVisible = false; + this.cdr.detectChanges(); + }, 3000); } - @HostListener('window:mousemove', ['$event']) - onMouseMove(event: MouseEvent) { - // Show if mouse is in the top 20px (hover zone) - if (event.clientY < 20) { - this.showControls = true; - } - // Hide if mouse moves below 120px - else if (event.clientY > 120) { + @HostListener('document:click', ['$event']) + clickout(event: Event) { + const settingsButton = this.eRef.nativeElement.querySelector('.settings-button'); + const controls = this.eRef.nativeElement.querySelector('.controls'); + + if (this.showControls && + settingsButton && !settingsButton.contains(event.target) && + controls && !controls.contains(event.target)) { this.showControls = false; } } diff --git a/src/app/components/mobile/mobile.component.html b/src/app/components/mobile/mobile.component.html new file mode 100644 index 0000000..73d80f8 --- /dev/null +++ b/src/app/components/mobile/mobile.component.html @@ -0,0 +1,19 @@ +
+
+ KOMÓRKA +
+
+
+
+ + +
+ Generuj numer telefonu + Skopiuj numer telefonu do schowka + Definicja numerów w Polsce na Wikipedii +
+
+
diff --git a/src/app/components/mobile/mobile.component.scss b/src/app/components/mobile/mobile.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/mobile/mobile.component.ts b/src/app/components/mobile/mobile.component.ts new file mode 100644 index 0000000..9bf005e --- /dev/null +++ b/src/app/components/mobile/mobile.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import { AbstractControl, FormControl } from '@angular/forms'; +import { MobileService } from 'src/app/service/mobile.service'; +import { ClipboardService } from 'src/app/service/gui/clipboard.service'; + +@Component({ + selector: 'app-mobile', + templateUrl: './mobile.component.html', + styleUrls: ['./mobile.component.scss'], + standalone: false +}) +export class MobileComponent implements OnInit { + public valueField: FormControl; + + constructor(private mobileService: MobileService, private clipboardService: ClipboardService) { + this.valueField = new FormControl(''); + this.valueField.setValidators([(control: AbstractControl) => this.mobileService.validateMobile(control.value)]); + } + + ngOnInit(): void { + this.generate(); + } + + generate(): void { + this.valueField.setValue(this.mobileService.generateMobile()); + } + + copyToClipboard(): void { + this.clipboardService.copyToClipboard(this.valueField.value); + } + + navigateToDocs(): void { + window.open('https://pl.wikipedia.org/wiki/Numery_telefonu_w_Polsce', "_blank"); + } +} diff --git a/src/app/service/mobile.service.ts b/src/app/service/mobile.service.ts new file mode 100644 index 0000000..b2fdfe7 --- /dev/null +++ b/src/app/service/mobile.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@angular/core'; +import { CommonService } from './common.service'; +import { ValidationErrors } from '@angular/forms'; + +@Injectable({ + providedIn: 'any' +}) +export class MobileService { + + private static readonly mobilePrefixes: string[] = [ + '450', '451', '452', '453', '454', '455', '456', '457', '458', '459', + '500', '501', '502', '503', '504', '505', '506', '507', '508', '509', + '510', '511', '512', '513', '514', '515', '516', '517', '518', '519', + '530', '531', '532', '533', '534', '535', '536', '537', '538', '539', + '570', '571', '572', '573', '574', '575', '576', '577', '578', '579', + '600', '601', '602', '603', '604', '605', '606', '607', '608', '609', + '660', '661', '662', '663', '664', '665', '666', '667', '668', '669', + '690', '691', '692', '693', '694', '695', '696', '697', '698', '699', + '720', '721', '722', '723', '724', '725', '726', '727', '728', '729', + '730', '731', '732', '733', '734', '735', '736', '737', '738', '739', + '780', '781', '782', '783', '784', '785', '786', '787', '788', '789', + '790', '791', '792', '793', '794', '795', '796', '797', '798', '799', + '880', '881', '882', '883', '884', '885', '886', '887', '888', '889' + ]; + + constructor(private common: CommonService) { } + + public generateMobile(): string { + const prefixIndex = this.common.getRandomInt(0, MobileService.mobilePrefixes.length - 1); + const prefix = MobileService.mobilePrefixes[prefixIndex]; + const rest = this.common.getRandomInt(0, 999999); + return prefix + this.common.pad(rest, 6); + } + + public validateMobile(mobile: string): ValidationErrors | null { + const errors: ValidationErrors = {}; + + if (!mobile) { + return null; + } + + if (mobile.length < 9) { + errors.tooShort = true; + } + + if (mobile.length > 9) { + errors.tooLong = true; + } + + if (!CommonService.numericRegExp.test(mobile)) { + errors.invalidPattern = true; + } + + if (mobile.length >= 3) { + const prefix = mobile.substring(0, 3); + if (!MobileService.mobilePrefixes.includes(prefix)) { + errors.invalidPrefix = true; + } + } + + return Object.keys(errors).length > 0 ? errors : null; + } +} diff --git a/src/styles.scss b/src/styles.scss index 58a0759..a0f2da4 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -5,7 +5,6 @@ html, body { height: 100%; - overflow-y: hidden; } body { @@ -37,7 +36,7 @@ body { } .generator { - margin: 0 1em 2em 1em; + margin: 1em; background: rgba( 255, 255, 255, 0.15 ); box-shadow: 0 8px 32px 0 rgba( 0, 38, 135, 0.37 ); border-radius: 3px; -- 2.49.1