Skip to content

Commit

Permalink
feat: input widgets (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
totraev authored Dec 12, 2024
1 parent 1246647 commit c612eae
Show file tree
Hide file tree
Showing 33 changed files with 660 additions and 195 deletions.
5 changes: 5 additions & 0 deletions .changeset/mighty-lobsters-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@babylonlabs-io/bbn-core-ui": minor
---

add input widgets
23 changes: 23 additions & 0 deletions src/components/Form/FormControl.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.bbn-form-control {
@apply flex flex-col;

&-title {
@apply mb-2 flex items-center text-sm text-primary-light;
}

&-hint {
@apply mt-1 text-sm;

&.bbn-form-control-hint-error {
@apply text-error-main;
}

&.bbn-form-control-hint-warning {
@apply text-warning-main;
}

&.bbn-form-control-hint-success {
@apply text-success-main;
}
}
}
38 changes: 38 additions & 0 deletions src/components/Form/FormControl.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from "@storybook/react";

import { FormControl } from "./FormControl";
import { Input } from "./Input";

const meta: Meta<typeof FormControl> = {
component: FormControl,
tags: ["autodocs"],
};

export default meta;

type Story = StoryObj<typeof FormControl>;

export const Default: Story = {
args: {
label: "Label",
children: <Input defaultValue="Hello" />,
hint: "Some random hint",
},
};

export const WithoutLabel: Story = {
args: {
children: <Input defaultValue="Hello Error" state="error" />,
hint: "Some random error",
state: "error",
},
};

export const WithError: Story = {
args: {
label: "Label",
children: <Input defaultValue="Hello Error" state="error" />,
hint: "Some random error",
state: "error",
},
};
27 changes: 27 additions & 0 deletions src/components/Form/FormControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { type PropsWithChildren } from "react";
import { twJoin } from "tailwind-merge";
import "./FormControl.css";

export interface FormControlProps extends PropsWithChildren {
label?: string | JSX.Element;
hint?: string | JSX.Element;
state?: "default" | "error" | "warning" | "success";
className?: string;
}

export function FormControl({ children, label, hint, state = "default", className }: FormControlProps) {
return (
<div className={twJoin("bbn-form-control", className)}>
{label ? (
<label className="bbn-form-control-label">
<div className="bbn-form-control-title">{label}</div>
{children}
</label>
) : (
children
)}

{hint && <div className={twJoin("bbn-form-control-hint", `bbn-form-control-hint-${state}`)}>{hint}</div>}
</div>
);
}
36 changes: 16 additions & 20 deletions src/components/Form/Input.css
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
.bbn-input {
@apply relative flex flex-col;
@apply flex items-center rounded border border-primary-light/20 bg-secondary-contrast px-4 py-2 text-primary-light transition-colors;

&-wrapper {
@apply flex items-center rounded border border-primary-light/20 bg-secondary-contrast px-4 py-2 text-primary-light transition-colors;

&:focus-within {
@apply border-primary-light;
}
&:focus-within {
@apply border-primary-light;
}

&.bbn-input-error {
@apply border-error-main;
}
&.bbn-input-error {
@apply border-error-main;
}

&.bbn-input-warning {
@apply border-warning-main;
}
&.bbn-input-warning {
@apply border-warning-main;
}

&.bbn-input-success {
@apply border-success-main;
}
&.bbn-input-success {
@apply border-success-main;
}

&.bbn-input-disabled {
@apply opacity-50 pointer-events-none;
}
&.bbn-input-disabled {
@apply pointer-events-none opacity-50;
}

&-field {
Expand All @@ -36,4 +32,4 @@
&-prefix {
@apply mr-2 flex items-center text-primary-light/50;
}
}
}
10 changes: 1 addition & 9 deletions src/components/Form/Input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ export const Disabled: Story = {
},
};

export const WithError: Story = {
args: {
placeholder: "Input with error",
state: "error",
hint: "This field is required",
},
};

export const WithSuffix: Story = {
args: {
placeholder: "Search...",
Expand Down Expand Up @@ -63,7 +55,7 @@ export const LoadingWithInteraction: Story = {
<Input
placeholder="Click search to see loading"
suffix={
<button onClick={handleSearch} disabled={isLoading} className="h-5 w-5">
<button onClick={handleSearch} disabled={isLoading} className="size-5">
{isLoading ? <Loader size={20} /> : <RiSearchLine size={20} />}
</button>
}
Expand Down
21 changes: 6 additions & 15 deletions src/components/Form/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
import { forwardRef, type DetailedHTMLProps, type InputHTMLAttributes, type ReactNode } from "react";
import { twJoin } from "tailwind-merge";
import "./Input.css";
import { FormControl } from "./components/FormControl";

export interface InputProps
extends Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "prefix" | "suffix"> {
className?: string;
wrapperClassName?: string;
prefix?: ReactNode;
suffix?: ReactNode;
disabled?: boolean;
state?: "default" | "error" | "warning";
hint?: string;
label?: string;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(
(
{ className, wrapperClassName, prefix, suffix, disabled = false, state = "default", hint, label, ...props },
ref,
) => {
({ className, prefix, suffix, disabled = false, state = "default", ...props }, ref) => {
return (
<FormControl label={label} hint={hint} state={state} wrapperClassName={wrapperClassName}>
<div className={twJoin("bbn-input-wrapper", disabled && "bbn-input-disabled", `bbn-input-${state}`)}>
{prefix && <div className="bbn-input-prefix">{prefix}</div>}
<input ref={ref} className={twJoin("bbn-input-field", className)} disabled={disabled} {...props} />
{suffix && <div className="bbn-input-suffix">{suffix}</div>}
</div>
</FormControl>
<div className={twJoin("bbn-input", disabled && "bbn-input-disabled", `bbn-input-${state}`)}>
{prefix && <div className="bbn-input-prefix">{prefix}</div>}
<input ref={ref} className={twJoin("bbn-input-field", className)} disabled={disabled} {...props} />
{suffix && <div className="bbn-input-suffix">{suffix}</div>}
</div>
);
},
);
Expand Down
1 change: 0 additions & 1 deletion src/components/Form/Select.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.bbn-select {
@apply relative flex w-full cursor-pointer items-center justify-between rounded border border-primary-light bg-secondary-contrast px-4 py-2 text-sm text-primary-light outline-none transition-all;
width: var(--select-width, auto);

&:focus-visible {
@apply border-primary-light ring-1 ring-primary-light;
Expand Down
50 changes: 0 additions & 50 deletions src/components/Form/Select.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,43 +33,6 @@ export const Controlled: Story = {
},
};

export const CenterAligned: Story = {
args: {
options,
placeholder: "Select status",
},
render: (args) => (
<div className="flex w-full justify-center gap-1">
<Select {...args} />
</div>
),
};

export const LeftAligned: Story = {
args: {
placeholder: "Select status",
options,
},
render: (args) => (
<div className="flex w-full">
<Select {...args} />
</div>
),
};

export const RightAligned: Story = {
args: {
placeholder: "Select status",
options,
},
render: (args) => (
<div className="flex w-full justify-between gap-1">
<div className="w-1/2">Title</div>
<Select {...args} />
</div>
),
};

export const Disabled: Story = {
args: {
options,
Expand All @@ -85,16 +48,3 @@ export const CustomSelectedDisplay: Story = {
renderSelectedOption: (option) => `Showing ${option.value}`,
},
};

export const WithError: Story = {
args: {
options: [
{ value: "active", label: "Active" },
{ value: "inactive", label: "Inactive" },
{ value: "pending", label: "Pending" },
],
placeholder: "Select status",
state: "error",
hint: "",
},
};
Loading

0 comments on commit c612eae

Please sign in to comment.