Compare commits

...

7 Commits

8 changed files with 139 additions and 26 deletions

View File

@ -27,6 +27,7 @@ const ZRadioFieldFormSchema = z
.optional(), .optional(),
required: z.boolean().optional(), required: z.boolean().optional(),
readOnly: z.boolean().optional(), readOnly: z.boolean().optional(),
direction: z.enum(['vertical', 'horizontal']).optional(),
}) })
.refine( .refine(
(data) => { (data) => {
@ -53,6 +54,7 @@ export type EditorFieldRadioFormProps = {
export const EditorFieldRadioForm = ({ export const EditorFieldRadioForm = ({
value = { value = {
type: 'radio', type: 'radio',
direction: 'vertical',
}, },
onValueChange, onValueChange,
}: EditorFieldRadioFormProps) => { }: EditorFieldRadioFormProps) => {
@ -64,6 +66,7 @@ export const EditorFieldRadioForm = ({
values: value.values || [{ id: 1, checked: false, value: 'Default value' }], values: value.values || [{ id: 1, checked: false, value: 'Default value' }],
required: value.required || false, required: value.required || false,
readOnly: value.readOnly || false, readOnly: value.readOnly || false,
direction: value.direction || 'vertical',
}, },
}); });
@ -100,6 +103,7 @@ export const EditorFieldRadioForm = ({
onValueChange({ onValueChange({
type: 'radio', type: 'radio',
...validatedFormValues.data, ...validatedFormValues.data,
direction: validatedFormValues.data.direction || 'vertical',
}); });
} }
}, [formValues]); }, [formValues]);

View File

@ -14,6 +14,7 @@ import type {
TRemovedSignedFieldWithTokenMutationSchema, TRemovedSignedFieldWithTokenMutationSchema,
TSignFieldWithTokenMutationSchema, TSignFieldWithTokenMutationSchema,
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
@ -156,7 +157,12 @@ export const DocumentSigningRadioField = ({
{!field.inserted && ( {!field.inserted && (
<RadioGroup <RadioGroup
onValueChange={(value) => handleSelectItem(value)} onValueChange={(value) => handleSelectItem(value)}
className="z-10 my-0.5 gap-y-1" className={cn(
'z-10 my-0.5 gap-1',
parsedFieldMeta.direction === 'horizontal'
? 'flex flex-row flex-wrap'
: 'flex flex-col gap-y-1',
)}
> >
{values?.map((item, index) => ( {values?.map((item, index) => (
<div key={index} className="flex items-center"> <div key={index} className="flex items-center">
@ -181,7 +187,14 @@ export const DocumentSigningRadioField = ({
)} )}
{field.inserted && ( {field.inserted && (
<RadioGroup className="my-0.5 gap-y-1"> <RadioGroup
className={cn(
'my-0.5 gap-1',
parsedFieldMeta.direction === 'horizontal'
? 'flex flex-row flex-wrap'
: 'flex flex-col gap-y-1',
)}
>
{values?.map((item, index) => ( {values?.map((item, index) => (
<div key={index} className="flex items-center"> <div key={index} className="flex items-center">
<RadioGroupItem <RadioGroupItem

View File

@ -331,36 +331,75 @@ export const insertFieldInPDFV1 = async (pdf: PDFDocument, field: FieldWithSigna
})); }));
const selected = field.customText.split(','); const selected = field.customText.split(',');
const direction = meta.data.direction ?? 'vertical';
const topPadding = 12; const topPadding = 12;
const leftRadioPadding = 8; const leftRadioPadding = 8;
const leftRadioLabelPadding = 12; const leftRadioLabelPadding = 12;
const radioSpaceY = 13; const radioSpaceY = 13;
for (const [index, item] of (values ?? []).entries()) { if (direction === 'horizontal') {
const offsetY = index * radioSpaceY + topPadding; let currentX = leftRadioPadding;
let currentY = topPadding;
const maxWidth = pageWidth - fieldX - leftRadioPadding * 2;
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`); for (const [index, item] of (values ?? []).entries()) {
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`);
// Draw label. const labelText = item.value.includes('empty-value-') ? '' : item.value;
page.drawText(item.value.includes('empty-value-') ? '' : item.value, { const labelWidth = font.widthOfTextAtSize(labelText, 12);
x: fieldX + leftRadioPadding + leftRadioLabelPadding, const itemWidth = leftRadioLabelPadding + labelWidth + 16;
y: pageHeight - (fieldY + offsetY),
size: 12,
font,
rotate: degrees(pageRotationInDegrees),
});
// Draw radio button. if (currentX + itemWidth > maxWidth && index > 0) {
radio.addOptionToPage(item.value, page, { currentX = leftRadioPadding;
x: fieldX + leftRadioPadding, currentY += radioSpaceY;
y: pageHeight - (fieldY + offsetY), }
height: 8,
width: 8,
});
if (selected.includes(item.value)) { page.drawText(labelText, {
radio.select(item.value); x: fieldX + currentX + leftRadioLabelPadding,
y: pageHeight - (fieldY + currentY),
size: 12,
font,
rotate: degrees(pageRotationInDegrees),
});
radio.addOptionToPage(item.value, page, {
x: fieldX + currentX,
y: pageHeight - (fieldY + currentY),
height: 8,
width: 8,
});
if (selected.includes(item.value)) {
radio.select(item.value);
}
currentX += itemWidth;
}
} else {
for (const [index, item] of (values ?? []).entries()) {
const offsetY = index * radioSpaceY + topPadding;
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`);
page.drawText(item.value.includes('empty-value-') ? '' : item.value, {
x: fieldX + leftRadioPadding + leftRadioLabelPadding,
y: pageHeight - (fieldY + offsetY),
size: 12,
font,
rotate: degrees(pageRotationInDegrees),
});
radio.addOptionToPage(item.value, page, {
x: fieldX + leftRadioPadding,
y: pageHeight - (fieldY + offsetY),
height: 8,
width: 8,
});
if (selected.includes(item.value)) {
radio.select(item.value);
}
} }
} }
}) })

View File

@ -203,6 +203,7 @@ const getUpdatedFieldMeta = (field: Field, prefillField?: TFieldMetaPrefillField
type: 'radio', type: 'radio',
label: field.label, label: field.label,
values: newValues, values: newValues,
direction: radioMeta.direction ?? 'vertical',
}; };
return meta; return meta;

View File

@ -81,6 +81,7 @@ export const ZRadioFieldMeta = ZBaseFieldMeta.extend({
}), }),
) )
.optional(), .optional(),
direction: z.enum(['vertical', 'horizontal']).optional().default('vertical'),
}); });
export type TRadioFieldMeta = z.infer<typeof ZRadioFieldMeta>; export type TRadioFieldMeta = z.infer<typeof ZRadioFieldMeta>;
@ -278,6 +279,7 @@ export const FIELD_RADIO_META_DEFAULT_VALUES: TRadioFieldMeta = {
values: [{ id: 1, checked: false, value: '' }], values: [{ id: 1, checked: false, value: '' }],
required: false, required: false,
readOnly: false, readOnly: false,
direction: 'vertical',
}; };
export const FIELD_CHECKBOX_META_DEFAULT_VALUES: TCheckboxFieldMeta = { export const FIELD_CHECKBOX_META_DEFAULT_VALUES: TCheckboxFieldMeta = {

View File

@ -108,8 +108,15 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
field.fieldMeta.values.length > 0 field.fieldMeta.values.length > 0
) { ) {
return ( return (
<div className="flex flex-col gap-y-2 py-0.5"> <div className="py-0.5">
<RadioGroup className="gap-y-1"> <RadioGroup
className={cn(
'gap-1',
field.fieldMeta.direction === 'horizontal'
? 'flex flex-row flex-wrap'
: 'flex flex-col gap-y-1',
)}
>
{field.fieldMeta.values.map((item, index) => ( {field.fieldMeta.values.map((item, index) => (
<div key={index} className="flex items-center"> <div key={index} className="flex items-center">
<RadioGroupItem <RadioGroupItem

View File

@ -122,6 +122,7 @@ const getDefaultState = (fieldType: FieldType): FieldMeta => {
values: [], values: [],
required: false, required: false,
readOnly: false, readOnly: false,
direction: 'vertical',
}; };
case FieldType.CHECKBOX: case FieldType.CHECKBOX:
return { return {

View File

@ -11,6 +11,13 @@ import { Button } from '@documenso/ui/primitives/button';
import { Checkbox } from '@documenso/ui/primitives/checkbox'; import { Checkbox } from '@documenso/ui/primitives/checkbox';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch'; import { Switch } from '@documenso/ui/primitives/switch';
export type RadioFieldAdvancedSettingsProps = { export type RadioFieldAdvancedSettingsProps = {
@ -35,6 +42,9 @@ export const RadioFieldAdvancedSettings = ({
); );
const [readOnly, setReadOnly] = useState(fieldState.readOnly ?? false); const [readOnly, setReadOnly] = useState(fieldState.readOnly ?? false);
const [required, setRequired] = useState(fieldState.required ?? false); const [required, setRequired] = useState(fieldState.required ?? false);
const [direction, setDirection] = useState<'vertical' | 'horizontal'>(
fieldState.direction ?? 'vertical',
);
const addValue = () => { const addValue = () => {
const newId = values.length > 0 ? Math.max(...values.map((val) => val.id)) + 1 : 1; const newId = values.length > 0 ? Math.max(...values.map((val) => val.id)) + 1 : 1;
@ -69,10 +79,19 @@ export const RadioFieldAdvancedSettings = ({
const handleToggleChange = (field: keyof RadioFieldMeta, value: string | boolean) => { const handleToggleChange = (field: keyof RadioFieldMeta, value: string | boolean) => {
const readOnly = field === 'readOnly' ? Boolean(value) : Boolean(fieldState.readOnly); const readOnly = field === 'readOnly' ? Boolean(value) : Boolean(fieldState.readOnly);
const required = field === 'required' ? Boolean(value) : Boolean(fieldState.required); const required = field === 'required' ? Boolean(value) : Boolean(fieldState.required);
const currentDirection =
field === 'direction' && String(value) === 'horizontal' ? 'horizontal' : 'vertical';
setReadOnly(readOnly); setReadOnly(readOnly);
setRequired(required); setRequired(required);
setDirection(currentDirection);
const errors = validateRadioField(String(value), { readOnly, required, values, type: 'radio' }); const errors = validateRadioField(String(value), {
readOnly,
required,
values,
type: 'radio',
direction: currentDirection,
});
handleErrors(errors); handleErrors(errors);
handleFieldChange(field, value); handleFieldChange(field, value);
@ -97,7 +116,13 @@ export const RadioFieldAdvancedSettings = ({
}, [fieldState.values]); }, [fieldState.values]);
useEffect(() => { useEffect(() => {
const errors = validateRadioField(undefined, { readOnly, required, values, type: 'radio' }); const errors = validateRadioField(undefined, {
readOnly,
required,
values,
type: 'radio',
direction,
});
handleErrors(errors); handleErrors(errors);
}, [values]); }, [values]);
@ -116,6 +141,27 @@ export const RadioFieldAdvancedSettings = ({
onChange={(e) => handleFieldChange('label', e.target.value)} onChange={(e) => handleFieldChange('label', e.target.value)}
/> />
</div> </div>
<div>
<Label>
<Trans>Direction</Trans>
</Label>
<Select
value={fieldState.direction ?? 'vertical'}
onValueChange={(val) => handleToggleChange('direction', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectValue placeholder={_(msg`Select direction`)} />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="vertical">
<Trans>Vertical</Trans>
</SelectItem>
<SelectItem value="horizontal">
<Trans>Horizontal</Trans>
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
<Switch <Switch
className="bg-background" className="bg-background"