From 1861d8415cbab2dad71394f1586a788fb0605193 Mon Sep 17 00:00:00 2001 From: ajnart Date: Sat, 22 Aug 2026 17:00:06 +0200 Subject: [PATCH] fix: isolate widget editor forms --- .../src/modals/widget-edit-modal.module.css | 26 +- .../widgets/src/modals/widget-edit-modal.tsx | 317 +++++++++--------- 2 files changed, 180 insertions(+), 163 deletions(-) diff --git a/packages/widgets/src/modals/widget-edit-modal.module.css b/packages/widgets/src/modals/widget-edit-modal.module.css index 87dafc094..25afd728d 100644 --- a/packages/widgets/src/modals/widget-edit-modal.module.css +++ b/packages/widgets/src/modals/widget-edit-modal.module.css @@ -5,8 +5,8 @@ align-items: start; } -.workspace[data-with-preview] { - grid-template-columns: minmax(0, 3fr) minmax(34rem, 2fr); +.workspace[data-with-preview][data-with-settings] { + grid-template-columns: minmax(0, 3fr) auto minmax(34rem, 2fr); } .previewPanel { @@ -28,7 +28,8 @@ .previewCanvas { position: relative; - align-self: center; + width: 100%; + height: clamp(24rem, calc(100dvh - 17rem), 44rem); padding: var(--mantine-spacing-lg); overflow: hidden; border: 1px solid var(--mantine-color-default-border); @@ -40,6 +41,11 @@ background-size: 2rem 2rem; } +.previewDivider { + height: auto; + align-self: stretch; +} + .previewViewport { position: relative; flex: 0 0 auto; @@ -78,6 +84,10 @@ scrollbar-gutter: stable; } +.settingsForm { + min-width: 0; +} + .fullWidthField { grid-column: 1 / -1; } @@ -142,7 +152,7 @@ } @media (max-width: 68em) { - .workspace[data-with-preview] { + .workspace[data-with-preview][data-with-settings] { grid-template-columns: 1fr; } @@ -151,6 +161,14 @@ max-width: 44rem; } + .previewDivider { + display: none; + } + + .previewCanvas { + height: 24rem; + } + .settingsPanel { max-height: none; overflow: visible; diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx index 67ec03ef9..8922f0d6a 100644 --- a/packages/widgets/src/modals/widget-edit-modal.tsx +++ b/packages/widgets/src/modals/widget-edit-modal.tsx @@ -2,9 +2,9 @@ /* eslint-disable react/no-unstable-nested-components -- Widget modules and definition-bound fallbacks are loaded dynamically. */ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useId, useRef, useState } from "react"; import type { ComponentType, FormEvent } from "react"; -import { Alert, Badge, Box, Button, Card, Center, Group, SimpleGrid, Stack, Tabs, Text } from "@mantine/core"; +import { Alert, Badge, Box, Button, Card, Center, Divider, Group, SimpleGrid, Stack, Tabs, Text } from "@mantine/core"; import { schemaResolver } from "@mantine/form"; import { useElementSize } from "@mantine/hooks"; import { IconArrowLeft, IconEye, IconPencil, IconSettings } from "@tabler/icons-react"; @@ -83,9 +83,6 @@ interface WidgetEditPreviewProps { onChangeOptions: (newOptions: Record) => void; } -const PREVIEW_STAGE_INSET = 40; -const MAXIMUM_PREVIEW_HEIGHT = 544; - const WidgetEditPreview = ({ kind, Component, @@ -98,15 +95,15 @@ const WidgetEditPreview = ({ onChangeOptions, }: WidgetEditPreviewProps) => { const tItem = useI18n("item.edit"); - const { ref, width: availableWidth } = useElementSize(); + const { ref, width: availableWidth, height: availableHeight } = useElementSize(); const sourceWidth = Math.max(dimensions.width, 1); const sourceHeight = Math.max(dimensions.height, 1); let maximumScale = dimensions.scale ?? 0.9; if (!Number.isFinite(maximumScale) || maximumScale <= 0) maximumScale = 0.9; maximumScale = Math.min(maximumScale, 0.9); - let previewScale = Math.min(MAXIMUM_PREVIEW_HEIGHT / sourceHeight, maximumScale); - if (availableWidth > PREVIEW_STAGE_INSET) { - previewScale = Math.min((availableWidth - PREVIEW_STAGE_INSET) / sourceWidth, previewScale); + let previewScale = maximumScale; + if (availableWidth > 0 && availableHeight > 0) { + previewScale = Math.min(availableWidth / sourceWidth, availableHeight / sourceHeight, maximumScale); } const previewWidth = sourceWidth * previewScale; const previewHeight = sourceHeight * previewScale; @@ -114,12 +111,6 @@ const WidgetEditPreview = ({ const hasIntegrationSupport = "supportedIntegrations" in definition; const integrationRequired = hasIntegrationSupport && definition.integrationsRequired !== false; const isMissingIntegration = integrationRequired && state.integrationIds.length === 0; - let previewStageWidth = previewWidth + PREVIEW_STAGE_INSET; - let previewStageHeight = previewHeight + PREVIEW_STAGE_INSET; - if (isMissingIntegration) { - previewStageWidth = Math.max(previewStageWidth, 360); - previewStageHeight = Math.max(previewStageHeight, 160); - } const previewIntegrations = integrationData.map((integration) => ({ id: integration.id, permissions: integration.permissions ?? { @@ -130,11 +121,8 @@ const WidgetEditPreview = ({ })); return ( - -
+ +
{Math.round(sourceWidth)} × {Math.round(sourceHeight)} @@ -220,6 +208,7 @@ export const WidgetEditModal = createModal>(({ const [mountedIntegrationIds, setMountedIntegrationIds] = useState([]); const [activeIntegrationId, setActiveIntegrationId] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); + const widgetFormId = useId(); z.config({ customError: zodErrorMap(t), @@ -390,9 +379,14 @@ export const WidgetEditModal = createModal>(({ }); const switchOptions = visibleOptions.filter(([, value]) => value.type === "switch"); const otherOptions = visibleOptions.filter(([, value]) => value.type !== "switch"); + const hasWidgetSettings = canConfigureWidget && (innerProps.integrationSupport || visibleOptions.length > 0); const widgetFormContent = ( - + {innerProps.previewComponent && ( >(({ onChangeOptions={handlePreviewOptionsChange} /> )} - - - - - {tItem("settings.label")} - - - {canConfigureWidget && innerProps.integrationSupport && ( - - 1} - withAsterisk={integrationsRequired} - onOpenNewIntegration={canCreateIntegration ? handleOpenNewIntegration : undefined} - {...form.getInputProps("integrationIds")} - /> - - )} - {canConfigureWidget && ( - - {switchOptions.length > 0 && ( - - {switchOptions.map(([key, value]) => { + {innerProps.previewComponent && hasWidgetSettings && ( + + )} + ); @@ -494,7 +495,7 @@ export const WidgetEditModal = createModal>(({ - @@ -503,100 +504,98 @@ export const WidgetEditModal = createModal>(({ ); return ( -
- - - {showResourceTabs ? ( - - - {tItem("tab.widget")} - {showAppTab && {tItem("tab.app")}} - {showIntegrationTab && {tItem("tab.integration")}} - - - {widgetFormContent} + + + {showResourceTabs ? ( + + + {tItem("tab.widget")} + {showAppTab && {tItem("tab.app")}} + {showIntegrationTab && {tItem("tab.integration")}} + + + {widgetFormContent} + + {showAppTab && ( + + {appId && } - {showAppTab && ( - - {appId && } - - )} - {showIntegrationTab && ( - - - {displayedIntegrationId === null && ( - <> - - {tItem("integration.description")} - - {selectedIntegrations.map((integration) => { - const canEdit = editableIntegrationIds.includes(integration.id); - return ( - - - - - - {integration.name} - - - {integration.url} - - - - {canEdit ? ( - - ) : ( - - {tItem("integration.fullAccessRequired")} + )} + {showIntegrationTab && ( + + + {displayedIntegrationId === null && ( + <> + + {tItem("integration.description")} + + {selectedIntegrations.map((integration) => { + const canEdit = editableIntegrationIds.includes(integration.id); + return ( + + + + + + {integration.name} - )} + + {integration.url} + + - ); - })} - - )} - {displayedIntegrationId !== null && selectedIntegrations.length > 1 && ( - - )} - {integrationIdsToRender.map((integrationId) => ( - - ))} - - - )} - - ) : ( - widgetFormContent - )} - {footer} - - - + {canEdit ? ( + + ) : ( + + {tItem("integration.fullAccessRequired")} + + )} + + ); + })} + + )} + {displayedIntegrationId !== null && selectedIntegrations.length > 1 && ( + + )} + {integrationIdsToRender.map((integrationId) => ( + + ))} + + + )} + + ) : ( + widgetFormContent + )} + {footer} + +
); }).withOptions({ keepMounted: true,