From 2984af769cc12ae695b433683a035fb4e38787ea Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Tue, 28 Jan 2025 12:29:38 +1100 Subject: [PATCH] feat: add text align option to fields (#1610) ## Description Adds the ability to align text to the left, center or right for relevant fields. Previously text was always centered which can be less desirable. See attached debug document which has left, center and right text alignments set for fields. image ## Related Issue N/A ## Changes Made - Added text align option - Update the insert in pdf method to support different alignments - Added a debug mode to field insertion ## Testing Performed - Ran manual tests using the debug mode --- .../app/(signing)/sign/[token]/date-field.tsx | 23 +++++++-- .../(signing)/sign/[token]/email-field.tsx | 23 +++++++-- .../app/(signing)/sign/[token]/name-field.tsx | 23 +++++++-- .../(signing)/sign/[token]/number-field.tsx | 45 +++++++++++------- .../app/(signing)/sign/[token]/text-field.tsx | 29 +++++++++--- .../server-only/pdf/insert-field-in-pdf.ts | 47 ++++++++++++++++++- packages/lib/types/field-meta.ts | 10 ++++ .../field-item-advanced-settings.tsx | 6 +++ .../date-field.tsx | 28 +++++++++++ .../email-field.tsx | 28 +++++++++++ .../initials-field.tsx | 23 +++++++++ .../name-field.tsx | 28 +++++++++++ .../number-field.tsx | 25 +++++++++- .../text-field.tsx | 30 +++++++++++- 14 files changed, 332 insertions(+), 36 deletions(-) diff --git a/apps/web/src/app/(signing)/sign/[token]/date-field.tsx b/apps/web/src/app/(signing)/sign/[token]/date-field.tsx index b62eaf652..9d03ee690 100644 --- a/apps/web/src/app/(signing)/sign/[token]/date-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/date-field.tsx @@ -16,6 +16,7 @@ import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones' import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; +import { ZDateFieldMeta } from '@documenso/lib/types/field-meta'; import type { Recipient } from '@documenso/prisma/client'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import { trpc } from '@documenso/trpc/react'; @@ -23,6 +24,7 @@ import type { TRemovedSignedFieldWithTokenMutationSchema, TSignFieldWithTokenMutationSchema, } from '@documenso/trpc/server/field-router/schema'; +import { cn } from '@documenso/ui/lib/utils'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { SigningFieldContainer } from './signing-field-container'; @@ -59,6 +61,9 @@ export const DateField = ({ isPending: isRemoveSignedFieldWithTokenLoading, } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); + const safeFieldMeta = ZDateFieldMeta.safeParse(field.fieldMeta); + const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null; + const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; const localDateString = convertToLocalSystemFormat(field.customText, dateFormat, timezone); @@ -150,9 +155,21 @@ export const DateField = ({ )} {field.inserted && ( -

- {localDateString} -

+
+

+ {localDateString} +

+
)} ); diff --git a/apps/web/src/app/(signing)/sign/[token]/email-field.tsx b/apps/web/src/app/(signing)/sign/[token]/email-field.tsx index 9300aef63..f3d664e23 100644 --- a/apps/web/src/app/(signing)/sign/[token]/email-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/email-field.tsx @@ -11,6 +11,7 @@ import { Loader } from 'lucide-react'; import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; +import { ZEmailFieldMeta } from '@documenso/lib/types/field-meta'; import type { Recipient } from '@documenso/prisma/client'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import { trpc } from '@documenso/trpc/react'; @@ -18,6 +19,7 @@ import type { TRemovedSignedFieldWithTokenMutationSchema, TSignFieldWithTokenMutationSchema, } from '@documenso/trpc/server/field-router/schema'; +import { cn } from '@documenso/ui/lib/utils'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { useRequiredSigningContext } from './provider'; @@ -48,6 +50,9 @@ export const EmailField = ({ field, recipient, onSignField, onUnsignField }: Ema isPending: isRemoveSignedFieldWithTokenLoading, } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); + const safeFieldMeta = ZEmailFieldMeta.safeParse(field.fieldMeta); + const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null; + const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; const onSign = async (authOptions?: TRecipientActionAuth) => { @@ -128,9 +133,21 @@ export const EmailField = ({ field, recipient, onSignField, onUnsignField }: Ema )} {field.inserted && ( -

- {field.customText} -

+
+

+ {field.customText} +

+
)} ); diff --git a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx index bc83e5a49..1a0756d60 100644 --- a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx @@ -11,6 +11,7 @@ import { Loader } from 'lucide-react'; import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth'; +import { ZNameFieldMeta } from '@documenso/lib/types/field-meta'; import { type Recipient } from '@documenso/prisma/client'; import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature'; import { trpc } from '@documenso/trpc/react'; @@ -18,6 +19,7 @@ import type { TRemovedSignedFieldWithTokenMutationSchema, TSignFieldWithTokenMutationSchema, } from '@documenso/trpc/server/field-router/schema'; +import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog'; import { Input } from '@documenso/ui/primitives/input'; @@ -56,6 +58,9 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name isPending: isRemoveSignedFieldWithTokenLoading, } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); + const safeFieldMeta = ZNameFieldMeta.safeParse(field.fieldMeta); + const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null; + const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; const [showFullNameModal, setShowFullNameModal] = useState(false); @@ -172,9 +177,21 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name )} {field.inserted && ( -

- {field.customText} -

+
+

+ {field.customText} +

+
)} diff --git a/apps/web/src/app/(signing)/sign/[token]/number-field.tsx b/apps/web/src/app/(signing)/sign/[token]/number-field.tsx index ffd90df64..07846468c 100644 --- a/apps/web/src/app/(signing)/sign/[token]/number-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/number-field.tsx @@ -52,8 +52,19 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu const [isPending, startTransition] = useTransition(); const [showRadioModal, setShowRadioModal] = useState(false); - const parsedFieldMeta = field.fieldMeta ? ZNumberFieldMeta.parse(field.fieldMeta) : null; - const isReadOnly = parsedFieldMeta?.readOnly; + const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } = + trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); + + const { + mutateAsync: removeSignedFieldWithToken, + isPending: isRemoveSignedFieldWithTokenLoading, + } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); + + const safeFieldMeta = ZNumberFieldMeta.safeParse(field.fieldMeta); + const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null; + + const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; + const defaultValue = parsedFieldMeta?.value; const [localNumber, setLocalNumber] = useState( parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '0', @@ -71,16 +82,6 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu const { executeActionAuthProcedure } = useRequiredDocumentAuthContext(); - const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } = - trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); - - const { - mutateAsync: removeSignedFieldWithToken, - isPending: isRemoveSignedFieldWithTokenLoading, - } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); - - const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; - const handleNumberChange = (e: React.ChangeEvent) => { const text = e.target.value; setLocalNumber(text); @@ -208,7 +209,7 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu useEffect(() => { if ( (!field.inserted && defaultValue && localNumber) || - (!field.inserted && isReadOnly && defaultValue) + (!field.inserted && parsedFieldMeta?.readOnly && defaultValue) ) { void executeActionAuthProcedure({ onReauthFormSubmit: async (authOptions) => await onSign(authOptions), @@ -260,9 +261,21 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu )} {field.inserted && ( -

- {field.customText} -

+
+

+ {field.customText} +

+
)} diff --git a/apps/web/src/app/(signing)/sign/[token]/text-field.tsx b/apps/web/src/app/(signing)/sign/[token]/text-field.tsx index 0c4088d75..3f2229e0c 100644 --- a/apps/web/src/app/(signing)/sign/[token]/text-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/text-field.tsx @@ -62,7 +62,8 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text isPending: isRemoveSignedFieldWithTokenLoading, } = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION); - const parsedFieldMeta = field.fieldMeta ? ZTextFieldMeta.parse(field.fieldMeta) : null; + const safeFieldMeta = ZTextFieldMeta.safeParse(field.fieldMeta); + const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null; const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending; const shouldAutoSignField = @@ -261,11 +262,23 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text )} {field.inserted && ( -

- {field.customText.length < 20 - ? field.customText - : field.customText.substring(0, 15) + '...'} -

+
+

+ {field.customText.length < 20 + ? field.customText + : field.customText.substring(0, 15) + '...'} +

+
)} @@ -281,6 +294,10 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text className={cn('mt-2 w-full rounded-md', { 'border-2 border-red-300 ring-2 ring-red-200 ring-offset-2 ring-offset-red-200 focus-visible:border-red-400 focus-visible:ring-4 focus-visible:ring-red-200 focus-visible:ring-offset-2 focus-visible:ring-offset-red-200': userInputHasErrors, + 'text-left': parsedFieldMeta?.textAlign === 'left', + 'text-center': + !parsedFieldMeta?.textAlign || parsedFieldMeta?.textAlign === 'center', + 'text-right': parsedFieldMeta?.textAlign === 'right', })} value={localText} onChange={handleTextChange} diff --git a/packages/lib/server-only/pdf/insert-field-in-pdf.ts b/packages/lib/server-only/pdf/insert-field-in-pdf.ts index af760c7f4..dc2308978 100644 --- a/packages/lib/server-only/pdf/insert-field-in-pdf.ts +++ b/packages/lib/server-only/pdf/insert-field-in-pdf.ts @@ -1,7 +1,7 @@ // https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821 import fontkit from '@pdf-lib/fontkit'; import type { PDFDocument } from 'pdf-lib'; -import { RotationTypes, degrees, radiansToDegrees } from 'pdf-lib'; +import { RotationTypes, degrees, radiansToDegrees, rgb } from 'pdf-lib'; import { P, match } from 'ts-pattern'; import { @@ -36,6 +36,9 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu ); const isSignatureField = isSignatureFieldType(field.type); + const isDebugMode = + // eslint-disable-next-line turbo/no-undeclared-env-vars + process.env.DEBUG_PDF_INSERT === '1' || process.env.DEBUG_PDF_INSERT === 'true'; pdf.registerFontkit(fontkit); @@ -83,6 +86,35 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu const fieldX = pageWidth * (Number(field.positionX) / 100); const fieldY = pageHeight * (Number(field.positionY) / 100); + // Draw debug box if debug mode is enabled + if (isDebugMode) { + let debugX = fieldX; + let debugY = pageHeight - fieldY - fieldHeight; // Invert Y for PDF coordinates + + if (pageRotationInDegrees !== 0) { + const adjustedPosition = adjustPositionForRotation( + pageWidth, + pageHeight, + debugX, + debugY, + pageRotationInDegrees, + ); + + debugX = adjustedPosition.xPos; + debugY = adjustedPosition.yPos; + } + + page.drawRectangle({ + x: debugX, + y: debugY, + width: fieldWidth, + height: fieldHeight, + borderColor: rgb(1, 0, 0), // Red + borderWidth: 1, + rotate: degrees(pageRotationInDegrees), + }); + } + const font = await pdf.embedFont( isSignatureField ? fontCaveat : fontNoto, isSignatureField ? { features: { calt: false } } : undefined, @@ -278,6 +310,7 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu const meta = Parser ? Parser.safeParse(field.fieldMeta) : null; const customFontSize = meta?.success && meta.data.fontSize ? meta.data.fontSize : null; + const textAlign = meta?.success && meta.data.textAlign ? meta.data.textAlign : 'center'; const longestLineInTextForWidth = field.customText .split('\n') .sort((a, b) => b.length - a.length)[0]; @@ -293,7 +326,17 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize); - let textX = fieldX + (fieldWidth - textWidth) / 2; + // Add padding similar to web display (roughly 0.5rem equivalent in PDF units) + const padding = 8; // PDF points, roughly equivalent to 0.5rem + + // Calculate X position based on text alignment with padding + let textX = fieldX + padding; // Left alignment starts after padding + if (textAlign === 'center') { + textX = fieldX + (fieldWidth - textWidth) / 2; // Center alignment ignores padding + } else if (textAlign === 'right') { + textX = fieldX + fieldWidth - textWidth - padding; // Right alignment respects right padding + } + let textY = fieldY + (fieldHeight - textHeight) / 2; // Invert the Y axis since PDFs use a bottom-left coordinate system diff --git a/packages/lib/types/field-meta.ts b/packages/lib/types/field-meta.ts index f4e4da8f3..674cccb4b 100644 --- a/packages/lib/types/field-meta.ts +++ b/packages/lib/types/field-meta.ts @@ -11,9 +11,14 @@ export const ZBaseFieldMeta = z.object({ export type TBaseFieldMeta = z.infer; +export const ZFieldTextAlignSchema = z.enum(['left', 'center', 'right']); + +export type TFieldTextAlignSchema = z.infer; + export const ZInitialsFieldMeta = ZBaseFieldMeta.extend({ type: z.literal('initials'), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TInitialsFieldMeta = z.infer; @@ -21,6 +26,7 @@ export type TInitialsFieldMeta = z.infer; export const ZNameFieldMeta = ZBaseFieldMeta.extend({ type: z.literal('name'), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TNameFieldMeta = z.infer; @@ -28,6 +34,7 @@ export type TNameFieldMeta = z.infer; export const ZEmailFieldMeta = ZBaseFieldMeta.extend({ type: z.literal('email'), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TEmailFieldMeta = z.infer; @@ -35,6 +42,7 @@ export type TEmailFieldMeta = z.infer; export const ZDateFieldMeta = ZBaseFieldMeta.extend({ type: z.literal('date'), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TDateFieldMeta = z.infer; @@ -44,6 +52,7 @@ export const ZTextFieldMeta = ZBaseFieldMeta.extend({ text: z.string().optional(), characterLimit: z.number().optional(), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TTextFieldMeta = z.infer; @@ -55,6 +64,7 @@ export const ZNumberFieldMeta = ZBaseFieldMeta.extend({ minValue: z.number().optional(), maxValue: z.number().optional(), fontSize: z.number().min(8).max(96).optional(), + textAlign: ZFieldTextAlignSchema.optional(), }); export type TNumberFieldMeta = z.infer; diff --git a/packages/ui/primitives/document-flow/field-item-advanced-settings.tsx b/packages/ui/primitives/document-flow/field-item-advanced-settings.tsx index a9123486b..e30763a7f 100644 --- a/packages/ui/primitives/document-flow/field-item-advanced-settings.tsx +++ b/packages/ui/primitives/document-flow/field-item-advanced-settings.tsx @@ -71,21 +71,25 @@ const getDefaultState = (fieldType: FieldType): FieldMeta => { return { type: 'initials', fontSize: 14, + textAlign: 'left', }; case FieldType.NAME: return { type: 'name', fontSize: 14, + textAlign: 'left', }; case FieldType.EMAIL: return { type: 'email', fontSize: 14, + textAlign: 'left', }; case FieldType.DATE: return { type: 'date', fontSize: 14, + textAlign: 'left', }; case FieldType.TEXT: return { @@ -97,6 +101,7 @@ const getDefaultState = (fieldType: FieldType): FieldMeta => { fontSize: 14, required: false, readOnly: false, + textAlign: 'left', }; case FieldType.NUMBER: return { @@ -110,6 +115,7 @@ const getDefaultState = (fieldType: FieldType): FieldMeta => { required: false, readOnly: false, fontSize: 14, + textAlign: 'left', }; case FieldType.RADIO: return { diff --git a/packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx b/packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx index c3108b20b..99fbba491 100644 --- a/packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx +++ b/packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx @@ -5,6 +5,13 @@ import { validateFields as validateDateFields } from '@documenso/lib/advanced-fi import { type TDateFieldMeta as DateFieldMeta } from '@documenso/lib/types/field-meta'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@documenso/ui/primitives/select'; type DateFieldAdvancedSettingsProps = { fieldState: DateFieldMeta; @@ -66,6 +73,27 @@ export const DateFieldAdvancedSettings = ({ max={96} /> + +
+ + + +
); }; diff --git a/packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx b/packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx index 0b6c644eb..92ddafd3c 100644 --- a/packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx +++ b/packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx @@ -5,6 +5,13 @@ import { validateFields as validateEmailFields } from '@documenso/lib/advanced-f import { type TEmailFieldMeta as EmailFieldMeta } from '@documenso/lib/types/field-meta'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@documenso/ui/primitives/select'; type EmailFieldAdvancedSettingsProps = { fieldState: EmailFieldMeta; @@ -48,6 +55,27 @@ export const EmailFieldAdvancedSettings = ({ max={96} /> + +
+ + + +
); }; diff --git a/packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx b/packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx index b117d0913..472d0c4ff 100644 --- a/packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx +++ b/packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx @@ -6,6 +6,8 @@ import { type TInitialsFieldMeta as InitialsFieldMeta } from '@documenso/lib/typ import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../select'; + type InitialsFieldAdvancedSettingsProps = { fieldState: InitialsFieldMeta; handleFieldChange: (key: keyof InitialsFieldMeta, value: string | boolean) => void; @@ -48,6 +50,27 @@ export const InitialsFieldAdvancedSettings = ({ max={96} /> + +
+ + + +
); }; diff --git a/packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx b/packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx index d6159e0d5..e9b10e13c 100644 --- a/packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx +++ b/packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx @@ -5,6 +5,13 @@ import { validateFields as validateNameFields } from '@documenso/lib/advanced-fi import { type TNameFieldMeta as NameFieldMeta } from '@documenso/lib/types/field-meta'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@documenso/ui/primitives/select'; type NameFieldAdvancedSettingsProps = { fieldState: NameFieldMeta; @@ -48,6 +55,27 @@ export const NameFieldAdvancedSettings = ({ max={96} /> + +
+ + + +
); }; diff --git a/packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx b/packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx index cf193c6e3..60d1cf538 100644 --- a/packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx +++ b/packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx @@ -38,12 +38,12 @@ export const NumberFieldAdvancedSettings = ({ const [showValidation, setShowValidation] = useState(false); const handleInput = (field: keyof NumberFieldMeta, value: string | boolean) => { - const userValue = field === 'value' ? value : fieldState.value ?? 0; + const userValue = field === 'value' ? value : (fieldState.value ?? 0); const userMinValue = field === 'minValue' ? Number(value) : Number(fieldState.minValue ?? 0); const userMaxValue = field === 'maxValue' ? Number(value) : Number(fieldState.maxValue ?? 0); const readOnly = field === 'readOnly' ? Boolean(value) : Boolean(fieldState.readOnly); const required = field === 'required' ? Boolean(value) : Boolean(fieldState.required); - const numberFormat = field === 'numberFormat' ? String(value) : fieldState.numberFormat ?? ''; + const numberFormat = field === 'numberFormat' ? String(value) : (fieldState.numberFormat ?? ''); const fontSize = field === 'fontSize' ? Number(value) : Number(fieldState.fontSize ?? 14); const valueErrors = validateNumberField(String(userValue), { @@ -135,6 +135,27 @@ export const NumberFieldAdvancedSettings = ({ /> +
+ + + +
+
{ - const text = field === 'text' ? String(value) : fieldState.text ?? ''; + const text = field === 'text' ? String(value) : (fieldState.text ?? ''); const limit = field === 'characterLimit' ? Number(value) : Number(fieldState.characterLimit ?? 0); const fontSize = field === 'fontSize' ? Number(value) : Number(fieldState.fontSize ?? 14); @@ -112,6 +119,27 @@ export const TextFieldAdvancedSettings = ({ />
+
+ + + +
+