improved menus in constructor
This commit is contained in:
parent
4634ad9207
commit
f06a2b2c97
@ -58,7 +58,7 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -81,17 +81,17 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{durationNote && (
|
{durationNote && (
|
||||||
<p className='mt-1 text-[11px] text-gray-500'>{durationNote}</p>
|
<p className='mt-1 text-[11px] text-white/60'>{durationNote}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Video Playback Settings */}
|
{/* Video Playback Settings */}
|
||||||
{showVideoSettings && (
|
{showVideoSettings && (
|
||||||
<div className='mt-3 space-y-2 border-t border-gray-200 pt-3'>
|
<div className='mt-3 space-y-2 border-t border-white/20 pt-3'>
|
||||||
<p className='text-[10px] font-semibold uppercase text-gray-500'>
|
<p className='text-[10px] font-semibold uppercase text-white/70'>
|
||||||
Playback Settings
|
Playback Settings
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
className='h-3 w-3 rounded border-gray-300'
|
className='h-3 w-3 rounded border-gray-300'
|
||||||
@ -103,7 +103,7 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
Autoplay
|
Autoplay
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
className='h-3 w-3 rounded border-gray-300'
|
className='h-3 w-3 rounded border-gray-300'
|
||||||
@ -115,7 +115,7 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
Loop
|
Loop
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex cursor-pointer items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
className='h-3 w-3 rounded border-gray-300'
|
className='h-3 w-3 rounded border-gray-300'
|
||||||
@ -129,7 +129,7 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Start (sec)
|
Start (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -149,7 +149,7 @@ const BackgroundSettingsEditor: React.FC<BackgroundSettingsEditorProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
End (sec)
|
End (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* ConstructorControlsPanel Component
|
|
||||||
*
|
|
||||||
* Draggable panel with page selector, mode toggle, and exit button.
|
|
||||||
* Used in constructor for top-level navigation controls.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import BaseButton from '../BaseButton';
|
|
||||||
import { mdiExitToApp } from '@mdi/js';
|
|
||||||
import PageSelector from './PageSelector';
|
|
||||||
import InteractionModeToggle from './InteractionModeToggle';
|
|
||||||
import type { Position, TourPage, ConstructorInteractionMode } from './types';
|
|
||||||
|
|
||||||
interface ConstructorControlsPanelProps {
|
|
||||||
projectId: string;
|
|
||||||
pages: TourPage[];
|
|
||||||
activePageId: string;
|
|
||||||
interactionMode: ConstructorInteractionMode;
|
|
||||||
position: Position;
|
|
||||||
onPageChange: (pageId: string) => void;
|
|
||||||
onModeChange: (mode: ConstructorInteractionMode) => void;
|
|
||||||
onDragStart: (event: React.MouseEvent) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConstructorControlsPanel: React.FC<ConstructorControlsPanelProps> = ({
|
|
||||||
projectId,
|
|
||||||
pages,
|
|
||||||
activePageId,
|
|
||||||
interactionMode,
|
|
||||||
position,
|
|
||||||
onPageChange,
|
|
||||||
onModeChange,
|
|
||||||
onDragStart,
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className='fixed z-[1000] w-[min(92vw,460px)] rounded-lg border border-gray-200 bg-white shadow-xl'
|
|
||||||
style={{
|
|
||||||
left: position.x,
|
|
||||||
top: position.y,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className='flex cursor-move items-center justify-between rounded-t-lg border-b border-gray-200 bg-gray-50 px-3 py-2'
|
|
||||||
onMouseDown={onDragStart}
|
|
||||||
>
|
|
||||||
<span className='text-xs font-bold uppercase'>
|
|
||||||
Constructor Controls
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className='space-y-2 p-3'>
|
|
||||||
<div className='flex flex-wrap items-center gap-2'>
|
|
||||||
<PageSelector
|
|
||||||
pages={pages}
|
|
||||||
activePageId={activePageId}
|
|
||||||
onPageChange={onPageChange}
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
color='lightDark'
|
|
||||||
label='Exit to Assets'
|
|
||||||
icon={mdiExitToApp}
|
|
||||||
href={
|
|
||||||
projectId ? `/projects/${projectId}` : '/projects/projects-list'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<InteractionModeToggle
|
|
||||||
mode={interactionMode}
|
|
||||||
onModeChange={onModeChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ConstructorControlsPanel;
|
|
||||||
@ -1,195 +0,0 @@
|
|||||||
/**
|
|
||||||
* ConstructorMenu Component
|
|
||||||
*
|
|
||||||
* Draggable menu panel with actions for adding elements, backgrounds, etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { forwardRef } from 'react';
|
|
||||||
import BaseIcon from '../BaseIcon';
|
|
||||||
import BaseButton from '../BaseButton';
|
|
||||||
import {
|
|
||||||
mdiMenu,
|
|
||||||
mdiImageMultiple,
|
|
||||||
mdiViewCarousel,
|
|
||||||
mdiTooltipText,
|
|
||||||
mdiSwapHorizontal,
|
|
||||||
mdiText,
|
|
||||||
mdiPlus,
|
|
||||||
mdiExitToApp,
|
|
||||||
} from '@mdi/js';
|
|
||||||
import MenuActionButton from './MenuActionButton';
|
|
||||||
import dataFormatter from '../../helpers/dataFormatter';
|
|
||||||
import type {
|
|
||||||
Position,
|
|
||||||
CanvasElementType,
|
|
||||||
NavigationElementType,
|
|
||||||
} from './types';
|
|
||||||
import type { EditorMenuItem } from '../../types/constructor';
|
|
||||||
|
|
||||||
interface ConstructorMenuProps {
|
|
||||||
position: Position;
|
|
||||||
isOpen: boolean;
|
|
||||||
allowedNavigationTypes: NavigationElementType[];
|
|
||||||
isCreatingPage: boolean;
|
|
||||||
isSaving: boolean;
|
|
||||||
isSavingToStage: boolean;
|
|
||||||
onDragStart: (event: React.MouseEvent) => void;
|
|
||||||
onToggleOpen: () => void;
|
|
||||||
onSelectMenuItem: (item: EditorMenuItem) => void;
|
|
||||||
onAddElement: (type: CanvasElementType) => void;
|
|
||||||
onCreatePage: () => void;
|
|
||||||
onSave: () => void;
|
|
||||||
onSaveToStage: () => void;
|
|
||||||
onExit: () => void;
|
|
||||||
/** Page's last saved timestamp (updatedAt from tour_pages) */
|
|
||||||
lastSavedAt?: string | null;
|
|
||||||
/** Last save-to-stage timestamp */
|
|
||||||
lastSavedToStageAt?: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConstructorMenu = forwardRef<HTMLDivElement, ConstructorMenuProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
position,
|
|
||||||
isOpen,
|
|
||||||
allowedNavigationTypes,
|
|
||||||
isCreatingPage,
|
|
||||||
isSaving,
|
|
||||||
isSavingToStage,
|
|
||||||
onDragStart,
|
|
||||||
onToggleOpen,
|
|
||||||
onSelectMenuItem,
|
|
||||||
onAddElement,
|
|
||||||
onCreatePage,
|
|
||||||
onSave,
|
|
||||||
onSaveToStage,
|
|
||||||
onExit,
|
|
||||||
lastSavedAt,
|
|
||||||
lastSavedToStageAt,
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
className='fixed z-[1000] w-60 border border-gray-200 rounded-lg bg-white shadow-xl'
|
|
||||||
style={{ left: position.x, top: position.y }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className='flex items-center justify-between px-3 py-2 border-b border-gray-200 cursor-move bg-gray-50 rounded-t-lg'
|
|
||||||
onMouseDown={onDragStart}
|
|
||||||
>
|
|
||||||
<span className='text-xs font-bold uppercase'>Constructor Menu</span>
|
|
||||||
<button type='button' onClick={onToggleOpen}>
|
|
||||||
<BaseIcon path={mdiMenu} size={18} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isOpen && (
|
|
||||||
<div className='p-2 space-y-1 max-h-[calc(100vh-120px)] overflow-y-auto'>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiImageMultiple}
|
|
||||||
label='Background Image'
|
|
||||||
onClick={() => onSelectMenuItem('background_image')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiViewCarousel}
|
|
||||||
label='Background Video'
|
|
||||||
onClick={() => onSelectMenuItem('background_video')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiTooltipText}
|
|
||||||
label='Background Audio'
|
|
||||||
onClick={() => onSelectMenuItem('background_audio')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiSwapHorizontal}
|
|
||||||
label='Add Navigation Button'
|
|
||||||
onClick={() => onAddElement(allowedNavigationTypes[0])}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiSwapHorizontal}
|
|
||||||
label='Add Transition'
|
|
||||||
onClick={() => onSelectMenuItem('create_transition')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiImageMultiple}
|
|
||||||
label='Add Gallery'
|
|
||||||
onClick={() => onAddElement('gallery')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiViewCarousel}
|
|
||||||
label='Add Carousel'
|
|
||||||
onClick={() => onAddElement('carousel')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiTooltipText}
|
|
||||||
label='Add Tooltip'
|
|
||||||
onClick={() => onAddElement('tooltip')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiText}
|
|
||||||
label='Add Description'
|
|
||||||
onClick={() => onAddElement('description')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiViewCarousel}
|
|
||||||
label='Add Video Player'
|
|
||||||
onClick={() => onAddElement('video_player')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiTooltipText}
|
|
||||||
label='Add Audio Player'
|
|
||||||
onClick={() => onAddElement('audio_player')}
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiPlus}
|
|
||||||
label={isCreatingPage ? 'Creating Page...' : 'Create New Page'}
|
|
||||||
onClick={onCreatePage}
|
|
||||||
disabled={isCreatingPage}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className='pt-2 border-t border-gray-200 space-y-1'>
|
|
||||||
<BaseButton
|
|
||||||
small
|
|
||||||
color='info'
|
|
||||||
label={isSaving ? 'Saving...' : 'Save'}
|
|
||||||
subtitle={
|
|
||||||
lastSavedAt
|
|
||||||
? dataFormatter.relativeTimestamp(lastSavedAt)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onClick={onSave}
|
|
||||||
disabled={isSaving}
|
|
||||||
className='w-full'
|
|
||||||
/>
|
|
||||||
<BaseButton
|
|
||||||
small
|
|
||||||
color='success'
|
|
||||||
label={isSavingToStage ? 'Saving...' : 'Save to Stage'}
|
|
||||||
subtitle={
|
|
||||||
lastSavedToStageAt
|
|
||||||
? dataFormatter.relativeTimestamp(lastSavedToStageAt)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onClick={onSaveToStage}
|
|
||||||
disabled={isSavingToStage}
|
|
||||||
className='w-full'
|
|
||||||
/>
|
|
||||||
<MenuActionButton
|
|
||||||
icon={mdiExitToApp}
|
|
||||||
label='Exit'
|
|
||||||
onClick={onExit}
|
|
||||||
className='!text-red-700'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ConstructorMenu.displayName = 'ConstructorMenu';
|
|
||||||
|
|
||||||
export default ConstructorMenu;
|
|
||||||
352
frontend/src/components/Constructor/ConstructorToolbar.tsx
Normal file
352
frontend/src/components/Constructor/ConstructorToolbar.tsx
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
/**
|
||||||
|
* ConstructorToolbar Component
|
||||||
|
*
|
||||||
|
* Unified toolbar combining page controls and element actions.
|
||||||
|
* Glassmorphism styling with draggable positioning.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState, useRef, useEffect, forwardRef } from 'react';
|
||||||
|
import {
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiChevronDown,
|
||||||
|
mdiImageMultiple,
|
||||||
|
mdiViewCarousel,
|
||||||
|
mdiTooltipText,
|
||||||
|
mdiSwapHorizontal,
|
||||||
|
mdiText,
|
||||||
|
mdiPlus,
|
||||||
|
mdiExitToApp,
|
||||||
|
mdiChevronLeft,
|
||||||
|
mdiChevronRight,
|
||||||
|
mdiMusicNote,
|
||||||
|
mdiVideo,
|
||||||
|
} from '@mdi/js';
|
||||||
|
import BaseIcon from '../BaseIcon';
|
||||||
|
import BaseButton from '../BaseButton';
|
||||||
|
import ClickOutside from '../ClickOutside';
|
||||||
|
import PageSelector from './PageSelector';
|
||||||
|
import InteractionModeToggle from './InteractionModeToggle';
|
||||||
|
import MenuActionButton from './MenuActionButton';
|
||||||
|
import dataFormatter from '../../helpers/dataFormatter';
|
||||||
|
import type { ConstructorToolbarProps } from './types';
|
||||||
|
|
||||||
|
const ConstructorToolbar = forwardRef<HTMLDivElement, ConstructorToolbarProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
position,
|
||||||
|
onDragStart,
|
||||||
|
pages,
|
||||||
|
activePageId,
|
||||||
|
onPageChange,
|
||||||
|
interactionMode,
|
||||||
|
onModeChange,
|
||||||
|
onSelectMenuItem,
|
||||||
|
allowedNavigationTypes,
|
||||||
|
onAddElement,
|
||||||
|
onCreatePage,
|
||||||
|
isCreatingPage,
|
||||||
|
onSave,
|
||||||
|
onSaveToStage,
|
||||||
|
isSaving,
|
||||||
|
isSavingToStage,
|
||||||
|
lastSavedAt,
|
||||||
|
lastSavedToStageAt,
|
||||||
|
onExit,
|
||||||
|
},
|
||||||
|
ref,
|
||||||
|
) => {
|
||||||
|
// Local UI state
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||||
|
const [activeDropdown, setActiveDropdown] = useState<
|
||||||
|
'bg' | 'elements' | null
|
||||||
|
>(null);
|
||||||
|
|
||||||
|
// Refs for ClickOutside exclusion (following NavBarItem pattern)
|
||||||
|
const bgTriggerRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const elementsTriggerRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
// Keyboard handling (Escape closes dropdown)
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape' && activeDropdown) {
|
||||||
|
setActiveDropdown(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
}, [activeDropdown]);
|
||||||
|
|
||||||
|
// Dropdown handlers
|
||||||
|
const closeDropdown = () => setActiveDropdown(null);
|
||||||
|
const toggleDropdown = (dropdown: 'bg' | 'elements') => {
|
||||||
|
setActiveDropdown((prev) => (prev === dropdown ? null : dropdown));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close dropdown after menu action
|
||||||
|
const handleMenuAction = (action: () => void) => {
|
||||||
|
action();
|
||||||
|
closeDropdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shared button styles
|
||||||
|
const triggerBtnClass =
|
||||||
|
'flex items-center gap-1.5 px-3 py-2 rounded text-sm font-medium text-white/90 hover:bg-white/20 transition-colors';
|
||||||
|
const dropdownPanelClass =
|
||||||
|
'absolute top-full left-0 mt-1 min-w-[180px] py-1 rounded-lg bg-white/50 backdrop-blur-xl border border-white/30 shadow-lg z-10';
|
||||||
|
|
||||||
|
// Collapsed state
|
||||||
|
if (isCollapsed) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className='fixed z-[1000] flex items-center gap-1 px-2 py-1.5 rounded-lg bg-white/10 backdrop-blur-xl border border-white/30 shadow-xl'
|
||||||
|
style={{ left: position.x, top: position.y }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='cursor-move flex items-center justify-center w-10 h-10 text-white/60'
|
||||||
|
onMouseDown={onDragStart}
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiDotsVertical} size={24} />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() => setIsCollapsed(false)}
|
||||||
|
className='flex items-center justify-center w-10 h-10 rounded text-white/60 hover:text-white/90 hover:bg-white/20 transition-colors'
|
||||||
|
title='Expand toolbar'
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiChevronRight} size={26} />
|
||||||
|
</button>
|
||||||
|
<span className='text-base font-medium text-white/80 truncate max-w-[140px] pr-2'>
|
||||||
|
{pages.find((p) => p.id === activePageId)?.name || 'Page'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className='fixed z-[1000] flex items-center gap-2 px-2 py-1.5 rounded-lg bg-white/10 backdrop-blur-xl border border-white/30 shadow-xl max-w-[95vw]'
|
||||||
|
style={{ left: position.x, top: position.y }}
|
||||||
|
>
|
||||||
|
{/* Drag Handle */}
|
||||||
|
<div
|
||||||
|
className='cursor-move flex items-center justify-center w-10 h-10 text-white/60 hover:text-white/90'
|
||||||
|
onMouseDown={onDragStart}
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiDotsVertical} size={24} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Page Selector - reuse existing component */}
|
||||||
|
<PageSelector
|
||||||
|
pages={pages}
|
||||||
|
activePageId={activePageId}
|
||||||
|
onPageChange={onPageChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Mode Toggle - reuse with compact=true */}
|
||||||
|
<InteractionModeToggle
|
||||||
|
mode={interactionMode}
|
||||||
|
onModeChange={onModeChange}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className='w-px h-8 bg-white/30' />
|
||||||
|
|
||||||
|
{/* Backgrounds Dropdown */}
|
||||||
|
<div className='relative'>
|
||||||
|
<button
|
||||||
|
ref={bgTriggerRef}
|
||||||
|
type='button'
|
||||||
|
onClick={() => toggleDropdown('bg')}
|
||||||
|
className={triggerBtnClass}
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiImageMultiple} size={18} />
|
||||||
|
<span>BG</span>
|
||||||
|
<BaseIcon path={mdiChevronDown} size={16} />
|
||||||
|
</button>
|
||||||
|
{activeDropdown === 'bg' && (
|
||||||
|
<ClickOutside
|
||||||
|
onClickOutside={closeDropdown}
|
||||||
|
excludedElements={[bgTriggerRef]}
|
||||||
|
>
|
||||||
|
<div className={dropdownPanelClass}>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiImageMultiple}
|
||||||
|
label='Background Image'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onSelectMenuItem('background_image'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiVideo}
|
||||||
|
label='Background Video'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onSelectMenuItem('background_video'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiMusicNote}
|
||||||
|
label='Background Audio'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onSelectMenuItem('background_audio'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ClickOutside>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Elements Dropdown */}
|
||||||
|
<div className='relative'>
|
||||||
|
<button
|
||||||
|
ref={elementsTriggerRef}
|
||||||
|
type='button'
|
||||||
|
onClick={() => toggleDropdown('elements')}
|
||||||
|
className={triggerBtnClass}
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiPlus} size={18} />
|
||||||
|
<span>Elements</span>
|
||||||
|
<BaseIcon path={mdiChevronDown} size={16} />
|
||||||
|
</button>
|
||||||
|
{activeDropdown === 'elements' && (
|
||||||
|
<ClickOutside
|
||||||
|
onClickOutside={closeDropdown}
|
||||||
|
excludedElements={[elementsTriggerRef]}
|
||||||
|
>
|
||||||
|
<div className={dropdownPanelClass}>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiSwapHorizontal}
|
||||||
|
label='Navigation Button'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() =>
|
||||||
|
onAddElement(allowedNavigationTypes[0]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiSwapHorizontal}
|
||||||
|
label='Transition'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() =>
|
||||||
|
onSelectMenuItem('create_transition'),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiImageMultiple}
|
||||||
|
label='Gallery'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('gallery'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiViewCarousel}
|
||||||
|
label='Carousel'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('carousel'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiTooltipText}
|
||||||
|
label='Tooltip'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('tooltip'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiText}
|
||||||
|
label='Description'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('description'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiVideo}
|
||||||
|
label='Video Player'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('video_player'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<MenuActionButton
|
||||||
|
icon={mdiMusicNote}
|
||||||
|
label='Audio Player'
|
||||||
|
onClick={() =>
|
||||||
|
handleMenuAction(() => onAddElement('audio_player'))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ClickOutside>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className='w-px h-8 bg-white/30' />
|
||||||
|
|
||||||
|
{/* Create Page Button */}
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={onCreatePage}
|
||||||
|
disabled={isCreatingPage}
|
||||||
|
className={`${triggerBtnClass} ${isCreatingPage ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiPlus} size={18} />
|
||||||
|
<span>{isCreatingPage ? 'Creating...' : 'Page'}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Save Button - reuse BaseButton with subtitle */}
|
||||||
|
<BaseButton
|
||||||
|
small
|
||||||
|
color='info'
|
||||||
|
label={isSaving ? 'Saving...' : 'Save'}
|
||||||
|
subtitle={
|
||||||
|
lastSavedAt
|
||||||
|
? dataFormatter.relativeTimestamp(lastSavedAt)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={onSave}
|
||||||
|
disabled={isSaving}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Save to Stage Button */}
|
||||||
|
<BaseButton
|
||||||
|
small
|
||||||
|
color='success'
|
||||||
|
label={isSavingToStage ? 'Saving...' : 'Stage'}
|
||||||
|
subtitle={
|
||||||
|
lastSavedToStageAt
|
||||||
|
? dataFormatter.relativeTimestamp(lastSavedToStageAt)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onClick={onSaveToStage}
|
||||||
|
disabled={isSavingToStage}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Exit Button */}
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={onExit}
|
||||||
|
className='flex items-center justify-center w-10 h-10 rounded text-red-400 hover:text-red-300 hover:bg-red-500/20 transition-colors'
|
||||||
|
title='Exit constructor'
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiExitToApp} size={26} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Collapse Toggle */}
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
onClick={() => setIsCollapsed(true)}
|
||||||
|
className='flex items-center justify-center w-10 h-10 rounded text-white/60 hover:text-white/90 hover:bg-white/20 transition-colors'
|
||||||
|
title='Collapse toolbar'
|
||||||
|
>
|
||||||
|
<BaseIcon path={mdiChevronLeft} size={26} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ConstructorToolbar.displayName = 'ConstructorToolbar';
|
||||||
|
|
||||||
|
export default ConstructorToolbar;
|
||||||
@ -36,8 +36,8 @@ const CreateTransitionForm: React.FC<CreateTransitionFormProps> = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<p className='text-[11px] font-semibold text-gray-600'>
|
<p className='text-[11px] font-semibold text-white/80'>
|
||||||
Create next page transition
|
Create next page transition
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -61,11 +61,11 @@ const CreateTransitionForm: React.FC<CreateTransitionFormProps> = ({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<p className='text-[11px] text-gray-500'>
|
<p className='text-[11px] text-white/60'>
|
||||||
Transition duration is automatic from video metadata. {durationNote}
|
Transition duration is automatic from video metadata. {durationNote}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
checked={supportsReverse}
|
checked={supportsReverse}
|
||||||
|
|||||||
@ -26,16 +26,16 @@ const ElementEditorHeader: React.FC<ElementEditorHeaderProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='mb-3 flex items-center justify-between gap-2 cursor-move'
|
className='mb-2 flex items-center justify-between gap-1 cursor-move'
|
||||||
onMouseDown={onDragStart}
|
onMouseDown={onDragStart}
|
||||||
>
|
>
|
||||||
<p className='text-xs font-bold uppercase tracking-wide text-gray-700'>
|
<p className='text-[10px] font-bold uppercase tracking-wide text-white/90 truncate'>
|
||||||
{title}
|
{title}
|
||||||
</p>
|
</p>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-1.5 shrink-0'>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className='text-xs text-gray-700 hover:underline'
|
className='text-[10px] text-white/70 hover:text-white hover:underline'
|
||||||
onClick={onToggleCollapse}
|
onClick={onToggleCollapse}
|
||||||
>
|
>
|
||||||
{isCollapsed ? 'Expand' : 'Collapse'}
|
{isCollapsed ? 'Expand' : 'Collapse'}
|
||||||
@ -43,10 +43,10 @@ const ElementEditorHeader: React.FC<ElementEditorHeaderProps> = ({
|
|||||||
{showRemoveButton && (
|
{showRemoveButton && (
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className='text-xs text-red-600 hover:underline'
|
className='text-[10px] text-red-400 hover:text-red-300 hover:underline'
|
||||||
onClick={onRemove}
|
onClick={onRemove}
|
||||||
>
|
>
|
||||||
Remove element
|
Remove
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -183,7 +183,7 @@ export function ElementEditorPanel({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={elementEditorRef}
|
ref={elementEditorRef}
|
||||||
className={`fixed z-[1000] ${isCollapsed ? 'w-[260px]' : 'w-[380px]'} max-h-[calc(100vh-2rem)] overflow-auto rounded-lg border border-gray-200 bg-white/95 p-3 shadow-xl`}
|
className={`fixed z-[1000] ${isCollapsed ? 'w-[220px]' : 'w-[300px]'} max-h-[calc(100vh-1rem)] overflow-auto rounded-lg border border-white/30 bg-white/10 backdrop-blur-xl p-2 shadow-xl text-sm`}
|
||||||
style={{ left: position.x, top: position.y }}
|
style={{ left: position.x, top: position.y }}
|
||||||
>
|
>
|
||||||
<ElementEditorHeader
|
<ElementEditorHeader
|
||||||
@ -547,7 +547,7 @@ export function ElementEditorPanel({
|
|||||||
{/* Gallery Section Styles (shown first for gallery elements) */}
|
{/* Gallery Section Styles (shown first for gallery elements) */}
|
||||||
{isGalleryElementType(selectedElement.type) && (
|
{isGalleryElementType(selectedElement.type) && (
|
||||||
<div className='space-y-2 mb-4'>
|
<div className='space-y-2 mb-4'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Gallery Section Styles
|
Gallery Section Styles
|
||||||
</p>
|
</p>
|
||||||
<GallerySectionStyleInputs
|
<GallerySectionStyleInputs
|
||||||
@ -692,7 +692,7 @@ export function ElementEditorPanel({
|
|||||||
showTitleStyles
|
showTitleStyles
|
||||||
showAspectRatio
|
showAspectRatio
|
||||||
/>
|
/>
|
||||||
<p className='text-[11px] font-semibold text-gray-700 pt-2'>
|
<p className='text-[11px] font-semibold text-white/90 pt-2'>
|
||||||
General Element Styles
|
General Element Styles
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,23 +10,25 @@ import type { ConstructorInteractionMode } from './types';
|
|||||||
interface InteractionModeToggleProps {
|
interface InteractionModeToggleProps {
|
||||||
mode: ConstructorInteractionMode;
|
mode: ConstructorInteractionMode;
|
||||||
onModeChange: (mode: ConstructorInteractionMode) => void;
|
onModeChange: (mode: ConstructorInteractionMode) => void;
|
||||||
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const InteractionModeToggle: React.FC<InteractionModeToggleProps> = ({
|
const InteractionModeToggle: React.FC<InteractionModeToggleProps> = ({
|
||||||
mode,
|
mode,
|
||||||
onModeChange,
|
onModeChange,
|
||||||
|
compact = false,
|
||||||
}) => {
|
}) => {
|
||||||
const isEditMode = mode === 'edit';
|
const isEditMode = mode === 'edit';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-wrap items-center gap-2'>
|
<div className='flex flex-wrap items-center gap-2'>
|
||||||
<div className='inline-flex overflow-hidden rounded border border-gray-300 bg-white text-xs font-semibold'>
|
<div className='inline-flex overflow-hidden rounded border border-white/30 bg-white/10 text-xs font-semibold'>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={`px-3 py-1.5 ${
|
className={`px-3 py-1.5 transition-colors ${
|
||||||
isEditMode
|
isEditMode
|
||||||
? 'bg-blue-600 text-white'
|
? 'bg-blue-500/80 text-white'
|
||||||
: 'text-gray-700 hover:bg-gray-50'
|
: 'text-white/70 hover:bg-white/10'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onModeChange('edit')}
|
onClick={() => onModeChange('edit')}
|
||||||
>
|
>
|
||||||
@ -34,21 +36,23 @@ const InteractionModeToggle: React.FC<InteractionModeToggleProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className={`border-l border-gray-300 px-3 py-1.5 ${
|
className={`border-l border-white/30 px-3 py-1.5 transition-colors ${
|
||||||
!isEditMode
|
!isEditMode
|
||||||
? 'bg-blue-600 text-white'
|
? 'bg-blue-500/80 text-white'
|
||||||
: 'text-gray-700 hover:bg-gray-50'
|
: 'text-white/70 hover:bg-white/10'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onModeChange('interact')}
|
onClick={() => onModeChange('interact')}
|
||||||
>
|
>
|
||||||
Interact mode
|
Interact mode
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-[11px] text-gray-600'>
|
{!compact && (
|
||||||
{isEditMode
|
<span className='text-[11px] text-white/60'>
|
||||||
? 'Drag & configure elements.'
|
{isEditMode
|
||||||
: 'Click and interact with rendered elements.'}
|
? 'Drag & configure elements.'
|
||||||
</span>
|
: 'Click and interact with rendered elements.'}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* MenuActionButton Component
|
* MenuActionButton Component
|
||||||
*
|
*
|
||||||
* Compact button for constructor menu actions.
|
* Compact button for constructor menu actions.
|
||||||
* Used in ConstructorMenu for adding elements, backgrounds, etc.
|
* Used in ConstructorToolbar for adding elements, backgrounds, etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|||||||
@ -45,7 +45,10 @@ const PageSelector: React.FC<PageSelectorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
className='rounded border border-gray-300 bg-white px-3 py-2 text-sm'
|
className='rounded border border-white/30 bg-white/20 pl-3 pr-8 py-1.5 text-sm text-white/90 backdrop-blur-sm focus:outline-none focus:ring-1 focus:ring-white/40 appearance-none bg-no-repeat bg-[length:16px] bg-[right_8px_center]'
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='rgba(255,255,255,0.7)' d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E")`,
|
||||||
|
}}
|
||||||
value={activePageId ?? ''}
|
value={activePageId ?? ''}
|
||||||
onChange={(event) => onPageChange(event.target.value)}
|
onChange={(event) => onPageChange(event.target.value)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Types only - import components directly from their files:
|
* Types only - import components directly from their files:
|
||||||
* import CanvasElement from './Constructor/CanvasElement';
|
* import CanvasElement from './Constructor/CanvasElement';
|
||||||
* import ConstructorMenu from './Constructor/ConstructorMenu';
|
* import ConstructorToolbar from './Constructor/ConstructorToolbar';
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
@ -83,6 +83,7 @@ export interface PageSelectorProps {
|
|||||||
export interface InteractionModeToggleProps {
|
export interface InteractionModeToggleProps {
|
||||||
mode: ConstructorInteractionMode;
|
mode: ConstructorInteractionMode;
|
||||||
onModeChange: (mode: ConstructorInteractionMode) => void;
|
onModeChange: (mode: ConstructorInteractionMode) => void;
|
||||||
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,6 +302,48 @@ export interface ConstructorMenuProps {
|
|||||||
isSavingToStage: boolean;
|
isSavingToStage: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified toolbar props - combines controls panel and menu functionality
|
||||||
|
* Replaces ConstructorControlsPanelProps and ConstructorMenuProps
|
||||||
|
*/
|
||||||
|
export interface ConstructorToolbarProps {
|
||||||
|
// Positioning (from useDraggable)
|
||||||
|
position: Position;
|
||||||
|
onDragStart: (event: React.MouseEvent) => void;
|
||||||
|
|
||||||
|
// Page selector (reuse PageSelector component)
|
||||||
|
pages: TourPage[];
|
||||||
|
activePageId: string;
|
||||||
|
onPageChange: (pageId: string) => void;
|
||||||
|
|
||||||
|
// Mode toggle (reuse InteractionModeToggle with compact=true)
|
||||||
|
interactionMode: ConstructorInteractionMode;
|
||||||
|
onModeChange: (mode: ConstructorInteractionMode) => void;
|
||||||
|
|
||||||
|
// Background actions (opens ElementEditorPanel)
|
||||||
|
onSelectMenuItem: (item: EditorMenuItem) => void;
|
||||||
|
|
||||||
|
// Element actions
|
||||||
|
allowedNavigationTypes: NavigationElementType[];
|
||||||
|
onAddElement: (type: CanvasElementType) => void;
|
||||||
|
|
||||||
|
// Page actions
|
||||||
|
onCreatePage: () => void;
|
||||||
|
isCreatingPage: boolean;
|
||||||
|
|
||||||
|
// Save actions (reuse BaseButton with subtitle for timestamps)
|
||||||
|
onSave: () => void;
|
||||||
|
onSaveToStage: () => void;
|
||||||
|
isSaving: boolean;
|
||||||
|
isSavingToStage: boolean;
|
||||||
|
lastSavedAt?: string | null;
|
||||||
|
lastSavedToStageAt?: string | null;
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
projectId: string;
|
||||||
|
onExit: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Menu action button props
|
* Menu action button props
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -71,14 +71,14 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor='carouselFullWidth'
|
htmlFor='carouselFullWidth'
|
||||||
className='text-[11px] text-gray-700'
|
className='text-[11px] text-white/80'
|
||||||
>
|
>
|
||||||
Full-width mode (background layer)
|
Full-width mode (background layer)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Navigation icons
|
Navigation icons
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='text-[10px] text-gray-600'>Caption font:</label>
|
<label className='text-[10px] text-white/70'>Caption font:</label>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={carouselCaptionFontFamily}
|
value={carouselCaptionFontFamily}
|
||||||
@ -195,7 +195,7 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{carouselFullWidth && (
|
{carouselFullWidth && (
|
||||||
<p className='text-[10px] text-gray-500 mt-1'>
|
<p className='text-[10px] text-white/60 mt-1'>
|
||||||
In full-width mode: set icon + dimensions for navigation-style
|
In full-width mode: set icon + dimensions for navigation-style
|
||||||
buttons. Drag to reposition in editor.
|
buttons. Drag to reposition in editor.
|
||||||
</p>
|
</p>
|
||||||
@ -203,7 +203,7 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<p className='text-[11px] font-semibold text-gray-600'>
|
<p className='text-[11px] font-semibold text-white/80'>
|
||||||
Carousel slides
|
Carousel slides
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
@ -218,10 +218,10 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
{carouselSlides.map((slide, index) => (
|
{carouselSlides.map((slide, index) => (
|
||||||
<div
|
<div
|
||||||
key={slide.id}
|
key={slide.id}
|
||||||
className='rounded border border-gray-200 p-2 space-y-2'
|
className='rounded border border-white/20 p-2 space-y-2'
|
||||||
>
|
>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Slide {index + 1}
|
Slide {index + 1}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
@ -264,7 +264,7 @@ const CarouselSettingsSectionCompact: React.FC<
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{carouselSlides.length === 0 && (
|
{carouselSlides.length === 0 && (
|
||||||
<p className='text-[11px] text-gray-500'>
|
<p className='text-[11px] text-white/60'>
|
||||||
No slides yet. Click "+ Add slide" to create one.
|
No slides yet. Click "+ Add slide" to create one.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -16,25 +16,25 @@ const CommonSettingsSectionCompact: React.FC<CommonSettingsSectionProps> = ({
|
|||||||
showLabel = true,
|
showLabel = true,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='mb-2 space-y-2'>
|
<div className='mb-1.5 space-y-1.5'>
|
||||||
{showLabel && (
|
{showLabel && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-0.5 block text-[10px] font-semibold text-white/80'>
|
||||||
Label
|
Label
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-1.5 py-0.5 text-[11px]'
|
||||||
value={label}
|
value={label}
|
||||||
onChange={(event) => onChange('label', event.target.value)}
|
onChange={(event) => onChange('label', event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-0.5 block text-[10px] font-semibold text-white/80'>
|
||||||
Appear delay (sec)
|
Appear delay (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-1.5 py-0.5 text-[11px]'
|
||||||
type='number'
|
type='number'
|
||||||
min='0'
|
min='0'
|
||||||
step='0.1'
|
step='0.1'
|
||||||
@ -43,11 +43,11 @@ const CommonSettingsSectionCompact: React.FC<CommonSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-0.5 block text-[10px] font-semibold text-white/80'>
|
||||||
Appear duration (sec)
|
Appear duration (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-1.5 py-0.5 text-[11px]'
|
||||||
type='number'
|
type='number'
|
||||||
min='0.1'
|
min='0.1'
|
||||||
step='0.1'
|
step='0.1'
|
||||||
@ -57,7 +57,7 @@ const CommonSettingsSectionCompact: React.FC<CommonSettingsSectionProps> = ({
|
|||||||
onChange('appearDurationSec', event.target.value)
|
onChange('appearDurationSec', event.target.value)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<p className='mt-1 text-[11px] text-gray-500'>
|
<p className='mt-0.5 text-[10px] text-white/60'>
|
||||||
Leave empty for unlimited.
|
Leave empty for unlimited.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -42,7 +42,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
return (
|
return (
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Icon
|
Icon
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -64,7 +64,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Description title
|
Description title
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -75,7 +75,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Description text
|
Description text
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@ -87,7 +87,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Title font size (px)
|
Title font size (px)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -101,7 +101,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text font size (px)
|
Text font size (px)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -115,7 +115,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Title font family
|
Title font family
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -135,7 +135,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text font family
|
Text font family
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -155,7 +155,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Title color
|
Title color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -169,7 +169,7 @@ const DescriptionSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text color
|
Text color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -24,12 +24,12 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
{/* Appear Animation */}
|
{/* Appear Animation */}
|
||||||
<div>
|
<div>
|
||||||
<p className='mb-2 text-[11px] font-semibold text-gray-700'>
|
<p className='mb-2 text-[11px] font-semibold text-white/90'>
|
||||||
Appear Animation
|
Appear Animation
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>Type</label>
|
<label className='mb-1 block text-[10px] text-white/60'>Type</label>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values.appearAnimation || ''}
|
value={values.appearAnimation || ''}
|
||||||
@ -45,7 +45,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Duration (sec)
|
Duration (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -58,7 +58,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Easing
|
Easing
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -80,12 +80,12 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Hover Effects */}
|
{/* Hover Effects */}
|
||||||
<div>
|
<div>
|
||||||
<p className='mb-2 text-[11px] font-semibold text-gray-700'>
|
<p className='mb-2 text-[11px] font-semibold text-white/90'>
|
||||||
Hover Effects
|
Hover Effects
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Scale
|
Scale
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -96,7 +96,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Opacity
|
Opacity
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -107,7 +107,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
BG color
|
BG color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -118,7 +118,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Text color
|
Text color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -129,7 +129,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Box shadow
|
Box shadow
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -140,7 +140,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Transition (sec)
|
Transition (sec)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -157,12 +157,12 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Focus Effects */}
|
{/* Focus Effects */}
|
||||||
<div>
|
<div>
|
||||||
<p className='mb-2 text-[11px] font-semibold text-gray-700'>
|
<p className='mb-2 text-[11px] font-semibold text-white/90'>
|
||||||
Focus Effects
|
Focus Effects
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Scale
|
Scale
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -173,7 +173,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Opacity
|
Opacity
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -184,7 +184,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Outline
|
Outline
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -195,7 +195,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Box shadow
|
Box shadow
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -210,12 +210,12 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Active/Press Effects */}
|
{/* Active/Press Effects */}
|
||||||
<div>
|
<div>
|
||||||
<p className='mb-2 text-[11px] font-semibold text-gray-700'>
|
<p className='mb-2 text-[11px] font-semibold text-white/90'>
|
||||||
Active/Press Effects
|
Active/Press Effects
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Scale
|
Scale
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -226,7 +226,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Opacity
|
Opacity
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -237,7 +237,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='col-span-2'>
|
<div className='col-span-2'>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
BG color
|
BG color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -254,17 +254,17 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Slide Transition Override - Gallery/Carousel only */}
|
{/* Slide Transition Override - Gallery/Carousel only */}
|
||||||
{showSlideTransition && (
|
{showSlideTransition && (
|
||||||
<div className='mt-3 border-t border-gray-200 pt-3'>
|
<div className='mt-3 border-t border-white/20 pt-3'>
|
||||||
<p className='mb-1 text-[11px] font-semibold text-gray-700'>
|
<p className='mb-1 text-[11px] font-semibold text-white/90'>
|
||||||
Slide Transition
|
Slide Transition
|
||||||
</p>
|
</p>
|
||||||
<p className='mb-2 text-[10px] text-gray-500'>
|
<p className='mb-2 text-[10px] text-white/60'>
|
||||||
Override page transition for slides. Leave empty for defaults.
|
Override page transition for slides. Leave empty for defaults.
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
{/* Type */}
|
{/* Type */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Type
|
Type
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -282,7 +282,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Duration */}
|
{/* Duration */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Duration (ms)
|
Duration (ms)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -300,7 +300,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Easing */}
|
{/* Easing */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Easing
|
Easing
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -320,7 +320,7 @@ const EffectsSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Overlay Color */}
|
{/* Overlay Color */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-500'>
|
<label className='mb-1 block text-[10px] text-white/60'>
|
||||||
Overlay Color
|
Overlay Color
|
||||||
</label>
|
</label>
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
|
|||||||
@ -42,15 +42,15 @@ export const ElementSettingsTabsCompact: React.FC<ElementSettingsTabsProps> = ({
|
|||||||
tabs,
|
tabs,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='mb-3 inline-flex w-full overflow-hidden rounded border border-gray-300'>
|
<div className='mb-2 inline-flex w-full overflow-hidden rounded border border-white/30'>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
type='button'
|
type='button'
|
||||||
className={`flex-1 px-2 py-1.5 text-[11px] font-semibold transition-colors ${
|
className={`flex-1 px-1.5 py-1 text-[10px] font-semibold transition-colors ${
|
||||||
activeTab === tab.id
|
activeTab === tab.id
|
||||||
? 'bg-blue-600 text-white'
|
? 'bg-blue-600 text-white'
|
||||||
: 'bg-white text-gray-700 hover:bg-gray-50'
|
: 'bg-white/20 text-white/80 hover:bg-white/30'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onTabChange(tab.id)}
|
onClick={() => onTabChange(tab.id)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -54,13 +54,13 @@ const GalleryCarouselSettingsSectionCompact: React.FC<
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='mt-3 space-y-2'>
|
<div className='mt-3 space-y-2'>
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Carousel navigation
|
Carousel navigation
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Previous button */}
|
{/* Previous button */}
|
||||||
<p className='text-[10px] font-medium text-gray-600 mt-1'>Previous</p>
|
<p className='text-[10px] font-medium text-white/70 mt-1'>Previous</p>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={prevIconUrl}
|
value={prevIconUrl}
|
||||||
@ -111,7 +111,7 @@ const GalleryCarouselSettingsSectionCompact: React.FC<
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Next button */}
|
{/* Next button */}
|
||||||
<p className='text-[10px] font-medium text-gray-600 mt-1'>Next</p>
|
<p className='text-[10px] font-medium text-white/70 mt-1'>Next</p>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={nextIconUrl}
|
value={nextIconUrl}
|
||||||
@ -162,7 +162,7 @@ const GalleryCarouselSettingsSectionCompact: React.FC<
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Back button */}
|
{/* Back button */}
|
||||||
<p className='text-[10px] font-medium text-gray-600 mt-1'>Back</p>
|
<p className='text-[10px] font-medium text-white/70 mt-1'>Back</p>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={backIconUrl}
|
value={backIconUrl}
|
||||||
@ -221,7 +221,7 @@ const GalleryCarouselSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className='text-[10px] text-gray-500 mt-1'>
|
<p className='text-[10px] text-white/60 mt-1'>
|
||||||
Set icon + dimensions for navigation-style buttons. Drag to
|
Set icon + dimensions for navigation-style buttons. Drag to
|
||||||
reposition.
|
reposition.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -41,13 +41,13 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
showTextAlign = false,
|
showTextAlign = false,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>{sectionLabel}</p>
|
<p className='text-[11px] font-semibold text-white/90'>{sectionLabel}</p>
|
||||||
|
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
{/* Background Color */}
|
{/* Background Color */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
BG color
|
BG color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -63,7 +63,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Text Color (not for wrapper) */}
|
{/* Text Color (not for wrapper) */}
|
||||||
{prefix !== 'galleryWrapper' && !showTitleStyles && (
|
{prefix !== 'galleryWrapper' && !showTitleStyles && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Text color
|
Text color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -77,7 +77,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
|
|
||||||
{/* Padding */}
|
{/* Padding */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Padding
|
Padding
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -90,7 +90,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
|
|
||||||
{/* Border Radius */}
|
{/* Border Radius */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>Radius</label>
|
<label className='mb-1 block text-[10px] text-white/70'>Radius</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values[`${prefix}BorderRadius`] || ''}
|
value={values[`${prefix}BorderRadius`] || ''}
|
||||||
@ -101,7 +101,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
|
|
||||||
{/* Border */}
|
{/* Border */}
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>Border</label>
|
<label className='mb-1 block text-[10px] text-white/70'>Border</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values[`${prefix}Border`] || ''}
|
value={values[`${prefix}Border`] || ''}
|
||||||
@ -113,7 +113,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Text Alignment (optional) */}
|
{/* Text Alignment (optional) */}
|
||||||
{showTextAlign && (
|
{showTextAlign && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Align
|
Align
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -131,7 +131,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Gap (optional) */}
|
{/* Gap (optional) */}
|
||||||
{showGap && (
|
{showGap && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>Gap</label>
|
<label className='mb-1 block text-[10px] text-white/70'>Gap</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values[`${prefix}Gap`] || ''}
|
value={values[`${prefix}Gap`] || ''}
|
||||||
@ -144,7 +144,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Backdrop Blur (wrapper only) */}
|
{/* Backdrop Blur (wrapper only) */}
|
||||||
{showBlur && (
|
{showBlur && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>Blur</label>
|
<label className='mb-1 block text-[10px] text-white/70'>Blur</label>
|
||||||
<input
|
<input
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values[`${prefix}BackdropBlur`] || ''}
|
value={values[`${prefix}BackdropBlur`] || ''}
|
||||||
@ -159,7 +159,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Grid Columns (optional) */}
|
{/* Grid Columns (optional) */}
|
||||||
{showColumns && (
|
{showColumns && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Columns
|
Columns
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -179,7 +179,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Font Size (optional) */}
|
{/* Font Size (optional) */}
|
||||||
{showFont && (
|
{showFont && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Font size
|
Font size
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -194,7 +194,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Font Weight (optional) */}
|
{/* Font Weight (optional) */}
|
||||||
{showFont && (
|
{showFont && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Font weight
|
Font weight
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -210,7 +210,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Font Family (optional - full width) */}
|
{/* Font Family (optional - full width) */}
|
||||||
{showFont && (
|
{showFont && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>Font</label>
|
<label className='mb-1 block text-[10px] text-white/70'>Font</label>
|
||||||
<select
|
<select
|
||||||
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
|
||||||
value={values[`${prefix}FontFamily`] || ''}
|
value={values[`${prefix}FontFamily`] || ''}
|
||||||
@ -229,10 +229,10 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Header Dimensions (header section only) */}
|
{/* Header Dimensions (header section only) */}
|
||||||
{showDimensions && (
|
{showDimensions && (
|
||||||
<>
|
<>
|
||||||
<p className='text-[10px] text-gray-500 pt-1'>Dimensions:</p>
|
<p className='text-[10px] text-white/60 pt-1'>Dimensions:</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Width
|
Width
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -243,7 +243,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Height
|
Height
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -254,7 +254,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Min Height
|
Min Height
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -265,7 +265,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Max Height
|
Max Height
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -282,10 +282,10 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Card Aspect Ratio and Min Height (cards only) */}
|
{/* Card Aspect Ratio and Min Height (cards only) */}
|
||||||
{showAspectRatio && (
|
{showAspectRatio && (
|
||||||
<>
|
<>
|
||||||
<p className='text-[10px] text-gray-500 pt-1'>Card dimensions:</p>
|
<p className='text-[10px] text-white/60 pt-1'>Card dimensions:</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Aspect Ratio
|
Aspect Ratio
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -303,7 +303,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Min Height
|
Min Height
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -320,10 +320,10 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
{/* Card Title Styles (cards only) */}
|
{/* Card Title Styles (cards only) */}
|
||||||
{showTitleStyles && (
|
{showTitleStyles && (
|
||||||
<>
|
<>
|
||||||
<p className='text-[10px] text-gray-500 pt-1'>Card title overlay:</p>
|
<p className='text-[10px] text-white/60 pt-1'>Card title overlay:</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Title color
|
Title color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -336,7 +336,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Title BG
|
Title BG
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -349,7 +349,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Title size
|
Title size
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -362,7 +362,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Title weight
|
Title weight
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -376,7 +376,7 @@ const GallerySectionStyleInputs: React.FC<GallerySectionStyleInputsProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[10px] text-gray-600'>
|
<label className='mb-1 block text-[10px] text-white/70'>
|
||||||
Title shadow
|
Title shadow
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -60,8 +60,8 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
return (
|
return (
|
||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
{/* Header Settings */}
|
{/* Header Settings */}
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Gallery header
|
Gallery header
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -104,9 +104,9 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Info Spans */}
|
{/* Info Spans */}
|
||||||
<div className='rounded border border-gray-200 p-2 space-y-2'>
|
<div className='rounded border border-white/20 p-2 space-y-2'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>Info spans</p>
|
<p className='text-[11px] font-semibold text-white/90'>Info spans</p>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className='text-xs text-blue-700 hover:underline'
|
className='text-xs text-blue-700 hover:underline'
|
||||||
@ -157,7 +157,7 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{galleryInfoSpans.length === 0 && (
|
{galleryInfoSpans.length === 0 && (
|
||||||
<p className='text-[10px] text-gray-500'>
|
<p className='text-[10px] text-white/60'>
|
||||||
Add spans for brief notes (capacity, price, icons, etc.)
|
Add spans for brief notes (capacity, price, icons, etc.)
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@ -165,7 +165,7 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Gallery Cards */}
|
{/* Gallery Cards */}
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<p className='text-[11px] font-semibold text-gray-600'>Gallery cards</p>
|
<p className='text-[11px] font-semibold text-white/80'>Gallery cards</p>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className='text-xs text-blue-700 hover:underline'
|
className='text-xs text-blue-700 hover:underline'
|
||||||
@ -178,10 +178,10 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
{galleryCards.map((card, index) => (
|
{galleryCards.map((card, index) => (
|
||||||
<div
|
<div
|
||||||
key={card.id}
|
key={card.id}
|
||||||
className='rounded border border-gray-200 p-2 space-y-2'
|
className='rounded border border-white/20 p-2 space-y-2'
|
||||||
>
|
>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<p className='text-[11px] font-semibold text-gray-700'>
|
<p className='text-[11px] font-semibold text-white/90'>
|
||||||
Card {index + 1}
|
Card {index + 1}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
@ -234,7 +234,7 @@ const GallerySettingsSectionCompact: React.FC<
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
{galleryCards.length === 0 && (
|
{galleryCards.length === 0 && (
|
||||||
<p className='text-[11px] text-gray-500'>
|
<p className='text-[11px] text-white/60'>
|
||||||
No cards yet. Click "+ Add card" to create one.
|
No cards yet. Click "+ Add card" to create one.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const MediaSettingsSectionCompact: React.FC<
|
|||||||
return (
|
return (
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
{assetLabel}
|
{assetLabel}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -60,7 +60,7 @@ const MediaSettingsSectionCompact: React.FC<
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
checked={mediaAutoplay}
|
checked={mediaAutoplay}
|
||||||
@ -69,7 +69,7 @@ const MediaSettingsSectionCompact: React.FC<
|
|||||||
Autoplay
|
Autoplay
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
checked={mediaLoop}
|
checked={mediaLoop}
|
||||||
@ -79,7 +79,7 @@ const MediaSettingsSectionCompact: React.FC<
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
{mediaType === 'video' && (
|
{mediaType === 'video' && (
|
||||||
<label className='flex items-center gap-2 text-[11px] text-gray-700'>
|
<label className='flex items-center gap-2 text-[11px] text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
checked={mediaMuted}
|
checked={mediaMuted}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
return (
|
return (
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Type
|
Type
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -140,7 +140,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Button text
|
Button text
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -151,7 +151,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Label font
|
Label font
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -171,7 +171,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='flex items-center gap-2 text-[11px] font-semibold text-gray-600'>
|
<label className='flex items-center gap-2 text-[11px] font-semibold text-white/80'>
|
||||||
<input
|
<input
|
||||||
type='checkbox'
|
type='checkbox'
|
||||||
checked={navDisabled}
|
checked={navDisabled}
|
||||||
@ -182,7 +182,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Icon
|
Icon
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -202,7 +202,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{selectedMediaDurationNote && (
|
{selectedMediaDurationNote && (
|
||||||
<p className='mt-1 text-[11px] text-gray-500'>
|
<p className='mt-1 text-[11px] text-white/60'>
|
||||||
{selectedMediaDurationNote}
|
{selectedMediaDurationNote}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@ -210,7 +210,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{/* Back navigation info text */}
|
{/* Back navigation info text */}
|
||||||
{currentKind === 'back' && (
|
{currentKind === 'back' && (
|
||||||
<p className='text-[11px] italic text-gray-500'>
|
<p className='text-[11px] italic text-white/60'>
|
||||||
Back button returns to the previous page using the original forward
|
Back button returns to the previous page using the original forward
|
||||||
transition in reverse.
|
transition in reverse.
|
||||||
</p>
|
</p>
|
||||||
@ -220,7 +220,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
{currentKind === 'forward' && (
|
{currentKind === 'forward' && (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Target page
|
Target page
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -243,7 +243,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Transition video asset
|
Transition video asset
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -265,14 +265,14 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{selectedTransitionDurationNote && (
|
{selectedTransitionDurationNote && (
|
||||||
<p className='mt-1 text-[11px] text-gray-500'>
|
<p className='mt-1 text-[11px] text-white/60'>
|
||||||
{selectedTransitionDurationNote}
|
{selectedTransitionDurationNote}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Back transition mode
|
Back transition mode
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -298,7 +298,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
|
|
||||||
{transitionReverseMode === 'separate_video' && (
|
{transitionReverseMode === 'separate_video' && (
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Back transition video asset
|
Back transition video asset
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -325,11 +325,11 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
{/* CSS Transition Settings (when no video selected) */}
|
{/* CSS Transition Settings (when no video selected) */}
|
||||||
{!transitionVideoUrl && (
|
{!transitionVideoUrl && (
|
||||||
<>
|
<>
|
||||||
<p className='mt-2 text-[11px] italic text-gray-500'>
|
<p className='mt-2 text-[11px] italic text-white/60'>
|
||||||
No transition video selected. Configure CSS transition instead:
|
No transition video selected. Configure CSS transition instead:
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Transition type
|
Transition type
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -347,7 +347,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Duration (ms)
|
Duration (ms)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -366,7 +366,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Easing
|
Easing
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -384,7 +384,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Overlay color
|
Overlay color
|
||||||
</label>
|
</label>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
@ -411,7 +411,7 @@ const NavigationSettingsSectionCompact: React.FC<
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{transitionVideoUrl && (
|
{transitionVideoUrl && (
|
||||||
<p className='text-[11px] text-gray-500'>
|
<p className='text-[11px] text-white/60'>
|
||||||
Transition duration is set automatically from the selected video.
|
Transition duration is set automatically from the selected video.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -14,12 +14,12 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<p className='text-[10px] text-gray-500'>
|
<p className='text-[10px] text-white/60'>
|
||||||
Dimensions = % of canvas, border/radius = px
|
Dimensions = % of canvas, border/radius = px
|
||||||
</p>
|
</p>
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Width (%)
|
Width (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -33,7 +33,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Height (%)
|
Height (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -47,7 +47,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Min W (%)
|
Min W (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -60,7 +60,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Max W (%)
|
Max W (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -73,7 +73,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Min H (%)
|
Min H (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -86,7 +86,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Max H (%)
|
Max H (%)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -99,7 +99,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Margin
|
Margin
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -110,7 +110,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Padding
|
Padding
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -121,7 +121,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Gap (rem)
|
Gap (rem)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -135,7 +135,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Font size
|
Font size
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -146,7 +146,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Line height
|
Line height
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -156,7 +156,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Font weight
|
Font weight
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -167,7 +167,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Border (px)
|
Border (px)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -181,7 +181,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Radius (px)
|
Radius (px)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -195,7 +195,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Opacity
|
Opacity
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -206,7 +206,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
z-index
|
z-index
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -219,7 +219,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Box shadow
|
Box shadow
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -232,7 +232,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
|
|
||||||
<div className='grid grid-cols-2 gap-2'>
|
<div className='grid grid-cols-2 gap-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Display
|
Display
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -250,7 +250,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Position
|
Position
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -267,7 +267,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Justify
|
Justify
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -285,7 +285,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Align
|
Align
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -302,7 +302,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text align
|
Text align
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -318,7 +318,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
BG color
|
BG color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -329,7 +329,7 @@ const StyleSettingsSectionCompact: React.FC<StyleSettingsSectionProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text color
|
Text color
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const TooltipSettingsSectionCompact: React.FC<
|
|||||||
return (
|
return (
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Icon
|
Icon
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -56,7 +56,7 @@ const TooltipSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Tooltip title
|
Tooltip title
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -67,7 +67,7 @@ const TooltipSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Tooltip text
|
Tooltip text
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@ -79,7 +79,7 @@ const TooltipSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Title font family
|
Title font family
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@ -99,7 +99,7 @@ const TooltipSettingsSectionCompact: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
|
<label className='mb-1 block text-[11px] font-semibold text-white/80'>
|
||||||
Text font family
|
Text font family
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@ -280,7 +280,7 @@ export function useConstructorPageActions({
|
|||||||
background_audio_url: '',
|
background_audio_url: '',
|
||||||
background_loop: false,
|
background_loop: false,
|
||||||
requires_auth: false,
|
requires_auth: false,
|
||||||
ui_schema_json: JSON.stringify({ elements: [] }),
|
ui_schema_json: { elements: [] },
|
||||||
// Copy project design dimensions to new page
|
// Copy project design dimensions to new page
|
||||||
design_width: project?.design_width ?? null,
|
design_width: project?.design_width ?? null,
|
||||||
design_height: project?.design_height ?? null,
|
design_height: project?.design_height ?? null,
|
||||||
|
|||||||
@ -210,8 +210,8 @@ export const createDefaultElement = (
|
|||||||
id: createLocalId(),
|
id: createLocalId(),
|
||||||
type,
|
type,
|
||||||
label: ELEMENT_TYPE_LABELS[type] || type,
|
label: ELEMENT_TYPE_LABELS[type] || type,
|
||||||
xPercent: clamp(12 + index * 4, 5, 80),
|
xPercent: clamp(45 + index * 3, 10, 85), // Center horizontally
|
||||||
yPercent: clamp(16 + index * 6, 8, 85),
|
yPercent: clamp(45 + index * 4, 15, 80), // Center vertically
|
||||||
appearDelaySec: 0,
|
appearDelaySec: 0,
|
||||||
appearDurationSec: null,
|
appearDurationSec: null,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,15 +20,30 @@ export const parseJsonObject = <T>(value?: unknown, fallback?: T): T => {
|
|||||||
if (!value) return (fallback || ({} as T)) as T;
|
if (!value) return (fallback || ({} as T)) as T;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (typeof value === 'string') {
|
let result: unknown = value;
|
||||||
const parsed = JSON.parse(value);
|
|
||||||
return (parsed || fallback || {}) as T;
|
// Handle string input - parse JSON
|
||||||
|
if (typeof result === 'string') {
|
||||||
|
result = JSON.parse(result);
|
||||||
|
|
||||||
|
// Handle double-encoded JSON (string that parses to another string)
|
||||||
|
// This can happen if JSON was stringified twice
|
||||||
|
while (typeof result === 'string') {
|
||||||
|
try {
|
||||||
|
result = JSON.parse(result);
|
||||||
|
} catch {
|
||||||
|
// Not valid JSON, use fallback
|
||||||
|
return (fallback || ({} as T)) as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value === 'object') {
|
// Ensure we return an object, not a primitive
|
||||||
return value as T;
|
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
||||||
|
return result as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it's an array or primitive, return fallback
|
||||||
return (fallback || ({} as T)) as T;
|
return (fallback || ({} as T)) as T;
|
||||||
} catch {
|
} catch {
|
||||||
return (fallback || ({} as T)) as T;
|
return (fallback || ({} as T)) as T;
|
||||||
|
|||||||
@ -14,8 +14,7 @@ import { flushSync } from 'react-dom';
|
|||||||
import BaseButton from '../components/BaseButton';
|
import BaseButton from '../components/BaseButton';
|
||||||
import CanvasBackground from '../components/Constructor/CanvasBackground';
|
import CanvasBackground from '../components/Constructor/CanvasBackground';
|
||||||
import TransitionBlackOverlay from '../components/TransitionBlackOverlay';
|
import TransitionBlackOverlay from '../components/TransitionBlackOverlay';
|
||||||
import ConstructorControlsPanel from '../components/Constructor/ConstructorControlsPanel';
|
import ConstructorToolbar from '../components/Constructor/ConstructorToolbar';
|
||||||
import ConstructorMenu from '../components/Constructor/ConstructorMenu';
|
|
||||||
import TransitionPreviewOverlay from '../components/Constructor/TransitionPreviewOverlay';
|
import TransitionPreviewOverlay from '../components/Constructor/TransitionPreviewOverlay';
|
||||||
import CanvasElementComponent from '../components/Constructor/CanvasElement';
|
import CanvasElementComponent from '../components/Constructor/CanvasElement';
|
||||||
import GalleryCarouselOverlay from '../components/UiElements/GalleryCarouselOverlay';
|
import GalleryCarouselOverlay from '../components/UiElements/GalleryCarouselOverlay';
|
||||||
@ -133,7 +132,7 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
);
|
);
|
||||||
const canvasRef = useRef<HTMLDivElement>(null);
|
const canvasRef = useRef<HTMLDivElement>(null);
|
||||||
const elementEditorRef = useRef<HTMLDivElement>(null);
|
const elementEditorRef = useRef<HTMLDivElement>(null);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const toolbarRef = useRef<HTMLDivElement>(null);
|
||||||
const [isAuthReady, setIsAuthReady] = useState(false);
|
const [isAuthReady, setIsAuthReady] = useState(false);
|
||||||
const isElementEditMode = mode === 'element_edit';
|
const isElementEditMode = mode === 'element_edit';
|
||||||
|
|
||||||
@ -278,7 +277,8 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
|
|
||||||
const [constructorInteractionMode, setConstructorInteractionMode] =
|
const [constructorInteractionMode, setConstructorInteractionMode] =
|
||||||
useState<ConstructorInteractionMode>('edit');
|
useState<ConstructorInteractionMode>('edit');
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
// Note: isMenuOpen kept for context backward compatibility, not used by ConstructorToolbar
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
||||||
const [isEditorCollapsed, setIsEditorCollapsed] = useState(false);
|
const [isEditorCollapsed, setIsEditorCollapsed] = useState(false);
|
||||||
const [elementEditorTab, setElementEditorTab] = useState<
|
const [elementEditorTab, setElementEditorTab] = useState<
|
||||||
'general' | 'css' | 'effects'
|
'general' | 'css' | 'effects'
|
||||||
@ -353,27 +353,17 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
}, [activeGalleryCarousel, elements]);
|
}, [activeGalleryCarousel, elements]);
|
||||||
|
|
||||||
// Draggable panels using useDraggable hook
|
// Draggable panels using useDraggable hook
|
||||||
const {
|
const { position: toolbarPosition, onDragStart: onToolbarDragStart } =
|
||||||
position: constructorControlsPosition,
|
useDraggable({
|
||||||
onDragStart: onConstructorControlsDragStart,
|
initialPosition: { x: 20, y: 20 },
|
||||||
} = useDraggable({
|
elementWidth: 200, // Use collapsed width for bounds - expanded can go off-screen
|
||||||
initialPosition: { x: 20, y: 20 },
|
elementHeight: 56,
|
||||||
elementWidth: 460,
|
});
|
||||||
elementHeight: 64,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { position: menuPosition, onDragStart: onMenuDragStart } = useDraggable(
|
|
||||||
{
|
|
||||||
initialPosition: { x: 9999, y: 10 }, // Top right corner (x will be clamped)
|
|
||||||
elementWidth: 240,
|
|
||||||
elementHeight: 60,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const { position: editorPosition, onDragStart: onElementEditorDragStart } =
|
const { position: editorPosition, onDragStart: onElementEditorDragStart } =
|
||||||
useDraggable({
|
useDraggable({
|
||||||
initialPosition: { x: 0, y: 72 },
|
initialPosition: { x: 9999, y: 0 }, // Top-right corner (x will be clamped)
|
||||||
elementWidth: isEditorCollapsed ? 260 : 380,
|
elementWidth: isEditorCollapsed ? 220 : 300,
|
||||||
elementHeight: 60,
|
elementHeight: 60,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1141,7 +1131,7 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
// Ignore clicks on menu to allow menu item selection
|
// Ignore clicks on menu to allow menu item selection
|
||||||
useOutsideClick({
|
useOutsideClick({
|
||||||
containerRef: elementEditorRef,
|
containerRef: elementEditorRef,
|
||||||
ignoreRefs: [menuRef],
|
ignoreRefs: [toolbarRef],
|
||||||
ignoreDataAttribute: 'data-constructor-element-id',
|
ignoreDataAttribute: 'data-constructor-element-id',
|
||||||
selectedValue: selectedElementId,
|
selectedValue: selectedElementId,
|
||||||
onOutsideClick: useCallback(() => {
|
onOutsideClick: useCallback(() => {
|
||||||
@ -1648,18 +1638,37 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{pages.length > 0 && !isElementEditMode && (
|
{pages.length > 0 && !isElementEditMode && (
|
||||||
<ConstructorControlsPanel
|
<ConstructorToolbar
|
||||||
projectId={projectId}
|
ref={toolbarRef}
|
||||||
|
position={toolbarPosition}
|
||||||
|
onDragStart={onToolbarDragStart}
|
||||||
pages={pages}
|
pages={pages}
|
||||||
activePageId={activePageId}
|
activePageId={activePageId}
|
||||||
interactionMode={constructorInteractionMode}
|
|
||||||
position={constructorControlsPosition}
|
|
||||||
onPageChange={(pageId) => {
|
onPageChange={(pageId) => {
|
||||||
const page = pages.find((p) => p.id === pageId);
|
const page = pages.find((p) => p.id === pageId);
|
||||||
if (page) switchToPage(page);
|
if (page) switchToPage(page);
|
||||||
}}
|
}}
|
||||||
|
interactionMode={constructorInteractionMode}
|
||||||
onModeChange={setConstructorInteractionMode}
|
onModeChange={setConstructorInteractionMode}
|
||||||
onDragStart={onConstructorControlsDragStart}
|
onSelectMenuItem={selectMenuItemForEdit}
|
||||||
|
allowedNavigationTypes={allowedNavigationTypes}
|
||||||
|
onAddElement={addElement}
|
||||||
|
onCreatePage={createPage}
|
||||||
|
isCreatingPage={isCreatingPage}
|
||||||
|
onSave={saveConstructor}
|
||||||
|
onSaveToStage={handleSaveToStage}
|
||||||
|
isSaving={isSaving}
|
||||||
|
isSavingToStage={isSavingToStage}
|
||||||
|
lastSavedAt={lastProjectSaveAt}
|
||||||
|
lastSavedToStageAt={lastSavedToStage}
|
||||||
|
projectId={projectId}
|
||||||
|
onExit={() =>
|
||||||
|
router.push(
|
||||||
|
projectId
|
||||||
|
? `/projects/${projectId}`
|
||||||
|
: '/projects/projects-list',
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -1801,34 +1810,6 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
|||||||
title={editorTitle}
|
title={editorTitle}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{pages.length > 0 && !isElementEditMode && (
|
|
||||||
<ConstructorMenu
|
|
||||||
ref={menuRef}
|
|
||||||
position={menuPosition}
|
|
||||||
isOpen={isMenuOpen}
|
|
||||||
allowedNavigationTypes={allowedNavigationTypes}
|
|
||||||
isCreatingPage={isCreatingPage}
|
|
||||||
isSaving={isSaving}
|
|
||||||
isSavingToStage={isSavingToStage}
|
|
||||||
onDragStart={onMenuDragStart}
|
|
||||||
onToggleOpen={() => setIsMenuOpen((prev) => !prev)}
|
|
||||||
onSelectMenuItem={selectMenuItemForEdit}
|
|
||||||
onAddElement={addElement}
|
|
||||||
onCreatePage={createPage}
|
|
||||||
onSave={saveConstructor}
|
|
||||||
onSaveToStage={handleSaveToStage}
|
|
||||||
onExit={() =>
|
|
||||||
router.push(
|
|
||||||
projectId
|
|
||||||
? `/projects/${projectId}`
|
|
||||||
: '/projects/projects-list',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
lastSavedAt={lastProjectSaveAt}
|
|
||||||
lastSavedToStageAt={lastSavedToStage}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TransitionPreviewOverlay
|
<TransitionPreviewOverlay
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user