456 lines
17 KiB
TypeScript
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;
|