-
Notifications
You must be signed in to change notification settings - Fork 544
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: new input component * fix: add type table instead of harcoded types
- Loading branch information
1 parent
5429532
commit 59f642d
Showing
6 changed files
with
325 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
--- | ||
title: Input | ||
description: A text input field component with different states, validations, and icon support. | ||
--- | ||
|
||
import { Input } from "@unkey/ui" | ||
import { RenderComponentWithSnippet } from "@/app/components/render" | ||
import { | ||
InputDefaultVariant, | ||
InputSuccessVariant, | ||
InputWarningVariant, | ||
InputErrorVariant, | ||
InputDisabledVariant, | ||
InputWithDefaultValue, | ||
InputWithPasswordToggle, | ||
InputWithBothIcons | ||
} from "./input/input.variants.tsx" | ||
|
||
# Input | ||
|
||
A versatile input component that supports various states, validations, and icon placements. Use it to collect user input with appropriate visual feedback and enhanced usability through icons. | ||
|
||
## Default | ||
|
||
The default input style with neutral colors. Can include optional icons for better visual context. | ||
|
||
<InputDefaultVariant /> | ||
|
||
## States | ||
|
||
Input components can reflect different states through visual styling: | ||
|
||
### Success State | ||
|
||
Use the success state to indicate valid input or successful validation. The checkmark icon provides immediate visual feedback. | ||
|
||
<InputSuccessVariant /> | ||
|
||
### Warning State | ||
|
||
The warning state can be used to show potential issues that don't prevent form submission. The alert icon draws attention to the warning state. | ||
|
||
<InputWarningVariant /> | ||
|
||
### Error State | ||
|
||
Use the error state to indicate invalid input that needs correction. The alert icon emphasizes the error state. | ||
|
||
<InputErrorVariant /> | ||
|
||
### Disabled State | ||
|
||
Use the disabled state when user interaction should be prevented, such as during form submission or when the input depends on other conditions. | ||
|
||
<InputDisabledVariant /> | ||
|
||
### With Default Value State | ||
|
||
Input pre-populated with an initial value that users can modify or build upon. | ||
|
||
<InputWithDefaultValue /> | ||
|
||
## Interactive Elements | ||
|
||
### Password Toggle | ||
|
||
Example of an input with a clickable icon to toggle password visibility. | ||
|
||
<InputWithPasswordToggle /> | ||
|
||
### Search with Icons | ||
|
||
Example of an input with both leading and trailing icons for enhanced functionality. | ||
|
||
<InputWithBothIcons /> | ||
|
||
## Props | ||
|
||
The Input component accepts all standard HTML input attributes plus the following: | ||
|
||
<AutoTypeTable | ||
name="InputProps" | ||
type={`import { ReactNode } from "react" | ||
export interface InputProps { | ||
/** Determines the visual style and state of the input */ | ||
variant?: 'default' | 'success' | 'warning' | 'error'; | ||
/** Optional icon component to display on the left side */ | ||
leftIcon?: ReactNode; | ||
/** Optional icon component to display on the right side */ | ||
rightIcon?: ReactNode; | ||
/** Additional classes to apply to the wrapper div */ | ||
wrapperClassName?: string; | ||
}`} | ||
/> | ||
|
||
|
||
## Icon Guidelines | ||
|
||
When using icons with the Input component: | ||
|
||
- Icons should be sized appropriately (recommended: 16x16px using `h-4 w-4` classes) | ||
- Icons inherit colors based on the input's variant state | ||
- Interactive icons (like password toggle) should be wrapped in buttons | ||
- Avoid using too many icons which might clutter the interface | ||
- Left icons are typically used for input type indication (search, email, etc.) | ||
- Right icons are commonly used for interactive elements (password toggle, clear button, etc.) |
89 changes: 89 additions & 0 deletions
89
apps/engineering/content/design/components/input/input.variants.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
"use client"; | ||
|
||
import { RenderComponentWithSnippet } from "@/app/components/render"; | ||
import { InputSearch } from "@unkey/icons"; | ||
import { Input } from "@unkey/ui"; | ||
import { EyeIcon, EyeOff } from "lucide-react"; | ||
import { useState } from "react"; | ||
|
||
export const InputDefaultVariant = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input placeholder="All we have to decide is what to do with the time that is given us" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputSuccessVariant = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input variant="success" placeholder="Not all those who wander are lost" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputWarningVariant = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input variant="warning" placeholder="It's a dangerous business, going out your door" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputErrorVariant = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input variant="error" placeholder="One Ring to rule them all, One Ring to find them" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputDisabledVariant = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input disabled placeholder="Even the smallest person can change the course of the future" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputWithDefaultValue = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input defaultValue="Speak friend and enter" placeholder="The password is mellon" /> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputWithPasswordToggle = () => { | ||
const [showPassword, setShowPassword] = useState(false); | ||
|
||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input | ||
type={showPassword ? "text" : "password"} | ||
placeholder="Speak friend and enter" | ||
rightIcon={ | ||
<button | ||
type="button" | ||
onClick={() => setShowPassword(!showPassword)} | ||
className="focus:outline-none" | ||
> | ||
{showPassword ? <EyeIcon className="h-4 w-4" /> : <EyeOff className="h-4 w-4" />} | ||
</button> | ||
} | ||
/> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; | ||
|
||
export const InputWithBothIcons = () => { | ||
return ( | ||
<RenderComponentWithSnippet> | ||
<Input | ||
placeholder="Search in emails" | ||
leftIcon={<InputSearch className="h-4 w-4" />} | ||
rightIcon={<InputSearch className="h-4 w-4" />} | ||
/> | ||
</RenderComponentWithSnippet> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { type VariantProps, cva } from "class-variance-authority"; | ||
import * as React from "react"; | ||
import { cn } from "../lib/utils"; | ||
|
||
const inputVariants = cva( | ||
"flex min-h-9 w-full rounded-lg text-[13px] leading-5 transition-colors duration-300 disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-gray-7 text-gray-12", | ||
{ | ||
variants: { | ||
variant: { | ||
default: [ | ||
"border border-gray-5 hover:border-gray-8 bg-gray-2", | ||
"focus:border focus:border-accent-12 focus:ring-4 focus:ring-gray-5 focus-visible:outline-none focus:ring-offset-0", | ||
"[&:not(:placeholder-shown)]:focus:ring-0", | ||
], | ||
success: [ | ||
"border border-success-6 hover:border-success-7 bg-gray-2", | ||
"focus:border-success-8 focus:ring-2 focus:ring-success-2 focus-visible:outline-none", | ||
"[&:not(:placeholder-shown)]:focus:ring-success-3", | ||
], | ||
warning: [ | ||
"border border-warning-6 hover:border-warning-7 bg-gray-2", | ||
"focus:border-warning-8 focus:ring-2 focus:ring-warning-2 focus-visible:outline-none", | ||
"[&:not(:placeholder-shown)]:focus:ring-warning-3", | ||
], | ||
error: [ | ||
"border border-error-6 hover:border-error-7 bg-gray-2", | ||
"focus:border-error-8 focus:ring-2 focus:ring-error-2 focus-visible:outline-none", | ||
"[&:not(:placeholder-shown)]:focus:ring-error-3", | ||
], | ||
}, | ||
}, | ||
defaultVariants: { | ||
variant: "default", | ||
}, | ||
}, | ||
); | ||
|
||
const inputWrapperVariants = cva("relative flex items-center w-full", { | ||
variants: { | ||
variant: { | ||
default: "text-gray-11", | ||
success: "text-success-11", | ||
warning: "text-warning-11", | ||
error: "text-error-11", | ||
}, | ||
}, | ||
defaultVariants: { | ||
variant: "default", | ||
}, | ||
}); | ||
|
||
export interface InputProps | ||
extends React.InputHTMLAttributes<HTMLInputElement>, | ||
VariantProps<typeof inputVariants> { | ||
leftIcon?: React.ReactNode; | ||
rightIcon?: React.ReactNode; | ||
wrapperClassName?: string; | ||
} | ||
|
||
export const Input = React.forwardRef<HTMLInputElement, InputProps>( | ||
({ className, variant, type, leftIcon, rightIcon, wrapperClassName, ...props }, ref) => { | ||
return ( | ||
<div className={cn(inputWrapperVariants({ variant }), wrapperClassName)}> | ||
{leftIcon && ( | ||
<div className="absolute left-3 flex items-center pointer-events-none">{leftIcon}</div> | ||
)} | ||
<input | ||
type={type} | ||
className={cn( | ||
inputVariants({ variant, className }), | ||
"px-3 py-2", | ||
leftIcon && "pl-9", | ||
rightIcon && "pr-9", | ||
)} | ||
ref={ref} | ||
{...props} | ||
/> | ||
{rightIcon && <div className="absolute right-3 flex items-center">{rightIcon}</div>} | ||
</div> | ||
); | ||
}, | ||
); | ||
|
||
Input.displayName = "Input"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters