🎯 empfohlene Sammlungen
Balanced sample collections from various categories for you to explore
Web TypeScript Desktop-Funktionsbeispiele
Web TypeScript Desktop-Funktionsbeispiele einschließlich Dateidialoge, Meldungsfelder und Systemleiste
💻 Dateidialog typescript
🟢 simple
⭐⭐⭐
Datei-Speichern- und -Öffnen-Dialoge mit File System Access API und herkömmlicher Dateieingabe öffnen
⏱️ 20 min
🏷️ typescript, web, desktop features
Prerequisites:
Basic TypeScript, File System Access API
// Web TypeScript File Dialog Examples
// Using File System Access API and File Input for desktop-like file dialogs
// 1. File Dialog Manager
class FileDialogManager {
// Check if File System Access API is supported
isFileSystemAccessAPISupported(): boolean {
return 'showOpenFilePicker' in window;
}
// Open file picker (modern API)
async openFile(options: {
multiple?: boolean;
types?: Array<{ description: string; accept: Record<string, string[]> }>;
} = {}): Promise<File[]> {
if (!this.isFileSystemAccessAPISupported()) {
return this.openFileLegacy(options);
}
const opts: OpenFilePickerOptions = {
multiple: options.multiple || false
};
if (options.types) {
opts.types = options.types;
}
try {
const handles = await (window as any).showOpenFilePicker(opts);
const files: File[] = [];
for (const handle of handles) {
const file = await handle.getFile();
files.push(file);
}
return files;
} catch (error) {
// User cancelled
if ((error as Error).name === 'AbortError') {
return [];
}
throw error;
}
}
// Save file picker (modern API)
async saveFile(options: {
suggestedName?: string;
types?: Array<{ description: string; accept: Record<string, string[]> }>;
} = {}): Promise<FileSystemFileHandle | null> {
if (!this.isFileSystemAccessAPISupported()) {
return this.saveFileLegacy(options);
}
const opts: SaveFilePickerOptions = {};
if (options.suggestedName) {
opts.suggestedName = options.suggestedName;
}
if (options.types) {
opts.types = options.types;
}
try {
const handle = await (window as any).showSaveFilePicker(opts);
return handle;
} catch (error) {
// User cancelled
if ((error as Error).name === 'AbortError') {
return null;
}
throw error;
}
}
// Open directory picker
async openDirectory(): Promise<FileSystemDirectoryHandle | null> {
if (!this.isFileSystemAccessAPISupported()) {
throw new Error('Directory picker not supported in this browser');
}
try {
const handle = await (window as any).showDirectoryPicker();
return handle;
} catch (error) {
// User cancelled
if ((error as Error).name === 'AbortError') {
return null;
}
throw error;
}
}
// Legacy file open (input element)
private openFileLegacy(options: {
multiple?: boolean;
types?: Array<{ description: string; accept: Record<string, string[]> }>;
}): Promise<File[]> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.multiple = options.multiple || false;
if (options.types && options.types.length > 0) {
const accept = options.types.flatMap(t => Object.keys(t.accept).flatMap(ext => t.accept[ext]));
input.accept = accept.join(',');
}
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(Array.from(files));
} else {
resolve([]);
}
};
input.oncancel = () => {
resolve([]);
};
input.onerror = () => {
reject(new Error('File selection failed'));
};
input.click();
});
}
// Legacy file save (download)
private async saveFileLegacy(options: {
suggestedName?: string;
}): Promise<FileSystemFileHandle | null> {
// For legacy, we'll create a download trigger
// Note: Can't return FileSystemFileHandle in legacy mode
return null;
}
}
// 2. File Type Presets
class FileTypePresets {
// Get common file type presets
static getPresets(): Record<string, Array<{ description: string; accept: Record<string, string[]> }>> {
return {
images: [
{
description: 'Image Files',
accept: {
'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']
}
}
],
text: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
'text/html': ['.html', '.htm'],
'text/css': ['.css'],
'text/javascript': ['.js']
}
}
],
json: [
{
description: 'JSON Files',
accept: {
'application/json': ['.json']
}
}
],
documents: [
{
description: 'Documents',
accept: {
'application/pdf': ['.pdf'],
'application/msword': ['.doc'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx']
}
}
],
all: []
};
}
// Get preset by name
static getPreset(name: string): Array<{ description: string; accept: Record<string, string[]> }> {
return this.getPresets()[name] || [];
}
}
// 3. File Content Manager
class FileContentManager {
// Read file content
async readFile(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsText(file);
});
}
// Read file as binary
async readFileAsBinary(file: File): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as ArrayBuffer);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsArrayBuffer(file);
});
}
// Write content to file handle
async writeFile(handle: FileSystemFileHandle, content: string): Promise<void> {
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
}
// Write binary to file handle
async writeBinaryFile(handle: FileSystemFileHandle, content: ArrayBuffer): Promise<void> {
const writable = await handle.createWritable();
await writable.write(content);
await writable.close();
}
// Append to file
async appendToFile(handle: FileSystemFileHandle, content: string): Promise<void> {
const writable = await handle.createWritable({ keepExistingData: true });
const existing = await this.readFile(await handle.getFile());
await writable.write(existing + content);
await writable.close();
}
}
// 4. File Dialog Builder
class FileDialogBuilder {
private options: {
multiple?: boolean;
suggestedName?: string;
types?: Array<{ description: string; accept: Record<string, string[]> }>;
} = {};
// Set multiple selection
allowMultiple(allow: boolean = true): this {
this.options.multiple = allow;
return this;
}
// Set suggested name
setSuggestedName(name: string): this {
this.options.suggestedName = name;
return this;
}
// Add file type filter
addTypeFilter(description: string, mimeType: string, extensions: string[]): this {
if (!this.options.types) {
this.options.types = [];
}
this.options.types.push({
description,
accept: { [mimeType]: extensions }
});
return this;
}
// Add preset type filter
addPresetFilter(presetName: string): this {
const preset = FileTypePresets.getPreset(presetName);
if (preset.length > 0) {
if (!this.options.types) {
this.options.types = [];
}
this.options.types.push(...preset);
}
return this;
}
// Build and open dialog
async open(): Promise<File[]> {
const manager = new FileDialogManager();
return manager.openFile(this.options);
}
// Build and save dialog
async save(): Promise<FileSystemFileHandle | null> {
const manager = new FileDialogManager();
return manager.saveFile(this.options);
}
}
// 5. Recent Files Manager
class RecentFilesManager {
private storageKey: string = 'recentFiles';
private maxFiles: number = 10;
// Add file to recent list
async addFile(file: File): Promise<void> {
const recent = await getRecentFiles();
const fileData = {
name: file.name,
size: file.size,
type: file.type,
lastModified: file.lastModified,
timestamp: Date.now()
};
// Remove if already exists
const filtered = recent.filter(f => f.name !== fileData.name);
// Add to front
filtered.unshift(fileData);
// Keep only max files
const trimmed = filtered.slice(0, this.maxFiles);
localStorage.setItem(this.storageKey, JSON.stringify(trimmed));
}
// Get recent files
async getRecentFiles(): Promise<Array<{
name: string;
size: number;
type: string;
lastModified: number;
timestamp: number;
}>> {
const stored = localStorage.getItem(this.storageKey);
if (!stored) {
return [];
}
return JSON.parse(stored);
}
// Clear recent files
clearRecentFiles(): void {
localStorage.removeItem(this.storageKey);
}
// Remove specific file
async removeFile(fileName: string): Promise<void> {
const recent = await this.getRecentFiles();
const filtered = recent.filter(f => f.name !== fileName);
localStorage.setItem(this.storageKey, JSON.stringify(filtered));
}
}
// 6. File Drop Zone Handler
class FileDropZoneHandler {
private element: HTMLElement;
private onDrop?: (files: File[]) => void;
private onDragEnter?: () => void;
private onDragLeave?: () => void;
constructor(element: HTMLElement) {
this.element = element;
}
// Set drop handler
setDropHandler(handler: (files: File[]) => void): this {
this.onDrop = handler;
return this;
}
// Set drag enter handler
setDragEnterHandler(handler: () => void): this {
this.onDragEnter = handler;
return this;
}
// Set drag leave handler
setDragLeaveHandler(handler: () => void): this {
this.onDragLeave = handler;
return this;
}
// Enable drop zone
enable(): void {
this.element.addEventListener('dragover', this.handleDragOver);
this.element.addEventListener('dragenter', this.handleDragEnter);
this.element.addEventListener('dragleave', this.handleDragLeave);
this.element.addEventListener('drop', this.handleDrop);
}
// Disable drop zone
disable(): void {
this.element.removeEventListener('dragover', this.handleDragOver);
this.element.removeEventListener('dragenter', this.handleDragEnter);
this.element.removeEventListener('dragleave', this.handleDragLeave);
this.element.removeEventListener('drop', this.handleDrop);
}
private handleDragOver = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
if (e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy';
}
};
private handleDragEnter = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
this.element.classList.add('drag-over');
if (this.onDragEnter) {
this.onDragEnter();
}
};
private handleDragLeave = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
this.element.classList.remove('drag-over');
if (this.onDragLeave) {
this.onDragLeave();
}
};
private handleDrop = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
this.element.classList.remove('drag-over');
if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {
const files = Array.from(e.dataTransfer.files);
if (this.onDrop) {
this.onDrop(files);
}
}
};
}
// Usage Examples
async function demonstrateFileDialog() {
console.log('=== Web TypeScript File Dialog Examples ===\n');
const dialogManager = new FileDialogManager();
const contentManager = new FileContentManager();
const recentManager = new RecentFilesManager();
// 1. Check support
console.log('--- 1. API Support ---');
console.log(`File System Access API: ${dialogManager.isFileSystemAccessAPISupported() ? 'Supported' : 'Not Supported'}`);
// 2. Open single file
console.log('\n--- 2. Open Single File ---');
const singleFile = await dialogManager.openFile({
types: FileTypePresets.getPreset('text')
});
console.log(`Selected ${singleFile.length} file(s)`);
// 3. Open multiple files
console.log('\n--- 3. Open Multiple Files ---');
const multipleFiles = await dialogManager.openFile({
multiple: true,
types: FileTypePresets.getPreset('images')
});
console.log(`Selected ${multipleFiles.length} file(s)`);
// 4. Save file
console.log('\n--- 4. Save File ---');
const savedHandle = await dialogManager.saveFile({
suggestedName: 'document.txt',
types: FileTypePresets.getPreset('text')
});
if (savedHandle) {
await contentManager.writeFile(savedHandle, 'Hello, World!');
console.log('File saved successfully');
}
// 5. File dialog builder
console.log('\n--- 5. File Dialog Builder ---');
const builder = new FileDialogBuilder()
.allowMultiple()
.addPresetFilter('images');
const files = await builder.open();
console.log(`Builder selected ${files.length} file(s)`);
// 6. Recent files
console.log('\n--- 6. Recent Files ---');
if (files.length > 0) {
await recentManager.addFile(files[0]);
}
const recentFiles = await recentManager.getRecentFiles();
console.log(`Recent files: ${recentFiles.map(f => f.name).join(', ')}`);
// 7. Drop zone
console.log('\n--- 7. Drop Zone ---');
const dropZone = document.createElement('div');
dropZone.style.width = '400px';
dropZone.style.height = '200px';
dropZone.style.border = '2px dashed #ccc';
dropZone.style.display = 'flex';
dropZone.style.alignItems = 'center';
dropZone.style.justifyContent = 'center';
dropZone.textContent = 'Drop files here';
const dropHandler = new FileDropZoneHandler(dropZone)
.setDropHandler((files) => {
console.log(`Dropped ${files.length} file(s)`);
files.forEach(f => console.log(` - ${f.name}`));
})
.setDragEnterHandler(() => {
dropZone.style.borderColor = '#4CAF50';
dropZone.style.backgroundColor = '#f0f0f0';
})
.setDragLeaveHandler(() => {
dropZone.style.borderColor = '#ccc';
dropZone.style.backgroundColor = 'transparent';
});
dropHandler.enable();
console.log('Drop zone created (would be attached to DOM in real usage)');
console.log('\n=== All File Dialog Examples Completed ===');
}
// Helper function for RecentFilesManager
async function getRecentFiles() {
return new Promise<Array<any>>((resolve) => {
const stored = localStorage.getItem('recentFiles');
resolve(stored ? JSON.parse(stored) : []);
});
}
// Export functions
export { FileDialogManager, FileTypePresets, FileContentManager, FileDialogBuilder, RecentFilesManager, FileDropZoneHandler };
export { demonstrateFileDialog };
💻 Meldungsfeld typescript
🟢 simple
⭐⭐⭐
Warnungen, Bestätigungen und benutzerdefinierte Modaldialoge für Benutzerinteraktionen anzeigen
⏱️ 20 min
🏷️ typescript, web, desktop features
Prerequisites:
Basic TypeScript, DOM manipulation
// Web TypeScript Message Box Examples
// Custom dialogs and modal boxes for desktop-like messaging
// 1. Message Box Types
type MessageBoxType = 'info' | 'warning' | 'error' | 'success' | 'question';
type MessageBoxIcon = 'info' | 'warning' | 'error' | 'success' | 'question';
// 2. Message Box Options
interface MessageBoxOptions {
title?: string;
message: string;
type?: MessageBoxType;
buttons?: Array<{ text: string; value?: any; primary?: boolean }>;
defaultButton?: number;
width?: string;
closable?: boolean;
backdrop?: boolean;
}
// 3. Message Box Result
interface MessageBoxResult {
closed: boolean;
buttonValue?: any;
}
// 4. Message Box Manager
class MessageBoxManager {
private overlay: HTMLDivElement | null = null;
private activeMessageBox: HTMLElement | null = null;
// Show alert message
async alert(message: string, title: string = 'Alert'): Promise<void> {
await this.show({
title,
message,
type: 'info',
buttons: [{ text: 'OK', value: 'ok', primary: true }]
});
}
// Show confirm dialog
async confirm(message: string, title: string = 'Confirm'): Promise<boolean> {
const result = await this.show({
title,
message,
type: 'question',
buttons: [
{ text: 'Yes', value: true, primary: true },
{ text: 'No', value: false }
]
});
return result.buttonValue === true;
}
// Show prompt dialog
async prompt(message: string, defaultValue: string = '', title: string = 'Prompt'): Promise<string | null> {
const result = await this.show({
title,
message,
type: 'question',
buttons: [
{ text: 'OK', value: 'ok', primary: true },
{ text: 'Cancel', value: 'cancel' }
]
});
if (result.buttonValue === 'ok') {
const input = this.activeMessageBox?.querySelector('input') as HTMLInputElement;
return input ? input.value : defaultValue;
}
return null;
}
// Show custom message box
async show(options: MessageBoxOptions): Promise<MessageBoxResult> {
return new Promise((resolve) => {
// Create overlay
this.createOverlay();
// Create message box
const messageBox = this.createMessageBox(options);
// Add to DOM
document.body.appendChild(messageBox);
this.activeMessageBox = messageBox;
// Focus
setTimeout(() => {
const primaryButton = messageBox.querySelector('.btn-primary') as HTMLButtonElement;
if (primaryButton) {
primaryButton.focus();
}
}, 100);
// Setup handlers
const closeHandler = (buttonValue?: any) => {
this.close(messageBox);
resolve({ closed: true, buttonValue });
};
// Add button click handlers
const buttons = messageBox.querySelectorAll('button');
buttons.forEach((button, index) => {
button.addEventListener('click', () => {
const value = options.buttons?.[index].value;
closeHandler(value);
});
});
// Handle backdrop click
if (options.backdrop !== false) {
this.overlay?.addEventListener('click', () => {
if (options.closable !== false) {
closeHandler();
}
});
}
// Handle ESC key
const escapeHandler = (e: KeyboardEvent) => {
if (e.key === 'Escape' && options.closable !== false) {
closeHandler();
document.removeEventListener('keydown', escapeHandler);
}
};
document.addEventListener('keydown', escapeHandler);
});
}
// Create overlay
private createOverlay(): void {
if (!this.overlay) {
this.overlay = document.createElement('div');
this.overlay.className = 'message-box-overlay';
this.overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
`;
document.body.appendChild(this.overlay);
}
}
// Create message box
private createMessageBox(options: MessageBoxOptions): HTMLElement {
const box = document.createElement('div');
box.className = 'message-box';
const type = options.type || 'info';
box.style.cssText = `
background: white;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
min-width: 300px;
max-width: ${options.width || '500px'};
max-height: 80vh;
overflow: auto;
display: flex;
flex-direction: column;
`;
// Header
const header = this.createHeader(options.title || this.getDefaultTitle(type), type, options.closable);
box.appendChild(header);
// Content
const content = this.createContent(options.message, type);
box.appendChild(content);
// Footer (buttons)
const footer = this.createFooter(options.buttons || [{ text: 'OK', value: 'ok', primary: true }]);
box.appendChild(footer);
return box;
}
// Create header
private createHeader(title: string, type: MessageBoxType, closable?: boolean): HTMLElement {
const header = document.createElement('div');
header.style.cssText = `
padding: 16px 20px;
border-bottom: 1px solid #e0e0e0;
display: flex;
align-items: center;
gap: 12px;
`;
// Icon
const icon = document.createElement('div');
icon.textContent = this.getIconForType(type);
icon.style.cssText = 'font-size: 24px;';
header.appendChild(icon);
// Title
const titleElement = document.createElement('div');
titleElement.textContent = title;
titleElement.style.cssText = `
flex: 1;
font-size: 18px;
font-weight: 600;
color: #333;
`;
header.appendChild(titleElement);
// Close button
if (closable !== false) {
const closeButton = document.createElement('button');
closeButton.innerHTML = '×';
closeButton.style.cssText = `
background: none;
border: none;
font-size: 24px;
cursor: pointer;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
`;
closeButton.onclick = () => {
this.close(header.parentElement as HTMLElement);
};
header.appendChild(closeButton);
}
return header;
}
// Create content
private createContent(message: string, type: MessageBoxType): HTMLElement {
const content = document.createElement('div');
content.style.cssText = `
padding: 20px;
color: #666;
line-height: 1.5;
`;
if (type === 'question') {
// Add input for prompt
const input = document.createElement('input');
input.type = 'text';
input.style.cssText = `
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
margin-top: 12px;
`;
content.appendChild(document.createTextNode(message));
content.appendChild(document.createElement('br'));
content.appendChild(input);
} else {
content.textContent = message;
}
return content;
}
// Create footer
private createFooter(buttons: Array<{ text: string; value?: any; primary?: boolean }>): HTMLElement {
const footer = document.createElement('div');
footer.style.cssText = `
padding: 12px 20px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
gap: 8px;
`;
buttons.forEach(buttonConfig => {
const button = document.createElement('button');
button.textContent = buttonConfig.text;
button.className = buttonConfig.primary ? 'btn-primary' : '';
button.style.cssText = `
padding: 8px 16px;
border: 1px solid #ddd;
border-radius: 4px;
background: ${buttonConfig.primary ? '#2196F3' : 'white'};
color: ${buttonConfig.primary ? 'white' : '#666'};
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
`;
button.addEventListener('mouseenter', () => {
button.style.background = buttonConfig.primary ? '#1976D2' : '#f5f5f5';
});
button.addEventListener('mouseleave', () => {
button.style.background = buttonConfig.primary ? '#2196F3' : 'white';
});
footer.appendChild(button);
});
return footer;
}
// Get icon for type
private getIconForType(type: MessageBoxType): string {
const icons: Record<MessageBoxIcon, string> = {
info: 'ℹ️',
warning: '⚠️',
error: '❌',
success: '✅',
question: '❓'
};
return icons[type as MessageBoxIcon] || 'ℹ️';
}
// Get default title
private getDefaultTitle(type: MessageBoxType): string {
const titles: Record<MessageBoxType, string> = {
info: 'Information',
warning: 'Warning',
error: 'Error',
success: 'Success',
question: 'Question'
};
return titles[type];
}
// Close message box
private close(messageBox: HTMLElement): void {
messageBox.remove();
if (this.overlay && document.querySelectorAll('.message-box').length === 0) {
this.overlay.remove();
this.overlay = null;
}
this.activeMessageBox = null;
}
}
// 5. Toast Manager
class ToastManager {
private container: HTMLElement | null = null;
// Show toast notification
show(
message: string,
type: 'info' | 'success' | 'warning' | 'error' = 'info',
duration: number = 3000
): void {
this.ensureContainer();
const toast = this.createToast(message, type);
this.container!.appendChild(toast);
// Auto dismiss
setTimeout(() => {
this.dismiss(toast);
}, duration);
}
// Show success toast
success(message: string, duration?: number): void {
this.show(message, 'success', duration);
}
// Show error toast
error(message: string, duration?: number): void {
this.show(message, 'error', duration);
}
// Show warning toast
warning(message: string, duration?: number): void {
this.show(message, 'warning', duration);
}
// Ensure container exists
private ensureContainer(): void {
if (!this.container) {
this.container = document.createElement('div');
this.container.className = 'toast-container';
this.container.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 10001;
display: flex;
flex-direction: column;
gap: 10px;
`;
document.body.appendChild(this.container);
}
}
// Create toast element
private createToast(message: string, type: string): HTMLElement {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
const colors: Record<string, string> = {
info: '#2196F3',
success: '#4CAF50',
warning: '#FF9800',
error: '#F44336'
};
toast.style.cssText = `
background: ${colors[type] || colors.info};
color: white;
padding: 12px 16px;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
min-width: 250px;
max-width: 400px;
animation: slideIn 0.3s ease-out;
display: flex;
align-items: center;
gap: 8px;
`;
const icon = document.createElement('span');
icon.textContent = this.getToastIcon(type);
toast.appendChild(icon);
const text = document.createElement('span');
text.textContent = message;
toast.appendChild(text);
return toast;
}
// Get toast icon
private getToastIcon(type: string): string {
const icons: Record<string, string> = {
info: 'ℹ️',
success: '✅',
warning: '⚠️',
error: '❌'
};
return icons[type] || 'ℹ️';
}
// Dismiss toast
private dismiss(toast: HTMLElement): void {
toast.style.animation = 'slideOut 0.3s ease-in';
setTimeout(() => {
toast.remove();
if (this.container && this.container.children.length === 0) {
this.container.remove();
this.container = null;
}
}, 300);
}
}
// 6. Progress Dialog
class ProgressDialog {
private messageBox: MessageBoxManager;
private currentElement: HTMLElement | null = null;
constructor() {
this.messageBox = new MessageBoxManager();
}
// Show progress
async show(options: {
title: string;
message: string;
progress: number;
}): Promise<void> {
// Create custom progress dialog
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
`;
const dialog = document.createElement('div');
dialog.style.cssText = `
background: white;
border-radius: 8px;
padding: 24px;
min-width: 300px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
`;
dialog.innerHTML = `
<h3 style="margin: 0 0 16px 0; font-size: 18px;">${options.title}</h3>
<p style="margin: 0 0 16px 0; color: #666;">${options.message}</p>
<div style="
width: 100%;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
">
<div style="
width: ${options.progress}%;
height: 100%;
background: #2196F3;
transition: width 0.3s;
"></div>
</div>
<p style="margin: 8px 0 0 0; text-align: center; color: #999; font-size: 14px;">
${Math.round(options.progress)}%
</p>
`;
overlay.appendChild(dialog);
document.body.appendChild(overlay);
this.currentElement = overlay;
}
// Update progress
update(progress: number): void {
if (this.currentElement) {
const progressBar = this.currentElement.querySelector('div > div') as HTMLDivElement;
const progressText = this.currentElement.querySelector('p:last-child') as HTMLParagraphElement;
if (progressBar) {
progressBar.style.width = `${progress}%`;
}
if (progressText) {
progressText.textContent = `${Math.round(progress)}%`;
}
}
}
// Close progress dialog
close(): void {
if (this.currentElement) {
this.currentElement.remove();
this.currentElement = null;
}
}
}
// Usage Examples
async function demonstrateMessageBox() {
console.log('=== Web TypeScript Message Box Examples ===\n');
const messageBox = new MessageBoxManager();
const toast = new ToastManager();
const progressDialog = new ProgressDialog();
// 1. Alert
console.log('--- 1. Alert ---');
await messageBox.alert('This is an alert message!', 'Alert');
// 2. Confirm
console.log('\n--- 2. Confirm ---');
const confirmed = await messageBox.confirm('Do you want to continue?', 'Confirm');
console.log(`Confirmed: ${confirmed}`);
// 3. Prompt
console.log('\n--- 3. Prompt ---');
const name = await messageBox.prompt('Please enter your name:', '', 'Input Required');
console.log(`Name: ${name}`);
// 4. Custom message box
console.log('\n--- 4. Custom Message Box ---');
const customResult = await messageBox.show({
title: 'Custom Dialog',
message: 'This is a custom message box with multiple buttons.',
type: 'question',
buttons: [
{ text: 'Option A', value: 'a', primary: false },
{ text: 'Option B', value: 'b', primary: true },
{ text: 'Option C', value: 'c', primary: false }
]
});
console.log(`Selected: ${customResult.buttonValue}`);
// 5. Toast notifications
console.log('\n--- 5. Toast Notifications ---');
toast.show('This is an info toast');
toast.success('Operation completed successfully!');
toast.warning('This is a warning message');
toast.error('An error occurred!');
// 6. Progress dialog
console.log('\n--- 6. Progress Dialog ---');
await progressDialog.show({
title: 'Processing',
message: 'Please wait while we process...',
progress: 0
});
for (let i = 0; i <= 100; i += 10) {
progressDialog.update(i);
await new Promise(resolve => setTimeout(resolve, 200));
}
progressDialog.close();
console.log('\n=== All Message Box Examples Completed ===');
}
// Export functions
export { MessageBoxManager, ToastManager, ProgressDialog };
export { demonstrateMessageBox };
export type { MessageBoxOptions, MessageBoxResult, MessageBoxType };
💻 Systemleiste typescript
🟡 intermediate
⭐⭐⭐
Notification API für Systemleisten-ähnliche Funktionen mit Badges und Berechtigungen verwenden
⏱️ 25 min
🏷️ typescript, web, desktop features
Prerequisites:
Intermediate TypeScript, Notification API
// Web TypeScript System Tray Examples
// Using Notification API and favicon badges for system tray-like features
// 1. Notification Manager
class NotificationManager {
private permission: NotificationPermission = 'default';
// Initialize notification manager
async initialize(): Promise<void> {
if (!this.isSupported()) {
console.warn('Notifications not supported');
return;
}
this.permission = Notification.permission;
if (this.permission === 'default') {
this.permission = await Notification.requestPermission();
}
}
// Check if notifications are supported
isSupported(): boolean {
return 'Notification' in window;
}
// Check if permission is granted
isGranted(): boolean {
return this.permission === 'granted';
}
// Request permission
async requestPermission(): Promise<NotificationPermission> {
if (!this.isSupported()) {
return 'denied';
}
this.permission = await Notification.requestPermission();
return this.permission;
}
// Show notification
show(options: {
title: string;
body: string;
icon?: string;
image?: string;
badge?: string;
tag?: string;
data?: any;
actions?: Array<{ action: string; title: string; icon?: string }>;
vibrate?: number[];
sound?: string;
requireInteraction?: boolean;
}): Notification | null {
if (!this.isGranted()) {
console.warn('Notification permission not granted');
return null;
}
const notification = new Notification(options.title, {
body: options.body,
icon: options.icon,
image: options.image,
badge: options.badge,
tag: options.tag,
data: options.data,
actions: options.actions,
vibrate: options.vibrate,
requireInteraction: options.requireInteraction
});
return notification;
}
// Show simple notification
notify(title: string, body: string): Notification | null {
return this.show({ title, body });
}
// Show success notification
success(message: string, title: string = 'Success'): Notification | null {
return this.show({
title,
body: message,
icon: this.getIcon('success')
});
}
// Show error notification
error(message: string, title: string = 'Error'): Notification | null {
return this.show({
title,
body: message,
icon: this.getIcon('error'),
vibrate: [200, 100, 200]
});
}
// Show warning notification
warning(message: string, title: string = 'Warning'): Notification | null {
return this.show({
title,
body: message,
icon: this.getIcon('warning')
});
}
// Show info notification
info(message: string, title: string = 'Information'): Notification | null {
return this.show({
title,
body: message,
icon: this.getIcon('info')
});
}
// Show progress notification
async showProgress(
title: string,
progress: number,
options: {
body?: string;
tag?: string;
} = {}
): Promise<Notification | null> {
const notification = this.show({
title,
body: options.body || `${Math.round(progress)}% complete`,
tag: options.tag || 'progress',
requireInteraction: false,
data: { progress }
});
return notification;
}
// Close notification by tag
closeByTag(tag: string): void {
// Note: Notification.close() only works on the notification instance
// This is a placeholder for a tracking system
}
// Get icon URL
private getIcon(type: 'success' | 'error' | 'warning' | 'info'): string {
// In a real app, these would be actual icon URLs
const icons: Record<string, string> = {
success: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%234CAF50"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>',
error: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23F44336"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>',
warning: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23FF9800"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg>',
info: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%232196F3"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>'
};
return icons[type];
}
}
// 2. Favicon Badge Manager
class FaviconBadgeManager {
private originalFavicon: string = '';
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
constructor() {
this.canvas = document.createElement('canvas');
this.canvas.width = 32;
this.canvas.height = 32;
this.ctx = this.canvas.getContext('2d')!;
// Store original favicon
const link = document.querySelector('link[rel="icon"]') as HTMLLinkElement;
if (link) {
this.originalFavicon = link.href;
}
}
// Set badge number
setBadge(count: number, color: string = '#F44336'): void {
// Clear canvas
this.ctx.clearRect(0, 0, 32, 32);
// Draw original favicon (simplified - would need to load actual favicon)
this.ctx.fillStyle = '#2196F3';
this.ctx.fillRect(0, 0, 32, 32);
// Draw badge circle
if (count > 0) {
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(24, 24, 10, 0, Math.PI * 2);
this.ctx.fill();
// Draw count text
this.ctx.fillStyle = 'white';
this.ctx.font = 'bold 12px Arial';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
const displayCount = count > 99 ? '99+' : count.toString();
this.ctx.fillText(displayCount, 24, 24);
}
// Update favicon
const dataURL = this.canvas.toDataURL();
this.updateFavicon(dataURL);
}
// Set badge text
setBadgeText(text: string, color: string = '#F44336'): void {
this.ctx.clearRect(0, 0, 32, 32);
// Draw original favicon background
this.ctx.fillStyle = '#2196F3';
this.ctx.fillRect(0, 0, 32, 32);
if (text) {
// Draw badge circle
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(24, 24, 10, 0, Math.PI * 2);
this.ctx.fill();
// Draw text
this.ctx.fillStyle = 'white';
this.ctx.font = 'bold 10px Arial';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
this.ctx.fillText(text.substring(0, 3), 24, 24);
}
const dataURL = this.canvas.toDataURL();
this.updateFavicon(dataURL);
}
// Set badge dot
setBadgeDot(color: string = '#F44336'): void {
this.ctx.clearRect(0, 0, 32, 32);
// Draw original favicon background
this.ctx.fillStyle = '#2196F3';
this.ctx.fillRect(0, 0, 32, 32);
// Draw dot
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(26, 26, 6, 0, Math.PI * 2);
this.ctx.fill();
const dataURL = this.canvas.toDataURL();
this.updateFavicon(dataURL);
}
// Clear badge
clearBadge(): void {
if (this.originalFavicon) {
this.updateFavicon(this.originalFavicon);
}
}
// Update favicon
private updateFavicon(href: string): void {
let link = document.querySelector('link[rel="icon"]') as HTMLLinkElement;
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
}
link.href = href;
}
}
// 3. Notification Scheduler
class NotificationScheduler {
private notifications: Map<string, NodeJS.Timeout> = new Map();
// Schedule notification
schedule(
id: string,
delay: number,
options: {
title: string;
body: string;
icon?: string;
}
): void {
// Clear existing if any
this.cancel(id);
// Schedule new notification
const timeout = setTimeout(() => {
const manager = new NotificationManager();
manager.show(options);
this.notifications.delete(id);
}, delay);
this.notifications.set(id, timeout);
}
// Schedule at specific time
scheduleAt(
id: string,
date: Date,
options: {
title: string;
body: string;
icon?: string;
}
): void {
const delay = date.getTime() - Date.now();
if (delay > 0) {
this.schedule(id, delay, options);
}
}
// Cancel scheduled notification
cancel(id: string): void {
const timeout = this.notifications.get(id);
if (timeout) {
clearTimeout(timeout);
this.notifications.delete(id);
}
}
// Cancel all scheduled notifications
cancelAll(): void {
for (const [id, timeout] of this.notifications) {
clearTimeout(timeout);
}
this.notifications.clear();
}
}
// 4. Notification Categories
class NotificationCategories {
private manager: NotificationManager;
constructor() {
this.manager = new NotificationManager();
}
// New message notification
newMessage(from: string, message: string): Notification | null {
return this.manager.show({
title: `New message from ${from}`,
body: message,
icon: this.getIcon('message'),
tag: 'message',
data: { type: 'message', from }
});
}
// Download complete notification
downloadComplete(fileName: string): Notification | null {
return this.manager.show({
title: 'Download Complete',
body: `${fileName} has been downloaded`,
icon: this.getIcon('download'),
tag: 'download',
actions: [
{ action: 'open', title: 'Open' },
{ action: 'dismiss', title: 'Dismiss' }
]
});
}
// System update notification
systemUpdate(version: string): Notification | null {
return this.manager.show({
title: 'System Update Available',
body: `Version ${version} is ready to install`,
icon: this.getIcon('update'),
tag: 'system-update',
requireInteraction: true,
actions: [
{ action: 'install', title: 'Install Now' },
{ action: 'later', title: 'Later' }
]
});
}
// Reminder notification
reminder(title: string, message: string): Notification | null {
return this.manager.show({
title,
body: message,
icon: this.getIcon('reminder'),
tag: 'reminder',
vibrate: [200, 100, 200]
});
}
private getIcon(type: string): string {
// Placeholder for actual icon URLs
return 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%232196F3"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/></svg>';
}
}
// 5. Notification History
class NotificationHistory {
private storageKey: string = 'notificationHistory';
private maxEntries: number = 50;
// Add to history
add(notification: {
title: string;
body: string;
timestamp: number;
type?: string;
}): void {
const history = this.getHistory();
history.unshift(notification);
// Keep only max entries
const trimmed = history.slice(0, this.maxEntries);
localStorage.setItem(this.storageKey, JSON.stringify(trimmed));
}
// Get history
getHistory(): Array<{
title: string;
body: string;
timestamp: number;
type?: string;
}> {
const stored = localStorage.getItem(this.storageKey);
if (!stored) {
return [];
}
return JSON.parse(stored);
}
// Clear history
clearHistory(): void {
localStorage.removeItem(this.storageKey);
}
// Get by type
getByType(type: string): Array<{
title: string;
body: string;
timestamp: number;
type?: string;
}> {
const history = this.getHistory();
return history.filter(n => n.type === type);
}
}
// 6. Desktop Notification Manager (Combined)
class DesktopNotificationManager {
private notificationManager: NotificationManager;
private badgeManager: FaviconBadgeManager;
private scheduler: NotificationScheduler;
private categories: NotificationCategories;
private history: NotificationHistory;
constructor() {
this.notificationManager = new NotificationManager();
this.badgeManager = new FaviconBadgeManager();
this.scheduler = new NotificationScheduler();
this.categories = new NotificationCategories();
this.history = new NotificationHistory();
}
// Initialize all systems
async initialize(): Promise<void> {
await this.notificationManager.initialize();
}
// Show notification with badge
notifyWithBadge(
title: string,
body: string,
badgeCount?: number
): Notification | null {
const notification = this.notificationManager.show({ title, body });
if (badgeCount !== undefined) {
this.badgeManager.setBadge(badgeCount);
}
// Add to history
this.history.add({
title,
body,
timestamp: Date.now(),
type: 'notification'
});
return notification;
}
// Update badge
updateBadge(count: number, color?: string): void {
if (count === 0) {
this.badgeManager.clearBadge();
} else {
this.badgeManager.setBadge(count, color);
}
}
// Get notification manager
getNotificationManager(): NotificationManager {
return this.notificationManager;
}
// Get badge manager
getBadgeManager(): FaviconBadgeManager {
return this.badgeManager;
}
// Get scheduler
getScheduler(): NotificationScheduler {
return this.scheduler;
}
// Get categories
getCategories(): NotificationCategories {
return this.categories;
}
// Get history
getHistory(): NotificationHistory {
return this.history;
}
}
// Usage Examples
async function demonstrateSystemTray() {
console.log('=== Web TypeScript System Tray Examples ===\n');
const manager = new DesktopNotificationManager();
// 1. Initialize
console.log('--- 1. Initialize ---');
await manager.initialize();
console.log(`Notifications supported: ${manager.getNotificationManager().isSupported()}`);
console.log(`Permission granted: ${manager.getNotificationManager().isGranted()}`);
// 2. Basic notifications
console.log('\n--- 2. Basic Notifications ---');
manager.getNotificationManager().info('This is an info notification');
await new Promise(resolve => setTimeout(resolve, 1000));
manager.getNotificationManager().success('Operation completed successfully!');
await new Promise(resolve => setTimeout(resolve, 1000));
manager.getNotificationManager().warning('This is a warning message');
await new Promise(resolve => setTimeout(resolve, 1000));
manager.getNotificationManager().error('An error has occurred!');
// 3. Badge notifications
console.log('\n--- 3. Badge Notifications ---');
manager.updateBadge(5);
manager.notifyWithBadge('New messages', 'You have 5 unread messages');
await new Promise(resolve => setTimeout(resolve, 2000));
manager.updateBadge(1);
manager.notifyWithBadge('New message', 'You received a new message', 1);
await new Promise(resolve => setTimeout(resolve, 2000));
manager.updateBadge(0);
// 4. Scheduled notifications
console.log('\n--- 4. Scheduled Notifications ---');
manager.getScheduler().schedule('test', 3000, {
title: 'Scheduled Notification',
body: 'This notification was scheduled 3 seconds ago'
});
console.log('Notification scheduled in 3 seconds...');
// 5. Category notifications
console.log('\n--- 5. Category Notifications ---');
await new Promise(resolve => setTimeout(resolve, 4000));
manager.getCategories().newMessage('Alice', 'Hey, how are you?');
await new Promise(resolve => setTimeout(resolve, 1000));
manager.getCategories().downloadComplete('document.pdf');
await new Promise(resolve => setTimeout(resolve, 1000));
manager.getCategories().reminder('Meeting', 'Team meeting in 15 minutes');
// 6. History
console.log('\n--- 6. Notification History ---');
const history = manager.getHistory().getHistory();
console.log(`Total notifications: ${history.length}`);
history.slice(0, 3).forEach(n => {
console.log(`- ${n.title}: ${n.body}`);
});
console.log('\n=== All System Tray Examples Completed ===');
}
// Export functions
export { NotificationManager, FaviconBadgeManager, NotificationScheduler, NotificationCategories, NotificationHistory, DesktopNotificationManager };
export { demonstrateSystemTray };