Skip to content

Commit

Permalink
feat(web): create HorizontalCheckableTile component and use it in soi…
Browse files Browse the repository at this point in the history
…ls transformation project form
  • Loading branch information
stephane-ruhlmann committed Dec 16, 2024
1 parent 0f84ccf commit 3423dfc
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import { Controller, useForm } from "react-hook-form";
import { SoilsTransformationProject } from "@/features/create-project/domain/soilsTransformation";
import BackNextButtonsGroup from "@/shared/views/components/BackNextButtons/BackNextButtons";
import Badge from "@/shared/views/components/Badge/Badge";
import HorizontalCheckableTile from "@/shared/views/components/CheckableTile/HorizontalCheckableTile";
import Fieldset from "@/shared/views/components/form/Fieldset/Fieldset";
import FormInfo from "@/shared/views/layout/WizardFormLayout/FormInfo";
import WizardFormLayout from "@/shared/views/layout/WizardFormLayout/WizardFormLayout";

import SoilsTransformationProjectRadioInput from "./SoilsTransformationProjectOption";

type Props = {
onSubmit: (data: FormValues) => void;
onBack: () => void;
Expand Down Expand Up @@ -116,16 +115,18 @@ function SoilsTransformationProjectForm({ onSubmit, onBack }: Props) {
rules={{ required: true }}
render={({ field }) => {
return (
<SoilsTransformationProjectRadioInput
onChange={() => {
field.onChange(option.value);
}}
checked={field.value === option.value}
title={option.title}
description={option.description}
imgSrc={option.imgSrc}
className="tw-mb-4"
/>
<div className="tw-mb-4">
<HorizontalCheckableTile
checked={field.value === option.value}
title={option.title}
description={option.description}
imgSrc={option.imgSrc}
onChange={() => {
field.onChange(option.value);
}}
checkType="radio"
/>
</div>
);
}}
/>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,43 @@ import React, { useId } from "react";

import classNames from "@/shared/views/clsx";

import { getCustomCheckboxStyle, getCustomRadioButtonStyle } from "./styles";

type Props = {
title: string;
description?: React.ReactNode;
imgSrc: string;
checked: boolean;
onChange: () => void;
className?: string;
disabled?: boolean;
checkType?: "checkbox" | "radio";
};

function RadioInputIcon({ checked, disabled = false }: { checked: boolean; disabled?: boolean }) {
type CheckIconProps = {
checked: boolean;
disabled?: boolean;
};

function RadioInputIcon({ checked, disabled = false }: CheckIconProps) {
return (
<div
className={classNames(
"tw-absolute tw-top-0 tw-right-0 tw-w-full tw-h-12",
disabled && "tw-filter tw-grayscale tw-opacity-50",
)}
style={{
backgroundSize: "1.875rem 1.875rem",
backgroundImage: checked
? `radial-gradient(transparent 10px, var(--border-active-blue-france) 11px, transparent 12px), radial-gradient(var(--background-active-blue-france) 5px, transparent 6px)`
: `radial-gradient(transparent 10px, var(--border-action-high-blue-france) 11px, transparent 12px)`,
backgroundRepeat: "no-repeat",
backgroundPosition: "top 16px right 16px",
}}
style={{ ...getCustomRadioButtonStyle(checked), backgroundPosition: "right 16px top 16px" }}
/>
);
}

function CheckboxInputIcon({
checked,
disabled = false,
}: {
checked: boolean;
disabled?: boolean;
}) {
function CheckboxInputIcon({ checked, disabled = false }: CheckIconProps) {
return (
<div
className={classNames(
"tw-absolute tw-top-0 tw-right-0 tw-w-full tw-h-12 tw-m-4",
disabled && "tw-filter tw-grayscale tw-opacity-50",
)}
style={{
width: "1.5rem",
height: "1.5rem",
borderRadius: "0.25rem",
backgroundSize:
"0.25rem 0.25rem, calc(100% - 0.25rem) 1px, 0.25rem 0.25rem, 1px calc(100% - 0.5rem), 0.25rem 0.25rem, calc(100% - 0.5rem) 1px, 0.25rem 0.25rem, 1px calc(100% - 0.5rem), 1rem",
backgroundPosition:
"0 0, 0.25rem 0, 100% 0, 0 0.25rem, 100% 100%, calc(100% - 0.25rem) 100%, 0 100%, 100% 0.25rem, center",
backgroundRepeat: "no-repeat",
backgroundColor: checked ? "var(--background-active-blue-france)" : "initial",
backgroundImage: checked
? `radial-gradient(at 5px 4px, transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at calc(100% - 5px) 4px, transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at calc(100% - 5px) calc(100% - 4px), transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at 5px calc(100% - 4px), transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23f5f5fe' d='M10 15.17l9.2-9.2 1.4 1.42L10 18l-6.36-6.36 1.4-1.42z'/></svg>")`
: "radial-gradient(at 5px 4px, transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at calc(100% - 5px) 4px, transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at calc(100% - 5px) calc(100% - 4px), transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at 5px calc(100% - 4px), transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france))",
}}
style={getCustomCheckboxStyle(checked)}
/>
);
}
Expand All @@ -70,7 +50,6 @@ export default function CheckableTile({
imgSrc,
checked,
onChange,
className,
disabled = false,
checkType = "radio",
}: Props) {
Expand All @@ -80,7 +59,6 @@ export default function CheckableTile({
className={classNames(
"tw-relative tw-border tw-border-solid tw-rounded-lg tw-h-full",
checked ? "tw-border-dsfr-borderBlue" : "tw-border-borderGrey",
className,
)}
role={checkType}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { fr } from "@codegouvfr/react-dsfr";
import React, { useId } from "react";

import classNames from "@/shared/views/clsx";

import { getCustomCheckboxStyle, getCustomRadioButtonStyle } from "./styles";

type Props = {
title: string;
description?: React.ReactNode;
imgSrc?: string;
checked: boolean;
onChange: () => void;
disabled?: boolean;
checkType?: "checkbox" | "radio";
};

type CheckIconProps = {
checked: boolean;
disabled?: boolean;
checkType: "checkbox" | "radio";
};

function CheckIcon({ checkType, checked, disabled = false }: CheckIconProps) {
return (
<div
className={classNames(
"tw-min-w-6 tw-h-6 tw-mr-4",
disabled && "tw-filter tw-grayscale tw-opacity-50",
)}
style={
checkType === "radio" ? getCustomRadioButtonStyle(checked) : getCustomCheckboxStyle(checked)
}
/>
);
}

export default function HorizontalCheckableTile({
title,
description,
imgSrc,
checked,
onChange,
disabled = false,
checkType = "radio",
}: Props) {
const id = useId();
return (
<div
className={classNames(
"tw-relative tw-border tw-border-solid tw-rounded-lg tw-h-full",
checked ? "tw-border-dsfr-borderBlue" : "tw-border-borderGrey",
)}
role={checkType}
>
<input
type={checkType}
className="!tw-opacity-0 tw-h-6 tw-w-6 tw-absolute tw-top-[19px] tw-right-[19px]"
id={id}
value={id}
checked={checked}
disabled={disabled}
onChange={onChange}
/>
<label htmlFor={id} className="tw-w-full">
<div className="tw-p-4 tw-flex tw-items-center">
<CheckIcon checkType={checkType} checked={checked} disabled={disabled} />
{imgSrc && (
<img
src={imgSrc}
width="80px"
height="80px"
alt={`Illustration pour la tuile "${title}"`}
className={classNames("tw-mr-4", disabled && "tw-filter tw-grayscale tw-opacity-50")}
/>
)}
<div>
<div
className={classNames(
description ? "tw-mb-1" : "tw-mb-0",
fr.cx("fr-text--lg", "fr-text--bold"),
)}
>
{title}
</div>
{description && (
<legend className={fr.cx("fr-text--sm", "fr-mb-0")}>{description}</legend>
)}
</div>
</div>
</label>
</div>
);
}
25 changes: 25 additions & 0 deletions apps/web/src/shared/views/components/CheckableTile/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";

export const getCustomRadioButtonStyle = (checked: boolean): React.CSSProperties => ({
backgroundSize: "1.875rem 1.875rem",
backgroundImage: checked
? `radial-gradient(transparent 10px, var(--border-active-blue-france) 11px, transparent 12px), radial-gradient(var(--background-active-blue-france) 5px, transparent 6px)`
: `radial-gradient(transparent 10px, var(--border-action-high-blue-france) 11px, transparent 12px)`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
});

export const getCustomCheckboxStyle = (checked: boolean): React.CSSProperties => ({
width: "1.5rem",
height: "1.5rem",
borderRadius: "0.25rem",
backgroundSize:
"0.25rem 0.25rem, calc(100% - 0.25rem) 1px, 0.25rem 0.25rem, 1px calc(100% - 0.5rem), 0.25rem 0.25rem, calc(100% - 0.5rem) 1px, 0.25rem 0.25rem, 1px calc(100% - 0.5rem), 1rem",
backgroundPosition:
"0 0, 0.25rem 0, 100% 0, 0 0.25rem, 100% 100%, calc(100% - 0.25rem) 100%, 0 100%, 100% 0.25rem, center",
backgroundRepeat: "no-repeat",
backgroundColor: checked ? "var(--background-active-blue-france)" : "initial",
backgroundImage: checked
? `radial-gradient(at 5px 4px, transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at calc(100% - 5px) 4px, transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at calc(100% - 5px) calc(100% - 4px), transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), radial-gradient(at 5px calc(100% - 4px), transparent 4px, var(--border-active-blue-france) 4px, var(--border-active-blue-france) 5px, transparent 6px), linear-gradient(var(--border-active-blue-france), var(--border-active-blue-france)), url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%23f5f5fe' d='M10 15.17l9.2-9.2 1.4 1.42L10 18l-6.36-6.36 1.4-1.42z'/></svg>")`
: "radial-gradient(at 5px 4px, transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at calc(100% - 5px) 4px, transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at calc(100% - 5px) calc(100% - 4px), transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france)), radial-gradient(at 5px calc(100% - 4px), transparent 4px, var(--border-action-high-blue-france) 4px, var(--border-action-high-blue-france) 5px, transparent 6px), linear-gradient(var(--border-action-high-blue-france), var(--border-action-high-blue-france))",
});

0 comments on commit 3423dfc

Please sign in to comment.