WIP
This commit is contained in:
parent
54b057886c
commit
7fc1ae0650
204 changed files with 4345 additions and 134 deletions
182
PlanTempus.Application/wwwroot/ts/modules/lockscreen.ts
Normal file
182
PlanTempus.Application/wwwroot/ts/modules/lockscreen.ts
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/**
|
||||
* Lock Screen Controller
|
||||
*
|
||||
* Handles PIN-based lock screen functionality
|
||||
*/
|
||||
|
||||
import { DrawerController } from './drawers';
|
||||
|
||||
export class LockScreenController {
|
||||
private static readonly CORRECT_PIN = '1234'; // Demo PIN
|
||||
|
||||
private lockScreen: HTMLElement | null = null;
|
||||
private pinInput: HTMLElement | null = null;
|
||||
private pinKeypad: HTMLElement | null = null;
|
||||
private lockTimeEl: HTMLElement | null = null;
|
||||
private pinDigits: NodeListOf<HTMLElement> | null = null;
|
||||
private currentPin = '';
|
||||
private drawers: DrawerController | null = null;
|
||||
|
||||
constructor(drawers?: DrawerController) {
|
||||
this.drawers = drawers ?? null;
|
||||
this.lockScreen = document.getElementById('lockScreen');
|
||||
this.pinInput = document.getElementById('pinInput');
|
||||
this.pinKeypad = document.getElementById('pinKeypad');
|
||||
this.lockTimeEl = document.getElementById('lockTime');
|
||||
this.pinDigits = this.pinInput?.querySelectorAll<HTMLElement>('swp-pin-digit') ?? null;
|
||||
|
||||
this.setupListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lock screen is active
|
||||
*/
|
||||
get isActive(): boolean {
|
||||
return this.lockScreen?.classList.contains('active') ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the lock screen
|
||||
*/
|
||||
show(): void {
|
||||
this.drawers?.closeAll();
|
||||
|
||||
if (this.lockScreen) {
|
||||
this.lockScreen.classList.add('active');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
this.currentPin = '';
|
||||
this.updateDisplay();
|
||||
|
||||
// Update lock time
|
||||
if (this.lockTimeEl) {
|
||||
this.lockTimeEl.textContent = `Låst kl. ${this.formatTime()}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the lock screen
|
||||
*/
|
||||
hide(): void {
|
||||
if (this.lockScreen) {
|
||||
this.lockScreen.classList.remove('active');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
this.currentPin = '';
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
private formatTime(): string {
|
||||
const now = new Date();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
return `${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
private updateDisplay(): void {
|
||||
if (!this.pinDigits) return;
|
||||
|
||||
this.pinDigits.forEach((digit, index) => {
|
||||
digit.classList.remove('filled', 'error');
|
||||
if (index < this.currentPin.length) {
|
||||
digit.textContent = '•';
|
||||
digit.classList.add('filled');
|
||||
} else {
|
||||
digit.textContent = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private showError(): void {
|
||||
if (!this.pinDigits) return;
|
||||
|
||||
this.pinDigits.forEach(digit => digit.classList.add('error'));
|
||||
|
||||
// Shake animation
|
||||
this.pinInput?.classList.add('shake');
|
||||
|
||||
setTimeout(() => {
|
||||
this.currentPin = '';
|
||||
this.updateDisplay();
|
||||
this.pinInput?.classList.remove('shake');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
private verify(): void {
|
||||
if (this.currentPin === LockScreenController.CORRECT_PIN) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.showError();
|
||||
}
|
||||
}
|
||||
|
||||
private addDigit(digit: string): void {
|
||||
if (this.currentPin.length >= 4) return;
|
||||
|
||||
this.currentPin += digit;
|
||||
this.updateDisplay();
|
||||
|
||||
// Auto-verify when 4 digits entered
|
||||
if (this.currentPin.length === 4) {
|
||||
setTimeout(() => this.verify(), 200);
|
||||
}
|
||||
}
|
||||
|
||||
private removeDigit(): void {
|
||||
if (this.currentPin.length === 0) return;
|
||||
this.currentPin = this.currentPin.slice(0, -1);
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
private clearPin(): void {
|
||||
this.currentPin = '';
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
private setupListeners(): void {
|
||||
// Keypad click handler
|
||||
this.pinKeypad?.addEventListener('click', (e) => this.handleKeypadClick(e));
|
||||
|
||||
// Keyboard input
|
||||
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
|
||||
|
||||
// Lock button in sidebar
|
||||
document.querySelector<HTMLElement>('swp-side-menu-action.lock')
|
||||
?.addEventListener('click', () => this.show());
|
||||
}
|
||||
|
||||
private handleKeypadClick(e: Event): void {
|
||||
const target = e.target as HTMLElement;
|
||||
const key = target.closest<HTMLElement>('swp-pin-key');
|
||||
|
||||
if (!key) return;
|
||||
|
||||
const digit = key.dataset.digit;
|
||||
const action = key.dataset.action;
|
||||
|
||||
if (digit) {
|
||||
this.addDigit(digit);
|
||||
} else if (action === 'backspace') {
|
||||
this.removeDigit();
|
||||
} else if (action === 'clear') {
|
||||
this.clearPin();
|
||||
}
|
||||
}
|
||||
|
||||
private handleKeyboard(e: KeyboardEvent): void {
|
||||
if (!this.isActive) return;
|
||||
|
||||
// Prevent default to avoid other interactions
|
||||
e.preventDefault();
|
||||
|
||||
if (e.key >= '0' && e.key <= '9') {
|
||||
this.addDigit(e.key);
|
||||
} else if (e.key === 'Backspace') {
|
||||
this.removeDigit();
|
||||
} else if (e.key === 'Escape') {
|
||||
this.clearPin();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue