Skip to content
Snippets Groups Projects
Commit 83375641 authored by Alissa Cheng's avatar Alissa Cheng
Browse files

feat: label styling support for form components

parent b67da7ac
Branches
Tags
1 merge request!32Update form components
......@@ -3,6 +3,7 @@ import { Dispatch, SetStateAction } from "react";
import { clsx } from "clsx";
import Icon from "./Icon.tsx";
import { formFieldBg, cursorPointer } from "./utils/classes.ts";
import Typography from "./Typography.tsx";
type CheckboxProps = {
isDisabled?: boolean;
......@@ -10,6 +11,8 @@ type CheckboxProps = {
isSelected: boolean;
setIsSelected?: Dispatch<SetStateAction<boolean>>;
label?: string;
labelClass?: string;
labelCustomColor?: boolean;
};
const Checkbox = ({
......@@ -18,6 +21,8 @@ const Checkbox = ({
isSelected,
setIsSelected,
label,
labelClass = "",
labelCustomColor = false,
}: CheckboxProps) => {
return (
<NextCheckbox
......@@ -39,7 +44,14 @@ const Checkbox = ({
),
}}
>
{label}
{label && (
<Typography
text={label}
variant="paragraph"
customClass={labelClass}
customColor={labelCustomColor}
/>
)}
</NextCheckbox>
);
};
......
......@@ -15,6 +15,8 @@ type DateInputProps = {
isRequired?: boolean;
label: string;
labelPlacement?: LabelPlacement;
labelClass?: string;
labelCustomColor?: boolean;
isRange?: boolean;
value?: DateNull;
startDate?: DateNull;
......@@ -31,6 +33,8 @@ const DateInput = ({
isRequired = false,
label,
labelPlacement = "outside",
labelClass = "",
labelCustomColor = false,
value,
startDate,
endDate,
......@@ -66,6 +70,8 @@ const DateInput = ({
isRequired={isRequired}
label={label}
labelPlacement={labelPlacement}
labelClass={labelClass}
labelCustomColor={labelCustomColor}
endContent={
<Icon
name="datepicker"
......
......@@ -4,10 +4,15 @@ import { clsx } from "clsx";
import { baseInputClassNames } from "./utils/baseComponents.tsx";
import Icon from "./Icon.tsx";
import { textColor } from "./utils/classes.ts";
import Typography from "./Typography.tsx";
type DropdownProps = {
isRequired?: boolean;
isDisabled?: boolean;
label?: string;
labelPlacement?: "outside" | "outside-left";
labelClass?: string;
labelCustomColor?: boolean;
valueOptions: { value: string; label: string }[];
value?: string;
onValueChange: Dispatch<SetStateAction<string | undefined>>;
......@@ -15,7 +20,11 @@ type DropdownProps = {
const Dropdown = ({
isRequired = false,
isDisabled = false,
label,
labelPlacement = "outside",
labelClass = "",
labelCustomColor = false,
valueOptions,
value,
onValueChange,
......@@ -27,7 +36,16 @@ const Dropdown = ({
return (
<Autocomplete
isRequired={isRequired}
label={label}
label={
label && (
<Typography
text={label}
variant="paragraph"
customClass={labelClass}
customColor={labelCustomColor}
/>
)
}
selectedKey={value}
onSelectionChange={(key) => onValueChange(key as string)}
labelPlacement="outside"
......@@ -35,7 +53,7 @@ const Dropdown = ({
disableAnimation
disableSelectorIconRotation
inputProps={{
classNames: baseInputClassNames(false),
classNames: baseInputClassNames(isDisabled, labelPlacement),
variant: "bordered",
isRequired: isRequired,
}} // use the same props as BaseInput
......
......@@ -3,12 +3,15 @@ import { Textarea } from "@nextui-org/react";
import { TextInputProps } from "./TextInput";
import { formFieldBg, cursorPointer } from "./utils/classes.ts";
import { LabelPlacement } from "./utils/baseComponents.tsx";
import Typography from "./Typography.tsx";
interface TextAreaProps extends Omit<TextInputProps, OmittedTextInputProps> {
minRows?: number;
maxRows?: number;
disableAutosize?: boolean;
labelPlacement?: LabelPlacement;
labelClass?: string;
labelCustomColor?: boolean;
}
type OmittedTextInputProps = "isClearable" | "type" | "inputMode";
......@@ -35,24 +38,23 @@ const classNames = (
return {
base: clsx(
labelPlacement === "outside-left"
? "flex flex-row justify-between items-start"
: "",
{
"flex flex-row justify-between items-start":
labelPlacement === "outside-left",
},
cursorPointer(isDisabled)
),
label: clsx(
"!text-foreground-body ml-[1px]",
labelPlacement === "outside-left" ? "w-1/3" : ""
),
label: clsx("!text-foreground-body ml-[1px]", {
"w-1/3": labelPlacement === "outside-left",
}),
inputWrapper: clsx(
inputWrapperClass(isDisabled),
"px-2 rounded-[4px]",
formFieldBg(isDisabled)
),
input: clsx(
"!text-foreground-body bg-default",
disableAutosize ? "resize-y" : ""
),
input: clsx("!text-foreground-body bg-default", {
"resize-y": disableAutosize,
}),
};
};
......@@ -70,13 +72,24 @@ const TextArea = ({
errorMessage,
disableAutosize = false,
labelPlacement = "outside",
labelClass = "",
labelCustomColor = false,
}: TextAreaProps) => {
return (
<Textarea
isRequired={isRequired}
isDisabled={isDisabled}
isReadOnly={isReadOnly}
label={label}
label={
label && (
<Typography
text={label}
variant="paragraph"
customClass={labelClass}
customColor={labelCustomColor}
/>
)
}
minRows={minRows}
maxRows={maxRows}
maxLength={maxLength}
......
......@@ -5,6 +5,8 @@ type TextInputProps = {
isRequired?: boolean;
label?: string;
labelPlacement?: LabelPlacement;
labelClass?: string;
labelCustomColor?: boolean;
endContent?: ReactNode; // such as units (as text element e.g. <span>)
isClearable?: boolean;
value: string;
......@@ -37,6 +39,8 @@ const TextInput = ({
isRequired = false,
label = "",
labelPlacement = "outside",
labelClass = "",
labelCustomColor = false,
endContent,
isClearable = false,
value,
......@@ -54,6 +58,8 @@ const TextInput = ({
isRequired={isRequired}
label={label}
labelPlacement={labelPlacement}
labelClass={labelClass}
labelCustomColor={labelCustomColor}
endContent={endContent}
placeholder=" "
isClearable={isClearable}
......
......@@ -2,6 +2,7 @@ import { Dispatch, SetStateAction } from "react";
import { Switch } from "@nextui-org/react";
import { clsx } from "clsx";
import { cursorPointer } from "./utils/classes.ts";
import Typography from "./Typography.tsx";
type ToggleProps = {
isDisabled?: boolean;
......@@ -9,6 +10,8 @@ type ToggleProps = {
setIsSelected?: Dispatch<SetStateAction<boolean>>;
label?: string;
labelPlacement?: "right" | "left";
labelClass?: string;
labelCustomColor?: boolean;
width?: "fit" | "full";
};
......@@ -18,6 +21,8 @@ const Toggle = ({
setIsSelected,
label,
labelPlacement = "right",
labelClass = "",
labelCustomColor = false,
width = "fit",
}: ToggleProps) => {
const selectedClass = (isSelected: boolean) =>
......@@ -29,7 +34,14 @@ const Toggle = ({
width === "fit" ? "w-fit" : "w-full"
)}
>
{labelPlacement === "left" && <div className="mr-2">{label}</div>}
{labelPlacement === "left" && label && (
<Typography
variant="paragraph"
customClass={clsx("mr-2", labelClass)}
text={label}
customColor={labelCustomColor}
/>
)}
<Switch
isDisabled={isDisabled}
// Does not support "isRequired": https://github.com/nextui-org/nextui/issues/1610
......@@ -43,7 +55,14 @@ const Toggle = ({
"h-[8px] w-[25px] p-0 overflow-visible bg-lightGrey dark:bg-mediumGrey",
}}
/>
{labelPlacement === "right" && label}
{labelPlacement === "right" && label && (
<Typography
variant="paragraph"
text={label}
customClass={labelClass}
customColor={labelCustomColor}
/>
)}
</div>
);
};
......
......@@ -32,6 +32,7 @@ const Typography = ({
customColor = false,
customClass = "",
}: TypographyProps) => {
// font-body is Inter, font-heading is Montserrat
const font = {
overtitle: "text-[16px] font-heading font-bold",
title: "text-[70px] font-heading font-bold",
......
......@@ -2,6 +2,7 @@ import { Input } from "@nextui-org/react";
import { Dispatch, ReactNode, SetStateAction } from "react";
import { InputSlots } from "@nextui-org/theme";
import { clsx } from "clsx";
import Typography from "src/components/Typography.tsx";
import { formFieldBg, cursorPointer } from "./classes.ts";
export type LabelPlacement = "outside" | "outside-left";
......@@ -10,6 +11,8 @@ type BaseInputProps = {
// basic info
label: string;
labelPlacement: LabelPlacement;
labelClass: string;
labelCustomColor: boolean;
endContent: ReactNode; // such as units or icon
// value and validation
placeholder: string;
......@@ -45,7 +48,8 @@ type BaseInputProps = {
type baseInputClassNamesObj = { [key in InputSlots]: string };
export const baseInputClassNames = (
isDisabled: boolean
isDisabled: boolean,
labelPlacement: LabelPlacement
): baseInputClassNamesObj => {
// this is the border color (use text-*)
const mainWrapperClass = (isDisabled: boolean) =>
......@@ -54,24 +58,29 @@ export const baseInputClassNames = (
: "text-grey dark:text-mediumGrey hover:text-baseBlack focus-within:!text-primary";
const clearButtonClass = (isDisabled: boolean) => {
if (isDisabled) return "";
// we cannot do "[&>svg]:" + className because Tailwind does not support dynamic class names
const clearButtonSvgClass: string =
"[&>svg]:w-[30px] [&>svg]:h-[30px] [&>svg]:mt-[-4px] [&>svg]:ml-[-4px]";
const clearButtonBaseClass: string =
"bg-mediumGrey text-baseWhite dark:bg-baseWhite dark:text-mediumGrey w-[22px] h-[22px] p-0 !opacity-100";
return isDisabled ? "" : clsx(clearButtonSvgClass, clearButtonBaseClass);
return clsx(clearButtonSvgClass, clearButtonBaseClass);
};
return {
base: clsx("flex flex-row justify-between", cursorPointer(isDisabled)),
label: "!text-foreground-body",
input: "!text-foreground-body bg-default",
inputWrapper: clsx(
"px-2 rounded-[4px] min-h-[33px] max-h-[33px]",
formFieldBg(isDisabled)
),
mainWrapper: clsx("w-2/3", mainWrapperClass(isDisabled)),
mainWrapper: clsx(
{
"w-2/3": labelPlacement === "outside-left",
},
mainWrapperClass(isDisabled)
),
clearButton: clearButtonClass(isDisabled),
} as baseInputClassNamesObj;
};
......@@ -79,6 +88,8 @@ export const baseInputClassNames = (
export const BaseInput = ({
label,
labelPlacement = "outside",
labelClass,
labelCustomColor,
endContent,
placeholder,
isClearable = false,
......@@ -95,7 +106,14 @@ export const BaseInput = ({
}: BaseInputProps) => {
return (
<Input
label={label}
label={
<Typography
text={label}
variant="paragraph"
customClass={labelClass}
customColor={labelCustomColor}
/>
}
endContent={endContent}
placeholder={placeholder}
value={value}
......@@ -112,7 +130,7 @@ export const BaseInput = ({
// appearance
labelPlacement={labelPlacement}
variant="bordered"
classNames={baseInputClassNames(isDisabled)}
classNames={baseInputClassNames(isDisabled, labelPlacement)}
/>
);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment