feat(phase7.1): add HelpTooltip system with contextual help icons
- New HelpTooltip component: hover-triggered floating panel, themed via CSS variables, supports top/right/bottom/left positioning, no deps - New helpTexts.ts registry: 14 entries covering render settings, admin actions, template fields, and wizard fields - Admin.tsx: tooltips on Cycles/EEVEE samples, smooth angle, regenerate thumbnails, process unprocessed - RenderTemplateTable.tsx: tooltips on material replace, lighting only, shadow catcher column headers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { HelpCircle } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { HELP_TEXTS, type HelpText } from '../help/helpTexts'
|
||||
|
||||
interface HelpTooltipProps {
|
||||
helpKey?: string
|
||||
help?: HelpText
|
||||
position?: 'top' | 'right' | 'bottom' | 'left'
|
||||
size?: number
|
||||
}
|
||||
|
||||
export function HelpTooltip({
|
||||
helpKey,
|
||||
help: directHelp,
|
||||
position = 'right',
|
||||
size = 14,
|
||||
}: HelpTooltipProps) {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const help = directHelp ?? (helpKey ? HELP_TEXTS[helpKey] : undefined)
|
||||
|
||||
if (!help) return null
|
||||
|
||||
const tooltipPositionStyle: React.CSSProperties = (() => {
|
||||
switch (position) {
|
||||
case 'top':
|
||||
return { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: 6 }
|
||||
case 'bottom':
|
||||
return { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: 6 }
|
||||
case 'left':
|
||||
return { right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: 6 }
|
||||
case 'right':
|
||||
default:
|
||||
return { left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: 6 }
|
||||
}
|
||||
})()
|
||||
|
||||
return (
|
||||
<span
|
||||
className="relative inline-flex items-center"
|
||||
onMouseEnter={() => setVisible(true)}
|
||||
onMouseLeave={() => setVisible(false)}
|
||||
>
|
||||
<HelpCircle
|
||||
size={size}
|
||||
className="text-content-muted hover:text-content-secondary transition-colors cursor-help"
|
||||
/>
|
||||
|
||||
{visible && (
|
||||
<div
|
||||
className="absolute z-50 rounded-lg border shadow-lg p-3 text-sm"
|
||||
style={{
|
||||
...tooltipPositionStyle,
|
||||
backgroundColor: 'var(--color-bg-surface, var(--color-surface, #fff))',
|
||||
borderColor: 'var(--color-border-default, #e5e7eb)',
|
||||
color: 'var(--color-text-content, var(--color-content, #111827))',
|
||||
maxWidth: 280,
|
||||
minWidth: 180,
|
||||
whiteSpace: 'normal',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<p className="font-semibold mb-1" style={{ color: 'var(--color-text-content, inherit)' }}>
|
||||
{help.title}
|
||||
</p>
|
||||
<p className="text-xs leading-relaxed" style={{ color: 'var(--color-content-secondary, #6b7280)' }}>
|
||||
{help.body}
|
||||
</p>
|
||||
{help.unit && help.range && (
|
||||
<p className="text-xs mt-1" style={{ color: 'var(--color-content-muted, #9ca3af)' }}>
|
||||
Range: {help.range[0]}–{help.range[1]} {help.unit}
|
||||
</p>
|
||||
)}
|
||||
{help.recommendation && (
|
||||
<p
|
||||
className="text-xs mt-1.5 px-2 py-1 rounded"
|
||||
style={{ backgroundColor: 'rgba(16, 185, 129, 0.1)', color: 'var(--color-status-success-text, #065f46)' }}
|
||||
>
|
||||
Recommended: {help.recommendation}
|
||||
</p>
|
||||
)}
|
||||
{help.warning && (
|
||||
<p
|
||||
className="text-xs mt-1.5 px-2 py-1 rounded"
|
||||
style={{ backgroundColor: 'rgba(245, 158, 11, 0.1)', color: 'var(--color-status-warning-text, #92400e)' }}
|
||||
>
|
||||
Warning: {help.warning}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default HelpTooltip
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { Pencil, Trash2, Plus, Check, X, Upload, Download } from 'lucide-react'
|
||||
import HelpTooltip from '../HelpTooltip'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
listRenderTemplates,
|
||||
@@ -165,9 +166,24 @@ export default function RenderTemplateTable() {
|
||||
<th className="px-3 py-2 font-medium">Category</th>
|
||||
<th className="px-3 py-2 font-medium">Output Type</th>
|
||||
<th className="px-3 py-2 font-medium">Collection</th>
|
||||
<th className="px-3 py-2 font-medium">Mat. Replace</th>
|
||||
<th className="px-3 py-2 font-medium">Lighting Only</th>
|
||||
<th className="px-3 py-2 font-medium" title="Enable Shadowcatcher collection (Cycles only)">Shadow Catcher</th>
|
||||
<th className="px-3 py-2 font-medium">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
Mat. Replace
|
||||
<HelpTooltip helpKey="template.material_replace_enabled" position="bottom" size={12} />
|
||||
</span>
|
||||
</th>
|
||||
<th className="px-3 py-2 font-medium">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
Lighting Only
|
||||
<HelpTooltip helpKey="template.lighting_only" position="bottom" size={12} />
|
||||
</span>
|
||||
</th>
|
||||
<th className="px-3 py-2 font-medium">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
Shadow Catcher
|
||||
<HelpTooltip helpKey="template.shadow_catcher" position="bottom" size={12} />
|
||||
</span>
|
||||
</th>
|
||||
<th className="px-3 py-2 font-medium" title="Rotate camera around product instead of product rotation (faster GPU rendering)">Cam Orbit</th>
|
||||
<th className="px-3 py-2 font-medium">.blend File</th>
|
||||
<th className="px-3 py-2 font-medium">Active</th>
|
||||
|
||||
Reference in New Issue
Block a user