39948-vm/frontend/src/components/ElementSettings/EffectsSettingsSectionCompact.tsx

456 lines
17 KiB
TypeScript

/**
* EffectsSettingsSectionCompact
*
* Compact animation and interaction effect settings for the constructor sidebar.
* Uses smaller inputs and labels to fit in the narrow sidebar.
*/
import React from 'react';
import type { EffectsSettingsFormValues } from './types';
import type { CanvasElementType } from '../../types/constructor';
interface EffectsSettingsSectionCompactProps {
values: EffectsSettingsFormValues;
onChange: (prop: keyof EffectsSettingsFormValues, value: string) => void;
elementType?: CanvasElementType;
}
const EffectsSettingsSectionCompact: React.FC<
EffectsSettingsSectionCompactProps
> = ({ values, onChange, elementType }) => {
const showSlideTransition =
elementType === 'gallery' || elementType === 'carousel';
return (
<div className='space-y-3'>
{/* Appear Animation */}
<div>
<p className='mb-2 text-[11px] font-semibold text-white/90'>
Appear Animation
</p>
<div className='grid grid-cols-2 gap-2'>
<div className='col-span-2'>
<label className='mb-1 block text-[10px] text-white/60'>Type</label>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.appearAnimation || ''}
onChange={(e) => onChange('appearAnimation', e.target.value)}
>
<option value=''>None</option>
<option value='fade'>Fade</option>
<option value='slide-up'>Slide Up</option>
<option value='slide-down'>Slide Down</option>
<option value='slide-left'>Slide Left</option>
<option value='slide-right'>Slide Right</option>
<option value='scale'>Scale</option>
</select>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Duration (sec)
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.appearAnimationDuration || ''}
onChange={(e) =>
onChange('appearAnimationDuration', e.target.value)
}
placeholder='0.3'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Easing
</label>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.appearAnimationEasing || ''}
onChange={(e) =>
onChange('appearAnimationEasing', e.target.value)
}
>
<option value=''>ease</option>
<option value='linear'>linear</option>
<option value='ease-in'>ease-in</option>
<option value='ease-out'>ease-out</option>
<option value='ease-in-out'>ease-in-out</option>
</select>
</div>
</div>
</div>
{/* Hover Effects */}
<div>
<p className='mb-2 text-[11px] font-semibold text-white/90'>
Hover Effects
</p>
<div className='grid grid-cols-2 gap-2'>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Scale
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverScale || ''}
onChange={(e) => onChange('hoverScale', e.target.value)}
placeholder='1.05'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Opacity
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverOpacity || ''}
onChange={(e) => onChange('hoverOpacity', e.target.value)}
placeholder='0..1'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
BG color
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverBackgroundColor || ''}
onChange={(e) => onChange('hoverBackgroundColor', e.target.value)}
placeholder='#B39368'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Text color
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverColor || ''}
onChange={(e) => onChange('hoverColor', e.target.value)}
placeholder='#FFFFFF'
/>
</div>
<div className='col-span-2'>
<label className='mb-1 block text-[10px] text-white/60'>
Box shadow
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverBoxShadow || ''}
onChange={(e) => onChange('hoverBoxShadow', e.target.value)}
placeholder='0 4px 12px rgba(...)'
/>
</div>
<div className='col-span-2'>
<label className='mb-1 block text-[10px] text-white/60'>
Transition (sec)
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverTransitionDuration || ''}
onChange={(e) =>
onChange('hoverTransitionDuration', e.target.value)
}
placeholder='0.2'
/>
</div>
<div className='col-span-2'>
<label className='mb-1 flex items-center gap-2 text-[10px] text-white/60'>
<input
type='checkbox'
checked={values.hoverPersistOnClick === 'true'}
onChange={(e) =>
onChange(
'hoverPersistOnClick',
e.target.checked ? 'true' : '',
)
}
className='rounded border-gray-300'
/>
Persist effects after click
</label>
</div>
{/* Hover Reveal Subsection */}
<div className='col-span-2 mt-2 pt-2 border-t border-white/20'>
<p className='mb-2 text-[10px] font-medium text-white/80'>
Hover Reveal
</p>
</div>
<div className='col-span-2'>
<label className='mb-1 flex items-center gap-2 text-[10px] text-white/60'>
<input
type='checkbox'
checked={values.hoverReveal === 'true'}
onChange={(e) =>
onChange('hoverReveal', e.target.checked ? 'true' : '')
}
className='rounded border-gray-300'
/>
Enable Hover Reveal
</label>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Initial Opacity
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverRevealInitialOpacity || ''}
onChange={(e) =>
onChange('hoverRevealInitialOpacity', e.target.value)
}
placeholder='0'
disabled={values.hoverReveal !== 'true'}
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Target Opacity
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverRevealTargetOpacity || ''}
onChange={(e) =>
onChange('hoverRevealTargetOpacity', e.target.value)
}
placeholder='1'
disabled={values.hoverReveal !== 'true'}
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Duration (sec)
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverRevealDuration || ''}
onChange={(e) => onChange('hoverRevealDuration', e.target.value)}
placeholder='0.3'
disabled={values.hoverReveal !== 'true'}
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Delay (sec)
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.hoverRevealDelay || ''}
onChange={(e) => onChange('hoverRevealDelay', e.target.value)}
placeholder='0'
disabled={values.hoverReveal !== 'true'}
/>
</div>
<div className='col-span-2'>
<label className='mb-1 flex items-center gap-2 text-[10px] text-white/60'>
<input
type='checkbox'
checked={values.hoverRevealPersist === 'true'}
onChange={(e) =>
onChange('hoverRevealPersist', e.target.checked ? 'true' : '')
}
className='rounded border-gray-300'
disabled={values.hoverReveal !== 'true'}
/>
Stay visible after hover (persist)
</label>
</div>
</div>
</div>
{/* Focus Effects */}
<div>
<p className='mb-2 text-[11px] font-semibold text-white/90'>
Focus Effects
</p>
<div className='grid grid-cols-2 gap-2'>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Scale
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.focusScale || ''}
onChange={(e) => onChange('focusScale', e.target.value)}
placeholder='1.02'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Opacity
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.focusOpacity || ''}
onChange={(e) => onChange('focusOpacity', e.target.value)}
placeholder='0..1'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Outline
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.focusOutline || ''}
onChange={(e) => onChange('focusOutline', e.target.value)}
placeholder='2px solid #B39368'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Box shadow
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.focusBoxShadow || ''}
onChange={(e) => onChange('focusBoxShadow', e.target.value)}
placeholder='0 0 0 3px rgba(...)'
/>
</div>
</div>
</div>
{/* Active/Press Effects */}
<div>
<p className='mb-2 text-[11px] font-semibold text-white/90'>
Active/Press Effects
</p>
<div className='grid grid-cols-2 gap-2'>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Scale
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.activeScale || ''}
onChange={(e) => onChange('activeScale', e.target.value)}
placeholder='0.95'
/>
</div>
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Opacity
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.activeOpacity || ''}
onChange={(e) => onChange('activeOpacity', e.target.value)}
placeholder='0..1'
/>
</div>
<div className='col-span-2'>
<label className='mb-1 block text-[10px] text-white/60'>
BG color
</label>
<input
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.activeBackgroundColor || ''}
onChange={(e) =>
onChange('activeBackgroundColor', e.target.value)
}
placeholder='#131C22'
/>
</div>
</div>
</div>
{/* Slide Transition Override - Gallery/Carousel only */}
{showSlideTransition && (
<div className='mt-3 border-t border-white/20 pt-3'>
<p className='mb-1 text-[11px] font-semibold text-white/90'>
Slide Transition
</p>
<p className='mb-2 text-[10px] text-white/60'>
Override page transition for slides. Leave empty for defaults.
</p>
<div className='grid grid-cols-2 gap-2'>
{/* Type */}
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Type
</label>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.slideTransitionType || ''}
onChange={(e) =>
onChange('slideTransitionType', e.target.value)
}
>
<option value=''>Use Default</option>
<option value='fade'>Fade</option>
<option value='none'>None (Instant)</option>
</select>
</div>
{/* Duration */}
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Duration (ms)
</label>
<input
type='number'
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.slideTransitionDurationMs || ''}
onChange={(e) =>
onChange('slideTransitionDurationMs', e.target.value)
}
placeholder='400'
min='0'
step='50'
/>
</div>
{/* Easing */}
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Easing
</label>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.slideTransitionEasing || ''}
onChange={(e) =>
onChange('slideTransitionEasing', e.target.value)
}
>
<option value=''>Use Default</option>
<option value='ease-in-out'>Ease In-Out</option>
<option value='ease-in'>Ease In</option>
<option value='ease-out'>Ease Out</option>
<option value='linear'>Linear</option>
</select>
</div>
{/* Overlay Color */}
<div>
<label className='mb-1 block text-[10px] text-white/60'>
Overlay Color
</label>
<div className='flex gap-1'>
<input
type='color'
className='h-[26px] w-8 cursor-pointer rounded border border-gray-300'
value={values.slideTransitionOverlayColor || '#000000'}
onChange={(e) =>
onChange('slideTransitionOverlayColor', e.target.value)
}
/>
<input
type='text'
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={values.slideTransitionOverlayColor || ''}
onChange={(e) =>
onChange('slideTransitionOverlayColor', e.target.value)
}
placeholder='#000000'
/>
</div>
</div>
</div>
</div>
)}
</div>
);
};
export default EffectsSettingsSectionCompact;