first commit
This commit is contained in:
35
calcom/packages/ui/form/AddressInput.tsx
Normal file
35
calcom/packages/ui/form/AddressInput.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import cx from "@calcom/lib/classNames";
|
||||
|
||||
import { Icon } from "..";
|
||||
import { Input } from "../components/form";
|
||||
|
||||
export type AddressInputProps = {
|
||||
value: string;
|
||||
id?: string;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
onChange: (val: string) => void;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function AddressInput({ value, onChange, ...rest }: AddressInputProps) {
|
||||
return (
|
||||
<div className="relative flex items-center">
|
||||
<Icon
|
||||
name="map-pin"
|
||||
className="text-muted absolute left-0.5 ml-3 h-4 w-4 -translate-y-1/2"
|
||||
style={{ top: "44%" }}
|
||||
/>
|
||||
<Input
|
||||
{...rest}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
className={cx("pl-10", rest?.className)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddressInput;
|
||||
8
calcom/packages/ui/form/AddressInputLazy.tsx
Normal file
8
calcom/packages/ui/form/AddressInputLazy.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
/** These are like 40kb that not every user needs */
|
||||
const AddressInput = dynamic(
|
||||
() => import("./AddressInput")
|
||||
) as unknown as typeof import("./AddressInput").default;
|
||||
|
||||
export default AddressInput;
|
||||
87
calcom/packages/ui/form/PhoneInput.tsx
Normal file
87
calcom/packages/ui/form/PhoneInput.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { isSupportedCountry } from "libphonenumber-js";
|
||||
import { useState, useEffect } from "react";
|
||||
import PhoneInput from "react-phone-input-2";
|
||||
import "react-phone-input-2/lib/style.css";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
export type PhoneInputProps = {
|
||||
value?: string;
|
||||
id?: string;
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
className?: string;
|
||||
name?: string;
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
||||
function BasePhoneInput({ name, className = "", onChange, value, ...rest }: PhoneInputProps) {
|
||||
const defaultCountry = useDefaultCountry();
|
||||
|
||||
return (
|
||||
<PhoneInput
|
||||
{...rest}
|
||||
value={value ? value.trim().replace(/^\+?/, "+") : undefined}
|
||||
country={value ? undefined : defaultCountry}
|
||||
enableSearch
|
||||
disableSearchIcon
|
||||
inputProps={{
|
||||
name: name,
|
||||
required: rest.required,
|
||||
placeholder: rest.placeholder,
|
||||
}}
|
||||
onChange={(value) => {
|
||||
onChange(`+${value}`);
|
||||
}}
|
||||
containerClass={classNames(
|
||||
"hover:border-emphasis dark:focus:border-emphasis border-default !bg-default rounded-md border focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default disabled:cursor-not-allowed",
|
||||
className
|
||||
)}
|
||||
inputClass="text-sm focus:ring-0 !bg-default text-default"
|
||||
buttonClass="text-emphasis !bg-default hover:!bg-emphasis"
|
||||
searchClass="!text-default !bg-default hover:!bg-emphasis"
|
||||
dropdownClass="!text-default !bg-default"
|
||||
inputStyle={{ width: "inherit", border: 0 }}
|
||||
searchStyle={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
padding: "6px 12px",
|
||||
gap: "8px",
|
||||
width: "296px",
|
||||
height: "28px",
|
||||
marginLeft: "-4px",
|
||||
}}
|
||||
dropdownStyle={{ width: "max-content" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const useDefaultCountry = () => {
|
||||
const [defaultCountry, setDefaultCountry] = useState("us");
|
||||
const query = trpc.viewer.public.countryCode.useQuery(undefined, {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
retry: false,
|
||||
});
|
||||
|
||||
useEffect(
|
||||
function refactorMeWithoutEffect() {
|
||||
const data = query.data;
|
||||
if (!data?.countryCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSupportedCountry(data?.countryCode)
|
||||
? setDefaultCountry(data.countryCode.toLowerCase())
|
||||
: setDefaultCountry(navigator.language.split("-")[1]?.toLowerCase() || "us");
|
||||
},
|
||||
[query.data]
|
||||
);
|
||||
|
||||
return defaultCountry;
|
||||
};
|
||||
|
||||
export default BasePhoneInput;
|
||||
8
calcom/packages/ui/form/PhoneInputLazy.tsx
Normal file
8
calcom/packages/ui/form/PhoneInputLazy.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
/** These are like 40kb that not every user needs */
|
||||
const PhoneInputLazy = dynamic(
|
||||
() => import("./PhoneInput")
|
||||
) as unknown as typeof import("./PhoneInput").default;
|
||||
|
||||
export default PhoneInputLazy;
|
||||
106
calcom/packages/ui/form/Select.tsx
Normal file
106
calcom/packages/ui/form/Select.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { GroupBase, InputProps, Props } from "react-select";
|
||||
import ReactSelect, { components } from "react-select";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
|
||||
export type SelectProps<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
> = Props<Option, IsMulti, Group>;
|
||||
|
||||
export const InputComponent = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>({
|
||||
inputClassName,
|
||||
...props
|
||||
}: InputProps<Option, IsMulti, Group>) => {
|
||||
return (
|
||||
<components.Input
|
||||
// disables our default form focus hightlight on the react-select input element
|
||||
inputClassName={classNames("focus:ring-0 focus:ring-offset-0", inputClassName)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function Select<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
|
||||
return (
|
||||
<ReactSelect
|
||||
theme={(theme) => ({
|
||||
...theme,
|
||||
borderRadius: 2,
|
||||
colors: {
|
||||
...theme.colors,
|
||||
primary: "var(--brand-color)",
|
||||
|
||||
primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
|
||||
primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
|
||||
},
|
||||
})}
|
||||
styles={{
|
||||
option: (provided, state) => ({
|
||||
...provided,
|
||||
color: state.isSelected ? "var(--brand-text-color)" : "black",
|
||||
":active": {
|
||||
backgroundColor: state.isSelected ? "" : "var(--brand-color)",
|
||||
color: "var(--brand-text-color)",
|
||||
},
|
||||
}),
|
||||
}}
|
||||
components={{
|
||||
...components,
|
||||
IndicatorSeparator: () => null,
|
||||
Input: InputComponent,
|
||||
}}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Select;
|
||||
|
||||
export function UnstyledSelect<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
>({ ...props }: SelectProps<Option, IsMulti, Group>) {
|
||||
return (
|
||||
<ReactSelect
|
||||
{...props}
|
||||
isSearchable={false}
|
||||
theme={(theme) => ({ ...theme, borderRadius: 0, border: "none" })}
|
||||
components={{
|
||||
IndicatorSeparator: () => null,
|
||||
Input: InputComponent,
|
||||
}}
|
||||
styles={{
|
||||
container: (provided) => ({
|
||||
...provided,
|
||||
width: "100%",
|
||||
}),
|
||||
control: (provided) => ({
|
||||
...provided,
|
||||
backgroundColor: " transparent",
|
||||
border: "none",
|
||||
boxShadow: "none",
|
||||
}),
|
||||
option: (provided, state) => ({
|
||||
...provided,
|
||||
color: state.isSelected ? "var(--brand-text-color)" : "black",
|
||||
":active": {
|
||||
backgroundColor: state.isSelected ? "" : "var(--brand-color)",
|
||||
color: "var(--brand-text-color)",
|
||||
},
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
display: "hidden",
|
||||
color: "black",
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
263
calcom/packages/ui/form/fields.tsx
Normal file
263
calcom/packages/ui/form/fields.tsx
Normal file
@@ -0,0 +1,263 @@
|
||||
import { useId } from "@radix-ui/react-id";
|
||||
import type { ReactElement, ReactNode, Ref } from "react";
|
||||
import React, { forwardRef } from "react";
|
||||
import type { FieldValues, SubmitHandler, UseFormReturn } from "react-hook-form";
|
||||
import { FormProvider, useFormContext } from "react-hook-form";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
import { Alert, showToast } from "../";
|
||||
|
||||
type InputProps = Omit<JSX.IntrinsicElements["input"], "name"> & { name: string };
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(props, ref) {
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
"border-default mt-1 block w-full rounded-sm border px-3 py-2 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-1 focus:ring-neutral-800 sm:text-sm",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export function Label(props: JSX.IntrinsicElements["label"]) {
|
||||
return (
|
||||
<label {...props} className={classNames("text-default block text-sm font-medium", props.className)}>
|
||||
{props.children}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function InputLeading(props: JSX.IntrinsicElements["div"]) {
|
||||
return (
|
||||
<span className="bg-muted border-default text-subtle inline-flex flex-shrink-0 items-center rounded-l-sm border border-r-0 px-3 sm:text-sm">
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
type InputFieldProps = {
|
||||
label?: ReactNode;
|
||||
hint?: ReactNode;
|
||||
addOnLeading?: ReactNode;
|
||||
} & React.ComponentProps<typeof Input> & {
|
||||
labelProps?: React.ComponentProps<typeof Label>;
|
||||
};
|
||||
|
||||
const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputField(props, ref) {
|
||||
const id = useId();
|
||||
const { t } = useLocale();
|
||||
const methods = useFormContext();
|
||||
const {
|
||||
label = t(props.name),
|
||||
labelProps,
|
||||
placeholder = t(`${props.name}_placeholder`) !== `${props.name}_placeholder`
|
||||
? t(`${props.name}_placeholder`)
|
||||
: "",
|
||||
className,
|
||||
addOnLeading,
|
||||
hint,
|
||||
...passThrough
|
||||
} = props;
|
||||
return (
|
||||
<div>
|
||||
{!!props.name && (
|
||||
<Label htmlFor={id} {...labelProps}>
|
||||
{label}
|
||||
</Label>
|
||||
)}
|
||||
{addOnLeading ? (
|
||||
<div className="mt-1 flex rounded-md shadow-sm">
|
||||
{addOnLeading}
|
||||
<Input
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
className={classNames("mt-0", props.addOnLeading && "rounded-l-none", className)}
|
||||
{...passThrough}
|
||||
ref={ref}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Input id={id} placeholder={placeholder} className={className} {...passThrough} ref={ref} />
|
||||
)}
|
||||
{hint}
|
||||
{methods?.formState?.errors[props.name]?.message && (
|
||||
<Alert
|
||||
className="mt-1"
|
||||
severity="error"
|
||||
message={<>{methods.formState.errors[props.name]?.message}</>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const TextField = forwardRef<HTMLInputElement, InputFieldProps>(function TextField(props, ref) {
|
||||
return <InputField ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
export const PasswordField = forwardRef<HTMLInputElement, InputFieldProps>(function PasswordField(
|
||||
props,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<InputField data-testid="password" type="password" placeholder="•••••••••••••" ref={ref} {...props} />
|
||||
);
|
||||
});
|
||||
|
||||
export const EmailInput = forwardRef<HTMLInputElement, InputFieldProps>(function EmailInput(props, ref) {
|
||||
return (
|
||||
<Input
|
||||
ref={ref}
|
||||
type="email"
|
||||
autoCapitalize="none"
|
||||
autoComplete="email"
|
||||
autoCorrect="off"
|
||||
inputMode="email"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const EmailField = forwardRef<HTMLInputElement, InputFieldProps>(function EmailField(props, ref) {
|
||||
return (
|
||||
<InputField
|
||||
ref={ref}
|
||||
type="email"
|
||||
autoCapitalize="none"
|
||||
autoComplete="email"
|
||||
autoCorrect="off"
|
||||
inputMode="email"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type TextAreaProps = Omit<JSX.IntrinsicElements["textarea"], "name"> & { name: string };
|
||||
|
||||
export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(function TextAreaInput(props, ref) {
|
||||
return (
|
||||
<textarea
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={classNames(
|
||||
"border-default block w-full rounded-sm shadow-sm focus:border-neutral-900 focus:ring-neutral-900 sm:text-sm",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type TextAreaFieldProps = {
|
||||
label?: ReactNode;
|
||||
} & React.ComponentProps<typeof TextArea> & {
|
||||
labelProps?: React.ComponentProps<typeof Label>;
|
||||
};
|
||||
|
||||
export const TextAreaField = forwardRef<HTMLTextAreaElement, TextAreaFieldProps>(function TextField(
|
||||
props,
|
||||
ref
|
||||
) {
|
||||
const id = useId();
|
||||
const { t } = useLocale();
|
||||
const methods = useFormContext();
|
||||
const {
|
||||
label = t(props.name as string),
|
||||
labelProps,
|
||||
placeholder = t(`${props.name}_placeholder`) !== `${props.name}_placeholder`
|
||||
? t(`${props.name}_placeholder`)
|
||||
: "",
|
||||
...passThrough
|
||||
} = props;
|
||||
return (
|
||||
<div>
|
||||
{!!props.name && (
|
||||
<Label htmlFor={id} {...labelProps}>
|
||||
{label}
|
||||
</Label>
|
||||
)}
|
||||
<TextArea ref={ref} placeholder={placeholder} {...passThrough} />
|
||||
{methods?.formState?.errors[props.name]?.message && (
|
||||
<Alert
|
||||
className="mt-1"
|
||||
severity="error"
|
||||
message={<>{methods.formState.errors[props.name]?.message}</>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
type FormProps<T extends object> = { form: UseFormReturn<T>; handleSubmit: SubmitHandler<T> } & Omit<
|
||||
JSX.IntrinsicElements["form"],
|
||||
"onSubmit"
|
||||
>;
|
||||
|
||||
const PlainForm = <T extends FieldValues>(props: FormProps<T>, ref: Ref<HTMLFormElement>) => {
|
||||
const { form, handleSubmit, ...passThrough } = props;
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form
|
||||
ref={ref}
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
form
|
||||
.handleSubmit(handleSubmit)(event)
|
||||
.catch((err) => {
|
||||
showToast(`${getErrorFromUnknown(err).message}`, "error");
|
||||
});
|
||||
}}
|
||||
{...passThrough}>
|
||||
{
|
||||
/* @see https://react-hook-form.com/advanced-usage/#SmartFormComponent */
|
||||
React.Children.map(props.children, (child) => {
|
||||
return typeof child !== "string" &&
|
||||
typeof child !== "number" &&
|
||||
typeof child !== "boolean" &&
|
||||
child &&
|
||||
"props" in child &&
|
||||
child.props.name
|
||||
? React.createElement(child.type, {
|
||||
...{
|
||||
...child.props,
|
||||
register: form.register,
|
||||
key: child.props.name,
|
||||
},
|
||||
})
|
||||
: child;
|
||||
})
|
||||
}
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const Form = forwardRef(PlainForm) as <T extends FieldValues>(
|
||||
p: FormProps<T> & { ref?: Ref<HTMLFormElement> }
|
||||
) => ReactElement;
|
||||
|
||||
export function FieldsetLegend(props: JSX.IntrinsicElements["legend"]) {
|
||||
return (
|
||||
<legend {...props} className={classNames("text-default text-sm font-medium", props.className)}>
|
||||
{props.children}
|
||||
</legend>
|
||||
);
|
||||
}
|
||||
|
||||
export function InputGroupBox(props: JSX.IntrinsicElements["div"]) {
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={classNames("bg-default border-default space-y-2 rounded-sm border p-2", props.className)}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
60
calcom/packages/ui/form/radio-area/Radio.tsx
Normal file
60
calcom/packages/ui/form/radio-area/Radio.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import type { ReactNode } from "react";
|
||||
import React from "react";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
|
||||
export const Group = (props: RadioGroupPrimitive.RadioGroupProps & { children: ReactNode }) => (
|
||||
<RadioGroupPrimitive.Root {...props}>{props.children}</RadioGroupPrimitive.Root>
|
||||
);
|
||||
export const Radio = (props: RadioGroupPrimitive.RadioGroupItemProps & { children: ReactNode }) => (
|
||||
<RadioGroupPrimitive.Item
|
||||
{...props}
|
||||
className={classNames(
|
||||
"hover:bg-subtle border-default dark:checked:bg-brand-default dark:bg-darkgray-100 dark:hover:bg-subtle dark:checked:hover:bg-brand-default focus:ring-brand-default hover:border-emphasis me-1.5 mt-0.5 h-4 w-4 flex-shrink-0 rounded-full border text-[--cal-brand] focus:border-0 focus:ring-1",
|
||||
props.disabled && "opacity-60"
|
||||
)}>
|
||||
{props.children}
|
||||
</RadioGroupPrimitive.Item>
|
||||
);
|
||||
export const Indicator = ({ disabled }: { disabled?: boolean }) => (
|
||||
<RadioGroupPrimitive.Indicator
|
||||
className={classNames(
|
||||
"after:bg-default dark:after:bg-brand-accent relative flex h-full w-full items-center justify-center rounded-full bg-black after:h-[6px] after:w-[6px] after:rounded-full after:content-['']",
|
||||
disabled ? "after:bg-muted" : "bg-brand-default"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
export const Label = (props: JSX.IntrinsicElements["label"] & { disabled?: boolean }) => (
|
||||
<label
|
||||
{...props}
|
||||
className={classNames(
|
||||
"text-emphasis ms-2 w-full text-sm font-medium leading-5",
|
||||
props.disabled && "text-subtle"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
export const RadioField = ({
|
||||
label,
|
||||
disabled,
|
||||
id,
|
||||
value,
|
||||
className,
|
||||
}: {
|
||||
label: string | ReactNode;
|
||||
disabled?: boolean;
|
||||
id: string;
|
||||
value: string;
|
||||
className?: string;
|
||||
}) => (
|
||||
<div className={classNames("flex items-start", className)}>
|
||||
<Radio value={value} disabled={disabled} id={id}>
|
||||
<Indicator disabled={disabled} />
|
||||
</Radio>
|
||||
<Label htmlFor={id} disabled={disabled}>
|
||||
{label}
|
||||
</Label>
|
||||
</div>
|
||||
);
|
||||
59
calcom/packages/ui/form/radio-area/RadioAreaGroup.tsx
Normal file
59
calcom/packages/ui/form/radio-area/RadioAreaGroup.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { useId } from "@radix-ui/react-id";
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
|
||||
type RadioAreaProps = RadioGroupPrimitive.RadioGroupItemProps & {
|
||||
children: ReactNode;
|
||||
classNames?: { container?: string };
|
||||
};
|
||||
|
||||
const RadioArea = ({ children, className, classNames: innerClassNames, ...props }: RadioAreaProps) => {
|
||||
const radioAreaId = useId();
|
||||
const id = props.id ?? radioAreaId;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle [&:has(input:checked)]:border-emphasis relative flex items-start rounded-md border ",
|
||||
className
|
||||
)}>
|
||||
<RadioGroupPrimitive.Item
|
||||
id={id}
|
||||
{...props}
|
||||
className={classNames(
|
||||
"hover:bg-subtle disabled:hover:bg-default border-default focus:ring-emphasis absolute left-3 top-[0.9rem] mt-0.5 h-4 w-4 flex-shrink-0 rounded-full border focus:ring-2 disabled:cursor-not-allowed",
|
||||
props.disabled && "opacity-60"
|
||||
)}>
|
||||
<RadioGroupPrimitive.Indicator
|
||||
className={classNames(
|
||||
"after:bg-default dark:after:bg-inverted relative flex h-full w-full items-center justify-center rounded-full bg-black after:h-[6px] after:w-[6px] after:rounded-full after:content-['']",
|
||||
props.disabled ? "after:bg-muted" : "bg-black"
|
||||
)}
|
||||
/>
|
||||
</RadioGroupPrimitive.Item>
|
||||
<label htmlFor={id} className={classNames("text-default p-4 pl-10 pt-3", innerClassNames?.container)}>
|
||||
{children}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RadioAreaGroup = ({
|
||||
children,
|
||||
className,
|
||||
onValueChange,
|
||||
...passThroughProps
|
||||
}: RadioGroupPrimitive.RadioGroupProps) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root className={className} onValueChange={onValueChange} {...passThroughProps}>
|
||||
{children}
|
||||
</RadioGroupPrimitive.Root>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = RadioArea;
|
||||
const Group = RadioAreaGroup;
|
||||
|
||||
export { RadioArea, RadioAreaGroup, Item, Group };
|
||||
2
calcom/packages/ui/form/radio-area/index.ts
Normal file
2
calcom/packages/ui/form/radio-area/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * as RadioGroup from "./RadioAreaGroup";
|
||||
export { Group, Indicator, Label, Radio, RadioField } from "./Radio";
|
||||
Reference in New Issue
Block a user