initial commit
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# Dev Mode - Usage Guide
|
||||
|
||||
**Purpose**: Easy feedback on prototypes by copying Object IDs to clipboard
|
||||
|
||||
---
|
||||
|
||||
## 🎯 What is Dev Mode?
|
||||
|
||||
Dev Mode is a built-in feature in all WDS prototypes that allows testers, stakeholders, and designers to easily reference specific UI elements when providing feedback.
|
||||
|
||||
Instead of saying *"The button in the top right"*, you can say *"Fix `customer-sign-bankid`"* - precise and unambiguous!
|
||||
|
||||
---
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
### Step 1: Activate Dev Mode
|
||||
|
||||
**Two ways**:
|
||||
1. Click the **Dev Mode button** (top-right corner)
|
||||
2. Press **Ctrl+E** on your keyboard
|
||||
|
||||
The button will turn blue and say **"Dev Mode: ON"**
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Find the Element
|
||||
|
||||
- **Hover** over any element you want to reference
|
||||
- You'll see a **gray outline** appear
|
||||
- A **tooltip** shows the Object ID
|
||||
|
||||
**Prototype still works normally!** You can click buttons, fill forms, etc.
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Copy the Object ID
|
||||
|
||||
- **Hold the Shift key** (outline turns **green**)
|
||||
- **Click the element** while holding Shift
|
||||
- **Object ID is copied!** ✓
|
||||
|
||||
You'll see a green success message: **"✓ Copied: [object-id]"**
|
||||
|
||||
**Important**: Shift key is **disabled when typing in form fields** (input, textarea, etc.) so you can type capital letters and special characters normally!
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Paste in Feedback
|
||||
|
||||
Now paste the Object ID in your feedback:
|
||||
|
||||
**Good feedback**:
|
||||
```
|
||||
❌ Issue with `customer-sign-bankid`:
|
||||
The button is disabled even after I check the consent checkbox.
|
||||
|
||||
💡 Suggestion for `sidebar-video`:
|
||||
Make the video auto-play on mobile.
|
||||
```
|
||||
|
||||
**Developer knows EXACTLY** which element you're talking about!
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Visual Guide
|
||||
|
||||
| State | Appearance | Action |
|
||||
|-------|------------|--------|
|
||||
| **Dev Mode OFF** | Normal prototype | Click button or press Ctrl+E |
|
||||
| **Dev Mode ON (hovering)** | Gray outline | Shows Object ID in tooltip |
|
||||
| **Shift held (hovering)** | Green outline | Click to copy |
|
||||
| **After copying** | Green flash | Object ID in clipboard |
|
||||
|
||||
---
|
||||
|
||||
## ⌨️ Keyboard Shortcuts
|
||||
|
||||
- **Ctrl+E**: Toggle Dev Mode on/off
|
||||
- **Shift + Click**: Copy Object ID (when dev mode is on)
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
1. **Activate once**, then navigate through prototype normally
|
||||
2. **Hold Shift only when copying** - prototype works without it
|
||||
3. **Type in fields normally** - Shift is disabled when focused on input/textarea
|
||||
4. **Deactivate when done** testing (Ctrl+E again)
|
||||
5. **Object IDs are permanent** - always refer to the same element
|
||||
|
||||
---
|
||||
|
||||
## 📋 Example Workflow
|
||||
|
||||
### Tester's Perspective:
|
||||
|
||||
1. Open prototype
|
||||
2. Press **Ctrl+E** (Dev Mode on)
|
||||
3. Test the prototype normally
|
||||
4. Find a bug - hover over problem element
|
||||
5. Hold **Shift**, click element
|
||||
6. Paste Object ID into bug report: "`customer-facility-startdate-group` shows wrong default date"
|
||||
7. Continue testing
|
||||
|
||||
### Designer's Perspective:
|
||||
|
||||
Receives feedback:
|
||||
```
|
||||
Bug: `customer-facility-startdate-group` shows wrong default date
|
||||
```
|
||||
|
||||
- Open prototype
|
||||
- Press **Ctrl+F** in browser, search for `customer-facility-startdate-group`
|
||||
- Find exact element in code
|
||||
- Fix the date calculation
|
||||
- Done! ✅
|
||||
|
||||
---
|
||||
|
||||
## 🔧 For Developers
|
||||
|
||||
When you receive Object IDs in feedback:
|
||||
|
||||
1. Open the HTML file
|
||||
2. Search for the Object ID (Ctrl+F)
|
||||
3. Element is either:
|
||||
- `id="object-id"` attribute
|
||||
- `data-object-id="object-id"` attribute
|
||||
4. Fix the issue in that specific element
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQs
|
||||
|
||||
**Q: Does Dev Mode affect the prototype?**
|
||||
A: No! The prototype works normally. You need to hold Shift to copy IDs.
|
||||
|
||||
**Q: Can I use this on mobile?**
|
||||
A: Yes! The button appears on mobile too. Use a Bluetooth keyboard or on-screen Shift key.
|
||||
|
||||
**Q: Can I type in form fields while Dev Mode is on?**
|
||||
A: Yes! Shift key is automatically disabled when you're typing in input fields or textareas, so you can type capital letters and special characters normally.
|
||||
|
||||
**Q: What if an element doesn't have an ID?**
|
||||
A: Dev Mode walks up the tree to find the nearest parent with an ID.
|
||||
|
||||
**Q: Can I copy multiple IDs?**
|
||||
A: Yes! Hold Shift, click first element, release Shift, hold again, click second element, etc.
|
||||
|
||||
**Q: Is this only for bugs?**
|
||||
A: No! Use it for any feedback - bugs, suggestions, questions, clarifications.
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Best Practices
|
||||
|
||||
### For Testers:
|
||||
- ✅ **DO**: Include Object ID in every piece of feedback
|
||||
- ✅ **DO**: Test prototype normally, copy IDs when needed
|
||||
- ✅ **DO**: Combine Object ID with description
|
||||
- ❌ **DON'T**: Leave Dev Mode on during normal use
|
||||
|
||||
### For Designers:
|
||||
- ✅ **DO**: Ensure all interactive elements have Object IDs
|
||||
- ✅ **DO**: Use descriptive, consistent naming
|
||||
- ✅ **DO**: Include Dev Mode in all prototypes
|
||||
- ❌ **DON'T**: Change Object IDs after sharing prototype
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
**Problem**: Dev Mode button not showing
|
||||
**Solution**: Check that `dev-mode.js` and `dev-mode.css` are loaded
|
||||
|
||||
**Problem**: Clicking doesn't copy
|
||||
**Solution**: Make sure you're holding **Shift** while clicking
|
||||
|
||||
**Problem**: Tooltip not showing
|
||||
**Solution**: Element might not have an ID - check console logs
|
||||
|
||||
**Problem**: Can't turn off Dev Mode
|
||||
**Solution**: Press Ctrl+E or refresh the page
|
||||
|
||||
---
|
||||
|
||||
**Dev Mode makes feedback precise, fast, and frustration-free!** 🎯
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/* ============================================================================
|
||||
PROTOTYPE DEV MODE STYLES
|
||||
|
||||
Styles for developer/feedback mode that allows copying Object IDs
|
||||
|
||||
Usage: Include these styles in your prototype HTML or CSS file
|
||||
============================================================================ */
|
||||
|
||||
/* Dev Mode Toggle Button */
|
||||
.dev-mode-toggle {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
background: #fff;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
padding: 10px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.dev-mode-toggle:hover {
|
||||
background: #f9fafb;
|
||||
border-color: #d1d5db;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.dev-mode-toggle.active {
|
||||
background: #0066CC;
|
||||
border-color: #0066CC;
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.3);
|
||||
}
|
||||
|
||||
.dev-mode-toggle svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Dev Mode Active State */
|
||||
body.dev-mode-active {
|
||||
cursor: help !important; /* Show help cursor to indicate special mode */
|
||||
}
|
||||
|
||||
/* Subtle element highlighting on hover (not Shift held) */
|
||||
body.dev-mode-active [id]:hover {
|
||||
outline: 2px solid #6b7280 !important;
|
||||
outline-offset: 2px !important;
|
||||
box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.2) !important;
|
||||
}
|
||||
|
||||
/* Active highlighting when Shift is held (ready to copy) */
|
||||
body.dev-mode-active.shift-held {
|
||||
cursor: copy !important;
|
||||
}
|
||||
|
||||
body.dev-mode-active.shift-held [id]:hover {
|
||||
outline: 3px solid #10B981 !important;
|
||||
outline-offset: 3px !important;
|
||||
box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Dev Mode Tooltip */
|
||||
.dev-mode-tooltip {
|
||||
position: fixed;
|
||||
background: #1F2937;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-family: 'Courier New', monospace;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.dev-mode-tooltip::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: inherit;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/* Disable only certain interactions when Shift is held in dev mode */
|
||||
body.dev-mode-active.shift-held button:not(#dev-mode-toggle),
|
||||
body.dev-mode-active.shift-held input,
|
||||
body.dev-mode-active.shift-held select,
|
||||
body.dev-mode-active.shift-held textarea,
|
||||
body.dev-mode-active.shift-held a {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* But allow the toggle button to work */
|
||||
body.dev-mode-active #dev-mode-toggle,
|
||||
body.dev-mode-active #dev-mode-toggle * {
|
||||
pointer-events: auto !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
/* Feedback overlay (created dynamically) */
|
||||
@keyframes fadeInOut {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(0.9);
|
||||
}
|
||||
20% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
80% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive: Adjust toggle button on mobile */
|
||||
@media (max-width: 768px) {
|
||||
.dev-mode-toggle {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dev-mode-toggle span {
|
||||
display: none; /* Hide text on mobile, show only icon */
|
||||
}
|
||||
|
||||
.dev-mode-toggle.active span {
|
||||
display: inline; /* Show "ON" status */
|
||||
max-width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional: Add visual indicator when Shift is held */
|
||||
body.dev-mode-active.shift-held .dev-mode-toggle::after {
|
||||
content: '⬆️';
|
||||
margin-left: 4px;
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.7; transform: scale(1.1); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<!-- ============================================================================
|
||||
PROTOTYPE DEV MODE - HTML SNIPPET
|
||||
|
||||
Add this HTML to your prototype page (inside <body>, preferably at the top)
|
||||
============================================================================ -->
|
||||
|
||||
<!-- Dev Mode Toggle Button (fixed position, top-right) -->
|
||||
<button id="dev-mode-toggle" class="dev-mode-toggle" title="Toggle Dev Mode (Ctrl+E)">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||
</svg>
|
||||
<span>Dev Mode: OFF</span>
|
||||
</button>
|
||||
|
||||
<!-- Dev Mode Tooltip (shown when hovering over elements in dev mode) -->
|
||||
<div id="dev-mode-tooltip" class="dev-mode-tooltip" style="display: none;"></div>
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
/* eslint-disable n/no-unsupported-features/node-builtins */
|
||||
/* global document, window */
|
||||
|
||||
/**
|
||||
* PROTOTYPE DEV MODE
|
||||
*
|
||||
* Developer/feedback mode that allows users to easily copy Object IDs to clipboard
|
||||
* for providing precise feedback on prototype elements.
|
||||
*
|
||||
* Features:
|
||||
* - Toggle dev mode with button or Ctrl+E
|
||||
* - Prototype works NORMALLY when dev mode is on
|
||||
* - Hold Shift + Click any element to copy its Object ID
|
||||
* - Visual highlights show what will be copied (green when Shift is held)
|
||||
* - Tooltip shows Object ID on hover
|
||||
* - Success feedback when copied
|
||||
*
|
||||
* Usage:
|
||||
* 1. Include this script in your prototype HTML
|
||||
* 2. Add the HTML toggle button and tooltip (see HTML template)
|
||||
* 3. Add the CSS styles (see CSS template)
|
||||
* 4. Call initDevMode() on page load
|
||||
*
|
||||
* How it works:
|
||||
* - Activate dev mode (Ctrl+E or click button)
|
||||
* - Hover over elements to see their Object IDs (gray outline)
|
||||
* - Hold Shift key (outline turns green)
|
||||
* - Click while holding Shift to copy Object ID
|
||||
* - Prototype works normally without Shift held
|
||||
* - **Shift is disabled when typing in form fields** (input, textarea, etc.)
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// DEV MODE STATE
|
||||
// ============================================================================
|
||||
|
||||
let devModeActive = false;
|
||||
let shiftKeyPressed = false;
|
||||
let currentHighlightedElement = null;
|
||||
|
||||
// ============================================================================
|
||||
// INITIALIZATION
|
||||
// ============================================================================
|
||||
|
||||
function initDevMode() {
|
||||
const toggleButton = document.querySelector('#dev-mode-toggle');
|
||||
const tooltip = document.querySelector('#dev-mode-tooltip');
|
||||
|
||||
if (!toggleButton || !tooltip) {
|
||||
console.warn('⚠️ Dev Mode: Toggle button or tooltip not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user agent supports clipboard API
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
||||
// Clipboard API available
|
||||
} else {
|
||||
console.warn('⚠️ Clipboard API not supported in this browser');
|
||||
return;
|
||||
}
|
||||
|
||||
setupKeyboardShortcuts();
|
||||
setupToggleButton(toggleButton, tooltip);
|
||||
setupHoverHighlight(tooltip);
|
||||
setupClickCopy();
|
||||
|
||||
console.log('%c💡 Dev Mode available: Press Ctrl+E or click the Dev Mode button', 'color: #0066CC; font-weight: bold;');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// KEYBOARD SHORTCUTS
|
||||
// ============================================================================
|
||||
|
||||
function setupKeyboardShortcuts() {
|
||||
// Track Shift key for container selection
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Shift') {
|
||||
// Don't activate if user is typing in a form field
|
||||
if (isTypingInField()) {
|
||||
return;
|
||||
}
|
||||
|
||||
shiftKeyPressed = true;
|
||||
document.body.classList.add('shift-held');
|
||||
if (devModeActive) {
|
||||
console.log('%c⬆️ Shift held: Click any element to copy its Object ID', 'color: #10B981; font-weight: bold;');
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl+E toggle
|
||||
if (e.ctrlKey && e.key === 'e') {
|
||||
e.preventDefault();
|
||||
document.querySelector('#dev-mode-toggle')?.click();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', (e) => {
|
||||
if (e.key === 'Shift') {
|
||||
shiftKeyPressed = false;
|
||||
document.body.classList.remove('shift-held');
|
||||
if (devModeActive) {
|
||||
console.log('%c⬇️ Shift released: Prototype works normally (hold Shift to copy)', 'color: #6b7280;');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TOGGLE BUTTON
|
||||
// ============================================================================
|
||||
|
||||
function setupToggleButton(toggleButton, tooltip) {
|
||||
toggleButton.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
globalThis.devModeActive = true;
|
||||
} else if (globalThis.window !== undefined) {
|
||||
globalThis.devModeActive = true;
|
||||
}
|
||||
devModeActive = !devModeActive;
|
||||
|
||||
// Update UI
|
||||
document.body.classList.toggle('dev-mode-active', devModeActive);
|
||||
toggleButton.classList.toggle('active', devModeActive);
|
||||
|
||||
const statusText = toggleButton.querySelector('span');
|
||||
if (statusText) {
|
||||
statusText.textContent = devModeActive ? 'Dev Mode: ON' : 'Dev Mode: OFF';
|
||||
}
|
||||
|
||||
// Log status
|
||||
console.log(`🔧 Dev Mode: ${devModeActive ? 'ACTIVATED' : 'DEACTIVATED'}`);
|
||||
|
||||
if (devModeActive) {
|
||||
console.log('%c🔧 DEV MODE ACTIVE', 'color: #0066CC; font-size: 16px; font-weight: bold;');
|
||||
console.log('%c⚠️ Hold SHIFT + Click any element to copy its Object ID', 'color: #FFB800; font-size: 14px; font-weight: bold;');
|
||||
console.log('%cWithout Shift: Prototype works normally', 'color: #6b7280;');
|
||||
console.log('%cPress Ctrl+E to toggle Dev Mode', 'color: #6b7280;');
|
||||
} else {
|
||||
tooltip.style.display = 'none';
|
||||
if (currentHighlightedElement) {
|
||||
clearHighlight();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HOVER HIGHLIGHT
|
||||
// ============================================================================
|
||||
|
||||
function setupHoverHighlight(tooltip) {
|
||||
// Show tooltip and highlight on hover
|
||||
document.addEventListener('mouseover', function (e) {
|
||||
if (!devModeActive) return;
|
||||
|
||||
// Don't highlight if user is typing in a field
|
||||
if (isTypingInField()) {
|
||||
tooltip.style.display = 'none';
|
||||
clearHighlight();
|
||||
return;
|
||||
}
|
||||
|
||||
clearHighlight();
|
||||
|
||||
let element = findElementWithId(e.target);
|
||||
|
||||
if (!element || !element.id || isSystemElement(element.id)) {
|
||||
tooltip.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Highlight element
|
||||
highlightElement(element, shiftKeyPressed);
|
||||
currentHighlightedElement = element;
|
||||
|
||||
// Show tooltip
|
||||
const prefix = shiftKeyPressed ? '✓ Click to Copy: ' : '⬆️ Hold Shift + Click: ';
|
||||
tooltip.textContent = prefix + element.id;
|
||||
tooltip.style.display = 'block';
|
||||
tooltip.style.background = shiftKeyPressed ? '#10B981' : '#6b7280';
|
||||
tooltip.style.color = '#fff';
|
||||
|
||||
updateTooltipPosition(e, tooltip);
|
||||
});
|
||||
|
||||
// Update tooltip position on mouse move
|
||||
document.addEventListener('mousemove', function (e) {
|
||||
if (devModeActive && tooltip.style.display === 'block') {
|
||||
updateTooltipPosition(e, tooltip);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear highlight on mouse out
|
||||
document.addEventListener('mouseout', function (e) {
|
||||
if (!devModeActive) return;
|
||||
if (e.target.id) {
|
||||
tooltip.style.display = 'none';
|
||||
clearHighlight();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CLICK TO COPY
|
||||
// ============================================================================
|
||||
|
||||
function setupClickCopy() {
|
||||
// Use capture phase to intercept clicks with Shift
|
||||
document.addEventListener(
|
||||
'click',
|
||||
function (e) {
|
||||
if (!devModeActive) return;
|
||||
|
||||
// Allow toggle button to work normally
|
||||
if (isToggleButton(e.target)) return;
|
||||
|
||||
// ONLY copy if Shift is held
|
||||
if (!shiftKeyPressed) {
|
||||
// Let prototype work normally without Shift
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't intercept if user is clicking in/around a form field
|
||||
if (isTypingInField() || isFormElement(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shift is held and not in a form field - intercept and copy
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
let element = findElementWithId(e.target);
|
||||
|
||||
if (!element || !element.id || isSystemElement(element.id)) {
|
||||
console.log('❌ No Object ID found');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy to clipboard
|
||||
const objectId = element.id;
|
||||
copyToClipboard(objectId);
|
||||
|
||||
// Show feedback
|
||||
showCopyFeedback(element, objectId);
|
||||
|
||||
return false;
|
||||
},
|
||||
true,
|
||||
); // Capture phase
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HELPER FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
function findElementWithId(element) {
|
||||
let current = element;
|
||||
let attempts = 0;
|
||||
|
||||
while (current && !current.id && attempts < 10) {
|
||||
current = current.parentElement;
|
||||
attempts++;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
function isSystemElement(id) {
|
||||
const systemIds = ['app', 'dev-mode-toggle', 'dev-mode-tooltip'];
|
||||
return systemIds.includes(id);
|
||||
}
|
||||
|
||||
function isToggleButton(element) {
|
||||
return element.id === 'dev-mode-toggle' || element.closest('#dev-mode-toggle') || element.classList.contains('dev-mode-toggle');
|
||||
}
|
||||
|
||||
function isTypingInField() {
|
||||
const activeElement = document.activeElement;
|
||||
if (!activeElement) return false;
|
||||
|
||||
const tagName = activeElement.tagName.toLowerCase();
|
||||
const isEditable = activeElement.isContentEditable;
|
||||
|
||||
// Check if user is currently typing in a form field
|
||||
return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || isEditable;
|
||||
}
|
||||
|
||||
function isFormElement(element) {
|
||||
if (!element) return false;
|
||||
|
||||
const tagName = element.tagName.toLowerCase();
|
||||
const isEditable = element.isContentEditable;
|
||||
|
||||
// Check if the clicked element is a form element
|
||||
return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || isEditable;
|
||||
}
|
||||
|
||||
function highlightElement(element, isShiftHeld) {
|
||||
const color = isShiftHeld ? '#10B981' : '#6b7280';
|
||||
const width = isShiftHeld ? '3px' : '2px';
|
||||
const offset = isShiftHeld ? '3px' : '2px';
|
||||
const shadowSpread = isShiftHeld ? '5px' : '2px';
|
||||
const shadowOpacity = isShiftHeld ? '0.4' : '0.2';
|
||||
|
||||
element.style.outline = `${width} solid ${color}`;
|
||||
element.style.outlineOffset = offset;
|
||||
element.style.boxShadow = `0 0 0 ${shadowSpread} rgba(${isShiftHeld ? '16, 185, 129' : '107, 114, 128'}, ${shadowOpacity})`;
|
||||
}
|
||||
|
||||
function clearHighlight() {
|
||||
if (currentHighlightedElement) {
|
||||
currentHighlightedElement.style.outline = '';
|
||||
currentHighlightedElement.style.boxShadow = '';
|
||||
currentHighlightedElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateTooltipPosition(e, tooltip) {
|
||||
const offset = 15;
|
||||
let x = e.clientX + offset;
|
||||
let y = e.clientY + offset;
|
||||
|
||||
// Keep tooltip on screen
|
||||
const rect = tooltip.getBoundingClientRect();
|
||||
if (x + rect.width > window.innerWidth) {
|
||||
x = e.clientX - rect.width - offset;
|
||||
}
|
||||
if (y + rect.height > window.innerHeight) {
|
||||
y = e.clientY - rect.height - offset;
|
||||
}
|
||||
|
||||
tooltip.style.left = x + 'px';
|
||||
tooltip.style.top = y + 'px';
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (typeof navigator !== 'undefined' && navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
console.log(`📋 Copied to clipboard: ${text}`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Dev Mode error:', error);
|
||||
fallbackCopy(text);
|
||||
});
|
||||
} else {
|
||||
fallbackCopy(text);
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopy(text) {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.left = '-999999px';
|
||||
document.body.append(textarea);
|
||||
textarea.focus();
|
||||
textarea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
console.log(`📋 Copied (fallback): ${text}`);
|
||||
} catch (error) {
|
||||
console.error('Dev Mode error:', error);
|
||||
}
|
||||
|
||||
textarea.remove();
|
||||
}
|
||||
|
||||
function showCopyFeedback(element, objectId) {
|
||||
// Create feedback overlay
|
||||
const feedback = document.createElement('div');
|
||||
feedback.textContent = '✓ Copied: ' + objectId;
|
||||
feedback.style.cssText = `
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #10B981;
|
||||
color: #fff;
|
||||
padding: 16px 32px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
z-index: 100000;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
|
||||
animation: fadeInOut 1.5s ease-in-out;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
document.body.append(feedback);
|
||||
|
||||
setTimeout(() => {
|
||||
feedback.remove();
|
||||
}, 1500);
|
||||
|
||||
// Flash element
|
||||
const originalOutline = element.style.outline;
|
||||
element.style.outline = '3px solid #10B981';
|
||||
setTimeout(() => {
|
||||
element.style.outline = originalOutline;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Add CSS animation
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes fadeInOut {
|
||||
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
|
||||
20% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
|
||||
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
|
||||
}
|
||||
`;
|
||||
document.head.append(style);
|
||||
|
||||
// ============================================================================
|
||||
// EXPORT
|
||||
// ============================================================================
|
||||
|
||||
// Make available globally
|
||||
globalThis.initDevMode = initDevMode;
|
||||
|
||||
// Export for use in other scripts
|
||||
if (typeof globalThis !== 'undefined' && globalThis.exports) {
|
||||
globalThis.exports = { initDevMode };
|
||||
}
|
||||
Reference in New Issue
Block a user