diff --git a/apps/remix/app/components/general/document/document-attachments-popover.tsx b/apps/remix/app/components/general/document/document-attachments-popover.tsx index 4fd3f385c..b65ea3ea0 100644 --- a/apps/remix/app/components/general/document/document-attachments-popover.tsx +++ b/apps/remix/app/components/general/document/document-attachments-popover.tsx @@ -21,6 +21,12 @@ export type DocumentAttachmentsPopoverProps = { envelopeId: string; buttonClassName?: string; buttonSize?: 'sm' | 'default'; + + /** + * Visually hide the button label below the `sm` breakpoint while keeping it + * available to screen readers. + */ + collapseLabelOnMobile?: boolean; }; const ZAttachmentFormSchema = z.object({ @@ -35,6 +41,7 @@ export const DocumentAttachmentsPopover = ({ envelopeId, buttonClassName, buttonSize, + collapseLabelOnMobile = false, }: DocumentAttachmentsPopoverProps) => { const { toast } = useToast(); const { _ } = useLingui(); @@ -129,7 +136,7 @@ export const DocumentAttachmentsPopover = ({ - editorFields.setSelectedRecipient(recipient.id)} - recipients={envelope.recipients} - fields={envelope.fields} - className="w-full" - align="end" + {isMobileFieldsPanelOpen && ( +
setIsMobileFieldsPanelOpen(false)} /> + )} - {editorFields.selectedRecipient && - !canRecipientFieldsBeModified(editorFields.selectedRecipient, envelope.fields) && ( - - - - This recipient can no longer be modified as they have signed a field, or completed the document. - - - - )} - - - - - {/* Add fields section. */} -
-

- Add Fields -

- - - - {editorConfig.fields?.allowAIDetection && ( - <> - - - - - - + {/* + * Below `md` the panel is a slide-in overlay, from `md` upwards it is a + * static sidebar. + * + * This is deliberately one always-mounted element toggled with CSS rather + * than a Sheet/Drawer: the field palette registers window-level mouse + * listeners that must survive the panel closing mid click-to-place, and + * unmounting would also double-mount the field settings forms and AI + * dialogs. `invisible` (with the transition covering visibility) keeps the + * closed overlay out of the tab order and accessibility tree. + */} +
+ > + {/* Panel header with close button, visible below the `md` breakpoint. */} +
+

+ Fields +

- {/* Field details section. */} - - {selectedField && ( -
- + +
- {searchParams.get('devmode') && ( - <> -
-

- Developer Mode -

+ {/* Recipient selector section. */} +
+

+ Selected Recipient +

-
- {selectedField.id && ( + editorFields.setSelectedRecipient(recipient.id)} + recipients={envelope.recipients} + fields={envelope.fields} + className="w-full" + align="end" + /> + + {editorFields.selectedRecipient && + !canRecipientFieldsBeModified(editorFields.selectedRecipient, envelope.fields) && ( + + + + This recipient can no longer be modified as they have signed a field, or completed the document. + + + + )} +
+ + + + {/* Add fields section. */} +
+

+ Add Fields +

+ + setIsMobileFieldsPanelOpen(false)} + /> + + {editorConfig.fields?.allowAIDetection && ( + <> + + + + + + + )} +
+ + {/* Field details section. */} + + {selectedField && ( +
+ + + {searchParams.get('devmode') && ( + <> +
+

+ Developer Mode +

+ +
+ {selectedField.id && ( +

+ + Field ID: + {' '} + {selectedField.id} +

+ )}

- Field ID: + Recipient ID: {' '} - {selectedField.id} + {selectedField.recipientId}

- )} -

- - Recipient ID: - {' '} - {selectedField.recipientId} -

-

- - Pos X: - {' '} - {selectedField.positionX.toFixed(2)} -

-

- - Pos Y: - {' '} - {selectedField.positionY.toFixed(2)} -

-

- - Width: - {' '} - {selectedField.width.toFixed(2)} -

-

- - Height: - {' '} - {selectedField.height.toFixed(2)} -

+

+ + Pos X: + {' '} + {selectedField.positionX.toFixed(2)} +

+

+ + Pos Y: + {' '} + {selectedField.positionY.toFixed(2)} +

+

+ + Width: + {' '} + {selectedField.width.toFixed(2)} +

+

+ + Height: + {' '} + {selectedField.height.toFixed(2)} +

+
-
- - - )} + + + )} -
-

{_(FieldSettingsTypeTranslations[selectedField.type])}

+
+

{_(FieldSettingsTypeTranslations[selectedField.type])}

- {match(selectedField.type) - .with(FieldType.SIGNATURE, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.CHECKBOX, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.DATE, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.DROPDOWN, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.EMAIL, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.INITIALS, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.NAME, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.NUMBER, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.RADIO, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .with(FieldType.TEXT, () => ( - updateSelectedFieldMeta(value)} - /> - )) - .otherwise(() => null)} -
-
- )} - -
+ {match(selectedField.type) + .with(FieldType.SIGNATURE, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.CHECKBOX, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.DATE, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.DROPDOWN, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.EMAIL, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.INITIALS, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.NAME, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.NUMBER, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.RADIO, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .with(FieldType.TEXT, () => ( + updateSelectedFieldMeta(value)} + /> + )) + .otherwise(() => null)} + + + )} + + + )} ); diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-header.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-header.tsx index 5e35ca895..7f678713c 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-header.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-header.tsx @@ -70,17 +70,18 @@ export default function EnvelopeEditorHeader() { return (