feat: Add hover tooltips to platform buttons
- Add CSS tooltip system with data-tooltip attribute - Tooltips appear on hover with smooth animation - Non-blocking: uses pointer-events: none - Added to ImageStudio, VideoStudio, and CinemaStudio buttons
This commit is contained in:
parent
fe2d8dd0ac
commit
2c95a86af8
4 changed files with 230 additions and 11 deletions
|
|
@ -187,6 +187,7 @@ export function CinemaStudio() {
|
|||
// Camera Builder Toggle Button
|
||||
const cameraBuilderBtn = document.createElement('button');
|
||||
cameraBuilderBtn.className = 'flex items-center gap-1.5 px-3 py-1.5 text-[10px] font-bold text-white/50 hover:text-white transition-colors bg-white/5 hover:bg-white/10 rounded-lg border border-white/5';
|
||||
cameraBuilderBtn.setAttribute('data-tooltip', 'Quick camera builder');
|
||||
cameraBuilderBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="6" width="20" height="12" rx="2"/><circle cx="12" cy="12" r="3"/></svg> Builder`;
|
||||
settingsToolbar.appendChild(cameraBuilderBtn);
|
||||
|
||||
|
|
@ -202,6 +203,7 @@ export function CinemaStudio() {
|
|||
const summaryCard = document.createElement('button');
|
||||
// Removed 'hidden' class, added 'flex' and refined width constraints for mobile
|
||||
summaryCard.className = 'flex flex-col items-start justify-center px-4 py-2 bg-[#2a2a2a] rounded-xl border border-white/5 hover:border-white/20 transition-colors text-left flex-1 min-w-[100px] md:min-w-[140px] max-w-[240px] h-[56px] relative group overflow-hidden';
|
||||
summaryCard.setAttribute('data-tooltip', 'Open camera settings');
|
||||
|
||||
// Dot indicator
|
||||
const dot = document.createElement('div');
|
||||
|
|
@ -233,6 +235,7 @@ export function CinemaStudio() {
|
|||
// Generate Button
|
||||
const generateBtn = document.createElement('button');
|
||||
generateBtn.className = 'h-[56px] px-8 bg-[#d9ff00] text-black rounded-xl font-black text-xs uppercase hover:bg-white transition-colors shadow-lg disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
generateBtn.setAttribute('data-tooltip', 'Generate cinema shot');
|
||||
generateBtn.innerHTML = `GENERATE ✨`;
|
||||
|
||||
rightGroup.appendChild(summaryCard);
|
||||
|
|
|
|||
|
|
@ -147,10 +147,11 @@ export function ImageStudio() {
|
|||
const controlsLeft = document.createElement('div');
|
||||
controlsLeft.className = 'flex items-center gap-1.5 md:gap-2.5 relative overflow-x-auto no-scrollbar pb-1 md:pb-0';
|
||||
|
||||
const createControlBtn = (icon, label, id) => {
|
||||
const createControlBtn = (icon, label, id, tooltip) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.id = id;
|
||||
btn.className = 'flex items-center gap-1.5 md:gap-2.5 px-3 md:px-4 py-2 md:py-2.5 bg-white/5 hover:bg-white/10 rounded-xl md:rounded-2xl transition-all border border-white/5 group whitespace-nowrap';
|
||||
if (tooltip) btn.setAttribute('data-tooltip', tooltip);
|
||||
btn.innerHTML = `
|
||||
${icon}
|
||||
<span id="${id}-label" class="text-xs font-bold text-white group-hover:text-primary transition-colors">${label}</span>
|
||||
|
|
@ -163,19 +164,50 @@ export function ImageStudio() {
|
|||
<div class="w-5 h-5 bg-primary rounded-md flex items-center justify-center shadow-lg shadow-primary/20">
|
||||
<span class="text-[10px] font-black text-black">G</span>
|
||||
</div>
|
||||
`, selectedModelName, 'model-btn');
|
||||
`, selectedModelName, 'model-btn', 'Select AI generation model');
|
||||
|
||||
const arBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/></svg>
|
||||
`, selectedAr, 'ar-btn');
|
||||
`, selectedAr, 'ar-btn', 'Change aspect ratio');
|
||||
|
||||
const qualityBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><path d="M6 2L3 6v15a2 2 0 002 2h14a2 2 0 002-2V6l-3-4H6z"/></svg>
|
||||
`, '720p', 'quality-btn');
|
||||
`, '720p', 'quality-btn', 'Set output quality');
|
||||
|
||||
controlsLeft.appendChild(modelBtn);
|
||||
controlsLeft.appendChild(arBtn);
|
||||
controlsLeft.appendChild(qualityBtn);
|
||||
|
||||
// Advanced options toggle button
|
||||
const advancedBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 001.82-.33 1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-1.82.33A1.65 1.65 0 0019.4 9a1.65 1.65 0 00-1.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||||
`, 'Advanced', 'advanced-btn', 'Show advanced options');
|
||||
controlsLeft.appendChild(advancedBtn);
|
||||
|
||||
// Quick Tools toggle button
|
||||
const toolsBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><path d="M14.7 6.3a1 1 0 000 1.4l1.6 1.6a1 1 0 001.4 0l3.77-3.77a6 6 0 01-7.94 7.94l-6.91 6.91a2.12 2.12 0 01-3-3l6.91-6.91a6 6 0 017.94-7.94l-3.76 3.76z"/></svg>
|
||||
`, 'Tools', 'tools-btn', 'Quick starters & prompt enhancer');
|
||||
controlsLeft.appendChild(toolsBtn);
|
||||
// Show quality button if the default model has quality/resolution options
|
||||
const _initResolutions = getResolutionsForModel(defaultModel.id);
|
||||
qualityBtn.style.display = _initResolutions.length > 0 ? 'flex' : 'none';
|
||||
if (_initResolutions.length > 0) {
|
||||
const qlabel = qualityBtn.querySelector('#quality-btn-label');
|
||||
if (qlabel) qlabel.textContent = _initResolutions[0];
|
||||
}
|
||||
|
||||
const generateBtn = document.createElement('button');
|
||||
generateBtn.className = 'bg-primary text-black px-6 md:px-8 py-3 md:py-3.5 rounded-xl md:rounded-[1.5rem] font-black text-sm md:text-base hover:shadow-glow hover:scale-105 active:scale-95 transition-all flex items-center justify-center gap-2.5 w-full sm:w-auto shadow-lg';
|
||||
generateBtn.setAttribute('data-tooltip', 'Generate AI image from prompt');
|
||||
generateBtn.innerHTML = `Generate ✨`;
|
||||
|
||||
bottomRow.appendChild(controlsLeft);
|
||||
bottomRow.appendChild(generateBtn);
|
||||
bar.appendChild(bottomRow);
|
||||
promptWrapper.appendChild(bar);
|
||||
container.appendChild(promptWrapper);
|
||||
|
||||
const inlineInstructions = createInlineInstructions('image');
|
||||
inlineInstructions.classList.add('max-w-4xl', 'mt-8');
|
||||
container.appendChild(inlineInstructions);
|
||||
|
|
|
|||
|
|
@ -252,10 +252,11 @@ export function VideoStudio() {
|
|||
const controlsLeft = document.createElement('div');
|
||||
controlsLeft.className = 'flex items-center gap-1.5 md:gap-2.5 relative overflow-x-auto no-scrollbar pb-1 md:pb-0';
|
||||
|
||||
const createControlBtn = (icon, label, id) => {
|
||||
const createControlBtn = (icon, label, id, tooltip) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.id = id;
|
||||
btn.className = 'flex items-center gap-1.5 md:gap-2.5 px-3 md:px-4 py-2 md:py-2.5 bg-white/5 hover:bg-white/10 rounded-xl md:rounded-2xl transition-all border border-white/5 group whitespace-nowrap';
|
||||
if (tooltip) btn.setAttribute('data-tooltip', tooltip);
|
||||
btn.innerHTML = `
|
||||
${icon}
|
||||
<span id="${id}-label" class="text-xs font-bold text-white group-hover:text-primary transition-colors">${label}</span>
|
||||
|
|
@ -268,23 +269,23 @@ export function VideoStudio() {
|
|||
<div class="w-5 h-5 bg-primary rounded-md flex items-center justify-center shadow-lg shadow-primary/20">
|
||||
<span class="text-[10px] font-black text-black">V</span>
|
||||
</div>
|
||||
`, selectedModelName, 'v-model-btn');
|
||||
`, selectedModelName, 'v-model-btn', 'Select AI video model');
|
||||
|
||||
const arBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/></svg>
|
||||
`, selectedAr, 'v-ar-btn');
|
||||
`, selectedAr, 'v-ar-btn', 'Change aspect ratio');
|
||||
|
||||
const durationBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
`, `${selectedDuration}s`, 'v-duration-btn');
|
||||
`, `${selectedDuration}s`, 'v-duration-btn', 'Set video duration');
|
||||
|
||||
const resolutionBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><path d="M6 2L3 6v15a2 2 0 002 2h14a2 2 0 002-2V6l-3-4H6z"/></svg>
|
||||
`, selectedResolution || '720p', 'v-resolution-btn');
|
||||
`, selectedResolution || '720p', 'v-resolution-btn', 'Set output resolution');
|
||||
|
||||
const qualityBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
||||
`, selectedQuality || 'basic', 'v-quality-btn');
|
||||
`, selectedQuality || 'basic', 'v-quality-btn', 'Set output quality');
|
||||
|
||||
const modeBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||||
|
|
@ -295,7 +296,12 @@ export function VideoStudio() {
|
|||
controlsLeft.appendChild(durationBtn);
|
||||
controlsLeft.appendChild(resolutionBtn);
|
||||
controlsLeft.appendChild(qualityBtn);
|
||||
controlsLeft.appendChild(modeBtn);
|
||||
|
||||
// Advanced options toggle button
|
||||
const advancedBtn = createControlBtn(`
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="opacity-60 text-secondary"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 001.82-.33 1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-1.82.33A1.65 1.65 0 0019.4 9a1.65 1.65 0 00-1.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||||
`, 'Advanced', 'v-advanced-btn', 'Show advanced options');
|
||||
controlsLeft.appendChild(advancedBtn);
|
||||
|
||||
// Initial visibility (t2v mode)
|
||||
const initDurations = getDurationsForModel(defaultModel.id);
|
||||
|
|
@ -307,6 +313,7 @@ export function VideoStudio() {
|
|||
|
||||
const generateBtn = document.createElement('button');
|
||||
generateBtn.className = 'bg-primary text-black px-6 md:px-8 py-3 md:py-3.5 rounded-xl md:rounded-[1.5rem] font-black text-sm md:text-base hover:shadow-glow hover:scale-105 active:scale-95 transition-all flex items-center justify-center gap-2.5 w-full sm:w-auto shadow-lg';
|
||||
generateBtn.setAttribute('data-tooltip', 'Generate AI video from prompt');
|
||||
generateBtn.innerHTML = `Generate ✨`;
|
||||
|
||||
bottomRow.appendChild(controlsLeft);
|
||||
|
|
|
|||
|
|
@ -72,4 +72,181 @@
|
|||
|
||||
.animate-fade-in-up {
|
||||
animation: fade-in-up 0.6s cubic-bezier(0.23, 1, 0.32, 1) forwards;
|
||||
}
|
||||
|
||||
/* Thumbnail cards */
|
||||
.thumb-hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.thumb-hero img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top center;
|
||||
transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
|
||||
.thumb-hero::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60%;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.thumb-hero:hover img,
|
||||
.group:hover .thumb-hero img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.thumb-skeleton {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
animation: skeleton-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes skeleton-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.thumb-fallback .thumb-hero {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-banner img {
|
||||
transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
|
||||
.hero-banner:hover img {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animate-fade-in-up {
|
||||
animation: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.thumb-hero img {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.thumb-skeleton {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================
|
||||
TOOLTIP SYSTEM
|
||||
======================== */
|
||||
|
||||
/* Base tooltip container */
|
||||
[data-tooltip] {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Tooltip arrow */
|
||||
[data-tooltip]::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(8px);
|
||||
border: 6px solid transparent;
|
||||
border-top-color: #1a1a1a;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* Tooltip body */
|
||||
[data-tooltip]::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(12px);
|
||||
padding: 8px 14px;
|
||||
background: #1a1a1a;
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.2s ease;
|
||||
z-index: 9999;
|
||||
box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* Show tooltip on hover */
|
||||
[data-tooltip]:hover::before,
|
||||
[data-tooltip]:hover::after {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(-6px);
|
||||
}
|
||||
|
||||
/* Tooltip positioning variants */
|
||||
[data-tooltip-bottom]::before {
|
||||
bottom: auto;
|
||||
top: 100%;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: #1a1a1a;
|
||||
transform: translateX(-50%) translateY(-8px);
|
||||
}
|
||||
|
||||
[data-tooltip-bottom]::after {
|
||||
bottom: auto;
|
||||
top: 100%;
|
||||
transform: translateX(-50%) translateY(-12px);
|
||||
}
|
||||
|
||||
[data-tooltip-bottom]:hover::before,
|
||||
[data-tooltip-bottom]:hover::after {
|
||||
transform: translateX(-50%) translateY(6px);
|
||||
}
|
||||
|
||||
/* Tooltip for left-aligned elements */
|
||||
[data-tooltip-left]::before {
|
||||
left: 0;
|
||||
transform: translateX(-100%) translateY(-50%);
|
||||
}
|
||||
|
||||
[data-tooltip-left]::after {
|
||||
left: 0;
|
||||
transform: translateX(calc(-100% - 10px)) translateY(-50%);
|
||||
}
|
||||
|
||||
[data-tooltip-left]:hover::before,
|
||||
[data-tooltip-left]:hover::after {
|
||||
transform: translateX(calc(-100% - 6px)) translateY(-50%);
|
||||
}
|
||||
|
||||
/* Tooltip for right-aligned elements */
|
||||
[data-tooltip-right]::before {
|
||||
left: auto;
|
||||
right: 0;
|
||||
transform: translateX(100%) translateY(-50%);
|
||||
}
|
||||
|
||||
[data-tooltip-right]::after {
|
||||
left: auto;
|
||||
right: 0;
|
||||
transform: translateX(calc(100% + 10px)) translateY(-50%);
|
||||
}
|
||||
|
||||
[data-tooltip-right]:hover::before,
|
||||
[data-tooltip-right]:hover::after {
|
||||
transform: translateX(calc(100% + 6px)) translateY(-50%);
|
||||
}
|
||||
Loading…
Reference in a new issue