/* Copyright 2026 Marimo. All rights reserved. */ import { EyeOffIcon, LayoutTemplateIcon, type LucideIcon, Rows2Icon, CookieIcon, PanelRightCloseIcon, PanelRightOpenIcon, } from "lucide-react"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import type { CellId } from "@/core/cells/ids"; import { cn } from "@/utils/cn"; import type { DeckTransition, SlidesLayout, SlideType, } from "../editor/renderers/slides-layout/types"; import { useState } from "react"; import { Tooltip } from "../ui/tooltip"; import { Button } from "../ui/button"; import type { RuntimeCell } from "@/core/cells/types"; export const DEFAULT_SLIDE_TYPE: SlideType = "slide"; export const DEFAULT_DECK_TRANSITION: DeckTransition = "slide"; const COLLAPSED_CONFIG_WIDTH = 36; export interface SlideTypeOption { value: SlideType; label: string; description: string; Icon: LucideIcon; } export const SLIDE_TYPE_OPTIONS: readonly SlideTypeOption[] = [ { value: "slide", label: "Slide", description: "A new top-level slide. Advances horizontally with the right arrow.", Icon: LayoutTemplateIcon, }, { value: "sub-slide", label: "Sub-slide", description: "Stacks vertically under the previous slide. Reached with the down arrow.", Icon: Rows2Icon, }, { value: "fragment", label: "Fragment", description: "Reveals step-by-step on the current slide without advancing.", Icon: CookieIcon, }, { value: "skip", label: "Skip", description: "Hidden from the presentation. Still visible here in the editor.", Icon: EyeOffIcon, }, ]; /** * Lookup form of {@link SLIDE_TYPE_OPTIONS} for O(1) access by `SlideType`. */ export const SLIDE_TYPE_OPTIONS_BY_VALUE: Readonly< Record > = Object.fromEntries( SLIDE_TYPE_OPTIONS.map((option) => [option.value, option]), ) as Record; interface DeckTransitionOption { value: DeckTransition; label: string; description: string; } const DECK_TRANSITION_OPTIONS: DeckTransitionOption[] = [ { value: "none", label: "None", description: "No animation between slides." }, { value: "fade", label: "Fade", description: "Cross-fade between slides." }, { value: "slide", label: "Slide", description: "Slides move horizontally / vertically.", }, { value: "convex", label: "Convex", description: "Rotate with a convex curve.", }, { value: "concave", label: "Concave", description: "Rotate with a concave curve.", }, { value: "zoom", label: "Zoom", description: "Zoom into the next slide." }, ]; const SlidesForm = ({ layout, setLayout, cellId, }: { layout: SlidesLayout; setLayout: (layout: SlidesLayout) => void; cellId: CellId; }) => { return ( Slide Deck ); }; const SlideConfigForm = ({ layout, setLayout, cellId, }: { layout: SlidesLayout; setLayout: (layout: SlidesLayout) => void; cellId: CellId; }) => { const currentSlideType: SlideType = layout.cells.get(cellId)?.type ?? DEFAULT_SLIDE_TYPE; const handleSlideTypeChange = (value: SlideType) => { const existingConfig = layout.cells.get(cellId); const newCells = new Map(layout.cells); newCells.set(cellId, { ...existingConfig, type: value }); setLayout({ ...layout, cells: newCells, }); }; return (
Slide type handleSlideTypeChange(value as SlideType)} className="flex flex-col gap-1.5" > {SLIDE_TYPE_OPTIONS.map(({ value, label, description, Icon }) => { const isSelected = currentSlideType === value; return (

{label}

{description}

); })}
); }; const DeckConfigForm = ({ layout, setLayout, }: { layout: SlidesLayout; setLayout: (layout: SlidesLayout) => void; }) => { const currentTransition: DeckTransition = layout.deck?.transition ?? DEFAULT_DECK_TRANSITION; const activeDescription = DECK_TRANSITION_OPTIONS.find( (opt) => opt.value === currentTransition, )?.description; const handleTransitionChange = (value: DeckTransition) => { setLayout({ ...layout, deck: { ...layout.deck, transition: value }, }); }; return (
{activeDescription && (

{activeDescription}

)}
); }; export const SlideSidebar = ({ configWidth, layout, setLayout, activeConfigCell, }: { configWidth: number; layout: SlidesLayout; setLayout: (layout: SlidesLayout) => void; activeConfigCell?: RuntimeCell; }) => { const [isConfigOpen, setIsConfigOpen] = useState(false); return ( ); };