mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
Compare commits
7 Commits
7fa86fe297
...
feat/chang
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c81ee8f6c | |||
| bdef3ec018 | |||
| 311815063b | |||
| e5bbbab62a | |||
| c98dff12e8 | |||
| 0cb0a708d5 | |||
| 592befe09a |
@ -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]);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -331,18 +331,57 @@ 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;
|
||||||
|
|
||||||
|
if (direction === 'horizontal') {
|
||||||
|
let currentX = leftRadioPadding;
|
||||||
|
let currentY = topPadding;
|
||||||
|
const maxWidth = pageWidth - fieldX - leftRadioPadding * 2;
|
||||||
|
|
||||||
|
for (const [index, item] of (values ?? []).entries()) {
|
||||||
|
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`);
|
||||||
|
|
||||||
|
const labelText = item.value.includes('empty-value-') ? '' : item.value;
|
||||||
|
const labelWidth = font.widthOfTextAtSize(labelText, 12);
|
||||||
|
const itemWidth = leftRadioLabelPadding + labelWidth + 16;
|
||||||
|
|
||||||
|
if (currentX + itemWidth > maxWidth && index > 0) {
|
||||||
|
currentX = leftRadioPadding;
|
||||||
|
currentY += radioSpaceY;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.drawText(labelText, {
|
||||||
|
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()) {
|
for (const [index, item] of (values ?? []).entries()) {
|
||||||
const offsetY = index * radioSpaceY + topPadding;
|
const offsetY = index * radioSpaceY + topPadding;
|
||||||
|
|
||||||
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`);
|
const radio = pdf.getForm().createRadioGroup(`radio.${field.secondaryId}.${index}`);
|
||||||
|
|
||||||
// Draw label.
|
|
||||||
page.drawText(item.value.includes('empty-value-') ? '' : item.value, {
|
page.drawText(item.value.includes('empty-value-') ? '' : item.value, {
|
||||||
x: fieldX + leftRadioPadding + leftRadioLabelPadding,
|
x: fieldX + leftRadioPadding + leftRadioLabelPadding,
|
||||||
y: pageHeight - (fieldY + offsetY),
|
y: pageHeight - (fieldY + offsetY),
|
||||||
@ -351,7 +390,6 @@ export const insertFieldInPDFV1 = async (pdf: PDFDocument, field: FieldWithSigna
|
|||||||
rotate: degrees(pageRotationInDegrees),
|
rotate: degrees(pageRotationInDegrees),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw radio button.
|
|
||||||
radio.addOptionToPage(item.value, page, {
|
radio.addOptionToPage(item.value, page, {
|
||||||
x: fieldX + leftRadioPadding,
|
x: fieldX + leftRadioPadding,
|
||||||
y: pageHeight - (fieldY + offsetY),
|
y: pageHeight - (fieldY + offsetY),
|
||||||
@ -363,6 +401,7 @@ export const insertFieldInPDFV1 = async (pdf: PDFDocument, field: FieldWithSigna
|
|||||||
radio.select(item.value);
|
radio.select(item.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.otherwise((field) => {
|
.otherwise((field) => {
|
||||||
const fieldMetaParsers = {
|
const fieldMetaParsers = {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 = {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user