From e3afa4789a1ac0fa929b2acaca5bd9c520567ab8 Mon Sep 17 00:00:00 2001 From: Junior Garcia Date: Mon, 29 Apr 2024 17:34:01 -0300 Subject: [PATCH] Cn utility refactor (#2915) * refactor(core): cn utility adjusted and moved to the theme package * chore(root): changeset * fix(storybook): stories that used cn --- .changeset/few-oranges-jam.md | 7 +++++++ .../calendar/stories/calendar.stories.tsx | 2 +- .../calendar/stories/range-calendar.stories.tsx | 2 +- .../date-picker/stories/date-picker.stories.tsx | 2 +- .../stories/date-range-picker.stories.tsx | 2 +- .../pagination/stories/pagination.stories.tsx | 2 +- .../slider/stories/slider.stories.tsx | 2 +- packages/core/system-rsc/package.json | 3 +-- packages/core/system-rsc/src/extend-variants.js | 7 ++++--- packages/core/system-rsc/src/index.ts | 1 - packages/core/system-rsc/src/utils.ts | 6 ------ .../system-rsc/test-utils/slots-component.tsx | 5 +++-- packages/core/system/src/index.ts | 1 - packages/core/theme/package.json | 4 +++- packages/core/theme/src/utils/cn.ts | 17 +++++++++++++++++ packages/core/theme/src/utils/index.ts | 17 ++++++++++++++--- packages/core/theme/src/utils/tv.ts | 11 +++-------- .../core/theme/src/utils/tw-merge-config.ts | 16 ++++++++++++++++ pnpm-lock.yaml | 6 ++++++ 19 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 .changeset/few-oranges-jam.md create mode 100644 packages/core/theme/src/utils/cn.ts create mode 100644 packages/core/theme/src/utils/tw-merge-config.ts diff --git a/.changeset/few-oranges-jam.md b/.changeset/few-oranges-jam.md new file mode 100644 index 0000000000..4978deed5e --- /dev/null +++ b/.changeset/few-oranges-jam.md @@ -0,0 +1,7 @@ +--- +"@nextui-org/system": patch +"@nextui-org/system-rsc": patch +"@nextui-org/theme": patch +--- + +The `cn` utility was moved the `theme` package and updated to support NextUI custom classes. diff --git a/packages/components/calendar/stories/calendar.stories.tsx b/packages/components/calendar/stories/calendar.stories.tsx index e9a70b1c56..60b7081434 100644 --- a/packages/components/calendar/stories/calendar.stories.tsx +++ b/packages/components/calendar/stories/calendar.stories.tsx @@ -12,7 +12,7 @@ import { import {I18nProvider, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; import {Radio, RadioGroup} from "@nextui-org/radio"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {Calendar, CalendarProps, DateValue} from "../src"; diff --git a/packages/components/calendar/stories/range-calendar.stories.tsx b/packages/components/calendar/stories/range-calendar.stories.tsx index 2a4ed35373..3f844510e5 100644 --- a/packages/components/calendar/stories/range-calendar.stories.tsx +++ b/packages/components/calendar/stories/range-calendar.stories.tsx @@ -16,7 +16,7 @@ import { import {I18nProvider, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; import {Radio, RadioGroup} from "@nextui-org/radio"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {RangeCalendar, RangeCalendarProps} from "../src"; diff --git a/packages/components/date-picker/stories/date-picker.stories.tsx b/packages/components/date-picker/stories/date-picker.stories.tsx index 0db7446cd8..7f54cf00a0 100644 --- a/packages/components/date-picker/stories/date-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-picker.stories.tsx @@ -16,7 +16,7 @@ import { import {I18nProvider, useDateFormatter, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; import {Radio, RadioGroup} from "@nextui-org/radio"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {DatePicker, DatePickerProps} from "../src"; diff --git a/packages/components/date-picker/stories/date-range-picker.stories.tsx b/packages/components/date-picker/stories/date-range-picker.stories.tsx index 89464e22a6..3bdd30c97e 100644 --- a/packages/components/date-picker/stories/date-range-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-range-picker.stories.tsx @@ -18,7 +18,7 @@ import {DateValue} from "@react-types/datepicker"; import {I18nProvider, useDateFormatter, useLocale} from "@react-aria/i18n"; import {Button, ButtonGroup} from "@nextui-org/button"; import {Radio, RadioGroup} from "@nextui-org/radio"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {DateRangePicker, DateRangePickerProps} from "../src"; diff --git a/packages/components/pagination/stories/pagination.stories.tsx b/packages/components/pagination/stories/pagination.stories.tsx index 6d687d16b4..7c77132cd0 100644 --- a/packages/components/pagination/stories/pagination.stories.tsx +++ b/packages/components/pagination/stories/pagination.stories.tsx @@ -1,7 +1,7 @@ import React from "react"; import {Meta} from "@storybook/react"; import {button, pagination} from "@nextui-org/theme"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {ChevronIcon} from "@nextui-org/shared-icons"; import {Pagination, PaginationItemRenderProps, PaginationItemType, usePagination} from "../src"; diff --git a/packages/components/slider/stories/slider.stories.tsx b/packages/components/slider/stories/slider.stories.tsx index 1d7abee816..bcbf3b79dd 100644 --- a/packages/components/slider/stories/slider.stories.tsx +++ b/packages/components/slider/stories/slider.stories.tsx @@ -4,7 +4,7 @@ import {Meta} from "@storybook/react"; import {slider} from "@nextui-org/theme"; import {InfoIcon, VolumeHighBoldIcon, VolumeLowBoldIcon} from "@nextui-org/shared-icons"; import {Tooltip} from "@nextui-org/tooltip"; -import {cn} from "@nextui-org/system"; +import {cn} from "@nextui-org/theme"; import {Slider, SliderProps, SliderValue} from "../src"; diff --git a/packages/core/system-rsc/package.json b/packages/core/system-rsc/package.json index f95e1f4dcd..e4f0ae1731 100644 --- a/packages/core/system-rsc/package.json +++ b/packages/core/system-rsc/package.json @@ -35,8 +35,7 @@ }, "peerDependencies": { "react": ">=18", - "@nextui-org/theme": ">=2.1.0", - "tailwind-variants": ">=0.1.13" + "@nextui-org/theme": ">=2.1.0" }, "devDependencies": { "react": "^18.0.0", diff --git a/packages/core/system-rsc/src/extend-variants.js b/packages/core/system-rsc/src/extend-variants.js index 097445c1c2..fbf1905595 100644 --- a/packages/core/system-rsc/src/extend-variants.js +++ b/packages/core/system-rsc/src/extend-variants.js @@ -1,7 +1,8 @@ import * as React from "react"; import {tv} from "@nextui-org/theme"; +import clsx from "clsx"; -import {cn, mapPropsVariants} from "./utils"; +import {mapPropsVariants} from "./utils"; function getSlots(variants) { return variants @@ -65,7 +66,7 @@ function getClassNamesWithProps({ // if no slots, the result is a string if (!hasSlots) { - newProps.className = cn(result, props.className); + newProps.className = clsx(result, props.className); } // if has slots, the result is an object with keys as slots functions else { @@ -78,7 +79,7 @@ function getClassNamesWithProps({ }); Object.entries(props.classNames ?? {}).forEach(([key, value]) => { - classNames[key] = cn(classNames[key], value); + classNames[key] = clsx(classNames[key], value); }); } diff --git a/packages/core/system-rsc/src/index.ts b/packages/core/system-rsc/src/index.ts index e579de6e30..f5e4d229c6 100644 --- a/packages/core/system-rsc/src/index.ts +++ b/packages/core/system-rsc/src/index.ts @@ -15,7 +15,6 @@ export type { } from "./types"; export { - cn, forwardRef, toIterator, mapPropsVariants, diff --git a/packages/core/system-rsc/src/utils.ts b/packages/core/system-rsc/src/utils.ts index 151c1536ab..d0e07a7022 100644 --- a/packages/core/system-rsc/src/utils.ts +++ b/packages/core/system-rsc/src/utils.ts @@ -1,7 +1,6 @@ import type {As, RightJoinProps, PropsOf, InternalForwardRefRenderFunction} from "./types"; import * as React from "react"; -import clsx from "clsx"; import {forwardRef as baseForwardRef} from "react"; export function forwardRef< @@ -96,11 +95,6 @@ export const mapPropsVariantsWithCommon = < return [props, variants] as const; }; -/** - * Classnames utility - */ -export const cn = clsx; - /** * Checks if a component is a NextUI component. * @param component - The component to check. diff --git a/packages/core/system-rsc/test-utils/slots-component.tsx b/packages/core/system-rsc/test-utils/slots-component.tsx index 9e7f17f6cb..b2a4c3a90d 100644 --- a/packages/core/system-rsc/test-utils/slots-component.tsx +++ b/packages/core/system-rsc/test-utils/slots-component.tsx @@ -4,8 +4,9 @@ import React, {useMemo} from "react"; import {SlotsToClasses, tv, type VariantProps} from "@nextui-org/theme"; import {filterDOMProps, ReactRef, useDOMRef} from "@nextui-org/react-utils"; import {objectToDeps} from "@nextui-org/shared-utils"; +import clsx from "clsx"; -import {cn, forwardRef, mapPropsVariants} from "../src/utils"; +import {forwardRef, mapPropsVariants} from "../src/utils"; const card = tv({ slots: { @@ -171,7 +172,7 @@ export const Card = forwardRef<"div", CardProps>((originalProps, ref) => { const styles = useMemo(() => card({...variantProps}), [objectToDeps(variantProps)]); - const baseStyles = cn(classNames?.base, className); + const baseStyles = clsx(classNames?.base, className); const domRef = useDOMRef(ref); diff --git a/packages/core/system/src/index.ts b/packages/core/system/src/index.ts index 55e95baadc..81253fc1d9 100644 --- a/packages/core/system/src/index.ts +++ b/packages/core/system/src/index.ts @@ -18,7 +18,6 @@ export type { } from "@nextui-org/system-rsc"; export { - cn, forwardRef, toIterator, mapPropsVariants, diff --git a/packages/core/theme/package.json b/packages/core/theme/package.json index c020814a37..8adc82f1ef 100644 --- a/packages/core/theme/package.json +++ b/packages/core/theme/package.json @@ -55,7 +55,9 @@ "lodash.kebabcase": "^4.1.1", "lodash.mapkeys": "^4.6.0", "lodash.omit": "^4.5.0", - "tailwind-variants": "^0.1.20" + "clsx": "^1.2.1", + "tailwind-variants": "^0.1.20", + "tailwind-merge": "^1.14.0" }, "peerDependencies": { "tailwindcss": ">=3.4.0" diff --git a/packages/core/theme/src/utils/cn.ts b/packages/core/theme/src/utils/cn.ts new file mode 100644 index 0000000000..d1f24280bc --- /dev/null +++ b/packages/core/theme/src/utils/cn.ts @@ -0,0 +1,17 @@ +import type {ClassValue} from "clsx"; + +import clsx from "clsx"; +import {extendTailwindMerge} from "tailwind-merge"; + +import {twMergeConfig} from "./tw-merge-config"; + +/** + * We need to extend the tailwind merge to include NextUI's custom classes. + * + * So we can use classes like `text-small` or `text-default-500` and override them. + */ +const twMerge = extendTailwindMerge(twMergeConfig); + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/packages/core/theme/src/utils/index.ts b/packages/core/theme/src/utils/index.ts index a18c5b9bf3..262655a859 100644 --- a/packages/core/theme/src/utils/index.ts +++ b/packages/core/theme/src/utils/index.ts @@ -1,3 +1,14 @@ -export * from "./classes"; -export * from "./types"; -export * from "./variants"; +export { + baseStyles, + ringClasses, + focusVisibleClasses, + dataFocusVisibleClasses, + groupDataFocusVisibleClasses, + translateCenterClasses, + absoluteFullClasses, + collapseAdjacentVariantBorders, +} from "./classes"; +export type {SlotsToClasses} from "./types"; +export {colorVariants} from "./variants"; +export {COMMON_UNITS, twMergeConfig} from "./tw-merge-config"; +export {cn} from "./cn"; diff --git a/packages/core/theme/src/utils/tv.ts b/packages/core/theme/src/utils/tv.ts index 4eaba8ac6f..faa7c356a4 100644 --- a/packages/core/theme/src/utils/tv.ts +++ b/packages/core/theme/src/utils/tv.ts @@ -1,6 +1,6 @@ import {tv as tvBase, TV} from "tailwind-variants"; -const COMMON_UNITS = ["small", "medium", "large"]; +import {twMergeConfig} from "./tw-merge-config"; export const tv: TV = (options, config) => tvBase(options, { @@ -10,16 +10,11 @@ export const tv: TV = (options, config) => ...config?.twMergeConfig, theme: { ...config?.twMergeConfig?.theme, - opacity: ["disabled"], - spacing: ["divider"], - borderWidth: COMMON_UNITS, - borderRadius: COMMON_UNITS, + ...twMergeConfig.theme, }, classGroups: { ...config?.twMergeConfig?.classGroups, - shadow: [{shadow: COMMON_UNITS}], - "font-size": [{text: ["tiny", ...COMMON_UNITS]}], - "bg-image": ["bg-stripe-gradient"], + ...twMergeConfig.classGroups, }, }, }); diff --git a/packages/core/theme/src/utils/tw-merge-config.ts b/packages/core/theme/src/utils/tw-merge-config.ts new file mode 100644 index 0000000000..bd5776fe0f --- /dev/null +++ b/packages/core/theme/src/utils/tw-merge-config.ts @@ -0,0 +1,16 @@ +import type {Config} from "tailwind-merge"; +export const COMMON_UNITS = ["small", "medium", "large"]; + +export const twMergeConfig: Partial = { + theme: { + opacity: ["disabled"], + spacing: ["divider"], + borderWidth: COMMON_UNITS, + borderRadius: COMMON_UNITS, + }, + classGroups: { + shadow: [{shadow: COMMON_UNITS}], + "font-size": [{text: ["tiny", ...COMMON_UNITS]}], + "bg-image": ["bg-stripe-gradient"], + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 143e45cc40..d19c64507a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3020,6 +3020,9 @@ importers: packages/core/theme: dependencies: + clsx: + specifier: ^1.2.1 + version: 1.2.1 color: specifier: ^4.2.3 version: 4.2.3 @@ -3047,6 +3050,9 @@ importers: lodash.omit: specifier: ^4.5.0 version: 4.5.0 + tailwind-merge: + specifier: ^1.14.0 + version: 1.14.0 tailwind-variants: specifier: ^0.1.20 version: 0.1.20(tailwindcss@3.4.3)