mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-25 14:05:03 +00:00
feat(ui): implemented new input component
This commit is contained in:
66
frontend/src/components/v2/Input/Input.stories.tsx
Normal file
66
frontend/src/components/v2/Input/Input.stories.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { faEye, faMailBulk } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { Input } from './Input';
|
||||
|
||||
const meta: Meta<typeof Input> = {
|
||||
title: 'Components/Input',
|
||||
component: Input,
|
||||
tags: ['v2'],
|
||||
argTypes: {
|
||||
isRounded: {
|
||||
defaultValue: true,
|
||||
type: 'boolean'
|
||||
},
|
||||
placeholder: {
|
||||
defaultValue: 'Type anything',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Input>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Filled: Story = {
|
||||
args: {}
|
||||
};
|
||||
|
||||
export const Outline: Story = {
|
||||
args: {
|
||||
variant: 'outline'
|
||||
}
|
||||
};
|
||||
|
||||
export const Plain: Story = {
|
||||
args: {
|
||||
variant: 'plain'
|
||||
}
|
||||
};
|
||||
|
||||
export const Error: Story = {
|
||||
args: {
|
||||
isError: true
|
||||
}
|
||||
};
|
||||
|
||||
export const AutoWidth: Story = {
|
||||
args: {
|
||||
isFullWidth: false,
|
||||
className: 'w-auto'
|
||||
}
|
||||
};
|
||||
|
||||
export const RightIcon: Story = {
|
||||
args: {
|
||||
rightIcon: <FontAwesomeIcon icon={faEye} />
|
||||
}
|
||||
};
|
||||
|
||||
export const LeftIcon: Story = {
|
||||
args: {
|
||||
leftIcon: <FontAwesomeIcon icon={faMailBulk} />
|
||||
}
|
||||
};
|
102
frontend/src/components/v2/Input/Input.tsx
Normal file
102
frontend/src/components/v2/Input/Input.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode } from 'react';
|
||||
import { cva, VariantProps } from 'cva';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
type Props = {
|
||||
placeholder?: string;
|
||||
isFullWidth?: boolean;
|
||||
isRequired?: boolean;
|
||||
leftIcon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
};
|
||||
|
||||
const inputVariants = cva(
|
||||
'input w-full py-2 text-gray-400 placeholder-gray-500 placeholder-opacity-50',
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: ['text-xs'],
|
||||
sm: ['text-sm'],
|
||||
md: ['text-md'],
|
||||
lg: ['text-lg']
|
||||
},
|
||||
isRounded: {
|
||||
true: ['rounded-md'],
|
||||
false: ''
|
||||
},
|
||||
variant: {
|
||||
filled: ['bg-bunker-800', 'text-gray-400'],
|
||||
outline: ['bg-transparent'],
|
||||
plain: 'bg-transparent outline-none'
|
||||
},
|
||||
isError: {
|
||||
true: 'focus:ring-red/50 placeholder-red-300',
|
||||
false: 'focus:ring-primary/50'
|
||||
}
|
||||
},
|
||||
compoundVariants: []
|
||||
}
|
||||
);
|
||||
|
||||
const inputParentContainerVariants = cva('inline-flex items-center border relative', {
|
||||
variants: {
|
||||
isRounded: {
|
||||
true: ['rounded-md'],
|
||||
false: ''
|
||||
},
|
||||
isError: {
|
||||
true: 'border-red',
|
||||
false: 'border-mineshaft-400'
|
||||
},
|
||||
isFullWidth: {
|
||||
true: 'w-full',
|
||||
false: ''
|
||||
},
|
||||
variant: {
|
||||
filled: ['bg-bunker-800', 'text-gray-400'],
|
||||
outline: ['bg-transparent'],
|
||||
plain: 'border-none'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> &
|
||||
VariantProps<typeof inputVariants> &
|
||||
Props;
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
isRounded = true,
|
||||
isFullWidth = true,
|
||||
isError = false,
|
||||
isRequired,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
variant = 'filled',
|
||||
size = 'md',
|
||||
...props
|
||||
},
|
||||
ref
|
||||
): JSX.Element => {
|
||||
return (
|
||||
<div className={inputParentContainerVariants({ isRounded, isError, isFullWidth, variant })}>
|
||||
{leftIcon && <span className="absolute left-0 ml-2">{leftIcon}</span>}
|
||||
<input
|
||||
{...props}
|
||||
required={isRequired}
|
||||
ref={ref}
|
||||
className={twMerge(
|
||||
leftIcon ? 'pl-9' : 'pl-4',
|
||||
rightIcon ? 'pr-9' : 'pr-4',
|
||||
inputVariants({ className, isError, size, isRounded, variant })
|
||||
)}
|
||||
/>
|
||||
{rightIcon && <span className="absolute right-0 mr-2">{rightIcon}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Input.displayName = 'Input';
|
2
frontend/src/components/v2/Input/index.tsx
Normal file
2
frontend/src/components/v2/Input/index.tsx
Normal file
@ -0,0 +1,2 @@
|
||||
export type { InputProps } from './Input';
|
||||
export { Input } from './Input';
|
Reference in New Issue
Block a user