diff --git a/client/src/app/App.tsx b/client/src/app/App.tsx index 8da56dc..3c4d614 100644 --- a/client/src/app/App.tsx +++ b/client/src/app/App.tsx @@ -293,7 +293,7 @@ export const App = () => { width: widget.width, height: widget.height, }); - return ``; + return ``; })(); await navigator.clipboard?.writeText(code); }; @@ -383,6 +383,11 @@ export const App = () => { locale={locale} onBack={() => navigate('/dashboard')} onOpenPublic={(slug) => navigate(`/w/${slug}`)} + onSave={(saved) => + setVisibleWidgets((current) => + current.map((widget) => (widget.id === saved.id ? toCardData(saved) : widget)), + ) + } /> ); diff --git a/client/src/entities/widget/ui/WidgetCard.module.css b/client/src/entities/widget/ui/WidgetCard.module.css index 5a1f662..df3c2c5 100644 --- a/client/src/entities/widget/ui/WidgetCard.module.css +++ b/client/src/entities/widget/ui/WidgetCard.module.css @@ -267,7 +267,7 @@ gap: 0.5rem; } -.actions button:last-child { +.actions > button:last-child { grid-column: 2 / 3; grid-row: 2 / 3; } @@ -307,12 +307,11 @@ } .copyMenu { - display: contents; + grid-column: 2 / 3; + grid-row: 1 / 2; } .copyAction { - grid-column: 2 / 3; - grid-row: 1 / 2; } @media (max-width: 760px) { @@ -328,7 +327,7 @@ grid-column: 1 / 2; } - .copyAction { + .copyMenu { grid-column: 1 / 2; grid-row: 1 / 2; } diff --git a/client/src/entities/widget/ui/WidgetCard.tsx b/client/src/entities/widget/ui/WidgetCard.tsx index cf00dfc..11eb6f4 100644 --- a/client/src/entities/widget/ui/WidgetCard.tsx +++ b/client/src/entities/widget/ui/WidgetCard.tsx @@ -47,9 +47,15 @@ const PreviewSkeleton = () => ( const WidgetPreviewFrame = ({ widget }: { widget: WidgetCardData }) => { const viewportRef = useRef(null); const iframeRef = useRef(null); - const [loadedSlug, setLoadedSlug] = useState(null); - const [scale, setScale] = useState(1); - const isLoaded = loadedSlug === widget.slug; + const [loaded, setLoaded] = useState<{ slug: string; width: number; height: number } | null>( + null, + ); + const [viewportSize, setViewportSize] = useState({ width: 0, height: 0 }); + const isLoaded = + loaded !== null && + loaded.slug === widget.slug && + loaded.width === widget.width && + loaded.height === widget.height; useEffect(() => { const handleMessage = (event: MessageEvent) => { @@ -75,18 +81,18 @@ const WidgetPreviewFrame = ({ widget }: { widget: WidgetCardData }) => { return; } - setLoadedSlug(widget.slug); + setLoaded({ slug: widget.slug, width: widget.width, height: widget.height }); }; window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); - }, [widget.slug]); + }, [widget.height, widget.slug, widget.width]); useEffect(() => { const viewport = viewportRef.current; if (!viewport) return; - const updateScale = () => { + const measure = () => { const { paddingLeft, paddingRight, paddingTop, paddingBottom } = window.getComputedStyle(viewport); const width = @@ -94,14 +100,19 @@ const WidgetPreviewFrame = ({ widget }: { widget: WidgetCardData }) => { const height = viewport.clientHeight - Number.parseFloat(paddingTop) - Number.parseFloat(paddingBottom); if (!width || !height) return; - setScale(Math.min(1, width / widget.width, height / widget.height)); + setViewportSize({ width, height }); }; - updateScale(); - const observer = new ResizeObserver(updateScale); + measure(); + const observer = new ResizeObserver(measure); observer.observe(viewport); return () => observer.disconnect(); - }, [widget.height, widget.width]); + }, []); + + const scale = + viewportSize.width && viewportSize.height + ? Math.min(1, viewportSize.width / widget.width, viewportSize.height / widget.height) + : 1; return (
void onCopy(widget, 'svg'), }, ]} + popupProps={{ placement: 'bottom-end' }} switcherWrapperClassName={styles.copyMenu} renderSwitcher={({ onClick, onKeyDown }) => (