fix(input): label position and styles (#1901)

* fix(input): label position and styles

* fix(textarea): invalid state
This commit is contained in:
Junior Garcia 2023-11-06 15:45:05 -03:00 committed by GitHub
parent a3cbb5997b
commit 6a6d426b10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 104 additions and 53 deletions

View File

@ -0,0 +1,8 @@
---
"@nextui-org/select": patch
"@nextui-org/input": patch
"@nextui-org/theme": patch
---
- Label position changed for Select and Input, this avoids to break the layout when having long descriptions and no placeholder
- Input/Select styles improved, label opacity removed

View File

@ -16,8 +16,7 @@ const Input = forwardRef<"input", InputProps>((props, ref) => {
endContent,
labelPlacement,
hasHelper,
isLabelOutside,
isLabelOutsideAsPlaceholder,
isOutsideLeft,
shouldLabelBeOutside,
errorMessage,
getBaseProps,
@ -86,7 +85,7 @@ const Input = forwardRef<"input", InputProps>((props, ref) => {
return (
<div {...getMainWrapperProps()}>
<div {...getInputWrapperProps()}>
{isLabelOutsideAsPlaceholder ? labelContent : null}
{!isOutsideLeft ? labelContent : null}
{innerWrapper}
</div>
{helperWrapper}
@ -107,7 +106,6 @@ const Input = forwardRef<"input", InputProps>((props, ref) => {
labelPlacement,
helperWrapper,
shouldLabelBeOutside,
isLabelOutsideAsPlaceholder,
labelContent,
innerWrapper,
errorMessage,
@ -120,7 +118,7 @@ const Input = forwardRef<"input", InputProps>((props, ref) => {
return (
<Component {...getBaseProps()}>
{isLabelOutside ? labelContent : null}
{isOutsideLeft ? labelContent : null}
{mainWrapper}
</Component>
);

View File

@ -195,6 +195,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
? (!domRef.current.value || domRef.current.value === "" || !inputValue || inputValue === "") &&
hasPlaceholder
: false;
const isOutsideLeft = labelPlacement === "outside-left";
const hasStartContent = !!startContent;
const isLabelOutside = shouldLabelBeOutside
@ -445,6 +446,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
hasHelper,
hasStartContent,
isLabelOutside,
isOutsideLeft,
isLabelOutsideAsPlaceholder,
shouldLabelBeOutside,
shouldLabelBeInside,

View File

@ -150,17 +150,27 @@ const LabelPlacementTemplate = (args) => (
<div className="flex flex-col gap-3">
<h3>Without placeholder</h3>
<div className="w-full max-w-xl flex flex-row items-end gap-4">
<Input {...args} />
<Input {...args} labelPlacement="outside" />
<Input {...args} labelPlacement="outside-left" />
<Input {...args} description="inside" />
<Input {...args} description="outside" labelPlacement="outside" />
<Input {...args} description="outside-left" labelPlacement="outside-left" />
</div>
</div>
<div className="flex flex-col gap-3">
<h3>With placeholder</h3>
<div className="w-full max-w-xl flex flex-row items-end gap-4">
<Input {...args} placeholder="Enter your email" />
<Input {...args} labelPlacement="outside" placeholder="Enter your email" />
<Input {...args} labelPlacement="outside-left" placeholder="Enter your email" />
<Input {...args} description="inside" placeholder="Enter your email" />
<Input
{...args}
description="outside"
labelPlacement="outside"
placeholder="Enter your email"
/>
<Input
{...args}
description="outside-left"
labelPlacement="outside-left"
placeholder="Enter your email"
/>
</div>
</div>
</div>

View File

@ -28,6 +28,7 @@ function Select<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLSelectE
endContent,
placeholder,
renderValue,
isOutsideLeft,
disableAnimation,
getBaseProps,
getLabelProps,
@ -37,7 +38,6 @@ function Select<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLSelectE
getPopoverProps,
getSpinnerProps,
getMainWrapperProps,
shouldLabelBeOutside,
getInnerWrapperProps,
getHiddenSelectProps,
getHelperWrapperProps,
@ -115,10 +115,10 @@ function Select<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLSelectE
return (
<div {...getBaseProps()}>
<HiddenSelect {...getHiddenSelectProps()} />
{shouldLabelBeOutside ? labelContent : null}
{isOutsideLeft ? labelContent : null}
<div {...getMainWrapperProps()}>
<Component {...getTriggerProps()}>
{!shouldLabelBeOutside ? labelContent : null}
{!isOutsideLeft ? labelContent : null}
<div {...getInnerWrapperProps()}>
{startContent}
<span {...getValueProps()}>

View File

@ -262,6 +262,7 @@ export function useSelect<T extends object>(originalProps: UseSelectProps<T>) {
labelPlacement === "outside-left" ||
(labelPlacement === "outside" && (hasPlaceholder || !!originalProps.isMultiline));
const shouldLabelBeInside = labelPlacement === "inside";
const isOutsideLeft = labelPlacement === "outside-left";
const isFilled =
state.isOpen ||
hasPlaceholder ||
@ -582,6 +583,7 @@ export function useSelect<T extends object>(originalProps: UseSelectProps<T>) {
hasPlaceholder,
renderValue,
selectionMode,
isOutsideLeft,
disableAnimation,
shouldLabelBeOutside,
shouldLabelBeInside,

View File

@ -33,19 +33,15 @@ const input = tv({
"block",
"text-small",
"text-foreground-500",
"opacity-70",
"group-data-[filled-within=true]:opacity-100",
],
mainWrapper: "h-full",
inputWrapper:
"relative w-full inline-flex tap-highlight-transparent flex-row items-center shadow-sm px-3 gap-3",
innerWrapper: "inline-flex w-full items-center h-full box-border",
input: [
"opacity-70",
"w-full font-normal bg-transparent !outline-none placeholder:text-foreground-500 focus-visible:outline-none",
"data-[has-start-content=true]:ps-1.5",
"data-[has-end-content=true]:pe-1.5",
"group-data-[has-value=true]:opacity-100",
],
clearButton: [
"p-2",
@ -77,8 +73,6 @@ const input = tv({
"data-[hover=true]:bg-default-200",
"group-data-[focus=true]:bg-default-100",
],
input: "placeholder:text-default-foreground",
label: "text-default-foreground",
},
faded: {
inputWrapper: [
@ -87,8 +81,7 @@ const input = tv({
"border-default-200",
"data-[hover=true]:border-default-400",
],
input: "placeholder:text-default-foreground",
label: "text-default-foreground",
value: "group-data-[has-value=true]:text-default-foreground",
},
bordered: {
inputWrapper: [
@ -97,8 +90,6 @@ const input = tv({
"data-[hover=true]:border-default-400",
"group-data-[focus=true]:border-default-foreground",
],
input: "placeholder:text-foreground",
label: "text-foreground",
},
underlined: {
inputWrapper: [
@ -124,14 +115,11 @@ const input = tv({
"group-data-[focus=true]:after:w-full",
],
innerWrapper: "pb-1",
input: "placeholder:text-foreground",
label: "text-foreground",
label: "group-data-[filled-within=true]:text-foreground",
},
},
color: {
default: {
label: "group-data-[filled-within=true]:opacity-80",
},
default: {},
primary: {},
secondary: {},
success: {},
@ -175,7 +163,6 @@ const input = tv({
},
labelPlacement: {
outside: {
label: "text-foreground pb-1.5",
mainWrapper: "flex flex-col",
},
"outside-left": {
@ -211,7 +198,7 @@ const input = tv({
isInvalid: {
true: {
label: "!text-danger",
input: "placeholder:text-danger text-danger",
input: "!placeholder:text-danger !text-danger",
},
},
isRequired: {
@ -257,6 +244,13 @@ const input = tv({
},
compoundVariants: [
// flat & color
{
variant: "flat",
color: "default",
class: {
input: "group-data-[has-value=true]:text-default-foreground",
},
},
{
variant: "flat",
color: "primary",
@ -380,6 +374,13 @@ const input = tv({
},
},
// underlined & color
{
variant: "underlined",
color: "default",
class: {
input: "group-data-[has-value=true]:text-foreground",
},
},
{
variant: "underlined",
color: "primary",
@ -461,6 +462,22 @@ const input = tv({
label: "text-danger",
},
},
// labelPlacement=inside & default
{
labelPlacement: "inside",
color: "default",
class: {
label: "group-data-[filled-within=true]:text-default-600",
},
},
// labelPlacement=outside & default
{
labelPlacement: "outside",
color: "default",
class: {
label: "group-data-[filled-within=true]:text-foreground",
},
},
// radius-full & size
{
radius: "full",

View File

@ -15,8 +15,6 @@ const select = tv({
"text-small",
"text-foreground-500",
"pointer-events-none",
"opacity-70",
"group-data-[filled=true]:opacity-100",
],
mainWrapper: "w-full flex flex-col",
trigger:
@ -25,13 +23,7 @@ const select = tv({
"inline-flex h-full w-[calc(100%_-_theme(spacing.unit-6))] min-h-unit-4 items-center gap-1.5 box-border",
selectorIcon: "absolute right-3 w-unit-4 h-unit-4",
spinner: "absolute right-3",
value: [
"font-normal",
"w-full",
"text-left",
"opacity-70",
"group-data-[has-value=true]:opacity-100",
],
value: ["text-foreground-500", "font-normal", "w-full", "text-left"],
listboxWrapper: "scroll-py-6 max-h-64 w-full",
listbox: "",
popoverContent: "w-full p-1 overflow-hidden",
@ -47,8 +39,6 @@ const select = tv({
"data-[hover=true]:bg-default-200",
"group-data-[focus=true]:bg-default-100",
],
label: "text-default-foreground",
value: "placeholder:text-default-foreground",
},
faded: {
trigger: [
@ -57,8 +47,7 @@ const select = tv({
"border-default-200",
"data-[hover=true]:border-default-400",
],
label: "text-default-foreground",
value: "placeholder:text-default-foreground",
value: "group-data-[has-value=true]:text-default-foreground",
},
bordered: {
trigger: [
@ -69,8 +58,6 @@ const select = tv({
"data-[focus=true]:border-default-foreground",
"data-[focus=true]:border-default-foreground",
],
label: "text-foreground",
value: "placeholder:text-foreground",
},
underlined: {
trigger: [
@ -96,14 +83,11 @@ const select = tv({
"data-[open=true]:after:w-full",
"data-[focus=true]:after:w-full",
],
label: "text-foreground",
value: "placeholder:text-foreground",
label: "group-data-[filled=true]:text-foreground",
},
},
color: {
default: {
label: "group-data-[filled=true]:opacity-80",
},
default: {},
primary: {},
secondary: {},
success: {},
@ -145,11 +129,10 @@ const select = tv({
labelPlacement: {
outside: {
base: "flex flex-col",
label: "text-foreground pb-1.5",
},
"outside-left": {
base: "flex-row items-center flex-nowrap items-start",
label: "relative text-foreground pr-2",
label: "relative pr-2 text-foreground",
},
inside: {
label: "text-tiny cursor-pointer",
@ -228,6 +211,13 @@ const select = tv({
},
compoundVariants: [
// flat & color
{
variant: "flat",
color: "default",
class: {
value: "group-data-[has-value=true]:text-default-foreground",
},
},
{
variant: "flat",
color: "primary",
@ -343,6 +333,14 @@ const select = tv({
},
},
// underlined & color
// underlined & color
{
variant: "underlined",
color: "default",
class: {
value: "group-data-[has-value=true]:text-foreground",
},
},
{
variant: "underlined",
color: "primary",
@ -424,6 +422,22 @@ const select = tv({
label: "text-danger",
},
},
// labelPlacement=outside & default
{
labelPlacement: "inside",
color: "default",
class: {
label: "group-data-[filled=true]:text-default-600",
},
},
// labelPlacement=outside & default
{
labelPlacement: "outside",
color: "default",
class: {
label: "group-data-[filled=true]:text-foreground",
},
},
// radius-full & size
{
radius: "full",