Skip to content

Commit

Permalink
feat: adding the support for labelPlacement globally
Browse files Browse the repository at this point in the history
  • Loading branch information
macci001 committed Dec 12, 2024
1 parent 77206bc commit 5fffcdd
Show file tree
Hide file tree
Showing 14 changed files with 99 additions and 34 deletions.
10 changes: 10 additions & 0 deletions .changeset/chilly-dancers-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@nextui-org/date-picker": patch
"@nextui-org/date-input": patch
"@nextui-org/select": patch
"@nextui-org/input": patch
"@nextui-org/system": patch
"@nextui-org/theme": patch
---

Adding support for global labelPlacement prop.(ENG-1694)
9 changes: 9 additions & 0 deletions apps/docs/content/docs/api-references/nextui-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ interface AppProviderProps {

<Spacer y={2}/>

`labelPlacement`

- **Description**: Determines the position where label should appear, such as inside, outside or outside-left of the component.
- **Type**: `string` | `undefined`
- **Possible Values**: `inside` | `outside` | `outside-left` | `undefined`
- **Default**: `undefined`

<Spacer y={2}/>

`disableAnimation`

- **Description**: Disables animations globally. This will also avoid `framer-motion` features to be loaded in the bundle which can potentially reduce the bundle size.
Expand Down
12 changes: 6 additions & 6 deletions packages/components/date-input/src/use-date-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,15 @@ export function useDateInput<T extends DateValue>(originalProps: UseDateInputPro
const isInvalid = isInvalidProp || ariaIsInvalid;

const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
if (
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
!props.label
) {
const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return originalProps.labelPlacement ?? "inside";
}, [originalProps.labelPlacement, props.label]);
return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

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

Expand Down
12 changes: 6 additions & 6 deletions packages/components/date-input/src/use-time-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ export function useTimeInput<T extends TimeValue>(originalProps: UseTimeInputPro
const baseStyles = clsx(classNames?.base, className);

const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
if (
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
!props.label
) {
const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return originalProps.labelPlacement ?? "inside";
}, [originalProps.labelPlacement, props.label]);
return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

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

Expand Down
15 changes: 13 additions & 2 deletions packages/components/date-picker/src/use-date-picker-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {ValueBase} from "@react-types/shared";

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

const labelPlacement = useMemo<TimeInputProps["labelPlacement"]>(() => {
const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

const timeInputProps = {
...userTimeInputProps,
size: "sm",
labelPlacement: "outside-left",
labelPlacement,
label: userTimeInputProps?.label || stringFormatter.format("time"),
placeholderValue: timePlaceholder,
hourCycle: props.hourCycle,
Expand Down
17 changes: 9 additions & 8 deletions packages/components/date-picker/src/use-date-range-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export type UseDateRangePickerProps<T extends DateValue> = Props<T> & AriaDateRa

export function useDateRangePicker<T extends DateValue>({
as,
label,
isInvalid: isInvalidProp,
description,
startContent,
Expand Down Expand Up @@ -144,15 +145,15 @@ export function useDateRangePicker<T extends DateValue>({
const showTimeField = !!timeGranularity;

const labelPlacement = useMemo<DateInputVariantProps["labelPlacement"]>(() => {
if (
(!originalProps.labelPlacement || originalProps.labelPlacement === "inside") &&
!originalProps.label
) {
const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return originalProps.labelPlacement ?? "inside";
}, [originalProps.labelPlacement, originalProps.label]);
return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

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

Expand Down Expand Up @@ -395,7 +396,7 @@ export function useDateRangePicker<T extends DateValue>({
const getDateInputGroupProps = () => {
return {
as,
label: originalProps.label,
label,
description,
endContent,
errorMessage,
Expand Down Expand Up @@ -423,7 +424,7 @@ export function useDateRangePicker<T extends DateValue>({

return {
state,
label: originalProps.label,
label,
slots,
classNames,
startContent,
Expand Down
20 changes: 16 additions & 4 deletions packages/components/input/src/use-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,15 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
if (isFileTypeInput) {
// if `labelPlacement` is not defined, choose `outside` instead
// since the default value `inside` is not supported in file input
if (!originalProps.labelPlacement) return "outside";
if (!originalProps.labelPlacement) {
if (globalContext?.labelPlacement === "inside") {
warn(
"Input with file type doesn't support inside label. Hence, labelPlacement from NextUI Provider is not applicable. Converting to outside ...",
);
}

return "outside";
}

// throw a warning if `labelPlacement` is `inside`
// and change it to `outside`
Expand All @@ -241,12 +249,16 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
return "outside";
}
}
if ((!originalProps.labelPlacement || originalProps.labelPlacement === "inside") && !label) {

const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return originalProps.labelPlacement ?? "inside";
}, [originalProps.labelPlacement, label]);
return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

const errorMessage =
typeof props.errorMessage === "function"
Expand Down
9 changes: 6 additions & 3 deletions packages/components/select/src/use-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,12 +344,15 @@ export function useSelect<T extends object>(originalProps: UseSelectProps<T>) {
const {isHovered, hoverProps} = useHover({isDisabled: originalProps.isDisabled});

const labelPlacement = useMemo<SelectVariantProps["labelPlacement"]>(() => {
if ((!originalProps.labelPlacement || originalProps.labelPlacement === "inside") && !label) {
const labelPlacement =
originalProps.labelPlacement ?? globalContext?.labelPlacement ?? "inside";

if (labelPlacement === "inside" && !label) {
return "outside";
}

return originalProps.labelPlacement ?? "inside";
}, [originalProps.labelPlacement, label]);
return labelPlacement;
}, [originalProps.labelPlacement, globalContext?.labelPlacement, label]);

const hasPlaceholder = !!placeholder;
const shouldLabelBeOutside =
Expand Down
7 changes: 7 additions & 0 deletions packages/core/system/src/provider-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export type ProviderContextProps = {
* @default false
*/
disableAnimation?: boolean;
/**
* Position where the label should appear.
*
* @default undefined
*/
labelPlacement?: "inside" | "outside" | "outside-left" | undefined;
/**
/**
* Whether to disable the ripple effect in the whole application.
* If `disableAnimation` is set to `true`, this prop will be ignored.
Expand Down
3 changes: 3 additions & 0 deletions packages/core/system/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
reducedMotion = "never",
validationBehavior,
locale = "en-US",
labelPlacement,
// if minDate / maxDate are not specified in `defaultDates`
// then they will be set in `use-date-input.ts` or `use-calendar-base.ts`
defaultDates,
Expand Down Expand Up @@ -85,6 +86,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
disableAnimation,
disableRipple,
validationBehavior,
labelPlacement,
};
}, [
createCalendar,
Expand All @@ -93,6 +95,7 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
disableAnimation,
disableRipple,
validationBehavior,
labelPlacement,
]);

return (
Expand Down
1 change: 0 additions & 1 deletion packages/core/theme/src/components/date-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ const dateInput = tv({
color: "default",
size: "md",
fullWidth: true,
labelPlacement: "inside",
isDisabled: false,
},
compoundVariants: [
Expand Down
1 change: 0 additions & 1 deletion packages/core/theme/src/components/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ const input = tv({
color: "default",
size: "md",
fullWidth: true,
labelPlacement: "inside",
isDisabled: false,
isMultiline: false,
},
Expand Down
1 change: 0 additions & 1 deletion packages/core/theme/src/components/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ const select = tv({
variant: "flat",
color: "default",
size: "md",
labelPlacement: "inside",
fullWidth: true,
isDisabled: false,
isMultiline: false,
Expand Down
16 changes: 14 additions & 2 deletions packages/storybook/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import "./style.css";
import {withStrictModeSwitcher} from "./addons/react-strict-mode";

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

return (
<NextUIProvider locale={locale} disableAnimation={disableAnimation}>
<NextUIProvider locale={locale} disableAnimation={disableAnimation} labelPlacement={labelPlacement}>
<div className="bg-dark" lang={locale} dir={direction}>
<Story />
</div>
Expand Down Expand Up @@ -127,6 +127,18 @@ const globalTypes: Preview["globalTypes"] = {
],
},
},
labelPlacement: {
name: "Label Placement",
description: "Position of label.",
toolbar: {
icon: "component",
items: [
{value: "inside", title: "Inside"},
{value: "outside", title: "Outside"},
{value: "outside-left", title: "Outside Left"},
],
},
}
};

const preview: Preview = {
Expand Down

0 comments on commit 5fffcdd

Please sign in to comment.