Skip to content

Commit

Permalink
add input and select components (#50)
Browse files Browse the repository at this point in the history
* add input and select components

* add popover and update props

* fix: refactor popover and select components

* feat: refactor input component

---------

Co-authored-by: David Totraev <[email protected]>
  • Loading branch information
jeremy-babylonlabs and 0xDazzer authored Dec 10, 2024
1 parent 4cbb008 commit ed0911d
Show file tree
Hide file tree
Showing 28 changed files with 711 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-pumpkins-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@babylonlabs-io/bbn-core-ui": minor
---

add input and select components
48 changes: 46 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,9 @@
"extends": [
"plugin:storybook/recommended"
]
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"react-popper": "^2.3.0"
}
}
2 changes: 1 addition & 1 deletion src/components/Dialog/Dialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Dialog, DialogFooter, DialogBody, DialogHeader } from "./index";

import { ScrollLocker } from "@/context/Dialog.context";
import { Button } from "@/components/Button";
import { Checkbox } from "@/components/Input";
import { Checkbox } from "@/components/Form";
import { Text } from "@/components/Text";
import { Heading } from "@/index";

Expand Down
2 changes: 1 addition & 1 deletion src/components/Dialog/MobileDialog.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MobileDialog, DialogFooter, DialogBody, DialogHeader } from "./index";

import { ScrollLocker } from "@/context/Dialog.context";
import { Button } from "@/components/Button";
import { Checkbox } from "@/components/Input";
import { Checkbox } from "@/components/Form";
import { Text } from "@/components/Text";

const meta: Meta<typeof MobileDialog> = {
Expand Down
File renamed without changes.
File renamed without changes.
55 changes: 55 additions & 0 deletions src/components/Form/Input.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.bbn-input {
@apply relative flex flex-col;

&-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;
}

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

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

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

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

&-field {
@apply w-full bg-transparent text-sm outline-none placeholder:text-primary-light/50;
}

&-suffix {
@apply ml-2 flex items-center text-primary-light/50;
}

&-prefix {
@apply mr-2 flex items-center text-primary-light/50;
}

&-state-text {
@apply mt-1 text-sm;
}

&-state-text-error {
@apply text-error-main;
}

&-state-text-warning {
@apply text-warning-main;
}

&-state-text-success {
@apply text-success-main;
}
}
74 changes: 74 additions & 0 deletions src/components/Form/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { Meta, StoryObj } from "@storybook/react";
import { RiSearchLine } from "react-icons/ri";
import { useState } from "react";

import { Loader } from "@/components/Loader";

import { Input } from "./Input";

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

export default meta;

type Story = StoryObj<typeof Input>;

export const Default: Story = {
args: {
placeholder: "Default input",
},
};

export const Disabled: Story = {
args: {
placeholder: "Disabled input",
disabled: true,
},
};

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

export const WithSuffix: Story = {
args: {
placeholder: "Search...",
suffix: <RiSearchLine size={20} />,
},
};

export const WithPrefix: Story = {
args: {
placeholder: "Amount",
prefix: "$",
},
};

export const LoadingWithInteraction: Story = {
render: () => {
const [isLoading, setIsLoading] = useState(false);

const handleSearch = () => {
setIsLoading(true);
setTimeout(() => setIsLoading(false), 2000);
};

return (
<Input
placeholder="Click search to see loading"
suffix={
<button onClick={handleSearch} disabled={isLoading} className="h-5 w-5">
{isLoading ? <Loader size={20} /> : <RiSearchLine size={20} />}
</button>
}
disabled={isLoading}
/>
);
},
};
33 changes: 33 additions & 0 deletions src/components/Form/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { forwardRef, type DetailedHTMLProps, type InputHTMLAttributes, type ReactNode } from "react";
import { twJoin } from "tailwind-merge";
import "./Input.css";

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";
stateText?: string;
}

export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, wrapperClassName, prefix, suffix, disabled = false, state = "default", stateText, ...props }, ref) => {
return (
<div className={twJoin("bbn-input", 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>
{stateText && (
<span className={twJoin("bbn-input-state-text", `bbn-input-state-text-${state}`)}>{stateText}</span>
)}
</div>
);
},
);

Input.displayName = "Input";
File renamed without changes.
File renamed without changes.
36 changes: 36 additions & 0 deletions src/components/Form/Select.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.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;
}

&-icon {
@apply ml-2 text-primary-light transition-transform;

&-open {
@apply rotate-180;
}
}

&-menu {
@apply z-50 max-h-60 overflow-auto rounded border border-primary-light/20 bg-secondary-contrast py-1 shadow-lg;
}

&-option {
@apply cursor-pointer px-4 py-3 text-sm text-primary-light transition-colors hover:bg-primary-light/10;

&-selected {
@apply border-primary-light bg-primary-light/10;
}

&-highlighted {
@apply bg-primary-light/20;
}
}

&-disabled {
@apply pointer-events-none cursor-not-allowed opacity-50;
}
}
Loading

0 comments on commit ed0911d

Please sign in to comment.