import { Box } from '@luke-ui/react/box'; import { createIcon } from '@luke-ui/react/icon'; import { vars } from '@luke-ui/react/theme'; import type { ComponentProps, ReactNode } from 'react'; import { useEffect, useId, useRef, useState } from 'react'; import type { GroupImperativeHandle } from 'react-resizable-panels'; import { Group, Panel, Separator } from 'react-resizable-panels'; import { StoryWrapper } from '../lib/story-wrapper.js'; import * as styles from './example-preview.css.js'; import { useIsDesktop } from './playground/use-is-desktop.js'; export function ExamplePreview({ children, layout, title, }: { children: ReactNode; layout?: ComponentProps['layout']; title: string; }) { const isDesktop = useIsDesktop(); const groupElement = useRef(null); const groupHandle = useRef(null); const [isCardWideEnough, setIsCardWideEnough] = useState(false); const previewId = useId(); const outsideId = useId(); const isResizable = isDesktop && isCardWideEnough; useEffect(() => { const element = groupElement.current; if (!element) return; const observer = new ResizeObserver(([entry]) => { if (!entry) return; setIsCardWideEnough(entry.contentRect.width >= styles.MIN_RESIZABLE_CARD_WIDTH); }); observer.observe(element); return () => observer.disconnect(); }, []); useEffect(() => { if (!isResizable) groupHandle.current?.setLayout({ [previewId]: 100, [outsideId]: 0, }); }, [isResizable, outsideId, previewId]); return ( {/* CSS sets the gutter before measurement. Examples query this canvas's width. */} {children} { groupHandle.current?.setLayout({ [previewId]: 100, [outsideId]: 0, }); }} > ); } const MIN_PREVIEW_WIDTH = 320; // Match sp24 and sp12 in example-preview.css.ts. const RESIZE_GUTTER_WIDTH = 24; const OUTSIDE_STRIP_WIDTH = 12; const EXAMPLE_RESIZE_TARGET_MINIMUM_SIZE = { coarse: 32, fine: 32 }; const GripIcon = createIcon({ path: ( <> ), }); const GRIP_BLOCK_SIZE = '3.75rem'; const GRIP_INLINE_SIZE = '0.75rem'; function ExamplePreviewResizeGrip() { return ( ); }