consolidation & ux improvements
This commit is contained in:
184
webroot/css/ux-enhancements.css
Normal file
184
webroot/css/ux-enhancements.css
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/* ============================================
|
||||||
|
UX ENHANCEMENTS STYLES - Optimized
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
/* Keyboard Shortcuts Hint */
|
||||||
|
.keyboard-hint {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-toggle {
|
||||||
|
display: block;
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 24px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-toggle:hover { transform: scale(1.1); }
|
||||||
|
|
||||||
|
.hint-content {
|
||||||
|
padding: 15px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-content h4 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
color: #4CAF50;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-content ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-content li {
|
||||||
|
padding: 5px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-content kbd {
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-family: 'Consolas', monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #ffffff;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact Mode Toggle Button */
|
||||||
|
.compact-toggle {
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #cccccc;
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-toggle:hover {
|
||||||
|
background: #3a3a3a;
|
||||||
|
border-color: #666666;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact Mode Styles - Condensed */
|
||||||
|
body.compact-mode .section { padding: 10px; }
|
||||||
|
body.compact-mode .section h2 { font-size: 14px; margin-bottom: 8px; }
|
||||||
|
body.compact-mode .input-group { margin-bottom: 8px; }
|
||||||
|
body.compact-mode .input-group label { font-size: 11px; }
|
||||||
|
body.compact-mode .input-group input { padding: 4px 8px; font-size: 12px; }
|
||||||
|
body.compact-mode .result-item { padding: 6px 10px; }
|
||||||
|
body.compact-mode .result-label,
|
||||||
|
body.compact-mode .result-value { font-size: 11px; }
|
||||||
|
body.compact-mode .hex-calculator { display: none; }
|
||||||
|
body.compact-mode .tab { padding: 6px 12px; font-size: 12px; }
|
||||||
|
body.compact-mode .formula-item { padding: 4px 8px; font-size: 11px; }
|
||||||
|
|
||||||
|
/* Context Menu - Condensed */
|
||||||
|
.context-menu {
|
||||||
|
position: fixed;
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 10000;
|
||||||
|
min-width: 250px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-header {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888888;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #3a3a3a;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu-item:hover { background: #3a3a3a; }
|
||||||
|
|
||||||
|
.format-label { color: #888888; font-weight: bold; margin-right: 15px; }
|
||||||
|
.format-value { color: #4CAF50; font-family: 'Consolas', monospace; font-weight: bold; }
|
||||||
|
|
||||||
|
/* Notification System - Condensed */
|
||||||
|
.notification {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
background: #2a2a2a;
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 13px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 10001;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(400px);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification.show { opacity: 1; transform: translateX(0); }
|
||||||
|
.notification-success { border-left: 4px solid #4CAF50; }
|
||||||
|
.notification-info { border-left: 4px solid #2196F3; }
|
||||||
|
.notification-warning { border-left: 4px solid #FF9800; }
|
||||||
|
.notification-error { border-left: 4px solid #F44336; }
|
||||||
|
|
||||||
|
/* Hover effects & Responsive - Condensed */
|
||||||
|
.result-value, .conv-value { cursor: pointer; transition: background-color 0.3s; }
|
||||||
|
.result-value:hover, .conv-value:hover { background-color: rgba(76, 175, 80, 0.1); }
|
||||||
|
|
||||||
|
button:focus-visible,
|
||||||
|
input:focus-visible,
|
||||||
|
.tab:focus-visible { outline: 2px solid #4CAF50; outline-offset: 2px; }
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.keyboard-hint, .notification { bottom: 10px; right: 10px; }
|
||||||
|
.notification { top: 10px; max-width: calc(100% - 20px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.keyboard-hint,
|
||||||
|
.compact-toggle,
|
||||||
|
.context-menu,
|
||||||
|
.notification { display: none !important; }
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<title>Dateisystem-Offset-Rechner</title>
|
<title>Dateisystem-Offset-Rechner</title>
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="css/main.css">
|
||||||
<link rel="stylesheet" href="css/components.css">
|
<link rel="stylesheet" href="css/components.css">
|
||||||
|
<link rel="stylesheet" href="css/ux-enhancements.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -62,26 +62,10 @@ export class ExFATFilesystem extends BaseFilesystem {
|
|||||||
|
|
||||||
calculate(variantId) {
|
calculate(variantId) {
|
||||||
if (variantId !== 'exfat') return;
|
if (variantId !== 'exfat') return;
|
||||||
|
|
||||||
const values = {};
|
const values = this.getInputValues(variantId);
|
||||||
const results = {};
|
const results = {};
|
||||||
|
|
||||||
// Gather all input values for this variant
|
|
||||||
const variant = this.variants.find(v => v.id === variantId);
|
|
||||||
if (!variant) return;
|
|
||||||
|
|
||||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
|
||||||
const el = document.getElementById(field.id);
|
|
||||||
if (el) {
|
|
||||||
let val = el.value;
|
|
||||||
if (val && val.startsWith('0x')) {
|
|
||||||
values[field.id] = parseInt(val, 16);
|
|
||||||
} else {
|
|
||||||
values[field.id] = Number(val) || 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.calculateExFAT(values, results);
|
this.calculateExFAT(values, results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,10 @@ import { checkDependencies, updateResultItem } from '../utils.js';
|
|||||||
export class FAT12_16Filesystem extends BaseFilesystem {
|
export class FAT12_16Filesystem extends BaseFilesystem {
|
||||||
calculate(variantId) {
|
calculate(variantId) {
|
||||||
if (variantId !== 'fat1216') return;
|
if (variantId !== 'fat1216') return;
|
||||||
const values = {};
|
|
||||||
|
const values = this.getInputValues(variantId);
|
||||||
const results = {};
|
const results = {};
|
||||||
// Gather all input values for this variant
|
|
||||||
const variant = this.variants.find(v => v.id === variantId);
|
|
||||||
if (!variant) return;
|
|
||||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
|
||||||
const el = document.getElementById(field.id);
|
|
||||||
if (el) {
|
|
||||||
let val = el.value;
|
|
||||||
if (val && val.startsWith('0x')) {
|
|
||||||
values[field.id] = parseInt(val, 16);
|
|
||||||
} else {
|
|
||||||
values[field.id] = Number(val) || 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.calculateFAT1216(values, results);
|
this.calculateFAT1216(values, results);
|
||||||
}
|
}
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -211,22 +199,10 @@ export class FAT12_16Filesystem extends BaseFilesystem {
|
|||||||
export class FAT32Filesystem extends BaseFilesystem {
|
export class FAT32Filesystem extends BaseFilesystem {
|
||||||
calculate(variantId) {
|
calculate(variantId) {
|
||||||
if (variantId !== 'fat32') return;
|
if (variantId !== 'fat32') return;
|
||||||
const values = {};
|
|
||||||
|
const values = this.getInputValues(variantId);
|
||||||
const results = {};
|
const results = {};
|
||||||
// Gather all input values for this variant
|
|
||||||
const variant = this.variants.find(v => v.id === variantId);
|
|
||||||
if (!variant) return;
|
|
||||||
[...variant.constants, ...variant.inputs].forEach(field => {
|
|
||||||
const el = document.getElementById(field.id);
|
|
||||||
if (el) {
|
|
||||||
let val = el.value;
|
|
||||||
if (val && val.startsWith('0x')) {
|
|
||||||
values[field.id] = parseInt(val, 16);
|
|
||||||
} else {
|
|
||||||
values[field.id] = Number(val) || 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.calculateFAT32(values, results);
|
this.calculateFAT32(values, results);
|
||||||
}
|
}
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Calculator } from './calculator.js';
|
|||||||
import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js';
|
import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js';
|
||||||
import { NTFSFilesystem } from './filesystems/ntfs.js';
|
import { NTFSFilesystem } from './filesystems/ntfs.js';
|
||||||
import { ExFATFilesystem } from './filesystems/exfat.js';
|
import { ExFATFilesystem } from './filesystems/exfat.js';
|
||||||
|
import { initializeUXEnhancements } from './ux-enhancements.js';
|
||||||
|
|
||||||
class FilesystemCalculator {
|
class FilesystemCalculator {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -87,6 +88,9 @@ class FilesystemCalculator {
|
|||||||
setupTooltips();
|
setupTooltips();
|
||||||
setupCopyButtons();
|
setupCopyButtons();
|
||||||
|
|
||||||
|
// Initialize UX enhancements
|
||||||
|
initializeUXEnhancements();
|
||||||
|
|
||||||
window.copyToClipboard = (elementId) => {
|
window.copyToClipboard = (elementId) => {
|
||||||
import('./utils.js').then(module => {
|
import('./utils.js').then(module => {
|
||||||
module.copyToClipboard(elementId);
|
module.copyToClipboard(elementId);
|
||||||
|
|||||||
340
webroot/js/ux-enhancements.js
Normal file
340
webroot/js/ux-enhancements.js
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
// Enhanced UX utilities for hex editor companion workflow
|
||||||
|
|
||||||
|
import { copyToClipboard as originalCopyToClipboard, formatHex } from './utils.js';
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 1. KEYBOARD SHORTCUTS
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function setupKeyboardShortcuts() {
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
// Ctrl+1-9: Switch tabs
|
||||||
|
if (e.ctrlKey && !e.shiftKey && !e.altKey) {
|
||||||
|
const num = parseInt(e.key);
|
||||||
|
if (num >= 1 && num <= 9) {
|
||||||
|
const tabs = document.querySelectorAll('.tab');
|
||||||
|
if (tabs[num - 1]) {
|
||||||
|
e.preventDefault();
|
||||||
|
tabs[num - 1].click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+K: Focus calculator
|
||||||
|
if (e.ctrlKey && e.key === 'k') {
|
||||||
|
e.preventDefault();
|
||||||
|
const calcInput = document.querySelector('.tab-content.active .calc-input-field');
|
||||||
|
if (calcInput) calcInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+L: Focus first input field
|
||||||
|
if (e.ctrlKey && e.key === 'l') {
|
||||||
|
e.preventDefault();
|
||||||
|
const firstInput = document.querySelector('.tab-content.active .input-group input');
|
||||||
|
if (firstInput) firstInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alt+C: Clear all inputs in active tab
|
||||||
|
if (e.altKey && e.key === 'c') {
|
||||||
|
e.preventDefault();
|
||||||
|
const inputs = document.querySelectorAll('.tab-content.active .input-group input');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
if (!input.id.toLowerCase().includes('size') && !input.id.toLowerCase().includes('offset')) {
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Trigger recalculation
|
||||||
|
if (inputs.length > 0) {
|
||||||
|
inputs[0].dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+Shift+C: Copy all visible results
|
||||||
|
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
||||||
|
e.preventDefault();
|
||||||
|
copyAllResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show keyboard shortcuts hint
|
||||||
|
showShortcutsHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showShortcutsHint() {
|
||||||
|
// Create hint element if it doesn't exist
|
||||||
|
if (document.getElementById('keyboard-hint')) return;
|
||||||
|
|
||||||
|
const hint = document.createElement('div');
|
||||||
|
hint.id = 'keyboard-hint';
|
||||||
|
hint.className = 'keyboard-hint';
|
||||||
|
hint.innerHTML = `
|
||||||
|
<span class="hint-toggle" title="Tastenkombinationen anzeigen">⌨️</span>
|
||||||
|
<div class="hint-content" style="display: none;">
|
||||||
|
<h4>Tastenkombinationen</h4>
|
||||||
|
<ul>
|
||||||
|
<li><kbd>Ctrl+1-9</kbd> Tabs wechseln</li>
|
||||||
|
<li><kbd>Ctrl+K</kbd> Rechner fokussieren</li>
|
||||||
|
<li><kbd>Ctrl+L</kbd> Erste Eingabe fokussieren</li>
|
||||||
|
<li><kbd>Alt+C</kbd> Eingaben löschen</li>
|
||||||
|
<li><kbd>Ctrl+Shift+C</kbd> Alle Ergebnisse kopieren</li>
|
||||||
|
<li><kbd>Doppelklick</kbd> Wert kopieren</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(hint);
|
||||||
|
|
||||||
|
// Toggle hint visibility
|
||||||
|
hint.querySelector('.hint-toggle').addEventListener('click', () => {
|
||||||
|
const content = hint.querySelector('.hint-content');
|
||||||
|
content.style.display = content.style.display === 'none' ? 'block' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyAllResults() {
|
||||||
|
const results = [];
|
||||||
|
const activeTab = document.querySelector('.tab-content.active');
|
||||||
|
if (!activeTab) return;
|
||||||
|
|
||||||
|
const resultItems = activeTab.querySelectorAll('.result-item.available');
|
||||||
|
resultItems.forEach(item => {
|
||||||
|
const label = item.querySelector('.result-label')?.textContent.trim();
|
||||||
|
const value = item.querySelector('.result-value')?.textContent.trim();
|
||||||
|
if (label && value && value !== '-') {
|
||||||
|
results.push(`${label}: ${value}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
const text = results.join('\n');
|
||||||
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
showNotification('Alle Ergebnisse kopiert!', 'success');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 2. DOUBLE-CLICK TO COPY
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function setupDoubleClickCopy() {
|
||||||
|
document.addEventListener('dblclick', (e) => {
|
||||||
|
// Check if double-clicked on a result value or any hex value
|
||||||
|
const target = e.target;
|
||||||
|
|
||||||
|
if (target.classList.contains('result-value') ||
|
||||||
|
target.classList.contains('conv-value') ||
|
||||||
|
target.closest('.result-value')) {
|
||||||
|
|
||||||
|
const element = target.classList.contains('result-value') ? target : target.closest('.result-value');
|
||||||
|
const text = element.textContent.trim();
|
||||||
|
|
||||||
|
// Extract hex value
|
||||||
|
const hexMatch = text.match(/0x[0-9a-fA-F]+/);
|
||||||
|
if (hexMatch) {
|
||||||
|
navigator.clipboard.writeText(hexMatch[0]).then(() => {
|
||||||
|
highlightElement(element);
|
||||||
|
showNotification('Kopiert: ' + hexMatch[0], 'success');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightElement(element) {
|
||||||
|
element.style.transition = 'background-color 0.3s';
|
||||||
|
element.style.backgroundColor = '#4CAF50';
|
||||||
|
setTimeout(() => {
|
||||||
|
element.style.backgroundColor = '';
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 3. COMPACT MODE TOGGLE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function setupCompactMode() {
|
||||||
|
const toggle = document.createElement('button');
|
||||||
|
toggle.id = 'compact-mode-toggle';
|
||||||
|
toggle.className = 'compact-toggle';
|
||||||
|
toggle.innerHTML = '⚡ Kompakt';
|
||||||
|
toggle.title = 'Kompaktansicht umschalten';
|
||||||
|
|
||||||
|
// Insert toggle button in header
|
||||||
|
const header = document.querySelector('.header-content .nav');
|
||||||
|
if (header) {
|
||||||
|
const toggleContainer = document.createElement('div');
|
||||||
|
toggleContainer.appendChild(toggle);
|
||||||
|
header.appendChild(toggleContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load saved preference
|
||||||
|
const isCompact = localStorage.getItem('compactMode') === 'true';
|
||||||
|
if (isCompact) {
|
||||||
|
document.body.classList.add('compact-mode');
|
||||||
|
toggle.innerHTML = '🔍 Normal';
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle.addEventListener('click', () => {
|
||||||
|
document.body.classList.toggle('compact-mode');
|
||||||
|
const isNowCompact = document.body.classList.contains('compact-mode');
|
||||||
|
toggle.innerHTML = isNowCompact ? '🔍 Normal' : '⚡ Kompakt';
|
||||||
|
localStorage.setItem('compactMode', isNowCompact);
|
||||||
|
showNotification(isNowCompact ? 'Kompaktmodus aktiviert' : 'Normalmodus aktiviert', 'info');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 4. AUTO-CALCULATE ON PASTE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function setupAutoCalculateOnPaste() {
|
||||||
|
document.addEventListener('paste', (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
|
||||||
|
// Only handle paste in input fields
|
||||||
|
if (target.tagName !== 'INPUT' || !target.closest('.input-group')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Trigger calculation after paste
|
||||||
|
target.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
showNotification('Wert eingefügt und berechnet', 'info');
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 5. ENHANCED COPY WITH FORMAT OPTIONS
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function setupContextMenu() {
|
||||||
|
let contextMenu = null;
|
||||||
|
|
||||||
|
document.addEventListener('contextmenu', (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
|
||||||
|
if (target.classList.contains('result-value') || target.classList.contains('conv-value')) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const text = target.textContent.trim();
|
||||||
|
const hexMatch = text.match(/0x[0-9a-fA-F]+/);
|
||||||
|
|
||||||
|
if (hexMatch) {
|
||||||
|
const hexValue = hexMatch[0];
|
||||||
|
const decValue = parseInt(hexValue, 16);
|
||||||
|
|
||||||
|
showContextMenu(e.pageX, e.pageY, hexValue, decValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu on click outside
|
||||||
|
document.addEventListener('click', () => {
|
||||||
|
if (contextMenu) {
|
||||||
|
contextMenu.remove();
|
||||||
|
contextMenu = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function showContextMenu(x, y, hexValue, decValue) {
|
||||||
|
// Remove existing menu
|
||||||
|
if (contextMenu) contextMenu.remove();
|
||||||
|
|
||||||
|
contextMenu = document.createElement('div');
|
||||||
|
contextMenu.className = 'context-menu';
|
||||||
|
contextMenu.style.left = x + 'px';
|
||||||
|
contextMenu.style.top = y + 'px';
|
||||||
|
|
||||||
|
const hexNoPrefix = hexValue.substring(2);
|
||||||
|
const binary = '0b' + decValue.toString(2);
|
||||||
|
const littleEndian = toLittleEndianBytes(decValue);
|
||||||
|
|
||||||
|
contextMenu.innerHTML = `
|
||||||
|
<div class="context-menu-header">Kopieren als:</div>
|
||||||
|
<div class="context-menu-item" data-value="${hexValue}">
|
||||||
|
<span class="format-label">Hex (0x)</span>
|
||||||
|
<span class="format-value">${hexValue}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-item" data-value="${hexNoPrefix}">
|
||||||
|
<span class="format-label">Hex (roh)</span>
|
||||||
|
<span class="format-value">${hexNoPrefix}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-item" data-value="${decValue}">
|
||||||
|
<span class="format-label">Dezimal</span>
|
||||||
|
<span class="format-value">${decValue}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-item" data-value="${binary}">
|
||||||
|
<span class="format-label">Binär</span>
|
||||||
|
<span class="format-value">${binary}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-menu-item" data-value="${littleEndian}">
|
||||||
|
<span class="format-label">Little-Endian</span>
|
||||||
|
<span class="format-value">${littleEndian}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(contextMenu);
|
||||||
|
|
||||||
|
// Handle menu item clicks
|
||||||
|
contextMenu.querySelectorAll('.context-menu-item').forEach(item => {
|
||||||
|
item.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const value = item.getAttribute('data-value');
|
||||||
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
|
showNotification('Kopiert: ' + value, 'success');
|
||||||
|
contextMenu.remove();
|
||||||
|
contextMenu = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toLittleEndianBytes(value) {
|
||||||
|
const bytes = [];
|
||||||
|
let num = value;
|
||||||
|
|
||||||
|
// Convert to 4-byte little-endian
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
bytes.push((num & 0xFF).toString(16).padStart(2, '0').toUpperCase());
|
||||||
|
num = num >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 6. NOTIFICATION SYSTEM
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
function showNotification(message, type = 'info') {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = `notification notification-${type}`;
|
||||||
|
notification.textContent = message;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// Trigger animation
|
||||||
|
setTimeout(() => notification.classList.add('show'), 10);
|
||||||
|
|
||||||
|
// Remove after 2 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.remove('show');
|
||||||
|
setTimeout(() => notification.remove(), 300);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// INITIALIZATION
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
export function initializeUXEnhancements() {
|
||||||
|
setupKeyboardShortcuts();
|
||||||
|
setupDoubleClickCopy();
|
||||||
|
setupCompactMode();
|
||||||
|
setupAutoCalculateOnPaste();
|
||||||
|
setupContextMenu();
|
||||||
|
|
||||||
|
console.log('✨ UX Enhancements activated!');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user