🎯 Exemplos recomendados
Balanced sample collections from various categories for you to explore
Exemplos de Recursos de Desktop Web TypeScript
Exemplos de recursos de desktop Web TypeScript incluindo caixas de diálogo de arquivo, caixas de mensagem e bandeja do sistema
💻 Caixa de Diálogo de Arquivo typescript
🟢 simple
⭐⭐⭐
Abrir caixas de diálogo de salvar e abrir usando File System Access API e entrada de arquivo tradicional
⏱️ 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 };
💻 Caixa de Mensagem typescript
🟢 simple
⭐⭐⭐
Exibir alertas, confirmações e diálogos modais personalizados para interação do usuário
⏱️ 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 };
💻 Bandeja do Sistema typescript
🟡 intermediate
⭐⭐⭐
Usar Notification API para funcionalidades tipo bandeja do sistema com badges e permissões
⏱️ 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 };