Skip to content

Commit 4457d0e

Browse files
committed
feat: adding the support for labelPlacement globally
1 parent 77206bc commit 4457d0e

File tree

14 files changed

+98
-34
lines changed

14 files changed

+98
-34
lines changed

.changeset/chilly-dancers-switch.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@nextui-org/date-picker": patch
3+
"@nextui-org/date-input": patch
4+
"@nextui-org/select": patch
5+
"@nextui-org/input": patch
6+
"@nextui-org/system": patch
7+
"@nextui-org/theme": patch
8+
---
9+
10+
Adding support for global labelPlacement prop.(ENG-1694)

apps/docs/content/docs/api-references/nextui-provider.mdx

+8
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ interface AppProviderProps {
142142

143143
<Spacer y={2}/>
144144

145+
`labelPlacement`
146+
147+
- **Description**: Determines where the label appears, such as inside, outside or outside-left of the component.
148+
- **Type**: `"inside"` | `"outside"` | `"outside-left"` | `undefined`
149+
- **Default**: `undefined`
150+
151+
<Spacer y={2}/>
152+
145153
`disableAnimation`
146154

147155
- **Description**: Disables animations globally. This will also avoid `framer-motion` features to be loaded in the bundle which can potentially reduce the bundle size.

packages/components/date-input/src/use-date-input.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,15 @@ export function useDateInput<T extends DateValue>(originalProps: UseDateInputPro
192192
const isInvalid = isInvalidProp || ariaIsInvalid;
193193

194194
const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
195-
if (
196-
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
197-
!props.label
198-
) {
195+
const labelPlacement =
196+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
197+
198+
if (labelPlacement === "inside" && !label) {
199199
return "outside";
200200
}
201201

202-
return originalProps.labelPlacement ?? "inside";
203-
}, [originalProps.labelPlacement, props.label]);
202+
return labelPlacement;
203+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
204204

205205
const shouldLabelBeOutside = labelPlacement === "outside" || labelPlacement === "outside-left";
206206

packages/components/date-input/src/use-time-input.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,15 @@ export function useTimeInput<T extends TimeValue>(originalProps: UseTimeInputPro
134134
const baseStyles = clsx(classNames?.base, className);
135135

136136
const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
137-
if (
138-
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
139-
!props.label
140-
) {
137+
const labelPlacement =
138+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
139+
140+
if (labelPlacement === "inside" && !label) {
141141
return "outside";
142142
}
143143

144-
return originalProps.labelPlacement ?? "inside";
145-
}, [originalProps.labelPlacement, props.label]);
144+
return labelPlacement;
145+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
146146

147147
const shouldLabelBeOutside = labelPlacement === "outside" || labelPlacement === "outside-left";
148148

packages/components/date-picker/src/use-date-picker-base.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {ValueBase} from "@react-types/shared";
99

1010
import {dataAttr} from "@nextui-org/shared-utils";
1111
import {dateInput, DatePickerVariantProps} from "@nextui-org/theme";
12-
import {useCallback} from "react";
12+
import {useCallback, useMemo} from "react";
1313
import {HTMLNextUIProps, mapPropsVariants, useProviderContext} from "@nextui-org/system";
1414
import {mergeProps} from "@react-aria/utils";
1515
import {useDOMRef} from "@nextui-org/react-utils";
@@ -234,10 +234,21 @@ export function useDatePickerBase<T extends DateValue>(originalProps: UseDatePic
234234
"data-invalid": dataAttr(originalProps?.isInvalid),
235235
} as DateInputProps;
236236

237+
const labelPlacement = useMemo<TimeInputProps["labelPlacement"]>(() => {
238+
const labelPlacement =
239+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
240+
241+
if (labelPlacement === "inside" && !label) {
242+
return "outside";
243+
}
244+
245+
return labelPlacement;
246+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
247+
237248
const timeInputProps = {
238249
...userTimeInputProps,
239250
size: "sm",
240-
labelPlacement: "outside-left",
251+
labelPlacement,
241252
label: userTimeInputProps?.label || stringFormatter.format("time"),
242253
placeholderValue: timePlaceholder,
243254
hourCycle: props.hourCycle,

packages/components/date-picker/src/use-date-range-picker.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export type UseDateRangePickerProps<T extends DateValue> = Props<T> & AriaDateRa
6060

6161
export function useDateRangePicker<T extends DateValue>({
6262
as,
63+
label,
6364
isInvalid: isInvalidProp,
6465
description,
6566
startContent,
@@ -144,15 +145,15 @@ export function useDateRangePicker<T extends DateValue>({
144145
const showTimeField = !!timeGranularity;
145146

146147
const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
147-
if (
148-
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
149-
!originalProps.label
150-
) {
148+
const labelPlacement =
149+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
150+
151+
if (labelPlacement === "inside" && !label) {
151152
return "outside";
152153
}
153154

154-
return originalProps.labelPlacement ?? "inside";
155-
}, [originalProps.labelPlacement, originalProps.label]);
155+
return labelPlacement;
156+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
156157

157158
const shouldLabelBeOutside = labelPlacement === "outside" || labelPlacement === "outside-left";
158159

@@ -395,7 +396,7 @@ export function useDateRangePicker<T extends DateValue>({
395396
const getDateInputGroupProps = () => {
396397
return {
397398
as,
398-
label: originalProps.label,
399+
label,
399400
description,
400401
endContent,
401402
errorMessage,
@@ -423,7 +424,7 @@ export function useDateRangePicker<T extends DateValue>({
423424

424425
return {
425426
state,
426-
label: originalProps.label,
427+
label,
427428
slots,
428429
classNames,
429430
startContent,

packages/components/input/src/use-input.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,15 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
231231
if (isFileTypeInput) {
232232
// if `labelPlacement` is not defined, choose `outside` instead
233233
// since the default value `inside` is not supported in file input
234-
if (!originalProps.labelPlacement) return "outside";
234+
if (!originalProps.labelPlacement) {
235+
if (globalContext?.labelPlacement === "inside") {
236+
warn(
237+
"Input with file type doesn't support inside label. Hence, labelPlacement from provider is not applicable. Converting to outside ...",
238+
);
239+
}
240+
241+
return "outside";
242+
}
235243

236244
// throw a warning if `labelPlacement` is `inside`
237245
// and change it to `outside`
@@ -241,12 +249,16 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
241249
return "outside";
242250
}
243251
}
244-
if ((!originalProps.labelPlacement || originalProps.labelPlacement === "inside") && !label) {
252+
253+
const labelPlacement =
254+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
255+
256+
if (labelPlacement === "inside" && !label) {
245257
return "outside";
246258
}
247259

248-
return originalProps.labelPlacement ?? "inside";
249-
}, [originalProps.labelPlacement, label]);
260+
return labelPlacement;
261+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
250262

251263
const errorMessage =
252264
typeof props.errorMessage === "function"

packages/components/select/src/use-select.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -344,12 +344,15 @@ export function useSelect<T extends object>(originalProps: UseSelectProps<T>) {
344344
const {isHovered, hoverProps} = useHover({isDisabled: originalProps.isDisabled});
345345

346346
const labelPlacement = useMemo<SelectVariantProps["labelPlacement"]>(() => {
347-
if ((!originalProps.labelPlacement || originalProps.labelPlacement === "inside") && !label) {
347+
const labelPlacement =
348+
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";
349+
350+
if (labelPlacement === "inside" && !label) {
348351
return "outside";
349352
}
350353

351-
return originalProps.labelPlacement ?? "inside";
352-
}, [originalProps.labelPlacement, label]);
354+
return labelPlacement;
355+
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);
353356

354357
const hasPlaceholder = !!placeholder;
355358
const shouldLabelBeOutside =

packages/core/system/src/provider-context.ts

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ export type ProviderContextProps = {
1111
* @default false
1212
*/
1313
disableAnimation?: boolean;
14+
/**
15+
* Position respect to the component where the label should be placed.
16+
*
17+
* @default undefined
18+
*/
19+
labelPlacement?: "inside" | "outside" | "outside-left" | undefined;
20+
/**
1421
/**
1522
* Whether to disable the ripple effect in the whole application.
1623
* If `disableAnimation` is set to `true`, this prop will be ignored.

packages/core/system/src/provider.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
5858
reducedMotion = "never",
5959
validationBehavior,
6060
locale = "en-US",
61+
labelPlacement,
6162
// if minDate / maxDate are not specified in `defaultDates`
6263
// then they will be set in `use-date-input.ts` or `use-calendar-base.ts`
6364
defaultDates,
@@ -85,6 +86,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
8586
disableAnimation,
8687
disableRipple,
8788
validationBehavior,
89+
labelPlacement,
8890
};
8991
}, [
9092
createCalendar,
@@ -93,6 +95,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
9395
disableAnimation,
9496
disableRipple,
9597
validationBehavior,
98+
labelPlacement,
9699
]);
97100

98101
return (

packages/core/theme/src/components/date-input.ts

-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ const dateInput = tv({
217217
color: "default",
218218
size: "md",
219219
fullWidth: true,
220-
labelPlacement: "inside",
221220
isDisabled: false,
222221
},
223222
compoundVariants: [

packages/core/theme/src/components/input.ts

-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ const input = tv({
258258
color: "default",
259259
size: "md",
260260
fullWidth: true,
261-
labelPlacement: "inside",
262261
isDisabled: false,
263262
isMultiline: false,
264263
},

packages/core/theme/src/components/select.ts

-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ const select = tv({
209209
variant: "flat",
210210
color: "default",
211211
size: "md",
212-
labelPlacement: "inside",
213212
fullWidth: true,
214213
isDisabled: false,
215214
isMultiline: false,

packages/storybook/.storybook/preview.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import "./style.css";
77
import {withStrictModeSwitcher} from "./addons/react-strict-mode";
88

99
const decorators: Preview["decorators"] = [
10-
(Story, {globals: {locale, disableAnimation}}) => {
10+
(Story, {globals: {locale, disableAnimation, labelPlacement}}) => {
1111
const direction =
1212
// @ts-ignore
1313
locale && new Intl.Locale(locale)?.textInfo?.direction === "rtl" ? "rtl" : undefined;
1414

1515
return (
16-
<NextUIProvider locale={locale} disableAnimation={disableAnimation}>
16+
<NextUIProvider locale={locale} disableAnimation={disableAnimation} labelPlacement={labelPlacement}>
1717
<div className="bg-dark" lang={locale} dir={direction}>
1818
<Story />
1919
</div>
@@ -127,6 +127,18 @@ const globalTypes: Preview["globalTypes"] = {
127127
],
128128
},
129129
},
130+
labelPlacement: {
131+
name: "Label Placement",
132+
description: "Position of label placement.",
133+
toolbar: {
134+
icon: "component",
135+
items: [
136+
{value: "inside", title: "Inside"},
137+
{value: "outside", title: "Outside"},
138+
{value: "outside-left", title: "Outside Left"},
139+
],
140+
},
141+
}
130142
};
131143

132144
const preview: Preview = {

0 commit comments

Comments
 (0)