diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx index 2c2c4516d..a6ae5c317 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx @@ -237,6 +237,16 @@ export const EnvelopeEditorFieldDragDrop = ({ [selectedRecipientId, getRecipientColorKey], ); + /** + * Begin placing a field type. Also clears the current field selection so the + * previously placed field's action toolbar can't intercept the next placement + * click on the canvas (the renderer mirrors this onto the canvas selection). + */ + const handleStartPlacingField = (type: FieldType) => { + setSelectedField(type); + editorFields.setSelectedField(null); + }; + return ( <>
@@ -245,8 +255,8 @@ export const EnvelopeEditorFieldDragDrop = ({ disabled={isFieldsDisabled} key={field.type} type="button" - onClick={() => setSelectedField(field.type)} - onMouseDown={() => setSelectedField(field.type)} + onClick={() => handleStartPlacingField(field.type)} + onMouseDown={() => handleStartPlacingField(field.type)} data-selected={selectedField === field.type ? true : undefined} className={cn( 'group flex h-12 cursor-pointer items-center justify-center rounded-lg border border-border px-4 transition-colors', 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..23d97ca8d 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 @@ -521,11 +521,42 @@ 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]); + } + } + } 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[]) => { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions