Skip to content

Commit 410313b

Browse files
committed
fix: Fixed card style for radios and checkboxed
1 parent 11ea989 commit 410313b

11 files changed

Lines changed: 101 additions & 44 deletions

File tree

‎src/app/layout.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export default function RootLayout({
5656
}: Readonly<{ children: React.ReactNode }>) {
5757
return (
5858
<html lang="en">
59-
<body className={cn(fontVariables)}>
59+
<body className={cn(fontVariables, "font-sans")}>
6060
<PostHogProvider>
6161
{children}
6262
<Analytics />

‎src/components/form-builder/form-components/form-checkbox-group.tsx‎

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { HtmlGroup } from "../sidebar/groups/html-group";
77
import { LabelGroup } from "../sidebar/groups/label-group";
88
import { InputGroup } from "../sidebar/groups/input-group";
99
import { OptionsGroup } from "../sidebar/groups/options-group";
10-
import { cn, escapeHtml } from "@/lib/utils";
10+
import { cn, escapeHtml, generateTWClassesForAllViewports } from "@/lib/utils";
1111
import {
1212
UseFormReturn,
1313
FieldValues,
@@ -20,26 +20,30 @@ import {
2020
FormItem,
2121
FormControl,
2222
} from "@/components/ui/form";
23+
import { useFormBuilderStore } from "@/stores/form-builder-store";
2324

2425
export function FormCheckboxGroup(
2526
component: FormComponentModel,
2627
form: UseFormReturn<FieldValues, undefined>,
2728
field: ControllerRenderProps
2829
) {
2930
const oneOptionHasLabelDescription = component.options?.some((option) => option.labelDescription);
31+
const asCardClasses = generateTWClassesForAllViewports(component, "asCard");
32+
const cardLayoutClasses = component.getField("properties.style.cardLayout");
3033

3134
return (
32-
<div className={cn("flex flex-col gap-2")}>
35+
<div className={cn("grid w-full gap-2", cardLayoutClasses === "horizontal" && "@3xl:grid-cols-2")}>
3336
{component.options?.map((option) => (
3437
<FormField
3538
name={component.id}
3639
key={option.value}
3740
control={form.control}
3841
render={({ field: OptionField }) => {
3942
return (
40-
<FormItem key={option.value} className="flex items-start gap-2">
43+
<FormLabel key={option.value} className={cn(asCardClasses, "flex items-start has-[[data-state=checked]]:border-primary")} htmlFor={`${component.getField("attributes.id")}-${option.value}`}>
4144
<FormControl>
4245
<Checkbox
46+
id={`${component.getField("attributes.id")}-${option.value}`}
4347
checked={OptionField.value?.includes(option.value)}
4448
onCheckedChange={(checked) => {
4549
return checked
@@ -55,8 +59,8 @@ export function FormCheckboxGroup(
5559
}}
5660
/>
5761
</FormControl>
58-
<div className="grid gap-2 leading-none">
59-
<FormLabel className={cn("text-sm leading-tight font-normal", oneOptionHasLabelDescription && "font-medium")}>
62+
<div className="grid gap-1 leading-none">
63+
<FormLabel htmlFor={`${component.getField("attributes.id")}-${option.value}`} className={cn("text-sm leading-tight font-normal", oneOptionHasLabelDescription && "font-medium")}>
6064
{option.label}
6165
</FormLabel>
6266
{option.labelDescription && (
@@ -65,7 +69,7 @@ export function FormCheckboxGroup(
6569
</p>
6670
)}
6771
</div>
68-
</FormItem>
72+
</FormLabel>
6973
);
7074
}}
7175
/>
@@ -81,10 +85,12 @@ type ReactCode = {
8185

8286
export function getReactCode(component: FormComponentModel): ReactCode {
8387
const oneOptionHasLabelDescription = component.options?.some((option) => option.labelDescription);
88+
const cardLayoutClasses = component.getField("properties.style.cardLayout");
89+
const asCardClasses = generateTWClassesForAllViewports(component, "asCard");
8490

8591
return {
8692
code: `
87-
<div className="flex flex-col gap-2">
93+
<div className="${escapeHtml(cn("grid w-full gap-2", cardLayoutClasses === "horizontal" && "@3xl:grid-cols-2"))}">
8894
${component.options
8995
?.map(
9096
(option) => `
@@ -93,14 +99,14 @@ export function getReactCode(component: FormComponentModel): ReactCode {
9399
control={form.control}
94100
render={({ field: OptionField }) => {
95101
return (
96-
<FormItem key="${option.value}" className="flex items-start gap-3">
102+
<FormItem key="${option.value}" className="${escapeHtml(cn(asCardClasses, "flex items-start"))}">
97103
<FormControl>
98104
<Checkbox
99105
checked={OptionField.value?.includes("${option.value}")}
100106
onCheckedChange={(checked) => {
101107
return checked
102108
? OptionField.onChange([
103-
...OptionField.value,
109+
...OptionField.value || [],
104110
"${option.value}",
105111
])
106112
: OptionField.onChange(
@@ -136,7 +142,7 @@ export const CheckboxGroupDesignProperties: DesignPropertiesViews = {
136142
grid: <GridGroup />,
137143
html: <HtmlGroup />,
138144
label: <LabelGroup whitelist={["label"]} />,
139-
input: <InputGroup whitelist={["description", "asCard"]} />,
145+
input: <InputGroup whitelist={["description", "asCard", "cardLayout"]} />,
140146
options: <OptionsGroup />,
141147
button: null,
142148
validation: <ValidationGroup />,

‎src/components/form-builder/form-components/form-checkbox.tsx‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,29 @@ import { HtmlGroup } from "../sidebar/groups/html-group";
77
import { LabelGroup } from "../sidebar/groups/label-group";
88
import { InputGroup } from "../sidebar/groups/input-group";
99
import { cn, generateTWClassesForAllViewports, escapeHtml } from "@/lib/utils";
10-
import { ControllerRenderProps, FieldValues, UseFormReturn } from "react-hook-form";
10+
import {
11+
ControllerRenderProps,
12+
FieldValues,
13+
UseFormReturn,
14+
} from "react-hook-form";
1115
import { ValidationGroup } from "../sidebar/groups/validation-group";
1216
import { FormLabel } from "@/components/ui/form";
17+
import { useFormBuilderStore } from "@/stores/form-builder-store";
1318

14-
export function FormCheckbox(component: FormComponentModel, form: UseFormReturn<FieldValues, undefined>, field: ControllerRenderProps) {
19+
export function FormCheckbox(
20+
component: FormComponentModel,
21+
form: UseFormReturn<FieldValues, undefined>,
22+
field: ControllerRenderProps
23+
) {
1524
const asCardClasses = generateTWClassesForAllViewports(component, "asCard");
1625

1726
return (
1827
<FormLabel
1928
key={component.id}
20-
className={cn(asCardClasses, "w-full flex items-start space-x-2 has-[[data-state=checked]]:border-primary")}
29+
className={cn(
30+
asCardClasses,
31+
"w-full flex items-start has-[[data-state=checked]]:border-primary",
32+
)}
2133
htmlFor={component.getField("attributes.id")}
2234
>
2335
<Checkbox
@@ -28,13 +40,11 @@ export function FormCheckbox(component: FormComponentModel, form: UseFormReturn<
2840
onCheckedChange={field.onChange}
2941
/>
3042
<div className="grid gap-1.5 leading-none">
31-
<FormLabel>
32-
{component.getField("label")}
33-
</FormLabel>
43+
<FormLabel htmlFor={component.getField("attributes.id")}>{component.getField("label")}</FormLabel>
3444
<p className="text-sm text-muted-foreground">
3545
{component.getField("label_description")}
3646
</p>
37-
</div>
47+
</div>r
3848
</FormLabel>
3949
);
4050
}
@@ -50,7 +60,7 @@ export function getReactCode(component: FormComponentModel): ReactCode {
5060
code: `
5161
<FormLabel
5262
key="${component.id}"
53-
className="${escapeHtml(cn(asCardClasses, "w-full flex items-start space-x-2 has-[[data-state=checked]]:border-primary"))}"
63+
className="${escapeHtml(cn(asCardClasses, "w-full flex items-start has-[[data-state=checked]]:border-primary"))}"
5464
htmlFor="${escapeHtml(component.getField("attributes.id"))}"
5565
>
5666
<Checkbox id="${escapeHtml(component.getField("attributes.id"))}" className="${escapeHtml(component.getField("attributes.class"))}" {...field} checked={field.value} onCheckedChange={field.onChange} />
@@ -67,7 +77,6 @@ export function getReactCode(component: FormComponentModel): ReactCode {
6777
dependencies: {
6878
"@/components/ui/checkbox": ["Checkbox"],
6979
"@/components/ui/form": ["FormLabel"],
70-
7180
},
7281
};
7382
}

‎src/components/form-builder/form-components/form-radio.tsx‎

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,58 @@
11
import { Label } from "@/components/ui/label";
22
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
3-
import { DesignPropertiesViews } from "@/types/form-builder.types";
3+
import { DesignPropertiesViews, Viewports } from "@/types/form-builder.types";
44
import { FormComponentModel } from "@/models/FormComponent";
55
import { HtmlGroup } from "../sidebar/groups/html-group";
66
import { GridGroup } from "../sidebar/groups/grid-group";
77
import { LabelGroup } from "../sidebar/groups/label-group";
88
import { InputGroup } from "../sidebar/groups/input-group";
99
import { OptionsGroup } from "../sidebar/groups/options-group";
10-
import { cn, escapeHtml } from "@/lib/utils";
10+
import { cn, escapeHtml, generateTWClassesForAllViewports } from "@/lib/utils";
1111
import {
1212
ControllerRenderProps,
1313
FieldValues,
1414
UseFormReturn,
1515
} from "react-hook-form";
1616
import { ValidationGroup } from "../sidebar/groups/validation-group";
1717
import { FormLabel } from "@/components/ui/form";
18+
import { useFormBuilderStore } from "@/stores/form-builder-store";
1819

1920
export function FormRadio(
2021
component: FormComponentModel,
2122
form: UseFormReturn<FieldValues, undefined>,
2223
field: ControllerRenderProps
2324
) {
24-
25-
const oneOptionHasLabelDescription = component.options?.some((option) => option.labelDescription);
25+
const oneOptionHasLabelDescription = component.options?.some(
26+
(option) => option.labelDescription
27+
);
28+
const asCardClasses = generateTWClassesForAllViewports(component, "asCard");
29+
const cardLayoutClasses = component.getField("properties.style.cardLayout");
2630

2731
return (
2832
<RadioGroup
2933
key={component.id}
3034
id={component.getField("attributes.id")}
31-
className={cn(component.getField("attributes.class"))}
35+
className={cn("w-full", component.getField("attributes.class"), cardLayoutClasses === "horizontal" && "@3xl:grid-cols-2")}
3236
{...field}
3337
onValueChange={field.onChange}
3438
>
3539
{component.options?.map((option) => (
36-
<div key={option.value} className="flex items-start space-x-2">
40+
<FormLabel
41+
key={option.value}
42+
className={cn(asCardClasses, "flex items-start has-[[data-state=checked]]:border-primary")}
43+
htmlFor={`${component.getField("attributes.id")}-${option.value}`}
44+
>
3745
<RadioGroupItem
3846
value={option.value}
3947
id={`${component.getField("attributes.id")}-${option.value}`}
4048
/>
41-
<div className="grid gap-2 leading-none">
49+
<div className="grid gap-1 leading-none">
4250
<FormLabel
4351
htmlFor={`${component.getField("attributes.id")}-${option.value}`}
44-
className={cn("font-normal", oneOptionHasLabelDescription && "font-medium")}
52+
className={cn(
53+
"font-normal",
54+
oneOptionHasLabelDescription && "font-medium"
55+
)}
4556
>
4657
{option.label}
4758
</FormLabel>
@@ -51,7 +62,7 @@ export function FormRadio(
5162
</p>
5263
)}
5364
</div>
54-
</div>
65+
</FormLabel>
5566
))}
5667
</RadioGroup>
5768
);
@@ -63,29 +74,32 @@ type ReactCode = {
6374
};
6475

6576
export function getReactCode(component: FormComponentModel): ReactCode {
66-
const oneOptionHasLabelDescription = component.options?.some((option) => option.labelDescription);
67-
77+
const oneOptionHasLabelDescription = component.options?.some(
78+
(option) => option.labelDescription
79+
);
80+
const asCardClasses = generateTWClassesForAllViewports(component, "asCard");
81+
const cardLayoutClasses = component.getField("properties.style.cardLayout");
6882
return {
6983
code: `
7084
<RadioGroup
7185
key="${component.id}"
7286
id="${escapeHtml(component.getField("attributes.id"))}"
73-
className="${escapeHtml(component.getField("attributes.class"))}"
87+
className="${escapeHtml(cn("w-full", component.getField("attributes.class"), cardLayoutClasses === "horizontal" && "@3xl:grid-cols-2"))}"
7488
{...field}
7589
onValueChange={field.onChange}
7690
>
7791
${component.options
7892
?.map(
7993
(option) => `
80-
<div key="${escapeHtml(option.value)}" className="flex items-center space-x-2">
94+
<FormLabel key="${escapeHtml(option.value)}" className="${escapeHtml(cn(asCardClasses, "flex items-center has-[[data-state=checked]]:border-primary"))}" htmlFor="${escapeHtml(component.getField("attributes.id"))}-${escapeHtml(option.value)}">
8195
<RadioGroupItem value="${escapeHtml(option.value)}" id="${escapeHtml(component.getField("attributes.id"))}-${escapeHtml(option.value)}" />
8296
<div className="grid gap-2 leading-none">
8397
<FormLabel htmlFor="${escapeHtml(component.getField("attributes.id"))}-${escapeHtml(option.value)}" className="${oneOptionHasLabelDescription ? "font-medium" : "font-normal"}">
8498
${escapeHtml(option.label)}
8599
</FormLabel>
86100
${option.labelDescription ? `<p className="text-sm text-muted-foreground">${escapeHtml(option.labelDescription)}</p>` : ""}
87101
</div>
88-
</div>
102+
</FormLabel>
89103
`
90104
)
91105
.join("\n")}
@@ -107,7 +121,7 @@ export const RadioDesignProperties: DesignPropertiesViews = {
107121
whitelist={["label", "labelPosition", "labelAlign", "showLabel"]}
108122
/>
109123
),
110-
input: <InputGroup whitelist={["placeholder", "description", "value"]} />,
124+
input: <InputGroup whitelist={["placeholder", "description", "value", "asCard", "cardLayout"]} />,
111125
options: <OptionsGroup />,
112126
button: null,
113127
validation: <ValidationGroup />,

‎src/components/form-builder/form-components/form-switch.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ export function FormSwitch(component: FormComponentModel, form: UseFormReturn<Fi
1818
return (
1919
<FormLabel
2020
key={component.id}
21-
className={cn(asCardClasses, "flex justify-between items-center space-x-2 w-full has-[[data-state=checked]]:border-primary")}
21+
className={cn(asCardClasses, "flex justify-between items-center w-full has-[[data-state=checked]]:border-primary")}
2222
htmlFor={component.getField("attributes.id")}
2323
>
24-
<div className="grid gap-1.5 leading-none">
24+
<div className="grid gap-1 leading-none">
2525
<FormLabel>
2626
{component.getField("label")}
2727
</FormLabel>
@@ -45,7 +45,7 @@ export function getReactCode(component: FormComponentModel): ReactCode {
4545
code: `
4646
<FormLabel
4747
key="${component.id}"
48-
className="${escapeHtml(cn(asCardClasses, "w-full flex justify-between items-center space-x-2 has-[[data-state=checked]]:border-primary"))}"
48+
className="${escapeHtml(cn(asCardClasses, "w-full flex justify-between items-center has-[[data-state=checked]]:border-primary"))}"
4949
htmlFor="${escapeHtml(component.getField("attributes.id"))}"
5050
>
5151
<div className="grid gap-1.5 leading-none">

‎src/components/form-builder/mainCanvas.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const JsonPreview = memo(
2626
);
2727

2828
return (
29-
<div className={`h-full overflow-scroll w-full`}>
29+
<div className={`absolute top-0 right-0 h-full w-full`}>
3030
<Pre language="json" code={jsonString} />
3131
</div>
3232
);

‎src/components/form-builder/sidebar/groups/input-group.tsx‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import {
1010
} from "@/components/ui/select";
1111
import { ViewportOverrideIndicator } from "@/components/form-builder/helpers/ViewportOverrideIndicator";
1212
import { ToggleGroupNav } from "@/components/form-builder/ui/toggle-group-nav";
13+
import { AlignCenter, Columns2, Columns3, Rows2, Rows3 } from "lucide-react";
14+
import { AlignLeft } from "lucide-react";
1315

14-
type propertiesWhitelist = "type" | "placeholder" | "description" | "asCard" | "value" | "checked";
16+
type propertiesWhitelist = "type" | "placeholder" | "description" | "asCard" | "value" | "checked" | "cardLayout";
1517

1618
export type InputGroupProps = {
1719
whitelist?: propertiesWhitelist[];
@@ -38,6 +40,9 @@ export function InputGroup({
3840
viewport
3941
);
4042

43+
const defaultValueCardLayout = selectedComponent.getField(
44+
"properties.style.cardLayout" );
45+
4146
const handleChange = (
4247
field: string,
4348
value: any,
@@ -151,6 +156,27 @@ export function InputGroup({
151156
</div>
152157
</div>
153158
)}
159+
{whitelist.includes("cardLayout") && (
160+
<div className="grid grid-cols-2 gap-2 items-center justify-between">
161+
<Label htmlFor="cardLayout" className="text-xs text-gray-400">
162+
Card Layout
163+
</Label>
164+
<div className="flex flex-row items-center gap-2">
165+
<ToggleGroupNav
166+
name="cardLayout"
167+
items={[
168+
{ value: "horizontal", icon: Columns3 },
169+
{ value: "vertical", icon: Rows3 },
170+
]}
171+
defaultValue={defaultValueCardLayout}
172+
onValueChange={(value) =>
173+
handleChange("properties.style.cardLayout", value, true)
174+
}
175+
className="w-full"
176+
/>
177+
</div>
178+
</div>
179+
)}
154180
</div>
155181
);
156182
}

‎src/config/available-components.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactCode } from "@/types/form-builder.types";
1+
import { ReactCode, Viewports } from "@/types/form-builder.types";
22
import { FormComponentModel } from "@/models/FormComponent";
33
import {
44
// Form Components

0 commit comments

Comments
 (0)