InputField with suffix select (#5683)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
co-authored by
Peer Richelsen
parent
798a4b0090
commit
ae9f66c9ef
@@ -13,5 +13,6 @@ export {
|
||||
TextArea,
|
||||
TextAreaField,
|
||||
TextField,
|
||||
InputFieldWithSelect,
|
||||
} from "./inputs/Input";
|
||||
export { Label } from "./inputs/Label";
|
||||
|
||||
@@ -6,19 +6,23 @@ import classNames from "@calcom/lib/classNames";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
import { Alert, showToast, Skeleton, Tooltip } from "../../..";
|
||||
import { Alert, showToast, Skeleton, Tooltip, UnstyledSelect } from "../../..";
|
||||
import { HintsOrErrors } from "./HintOrErrors";
|
||||
import { Label } from "./Label";
|
||||
|
||||
type InputProps = JSX.IntrinsicElements["input"];
|
||||
type InputProps = JSX.IntrinsicElements["input"] & { isFullWidth?: boolean };
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(props, ref) {
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
{ isFullWidth = true, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
"mb-2 block h-9 w-full rounded-md border border-gray-300 py-2 px-3 text-sm placeholder:text-gray-400 hover:border-gray-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-1",
|
||||
"mb-2 block h-9 rounded-md border border-gray-300 py-2 px-3 text-sm placeholder:text-gray-400 hover:border-gray-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-1",
|
||||
isFullWidth && "w-full",
|
||||
props.className
|
||||
)}
|
||||
/>
|
||||
@@ -39,6 +43,9 @@ type InputFieldProps = {
|
||||
hintErrors?: string[];
|
||||
addOnLeading?: ReactNode;
|
||||
addOnSuffix?: ReactNode;
|
||||
inputIsFullWidth?: boolean;
|
||||
/**@Default paddingX 12px */
|
||||
addOnPadding?: number;
|
||||
addOnFilled?: boolean;
|
||||
error?: string;
|
||||
labelSrOnly?: boolean;
|
||||
@@ -54,15 +61,13 @@ type AddonProps = {
|
||||
isFilled?: boolean;
|
||||
className?: string;
|
||||
error?: boolean;
|
||||
paddingX?: number;
|
||||
};
|
||||
|
||||
const Addon = ({ isFilled, children, className, error }: AddonProps) => (
|
||||
const Addon = ({ isFilled, children, className, error, paddingX = 12 }: AddonProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"addon-wrapper h-9 border border-gray-300 px-3",
|
||||
isFilled && "bg-gray-100",
|
||||
className
|
||||
)}>
|
||||
style={{ padding: `0 ${paddingX}px` }}
|
||||
className={classNames("addon-wrapper h-9 border border-gray-300", isFilled && "bg-gray-100", className)}>
|
||||
<div className={classNames("flex h-full flex-col justify-center text-sm", error && "text-red-900")}>
|
||||
<span className="whitespace-nowrap py-2.5">{children}</span>
|
||||
</div>
|
||||
@@ -83,6 +88,8 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
|
||||
addOnLeading,
|
||||
addOnSuffix,
|
||||
addOnFilled = true,
|
||||
addOnPadding,
|
||||
inputIsFullWidth,
|
||||
hint,
|
||||
hintErrors,
|
||||
labelSrOnly,
|
||||
@@ -107,13 +114,14 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
|
||||
{addOnLeading || addOnSuffix ? (
|
||||
<div className="relative mb-1 flex items-center rounded-md focus-within:outline-none focus-within:ring-2 focus-within:ring-neutral-800 focus-within:ring-offset-1">
|
||||
{addOnLeading && (
|
||||
<Addon isFilled={addOnFilled} className="rounded-l-md border-r-0">
|
||||
<Addon isFilled={addOnFilled} className="rounded-l-md border-r-0" paddingX={addOnPadding}>
|
||||
{addOnLeading}
|
||||
</Addon>
|
||||
)}
|
||||
<Input
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
isFullWidth={inputIsFullWidth}
|
||||
className={classNames(
|
||||
className,
|
||||
addOnLeading && "rounded-l-none",
|
||||
@@ -124,13 +132,20 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
|
||||
ref={ref}
|
||||
/>
|
||||
{addOnSuffix && (
|
||||
<Addon isFilled={addOnFilled} className="rounded-r-md border-l-0">
|
||||
<Addon isFilled={addOnFilled} className="rounded-r-md border-l-0" paddingX={addOnPadding}>
|
||||
{addOnSuffix}
|
||||
</Addon>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Input id={id} placeholder={placeholder} className={className} {...passThrough} ref={ref} />
|
||||
<Input
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
className={className}
|
||||
{...passThrough}
|
||||
ref={ref}
|
||||
isFullWidth={inputIsFullWidth}
|
||||
/>
|
||||
)}
|
||||
<HintsOrErrors hintErrors={hintErrors} fieldName={name} t={t} />
|
||||
{hint && <div className="text-gray mt-2 flex items-center text-sm text-gray-700">{hint}</div>}
|
||||
@@ -319,3 +334,18 @@ export function InputGroupBox(props: JSX.IntrinsicElements["div"]) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const InputFieldWithSelect = forwardRef<
|
||||
HTMLInputElement,
|
||||
InputFieldProps & { selectProps: typeof UnstyledSelect }
|
||||
>(function EmailField(props, ref) {
|
||||
return (
|
||||
<InputField
|
||||
ref={ref}
|
||||
{...props}
|
||||
inputIsFullWidth={false}
|
||||
addOnPadding={0}
|
||||
addOnSuffix={<UnstyledSelect {...props.selectProps} />}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
|
||||
import { Examples, Example, Note, Title,CustomArgsTable, VariantRow,VariantsTable} from '@calcom/storybook/components'
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Icon,Select,UnstyledSelect,InputFieldWithSelect } from "@calcom/ui";
|
||||
|
||||
import {InputField} from "./Input"
|
||||
|
||||
@@ -35,6 +35,9 @@ Text fields allow users to input and edit text into the product. Usually appear
|
||||
<Example title="Suffix">
|
||||
<InputField placeholder="Suffic" addOnSuffix={<>Suffix</>}/>
|
||||
</Example>
|
||||
<Example title="Suffix With Select">
|
||||
<InputFieldWithSelect placeholder="Suffix" selectProps={{options:[{value:"TEXT",label:"Text"}]}}/>
|
||||
</Example>
|
||||
<Example title="Focused">
|
||||
<InputField placeholder="Focused" className="sb-pseudo--focus"/>
|
||||
</Example>
|
||||
|
||||
@@ -22,6 +22,7 @@ export {
|
||||
TextArea,
|
||||
TextAreaField,
|
||||
TextField,
|
||||
InputFieldWithSelect,
|
||||
} from "./form";
|
||||
export { TopBanner } from "./top-banner";
|
||||
export type { TopBannerProps } from "./top-banner";
|
||||
|
||||
@@ -61,3 +61,45 @@ function Select<
|
||||
}
|
||||
|
||||
export default Select;
|
||||
|
||||
export function UnstyledSelect<
|
||||
Option,
|
||||
IsMulti extends boolean = false,
|
||||
Group extends GroupBase<Option> = GroupBase<Option>
|
||||
>({ className, ...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",
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export {
|
||||
Input,
|
||||
InputField,
|
||||
InputGroupBox,
|
||||
InputFieldWithSelect,
|
||||
InputLeading,
|
||||
Label,
|
||||
PasswordField,
|
||||
@@ -30,7 +31,7 @@ export { default as CheckboxField } from "./components/form/checkbox/Checkbox";
|
||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||
export { default as AddressInput } from "./form/AddressInputLazy";
|
||||
export { default as PhoneInput } from "./form/PhoneInputLazy";
|
||||
export { default as Select } from "./form/Select";
|
||||
export { default as Select, UnstyledSelect } from "./form/Select";
|
||||
export { BadgeCheckIcon, ClipboardCopyIcon, Icon, ShieldCheckIcon, StarIconSolid } from "./Icon";
|
||||
export { default as Loader } from "./Loader";
|
||||
export { default as Shell, ShellMain } from "./Shell";
|
||||
|
||||
Reference in New Issue
Block a user