From 977d07330b97ce451fb834447807b9d4163fc6bd Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Sun, 28 Jun 2026 15:07:33 +1000 Subject: [PATCH] fix: auto select field on drop (#3028) --- .../envelope-editor-fields-page-renderer.tsx | 89 ++++++++++++++----- .../envelope-fields.spec.ts | 1 + 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page-renderer.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page-renderer.tsx index db8f6ffbb..5cbd4addf 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page-renderer.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page-renderer.tsx @@ -49,6 +49,13 @@ export const EnvelopeEditorFieldsPageRenderer = ({ pageData }: { pageData: PageR const [isFieldChanging, setIsFieldChanging] = useState(false); const [pendingFieldCreation, setPendingFieldCreation] = useState(null); + /** + * Whether the field was automatically selected on creation (drag-drop or marquee). + * + * We purposefully supress the floating toolbar for newly created fields. + */ + const [isAutoSelectedField, setIsAutoSelectedField] = useState(false); + const { stage, pageLayer, konvaContainer, scaledViewport, unscaledViewport } = usePageRenderer( ({ stage, pageLayer }) => createPageCanvas(stage, pageLayer), pageData, @@ -521,13 +528,48 @@ export const EnvelopeEditorFieldsPageRenderer = ({ pageData }: { pageData: PageR setSelectedFields(liveSelectedFieldGroups); } + // Mirror the editor's single selected field onto the canvas (Konva) selection. + // + // `addField` already marks a newly created field as the selected field, so this + // makes a field placed via the palette (drag-drop) or marquee creation show its + // resize handles immediately -- no second click needed. It also clears the canvas + // selection when the selected field is cleared (e.g. when the author starts + // placing another field), so the floating action toolbar can't intercept the next + // placement click. Runs after the render loop above so the field's group exists. + const selectedFormId = editorFields.selectedField?.formId ?? null; + const isSingleCanvasSelection = selectedKonvaFieldGroups.length === 1; + + if (selectedFormId && localPageFields.some((field) => field.formId === selectedFormId)) { + const isAlreadySelected = isSingleCanvasSelection && selectedKonvaFieldGroups[0].id() === selectedFormId; + + if (!isAlreadySelected) { + const fieldGroupToSelect = pageLayer.current.findOne(`#${selectedFormId}`); + + if (fieldGroupToSelect instanceof Konva.Group) { + setSelectedFields([fieldGroupToSelect], { isAutoSelect: true }); + } + } + } else if (selectedFormId === null && isSingleCanvasSelection) { + setSelectedFields([]); + } + // Rerender the transformer interactiveTransformer.current?.forceUpdate(); pageLayer.current.batchDraw(); - }, [localPageFields, selectedKonvaFieldGroups, overlappingFieldFormIds, isFieldChanging]); + }, [ + localPageFields, + selectedKonvaFieldGroups, + overlappingFieldFormIds, + isFieldChanging, + editorFields.selectedField?.formId, + ]); + + const setSelectedFields = (nodes: Konva.Node[], options?: { isAutoSelect?: boolean }) => { + // Any explicit (user-driven) selection shows the action toolbar; only auto-selection + // on field creation suppresses it. + setIsAutoSelectedField(Boolean(options?.isAutoSelect)); - const setSelectedFields = (nodes: Konva.Node[]) => { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const fieldGroups = nodes.filter( (node) => node.hasName('field-group') && Boolean(node.getStage()) && Boolean(node.getParent()), @@ -663,25 +705,30 @@ export const EnvelopeEditorFieldsPageRenderer = ({ pageData }: { pageData: PageR return ( <> - {selectedKonvaFieldGroups.length > 0 && interactiveTransformer.current && !isFieldChanging && ( - field.id())} - style={{ - position: 'absolute', - top: interactiveTransformer.current.y() + interactiveTransformer.current.getClientRect().height + 5 + 'px', - left: interactiveTransformer.current.x() + interactiveTransformer.current.getClientRect().width / 2 + 'px', - transform: 'translateX(-50%)', - gap: '8px', - pointerEvents: 'auto', - zIndex: 50, - }} - /> - )} + {selectedKonvaFieldGroups.length > 0 && + interactiveTransformer.current && + !isFieldChanging && + !isAutoSelectedField && ( + field.id())} + style={{ + position: 'absolute', + top: + interactiveTransformer.current.y() + interactiveTransformer.current.getClientRect().height + 5 + 'px', + left: + interactiveTransformer.current.x() + interactiveTransformer.current.getClientRect().width / 2 + 'px', + transform: 'translateX(-50%)', + gap: '8px', + pointerEvents: 'auto', + zIndex: 50, + }} + /> + )} {pendingFieldCreation && (