From 866e66dd243099ea9c915cf9e7e6596c0ba477e2 Mon Sep 17 00:00:00 2001 From: winches <329487092@qq.com> Date: Sun, 12 Jan 2025 14:19:52 +0800 Subject: [PATCH 1/6] docs: optimize route higtlight (#4520) --- apps/docs/content/docs/guide/routing.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/content/docs/guide/routing.mdx b/apps/docs/content/docs/guide/routing.mdx index 7751b310ea..1fdd789f06 100644 --- a/apps/docs/content/docs/guide/routing.mdx +++ b/apps/docs/content/docs/guide/routing.mdx @@ -86,7 +86,7 @@ Go to your `app/providers.tsx` or `app/providers.jsx` (create it if it doesn't e #### Add the `useRouter` -```tsx {16} +```tsx {15} // app/providers.tsx "use client"; From b7de85231ca9525219c37dbf97a723fefeee595a Mon Sep 17 00:00:00 2001 From: winches <329487092@qq.com> Date: Sun, 12 Jan 2025 14:28:39 +0800 Subject: [PATCH 2/6] docs: optimize home display (#4519) * docs: optimize home display and route highlight * docs: optimize home display --- apps/docs/components/code-window/code-window.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/code-window/code-window.tsx b/apps/docs/components/code-window/code-window.tsx index 5e79c3c68f..e0c66e1d22 100644 --- a/apps/docs/components/code-window/code-window.tsx +++ b/apps/docs/components/code-window/code-window.tsx @@ -90,7 +90,7 @@ export const CodeWindow: React.FC = ({highlightLines, showCopy, return (
- {showCopy && } + {showCopy && }
); }; From cfff12746408524b89f036112c65b044e3d3e118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D5=A1=D3=84=D5=A1?= Date: Mon, 13 Jan 2025 11:39:44 +0800 Subject: [PATCH 3/6] fix(alert): propagate className (#4535) * fix(alert): propagate className * chore(alert): remove className from alert theme --- .changeset/hot-owls-sniff.md | 5 +++++ packages/components/alert/src/use-alert.ts | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 .changeset/hot-owls-sniff.md diff --git a/.changeset/hot-owls-sniff.md b/.changeset/hot-owls-sniff.md new file mode 100644 index 0000000000..2c713d7975 --- /dev/null +++ b/.changeset/hot-owls-sniff.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/alert": patch +--- + +propagate className (#4533) diff --git a/packages/components/alert/src/use-alert.ts b/packages/components/alert/src/use-alert.ts index 5c24fd1e03..44ce645a37 100644 --- a/packages/components/alert/src/use-alert.ts +++ b/packages/components/alert/src/use-alert.ts @@ -7,7 +7,7 @@ import {ReactNode, useCallback, useMemo} from "react"; import {mergeProps} from "@react-aria/utils"; import {alert} from "@nextui-org/theme"; import {useControlledState} from "@react-stately/utils"; -import {dataAttr, isEmpty, objectToDeps} from "@nextui-org/shared-utils"; +import {clsx, dataAttr, isEmpty, objectToDeps} from "@nextui-org/shared-utils"; interface Props extends HTMLNextUIProps<"div"> { /** @@ -103,6 +103,7 @@ export function useAlert(originalProps: UseAlertProps) { closeButtonProps = { size: "sm", }, + className, classNames, ...otherProps } = props; @@ -123,6 +124,8 @@ export function useAlert(originalProps: UseAlertProps) { onClose?.(); }, [setIsVisible, onClose]); + const baseStyles = clsx(classNames?.base, className); + const slots = useMemo( () => alert({hasContent: !isEmpty(description) || !isEmpty(children), ...variantProps}), [description, objectToDeps(variantProps)], @@ -140,9 +143,9 @@ export function useAlert(originalProps: UseAlertProps) { }), filterDOMProps(props), ), - className: slots.base({class: classNames?.base}), + className: slots.base({class: baseStyles}), }; - }, [slots, classNames?.base]); + }, [slots, baseStyles]); const getMainWrapperProps = useCallback(() => { return { From fcdf24a019718b18655e1c9f80616dd26cbf02ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D5=A1=D3=84=D5=A1?= Date: Mon, 13 Jan 2025 11:41:05 +0800 Subject: [PATCH 4/6] fix(avatar): title type in Avatar (#4529) * fix(avatar): title type in Avatar * fix(alert): apply isEmpty check on title * fix(alert): alert interface props type --- .changeset/quick-peas-care.md | 5 +++++ apps/docs/content/docs/components/alert.mdx | 2 +- packages/components/alert/src/alert.tsx | 2 +- packages/components/alert/src/use-alert.ts | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/quick-peas-care.md diff --git a/.changeset/quick-peas-care.md b/.changeset/quick-peas-care.md new file mode 100644 index 0000000000..5c453cbb55 --- /dev/null +++ b/.changeset/quick-peas-care.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/alert": patch +--- + +fix title type in Avatar (#4525) diff --git a/apps/docs/content/docs/components/alert.mdx b/apps/docs/content/docs/components/alert.mdx index 49000c2744..f7cf045722 100644 --- a/apps/docs/content/docs/components/alert.mdx +++ b/apps/docs/content/docs/components/alert.mdx @@ -138,7 +138,7 @@ Alert has the following slots: data={[ { attribute: "title", - type: "string", + type: "ReactNode", description: "The alert title", default: "-" }, diff --git a/packages/components/alert/src/alert.tsx b/packages/components/alert/src/alert.tsx index 2504a65829..5e4e8d9752 100644 --- a/packages/components/alert/src/alert.tsx +++ b/packages/components/alert/src/alert.tsx @@ -62,7 +62,7 @@ const Alert = forwardRef<"div", AlertProps>((props, ref) => { {customIcon || }
- {title &&
{title}
} + {!isEmpty(title) &&
{title}
} {!isEmpty(description) &&
{description}
} {children}
diff --git a/packages/components/alert/src/use-alert.ts b/packages/components/alert/src/use-alert.ts index 44ce645a37..793baeec73 100644 --- a/packages/components/alert/src/use-alert.ts +++ b/packages/components/alert/src/use-alert.ts @@ -9,7 +9,7 @@ import {alert} from "@nextui-org/theme"; import {useControlledState} from "@react-stately/utils"; import {clsx, dataAttr, isEmpty, objectToDeps} from "@nextui-org/shared-utils"; -interface Props extends HTMLNextUIProps<"div"> { +interface Props extends Omit, "title"> { /** * Ref to the DOM node. */ @@ -17,7 +17,7 @@ interface Props extends HTMLNextUIProps<"div"> { /** * title of the alert message */ - title?: string; + title?: ReactNode; /** * description of the alert message */ From 26fc5147910fc6c98275e467fdd8525e4c0dc45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D5=A1=D3=84=D5=A1?= Date: Mon, 13 Jan 2025 16:23:29 +0800 Subject: [PATCH 5/6] refactor: remove unnecessary props types (#4530) * refactor(docs): remove string type as it is included in ReactNode * refactor: remove unnecessary types * feat(changeset): add changeset * chore: remove changeset * refactor: remove null since ReactNode unions it already --- apps/docs/content/docs/components/accordion.mdx | 6 +++--- apps/docs/content/docs/components/navbar.mdx | 2 +- packages/components/accordion/src/accordion-item.tsx | 2 +- .../components/accordion/src/base/accordion-item-base.tsx | 6 +++--- .../components/listbox/src/base/listbox-item-base.tsx | 6 +++--- packages/components/listbox/src/listbox-item.tsx | 2 +- packages/components/menu/src/base/menu-item-base.tsx | 8 ++++---- packages/components/menu/src/menu-item.tsx | 2 +- packages/components/snippet/src/use-snippet.ts | 2 +- packages/components/tabs/src/base/tab-item-base.ts | 4 ++-- packages/components/user/src/use-user.ts | 4 ++-- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/docs/content/docs/components/accordion.mdx b/apps/docs/content/docs/components/accordion.mdx index 48532229c0..1ecec4b013 100644 --- a/apps/docs/content/docs/components/accordion.mdx +++ b/apps/docs/content/docs/components/accordion.mdx @@ -327,19 +327,19 @@ Here's an example of how to customize the accordion styles: data={[ { attribute: "children", - type: "ReactNode | string", + type: "ReactNode", description: "The content of the component.", default: "-" }, { attribute: "title", - type: "ReactNode | string", + type: "ReactNode", description: "The accordion item title.", default: "-" }, { attribute: "subtitle", - type: "ReactNode | string", + type: "ReactNode", description: "The accordion item subtitle.", default: "-" }, diff --git a/apps/docs/content/docs/components/navbar.mdx b/apps/docs/content/docs/components/navbar.mdx index f0ba7cbcb7..d12d316b95 100644 --- a/apps/docs/content/docs/components/navbar.mdx +++ b/apps/docs/content/docs/components/navbar.mdx @@ -412,7 +412,7 @@ When the `NavbarItem` is active, it will have a `data-active` attribute. You can data={[ { attribute: "icon", - type: "ReactNode | ((isOpen: boolean | undefined) => ReactNode | null)", + type: "ReactNode | ((isOpen: boolean | undefined) => ReactNode)", description: "The icon to render as the navbar menu toggle.", default: "-" }, diff --git a/packages/components/accordion/src/accordion-item.tsx b/packages/components/accordion/src/accordion-item.tsx index fcdea9b5e3..02d29055df 100644 --- a/packages/components/accordion/src/accordion-item.tsx +++ b/packages/components/accordion/src/accordion-item.tsx @@ -40,7 +40,7 @@ const AccordionItem = forwardRef<"button", AccordionItemProps>((props, ref) => { const willChange = useWillChange(); - const indicatorContent = useMemo(() => { + const indicatorContent = useMemo(() => { if (typeof indicator === "function") { return indicator({indicator: , isOpen, isDisabled}); } diff --git a/packages/components/accordion/src/base/accordion-item-base.tsx b/packages/components/accordion/src/base/accordion-item-base.tsx index d97226fb38..e5c67d64c6 100644 --- a/packages/components/accordion/src/base/accordion-item-base.tsx +++ b/packages/components/accordion/src/base/accordion-item-base.tsx @@ -33,15 +33,15 @@ export interface Props /** * The content of the component. */ - children?: ReactNode | null; + children?: ReactNode; /** * The accordion item title. */ - title?: ReactNode | string; + title?: ReactNode; /** * The accordion item subtitle. */ - subtitle?: ReactNode | string; + subtitle?: ReactNode; /** * The accordion item `expanded` indicator, it's usually an arrow icon. * If you pass a function, NextUI will expose the current indicator and the open status, diff --git a/packages/components/listbox/src/base/listbox-item-base.tsx b/packages/components/listbox/src/base/listbox-item-base.tsx index 571929b34e..acf1b12f2c 100644 --- a/packages/components/listbox/src/base/listbox-item-base.tsx +++ b/packages/components/listbox/src/base/listbox-item-base.tsx @@ -25,15 +25,15 @@ interface Props extends Omit, "childre /** * The content of the component. */ - children?: ReactNode | null; + children?: ReactNode; /** * The listbox item title. */ - title?: ReactNode | string; + title?: ReactNode; /** * The listbox item subtitle. */ - description?: ReactNode | string; + description?: ReactNode; /** * The listbox item start content. */ diff --git a/packages/components/listbox/src/listbox-item.tsx b/packages/components/listbox/src/listbox-item.tsx index 476f6c59d3..32b6cbf642 100644 --- a/packages/components/listbox/src/listbox-item.tsx +++ b/packages/components/listbox/src/listbox-item.tsx @@ -29,7 +29,7 @@ const ListboxItem = (props: ListboxItemProps) => { getSelectedIconProps, } = useListboxItem(props); - const selectedContent = useMemo(() => { + const selectedContent = useMemo(() => { const defaultIcon = ( ); diff --git a/packages/components/menu/src/base/menu-item-base.tsx b/packages/components/menu/src/base/menu-item-base.tsx index 0a4c7e4184..acfb2b2674 100644 --- a/packages/components/menu/src/base/menu-item-base.tsx +++ b/packages/components/menu/src/base/menu-item-base.tsx @@ -25,19 +25,19 @@ interface Props extends Omit, "childre /** * The content of the component. */ - children?: ReactNode | null; + children?: ReactNode; /** * The menu item title. */ - title?: ReactNode | string; + title?: ReactNode; /** * The menu item subtitle. */ - description?: ReactNode | string; + description?: ReactNode; /** * The menu item keyboard shortcut. */ - shortcut?: ReactNode | string; + shortcut?: ReactNode; /** * The menu item start content. */ diff --git a/packages/components/menu/src/menu-item.tsx b/packages/components/menu/src/menu-item.tsx index 594bcd3963..fa6b1d76a8 100644 --- a/packages/components/menu/src/menu-item.tsx +++ b/packages/components/menu/src/menu-item.tsx @@ -32,7 +32,7 @@ const MenuItem = (props: MenuItemProps) => { getSelectedIconProps, } = useMenuItem(props); - const selectedContent = useMemo(() => { + const selectedContent = useMemo(() => { const defaultIcon = ( ); diff --git a/packages/components/snippet/src/use-snippet.ts b/packages/components/snippet/src/use-snippet.ts index e45740452a..2e11fb94ac 100644 --- a/packages/components/snippet/src/use-snippet.ts +++ b/packages/components/snippet/src/use-snippet.ts @@ -24,7 +24,7 @@ export interface UseSnippetProps extends Omit, Snippe * The content of the snippet. * if `string[]` is passed, it will be rendered as a multi-line snippet. */ - children?: React.ReactNode | string | string[]; + children?: React.ReactNode | string[]; /** * The symbol to show before the snippet. * @default "$" diff --git a/packages/components/tabs/src/base/tab-item-base.ts b/packages/components/tabs/src/base/tab-item-base.ts index f92080f819..3cb6e318aa 100644 --- a/packages/components/tabs/src/base/tab-item-base.ts +++ b/packages/components/tabs/src/base/tab-item-base.ts @@ -4,11 +4,11 @@ interface Props extends Omit, "chi /** * The content of the component. */ - children?: ReactNode | null; + children?: ReactNode; /** * The title of the component. */ - title?: ReactNode | null; + title?: ReactNode; /** * A string representation of the item's contents. Use this when the title is not readable. * This will be used as native `title` attribute. diff --git a/packages/components/user/src/use-user.ts b/packages/components/user/src/use-user.ts index a7782aece7..a1742c72cf 100644 --- a/packages/components/user/src/use-user.ts +++ b/packages/components/user/src/use-user.ts @@ -17,11 +17,11 @@ interface Props { /** * The user name. */ - name: ReactNode | string; + name: ReactNode; /** * The user information, like email, phone, etc. */ - description?: ReactNode | string; + description?: ReactNode; /** * Whether the user can be focused. * @default false From 992220a5a3d2a4e1639653b2fc0a9e8b4d9428f4 Mon Sep 17 00:00:00 2001 From: Peterl561 <76144929+Peterl561@users.noreply.github.com> Date: Wed, 15 Jan 2025 22:11:40 +0800 Subject: [PATCH 6/6] fix(input): use onPress for wrapper click focus (#4483) * fix(input): use onPress for wrapper click focus * test(input): wrapper click focus test * chore(changeset): input onPress for wrapper click focus * chore(changeset): minor wording --- .changeset/friendly-hounds-walk.md | 5 ++++ .../components/input/__tests__/input.test.tsx | 27 +++++++++++++++++++ packages/components/input/src/use-input.ts | 23 ++++++++-------- 3 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .changeset/friendly-hounds-walk.md diff --git a/.changeset/friendly-hounds-walk.md b/.changeset/friendly-hounds-walk.md new file mode 100644 index 0000000000..3ca7b73589 --- /dev/null +++ b/.changeset/friendly-hounds-walk.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/input": patch +--- + +fixed input inconsistent focus behaviour on wrapper click (#4287) diff --git a/packages/components/input/__tests__/input.test.tsx b/packages/components/input/__tests__/input.test.tsx index c3b57009ba..5eee2e32cc 100644 --- a/packages/components/input/__tests__/input.test.tsx +++ b/packages/components/input/__tests__/input.test.tsx @@ -224,6 +224,33 @@ describe("Input", () => { expect(onClear).toHaveBeenCalledTimes(0); }); + + it("should focus input on click", async () => { + const {getByTestId} = render(); + + const input = getByTestId("input") as HTMLInputElement; + const innerWrapper = document.querySelector("[data-slot='inner-wrapper']") as HTMLDivElement; + const inputWrapper = document.querySelector("[data-slot='input-wrapper']") as HTMLDivElement; + + const user = userEvent.setup(); + + expect(document.activeElement).not.toBe(input); + + await user.click(input); + expect(document.activeElement).toBe(input); + input.blur(); + expect(document.activeElement).not.toBe(input); + + await user.click(innerWrapper); + expect(document.activeElement).toBe(input); + input.blur(); + expect(document.activeElement).not.toBe(input); + + await user.click(inputWrapper); + expect(document.activeElement).toBe(input); + input.blur(); + expect(document.activeElement).not.toBe(input); + }); }); describe("Input with React Hook Form", () => { diff --git a/packages/components/input/src/use-input.ts b/packages/components/input/src/use-input.ts index 12b7d13c2b..6ef82979e8 100644 --- a/packages/components/input/src/use-input.ts +++ b/packages/components/input/src/use-input.ts @@ -161,6 +161,12 @@ export function useInput { + if (domRef.current) { + domRef.current?.focus(); + } + }, [domRef.current]); + // if we use `react-hook-form`, it will set the input value using the ref in register // i.e. setting ref.current.value to something which is uncontrolled // hence, sync the state with `ref.current.value` @@ -225,6 +231,11 @@ export function useInput(() => { @@ -403,12 +414,7 @@ export function useInput { - if (domRef.current && e.currentTarget === e.target) { - domRef.current.focus(); - } - }, + ...mergeProps(props, hoverProps, inputWrapperPressProps), style: { cursor: "text", ...props.style, @@ -432,11 +438,6 @@ export function useInput { - if (domRef.current && e.currentTarget === e.target) { - domRef.current.focus(); - } - }, className: slots.innerWrapper({ class: clsx(classNames?.innerWrapper, props?.className), }),