From 67347d8713c55e0a6151284a85baa7c486f02046 Mon Sep 17 00:00:00 2001 From: Junior Garcia Date: Mon, 27 May 2024 19:51:30 -0300 Subject: [PATCH] v2.4.0 (#3101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(root): reat-aria packages updated (#2889) * chore(storybook): common colors enabled (#2902) * fix(range-calendar): hide only dates outside the month (#2906) * fix(range-calendar): hide only dates outside the month #2890 * fix(range-calendar): corrected spelling mistake in changeset description * fix(range-calendar): corrected capitalization in changeset description * chore(changeset): patch @nextui-org/theme --------- Co-authored-by: shrinidhi.upadhyaya Co-authored-by: աɨռɢӄաօռɢ * fix(date-picker): keep date picker style consistent for different variants (#2908) * fix: add missing TableRowProps export (#2866) * fix: add missing TableRowProps export * feat(changeset): add changeset for PR2866 * chore(changeset): revise changeset message --------- Co-authored-by: աɨռɢӄաօռɢ * fix(input): correct label margin for RTL required inputs (#2781) * fix(input): correct label margin for RTL required inputs * fix(theme): add changeset fr theme * docs(core): add storybook and canary release info (#2914) * Cn utility refactor (#2915) * refactor(core): cn utility adjusted and moved to the theme package * chore(root): changeset * fix(storybook): stories that used cn * docs(date-picker): change to jsx instead (#2919) * fix(switch): support uncontrolled switch in react-hook-form (#2924) * feat(switch): add @nextui-org/use-safe-layout-effect * chore(deps): add @nextui-org/use-safe-layout-effect * fix(switch): react-hook-form uncontrolled switch component * fix(switch): react-hook-form uncontrolled switch component * feat(switch): add rect-hook-form in dev dep * feat(switch): add WithReactHookFormTemplate * refactor(root): react aria packages fixed (#2944) * feat(docs): docs changes (#2868) * feat(docs): add example how to set locale (#2867) * docs(guide): add an explanation for the installation guide (#2769) * docs(guide): add an explanation for the installation guide * docs(guide): add an explanation for the cli guide * docs(guide): add support for cli output * fix: change sort priority - cmdk (#2873) * docs: remove unsupported props in range calendar and date range picker (#2881) * chore(calendar): remove showMonthAndYearPickers from range calendar story * docs(date-range-picker): remove showMonthAndYearPickers info * docs(range-calendar): remove unsupported props * docs: refactor typing in form.ts (#2882) * chore(docs): supplement errorMessage behaviour in input (#2892) * refactor(docs): revise NextUI Provider structure * chore(docs): add updated tag --------- Co-authored-by: Nozomi-Hijikata <116155762+Nozomi-Hijikata@users.noreply.github.com> Co-authored-by: HaRuki Co-authored-by: Kaben * fix(slider): missing marks when hideThumb is true & revise slider styles (#2883) * chore(slider): include marks in hideThumb * fix(slider): revise slider styles * feat(changeset): add changeset * feat(slider): add tests with marks and hideThumb * feat(test): react hook form tests & stories (#2931) * feat(input): add Input with React Hook Form tests * refactor(input): add missing types * feat(checkbox): add checkbox with React Hook Form tests * feat(select): add react-hook-form to dev dep * feat(select): add react hook form story * feat(select): react hook form tests * fix(select): incorrect button reference * feat(deps): add react-hook-form to dev dep in autocomplete * feat(autocomplete): react hook form story * feat(autocomplete): react hook form tests * fix(autocomplete): rollback wrapper type * feat(switch): add react hook form tests * refactor(stories): reorder stories items * fix: update accordion item heading tag to be customizable (#2265) * fix: update accordion item heading tag to be customizable * Update .changeset/heavy-hairs-join.md Co-authored-by: Junior Garcia * Update .changeset/heavy-hairs-join.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore(accordion): lint * chore(changeset): add issue number * feat(docs): add HeadingComponent prop --------- Co-authored-by: Shawn Dong Co-authored-by: Junior Garcia Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: աɨռɢӄաօռɢ * fix(theme): add pointer-events-none to skeleton base (#2972) * feat(tabs): add `destroyInactiveTabPanel` prop for Tabs component (#2973) * feat(tabs): add destroyInactiveTabPanel and set default to false * feat(tabs): integrate with destroyInactiveTabPanel * feat(theme): hidden inert tab panel * feat(changeset): add changeset * chore(changeset): add issue number * feat(docs): add `destroyInactiveTabPanel` prop to tabs page * chore(docs): set destroyInactiveTabPanel to true by default * chore(tabs): set destroyInactiveTabPanel to true by default * chore(tabs): revise destroyInactiveTabPanel logic * feat(tabs): add tests for destroyInactiveTabPanel * chore(tabs): change the default value of destroyInactiveTabPanel to true * refactor: add support for disabling the animation globally (#2929) * refactor: add support for disabling the animation globally * chore(docs): disableAnimation removed from global provider * feat(docs): nextui provider api updated, storybook preview adjusted * chore(theme): button is scalable when disabled, tooltip animation improved * fix(theme): remove origin-bottom from button (#2990) * fix(skeleton): overflow issue in skeleton (#2986) * fix(theme): set overflow visible after skeleton loaded * feat(changeset): add changeset * fix(table): v2 input/textarea don't allow spaces inside a table (#3020) * fix(table): set onKeyDownCapture to undefined * feat(changeset): add changeset * fix(slider): calculate the correct value on mark click (#3017) * fix(slider): calculate the correct value on mark click * refactor(slider): remove the tests inside describe block * feat(slider): add tests for thumb move on mark click * refactor(slider): use val instead of pos * fix(theme): revise input isInvalid styles (#3010) * fix(theme): revise isInvalid input styles * feat(changeset): add changeset * feat(date-picker): add missing ref to input wrapper (#3011) * fix(date-picker): add missing ref to input wrapper * feat(changeset): add changeset * fix(core): incorrect tailwind classnames (#3018) * fix(dropdown): focus behaviour on press / enter keydown (#2970) * fix(dropdown): set focus on the first item * feat(dropdown): add keyboard interactions tests * feat(changeset): add changeset * fix(dropdown): use fireEvent.keyDown instead * chore(deps): add @nextui-org/test-utils to dropdown * refactor(dropdown): pass onKeyDown to menu trigger and don't hardcode autoFocus * chore(dropdown): remove autoFocus * fix(menu): pass userMenuProps to useTreeState and useAriaMenu and remove from getListProps * chore(changeset): add menu package * fix(component): update type definition to prevent primitive values as items (#2953) * fix: update type definition to prevent primitive values as items * fix: typecheck * fix(select): onSelectionChange can handle number (#2937) * fix: onSelectionChange type for dynamic items in Select component * docs: remove unnecessary properties * docs: update highlightedLines * chore: add changeset * fix(calendar): scrolling is hidden when changing the month (#2949) * fix(calendar): scrolling is hidden when changing the month * chore(changeset): correct package name --------- Co-authored-by: Poli Sour Co-authored-by: WK Wong * fix: make VisuallyHidden's element type as span when it's inside phrasing element (#3013) * fix(checkbox): make VisuallyHidden's element type as span * feat(changeset): add changeset * fix(radio): make the VisuallyHidden element type as span * fix(switch): make the VisuallyHidden element type as span * fix(select): make the VisuallyHidden element type as span * feat(changeset): replace changeset * chore: fix formatting * docs: sync nextui-cli api (#3035) * docs: sync nextui-cli api * docs: update * chore: update routes.json with new path and set updated flag --------- Co-authored-by: Junior Garcia * feat: switch default validationBehavior to aria and allow switching via props (#2987) * chore: add support validationBehavior aria * chore: add validationBehavior to Provider * chore: add autocomplete validation test * chore: add checkbox validation test * fix(input): require condition * docs: add description of validationBehavior props * chore: add support validationBehavior props for date components * docs(dates): add description of validationBehavior props * chore: add changeset * chore: format * chore: fix test * fix: select validationBehavior is not support yet * fix: select validationBehavior not supported yet * chore(docs): validation behavior prop added to nextui-provider --------- Co-authored-by: Junior Garcia * fix: popover-based focus behaviour (#2854) * fix(autocomplete): autocomplete focus behaviour * feat(autocomplete): add test case for catching blur cases * refactor(autocomplete): use isOpen instead * feat(autocomplete): add "should focus when clicking autocomplete" test case * feat(autocomplete): add should set the input after selection * fix(autocomplete): remove shouldUseVirtualFocus * fix(autocomplete): uncomment blur logic * refactor(autocomplete): remove state as it is in getPopoverProps * refactor(autocomplete): remove unnecessary blur * refactor(select): remove unncessary props * fix(popover): use domRef instead * fix(popover): revise isNonModal and isDismissable * fix(popover): use dialogRef back * fix(popover): rollback * fix(autocomplete): onFocus logic * feat(popover): set disableFocusManagement to overlay * feat(modal): set disableFocusManagement to overlay * fix(autocomplete): set disableFocusManagement for autocomplete * feat(popover): include disableFocusManagement prop * refactor(autocomplete): revise type in selectorButton * fix(autocomplete): revise focus logic * feat(autocomplete): add internal focus state and add shouldCloseOnInteractOutside * feat(autocomplete): handle selectedItem change * feat(autocomplete): add clear button test * feat(changeset): add changeset * refactor(components): use the original order * refactor(autocomplete): add more comments * fix(autocomplete): revise focus behaviours * refactor(autocomplete): rename to listbox * chore(popover): remove disableFocusManagement from popover * chore(autocomplete): remove disableFocusManagement from autocomplete * chore(changeset): add issue number * fix(popover): don't set default value to transformOrigin * fix(autocomplete): revise shouldCloseOnInteractOutside logic * feat(autocomplete): should close listbox by clicking another autocomplete * fix(popover): add disableFocusManagement to overlay * refactor(autocomplete): revise comments and refactor shouldCloseOnInteractOutside * feat(changeset): add issue number * fix(autocomplete): merge with selectorButtonProps.onClick * refactor(autocomplete): remove extra line * refactor(autocomplete): revise comment * feat(select): add shouldCloseOnInteractOutside * feat(dropdown): add shouldCloseOnInteractOutside * feat(date-picker): add shouldCloseOnInteractOutside * feat(changeset): add dropdown and date-picker * fix(popover): revise shouldCloseOnInteractOutside * feat(date-picker): integrate with ariaShouldCloseOnInteractOutside * feat(select): integrate with ariaShouldCloseOnInteractOutside * feat(dropdown): integrate with ariaShouldCloseOnInteractOutside * feat(popover): integrate with ariaShouldCloseOnInteractOutside * feat(aria-utils): ariaShouldCloseOnInteractOutside * chore(deps): update pnpm-lock.yaml * feat(autocomplete): integrate with ariaShouldCloseOnInteractOutside * feat(aria-utils): handle setShouldFocus logic * feat(changeset): add @nextui-org/aria-utils * chore(autocomplete): put the test into correct group * feat(select): should close listbox by clicking another select * feat(dropdown): should close listbox by clicking another dropdown * feat(popover): should close listbox by clicking another popover * feat(date-picker): should close listbox by clicking another datepicker * chore(changeset): add issue numbers and revise changeset message * refactor(autocomplete): change to useRef instead * refactor(autocomplete): change to useRef instead * refactor(aria-utils): revise comments and format code * chore(changeset): add issue number * chore: take popoverProps.shouldCloseOnInteractOutside first * refactor(autocomplete): remove unnecessary logic * refactor(autocomplete): focus management logic * fix(components): Fix 'Tap to click' behavior on macOS with Edge/Chrome for Accordion and Tab (#2725) * fix(components): fix 'Tap to click' behavior on macOS * Add change file for accordion, menu, and tabs * Remove 'fix(components)' from the .changeset file * fix(components): undo dropdown change now that it's no longer applicable * fix(components): update changeset file now that we are no longer modifying the dropdown component * fix(date-picker): corrected inert value for true condition (#3054) * fix(date-picker): corrected inert value for true condition #3044 * refactor(calendar): add todo comment * feat(changeset): add changeset --------- Co-authored-by: shrinidhi.upadhyaya Co-authored-by: WK Wong * fix(hooks): resolve type error in onKeyDown event handler (#3064) * fix(hooks): resolve type error in onKeyDown event handler * chore(changeset): revise changeset --------- Co-authored-by: WK Wong * Update dependency array on setPage useCallback hook (#3029) Changes: Add the onChangeActivePage function to the dependency array of the setPage useCallback hook to ensure it always reflects the latest state. Impact: This fix ensures that the pagination component accurately reflects the current state when triggering onChangeActivePage. * fix: error peerDep in pkg (#3014) * fix: error peerDep in pkg * docs: changeset * Fix DatePicker Time Input (#2845) * fix(date-picker): set `isCalendarHeaderExpanded` to `false` when DatePicker is closed * fix(date-picker): calendar header controlled state on DatePicker * chore(date-picker): update test * chore(date-picker): remove unnecessary `async` in test * Update packages/components/date-picker/__tests__/date-picker.test.tsx --------- Co-authored-by: WK Wong Co-authored-by: Junior Garcia * fix(date-picker): test * fix(hooks): optimize useScrollPosition with useCallback and useRef (#3049) * fix(hooks): optimize useScrollPosition with useCallback and useRef * Update .changeset/lucky-cobras-jog.md * Update packages/hooks/use-scroll-position/src/index.ts * Update packages/hooks/use-scroll-position/src/index.ts --------- Co-authored-by: Junior Garcia * fix(select): placeholder text display for controlled component (#3081) * fix: return placeholder when selectedItems is empty * chore: add test and changeset * chore(docs): v2.4.0 (#3084) * chore(docs): v2.4.0 * chore(docs): v2.4.0 blog * chore(docs): revise typos based on coderabbitai * chore(docs): adjust navbar --------- Co-authored-by: WK Wong * chore(changese): update @nextui-org/react dependency to minor version * docs: update cli docs (#3096) * ci(changesets): version packages (#2903) Co-authored-by: Junior Garcia --------- Co-authored-by: Shrinidhi Upadhyaya Co-authored-by: shrinidhi.upadhyaya Co-authored-by: աɨռɢӄաօռɢ Co-authored-by: Paul Tiedtke Co-authored-by: Mohammad Reza Badri <85818966+mrbadri@users.noreply.github.com> Co-authored-by: Nozomi-Hijikata <116155762+Nozomi-Hijikata@users.noreply.github.com> Co-authored-by: HaRuki Co-authored-by: Kaben Co-authored-by: Shawn Dong Co-authored-by: Shawn Dong Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Ryo Matsukawa <76232929+ryo-manba@users.noreply.github.com> Co-authored-by: Poli Sour <57824881+novsource@users.noreply.github.com> Co-authored-by: Poli Sour Co-authored-by: Artem Pitikin Co-authored-by: winches <329487092@qq.com> Co-authored-by: Eric Abreu Co-authored-by: Minsu <52266597+Gaic4o@users.noreply.github.com> Co-authored-by: Jesus Perdomo Lampignano <38929969+jesuzon@users.noreply.github.com> Co-authored-by: chirokas <157580465+chirokas@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/docs/components/cmdk.tsx | 20 + apps/docs/components/marketing/hero/hero.tsx | 8 +- apps/docs/components/navbar.tsx | 10 +- apps/docs/config/routes.json | 12 +- apps/docs/content/blog/v2.4.0.mdx | 333 +++++++++ .../components/date-picker/time-zones.ts | 4 +- .../components/input/regex-validation.ts | 2 +- apps/docs/content/components/select/colors.ts | 40 +- .../components/select/custom-selector-icon.ts | 40 +- .../content/components/select/description.ts | 40 +- .../components/select/disabled-items.ts | 40 +- .../content/components/select/disabled.ts | 40 +- .../docs/content/components/select/dynamic.ts | 40 +- .../components/select/error-message.ts | 40 +- .../components/select/label-placements.ts | 42 +- .../select/multiple-controlled-onchange.ts | 42 +- .../components/select/multiple-controlled.ts | 42 +- .../content/components/select/multiple.ts | 40 +- .../content/components/select/open-state.ts | 40 +- apps/docs/content/components/select/radius.ts | 40 +- .../content/components/select/required.ts | 40 +- .../select/single-controlled-onchange.ts | 42 +- .../components/select/single-controlled.ts | 42 +- apps/docs/content/components/select/sizes.ts | 42 +- .../components/select/start-content.ts | 49 +- apps/docs/content/components/select/usage.ts | 42 +- .../content/components/select/variants.ts | 42 +- .../select/without-scroll-shadow.ts | 40 +- apps/docs/content/components/tabs/form.ts | 2 +- .../content/docs/api-references/cli-api.mdx | 104 ++- .../docs/api-references/nextui-provider.mdx | 180 +++-- .../content/docs/components/accordion.mdx | 31 +- .../content/docs/components/autocomplete.mdx | 1 + apps/docs/content/docs/components/badge.mdx | 2 +- .../docs/content/docs/components/calendar.mdx | 3 +- .../docs/components/checkbox-group.mdx | 45 +- .../content/docs/components/date-input.mdx | 67 +- .../content/docs/components/date-picker.mdx | 81 +- .../docs/components/date-range-picker.mdx | 7 +- apps/docs/content/docs/components/input.mdx | 57 +- .../content/docs/components/radio-group.mdx | 41 +- .../docs/components/range-calendar.mdx | 62 +- apps/docs/content/docs/components/select.mdx | 8 +- .../docs/content/docs/components/skeleton.mdx | 2 +- apps/docs/content/docs/components/tabs.mdx | 25 +- .../docs/content/docs/components/textarea.mdx | 59 +- .../content/docs/components/time-input.mdx | 5 +- apps/docs/content/docs/guide/cli.mdx | 59 +- apps/docs/content/docs/guide/installation.mdx | 12 +- apps/docs/package.json | 30 +- apps/docs/public/blog/v2.4.0.jpg | Bin 0 -> 121531 bytes apps/docs/public/blog/v2.4.0_2x.jpg | Bin 0 -> 122133 bytes package.json | 3 +- packages/components/accordion/CHANGELOG.md | 15 + packages/components/accordion/package.json | 18 +- .../accordion/src/accordion-item.tsx | 5 +- .../src/base/accordion-item-base.tsx | 7 + .../accordion/src/use-accordion-item.ts | 13 +- .../components/accordion/src/use-accordion.ts | 6 +- packages/components/autocomplete/CHANGELOG.md | 25 + .../__tests__/autocomplete.test.tsx | 486 +++++++++++- packages/components/autocomplete/package.json | 29 +- .../autocomplete/src/autocomplete.tsx | 7 +- .../autocomplete/src/use-autocomplete.ts | 58 +- .../stories/autocomplete.stories.tsx | 124 +++- packages/components/avatar/CHANGELOG.md | 6 + packages/components/avatar/package.json | 10 +- packages/components/avatar/src/use-avatar.ts | 24 +- packages/components/badge/CHANGELOG.md | 6 + packages/components/badge/package.json | 9 +- packages/components/badge/src/badge.tsx | 2 +- packages/components/badge/src/use-badge.ts | 10 +- packages/components/badge/tsup.config.ts | 1 + packages/components/breadcrumbs/CHANGELOG.md | 6 + packages/components/breadcrumbs/package.json | 14 +- .../breadcrumbs/src/use-breadcrumbs.ts | 13 +- packages/components/button/CHANGELOG.md | 10 + packages/components/button/package.json | 18 +- .../components/button/src/use-button-group.ts | 12 +- packages/components/button/src/use-button.ts | 13 +- packages/components/calendar/CHANGELOG.md | 18 + packages/components/calendar/package.json | 30 +- .../components/calendar/src/calendar-cell.tsx | 2 +- .../calendar/src/calendar-month.tsx | 3 +- .../calendar/src/calendar-picker.tsx | 3 +- .../calendar/src/use-calendar-base.ts | 15 +- .../components/calendar/src/use-calendar.ts | 2 +- .../calendar/src/use-range-calendar.ts | 2 +- .../calendar/stories/calendar.stories.tsx | 2 +- .../stories/range-calendar.stories.tsx | 3 +- packages/components/card/CHANGELOG.md | 9 + packages/components/card/package.json | 16 +- packages/components/card/src/use-card.ts | 29 +- packages/components/checkbox/CHANGELOG.md | 12 + .../__tests__/checkbox-group.test.tsx | 146 +++- .../checkbox/__tests__/checkbox.test.tsx | 150 +++- packages/components/checkbox/package.json | 22 +- packages/components/checkbox/src/checkbox.tsx | 2 +- .../checkbox/src/use-checkbox-group.ts | 39 +- .../components/checkbox/src/use-checkbox.ts | 39 +- .../stories/checkbox-group.stories.tsx | 6 + .../checkbox/stories/checkbox.stories.tsx | 14 +- packages/components/chip/package.json | 10 +- packages/components/code/CHANGELOG.md | 7 + packages/components/code/package.json | 4 +- packages/components/date-input/CHANGELOG.md | 14 + .../date-input/__tests__/date-input.test.tsx | 5 +- packages/components/date-input/package.json | 20 +- .../date-input/src/date-input-group.tsx | 2 +- .../date-input/src/use-date-input.ts | 17 +- .../date-input/src/use-time-input.ts | 20 +- .../date-input/stories/date-input.stories.tsx | 57 +- .../date-input/stories/time-input.stories.tsx | 53 +- packages/components/date-picker/CHANGELOG.md | 29 + .../__tests__/date-picker.test.tsx | 149 ++++ packages/components/date-picker/package.json | 25 +- .../src/date-range-picker-field.tsx | 3 +- .../date-picker/src/use-date-picker-base.ts | 56 +- .../date-picker/src/use-date-picker.ts | 21 +- .../date-picker/src/use-date-range-picker.ts | 21 +- .../stories/date-picker.stories.tsx | 44 +- .../stories/date-range-picker.stories.tsx | 48 +- packages/components/divider/CHANGELOG.md | 7 + packages/components/divider/package.json | 6 +- packages/components/dropdown/CHANGELOG.md | 17 + .../dropdown/__tests__/dropdown.test.tsx | 144 +++- packages/components/dropdown/package.json | 18 +- .../components/dropdown/src/dropdown-menu.tsx | 6 +- .../components/dropdown/src/use-dropdown.ts | 48 +- .../dropdown/stories/dropdown.stories.tsx | 1 - packages/components/image/CHANGELOG.md | 6 + packages/components/image/package.json | 4 +- packages/components/image/src/use-image.ts | 15 +- .../image/stories/image.stories.tsx | 5 + packages/components/input/CHANGELOG.md | 10 + .../components/input/__tests__/input.test.tsx | 89 ++- packages/components/input/package.json | 18 +- packages/components/input/src/use-input.ts | 35 +- .../input/stories/input.stories.tsx | 6 + .../input/stories/textarea.stories.tsx | 6 + packages/components/kbd/CHANGELOG.md | 7 + packages/components/kbd/package.json | 6 +- packages/components/link/CHANGELOG.md | 6 + packages/components/link/package.json | 12 +- packages/components/link/src/use-link.ts | 14 +- packages/components/listbox/CHANGELOG.md | 12 + packages/components/listbox/package.json | 18 +- packages/components/listbox/src/listbox.tsx | 4 +- .../listbox/src/use-listbox-item.ts | 12 +- .../components/listbox/src/use-listbox.ts | 6 +- packages/components/menu/CHANGELOG.md | 15 + packages/components/menu/package.json | 20 +- packages/components/menu/src/menu.tsx | 4 +- packages/components/menu/src/use-menu-item.ts | 12 +- packages/components/menu/src/use-menu.ts | 13 +- packages/components/modal/CHANGELOG.md | 11 + packages/components/modal/package.json | 20 +- packages/components/modal/src/modal.tsx | 6 +- packages/components/modal/src/use-modal.ts | 17 +- .../modal/stories/modal.stories.tsx | 1 - packages/components/navbar/CHANGELOG.md | 10 + packages/components/navbar/package.json | 18 +- packages/components/navbar/src/use-navbar.ts | 16 +- packages/components/pagination/CHANGELOG.md | 6 + packages/components/pagination/package.json | 12 +- .../pagination/src/use-pagination.ts | 23 +- .../pagination/stories/pagination.stories.tsx | 2 +- packages/components/popover/CHANGELOG.md | 13 + .../popover/__tests__/popover.test.tsx | 54 ++ packages/components/popover/package.json | 22 +- packages/components/popover/src/popover.tsx | 6 +- .../popover/src/use-aria-popover.ts | 10 +- .../components/popover/src/use-popover.ts | 12 +- .../popover/stories/popover.stories.tsx | 5 +- packages/components/progress/CHANGELOG.md | 6 + packages/components/progress/package.json | 12 +- .../progress/src/use-circular-progress.ts | 10 +- .../components/progress/src/use-progress.ts | 12 +- packages/components/radio/CHANGELOG.md | 12 + .../components/radio/__tests__/radio.test.tsx | 41 +- packages/components/radio/package.json | 20 +- packages/components/radio/src/radio.tsx | 2 +- .../components/radio/src/use-radio-group.ts | 24 +- packages/components/radio/src/use-radio.ts | 5 +- .../radio/stories/radio.stories.tsx | 6 + packages/components/ripple/CHANGELOG.md | 6 + packages/components/ripple/package.json | 6 +- packages/components/ripple/src/ripple.tsx | 73 +- .../components/scroll-shadow/package.json | 2 +- packages/components/select/CHANGELOG.md | 28 + .../select/__tests__/select.test.tsx | 464 +++++++++--- packages/components/select/package.json | 25 +- packages/components/select/src/select.tsx | 22 +- packages/components/select/src/use-select.ts | 36 +- .../select/stories/select.stories.tsx | 188 +++-- packages/components/skeleton/CHANGELOG.md | 6 + packages/components/skeleton/package.json | 9 +- packages/components/skeleton/src/skeleton.tsx | 2 +- .../components/skeleton/src/use-skeleton.ts | 13 +- packages/components/skeleton/tsup.config.ts | 1 + packages/components/slider/CHANGELOG.md | 11 + .../slider/__tests__/slider.test.tsx | 157 ++++ packages/components/slider/package.json | 18 +- packages/components/slider/src/use-slider.ts | 39 +- .../slider/stories/slider.stories.tsx | 21 +- packages/components/snippet/CHANGELOG.md | 10 + packages/components/snippet/package.json | 10 +- .../components/snippet/src/use-snippet.ts | 14 +- packages/components/spacer/CHANGELOG.md | 7 + packages/components/spacer/package.json | 4 +- packages/components/spinner/CHANGELOG.md | 7 + packages/components/spinner/package.json | 4 +- packages/components/switch/CHANGELOG.md | 10 + .../switch/__tests__/switch.test.tsx | 77 +- packages/components/switch/package.json | 22 +- packages/components/switch/src/switch.tsx | 2 +- packages/components/switch/src/use-switch.ts | 33 +- .../switch/stories/switch.stories.tsx | 48 ++ packages/components/table/CHANGELOG.md | 14 + packages/components/table/package.json | 24 +- packages/components/table/src/index.ts | 8 +- packages/components/table/src/use-table.ts | 14 +- packages/components/tabs/CHANGELOG.md | 16 + .../components/tabs/__tests__/tabs.test.tsx | 36 + packages/components/tabs/package.json | 20 +- packages/components/tabs/src/tab-panel.tsx | 23 +- packages/components/tabs/src/tab.tsx | 2 +- packages/components/tabs/src/tabs.tsx | 32 +- packages/components/tabs/src/use-tabs.ts | 26 +- packages/components/tooltip/CHANGELOG.md | 10 + packages/components/tooltip/package.json | 20 +- .../components/tooltip/src/use-tooltip.ts | 22 +- .../tooltip/stories/tooltip.stories.tsx | 1 - packages/components/user/CHANGELOG.md | 7 + packages/components/user/package.json | 8 +- packages/core/react/CHANGELOG.md | 53 ++ packages/core/react/README.md | 13 +- packages/core/react/package.json | 8 +- packages/core/system-rsc/CHANGELOG.md | 8 + packages/core/system-rsc/package.json | 7 +- .../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 | 7 +- packages/core/system/CHANGELOG.md | 15 + packages/core/system/package.json | 20 +- packages/core/system/src/index.ts | 1 - packages/core/system/src/provider-context.ts | 24 +- packages/core/system/src/provider.tsx | 37 +- packages/core/theme/CHANGELOG.md | 28 + packages/core/theme/package.json | 8 +- .../core/theme/src/components/accordion.ts | 1 - .../core/theme/src/components/autocomplete.ts | 1 - packages/core/theme/src/components/avatar.ts | 9 +- packages/core/theme/src/components/badge.ts | 1 - .../core/theme/src/components/breadcrumbs.ts | 1 - packages/core/theme/src/components/button.ts | 5 +- .../core/theme/src/components/calendar.ts | 5 +- packages/core/theme/src/components/card.ts | 4 +- .../core/theme/src/components/checkbox.ts | 2 - .../theme/src/components/circular-progress.ts | 1 - .../core/theme/src/components/date-input.ts | 1 - .../core/theme/src/components/dropdown.ts | 1 - packages/core/theme/src/components/image.ts | 48 +- packages/core/theme/src/components/input.ts | 14 +- packages/core/theme/src/components/link.ts | 1 - packages/core/theme/src/components/menu.ts | 1 - packages/core/theme/src/components/modal.ts | 26 +- .../core/theme/src/components/pagination.ts | 1 - packages/core/theme/src/components/popover.ts | 1 - .../core/theme/src/components/progress.ts | 1 - packages/core/theme/src/components/radio.ts | 2 - packages/core/theme/src/components/select.ts | 1 - .../core/theme/src/components/skeleton.ts | 6 +- packages/core/theme/src/components/slider.ts | 16 +- packages/core/theme/src/components/snippet.ts | 1 - packages/core/theme/src/components/table.ts | 1 - packages/core/theme/src/components/tabs.ts | 2 +- packages/core/theme/src/components/toggle.ts | 1 - 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 + .../use-aria-accordion-item/package.json | 10 +- .../hooks/use-aria-accordion/package.json | 14 +- packages/hooks/use-aria-button/package.json | 12 +- packages/hooks/use-aria-link/package.json | 12 +- packages/hooks/use-aria-menu/CHANGELOG.md | 6 + packages/hooks/use-aria-menu/package.json | 22 +- packages/hooks/use-aria-menu/src/use-menu.ts | 13 +- .../hooks/use-aria-modal-overlay/package.json | 10 +- .../hooks/use-aria-multiselect/CHANGELOG.md | 12 + .../hooks/use-aria-multiselect/package.json | 32 +- .../src/use-multiselect-list-state.ts | 4 +- .../src/use-multiselect-state.ts | 1 + .../hooks/use-aria-toggle-button/package.json | 10 +- packages/hooks/use-callback-ref/package.json | 2 +- packages/hooks/use-clipboard/package.json | 2 +- .../use-data-scroll-overflow/package.json | 2 +- packages/hooks/use-disclosure/package.json | 6 +- packages/hooks/use-image/package.json | 2 +- .../hooks/use-infinite-scroll/package.json | 2 +- .../use-intersection-observer/CHANGELOG.md | 6 + .../use-intersection-observer/package.json | 12 +- packages/hooks/use-is-mobile/package.json | 4 +- packages/hooks/use-is-mounted/package.json | 2 +- packages/hooks/use-measure/package.json | 2 +- packages/hooks/use-pagination/package.json | 4 +- packages/hooks/use-pagination/src/index.ts | 2 +- packages/hooks/use-real-shape/package.json | 2 +- packages/hooks/use-ref-state/package.json | 2 +- packages/hooks/use-resize/package.json | 2 +- .../hooks/use-safe-layout-effect/package.json | 2 +- .../hooks/use-scroll-position/CHANGELOG.md | 6 + .../hooks/use-scroll-position/package.json | 4 +- .../hooks/use-scroll-position/src/index.ts | 25 +- packages/hooks/use-ssr/package.json | 2 +- packages/hooks/use-update-effect/package.json | 2 +- packages/storybook/.storybook/preview.tsx | 15 +- packages/storybook/package.json | 2 +- packages/storybook/tailwind.config.js | 1 + packages/utilities/aria-utils/CHANGELOG.md | 9 + packages/utilities/aria-utils/package.json | 13 +- packages/utilities/aria-utils/src/index.ts | 1 + .../ariaShouldCloseOnInteractOutside.ts | 41 ++ .../aria-utils/src/overlays/index.ts | 1 + packages/utilities/framer-utils/CHANGELOG.md | 9 + packages/utilities/framer-utils/package.json | 6 +- .../framer-utils/src/transition-utils.ts | 6 +- .../utilities/react-rsc-utils/package.json | 2 +- packages/utilities/react-utils/package.json | 2 +- packages/utilities/shared-icons/package.json | 2 +- packages/utilities/shared-utils/package.json | 2 +- packages/utilities/stories-utils/package.json | 2 +- packages/utilities/test-utils/package.json | 2 +- pnpm-lock.yaml | 694 +++++++++--------- scripts/fix-rap.ts | 42 ++ 337 files changed, 6040 insertions(+), 2454 deletions(-) create mode 100644 apps/docs/content/blog/v2.4.0.mdx create mode 100644 apps/docs/public/blog/v2.4.0.jpg create mode 100644 apps/docs/public/blog/v2.4.0_2x.jpg create mode 100644 packages/core/theme/src/utils/cn.ts create mode 100644 packages/core/theme/src/utils/tw-merge-config.ts create mode 100644 packages/utilities/aria-utils/src/overlays/ariaShouldCloseOnInteractOutside.ts create mode 100644 scripts/fix-rap.ts diff --git a/apps/docs/components/cmdk.tsx b/apps/docs/components/cmdk.tsx index e259a641ec..25a33d03f5 100644 --- a/apps/docs/components/cmdk.tsx +++ b/apps/docs/components/cmdk.tsx @@ -154,6 +154,16 @@ export const Cmdk: FC<{}> = () => { } }; + const prioritizeFirstLevelItems = (a: SearchResultItem, b: SearchResultItem) => { + if (a.type === "lvl1") { + return -1; + } else if (b.type === "lvl1") { + return 1; + } + + return 0; + }; + const results = useMemo( function getResults() { if (query.length < 2) return []; @@ -165,12 +175,22 @@ export const Cmdk: FC<{}> = () => { if (words.length === 1) { return matchSorter(data, query, { keys: MATCH_KEYS, + sorter: (matches) => { + matches.sort((a, b) => prioritizeFirstLevelItems(a.item, b.item)); + + return matches; + }, }).slice(0, MAX_RESULTS); } const matchesForEachWord = words.map((word) => matchSorter(data, word, { keys: MATCH_KEYS, + sorter: (matches) => { + matches.sort((a, b) => prioritizeFirstLevelItems(a.item, b.item)); + + return matches; + }, }), ); diff --git a/apps/docs/components/marketing/hero/hero.tsx b/apps/docs/components/marketing/hero/hero.tsx index 6e004855fb..96085e8e02 100644 --- a/apps/docs/components/marketing/hero/hero.tsx +++ b/apps/docs/components/marketing/hero/hero.tsx @@ -35,11 +35,11 @@ export const Hero = () => { color="default" href="/blog/v2.3.0" variant="dot" - onClick={() => handlePressAnnouncement("Introducing v2.3.0", "/blog/v2.3.0")} + onClick={() => handlePressAnnouncement("New version v2.4.0", "/blog/v2.4.0")} > - Introducing v2.3.0  - - 🎉 + New version v2.4.0  + + 🚀 diff --git a/apps/docs/components/navbar.tsx b/apps/docs/components/navbar.tsx index 19d70e3a40..d9e95b7456 100644 --- a/apps/docs/components/navbar.tsx +++ b/apps/docs/components/navbar.tsx @@ -312,13 +312,13 @@ export const Navbar: FC = ({children, routes, mobileRoutes = [], sl as={NextLink} className="bg-default-100/50 hover:bg-default-100 border-default-200/80 dark:border-default-100/80 transition-colors cursor-pointer" color="default" - href="/blog/v2.3.0" + href="/blog/v2.4.0" variant="dot" - onClick={() => handlePressNavbarItem("Introducing v2.3.0", "/blog/v2.3.0")} + onClick={() => handlePressNavbarItem("New version v2.4.0", "/blog/v2.4.0")} > - Introducing v2.3.0  - - 🎉 + New version v2.4.0  + + 🚀 diff --git a/apps/docs/config/routes.json b/apps/docs/config/routes.json index cdc90c4417..c7fe48efba 100644 --- a/apps/docs/config/routes.json +++ b/apps/docs/config/routes.json @@ -154,7 +154,8 @@ "key": "badge", "title": "Badge", "keywords": "badge, markers, status indication, count display", - "path": "/docs/components/badge.mdx" + "path": "/docs/components/badge.mdx", + "updated": true }, { "key": "button", @@ -334,7 +335,8 @@ "key": "skeleton", "title": "Skeleton", "keywords": "skeleton, loading state, placeholder, content preview", - "path": "/docs/components/skeleton.mdx" + "path": "/docs/components/skeleton.mdx", + "updated": true }, { "key": "snippet", @@ -415,13 +417,15 @@ "key": "cli-api", "title": "NextUI CLI", "keywords": "api references, nextui, api, cli", - "path": "/docs/api-references/cli-api.mdx" + "path": "/docs/api-references/cli-api.mdx", + "updated": true }, { "key": "nextui-provider", "title": "NextUI Provider", "keywords": "api references, nextui, api, nextui provider", - "path": "/docs/api-references/nextui-provider.mdx" + "path": "/docs/api-references/nextui-provider.mdx", + "updated": true } ] } diff --git a/apps/docs/content/blog/v2.4.0.mdx b/apps/docs/content/blog/v2.4.0.mdx new file mode 100644 index 0000000000..49badad758 --- /dev/null +++ b/apps/docs/content/blog/v2.4.0.mdx @@ -0,0 +1,333 @@ +--- +title: "Introducing v2.4.0" +description: "NextUI v2.4.0 is here! includes tons of bug fixes, the possibility to globally disable the animations and improvements." +date: "2024-05-27" +image: "/blog/v2.4.0.jpg" +tags: ["nextui", "v2.4.0", "release", "bug fixes", "improvements", "animations"] +author: + name: "WK Wong" + username: "@wingkwong" + link: "https://github.com/wingkwong" + avatar: "https://avatars.githubusercontent.com/u/35857179?v=4" +--- + +NextUI v2.4.0 + +We are excited to announce the latest update to NextUI, version **2.4.0**! This release introduces 60+ bug fixes, dependencies upgrades, improvements in API and CLI and more. + +## What's New in v2.4.0? + +- [Disable Animations Globally](#disable-animations-globally) - Allows users to disable animation globally via NextUIProvider +- [API Improvements](#api-improvements) - Includes several props like `disableRipple`, `skipFramerMotionAnimations` and `validationBehavior` +- [CLI Improvements](#cli-improvements) - Includes new command options and optimizes performance +- [React Aria Packages Upgrades](#react-aria-packages-upgrades) - Upgrades and fixes the exact versions of React Aria +- [Other Changes](#other-changes) - Includes styling improvements, accessibility, usability enhancements and over 60 bug fixes across different component packages + + + +Upgrade today by running one of the following commands: + + + + + + + + +## Disable Animations Globally + +NextUI components are animated by default and can be disabled by setting the prop `disableAnimation` individually. Since v2.4.0, you can globally disable the animation by setting `disableAnimation` to `NextUIProvider`. + +```tsx +"use client"; + +import {type ReactNode} from "react"; +import {NextUIProvider} from "@nextui-org/react"; + +export function AppProvider(props: AppProviderProps) { + const {children, className} = props; + + return ( + + {children} + + ); +} + +interface AppProviderProps { + children: ReactNode; + className?: string; +} +``` + + + +## API Improvements + +### disableRipple + +By default, there is a ripple effect on press in Button and Card component and can be disabled by setting the prop `disableRipple` individually. Since v2.4.0, it can be disabled globally by setting `disableRipple` to `NextUIProvider`. + +```tsx +"use client"; + +import {type ReactNode} from "react"; +import {NextUIProvider} from "@nextui-org/react"; + +export function AppProvider(props: AppProviderProps) { + const {children, className} = props; + + return ( + + {children} + + ); +} + +interface AppProviderProps { + children: ReactNode; + className?: string; +} +``` + +### skipFramerMotionAnimations + +We can control whether `framer-motion` animations are skipped within the application. This property is automatically enabled (`true`) when the `disableAnimation` prop is set to `true`, effectively skipping all `framer-motion` animations. To retain `framer-motion` animations while using the `disableAnimation` prop for other purposes, set this to `false`. However, note that animations in NextUI Components are still omitted if the `disableAnimation` prop is `true`. + +```tsx +"use client"; + +import {type ReactNode} from "react"; +import {NextUIProvider} from "@nextui-org/react"; + +export function AppProvider(props: AppProviderProps) { + const {children, className} = props; + + return ( + + {children} + + ); +} + +interface AppProviderProps { + children: ReactNode; + className?: string; +} +``` + +### validationBehavior + +We can set `validationBehavior` to either `native` or `aria` to control whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. If it is not specified, `aria` will be used by default. + +```tsx +"use client"; + +import {type ReactNode} from "react"; +import {NextUIProvider} from "@nextui-org/react"; + +export function AppProvider(props: AppProviderProps) { + const {children, className} = props; + + return ( + + {children} + + ); +} + +interface AppProviderProps { + children: ReactNode; + className?: string; +} +``` + + + +## CLI Improvements + +### Refactor Init Flow View + +We've refactored the init flow view to provide a better user experience. + +The latest flow view output: + +```bash +┌ Create a new project +│ +◇ Select a template (Enter to select) +│ ● App (A Next.js 14 with app directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Pages (A Next.js 14 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Vite (A Vite template pre-configured with NextUI (v2) and Tailwind CSS.) +│ +◇ New project name (Enter to skip with default name) +│ my-nextui-app +│ +◇ Select a package manager (Enter to select) +│ ● npm +│ ○ yarn +│ ○ pnpm +│ ○ bun +│ +◇ Template created successfully! +│ +◇ Next steps ───────╮ +│ │ +│ cd my-nextui-app │ +│ npm install │ +│ │ +├────────────────────╯ +│ +└ 🚀 Get started with npm run dev +``` + +### Add Vite Template + +We've introduced a new Vite template pre-configured with NextUI v2 and TailwindCSS. The following command is to initialize a new Vite project named `my-vite-app`. + +```bash +nextui init my-vite-app -t vite +``` + +### Package Manager Flag + +We've introduced a new flag `-p` (or `--package`) to init command to allow users to choose the package manager to use for the new project. By default, `npm` is used. For example, the following command will initialize a new NextUI project with the app template named my-nextui-app using pnpm package manager. + +```bash +nextui init my-nextui-app -t app -p pnpm +``` + +### no-cache Flag + +We've introduced a new flag `--no-cache` to allow users to disable the cache. By default, the data will be cached for 30 mins after the first request. It is useful when the data is cached, and you wish to upgrade to the new version just released after the first request. In this way, you can run the following command + +```bash +nextui --no-cache upgrade +``` + +### Upgrade Version Output + +You can now run the upgrade command and see the summary version of the package you are upgrading to. + +![image](https://github.com/nextui-org/nextui-cli/assets/96854855/2a5945dd-5187-4e20-81b8-4136e9791dde) + +### Upgrade And Remove Select View Optimization + +The disabled option(s) will be displayed in the bottom of the list. + +![image](https://github.com/nextui-org/nextui-cli/assets/96854855/c7ddf9e4-3054-4bf1-b8e3-dc2f6226091a) + +### Doctor Command add peerDependencies check + +The `doctor` command now checks for peerDependencies and displays the incorrect peerDependencies, relation: [nextui-org/nextui#2954](https://github.com/nextui-org/nextui/issues/2954). + + + +## React Aria Packages Upgrades + +We've upgraded and fixed React Aria packages dependencies across our components. This update focuses on enhancing accessibility, ensuring better compatibility with the latest versions of React Aria, and resolving previously reported bugs. + + + +## Breaking Changes + +### Export improved `cn` utility + +> If you are using it from `@nextui-org/react`, no changes are required. + +The new `cn` utility exported from the `@nextui-org/theme` package includes `tailwind-merge` to avoid conflicts between tailwindcss classes overrides and includes the config for NextUI custom classes. + +If you are using the `cn` utility from the `@nextui-org/system`, + +```diff-tsx +-import {cn} from "@nextui-org/system" +``` + +or `@nextui-org/system-rsc` package, + +```diff-tsx +-import {cn} from "@nextui-org/system-rsc" +``` + +you need to update the import as follows: + +```diff-tsx ++ import {cn} from "@nextui-org/theme" +``` + + + + +## Other Changes + +**Bug Fixes**: + +- Fixed popover-based focus behaviours. [PR](https://github.com/nextui-org/nextui/pull/2889) - [@wingkwong](https://github.com/wingkwong) +- Fixed missing ref to input wrapper. [PR](https://github.com/nextui-org/nextui/pull/3008) - [@wingkwong](https://github.com/wingkwong) +- Fixed react-hook-form uncontrolled switch component. [PR](https://github.com/nextui-org/nextui/pull/3008) - [@wingkwong](https://github.com/wingkwong) +- Fixed focus on the first item when pressing Space / Enter key on dropdown menu open. [PR](https://github.com/nextui-org/nextui/pull/2970) - [@wingkwong](https://github.com/wingkwong) +- Fixed inputting spaces in textarea inside a Table row. [PR](https://github.com/nextui-org/nextui/pull/3020) - [@wingkwong](https://github.com/wingkwong) +- Fixed incorrect tailwind classnames. [PR](https://github.com/nextui-org/nextui/pull/3018) - [@wingkwong](https://github.com/wingkwong) +- Fixed onSelectionChange can handle number [PR](https://github.com/nextui-org/nextui/pull/2937) - [@ryo-manba](https://github.com/ryo-manba) +- Fixed update type definition to prevent primitive values as items [PR](https://github.com/nextui-org/nextui/pull/2938) - [@ryo-manba](https://github.com/ryo-manba) +- Fixed display placeholder text when unselected for controlled. [PR](https://github.com/nextui-org/nextui/pull/3081) - [@ryo-manba](https://github.com/ryo-manba) +- Fixed the inert attribute in `CalendarMonth` and `CalendarPicker`. [PR](https://github.com/nextui-org/nextui/pull/3054) - [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya) +- Fixed hiding of unavailable dates in RangeCalendar. [PR](https://github.com/nextui-org/nextui/pull/3054) - [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya) +- Fixed calendar header controlled state on DatePicker. [PR](https://github.com/nextui-org/nextui/pull/2845) - [@chirokas](https://github.com/chirokas) +- Fixed 'Tap to click' behavior on macOS for Accordion and Tab. [PR](https://github.com/nextui-org/nextui/pull/2725) - [@ericfabreu](https://github.com/ericfabreu) +- Fixed incorrect margin on labels for RTL required inputs. [PR](https://github.com/nextui-org/nextui/pull/2781) - [@mrbadri](https://github.com/mrbadri) +- Fixed a type error in the onKeyDown event handler for the menu component. [PR](https://github.com/nextui-org/nextui/pull/3064) - [@Gaic4o](https://github.com/Gaic4o) + +**Improvements** + +- Added `destroyInactiveTabPanel` prop for Tabs component. [PR](https://github.com/nextui-org/nextui/pull/2973) - [@wingkwong](https://github.com/wingkwong) +- Added pointer-events-none to skeleton base. [PR](https://github.com/nextui-org/nextui/pull/2972) - [@wingkwong](https://github.com/wingkwong) +- Revised isInvalid input styles. [PR](https://github.com/nextui-org/nextui/pull/3010) - [@wingkwong](https://github.com/wingkwong) +- Revised slider styles. [PR](https://github.com/nextui-org/nextui/pull/2883) - [@wingkwong](https://github.com/wingkwong) +- Removed unnecessary origin-bottom in button. [PR](https://github.com/nextui-org/nextui/pull/2990) - [@wingkwong](https://github.com/wingkwong) +- Set overflow visible after skeleton loaded. [PR](https://github.com/nextui-org/nextui/pull/2986) - [@wingkwong](https://github.com/wingkwong) +- Kept date picker style consistent for different variants. [PR](https://github.com/nextui-org/nextui/pull/2901) - [@wingkwong](https://github.com/wingkwong) +- Calculated the correct value on mark click. [PR](https://github.com/nextui-org/nextui/pull/3017) - [@wingkwong](https://github.com/wingkwong) +- Removed scrolling display during month change animation. [PR](https://github.com/nextui-org/nextui/pull/2949) - [@novsource](https://github.com/novsource) +- Added the correct peerDep version [PR](https://github.com/nextui-org/nextui/pull/3014) - [@winchesHe](https://github.com/winchesHe) +- Added missing export of TableRowProps type. [PR](https://github.com/nextui-org/nextui/pull/2866) - [@sapkra](https://github.com/sapkra) +- Changed validationBehavior from native to aria by default, with the option to change via props. [PR](https://github.com/nextui-org/nextui/pull/2987) - [@ryo-manba](https://github.com/ryo-manba) +- Made the VisuallyHidden elementType as span when the default parent element accepts only phrasing elements. [PR](https://github.com/nextui-org/nextui/pull/3013) - [@kosmotema](https://github.com/kosmotema) +- Refactored the useScrollPosition hook to improve performance and stability by using useCallback for the handler function and useRef for throttleTimeout. [PR](https://github.com/nextui-org/nextui/pull/3049) - [@Gaic4o](https://github.com/Gaic4o) + +**Documentation**: +- Updated documentation to reflect the new features and changes in the components, API references, and CLI. + +Special thanks to NextUI Team members [@kuri-sun](https://github.com/kuri-sun), [@ryo-manba](https://github.com/ryo-manba), +[@sudongyuer](https://github.com/sudongyuer), [@winchesHe](https://github.com/winchesHe), [@wingkwong](https://github.com/wingkwong), +[@tianenpang](https://github.com/tianenpang), [@smultar](https://github.com/smultar) and contributors for their contributions to this release. + +For a full list of changes, please refer to the [release notes](https://github.com/nextui-org/nextui/releases/tag/%40nextui-org%2Freact%402.4.0). + + + +Thanks for reading and happy coding! 🚀 + +--- + +## Community + +We're excited to see the community adopt NextUI, raise issues, and provide feedback. +Whether it's a feature request, bug report, or a project to showcase, please get involved! + + + +## Contributing + +PR's on NextUI are always welcome, please see our [contribution guidelines](https://github.com/nextui-org/nextui/blob/main/CONTRIBUTING.md) to learn how you can contribute to this project. diff --git a/apps/docs/content/components/date-picker/time-zones.ts b/apps/docs/content/components/date-picker/time-zones.ts index 830162ebcf..85c1d3c554 100644 --- a/apps/docs/content/components/date-picker/time-zones.ts +++ b/apps/docs/content/components/date-picker/time-zones.ts @@ -1,4 +1,4 @@ -const AppTs = `import {DatePicker} from "@nextui-org/react"; +const App = `import {DatePicker} from "@nextui-org/react"; import {parseZonedDateTime, parseAbsoluteToLocal} from "@internationalized/date"; export default function App() { @@ -21,7 +21,7 @@ export default function App() { }`; const react = { - "/App.tsx": AppTs, + "/App.jsx": App, }; export default { diff --git a/apps/docs/content/components/input/regex-validation.ts b/apps/docs/content/components/input/regex-validation.ts index d6c9828ce0..b3ce257e4f 100644 --- a/apps/docs/content/components/input/regex-validation.ts +++ b/apps/docs/content/components/input/regex-validation.ts @@ -19,7 +19,7 @@ export default function App() { variant="bordered" isInvalid={isInvalid} color={isInvalid ? "danger" : "success"} - errorMessage={isInvalid && "Please enter a valid email"} + errorMessage="Please enter a valid email" onValueChange={setValue} className="max-w-xs" /> diff --git a/apps/docs/content/components/select/colors.ts b/apps/docs/content/components/select/colors.ts index 5bda6e4c47..c830262ca1 100644 --- a/apps/docs/content/components/select/colors.ts +++ b/apps/docs/content/components/select/colors.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -51,7 +39,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/custom-selector-icon.ts b/apps/docs/content/components/select/custom-selector-icon.ts index 3d14d188f1..18770cfb44 100644 --- a/apps/docs/content/components/select/custom-selector-icon.ts +++ b/apps/docs/content/components/select/custom-selector-icon.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const SelectorIcon = `export const SelectorIcon = (props) => ( @@ -62,7 +50,7 @@ export default function App() { selectorIcon={} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/description.ts b/apps/docs/content/components/select/description.ts index c9429822de..b39c36847d 100644 --- a/apps/docs/content/components/select/description.ts +++ b/apps/docs/content/components/select/description.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -39,7 +27,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/disabled-items.ts b/apps/docs/content/components/select/disabled-items.ts index 0d5ed862d3..64bed55e89 100644 --- a/apps/docs/content/components/select/disabled-items.ts +++ b/apps/docs/content/components/select/disabled-items.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -38,7 +26,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/disabled.ts b/apps/docs/content/components/select/disabled.ts index e4cf9cd3f3..dc9b756812 100644 --- a/apps/docs/content/components/select/disabled.ts +++ b/apps/docs/content/components/select/disabled.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -39,7 +27,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/dynamic.ts b/apps/docs/content/components/select/dynamic.ts index 1a73d48ba8..6a7c5304c9 100644 --- a/apps/docs/content/components/select/dynamic.ts +++ b/apps/docs/content/components/select/dynamic.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -37,7 +25,7 @@ export default function App() { placeholder="Select an animal" className="max-w-xs" > - {(animal) => {animal.label}} + {(animal) => {animal.label}} ); }`; diff --git a/apps/docs/content/components/select/error-message.ts b/apps/docs/content/components/select/error-message.ts index 63b0763e1d..416a1d49bb 100644 --- a/apps/docs/content/components/select/error-message.ts +++ b/apps/docs/content/components/select/error-message.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -49,7 +37,7 @@ export default function App() { onClose={() => setTouched(true)} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/label-placements.ts b/apps/docs/content/components/select/label-placements.ts index 7bbfd5f01a..a147bb84a7 100644 --- a/apps/docs/content/components/select/label-placements.ts +++ b/apps/docs/content/components/select/label-placements.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -48,7 +36,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} @@ -67,7 +55,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/multiple-controlled-onchange.ts b/apps/docs/content/components/select/multiple-controlled-onchange.ts index 90de996ef3..e12d5cf7d3 100644 --- a/apps/docs/content/components/select/multiple-controlled-onchange.ts +++ b/apps/docs/content/components/select/multiple-controlled-onchange.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -47,7 +35,7 @@ export default function App() { onChange={handleSelectionChange} > {animals.map((animal) => ( - + {animal.label} ))} @@ -78,7 +66,7 @@ export default function App() { onChange={handleSelectionChange} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/multiple-controlled.ts b/apps/docs/content/components/select/multiple-controlled.ts index dc158135a9..2bfc63c6c7 100644 --- a/apps/docs/content/components/select/multiple-controlled.ts +++ b/apps/docs/content/components/select/multiple-controlled.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -43,7 +31,7 @@ export default function App() { onSelectionChange={setValues} > {animals.map((animal) => ( - + {animal.label} ))} @@ -70,7 +58,7 @@ export default function App() { onSelectionChange={setValues} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/multiple.ts b/apps/docs/content/components/select/multiple.ts index f01c8343c0..5edb2da50f 100644 --- a/apps/docs/content/components/select/multiple.ts +++ b/apps/docs/content/components/select/multiple.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -38,7 +26,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/open-state.ts b/apps/docs/content/components/select/open-state.ts index b1977ace5b..97e13a99a7 100644 --- a/apps/docs/content/components/select/open-state.ts +++ b/apps/docs/content/components/select/open-state.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem, Button} from "@nextui-org/react"; @@ -43,7 +31,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/radius.ts b/apps/docs/content/components/select/radius.ts index 4854e66618..543c0c91df 100644 --- a/apps/docs/content/components/select/radius.ts +++ b/apps/docs/content/components/select/radius.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -50,7 +38,7 @@ export default function App() { className="max-w-[45%]" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/required.ts b/apps/docs/content/components/select/required.ts index 85e5f34dda..f6447eba53 100644 --- a/apps/docs/content/components/select/required.ts +++ b/apps/docs/content/components/select/required.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -39,7 +27,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/single-controlled-onchange.ts b/apps/docs/content/components/select/single-controlled-onchange.ts index 2124028721..b74254621a 100644 --- a/apps/docs/content/components/select/single-controlled-onchange.ts +++ b/apps/docs/content/components/select/single-controlled-onchange.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -47,7 +35,7 @@ export default function App() { onChange={handleSelectionChange} > {animals.map((animal) => ( - + {animal.label} ))} @@ -78,7 +66,7 @@ export default function App() { onChange={handleSelectionChange} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/single-controlled.ts b/apps/docs/content/components/select/single-controlled.ts index 59e0d570a7..cae9bd5401 100644 --- a/apps/docs/content/components/select/single-controlled.ts +++ b/apps/docs/content/components/select/single-controlled.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const AppTs = `import {Select, SelectItem, Selection} from "@nextui-org/react"; @@ -43,7 +31,7 @@ export default function App() { onSelectionChange={setValue} > {animals.map((animal) => ( - + {animal.label} ))} @@ -70,7 +58,7 @@ export default function App() { onSelectionChange={setValue} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/sizes.ts b/apps/docs/content/components/select/sizes.ts index 6b5520492c..02b1cde2d0 100644 --- a/apps/docs/content/components/select/sizes.ts +++ b/apps/docs/content/components/select/sizes.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -42,7 +30,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} @@ -54,7 +42,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/start-content.ts b/apps/docs/content/components/select/start-content.ts index 8b0d2dca97..7b896eaf3f 100644 --- a/apps/docs/content/components/select/start-content.ts +++ b/apps/docs/content/components/select/start-content.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const PetIcon = `export const PetIcon = (props) => ( @@ -67,20 +55,19 @@ import {animals} from "./data"; export default function App() { return ( ); -}`; +} +`; const react = { "/App.jsx": App, diff --git a/apps/docs/content/components/select/usage.ts b/apps/docs/content/components/select/usage.ts index 0cde961b11..d676e3edb7 100644 --- a/apps/docs/content/components/select/usage.ts +++ b/apps/docs/content/components/select/usage.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -37,7 +25,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} @@ -48,7 +36,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/variants.ts b/apps/docs/content/components/select/variants.ts index 5d719be91a..040be0e123 100644 --- a/apps/docs/content/components/select/variants.ts +++ b/apps/docs/content/components/select/variants.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -42,7 +30,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} @@ -54,7 +42,7 @@ export default function App() { className="max-w-xs" > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/select/without-scroll-shadow.ts b/apps/docs/content/components/select/without-scroll-shadow.ts index 95b1dd2fc6..7a4d8b585d 100644 --- a/apps/docs/content/components/select/without-scroll-shadow.ts +++ b/apps/docs/content/components/select/without-scroll-shadow.ts @@ -1,29 +1,17 @@ const data = `export const animals = [ - {label: "Cat", value: "cat", description: "The second most popular pet in the world"}, - {label: "Dog", value: "dog", description: "The most popular pet in the world"}, - {label: "Elephant", value: "elephant", description: "The largest land animal"}, - {label: "Lion", value: "lion", description: "The king of the jungle"}, - {label: "Tiger", value: "tiger", description: "The largest cat species"}, - {label: "Giraffe", value: "giraffe", description: "The tallest land animal"}, - { - label: "Dolphin", - value: "dolphin", - description: "A widely distributed and diverse group of aquatic mammals", - }, - {label: "Penguin", value: "penguin", description: "A group of aquatic flightless birds"}, - {label: "Zebra", value: "zebra", description: "A several species of African equids"}, - { - label: "Shark", - value: "shark", - description: "A group of elasmobranch fish characterized by a cartilaginous skeleton", - }, - { - label: "Whale", - value: "whale", - description: "Diverse group of fully aquatic placental marine mammals", - }, - {label: "Otter", value: "otter", description: "A carnivorous mammal in the subfamily Lutrinae"}, - {label: "Crocodile", value: "crocodile", description: "A large semiaquatic reptile"}, + {key: "cat", label: "Cat"}, + {key: "dog", label: "Dog"}, + {key: "elephant", label: "Elephant"}, + {key: "lion", label: "Lion"}, + {key: "tiger", label: "Tiger"}, + {key: "giraffe", label: "Giraffe"}, + {key: "dolphin", label: "Dolphin"}, + {key: "penguin", label: "Penguin"}, + {key: "zebra", label: "Zebra"}, + {key: "shark", label: "Shark"}, + {key: "whale", label: "Whale"}, + {key: "otter", label: "Otter"}, + {key: "crocodile", label: "Crocodile"} ];`; const App = `import {Select, SelectItem} from "@nextui-org/react"; @@ -41,7 +29,7 @@ export default function App() { }} > {animals.map((animal) => ( - + {animal.label} ))} diff --git a/apps/docs/content/components/tabs/form.ts b/apps/docs/content/components/tabs/form.ts index 76ccac2840..679964350b 100644 --- a/apps/docs/content/components/tabs/form.ts +++ b/apps/docs/content/components/tabs/form.ts @@ -69,7 +69,7 @@ export default function App() { const AppTs = `import {Tabs, Tab, Input, Link, Button, Card, CardBody, CardHeader} from "@nextui-org/react"; export default function App() { - const [selected, setSelected] = React.useState("login"); + const [selected, setSelected] = React.useState("login"); return (
diff --git a/apps/docs/content/docs/api-references/cli-api.mdx b/apps/docs/content/docs/api-references/cli-api.mdx index 8e4b283fbd..fb7a443ba0 100644 --- a/apps/docs/content/docs/api-references/cli-api.mdx +++ b/apps/docs/content/docs/api-references/cli-api.mdx @@ -28,6 +28,7 @@ Usage: nextui [command] Options: -v, --version Show the version number + --no-cache Disable cache, by default data will be cached for 30m after the first request -h, --help Display help for commands Commands: @@ -51,6 +52,7 @@ nextui init [projectName] [options] ### Options - `-t --template [string]` The template to use for the new project e.g. app, pages +- `-p --package [string]` The package manager to use for the new project (default: `npm`) ### Example @@ -58,6 +60,39 @@ nextui init [projectName] [options] nextui init my-nextui-app -t app ``` +output: + +```codeBlock bash +NextUI CLI v0.2.1 + +┌ Create a new project +│ +◇ Select a template (Enter to select) +│ ● App (A Next.js 14 with app directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Pages (A Next.js 14 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Vite (A Vite template pre-configured with NextUI (v2) and Tailwind CSS.) +│ +◇ New project name (Enter to skip with default name) +│ my-nextui-app +│ +◇ Select a package manager (Enter to select) +│ ● npm +│ ○ yarn +│ ○ pnpm +│ ○ bun +│ +◇ Template created successfully! +│ +◇ Next steps ───────╮ +│ │ +│ cd my-nextui-app │ +│ npm install │ +│ │ +├────────────────────╯ +│ +└ 🚀 Get started with npm run dev +``` + ## add > 1. Auto add the missing required `dependencies` to your project @@ -79,7 +114,6 @@ nextui add [components...] [options] - `--prettier` [boolean] Add prettier format in the add content which required installed prettier - (default: false) - `--addApp` [boolean] Add App.tsx file content which required provider (default: `false`) - ### Example Without setting a specific component, the `add` command will show a list of available components. @@ -91,27 +125,22 @@ nextui add Output: ```codeBlock bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 -? Which components would you like to add? › - Space to select. Return to submit -Instructions: - ↑/↓: Highlight option - ←/→/[space]: Toggle selection - [a,b,c]/delete: Filter choices - enter/return: Complete answer +? Which components would you like to add? › - Space to select. Return to submit Filtered results for: Enter something to filter -◉ accordion -◯ autocomplete -◯ avatar -◯ badge -◯ breadcrumbs -◯ button -◯ card -◯ checkbox -◯ chip -◯ code +◯ accordion +◯ autocomplete +◯ avatar +◯ badge +◯ breadcrumbs +◯ button +◯ calendar +◯ card +◯ checkbox +◯ ↓ chip ``` If you want to add a specific component, you can specify the component name. @@ -123,7 +152,7 @@ nextui add button input Output: ```bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 Adding the required dependencies: @nextui-org/button @@ -142,7 +171,6 @@ Tailwind CSS settings have been updated in: /project-path/tailwind.config.js ✅ Components added successfully ``` - ## upgrade Upgrade the NextUI components to the latest version. @@ -157,27 +185,33 @@ nextui upgrade [components...] [options] - `-a --all` [boolean] Upgrade all the NextUI components (default: `false`). - `-h, --help` Display help for commands. - ### Example ```codeBlock bash nextui upgrade button ``` - Output: ```bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 + +╭───────────────────────── Component ─────────────────────────╮ +│ @nextui-org/button ^2.0.11 -> ^2.0.31 │ +╰─────────────────────────────────────────────────────────────╯ + +Required min version: @nextui-org/theme>=2.1.0, tailwindcss>=3.4.0 +╭───────────────────── PeerDependencies ─────────────────────╮ +│ @nextui-org/theme 2.0.1 -> 2.1.0 │ +│ tailwindcss ^3.2.3 -> ^3.4.0 │ +╰────────────────────────────────────────────────────────────╯ +2 minor, 1 patch -╭───────────────────────────────────────────────────────────╮ -│ @nextui-org/button 2.0.24 -> 2.0.27 │ -╰───────────────────────────────────────────────────────────╯ ? Would you like to proceed with the upgrade? › - Use arrow-keys. Return to submit. ❯ Yes No -pnpm add @nextui-org/button@2.0.27 +pnpm add @nextui-org/button@2.0.31 @nextui-org/theme@2.1.0 tailwindcss@3.4.0 Already up to date Progress: resolved 474, reused 465, downloaded 0, added 0, done Done in 2.9s @@ -195,7 +229,6 @@ Remove NextUI components from your project. nextui remove [components...] [options] ``` - ### Options - `-p --packagePath` [string] The path to the package.json file. @@ -213,7 +246,7 @@ nextui remove button Output: ```bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 ❗️ Components slated for removal: ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ @@ -249,8 +282,8 @@ nextui list [options] ### Options -- `-p --packagePath` [string] The path to the package.json file. -- `-c --current` List the current installed components. +- `-p --packagePath` [string] The path to the package.json file +- `-r --remote` List all components available remotely ### Example @@ -261,7 +294,7 @@ nextui list Output: ```codeBlock bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 Current installed components: @@ -283,6 +316,7 @@ Diagnose problems in your project. > 2. Check whether the NextUI components `required dependencies are installed` in the project > 3. Check the required `tailwind.config.js` file and the content is correct > 4. Check `.npmrc` is correct when using `pnpm` +> 5. Check `peerDependencies with required version` are installed in the project ```codeBlock bash nextui doctor [options] @@ -310,7 +344,7 @@ Output: If there is a problem in your project, the `doctor` command will display the problem information. ```codeBlock bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 NextUI CLI: ❌ Your project has 1 issue that require attention @@ -322,7 +356,7 @@ Missing tailwind.config.(j|t)s file. To set up, visit: https://nextui.org/docs/g Otherwise, the `doctor` command will display the following message. ```codeBlock bash -NextUI CLI v0.1.2 +NextUI CLI v0.2.1 ✅ Your project has no detected issues. ``` @@ -348,7 +382,7 @@ nextui env Output: ```codeBlock bash -NextUI CLI 0.1.0 +NextUI CLI 0.2.1 Current installed components: diff --git a/apps/docs/content/docs/api-references/nextui-provider.mdx b/apps/docs/content/docs/api-references/nextui-provider.mdx index 063ca19891..dcc6abb52f 100644 --- a/apps/docs/content/docs/api-references/nextui-provider.mdx +++ b/apps/docs/content/docs/api-references/nextui-provider.mdx @@ -5,80 +5,176 @@ description: API References for NextUI Provider # NextUI Provider +API reference for the `NextUIProvider`. + ------ -Here's the API reference for the `NextUIProvider`. +## Import + + + +## Usage + +```jsx +import * as React from "react"; +import {NextUIProvider} from "@nextui-org/react"; + +function App() { + return ( + + + + ); +} +``` ## Props -### navigate + -`navigate` provides a client side router to all nested components such as Link, Menu, Tabs, Table, etc. +`navigate` -**type**: `((path: string) => void) | undefined` +- **Description**: Provides a client side router to all nested components such as Link, Menu, Tabs, Table, etc. +- **Type**: `((path: string) => void) | undefined` -### locale + -The locale to apply to the children. The [BCP47](https://www.ietf.org/rfc/bcp/bcp47.txt) language code for the locale. By default, It is `en-US`. +`locale` -**type**: `string | undefined` +- **Description**: The locale to apply to the children. +- **Type**: `string | undefined` +- **Default**: `en-US` -### defaultDates -The default dates range that can be selected in the calendar. +Here's the supported locales. By default, It is `en-US`. -**type**: `{ minDate?: CalendarDate | undefined; maxDate?: CalendarDate | undefined; }` +```tsx +const localeValues = [ + 'fr-FR', 'fr-CA', 'de-DE', 'en-US', 'en-GB', 'ja-JP', + 'da-DK', 'nl-NL', 'fi-FI', 'it-IT', 'nb-NO', 'es-ES', + 'sv-SE', 'pt-BR', 'zh-CN', 'zh-TW', 'ko-KR', 'bg-BG', + 'hr-HR', 'cs-CZ', 'et-EE', 'hu-HU', 'lv-LV', 'lt-LT', + 'pl-PL', 'ro-RO', 'ru-RU', 'sr-SP', 'sk-SK', 'sl-SI', + 'tr-TR', 'uk-UA', 'ar-AE', 'ar-DZ', 'AR-EG', 'ar-SA', + 'el-GR', 'he-IL', 'fa-AF', 'am-ET', 'hi-IN', 'th-TH' +]; +``` -- minDate +Here's an example to set a Spanish locale. - The minimum date that can be selected in the calendar. +```tsx +"use client"; + +import {type ReactNode} from "react"; +import {NextUIProvider} from "@nextui-org/react"; - **type**: `CalendarDate | undefined` +export function AppProvider(props: AppProviderProps) { + const {children, className} = props; - **default**: `new CalendarDate(1900, 1, 1)` + return ( + + {children} + + ); +} -- maxDate +interface AppProviderProps { + children: ReactNode; + className?: string; +} +``` - The maximum date that can be selected in the calendar. + - **type**: `CalendarDate | undefined` +`defaultDates` - **default**: `new CalendarDate(2099, 12, 31)` +- **Description**: The default dates range that can be selected in the calendar. +- **Type**: `{ minDate?: CalendarDate | undefined; maxDate?: CalendarDate | undefined; }` +- **Default**: `{ minDate: new CalendarDate(1900, 1, 1), maxDate: new CalendarDate(2099, 12, 31) }` -### createCalendar + -This function helps to reduce the bundle size by providing a custom calendar system. +`createCalendar` -By default, this includes all calendar systems supported by `@internationalized/date`. However, -if your application supports a more limited set of regions, or you know you will only be picking dates -in a certain calendar system, you can reduce your bundle size by providing your own implementation -of `createCalendar` that includes a subset of these Calendar implementations. +- **Description**: + This function helps to reduce the bundle size by providing a custom calendar system. -For example, if your application only supports Gregorian dates, you could implement a `createCalendar` -function like this: + By default, this includes all calendar systems supported by `@internationalized/date`. However, + if your application supports a more limited set of regions, or you know you will only be picking dates + in a certain calendar system, you can reduce your bundle size by providing your own implementation + of `createCalendar` that includes a subset of these Calendar implementations. -```tsx -import {GregorianCalendar} from '@internationalized/date'; - -function createCalendar(identifier) { - switch (identifier) { - case 'gregory': - return new GregorianCalendar(); - default: - throw new Error(`Unsupported calendar ${identifier}`); - } -} -``` + For example, if your application only supports Gregorian dates, you could implement a `createCalendar` + function like this: -This way, only GregorianCalendar is imported, and the other calendar implementations can be tree-shaken. + ```tsx + import {GregorianCalendar} from '@internationalized/date'; -**type**: `((calendar: SupportedCalendars) => Calendar | null) | undefined` + function createCalendar(identifier) { + switch (identifier) { + case 'gregory': + return new GregorianCalendar(); + default: + throw new Error(`Unsupported calendar ${identifier}`); + } + } + ``` + + This way, only GregorianCalendar is imported, and the other calendar implementations can be tree-shaken. + +- **Type**: `((calendar: SupportedCalendars) => Calendar | null) | undefined` + + + +`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. +- **Type**: `boolean` +- **Default**: `false` + + + +`disableRipple` + +- **Description**: Disables ripple effect globally. +- **Type**: `boolean` +- **Default**: `false` + + + +`skipFramerMotionAnimations` + +- **Description**: + Controls whether `framer-motion` animations are skipped within the application. + This property is automatically enabled (`true`) when the `disableAnimation` prop is set to `true`, + effectively skipping all `framer-motion` animations. To retain `framer-motion` animations while + using the `disableAnimation` prop for other purposes, set this to `false`. However, note that + animations in NextUI Components are still omitted if the `disableAnimation` prop is `true`. +- **Type**: `boolean` +- **Default**: Same as `disableAnimation` + + + +`validationBehavior` + +- **Description**: Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, +or mark the field as required or invalid via ARIA. +- **Type**: `native | aria` +- **Default**: `aria` + +--- ## Types -### CalendarDate +`CalendarDate` -A [CalendarDate](https://react-spectrum.adobe.com/internationalized/date/CalendarDate.html) represents a date without any time components in a specific calendar system from `@internationalized/date`. +- **Description**: A [CalendarDate](https://react-spectrum.adobe.com/internationalized/date/CalendarDate.html) represents a date without any time components in a specific calendar system from `@internationalized/date`. +- **Type**: `import {CalendarDate} from '@internationalized/date';` ### SupportedCalendars diff --git a/apps/docs/content/docs/components/accordion.mdx b/apps/docs/content/docs/components/accordion.mdx index 18e987b642..0775958904 100644 --- a/apps/docs/content/docs/components/accordion.mdx +++ b/apps/docs/content/docs/components/accordion.mdx @@ -219,21 +219,22 @@ Here's an example of how to customize the accordion styles: ### Accordion Item Props -| Attribute | Type | Description | Default | -| ------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------- | -| children | `ReactNode` \| `string` | The content of the component. | | -| title | `ReactNode` \| `string` | The accordion item title. | | -| subtitle | `ReactNode` \| `string` | The accordion item subtitle. | | -| indicator | [IndicatorProps](#accordion-item-indicator-props) | The accordion item `expanded` indicator, usually an arrow icon. | | -| startContent | `ReactNode` | The accordion item start content, usually an icon or avatar. | | -| motionProps | [MotionProps](#motion-props) | The props to modify the framer motion animation. Use the `variants` API to create your own animation. | | -| isCompact | `boolean` | Whether the AccordionItem is compact. | `false` | -| isDisabled | `boolean` | The current disabled status. | `false` | -| keepContentMounted | `boolean` | Whether the AccordionItem content is kept mounted when closed. | `false` | -| hideIndicator | `boolean` | Whether the AccordionItem indicator is hidden. | `false` | -| disableAnimation | `boolean` | Whether the AccordionItem animation is disabled. | `false` | -| disableIndicatorAnimation | `boolean` | Whether the AccordionItem indicator animation is disabled. | `false` | -| classNames | [Classnames](#accordion-item-classnames) | Allows to set custom class names for the accordion item slots. | - | +| Attribute | Type | Description | Default | +|---------------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|---------| +| children | `ReactNode` \| `string` | The content of the component. | | +| title | `ReactNode` \| `string` | The accordion item title. | | +| subtitle | `ReactNode` \| `string` | The accordion item subtitle. | | +| indicator | [IndicatorProps](#accordion-item-indicator-props) | The accordion item `expanded` indicator, usually an arrow icon. | | +| startContent | `ReactNode` | The accordion item start content, usually an icon or avatar. | | +| motionProps | [MotionProps](#motion-props) | The props to modify the framer motion animation. Use the `variants` API to create your own animation. | | +| isCompact | `boolean` | Whether the AccordionItem is compact. | `false` | +| isDisabled | `boolean` | The current disabled status. | `false` | +| keepContentMounted | `boolean` | Whether the AccordionItem content is kept mounted when closed. | `false` | +| hideIndicator | `boolean` | Whether the AccordionItem indicator is hidden. | `false` | +| disableAnimation | `boolean` | Whether the AccordionItem animation is disabled. | `false` | +| disableIndicatorAnimation | `boolean` | Whether the AccordionItem indicator animation is disabled. | `false` | +| HeadingComponent | `React.ElementType` | Customizable heading tag for Web accessibility. Use headings to describe content and use them consistently and semantically. | `h2` | +| classNames | [Classnames](#accordion-item-classnames) | Allows to set custom class names for the accordion item slots. | - | ### Accordion Item Events diff --git a/apps/docs/content/docs/components/autocomplete.mdx b/apps/docs/content/docs/components/autocomplete.mdx index dfa32c8c73..6b127b7fbc 100644 --- a/apps/docs/content/docs/components/autocomplete.mdx +++ b/apps/docs/content/docs/components/autocomplete.mdx @@ -428,6 +428,7 @@ properties to customize the popover, listbox and input components. | disabledKeys | `all` \| `React.Key[]` | The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with. | - | | errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | An error message to display below the field. | - | | validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | | startContent | `ReactNode` | Element to be rendered in the left side of the Autocomplete. | - | | endContent | `ReactNode` | Element to be rendered in the right side of the Autocomplete. | - | | autoFocus | `boolean` | Whether the Autocomplete should be focused on render. | `false` | diff --git a/apps/docs/content/docs/components/badge.mdx b/apps/docs/content/docs/components/badge.mdx index cdffcc28b8..a4d2316adf 100644 --- a/apps/docs/content/docs/components/badge.mdx +++ b/apps/docs/content/docs/components/badge.mdx @@ -9,7 +9,7 @@ import {badgeContent} from "@/content/components/badge"; Badges are used as a small numerical value or status descriptor for UI elements. - + --- diff --git a/apps/docs/content/docs/components/calendar.mdx b/apps/docs/content/docs/components/calendar.mdx index b4a48c743a..fab85ee1f4 100644 --- a/apps/docs/content/docs/components/calendar.mdx +++ b/apps/docs/content/docs/components/calendar.mdx @@ -226,10 +226,9 @@ Here's the example to customize `topContent` and `bottomContent` to have some pr | isDateUnavailable | `(date: DateValue) => boolean` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | - | | createCalendar | `(calendar: SupportedCalendars) => Calendar \| null` | This function helps to reduce the bundle size by providing a custom calendar system. You can also use the NextUIProvider to provide the createCalendar function to all nested components. | `all
calendars` | | errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the field. | - | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate time input values when committing (e.g. on blur), and return error messages for invalid values. | - | | hideDisabledDates | `boolean` | Whether to hide the disabled or invalid dates. | `false` | | disableAnimation | `boolean` | Whether to disable the animation of the calendar. | `false` | -| classNames | `Record<"base"| "prevButton"| "nextButton"| "headerWrapper" \| "header" \| "title" \| "content" \| "gridWrapper" \| "grid" \| "gridHeader" \| "gridHeaderRow" \| "gridHeaderCell" \| "gridBody" \| "gridBodyRow" \| "cell" \| "cellButton" \| "pickerWrapper" \| "pickerMonthList" \| "pickerYearList" \| "pickerHighlight" \| "pickerItem" \| "helperWrapper" \| "errorMessage", string>` | Allows to set custom class names for the calendar slots. | - | +| classNames | `Record<"base"| "prevButton"| "nextButton"| "headerWrapper" \| "header" \| "title" \| "content" \| "gridWrapper" \| "grid" \| "gridHeader" \| "gridHeaderRow" \| "gridHeaderCell" \| "gridBody" \| "gridBodyRow" \| "cell" \| "cellButton" \| "pickerWrapper" \| "pickerMonthList" \| "pickerYearList" \| "pickerHighlight" \| "pickerItem" \| "helperWrapper" \| "errorMessage", string>` | Allows to set custom class names for the calendar slots. | - | ### Calendar Events diff --git a/apps/docs/content/docs/components/checkbox-group.mdx b/apps/docs/content/docs/components/checkbox-group.mdx index ffabd48256..ac1f1927a2 100644 --- a/apps/docs/content/docs/components/checkbox-group.mdx +++ b/apps/docs/content/docs/components/checkbox-group.mdx @@ -93,28 +93,29 @@ In case you need to customize the checkbox even further, you can use the `useChe ### Checkbox Group Props -| Attribute | Type | Description | Default | -| ---------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------- | -| children | `ReactNode[]` \| `ReactNode[]` | The checkboxes items. | - | -| orientation | `vertical` \| `horizontal` | The axis the checkbox group items should align with. | `vertical` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the checkboxes. | `primary` | -| size | `xs` \| `sm` \| `md` \| `lg` \| `xl` | The size of the checkboxes. | `md` | -| radius | `none` \| `base` \| `xs` \| `sm` \| `md` \| `lg` \| `xl` \| `full` | The radius of the checkboxes. | `md` | -| name | `string` | The name of the CheckboxGroup, used when submitting an HTML form. | - | -| label | `string` | The label of the CheckboxGroup. | - | -| value | `string[]` | The current selected values. (controlled). | - | -| lineThrough | `boolean` | Whether the checkboxes label should be crossed out. | `false` | -| defaultValue | `string[]` | The default selected values. (uncontrolled). | - | -| isInvalid | `boolean` | Whether the checkbox group is invalid. | `false` | -| validationState | `valid` \| `invalid` | Whether the inputs should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | -| description | `ReactNode` | The checkbox group description. | - | -| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | The checkbox group error message. | - | -| validate | `(value: string[]) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | -| isDisabled | `boolean` | Whether the checkbox group is disabled. | `false` | -| isRequired | `boolean` | Whether user checkboxes are required on the input before form submission. | `false` | -| isReadOnly | `boolean` | Whether the checkboxes can be selected but not changed by the user. | - | -| disableAnimation | `boolean` | Whether the animation should be disabled. | `false` | -| classNames | `Record<"base"| "wrapper"| "label", string>` | Allows to set custom class names for the checkbox group slots. | - | +| Attribute | Type | Description | Default | +| ------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| children | `ReactNode[]` \| `ReactNode[]` | The checkboxes items. | - | +| orientation | `vertical` \| `horizontal` | The axis the checkbox group items should align with. | `vertical` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the checkboxes. | `primary` | +| size | `xs` \| `sm` \| `md` \| `lg` \| `xl` | The size of the checkboxes. | `md` | +| radius | `none` \| `base` \| `xs` \| `sm` \| `md` \| `lg` \| `xl` \| `full` | The radius of the checkboxes. | `md` | +| name | `string` | The name of the CheckboxGroup, used when submitting an HTML form. | - | +| label | `string` | The label of the CheckboxGroup. | - | +| value | `string[]` | The current selected values. (controlled). | - | +| lineThrough | `boolean` | Whether the checkboxes label should be crossed out. | `false` | +| defaultValue | `string[]` | The default selected values. (uncontrolled). | - | +| isInvalid | `boolean` | Whether the checkbox group is invalid. | `false` | +| validationState | `valid` \| `invalid` | Whether the inputs should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | +| description | `ReactNode` | The checkbox group description. | - | +| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | The checkbox group error message. | - | +| validate | `(value: string[]) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| isDisabled | `boolean` | Whether the checkbox group is disabled. | `false` | +| isRequired | `boolean` | Whether user checkboxes are required on the input before form submission. | `false` | +| isReadOnly | `boolean` | Whether the checkboxes can be selected but not changed by the user. | - | +| disableAnimation | `boolean` | Whether the animation should be disabled. | `false` | +| classNames | `Record<"base"| "wrapper"| "label", string>` | Allows to set custom class names for the checkbox group slots. | - | ### Checkbox Group Events diff --git a/apps/docs/content/docs/components/date-input.mdx b/apps/docs/content/docs/components/date-input.mdx index 95929a0f2c..0c0f6d2452 100644 --- a/apps/docs/content/docs/components/date-input.mdx +++ b/apps/docs/content/docs/components/date-input.mdx @@ -284,39 +284,40 @@ import {parseZonedDateTime} from "@internationalized/date"; ### DateInput Props -| Attribute | Type | Description | Default | -| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------- | -| label | `ReactNode` | The content to display as the label. | - | -| value | `DateValue` | The current value of the date input (controlled). | - | -| defaultValue | `DateValue` | The default value of the date input (uncontrolled). | - | -| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the date input. | `flat` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the date input. | `default` | -| size | `sm` \| `md` \| `lg` | The size of the date input. | `md` | -| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the date input. | - | -| placeholderValue | `DateValue` | A placeholder time that influences the format of the placeholder shown when no value is selected. Defaults current date at midnight. | - | -| minValue | `DateValue` | The minimum allowed date that a user may select. | - | -| maxValue | `DateValue` | The maximum allowed date that a user may select. | - | -| locale | `string` | The locale to display and edit the value according to. | - | -| description | `ReactNode` | A description for the date input. Provides a hint such as specific requirements for what to choose. | - | -| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the date input. | - | -| startContent | `ReactNode` | Element to be rendered in the left side of the date input. | - | -| endContent | `ReactNode` | Element to be rendered in the right side of the date input. | - | -| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | -| isRequired | `boolean` | Whether user input is required on the input before form submission. | `false` | -| isReadOnly | `boolean` | Whether the input can be selected but not changed by the user. | - | -| isDisabled | `boolean` | Whether the input is disabled. | `false` | -| isInvalid | `boolean` | Whether the input value is invalid. | `false` | -| inputRef | `ReactRef` | A ref for the hidden input element for HTML form submission. | - | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | -| createCalendar | `(name: string) => Calendar` | A function that creates a Calendar object for a given calendar identifier. | - | -| isDateUnavailable | `(date: DateValue) => boolean` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | - | -| autoFocus | `boolean` | Whether the element should receive focus on render. | `false` | -| hourCycle | `12` \| `24` | Whether to display the time in 12 or 24 hour format. This is determined by the user's locale. | - | -| granularity | `day` \| `hour` \| `minute` \| `second` | Determines the smallest unit that is displayed in the date picker. Typically "day" for dates. | - | -| hideTimeZone | `boolean` | Whether to hide the time zone abbreviation. | `false` | -| shouldForceLeadingZeros | `boolean` | Whether to always show leading zeros in the month, day, and hour fields. | `true` | -| disableAnimation | `boolean` | Whether to disable animations. | `false` | -| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper"| "input"| "helperWrapper"| "description"| "errorMessage", string>` | Allows to set custom class names for the date input slots. | - | +| Attribute | Type | Description | Default | +| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| label | `ReactNode` | The content to display as the label. | - | +| value | `DateValue` | The current value of the date input (controlled). | - | +| defaultValue | `DateValue` | The default value of the date input (uncontrolled). | - | +| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the date input. | `flat` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the date input. | `default` | +| size | `sm` \| `md` \| `lg` | The size of the date input. | `md` | +| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the date input. | - | +| placeholderValue | `DateValue` | A placeholder time that influences the format of the placeholder shown when no value is selected. Defaults current date at midnight. | - | +| minValue | `DateValue` | The minimum allowed date that a user may select. | - | +| maxValue | `DateValue` | The maximum allowed date that a user may select. | - | +| locale | `string` | The locale to display and edit the value according to. | - | +| description | `ReactNode` | A description for the date input. Provides a hint such as specific requirements for what to choose. | - | +| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the date input. | - | +| validate | `(value: MappedDateValue) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| startContent | `ReactNode` | Element to be rendered in the left side of the date input. | - | +| endContent | `ReactNode` | Element to be rendered in the right side of the date input. | - | +| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | +| isRequired | `boolean` | Whether user input is required on the input before form submission. | `false` | +| isReadOnly | `boolean` | Whether the input can be selected but not changed by the user. | - | +| isDisabled | `boolean` | Whether the input is disabled. | `false` | +| isInvalid | `boolean` | Whether the input value is invalid. | `false` | +| inputRef | `ReactRef` | A ref for the hidden input element for HTML form submission. | - | +| createCalendar | `(name: string) => Calendar` | A function that creates a Calendar object for a given calendar identifier. | - | +| isDateUnavailable | `(date: DateValue) => boolean` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | - | +| autoFocus | `boolean` | Whether the element should receive focus on render. | `false` | +| hourCycle | `12` \| `24` | Whether to display the time in 12 or 24 hour format. This is determined by the user's locale. | - | +| granularity | `day` \| `hour` \| `minute` \| `second` | Determines the smallest unit that is displayed in the date picker. Typically "day" for dates. | - | +| hideTimeZone | `boolean` | Whether to hide the time zone abbreviation. | `false` | +| shouldForceLeadingZeros | `boolean` | Whether to always show leading zeros in the month, day, and hour fields. | `true` | +| disableAnimation | `boolean` | Whether to disable animations. | `false` | +| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper"| "input"| "helperWrapper"| "description"| "errorMessage", string>` | Allows to set custom class names for the date input slots. | - | ### DateInput Events diff --git a/apps/docs/content/docs/components/date-picker.mdx b/apps/docs/content/docs/components/date-picker.mdx index a0614d6813..0a305b854e 100644 --- a/apps/docs/content/docs/components/date-picker.mdx +++ b/apps/docs/content/docs/components/date-picker.mdx @@ -301,46 +301,47 @@ import {I18nProvider} from "@react-aria/i18n"; ### DatePicker Props -| Attribute | Type | Description | Default | -| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| label | `ReactNode` | The content to display as the label. | - | -| value | `ZonedDateTime` \| `CalendarDate` \| `CalendarDateTime` \| `undefined` \| `null` | The current value of the date-picker (controlled). | - | -| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the date input. | `flat` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the date input. | `default` | -| size | `sm` \| `md` \| `lg` | The size of the date input. | `md` | -| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the date input. | - | -| defaultValue | `string` \| undefined | The default value of the date-picker (uncontrolled). | - | -| placeholderValue | `ZonedDateTime` \| `CalendarDate` \| `CalendarDateTime` \| `undefined` \| `null` | The placeholder of the date-picker. | - | -| description | `ReactNode` | A description for the date-picker. Provides a hint such as specific requirements for what to choose. | - | -| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the date input. | - | -| startContent | `ReactNode` | Element to be rendered in the left side of the date-picker. | - | -| endContent | `ReactNode` | Element to be rendered in the right side of the date-picker. | - | -| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | -| isRequired | `boolean` | Whether user input is required on the date-picker before form submission. | `false` | -| isReadOnly | `boolean` | Whether the date-picker can be selected but not changed by the user. | | -| isDisabled | `boolean` | Whether the date-picker is disabled. | `false` | -| isInvalid | `boolean` | Whether the date-picker is invalid. | `false` | -| visibleMonths | `number` \| `undefined` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop. | `1` | -| selectorIcon | `ReactNode` | The icon to toggle the date picker popover. Usually a calendar icon. | | -| pageBehavior | `PageBehavior` \| `undefined` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. | `visible` | -| visibleMonths | `number` \| `undefined` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop. | `1` | -| calendarWidth | `number` | The width to be applied to the calendar component. | `256` | -| CalendarTopContent | `ReactNode` | Top content to be rendered in the calendar component. | | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | -| isDateUnavailable | `((date: DateValue) => boolean)` \| `undefined` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | -| autoFocus | `boolean` | Whether the element should receive focus on render. | `false` | -| hourCycle | `12` \| `24` | Whether to display the time in 12 or 24 hour format. This is determined by the user's locale. | - | -| granularity | `day` \| `hour` \| `minute` \| `second` | Determines the smallest unit that is displayed in the date picker. Typically "day" for dates. | - | -| hideTimeZone | `boolean` | Whether to hide the time zone abbreviation. | `false` | -| shouldForceLeadingZeros | `boolean` | Whether to always show leading zeros in the month, day, and hour fields. | `true` | -| CalendarBottomContent | `ReactNode` | Bottom content to be rendered in the calendar component. | | -| showMonthAndYearPickers | `boolean` \| `undefined` | Whether the calendar should show month and year pickers. | false | -| popoverProps | `PopoverProps` \| `undefined` | Props to be passed to the popover component. | `{ placement: "bottom", triggerScaleOnOpen: false, offset: 13 }` | -| selectorButtonProps | `ButtonProps` \| `undefined` | Props to be passed to the selector button component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | -| calendarProps | `CalendarProps` \| `undefined` | Props to be passed to the selector button component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | -| timeInputProps | `TimeInputProps` | Props to be passed to the time input component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | -| disableAnimation | `boolean` | Whether to disable all animations in the date picker. Including the DateInput, Button, Calendar, and Popover. | `false` | -| classNames | `Record<"base" \| "selectorButton" \| "selectorIcon" \| "popoverContent" \| "calendar" \| "calendarContent" \| "timeInputLabel" \| "timeInput", string>` | Allows to set custom class names for the date-picker slots. | - | +| Attribute | Type | Description | Default | +| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| label | `ReactNode` | The content to display as the label. | - | +| value | `ZonedDateTime` \| `CalendarDate` \| `CalendarDateTime` \| `undefined` \| `null` | The current value of the date-picker (controlled). | - | +| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the date input. | `flat` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the date input. | `default` | +| size | `sm` \| `md` \| `lg` | The size of the date input. | `md` | +| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the date input. | - | +| defaultValue | `string` \| undefined | The default value of the date-picker (uncontrolled). | - | +| placeholderValue | `ZonedDateTime` \| `CalendarDate` \| `CalendarDateTime` \| `undefined` \| `null` | The placeholder of the date-picker. | - | +| description | `ReactNode` | A description for the date-picker. Provides a hint such as specific requirements for what to choose. | - | +| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the date input. | - | +| validate | `(value: MappedDateValue) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| startContent | `ReactNode` | Element to be rendered in the left side of the date-picker. | - | +| endContent | `ReactNode` | Element to be rendered in the right side of the date-picker. | - | +| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | +| isRequired | `boolean` | Whether user input is required on the date-picker before form submission. | `false` | +| isReadOnly | `boolean` | Whether the date-picker can be selected but not changed by the user. | | +| isDisabled | `boolean` | Whether the date-picker is disabled. | `false` | +| isInvalid | `boolean` | Whether the date-picker is invalid. | `false` | +| visibleMonths | `number` \| `undefined` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop. | `1` | +| selectorIcon | `ReactNode` | The icon to toggle the date picker popover. Usually a calendar icon. | | +| pageBehavior | `PageBehavior` \| `undefined` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. | `visible` | +| visibleMonths | `number` \| `undefined` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop. | `1` | +| calendarWidth | `number` | The width to be applied to the calendar component. | `256` | +| CalendarTopContent | `ReactNode` | Top content to be rendered in the calendar component. | | +| isDateUnavailable | `((date: DateValue) => boolean)` \| `undefined` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | +| autoFocus | `boolean` | Whether the element should receive focus on render. | `false` | +| hourCycle | `12` \| `24` | Whether to display the time in 12 or 24 hour format. This is determined by the user's locale. | - | +| granularity | `day` \| `hour` \| `minute` \| `second` | Determines the smallest unit that is displayed in the date picker. Typically "day" for dates. | - | +| hideTimeZone | `boolean` | Whether to hide the time zone abbreviation. | `false` | +| shouldForceLeadingZeros | `boolean` | Whether to always show leading zeros in the month, day, and hour fields. | `true` | +| CalendarBottomContent | `ReactNode` | Bottom content to be rendered in the calendar component. | | +| showMonthAndYearPickers | `boolean` \| `undefined` | Whether the calendar should show month and year pickers. | false | +| popoverProps | `PopoverProps` \| `undefined` | Props to be passed to the popover component. | `{ placement: "bottom", triggerScaleOnOpen: false, offset: 13 }` | +| selectorButtonProps | `ButtonProps` \| `undefined` | Props to be passed to the selector button component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | +| calendarProps | `CalendarProps` \| `undefined` | Props to be passed to the selector button component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | +| timeInputProps | `TimeInputProps` | Props to be passed to the time input component. | `{ size: "sm", variant: "light", radius: "full", isIconOnly: true }` | +| disableAnimation | `boolean` | Whether to disable all animations in the date picker. Including the DateInput, Button, Calendar, and Popover. | `false` | +| classNames | `Record<"base" \| "selectorButton" \| "selectorIcon" \| "popoverContent" \| "calendar" \| "calendarContent" \| "timeInputLabel" \| "timeInput", string>` | Allows to set custom class names for the date-picker slots. | - | ### DatePicker Events diff --git a/apps/docs/content/docs/components/date-range-picker.mdx b/apps/docs/content/docs/components/date-range-picker.mdx index 94ab58a1f3..9d24d14332 100644 --- a/apps/docs/content/docs/components/date-range-picker.mdx +++ b/apps/docs/content/docs/components/date-range-picker.mdx @@ -341,7 +341,7 @@ import {I18nProvider} from "@react-aria/i18n"; ### DateRangePicker Props | Attribute | Type | Description | Default | -| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| | label | `ReactNode` | The content to display as the label. | - | | value | `RangeValue` \| `undefined` \| `null` | The current value of the date-range-picker (controlled). | - | | variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the date input. | `flat` | @@ -354,6 +354,8 @@ import {I18nProvider} from "@react-aria/i18n"; | placeholderValue | `ZonedDateTime` \| `CalendarDate` \| `CalendarDateTime` \| `undefined` \| `null` | The placeholder of the date-range-picker. | - | | description | `ReactNode` | A description for the date-range-picker. Provides a hint such as specific requirements for what to choose. | - | | errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the date input. | - | +| validate | `(value: RangeValue>) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. | `aria` | | startContent | `ReactNode` | Element to be rendered in the left side of the date-range-picker. | - | | endContent | `ReactNode` | Element to be rendered in the right side of the date-range-picker. | - | | startName | `string` | The name of the start date input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname) | - | @@ -367,8 +369,7 @@ import {I18nProvider} from "@react-aria/i18n"; | isInvalid | `boolean` | Whether the date-range-picker is invalid. | `false` | | selectorIcon | `ReactNode` | The icon to toggle the date picker popover. Usually a calendar icon. | | | pageBehavior | `single` \| `visible` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. | `visible` | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate input values when committing (e.g., on blur), and return error messages for invalid values. | - | -| visibleMonths | `number` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop. | `1` | +| visibleMonths | `number` | The number of months to display at once. Up to 3 months are supported. | `1` | | autoFocus | `boolean` | Whether the element should receive focus on render. | `false` | | hourCycle | `12` \| `24` | Whether to display the time in 12 or 24 hour format. This is determined by the user's locale. | - | | granularity | `day` \| `hour` \| `minute` \| `second` | Determines the smallest unit that is displayed in the date picker. Typically "day" for dates. | - | diff --git a/apps/docs/content/docs/components/input.mdx b/apps/docs/content/docs/components/input.mdx index 84631bdc57..35b0df3db1 100644 --- a/apps/docs/content/docs/components/input.mdx +++ b/apps/docs/content/docs/components/input.mdx @@ -108,7 +108,7 @@ You can add a description to the input by passing the `description` property. ### With Error Message -You can combine the `isInvalid` and `errorMessage` properties to show an invalid input. +You can combine the `isInvalid` and `errorMessage` properties to show an invalid input. `errorMessage` is only shown when `isInvalid` is set to `true`. @@ -194,33 +194,34 @@ In case you need to customize the input even further, you can use the `useInput` ### Input Props -| Attribute | Type | Description | Default | -| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------- | -| children | `ReactNode` | The content of the input. | - | -| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the input. | `flat` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the input. | `default` | -| size | `sm` \| `md` \| `lg` | The size of the input. | `md` | -| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the input. | - | -| label | `ReactNode` | The content to display as the label. | - | -| value | `string` | The current value of the input (controlled). | - | -| defaultValue | `string` | The default value of the input (uncontrolled). | - | -| placeholder | `string` | The placeholder of the input. | - | -| description | `ReactNode` | A description for the input. Provides a hint such as specific requirements for what to choose. | - | -| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | An error message for the input. | - | -| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | -| startContent | `ReactNode` | Element to be rendered in the left side of the input. | - | -| endContent | `ReactNode` | Element to be rendered in the right side of the input. | - | -| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | -| fullWidth | `boolean` | Whether the input should take up the width of its parent. | `true` | -| isClearable | `boolean` | Whether the input should have a clear button. | `false` | -| isRequired | `boolean` | Whether user input is required on the input before form submission. | `false` | -| isReadOnly | `boolean` | Whether the input can be selected but not changed by the user. | | -| isDisabled | `boolean` | Whether the input is disabled. | `false` | -| isInvalid | `boolean` | Whether the input is invalid. | `false` | -| baseRef | `RefObject` | The ref to the base element. | - | -| validationState | `valid` \| `invalid` | Whether the input should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | -| disableAnimation | `boolean` | Whether the input should be animated. | `false` | -| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper"| "mainWrapper" | "input" | "clearButton" | "helperWrapper" | "description" | "errorMessage", string>` | Allows to set custom class names for the Input slots. | - | +| Attribute | Type | Description | Default | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| children | `ReactNode` | The content of the input. | - | +| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the input. | `flat` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the input. | `default` | +| size | `sm` \| `md` \| `lg` | The size of the input. | `md` | +| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the input. | - | +| label | `ReactNode` | The content to display as the label. | - | +| value | `string` | The current value of the input (controlled). | - | +| defaultValue | `string` | The default value of the input (uncontrolled). | - | +| placeholder | `string` | The placeholder of the input. | - | +| description | `ReactNode` | A description for the input. Provides a hint such as specific requirements for what to choose. | - | +| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | An error message for the input. It is only shown when `isInvalid` is set to `true` | - | +| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| startContent | `ReactNode` | Element to be rendered in the left side of the input. | - | +| endContent | `ReactNode` | Element to be rendered in the right side of the input. | - | +| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | +| fullWidth | `boolean` | Whether the input should take up the width of its parent. | `true` | +| isClearable | `boolean` | Whether the input should have a clear button. | `false` | +| isRequired | `boolean` | Whether user input is required on the input before form submission. | `false` | +| isReadOnly | `boolean` | Whether the input can be selected but not changed by the user. | | +| isDisabled | `boolean` | Whether the input is disabled. | `false` | +| isInvalid | `boolean` | Whether the input is invalid. | `false` | +| baseRef | `RefObject` | The ref to the base element. | - | +| validationState | `valid` \| `invalid` | Whether the input should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | +| disableAnimation | `boolean` | Whether the input should be animated. | `false` | +| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper"| "mainWrapper" | "input" | "clearButton" | "helperWrapper" | "description" | "errorMessage", string>` | Allows to set custom class names for the Input slots. | - | ### Input Events diff --git a/apps/docs/content/docs/components/radio-group.mdx b/apps/docs/content/docs/components/radio-group.mdx index 762280b523..8bdfaa350e 100644 --- a/apps/docs/content/docs/components/radio-group.mdx +++ b/apps/docs/content/docs/components/radio-group.mdx @@ -147,26 +147,27 @@ In case you need to customize the radio group even further, you can use the `use ### RadioGroup Props -| Attribute | Type | Description | Default | -| ---------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| children | `ReactNode` \| `ReactNode[]` | The list of radio elements. | - | -| label | `ReactNode` | The label of the radio group. | - | -| size | `sm` \| `md` \| `lg` | The size of the radios. | `md` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the radios. | `primary` | -| orientation | `horizontal` \| `vertical` | The orientation of the radio group. | `vertical` | -| name | `string` | The name of the RadioGroup, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name_and_radio_buttons). | - | -| value | `string[]` | The current selected value. (controlled). | - | -| defaultValue | `string[]` | The default selected value. (uncontrolled). | - | -| description | `ReactNode` | Radio group description . | - | -| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | Radio group error message. | - | -| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | -| isDisabled | `boolean` | Whether the radio group is disabled. | `false` | -| isRequired | `boolean` | Whether user checkboxes are required on the input before form submission. | `false` | -| isReadOnly | `boolean` | Whether the checkboxes can be selected but not changed by the user. | - | -| isInvalid | `boolean` | Whether the radio group is invalid. | `false` | -| validationState | `valid` \| `invalid` | Whether the inputs should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | `false` | -| disableAnimation | `boolean` | Whether the animation should be disabled. | `false` | -| classNames | `Record<"base"| "wrapper"| "label", string>` | Allows to set custom class names for the radio group slots. | - | +| Attribute | Type | Description | Default | +| ------------------ | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | +| children | `ReactNode` \| `ReactNode[]` | The list of radio elements. | - | +| label | `ReactNode` | The label of the radio group. | - | +| size | `sm` \| `md` \| `lg` | The size of the radios. | `md` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the radios. | `primary` | +| orientation | `horizontal` \| `vertical` | The orientation of the radio group. | `vertical` | +| name | `string` | The name of the RadioGroup, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name_and_radio_buttons). | - | +| value | `string[]` | The current selected value. (controlled). | - | +| defaultValue | `string[]` | The default selected value. (uncontrolled). | - | +| description | `ReactNode` | Radio group description . | - | +| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | Radio group error message. | - | +| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| isDisabled | `boolean` | Whether the radio group is disabled. | `false` | +| isRequired | `boolean` | Whether user checkboxes are required on the input before form submission. | `false` | +| isReadOnly | `boolean` | Whether the checkboxes can be selected but not changed by the user. | - | +| isInvalid | `boolean` | Whether the radio group is invalid. | `false` | +| validationState | `valid` \| `invalid` | Whether the inputs should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | `false` | +| disableAnimation | `boolean` | Whether the animation should be disabled. | `false` | +| classNames | `Record<"base"| "wrapper"| "label", string>` | Allows to set custom class names for the radio group slots. | - | ### RadioGroup Events diff --git a/apps/docs/content/docs/components/range-calendar.mdx b/apps/docs/content/docs/components/range-calendar.mdx index 63be00cd2e..416f212254 100644 --- a/apps/docs/content/docs/components/range-calendar.mdx +++ b/apps/docs/content/docs/components/range-calendar.mdx @@ -201,38 +201,35 @@ Here's the example to customize `topContent` and `bottomContent` to have some pr ### RangeCalendar Props -| Attribute | Type | Description | Default | | -| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | --- | -| value | `RangeValue | null` | The current value (controlled). | - | -| defaultValue | `RangeValue | null` | The default value (uncontrolled). | - | -| minValue | `DateValue` | The minimum allowed date that a user may select. | - | | -| maxValue | `DateValue` | The maximum allowed date that a user may select. | - | | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the time input. | `default` | -| visibleMonths | `number` | The number of months to display at once. Up to 3 months are supported. Passing a number greater than 1 will disable the `showMonthAndYearPickers` prop | `1` | | -| focusedValue | `DateValue` | Controls the currently focused date within the calendar. | - | | -| defaultFocusedValue | `DateValue` | The date that is focused when the calendar first mounts (uncountrolled). | - | | -| calendarWidth | `number` \| `string` | The width to be applied to the calendar component. This value is multiplied by the `visibleMonths` number to determine the total width of the calendar. | `256` | -| pageBehavior | `PageBehavior` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. | `visible` | | -| weekdayStyle | `"narrow" \|"short" \| "long" \| undefined` | The style of weekday names to display in the calendar grid header, e.g. single letter, abbreviation, or full day name. | `narrow` | | -| allowsNonContiguousRanges | `boolean` | When combined with `isDateUnavailable`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. | `false` | | -| showMonthAndYearPickers | `boolean` | Whether the label should be crossed out. | `false` | | -| isDisabled | `boolean` | Whether the calendar is disabled. | `false` | | -| isReadOnly | `boolean` | Whether the calendar value is immutable. | `false` | | -| isInvalid | `boolean` | Whether the current selection is invalid according to application logic. | - | | -| autoFocus | `boolean` | Whether to automatically focus the calendar when it mounts. | `false` | | -| showHelper | `boolean` | Whether to show the description or error message. | `false` | | -| showShadow | `boolean` | Whether to show the shadow in the selected dates. | `false` | -| isHeaderExpanded | `boolean` | Whether the calendar header is expanded. This is only available if the `showMonthAndYearPickers` prop is set to `true`. | `false` | | -| isHeaderDefaultExpanded | `boolean` | Whether the calendar header should be expanded by default.This is only available if the `showMonthAndYearPickers` prop is set to `true`. | `false` | | -| topContent | `ReactNode` | Custom content to be included in the top of the calendar. | - | | -| bottomContent | `ReactNode` | Custom content to be included in the bottom of the calendar. | - | | -| isDateUnavailable | `(date: DateValue) => boolean` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | - | | -| createCalendar | `(calendar: SupportedCalendars) => Calendar \| null` | This function helps to reduce the bundle size by providing a custom calendar system. You can also use the NextUIProvider to provide the createCalendar function to all nested components. | `all
calendars` | | -| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the field. | - | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate time input values when committing (e.g. on blur), and return error messages for invalid values. | - | -| hideDisabledDates | `boolean` | Whether to hide the disabled or invalid dates. | `false` | -| disableAnimation | `boolean` | Whether to disable the animation of the calendar. | `false` | -| classNames | `Record<"base"| "prevButton"| "nextButton"| "headerWrapper" \| "header" \| "title" \| "content" \| "gridWrapper" \| "grid" \| "gridHeader" \| "gridHeaderRow" \| "gridHeaderCell" \| "gridBody" \| "gridBodyRow" \| "cell" \| "cellButton" \| "pickerWrapper" \| "pickerMonthList" \| "pickerYearList" \| "pickerHighlight" \| "pickerItem" \| "helperWrapper" \| "errorMessage", string>` | Allows to set custom class names for the calendar slots. | - | | +| Attribute | Type | Description | Default | | +|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|---| +| value | `RangeValue | null` | The current value (controlled). | - | +| defaultValue | `RangeValue | null` | The default value (uncontrolled). | - | +| minValue | `DateValue` | The minimum allowed date that a user may select. | - | | +| maxValue | `DateValue` | The maximum allowed date that a user may select. | - | | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the time input. | `default` | | +| visibleMonths | `number` | The number of months to display at once. Up to 3 months are supported. | `1` | | +| focusedValue | `DateValue` | Controls the currently focused date within the calendar. | - | | +| defaultFocusedValue | `DateValue` | The date that is focused when the calendar first mounts (uncountrolled). | - | | +| calendarWidth | `number` \| `string` | The width to be applied to the calendar component. This value is multiplied by the `visibleMonths` number to determine the total width of the calendar. | `256` | | +| pageBehavior | `PageBehavior` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. | `visible` | | +| weekdayStyle | `"narrow" \|"short" \| "long" \| undefined` | The style of weekday names to display in the calendar grid header, e.g. single letter, abbreviation, or full day name. | `narrow` | | +| allowsNonContiguousRanges | `boolean` | When combined with `isDateUnavailable`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. | `false` | | +| isDisabled | `boolean` | Whether the calendar is disabled. | `false` | | +| isReadOnly | `boolean` | Whether the calendar value is immutable. | `false` | | +| isInvalid | `boolean` | Whether the current selection is invalid according to application logic. | - | | +| autoFocus | `boolean` | Whether to automatically focus the calendar when it mounts. | `false` | | +| showHelper | `boolean` | Whether to show the description or error message. | `false` | | +| showShadow | `boolean` | Whether to show the shadow in the selected dates. | `false` | | +| topContent | `ReactNode` | Custom content to be included in the top of the calendar. | - | | +| bottomContent | `ReactNode` | Custom content to be included in the bottom of the calendar. | - | | +| isDateUnavailable | `(date: DateValue) => boolean` | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. | - | | +| createCalendar | `(calendar: SupportedCalendars) => Calendar \| null` | This function helps to reduce the bundle size by providing a custom calendar system. You can also use the NextUIProvider to provide the createCalendar function to all nested components. | `all
calendars` | | +| errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the field. | - | | +| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate time input values when committing (e.g. on blur), and return error messages for invalid values. | - | | +| hideDisabledDates | `boolean` | Whether to hide the disabled or invalid dates. | `false` | | +| disableAnimation | `boolean` | Whether to disable the animation of the calendar. | `false` | | +| classNames | `Record<"base"| "prevButton"| "nextButton"| "headerWrapper" \| "header" \| "title" \| "content" \| "gridWrapper" \| "grid" \| "gridHeader" \| "gridHeaderRow" \| "gridHeaderCell" \| "gridBody" \| "gridBodyRow" \| "cell" \| "cellButton" \| "pickerWrapper" \| "pickerMonthList" \| "pickerYearList" \| "pickerHighlight" \| "pickerItem" \| "helperWrapper" \| "errorMessage", string>` | Allows to set custom class names for the calendar slots. | - | | ### RangeCalendar Events @@ -240,7 +237,6 @@ Here's the example to customize `topContent` and `bottomContent` to have some pr | ---------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | onFocusChange | `(date: CalendarDate) => void` | Handler that is called when the focused date changes. | | onChange | `(value: RangeValue>) => void` | Handler that is called when the value changes. | -| onHeaderExpandedChange | `(isExpanded: boolean) => void` | The event handler for the calendar header expanded state. This is only available if the `showMonthAndYearPickers` prop is set to `true`. | #### Supported Calendars diff --git a/apps/docs/content/docs/components/select.mdx b/apps/docs/content/docs/components/select.mdx index 9324dc4c47..ee5d1b4d8e 100644 --- a/apps/docs/content/docs/components/select.mdx +++ b/apps/docs/content/docs/components/select.mdx @@ -70,7 +70,7 @@ You can use the `selectionMode="multiple"` property to allow multiple selection. You can disable specific items by using the `disabledKeys` property. - + ### Required @@ -81,7 +81,7 @@ the end of the label and the select will be required. ### Sizes - + ### Colors @@ -99,7 +99,7 @@ the end of the label and the select will be required. You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`. - + > **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default. @@ -108,7 +108,7 @@ You can change the position of the label by setting the `labelPlacement` propert You can use the `startContent` and `endContent` properties to add content to the start and end of the select. - + ### Item Start & End Content diff --git a/apps/docs/content/docs/components/skeleton.mdx b/apps/docs/content/docs/components/skeleton.mdx index 464c00017a..a0c841cbdb 100644 --- a/apps/docs/content/docs/components/skeleton.mdx +++ b/apps/docs/content/docs/components/skeleton.mdx @@ -9,7 +9,7 @@ import {skeletonContent} from "@/content/components/skeleton"; Skeleton is a placeholder to show a loading state and the expected shape of a component. - + --- diff --git a/apps/docs/content/docs/components/tabs.mdx b/apps/docs/content/docs/components/tabs.mdx index fb94a3e409..af20b8c657 100644 --- a/apps/docs/content/docs/components/tabs.mdx +++ b/apps/docs/content/docs/components/tabs.mdx @@ -274,18 +274,19 @@ You can customize the `Tabs` component by passing custom Tailwind CSS classes to ### Tab Props -| Attribute | Type | Description | Default | -| --------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| children\* | `ReactNode` | The content of the tab. | - | -| title | `ReactNode` | The title of the tab. | - | -| titleValue | `string` | A string representation of the item's contents. Use this when the `title` is not readable. | - | -| href | `string` | A URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href). | - | -| target | `HTMLAttributeAnchorTarget` | The target window for the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target). | - | -| rel | `string` | The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel). | - | -| download | `boolean` \| `string` | Causes the browser to download the linked URL. A string may be provided to suggest a file name. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download). | - | -| ping | `string` | A space-separated list of URLs to ping when the link is followed. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping). | - | -| referrerPolicy | `HTMLAttributeReferrerPolicy` | How much of the referrer to send when following the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy). | - | -| shouldSelectOnPressUp | `boolean` | Whether the tab selection should occur on press up instead of press down. | - | +| Attribute | Type | Description | Default | +|-------------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| children\* | `ReactNode` | The content of the tab. | - | +| title | `ReactNode` | The title of the tab. | - | +| titleValue | `string` | A string representation of the item's contents. Use this when the `title` is not readable. | - | +| href | `string` | A URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href). | - | +| target | `HTMLAttributeAnchorTarget` | The target window for the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target). | - | +| rel | `string` | The relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel). | - | +| download | `boolean` \| `string` | Causes the browser to download the linked URL. A string may be provided to suggest a file name. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download). | - | +| ping | `string` | A space-separated list of URLs to ping when the link is followed. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping). | - | +| referrerPolicy | `HTMLAttributeReferrerPolicy` | How much of the referrer to send when following the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy). | - | +| shouldSelectOnPressUp | `boolean` | Whether the tab selection should occur on press up instead of press down. | - | +| destroyInactiveTabPanel | `boolean` | Whether to destroy inactive tab panel when switching tabs. Inactive tab panels are inert and cannot be interacted with. | `true` | #### Motion Props diff --git a/apps/docs/content/docs/components/textarea.mdx b/apps/docs/content/docs/components/textarea.mdx index 5c2551e153..0148ce2825 100644 --- a/apps/docs/content/docs/components/textarea.mdx +++ b/apps/docs/content/docs/components/textarea.mdx @@ -139,35 +139,36 @@ You can use the `value` and `onValueChange` properties to control the input valu ### Textarea Props -| Attribute | Type | Description | Default | -| ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | --------- | -| children | `ReactNode` | The content of the textarea. | - | -| minRows | `number` | The minimum number of rows to display. | `3` | -| maxRows | `number` | Maximum number of rows up to which the textarea can grow. | `8` | -| cacheMeasurements | `boolean` | Reuse previously computed measurements when computing height of textarea. | `false` | -| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the textarea. | `flat` | -| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the textarea. | `default` | -| size | `sm`\|`md`\|`lg` | The size of the textarea. | `md` | -| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the textarea. | - | -| label | `ReactNode` | The content to display as the label. | - | -| value | `string` | The current value of the textarea (controlled). | - | -| defaultValue | `string` | The default value of the textarea (uncontrolled). | - | -| placeholder | `string` | The placeholder of the textarea. | - | -| startContent | `ReactNode` | Element to be rendered in the left side of the input. | - | -| endContent | `ReactNode` | Element to be rendered in the right side of the input. | - | -| description | `ReactNode` | A description for the textarea. Provides a hint such as specific requirements for what to choose. | - | -| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | An error message for the textarea. | - | -| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | -| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | -| fullWidth | `boolean` | Whether the textarea should take up the width of its parent. | `true` | -| isRequired | `boolean` | Whether user input is required on the textarea before form submission. | `false` | -| isReadOnly | `boolean` | Whether the textarea can be selected but not changed by the user. | | -| isDisabled | `boolean` | Whether the textarea is disabled. | `false` | -| isInvalid | `boolean` | Whether the textarea is invalid. | `false` | -| validationState | `valid` \| `invalid` | Whether the textarea should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | -| disableAutosize | `boolean` | Whether the textarea auto vertically resize should be disabled. | `false` | -| disableAnimation | `boolean` | Whether the textarea should be animated. | `false` | -| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper" | "input" | "description" | "errorMessage", string>` | Allows to set custom class names for the checkbox slots. | - | +| Attribute | Type | Description | Default | +| ------------------ | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| children | `ReactNode` | The content of the textarea. | - | +| minRows | `number` | The minimum number of rows to display. | `3` | +| maxRows | `number` | Maximum number of rows up to which the textarea can grow. | `8` | +| cacheMeasurements | `boolean` | Reuse previously computed measurements when computing height of textarea. | `false` | +| variant | `flat` \| `bordered` \| `faded` \| `underlined` | The variant of the textarea. | `flat` | +| color | `default` \| `primary` \| `secondary` \| `success` \| `warning` \| `danger` | The color of the textarea. | `default` | +| size | `sm`\|`md`\|`lg` | The size of the textarea. | `md` | +| radius | `none` \| `sm` \| `md` \| `lg` \| `full` | The radius of the textarea. | - | +| label | `ReactNode` | The content to display as the label. | - | +| value | `string` | The current value of the textarea (controlled). | - | +| defaultValue | `string` | The default value of the textarea (uncontrolled). | - | +| placeholder | `string` | The placeholder of the textarea. | - | +| startContent | `ReactNode` | Element to be rendered in the left side of the input. | - | +| endContent | `ReactNode` | Element to be rendered in the right side of the input. | - | +| description | `ReactNode` | A description for the textarea. Provides a hint such as specific requirements for what to choose. | - | +| errorMessage | `ReactNode` \| `((v: ValidationResult) => ReactNode)` | An error message for the textarea. | - | +| validate | `(value: string) => ValidationError | true | null | undefined` | Validate input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA.| `aria` | +| labelPlacement | `inside` \| `outside` \| `outside-left` | The position of the label. | `inside` | +| fullWidth | `boolean` | Whether the textarea should take up the width of its parent. | `true` | +| isRequired | `boolean` | Whether user input is required on the textarea before form submission. | `false` | +| isReadOnly | `boolean` | Whether the textarea can be selected but not changed by the user. | | +| isDisabled | `boolean` | Whether the textarea is disabled. | `false` | +| isInvalid | `boolean` | Whether the textarea is invalid. | `false` | +| validationState | `valid` \| `invalid` | Whether the textarea should display its "valid" or "invalid" visual styling. (**Deprecated**) use **isInvalid** instead. | - | +| disableAutosize | `boolean` | Whether the textarea auto vertically resize should be disabled. | `false` | +| disableAnimation | `boolean` | Whether the textarea should be animated. | `false` | +| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper" | "input" | "description" | "errorMessage", string>` | Allows to set custom class names for the checkbox slots. | - | ### Input Events diff --git a/apps/docs/content/docs/components/time-input.mdx b/apps/docs/content/docs/components/time-input.mdx index c35afc725b..6777f25753 100644 --- a/apps/docs/content/docs/components/time-input.mdx +++ b/apps/docs/content/docs/components/time-input.mdx @@ -223,9 +223,10 @@ By default, `TimeInput` displays times in either 12 or 24 hour hour format depen | autoFocus | `boolean` | Whether the element should receive focus on render. | - | | description | `ReactNode` | A description for the field. Provides a hint such as specific requirements for what to choose. | - | | errorMessage | `ReactNode \| (v: ValidationResult) => ReactNode` | An error message for the field. | - | -| validate | `(value: { inputValue: string, selectedKey: React.Key }) => ValidationError | true | null | undefined` | Validate time input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validate | `(value: MappedTimeValue) => ValidationError | true | null | undefined` | Validate time input values when committing (e.g. on blur), and return error messages for invalid values. | - | +| validationBehavior | `native` \| `aria` | Whether to use native HTML form validation to prevent form submission when the value is missing or invalid, or mark the field as required or invalid via ARIA. | `aria` | | disableAnimation | `boolean` | Whether to disable the animation of the time input. | - | -| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper" | "segment" | "helperWrapper" | "input" | "description" | "errorMessage", string>` | Allows to set custom class names for the time input slots. | - | +| classNames | `Record<"base"| "label"| "inputWrapper"| "innerWrapper" | "segment" | "helperWrapper" | "input" | "description" | "errorMessage", string>` | Allows to set custom class names for the time input slots. | - | ### TimeInput Events diff --git a/apps/docs/content/docs/guide/cli.mdx b/apps/docs/content/docs/guide/cli.mdx index cba3fd1f54..1c51a94fe5 100644 --- a/apps/docs/content/docs/guide/cli.mdx +++ b/apps/docs/content/docs/guide/cli.mdx @@ -57,6 +57,7 @@ Usage: nextui [command] Options: -v, --version Show the version number + --no-cache Disable cache, by default data will be cached for 30m after the first request -h, --help Display help for commands Commands: @@ -75,7 +76,7 @@ Commands: Initialize a new NextUI project using the `init` command. This sets up your project with the necessary configurations. ```codeBlock bash -nextui init [my-nextui-app-name] +nextui init my-nextui-app ``` @@ -83,11 +84,44 @@ nextui init [my-nextui-app-name] You will be prompted to configure your project: ```codeBlock bash -? Select a template › - Use arrow-keys. Return to submit. -❯ App - A Next.js 13 with app directory template pre-configured with NextUI (v2) and Tailwind CSS. - Pages - A Next.js 13 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS. +┌ Create a new project +│ +◇ Select a template (Enter to select) +│ ● App (A Next.js 14 with app directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Pages (A Next.js 14 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS.) +│ ○ Vite (A Vite template pre-configured with NextUI (v2) and Tailwind CSS.) +│ +◇ New project name (Enter to skip with default name) +│ my-nextui-app +│ +◇ Select a package manager (Enter to select) +│ ● npm +│ ○ yarn +│ ○ pnpm +│ ○ bun +│ +◇ Template created successfully! +│ +◇ Next steps ───────╮ +│ │ +│ cd my-nextui-app │ +│ npm install │ +│ │ +├────────────────────╯ +│ +└ 🚀 Get started with npm run dev +``` + +Install the dependencies to start the local server: + +```codeBlock bash +cd my-nextui-app && npm install +``` + +Start the local server: + +```codeBlock bash +npm run dev ``` ## add @@ -154,9 +188,16 @@ You will be asked to confirm the upgrade: ```codeBlock bash -╭───────────────────────────────────────────────────────────╮ -│ @nextui-org/button 2.0.24 -> 2.0.27 │ -╰───────────────────────────────────────────────────────────╯ +╭───────────────────────── Component ─────────────────────────╮ +│ @nextui-org/button ^2.0.11 -> ^2.0.31 │ +╰─────────────────────────────────────────────────────────────╯ + +Required min version: @nextui-org/theme>=2.1.0, tailwindcss>=3.4.0 +╭───────────────────── PeerDependencies ─────────────────────╮ +│ @nextui-org/theme 2.0.1 -> 2.1.0 │ +│ tailwindcss ^3.2.3 -> ^3.4.0 │ +╰────────────────────────────────────────────────────────────╯ +2 minor, 1 patch ? Would you like to proceed with the upgrade? › - Use arrow-keys. Return to submit. ❯ Yes diff --git a/apps/docs/content/docs/guide/installation.mdx b/apps/docs/content/docs/guide/installation.mdx index 135af33e90..614cacfcba 100644 --- a/apps/docs/content/docs/guide/installation.mdx +++ b/apps/docs/content/docs/guide/installation.mdx @@ -39,7 +39,17 @@ You will be prompted to configure your project: A Next.js 13 with pages directory template pre-configured with NextUI (v2) and Tailwind CSS. ``` -Once your NextUI project is initialized, you can add individual components using the CLI. For example, to add a button component: +Install the dependencies to start the local server: +```codeBlock bash +cd my-nextui-app && npm install +``` + +Start the local server: +```codeBlock bash +npm run dev +``` + +Once your NextUI project is ready to develop, you can add individual components using the CLI. For example, to add a button component: ```codeBlock bash nextui add button diff --git a/apps/docs/package.json b/apps/docs/package.json index d4ab2d7f03..1e9e1460c3 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -37,25 +37,25 @@ "@nextui-org/use-infinite-scroll": "workspace:*", "@nextui-org/use-is-mobile": "workspace:*", "@radix-ui/react-scroll-area": "^1.0.5", - "@react-aria/focus": "^3.14.3", - "@react-aria/i18n": "^3.8.4", - "@react-aria/interactions": "^3.19.1", - "@react-aria/selection": "^3.17.1", - "@react-aria/ssr": "^3.8.0", - "@react-aria/utils": "^3.21.1", - "@react-aria/virtualizer": "^3.9.4", - "@react-aria/visually-hidden": "^3.8.6", - "@react-stately/data": "^3.10.3", - "@react-stately/layout": "^3.13.3", - "@react-stately/tree": "^3.7.3", + "@react-aria/focus": "^3.16.2", + "@react-aria/i18n": "^3.10.2", + "@react-aria/interactions": "^3.21.1", + "@react-aria/selection": "^3.17.5", + "@react-aria/ssr": "^3.9.2", + "@react-aria/utils": "^3.23.2", + "@react-aria/virtualizer": "^3.9.10", + "@react-aria/visually-hidden": "^3.8.10", + "@react-stately/data": "^3.11.2", + "@react-stately/layout": "^3.13.7", + "@react-stately/tree": "^3.7.6", "@rehooks/local-storage": "^2.4.5", - "@vercel/analytics": "^1.1.1", - "canvas-confetti": "^1.9.0", + "@vercel/analytics": "^1.2.2", + "canvas-confetti": "^1.9.2", "cmdk": "^0.2.0", "color2k": "^2.0.2", "contentlayer": "^0.3.4", "date-fns": "^2.30.0", - "framer-motion": "^11.0.22", + "framer-motion": "^11.1.7", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", "hast-util-to-html": "7.1.2", @@ -101,7 +101,7 @@ "@docusaurus/utils": "2.0.0-beta.3", "@next/bundle-analyzer": "^13.4.6", "@next/env": "^13.4.12", - "@react-types/shared": "^3.22.0", + "@react-types/shared": "^3.22.1", "@tailwindcss/typography": "^0.5.9", "@types/canvas-confetti": "^1.4.2", "@types/lodash": "^4.14.194", diff --git a/apps/docs/public/blog/v2.4.0.jpg b/apps/docs/public/blog/v2.4.0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d40220cf83821ff0dd8a0d15200c8f6e234768ed GIT binary patch literal 121531 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 literal 0 HcmV?d00001 diff --git a/apps/docs/public/blog/v2.4.0_2x.jpg b/apps/docs/public/blog/v2.4.0_2x.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d7f5679e41ad078f44785b422ffa30e11a04f31d GIT binary patch literal 122133 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", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -54,13 +54,13 @@ "@nextui-org/framer-utils": "workspace:*", "@nextui-org/divider": "workspace:*", "@nextui-org/use-aria-accordion": "workspace:*", - "@react-aria/interactions": "^3.21.1", - "@react-aria/focus": "^3.16.2", - "@react-aria/utils": "^3.23.2", - "@react-stately/tree": "^3.7.6", - "@react-aria/button": "^3.9.3", + "@react-aria/interactions": "3.21.1", + "@react-aria/focus": "3.16.2", + "@react-aria/utils": "3.23.2", + "@react-stately/tree": "3.7.6", + "@react-aria/button": "3.9.3", "@react-types/accordion": "3.0.0-alpha.19", - "@react-types/shared": "^3.22.1" + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -75,4 +75,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/accordion/src/accordion-item.tsx b/packages/components/accordion/src/accordion-item.tsx index 21fb94a93d..580c0a1601 100644 --- a/packages/components/accordion/src/accordion-item.tsx +++ b/packages/components/accordion/src/accordion-item.tsx @@ -11,6 +11,7 @@ export interface AccordionItemProps extends UseAccordionItemProps {} const AccordionItem = forwardRef<"button", AccordionItemProps>((props, ref) => { const { Component, + HeadingComponent, classNames, slots, indicator, @@ -89,7 +90,7 @@ const AccordionItem = forwardRef<"button", AccordionItemProps>((props, ref) => { return ( -

+ -

+ {content} ); diff --git a/packages/components/accordion/src/base/accordion-item-base.tsx b/packages/components/accordion/src/base/accordion-item-base.tsx index 7c61e30002..d97226fb38 100644 --- a/packages/components/accordion/src/base/accordion-item-base.tsx +++ b/packages/components/accordion/src/base/accordion-item-base.tsx @@ -4,6 +4,7 @@ import type { SlotsToClasses, } from "@nextui-org/theme"; +import {As} from "@nextui-org/system"; import {ItemProps, BaseItem} from "@nextui-org/aria-utils"; import {FocusableProps, PressEvents} from "@react-types/shared"; import {ReactNode, MouseEventHandler} from "react"; @@ -85,6 +86,12 @@ export interface Props * ``` */ classNames?: SlotsToClasses; + /** + * Customizable heading tag for Web accessibility: + * use headings to describe content and use them consistently and semantically. + * This will help all users to better find the content they are looking for. + */ + HeadingComponent?: As; } export type AccordionItemBaseProps = Props & AccordionItemVariantProps; diff --git a/packages/components/accordion/src/use-accordion-item.ts b/packages/components/accordion/src/use-accordion-item.ts index ec4f85ef09..25ef283d2a 100644 --- a/packages/components/accordion/src/use-accordion-item.ts +++ b/packages/components/accordion/src/use-accordion-item.ts @@ -1,4 +1,4 @@ -import {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; import {useFocusRing} from "@react-aria/focus"; import {accordionItem} from "@nextui-org/theme"; import {clsx, callAllHandlers, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -39,6 +39,8 @@ export type UseAccordionItemProps = Props & Omit; export function useAccordionItem(props: UseAccordionItemProps) { + const globalContext = useProviderContext(); + const {ref, as, item, onFocusChange} = props; const { @@ -55,9 +57,10 @@ export function useAccordionItem(props: UseAccordionItemP classNames: classNamesProp = {}, isDisabled: isDisabledProp = false, hideIndicator = false, - disableAnimation = false, + disableAnimation = globalContext?.disableAnimation ?? false, keepContentMounted = false, disableIndicatorAnimation = false, + HeadingComponent = as || "h2", onPress, onPressStart, onPressEnd, @@ -169,8 +172,9 @@ export function useAccordionItem(props: UseAccordionItemP otherProps.onBlur, item.props?.onBlur, ), - ...mergeProps(buttonProps, hoverProps, pressProps, props), - onClick: chain(pressProps.onClick, onClick), + ...mergeProps(buttonProps, hoverProps, pressProps, props, { + onClick: chain(pressProps.onClick, onClick), + }), }; }; @@ -237,6 +241,7 @@ export function useAccordionItem(props: UseAccordionItemP return { Component, + HeadingComponent, item, slots, classNames, diff --git a/packages/components/accordion/src/use-accordion.ts b/packages/components/accordion/src/use-accordion.ts index 7feee76d9d..7f9e91c47a 100644 --- a/packages/components/accordion/src/use-accordion.ts +++ b/packages/components/accordion/src/use-accordion.ts @@ -1,8 +1,8 @@ -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import type {SelectionBehavior, MultipleSelection} from "@react-types/shared"; import type {AriaAccordionProps} from "@react-types/accordion"; import type {AccordionGroupVariantProps} from "@nextui-org/theme"; +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {ReactRef, filterDOMProps} from "@nextui-org/react-utils"; import React, {Key, useCallback} from "react"; import {TreeState, useTreeState} from "@react-stately/tree"; @@ -73,6 +73,8 @@ export type ValuesType = { }; export function useAccordion(props: UseAccordionProps) { + const globalContext = useProviderContext(); + const { ref, as, @@ -97,7 +99,7 @@ export function useAccordion(props: UseAccordionProps) { isDisabled = false, showDivider = true, hideIndicator = false, - disableAnimation = false, + disableAnimation = globalContext?.disableAnimation ?? false, disableIndicatorAnimation = false, itemClasses, ...otherProps diff --git a/packages/components/autocomplete/CHANGELOG.md b/packages/components/autocomplete/CHANGELOG.md index 9b008ffa99..ed532e3415 100644 --- a/packages/components/autocomplete/CHANGELOG.md +++ b/packages/components/autocomplete/CHANGELOG.md @@ -1,5 +1,30 @@ # @nextui-org/autocomplete +## 2.1.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2889](https://github.com/nextui-org/nextui/pull/2889) [`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Update React Aria packages + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a)]: + - @nextui-org/popover@2.1.22 + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/listbox@2.1.20 + - @nextui-org/button@2.0.32 + - @nextui-org/input@2.2.0 + - @nextui-org/scroll-shadow@2.1.16 + - @nextui-org/spinner@2.0.29 + ## 2.0.16 ### Patch Changes diff --git a/packages/components/autocomplete/__tests__/autocomplete.test.tsx b/packages/components/autocomplete/__tests__/autocomplete.test.tsx index 1c4c47103f..4574ae88de 100644 --- a/packages/components/autocomplete/__tests__/autocomplete.test.tsx +++ b/packages/components/autocomplete/__tests__/autocomplete.test.tsx @@ -1,8 +1,9 @@ import * as React from "react"; -import {act, render} from "@testing-library/react"; +import {within, render, renderHook, act} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import {useForm} from "react-hook-form"; -import {Autocomplete, AutocompleteItem, AutocompleteSection} from "../src"; +import {Autocomplete, AutocompleteItem, AutocompleteProps, AutocompleteSection} from "../src"; import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../../modal/src"; type Item = { @@ -47,21 +48,23 @@ const itemsSectionData = [ }, ]; +const AutocompleteExample = (props: Partial = {}) => ( + + + Penguin + + + Zebra + + + Shark + + +); + describe("Autocomplete", () => { it("should render correctly", () => { - const wrapper = render( - - - Penguin - - - Zebra - - - Shark - - , - ); + const wrapper = render(); expect(() => wrapper.unmount()).not.toThrow(); }); @@ -82,6 +85,7 @@ describe("Autocomplete", () => { , ); + expect(ref.current).not.toBeNull(); }); @@ -136,7 +140,136 @@ describe("Autocomplete", () => { expect(() => wrapper.unmount()).not.toThrow(); }); - it("should close dropdown when clicking outside autocomplete", async () => { + it("should focus when clicking autocomplete", async () => { + const wrapper = render( + + + Penguin + + + Zebra + + + Shark + + , + ); + + const autocomplete = wrapper.getByTestId("autocomplete"); + + // open the select listbox + await act(async () => { + await userEvent.click(autocomplete); + }); + + // assert that the autocomplete listbox is open + expect(autocomplete).toHaveAttribute("aria-expanded", "true"); + + // assert that input is focused + expect(autocomplete).toHaveFocus(); + }); + + it("should clear value after clicking clear button", async () => { + const wrapper = render( + + + Penguin + + + Zebra + + + Shark + + , + ); + + const autocomplete = wrapper.getByTestId("autocomplete"); + + // open the select listbox + await act(async () => { + await userEvent.click(autocomplete); + }); + + // assert that the autocomplete listbox is open + expect(autocomplete).toHaveAttribute("aria-expanded", "true"); + + let options = wrapper.getAllByRole("option"); + + // select the target item + await act(async () => { + await userEvent.click(options[0]); + }); + + const {container} = wrapper; + + const clearButton = container.querySelector( + "[data-slot='inner-wrapper'] button:nth-of-type(1)", + )!; + + expect(clearButton).not.toBeNull(); + + // select the target item + await act(async () => { + await userEvent.click(clearButton); + }); + + // assert that the input has empty value + expect(autocomplete).toHaveValue(""); + + // assert that input is focused + expect(autocomplete).toHaveFocus(); + }); + + it("should open and close listbox by clicking selector button", async () => { + const wrapper = render( + + + Penguin + + + Zebra + + + Shark + + , + ); + + const {container} = wrapper; + + const selectorButton = container.querySelector( + "[data-slot='inner-wrapper'] button:nth-of-type(2)", + )!; + + expect(selectorButton).not.toBeNull(); + + const autocomplete = wrapper.getByTestId("autocomplete"); + + // open the select listbox by clicking selector button + await act(async () => { + await userEvent.click(selectorButton); + }); + + // assert that the autocomplete listbox is open + expect(autocomplete).toHaveAttribute("aria-expanded", "true"); + + // assert that input is focused + expect(autocomplete).toHaveFocus(); + + // close the select listbox by clicking selector button again + await act(async () => { + await userEvent.click(selectorButton); + }); + + // assert that the autocomplete listbox is closed + expect(autocomplete).toHaveAttribute("aria-expanded", "false"); + + // assert that input is still focused + expect(autocomplete).toHaveFocus(); + }); + + it("should close listbox when clicking outside autocomplete", async () => { const wrapper = render( { const autocomplete = wrapper.getByTestId("close-when-clicking-outside-test"); - // open the select dropdown + // open the select listbox await act(async () => { await userEvent.click(autocomplete); }); - // assert that the autocomplete dropdown is open + // assert that the autocomplete listbox is open expect(autocomplete).toHaveAttribute("aria-expanded", "true"); // click outside the autocomplete component @@ -172,9 +305,12 @@ describe("Autocomplete", () => { // assert that the autocomplete is closed expect(autocomplete).toHaveAttribute("aria-expanded", "false"); + + // assert that input is not focused + expect(autocomplete).not.toHaveFocus(); }); - it("should close dropdown when clicking outside autocomplete with modal open", async () => { + it("should close listbox when clicking outside autocomplete with modal open", async () => { const wrapper = render( @@ -203,12 +339,12 @@ describe("Autocomplete", () => { const autocomplete = wrapper.getByTestId("close-when-clicking-outside-test"); - // open the autocomplete dropdown + // open the autocomplete listbox await act(async () => { await userEvent.click(autocomplete); }); - // assert that the autocomplete dropdown is open + // assert that the autocomplete listbox is open expect(autocomplete).toHaveAttribute("aria-expanded", "true"); // click outside the autocomplete component @@ -216,7 +352,313 @@ describe("Autocomplete", () => { await userEvent.click(document.body); }); - // assert that the autocomplete dropdown is closed + // assert that the autocomplete listbox is closed + expect(autocomplete).toHaveAttribute("aria-expanded", "false"); + + // assert that input is not focused + expect(autocomplete).not.toHaveFocus(); + }); + + it("should set the input after selection", async () => { + const wrapper = render( + + + Penguin + + + Zebra + + + Shark + + , + ); + + const autocomplete = wrapper.getByTestId("autocomplete"); + + // open the listbox + await act(async () => { + await userEvent.click(autocomplete); + }); + + // assert that the autocomplete listbox is open + expect(autocomplete).toHaveAttribute("aria-expanded", "true"); + + // assert that input is focused + expect(autocomplete).toHaveFocus(); + + let options = wrapper.getAllByRole("option"); + + expect(options.length).toBe(3); + + // select the target item + await act(async () => { + await userEvent.click(options[0]); + }); + + // assert that the input has target selection + expect(autocomplete).toHaveValue("Penguin"); + }); + + it("should close listbox by clicking another autocomplete", async () => { + const wrapper = render( + <> + + + Penguin + + + Zebra + + + Shark + + + + + Penguin + + + Zebra + + + Shark + + + , + ); + + const {container} = wrapper; + + const autocomplete = wrapper.getByTestId("autocomplete"); + + const autocomplete2 = wrapper.getByTestId("autocomplete2"); + + const innerWrappers = container.querySelectorAll("[data-slot='inner-wrapper']"); + + const selectorButton = innerWrappers[0].querySelector("button:nth-of-type(2)")!; + + const selectorButton2 = innerWrappers[1].querySelector("button:nth-of-type(2)")!; + + expect(selectorButton).not.toBeNull(); + + expect(selectorButton2).not.toBeNull(); + + // open the select listbox by clicking selector button in the first autocomplete + await act(async () => { + await userEvent.click(selectorButton); + }); + + // assert that the first autocomplete listbox is open + expect(autocomplete).toHaveAttribute("aria-expanded", "true"); + + // assert that input is focused + expect(autocomplete).toHaveFocus(); + + // close the select listbox by clicking the second autocomplete + await act(async () => { + await userEvent.click(selectorButton2); + }); + + // assert that the first autocomplete listbox is closed expect(autocomplete).toHaveAttribute("aria-expanded", "false"); + + // assert that the second autocomplete listbox is open + expect(autocomplete2).toHaveAttribute("aria-expanded", "true"); + + // assert that the first autocomplete is not focused + expect(autocomplete).not.toHaveFocus(); + + // assert that the second autocomplete is focused + expect(autocomplete2).toHaveFocus(); + }); + + describe("validation", () => { + let user; + + beforeAll(() => { + user = userEvent.setup(); + }); + + describe("validationBehavior=native", () => { + it("supports isRequired", async () => { + const {getByTestId, getByRole, findByRole} = render( +
+ + , + ); + + const input = getByRole("combobox") as HTMLInputElement; + + expect(input).toHaveAttribute("required"); + expect(input).not.toHaveAttribute("aria-required"); + expect(input).not.toHaveAttribute("aria-describedby"); + expect(input.validity.valid).toBe(false); + + act(() => { + (getByTestId("form") as HTMLFormElement).checkValidity(); + }); + + expect(input).toHaveAttribute("aria-describedby"); + expect(document.getElementById(input.getAttribute("aria-describedby")!)).toHaveTextContent( + "Constraints not satisfied", + ); + + await user.click(input); + await user.keyboard("pe"); + + const listbox = await findByRole("listbox"); + const items = within(listbox).getAllByRole("option"); + + await user.click(items[0]); + expect(input).toHaveAttribute("aria-describedby"); + }); + }); + + describe("validationBehavior=aria", () => { + it("supports validate function", async () => { + let {getByRole, findByRole} = render( +
+ (v === "Penguin" ? "Invalid value" : null)} + validationBehavior="aria" + /> + , + ); + + const input = getByRole("combobox") as HTMLInputElement; + + expect(input).toHaveAttribute("aria-describedby"); + expect(input).toHaveAttribute("aria-invalid", "true"); + expect(document.getElementById(input.getAttribute("aria-describedby")!)).toHaveTextContent( + "Invalid value", + ); + expect(input.validity.valid).toBe(true); + + await user.tab(); + await user.click(); + // open the select dropdown + await user.keyboard("{ArrowDown}"); + + const listbox = await findByRole("listbox"); + const item = within(listbox).getByRole("option", {name: "Zebra"}); + + await user.click(item); + + expect(input).not.toHaveAttribute("aria-describedby"); + expect(input).not.toHaveAttribute("aria-invalid"); + }); + }); + }); +}); + +describe("Autocomplete with React Hook Form", () => { + let autocomplete1: HTMLInputElement; + let autocomplete2: HTMLInputElement; + let autocomplete3: HTMLInputElement; + let submitButton: HTMLButtonElement; + let wrapper: any; + let onSubmit: () => void; + + beforeEach(() => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + withDefaultValue: "cat", + withoutDefaultValue: "", + requiredField: "", + }, + }), + ); + + const { + handleSubmit, + register, + formState: {errors}, + } = result.current; + + onSubmit = jest.fn(); + + wrapper = render( +
+ + {(item) => {item.label}} + + + {(item) => {item.label}} + + + {(item) => {item.label}} + + {errors.requiredField && This field is required} + +
, + ); + + autocomplete1 = wrapper.getByTestId("autocomplete-1"); + autocomplete2 = wrapper.getByTestId("autocomplete-2"); + autocomplete3 = wrapper.getByTestId("autocomplete-3"); + submitButton = wrapper.getByTestId("submit-button"); + }); + + it("should work with defaultValues", () => { + expect(autocomplete1).toHaveValue("Cat"); + expect(autocomplete2).toHaveValue(""); + expect(autocomplete3).toHaveValue(""); + }); + + it("should not submit form when required field is empty", async () => { + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(0); + }); + + it("should submit form when required field is not empty", async () => { + const user = userEvent.setup(); + + await user.click(autocomplete3); + + expect(autocomplete3).toHaveAttribute("aria-expanded", "true"); + + let listboxItems = wrapper.getAllByRole("option"); + + await user.click(listboxItems[1]); + + expect(autocomplete3).toHaveValue("Dog"); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/components/autocomplete/package.json b/packages/components/autocomplete/package.json index f3e7e0caa3..c40e475797 100644 --- a/packages/components/autocomplete/package.json +++ b/packages/components/autocomplete/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/autocomplete", - "version": "2.0.16", + "version": "2.1.0", "description": "An autocomplete combines a text input with a listbox, allowing users to filter a list of options to items matching a query.", "keywords": [ "autocomplete" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -53,15 +53,15 @@ "@nextui-org/use-aria-button": "workspace:*", "@nextui-org/shared-icons": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/combobox": "^3.8.4", - "@react-aria/focus": "^3.16.2", - "@react-aria/i18n": "^3.10.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/combobox": "^3.8.2", - "@react-types/combobox": "^3.10.1", - "@react-types/shared": "^3.22.1" + "@react-aria/combobox": "3.8.4", + "@react-aria/focus": "3.16.2", + "@react-aria/i18n": "3.10.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/combobox": "3.8.2", + "@react-types/combobox": "3.10.1", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -70,11 +70,12 @@ "@nextui-org/chip": "workspace:*", "@nextui-org/stories-utils": "workspace:*", "@nextui-org/use-infinite-scroll": "workspace:*", - "@react-stately/data": "^3.11.0", + "@react-stately/data": "3.11.2", "framer-motion": "^11.0.28", "clean-package": "2.2.0", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-hook-form": "^7.51.3" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/autocomplete/src/autocomplete.tsx b/packages/components/autocomplete/src/autocomplete.tsx index 7da035b20f..2eadca4ebd 100644 --- a/packages/components/autocomplete/src/autocomplete.tsx +++ b/packages/components/autocomplete/src/autocomplete.tsx @@ -15,7 +15,6 @@ interface Props extends UseAutocompleteProps {} function Autocomplete(props: Props, ref: ForwardedRef) { const { Component, - state, isOpen, disableAnimation, selectorIcon = , @@ -33,7 +32,7 @@ function Autocomplete(props: Props, ref: ForwardedRef({...props, ref}); const popoverContent = isOpen ? ( - + @@ -58,10 +57,10 @@ function Autocomplete(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type AutocompleteProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(Autocomplete) as ( +export default forwardRef(Autocomplete) as ( props: AutocompleteProps, ) => ReactElement; diff --git a/packages/components/autocomplete/src/use-autocomplete.ts b/packages/components/autocomplete/src/use-autocomplete.ts index 33ee107577..83e8a8caea 100644 --- a/packages/components/autocomplete/src/use-autocomplete.ts +++ b/packages/components/autocomplete/src/use-autocomplete.ts @@ -1,6 +1,7 @@ import type {AutocompleteVariantProps, SlotsToClasses, AutocompleteSlots} from "@nextui-org/theme"; +import type {DOMAttributes, HTMLNextUIProps, PropGetter} from "@nextui-org/system"; -import {DOMAttributes, HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import {mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; import {autocomplete} from "@nextui-org/theme"; import {useFilter} from "@react-aria/i18n"; @@ -17,6 +18,7 @@ import {chain, mergeProps} from "@react-aria/utils"; import {ButtonProps} from "@nextui-org/button"; import {AsyncLoadable, PressEvent} from "@react-types/shared"; import {useComboBox} from "@react-aria/combobox"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; interface Props extends Omit, keyof ComboBoxProps> { /** @@ -111,17 +113,17 @@ interface Props extends Omit, keyof ComboBoxProps } export type UseAutocompleteProps = Props & - Omit< - InputProps, - "children" | "value" | "isClearable" | "defaultValue" | "classNames" | "validationBehavior" - > & - Omit, "validationBehavior"> & + Omit & + ComboBoxProps & AsyncLoadable & AutocompleteVariantProps; export function useAutocomplete(originalProps: UseAutocompleteProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, autocomplete.variantKeys); - const disableAnimation = originalProps.disableAnimation ?? false; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; // TODO: Remove disableClearable prop in the next minor release. const isClearable = @@ -156,6 +158,7 @@ export function useAutocomplete(originalProps: UseAutocomplete clearButtonProps = {}, showScrollIndicators = true, allowsCustomValue = false, + validationBehavior = globalContext?.validationBehavior ?? "aria", className, classNames, errorMessage, @@ -172,7 +175,7 @@ export function useAutocomplete(originalProps: UseAutocomplete ...originalProps, children, menuTrigger, - validationBehavior: "native", + validationBehavior, shouldCloseOnBlur, allowsEmptyCollection, defaultFilter: defaultFilter && typeof defaultFilter === "function" ? defaultFilter : contains, @@ -199,6 +202,9 @@ export function useAutocomplete(originalProps: UseAutocomplete const inputRef = useDOMRef(ref); const scrollShadowRef = useDOMRef(scrollRefProp); + // control the input focus behaviours internally + const shouldFocus = useRef(false); + const { buttonProps, inputProps, @@ -208,7 +214,7 @@ export function useAutocomplete(originalProps: UseAutocomplete validationErrors, } = useComboBox( { - validationBehavior: "native", + validationBehavior, ...originalProps, inputRef, buttonRef, @@ -325,12 +331,14 @@ export function useAutocomplete(originalProps: UseAutocomplete } }, [isOpen]); - // unfocus the input when the popover closes & there's no selected item & no allows custom value + // react aria has different focus strategies internally + // hence, handle focus behaviours on our side for better flexibilty useEffect(() => { - if (!isOpen && !state.selectedItem && inputRef.current && !allowsCustomValue) { - inputRef.current.blur(); - } - }, [isOpen, allowsCustomValue]); + const action = shouldFocus.current || isOpen ? "focus" : "blur"; + + inputRef?.current?.[action](); + if (action === "blur") shouldFocus.current = false; + }, [shouldFocus.current, isOpen]); // to prevent the error message: // stopPropagation is now the default behavior for events in React Spectrum. @@ -363,6 +371,7 @@ export function useAutocomplete(originalProps: UseAutocomplete const onClear = useCallback(() => { state.setInputValue(""); state.setSelectedKey(null); + state.close(); }, [state]); const onFocus = useCallback( @@ -392,17 +401,20 @@ export function useAutocomplete(originalProps: UseAutocomplete const getClearButtonProps = () => ({ ...mergeProps(buttonProps, slotsProps.clearButtonProps), + // disable original focus and state toggle from react aria + onPressStart: () => {}, onPress: (e: PressEvent) => { slotsProps.clearButtonProps?.onPress?.(e); if (state.selectedItem) { onClear(); } else { - const inputFocused = inputRef.current === document.activeElement; - - allowsCustomValue && state.setInputValue(""); - !inputFocused && onFocus(true); + if (allowsCustomValue) { + state.setInputValue(""); + state.close(); + } } + inputRef?.current?.focus(); }, "data-visible": !!state.selectedItem || state.inputValue?.length > 0, className: slots.clearButton({ @@ -416,6 +428,7 @@ export function useAutocomplete(originalProps: UseAutocomplete ...inputProps, ...slotsProps.inputProps, isInvalid, + validationBehavior, errorMessage: typeof errorMessage === "function" ? errorMessage({isInvalid, validationErrors, validationDetails}) @@ -429,18 +442,19 @@ export function useAutocomplete(originalProps: UseAutocomplete ref: listBoxRef, ...mergeProps(slotsProps.listboxProps, listBoxProps, { shouldHighlightOnFocus: true, - shouldUseVirtualFocus: false, }), } as ListboxProps); const getPopoverProps = (props: DOMAttributes = {}) => { + const popoverProps = mergeProps(slotsProps.popoverProps, props); + return { state, ref: popoverRef, triggerRef: inputWrapperRef, scrollRef: listBoxRef, triggerType: "listbox", - ...mergeProps(slotsProps.popoverProps, props), + ...popoverProps, classNames: { content: slots.popoverContent({ class: clsx( @@ -450,6 +464,10 @@ export function useAutocomplete(originalProps: UseAutocomplete ), }), }, + shouldCloseOnInteractOutside: popoverProps?.shouldCloseOnInteractOutside + ? popoverProps.shouldCloseOnInteractOutside + : (element: Element) => + ariaShouldCloseOnInteractOutside(element, inputWrapperRef, state, shouldFocus), } as unknown as PopoverProps; }; diff --git a/packages/components/autocomplete/stories/autocomplete.stories.tsx b/packages/components/autocomplete/stories/autocomplete.stories.tsx index a3b422bbcc..022e249e6c 100644 --- a/packages/components/autocomplete/stories/autocomplete.stories.tsx +++ b/packages/components/autocomplete/stories/autocomplete.stories.tsx @@ -2,6 +2,7 @@ import type {ValidationResult} from "@react-types/shared"; import React, {Key} from "react"; import {Meta} from "@storybook/react"; +import {useForm} from "react-hook-form"; import {autocomplete, input, button} from "@nextui-org/theme"; import { Pokemon, @@ -63,6 +64,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, decorators: [ (Story) => ( @@ -686,6 +693,45 @@ const CustomStylesWithCustomItemsTemplate = ({color, ...args}: AutocompleteProps ); }; +const WithReactHookFormTemplate = (args: AutocompleteProps) => { + const { + register, + formState: {errors}, + handleSubmit, + } = useForm({ + defaultValues: { + withDefaultValue: "cat", + withoutDefaultValue: "", + requiredField: "", + }, + }); + + const onSubmit = (data: any) => { + // eslint-disable-next-line no-console + console.log(data); + alert("Submitted value: " + JSON.stringify(data)); + }; + + return ( +
+ + {items} + + + {items} + + + {items} + + + {errors.requiredField && This field is required} + +
+ ); +}; + export const Default = { render: Template, args: { @@ -733,15 +779,6 @@ export const DisabledOptions = { }, }; -export const WithDescription = { - render: MirrorTemplate, - - args: { - ...defaultProps, - description: "Select your favorite animal", - }, -}; - export const LabelPlacement = { render: LabelPlacementTemplate, @@ -782,6 +819,27 @@ export const EndContent = { }, }; +export const IsInvalid = { + render: Template, + + args: { + ...defaultProps, + isInvalid: true, + variant: "bordered", + defaultSelectedKey: "dog", + errorMessage: "Please select a valid animal", + }, +}; + +export const WithDescription = { + render: MirrorTemplate, + + args: { + ...defaultProps, + description: "Select your favorite animal", + }, +}; + export const WithoutScrollShadow = { render: Template, @@ -838,76 +896,74 @@ export const WithValidation = { args: { ...defaultProps, - isRequired: true, + label: "Select Cat or Dog", validate: (value) => { - if (value.inputValue === "Cat" || value.selectedKey === "dog") { - return "Please select a valid animal"; + if (value.selectedKey == null || value.selectedKey === "cat" || value.selectedKey === "dog") { + return; } + + return "Please select a valid animal"; }, }, }; -export const IsInvalid = { - render: Template, +export const WithSections = { + render: WithSectionsTemplate, args: { ...defaultProps, - isInvalid: true, - variant: "bordered", - defaultSelectedKey: "dog", - errorMessage: "Please select a valid animal", }, }; -export const Controlled = { - render: ControlledTemplate, +export const WithCustomSectionsStyles = { + render: WithCustomSectionsStylesTemplate, args: { ...defaultProps, }, }; -export const CustomSelectorIcon = { - render: Template, +export const WithAriaLabel = { + render: WithAriaLabelTemplate, args: { ...defaultProps, - disableSelectorIconRotation: true, - selectorIcon: , + label: "Select an animal 🐹", + "aria-label": "Select an animal", }, }; -export const CustomItems = { - render: CustomItemsTemplate, +export const WithReactHookForm = { + render: WithReactHookFormTemplate, args: { ...defaultProps, }, }; -export const WithSections = { - render: WithSectionsTemplate, +export const Controlled = { + render: ControlledTemplate, args: { ...defaultProps, }, }; -export const WithCustomSectionsStyles = { - render: WithCustomSectionsStylesTemplate, +export const CustomSelectorIcon = { + render: Template, args: { ...defaultProps, + disableSelectorIconRotation: true, + selectorIcon: , }, }; -export const WithAriaLabel = { - render: WithAriaLabelTemplate, +export const CustomItems = { + render: CustomItemsTemplate, args: { ...defaultProps, - label: "Select an animal 🐹", - "aria-label": "Select an animal", }, }; diff --git a/packages/components/avatar/CHANGELOG.md b/packages/components/avatar/CHANGELOG.md index c93386fb1f..c4b28b2843 100644 --- a/packages/components/avatar/CHANGELOG.md +++ b/packages/components/avatar/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/avatar +## 2.0.28 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.27 ### Patch Changes diff --git a/packages/components/avatar/package.json b/packages/components/avatar/package.json index 0b691c8188..3602905ba5 100644 --- a/packages/components/avatar/package.json +++ b/packages/components/avatar/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/avatar", - "version": "2.0.27", + "version": "2.0.28", "description": "The Avatar component is used to represent a user, and displays the profile picture, initials or fallback icon.", "keywords": [ "avatar" @@ -43,9 +43,9 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-image": "workspace:*", - "@react-aria/interactions": "^3.21.1", - "@react-aria/focus": "^3.16.2", - "@react-aria/utils": "^3.23.2" + "@react-aria/interactions": "3.21.1", + "@react-aria/focus": "3.16.2", + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -57,4 +57,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/avatar/src/use-avatar.ts b/packages/components/avatar/src/use-avatar.ts index 156b22f4b2..a7a89be77b 100644 --- a/packages/components/avatar/src/use-avatar.ts +++ b/packages/components/avatar/src/use-avatar.ts @@ -1,7 +1,7 @@ import type {AvatarSlots, AvatarVariantProps, SlotsToClasses} from "@nextui-org/theme"; import {avatar} from "@nextui-org/theme"; -import {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; import {mergeProps} from "@react-aria/utils"; import {useDOMRef} from "@nextui-org/react-utils"; import {clsx, safeText, dataAttr} from "@nextui-org/shared-utils"; @@ -96,7 +96,8 @@ interface Props extends HTMLNextUIProps<"span"> { export type UseAvatarProps = Props & Omit; -export function useAvatar(props: UseAvatarProps = {}) { +export function useAvatar(originalProps: UseAvatarProps = {}) { + const globalContext = useProviderContext(); const groupContext = useAvatarGroupContext(); const isInGroup = !!groupContext; @@ -124,7 +125,7 @@ export function useAvatar(props: UseAvatarProps = {}) { className, onError, ...otherProps - } = props; + } = originalProps; const Component = as || "span"; @@ -133,6 +134,8 @@ export function useAvatar(props: UseAvatarProps = {}) { const {isFocusVisible, isFocused, focusProps} = useFocusRing(); const {isHovered, hoverProps} = useHover({isDisabled}); + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const imageStatus = useImage({src, onError, ignoreFallback}); @@ -156,9 +159,19 @@ export function useAvatar(props: UseAvatarProps = {}) { isBordered, isDisabled, isInGroup, + disableAnimation, isInGridGroup: groupContext?.isGrid ?? false, }), - [color, radius, size, isBordered, isDisabled, isInGroup, groupContext?.isGrid], + [ + color, + radius, + size, + isBordered, + isDisabled, + disableAnimation, + isInGroup, + groupContext?.isGrid, + ], ); const baseStyles = clsx(classNames?.base, className); @@ -186,11 +199,12 @@ export function useAvatar(props: UseAvatarProps = {}) { (props = {}) => ({ ref: imgRef, src: src, + disableAnimation, "data-loaded": dataAttr(isImgLoaded), className: slots.img({class: classNames?.img}), ...mergeProps(imgProps, props), }), - [slots, isImgLoaded, imgProps, src, imgRef], + [slots, isImgLoaded, imgProps, disableAnimation, src, imgRef], ); return { diff --git a/packages/components/badge/CHANGELOG.md b/packages/components/badge/CHANGELOG.md index 3c7e97a8eb..8bdb2d9bab 100644 --- a/packages/components/badge/CHANGELOG.md +++ b/packages/components/badge/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/badge +## 2.0.28 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.27 ### Patch Changes diff --git a/packages/components/badge/package.json b/packages/components/badge/package.json index d47acaa993..a0cfff41dd 100644 --- a/packages/components/badge/package.json +++ b/packages/components/badge/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/badge", - "version": "2.0.27", + "version": "2.0.28", "description": "Badges are used as a small numerical value or status descriptor for UI elements.", "keywords": [ "badge" @@ -36,14 +36,15 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "@nextui-org/theme": ">=2.1.0" + "@nextui-org/theme": ">=2.1.0", + "@nextui-org/system": ">=2.0.0" }, "dependencies": { - "@nextui-org/system-rsc": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*" }, "devDependencies": { + "@nextui-org/system": "workspace:*", "@nextui-org/theme": "workspace:*", "@nextui-org/avatar": "workspace:*", "@nextui-org/shared-icons": "workspace:*", @@ -53,4 +54,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/badge/src/badge.tsx b/packages/components/badge/src/badge.tsx index 733ed492e5..ae05264fa5 100644 --- a/packages/components/badge/src/badge.tsx +++ b/packages/components/badge/src/badge.tsx @@ -1,4 +1,4 @@ -import {forwardRef} from "@nextui-org/system-rsc"; +import {forwardRef} from "@nextui-org/system"; import {UseBadgeProps, useBadge} from "./use-badge"; diff --git a/packages/components/badge/src/use-badge.ts b/packages/components/badge/src/use-badge.ts index db304c3733..e566ad034c 100644 --- a/packages/components/badge/src/use-badge.ts +++ b/packages/components/badge/src/use-badge.ts @@ -1,9 +1,9 @@ import type {BadgeSlots, BadgeVariantProps, SlotsToClasses} from "@nextui-org/theme"; import type {ReactNode} from "react"; -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system-rsc"; +import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import {badge} from "@nextui-org/theme"; -import {mapPropsVariants} from "@nextui-org/system-rsc"; +import {mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {clsx, objectToDeps} from "@nextui-org/shared-utils"; import {ReactRef} from "@nextui-org/react-utils"; import {useMemo} from "react"; @@ -45,6 +45,10 @@ interface Props extends HTMLNextUIProps<"span", "content"> { export type UseBadgeProps = Props & BadgeVariantProps; export function useBadge(originalProps: UseBadgeProps) { + const globalContext = useProviderContext(); + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; + const [props, variantProps] = mapPropsVariants(originalProps, badge.variantKeys); const {as, children, className, content, classNames, ...otherProps} = props; @@ -87,7 +91,7 @@ export function useBadge(originalProps: UseBadgeProps) { content, slots, classNames, - disableAnimation: originalProps?.disableAnimation, + disableAnimation, isInvisible: originalProps?.isInvisible, getBadgeProps, }; diff --git a/packages/components/badge/tsup.config.ts b/packages/components/badge/tsup.config.ts index e7ef417947..3e2bcff6cc 100644 --- a/packages/components/badge/tsup.config.ts +++ b/packages/components/badge/tsup.config.ts @@ -4,4 +4,5 @@ export default defineConfig({ clean: true, target: "es2019", format: ["cjs", "esm"], + banner: {js: '"use client";'}, }); diff --git a/packages/components/breadcrumbs/CHANGELOG.md b/packages/components/breadcrumbs/CHANGELOG.md index d40dd8fcc0..5b421ce028 100644 --- a/packages/components/breadcrumbs/CHANGELOG.md +++ b/packages/components/breadcrumbs/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/breadcrumbs +## 2.0.8 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.7 ### Patch Changes diff --git a/packages/components/breadcrumbs/package.json b/packages/components/breadcrumbs/package.json index 162cec659f..a736c3f46a 100644 --- a/packages/components/breadcrumbs/package.json +++ b/packages/components/breadcrumbs/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/breadcrumbs", - "version": "2.0.7", + "version": "2.0.8", "description": "Breadcrumbs display a hierarchy of links to the current page or resource in an application.", "keywords": [ "breadcrumbs" @@ -43,11 +43,11 @@ "@nextui-org/react-utils": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/shared-icons": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/breadcrumbs": "^3.5.11", - "@react-aria/utils": "^3.23.2", - "@react-types/breadcrumbs": "^3.7.3", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/breadcrumbs": "3.5.11", + "@react-aria/utils": "3.23.2", + "@react-types/breadcrumbs": "3.7.3", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -60,4 +60,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/breadcrumbs/src/use-breadcrumbs.ts b/packages/components/breadcrumbs/src/use-breadcrumbs.ts index ca99103b6f..48cb9ec935 100644 --- a/packages/components/breadcrumbs/src/use-breadcrumbs.ts +++ b/packages/components/breadcrumbs/src/use-breadcrumbs.ts @@ -2,7 +2,12 @@ import type {BreadcrumbsVariantProps, SlotsToClasses, BreadcrumbsSlots} from "@n import type {AriaBreadcrumbsProps} from "@react-types/breadcrumbs"; import {Children, ReactNode, Key, ReactElement} from "react"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {breadcrumbs} from "@nextui-org/theme"; import {filterDOMProps, pickChildren, ReactRef, useDOMRef} from "@nextui-org/react-utils"; import {mergeProps} from "@react-aria/utils"; @@ -103,6 +108,11 @@ export type UseBreadcrumbsProps = Props & >; export function useBreadcrumbs(originalProps: UseBreadcrumbsProps) { + const globalContext = useProviderContext(); + + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; + const [props, variantProps] = mapPropsVariants(originalProps, breadcrumbs.variantKeys); const { @@ -117,7 +127,6 @@ export function useBreadcrumbs(originalProps: UseBreadcrumbsProps) { itemsAfterCollapse = 2, maxItems = 8, hideSeparator, - disableAnimation, renderEllipsis, className, classNames, diff --git a/packages/components/button/CHANGELOG.md b/packages/components/button/CHANGELOG.md index 6d44449f75..3d2e283a0a 100644 --- a/packages/components/button/CHANGELOG.md +++ b/packages/components/button/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/button +## 2.0.32 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/ripple@2.0.29 + - @nextui-org/spinner@2.0.29 + ## 2.0.31 ### Patch Changes diff --git a/packages/components/button/package.json b/packages/components/button/package.json index c046a0f3d0..18f401cd85 100644 --- a/packages/components/button/package.json +++ b/packages/components/button/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/button", - "version": "2.0.31", + "version": "2.0.32", "description": "Buttons allow users to perform actions and choose with a single tap.", "keywords": [ "button" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -46,12 +46,12 @@ "@nextui-org/use-aria-button": "workspace:*", "@nextui-org/ripple": "workspace:*", "@nextui-org/spinner": "workspace:*", - "@react-aria/button": "^3.9.3", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-aria/focus": "^3.16.2", - "@react-types/shared": "^3.22.1", - "@react-types/button": "^3.9.2" + "@react-aria/button": "3.9.3", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-aria/focus": "3.16.2", + "@react-types/shared": "3.22.1", + "@react-types/button": "3.9.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -62,4 +62,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/button/src/use-button-group.ts b/packages/components/button/src/use-button-group.ts index fb7a785a59..dfe6807b66 100644 --- a/packages/components/button/src/use-button-group.ts +++ b/packages/components/button/src/use-button-group.ts @@ -3,7 +3,12 @@ import type {ReactRef} from "@nextui-org/react-utils"; import type {ButtonGroupVariantProps} from "@nextui-org/theme"; import {buttonGroup} from "@nextui-org/theme"; -import {HTMLNextUIProps, PropGetter, mapPropsVariants} from "@nextui-org/system"; +import { + HTMLNextUIProps, + PropGetter, + mapPropsVariants, + useProviderContext, +} from "@nextui-org/system"; import {useDOMRef} from "@nextui-org/react-utils"; import {useMemo, useCallback} from "react"; import {objectToDeps} from "@nextui-org/shared-utils"; @@ -40,6 +45,7 @@ export type UseButtonGroupProps = Props & >; export function useButtonGroup(originalProps: UseButtonGroupProps) { + const globalContext = useProviderContext(); const [props, variantProps] = mapPropsVariants(originalProps, buttonGroup.variantKeys); const { @@ -51,9 +57,9 @@ export function useButtonGroup(originalProps: UseButtonGroupProps) { variant = "solid", radius, isDisabled = false, - disableAnimation = false, - disableRipple = false, isIconOnly = false, + disableRipple = globalContext?.disableRipple ?? false, + disableAnimation = globalContext?.disableAnimation ?? false, className, ...otherProps } = props; diff --git a/packages/components/button/src/use-button.ts b/packages/components/button/src/use-button.ts index 1d09432273..637c1132d7 100644 --- a/packages/components/button/src/use-button.ts +++ b/packages/components/button/src/use-button.ts @@ -1,9 +1,9 @@ import type {ButtonVariantProps} from "@nextui-org/theme"; import type {AriaButtonProps} from "@nextui-org/use-aria-button"; -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import type {ReactNode} from "react"; import type {RippleProps} from "@nextui-org/ripple"; +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {dataAttr} from "@nextui-org/shared-utils"; import {ReactRef} from "@nextui-org/react-utils"; import {MouseEventHandler, useCallback} from "react"; @@ -65,6 +65,7 @@ export type UseButtonProps = Props & export function useButton(props: UseButtonProps) { const groupContext = useButtonGroupContext(); + const globalContext = useProviderContext(); const isInGroup = !!groupContext; const { @@ -76,16 +77,16 @@ export function useButton(props: UseButtonProps) { autoFocus, className, spinner, + isLoading = false, + disableRipple: disableRippleProp = false, fullWidth = groupContext?.fullWidth ?? false, + radius = groupContext?.radius, size = groupContext?.size ?? "md", color = groupContext?.color ?? "default", variant = groupContext?.variant ?? "solid", - disableAnimation = groupContext?.disableAnimation ?? false, - radius = groupContext?.radius, - disableRipple = groupContext?.disableRipple ?? false, + disableAnimation = groupContext?.disableAnimation ?? globalContext?.disableAnimation ?? false, isDisabled: isDisabledProp = groupContext?.isDisabled ?? false, isIconOnly = groupContext?.isIconOnly ?? false, - isLoading = false, spinnerPlacement = "start", onPress, onClick, @@ -97,6 +98,8 @@ export function useButton(props: UseButtonProps) { const domRef = useDOMRef(ref); + const disableRipple = (disableRippleProp || globalContext?.disableRipple) ?? disableAnimation; + const {isFocusVisible, isFocused, focusProps} = useFocusRing({ autoFocus, }); diff --git a/packages/components/calendar/CHANGELOG.md b/packages/components/calendar/CHANGELOG.md index dfa4e3d348..cd020df5fa 100644 --- a/packages/components/calendar/CHANGELOG.md +++ b/packages/components/calendar/CHANGELOG.md @@ -1,5 +1,23 @@ # @nextui-org/calendar +## 2.0.5 + +### Patch Changes + +- [#2889](https://github.com/nextui-org/nextui/pull/2889) [`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Update React Aria packages + +- [#3054](https://github.com/nextui-org/nextui/pull/3054) [`bf68c91b9`](https://github.com/nextui-org/nextui/commit/bf68c91b9a5be2014830859b0be2127d657ba90f) Thanks [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya)! - revise the inert attribute in `CalendarMonth` and `CalendarPicker` + +- [#2906](https://github.com/nextui-org/nextui/pull/2906) [`c83ff382b`](https://github.com/nextui-org/nextui/commit/c83ff382b9e5deaa08ed7e64eee484cc4904704d) Thanks [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya)! - Fixed hiding of unavailable dates in RangeCalendar (#2890) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3014](https://github.com/nextui-org/nextui/pull/3014) [`20ba81948`](https://github.com/nextui-org/nextui/commit/20ba81948d86ccc7ea4269cceb06e04899903b0e) Thanks [@winchesHe](https://github.com/winchesHe)! - add the correct peerDep version + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/button@2.0.32 + - @nextui-org/framer-utils@2.0.19 + ## 2.0.4 ### Patch Changes diff --git a/packages/components/calendar/package.json b/packages/components/calendar/package.json index 1ad57c3b1b..088d3d1cdc 100644 --- a/packages/components/calendar/package.json +++ b/packages/components/calendar/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/calendar", - "version": "2.0.4", + "version": "2.0.5", "description": "A calendar displays one or more date grids and allows users to select a single date.", "keywords": [ "calendar" @@ -34,8 +34,8 @@ "postpack": "clean-package restore" }, "peerDependencies": { - "@nextui-org/system": ">=2.0.0", - "@nextui-org/theme": ">=2.0.0", + "@nextui-org/system": ">=2.1.0", + "@nextui-org/theme": ">=2.2.0", "react": ">=18", "react-dom": ">=18" }, @@ -48,17 +48,17 @@ "@nextui-org/button": "workspace:*", "lodash.debounce": "^4.0.8", "@internationalized/date": "^3.5.2", - "@react-aria/calendar": "3.5.1", - "@react-aria/focus": "^3.14.3", - "@react-aria/i18n": "^3.8.4", - "@react-stately/calendar": "3.4.1", - "@react-types/button": "^3.9.0", - "@react-aria/visually-hidden": "^3.8.6", - "@react-aria/utils": "^3.21.1", - "@react-stately/utils": "^3.8.0", - "@react-types/calendar": "3.4.1", - "@react-aria/interactions": "^3.19.1", - "@react-types/shared": "3.21.0", + "@react-aria/calendar": "3.5.6", + "@react-aria/focus": "3.16.2", + "@react-aria/i18n": "3.10.2", + "@react-stately/calendar": "3.4.4", + "@react-types/button": "3.9.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-aria/utils": "3.23.2", + "@react-stately/utils": "3.9.1", + "@react-types/calendar": "3.4.4", + "@react-aria/interactions": "3.21.1", + "@react-types/shared": "3.22.1", "scroll-into-view-if-needed": "3.0.10", "@types/lodash.debounce": "^4.0.7" }, @@ -73,4 +73,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/calendar/src/calendar-cell.tsx b/packages/components/calendar/src/calendar-cell.tsx index 351fcb212c..7de61e6ce2 100644 --- a/packages/components/calendar/src/calendar-cell.tsx +++ b/packages/components/calendar/src/calendar-cell.tsx @@ -42,7 +42,7 @@ export function CalendarCell(originalProps: CalendarCellProps) { ref, ); - const isUnavailable = state.isCellUnavailable(props.date) && !isDisabled; + const isUnavailable = state.isCellUnavailable(props.date); const isLastSelectedBeforeDisabled = !isDisabled && !isInvalid && state.isCellUnavailable(props.date.add({days: 1})); const isFirstSelectedAfterDisabled = diff --git a/packages/components/calendar/src/calendar-month.tsx b/packages/components/calendar/src/calendar-month.tsx index 78cdd30252..e17fca20d9 100644 --- a/packages/components/calendar/src/calendar-month.tsx +++ b/packages/components/calendar/src/calendar-month.tsx @@ -40,8 +40,9 @@ export function CalendarMonth(props: CalendarMonthProps) { className={slots?.gridBodyRow({class: classNames?.gridBodyRow})} data-slot="grid-body-row" // makes the browser ignore the element and its children when tabbing + // TODO: invert inert when switching to React 19 (ref: https://github.com/facebook/react/issues/17157) // @ts-ignore - inert={isHeaderExpanded ? true : undefined} + inert={isHeaderExpanded ? "" : undefined} > {state .getDatesInWeek(weekIndex, startDate) diff --git a/packages/components/calendar/src/calendar-picker.tsx b/packages/components/calendar/src/calendar-picker.tsx index 575b665f77..76e4cf07e5 100644 --- a/packages/components/calendar/src/calendar-picker.tsx +++ b/packages/components/calendar/src/calendar-picker.tsx @@ -66,8 +66,9 @@ export function CalendarPicker(props: CalendarPickerProps) { })} data-slot="picker-wrapper" // makes the browser ignore the element and its children when tabbing + // TODO: invert inert when switching to React 19 (ref: https://github.com/facebook/react/issues/17157) // @ts-ignore - inert={isHeaderExpanded ? true : undefined} + inert={isHeaderExpanded ? undefined : ""} >
, keyof AriaCalendarPropsBase | "onChange">; @@ -182,7 +181,7 @@ export type ContextType = { export function useCalendarBase(originalProps: UseCalendarBasePropsComplete) { const [props, variantProps] = mapPropsVariants(originalProps, calendar.variantKeys); - const providerContext = useProviderContext(); + const globalContext = useProviderContext(); const { ref, @@ -199,9 +198,9 @@ export function useCalendarBase(originalProps: UseCalendarBasePropsComplete) { isHeaderExpanded: isHeaderExpandedProp, isHeaderDefaultExpanded, onHeaderExpandedChange = () => {}, - minValue = providerContext?.defaultDates?.minDate ?? new CalendarDate(1900, 1, 1), - maxValue = providerContext?.defaultDates?.maxDate ?? new CalendarDate(2099, 12, 31), - createCalendar: createCalendarProp = providerContext?.createCalendar ?? null, + minValue = globalContext?.defaultDates?.minDate ?? new CalendarDate(1900, 1, 1), + maxValue = globalContext?.defaultDates?.maxDate ?? new CalendarDate(2099, 12, 31), + createCalendar: createCalendarProp = globalContext?.createCalendar ?? null, prevButtonProps: prevButtonPropsProp, nextButtonProps: nextButtonPropsProp, errorMessage, @@ -254,7 +253,8 @@ export function useCalendarBase(originalProps: UseCalendarBasePropsComplete) { [objectToDeps(variantProps), showMonthAndYearPickers, isHeaderExpanded, className], ); - const disableAnimation = originalProps.disableAnimation ?? false; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const commonButtonProps: ButtonProps = { size: "sm", @@ -317,6 +317,7 @@ export function useCalendarBase(originalProps: UseCalendarBasePropsComplete) { shouldFilterDOMProps, isHeaderExpanded, showMonthAndYearPickers, + disableAnimation, createCalendar: createCalendarProp, getPrevButtonProps, getNextButtonProps, diff --git a/packages/components/calendar/src/use-calendar.ts b/packages/components/calendar/src/use-calendar.ts index 5776d8601c..82d9636f2a 100644 --- a/packages/components/calendar/src/use-calendar.ts +++ b/packages/components/calendar/src/use-calendar.ts @@ -39,6 +39,7 @@ export function useCalendar({ weekdayStyle, visibleDuration, baseProps, + disableAnimation, shouldFilterDOMProps, isHeaderExpanded, visibleMonths, @@ -73,7 +74,6 @@ export function useCalendar({ useAriaCalendar(originalProps, state); const baseStyles = clsx(classNames?.base, className); - const disableAnimation = originalProps.disableAnimation ?? false; const buttonPickerProps: ButtonProps = { ...buttonPickerPropsProp, diff --git a/packages/components/calendar/src/use-range-calendar.ts b/packages/components/calendar/src/use-range-calendar.ts index da6e4be0af..3b310d2cd1 100644 --- a/packages/components/calendar/src/use-range-calendar.ts +++ b/packages/components/calendar/src/use-range-calendar.ts @@ -39,6 +39,7 @@ export function useRangeCalendar({ shouldFilterDOMProps, isHeaderExpanded, visibleMonths, + disableAnimation, createCalendar: createCalendarProp, baseProps, getPrevButtonProps, @@ -70,7 +71,6 @@ export function useRangeCalendar({ useAriaRangeCalendar(originalProps, state, domRef); const baseStyles = clsx(classNames?.base, className); - const disableAnimation = originalProps.disableAnimation ?? false; const getBaseCalendarProps = (props = {}): CalendarBaseProps => { return { 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..d1d58410cd 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"; @@ -382,7 +382,6 @@ export const InternationalCalendars = { render: InternationalCalendarsTemplate, args: { ...defaultProps, - showMonthAndYearPickers: true, }, }; diff --git a/packages/components/card/CHANGELOG.md b/packages/components/card/CHANGELOG.md index 754319ebd5..53a44138b7 100644 --- a/packages/components/card/CHANGELOG.md +++ b/packages/components/card/CHANGELOG.md @@ -1,5 +1,14 @@ # @nextui-org/card +## 2.0.29 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/ripple@2.0.29 + ## 2.0.28 ### Patch Changes diff --git a/packages/components/card/package.json b/packages/components/card/package.json index d09ad582c1..6a91bdd6f7 100644 --- a/packages/components/card/package.json +++ b/packages/components/card/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/card", - "version": "2.0.28", + "version": "2.0.29", "description": "Card is a container for text, photos, and actions in the context of a single subject.", "keywords": [ "card" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -45,11 +45,11 @@ "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-aria-button": "workspace:*", "@nextui-org/ripple": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/utils": "^3.23.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/button": "^3.9.3", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/utils": "3.23.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/button": "3.9.3", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -64,4 +64,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/card/src/use-card.ts b/packages/components/card/src/use-card.ts index 45e7c0b985..913d3a8f27 100644 --- a/packages/components/card/src/use-card.ts +++ b/packages/components/card/src/use-card.ts @@ -9,7 +9,12 @@ import {chain, mergeProps} from "@react-aria/utils"; import {useFocusRing} from "@react-aria/focus"; import {useHover} from "@react-aria/interactions"; import {useAriaButton} from "@nextui-org/use-aria-button"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {ReactRef, filterDOMProps} from "@nextui-org/react-utils"; import {useDOMRef} from "@nextui-org/react-utils"; @@ -64,13 +69,14 @@ export type ContextType = { }; export function useCard(originalProps: UseCardProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, card.variantKeys); const { ref, as, children, - disableRipple = false, onClick, onPress, autoFocus, @@ -84,12 +90,16 @@ export function useCard(originalProps: UseCardProps) { const Component = as || (originalProps.isPressable ? "button" : "div"); const shouldFilterDOMProps = typeof Component === "string"; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; + const disableRipple = originalProps.disableRipple ?? globalContext?.disableRipple ?? false; + const baseStyles = clsx(classNames?.base, className); const {onClick: onRippleClickHandler, onClear: onClearRipple, ripples} = useRipple(); const handleClick = (e: MouseEvent) => { - if (!originalProps.disableAnimation && !disableRipple && domRef.current) { + if (!disableAnimation && !disableRipple && domRef.current) { onRippleClickHandler(e); } }; @@ -119,25 +129,26 @@ export function useCard(originalProps: UseCardProps) { () => card({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableAnimation], ); const context = useMemo( () => ({ + slots, + classNames, + disableAnimation, isDisabled: originalProps.isDisabled, isFooterBlurred: originalProps.isFooterBlurred, - disableAnimation: originalProps.disableAnimation, fullWidth: originalProps.fullWidth, - slots, - classNames, }), [ slots, classNames, originalProps.isDisabled, originalProps.isFooterBlurred, - originalProps.disableAnimation, + disableAnimation, originalProps.fullWidth, ], ); @@ -194,9 +205,9 @@ export function useCard(originalProps: UseCardProps) { children, isHovered, isPressed, + disableAnimation, isPressable: originalProps.isPressable, isHoverable: originalProps.isHoverable, - disableAnimation: originalProps.disableAnimation, disableRipple, handleClick, isFocusVisible, diff --git a/packages/components/checkbox/CHANGELOG.md b/packages/components/checkbox/CHANGELOG.md index 6d2f322d4c..376032edaa 100644 --- a/packages/components/checkbox/CHANGELOG.md +++ b/packages/components/checkbox/CHANGELOG.md @@ -1,5 +1,17 @@ # @nextui-org/checkbox +## 2.1.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3013](https://github.com/nextui-org/nextui/pull/3013) [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85) Thanks [@kosmotema](https://github.com/kosmotema)! - make the VisuallyHidden `elementType` as span when the default parent element accepts only phrasing elements + ## 2.0.29 ### Patch Changes diff --git a/packages/components/checkbox/__tests__/checkbox-group.test.tsx b/packages/components/checkbox/__tests__/checkbox-group.test.tsx index cef6cdd5b1..68ea6e93b5 100644 --- a/packages/components/checkbox/__tests__/checkbox-group.test.tsx +++ b/packages/components/checkbox/__tests__/checkbox-group.test.tsx @@ -139,11 +139,11 @@ describe("Checkbox.Group", () => { beforeAll(() => { user = userEvent.setup(); }); - describe("validationBehavior=native (default)", () => { + describe("validationBehavior=native", () => { it("supports group level isRequired", async () => { let {getAllByRole, getByRole, getByTestId} = render(
- + Terms and conditions Cookies Privacy policy @@ -181,6 +181,148 @@ describe("Checkbox.Group", () => { expect(group).not.toHaveAttribute("aria-describedby"); }); + + it("supports checkbox level isRequired", async () => { + let {getAllByRole, getByRole, getByTestId} = render( + + + + Terms and conditions + + + Cookies + + Privacy policy + + , + ); + + let group = getByRole("group"); + + expect(group).not.toHaveAttribute("aria-describedby"); + + let checkboxes = getAllByRole("checkbox") as HTMLInputElement[]; + + for (let input of checkboxes.slice(0, 2)) { + expect(input).toHaveAttribute("required"); + expect(input).not.toHaveAttribute("aria-required"); + expect(input.validity.valid).toBe(false); + } + expect(checkboxes[2]).not.toHaveAttribute("required"); + expect(checkboxes[2]).not.toHaveAttribute("aria-required"); + expect(checkboxes[2].validity.valid).toBe(true); + + act(() => { + (getByTestId("form") as HTMLFormElement).checkValidity(); + }); + + expect(group).toHaveAttribute("aria-describedby"); + expect(document.getElementById(group.getAttribute("aria-describedby")!)).toHaveTextContent( + "Constraints not satisfied", + ); + expect(document.activeElement).toBe(checkboxes[0]); + + await user.click(checkboxes[0]); + await user.click(checkboxes[1]); + expect(checkboxes[0].validity.valid).toBe(true); + expect(checkboxes[1].validity.valid).toBe(true); + expect(group).not.toHaveAttribute("aria-describedby"); + }); + + it("supports group level validate function", async () => { + let {getAllByRole, getByRole, getByTestId} = render( +
+ (v.length < 3 ? "You must accept all terms" : null)} + validationBehavior="native" + > + Terms and conditions + Cookies + Privacy policy + +
, + ); + + let group = getByRole("group"); + + expect(group).not.toHaveAttribute("aria-describedby"); + + let checkboxes = getAllByRole("checkbox") as HTMLInputElement[]; + + for (let input of checkboxes) { + expect(input).not.toHaveAttribute("required"); + expect(input).not.toHaveAttribute("aria-required"); + expect(input.validity.valid).toBe(false); + } + + act(() => { + (getByTestId("form") as HTMLFormElement).checkValidity(); + }); + + expect(group).toHaveAttribute("aria-describedby"); + expect(document.getElementById(group.getAttribute("aria-describedby")!)).toHaveTextContent( + "You must accept all terms", + ); + expect(document.activeElement).toBe(checkboxes[0]); + + await user.click(checkboxes[0]); + expect(group).toHaveAttribute("aria-describedby"); + for (let input of checkboxes) { + expect(input.validity.valid).toBe(false); + } + + await user.click(checkboxes[1]); + expect(group).toHaveAttribute("aria-describedby"); + for (let input of checkboxes) { + expect(input.validity.valid).toBe(false); + } + + await user.click(checkboxes[2]); + expect(group).not.toHaveAttribute("aria-describedby"); + for (let input of checkboxes) { + expect(input.validity.valid).toBe(true); + } + }); + }); + + describe("validationBehavior=aria", () => { + it("supports group level validate function", async () => { + let {getAllByRole, getByRole} = render( + (v.length < 3 ? "You must accept all terms" : null)} + validationBehavior="aria" + > + Terms and conditions + Cookies + Privacy policy + , + ); + + let group = getByRole("group"); + + expect(group).toHaveAttribute("aria-describedby"); + expect(document.getElementById(group.getAttribute("aria-describedby")!)).toHaveTextContent( + "You must accept all terms", + ); + + let checkboxes = getAllByRole("checkbox") as HTMLInputElement[]; + + for (let input of checkboxes) { + expect(input).toHaveAttribute("aria-invalid", "true"); + expect(input.validity.valid).toBe(true); + } + + await user.click(checkboxes[0]); + expect(group).toHaveAttribute("aria-describedby"); + + await user.click(checkboxes[1]); + expect(group).toHaveAttribute("aria-describedby"); + + await user.click(checkboxes[2]); + expect(group).toHaveAttribute("aria-describedby"); + }); }); }); }); diff --git a/packages/components/checkbox/__tests__/checkbox.test.tsx b/packages/components/checkbox/__tests__/checkbox.test.tsx index c3e7308b74..462f93e973 100644 --- a/packages/components/checkbox/__tests__/checkbox.test.tsx +++ b/packages/components/checkbox/__tests__/checkbox.test.tsx @@ -1,6 +1,7 @@ import * as React from "react"; -import {render, act} from "@testing-library/react"; +import {render, renderHook, act} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import {useForm} from "react-hook-form"; import {Checkbox, CheckboxProps} from "../src"; @@ -89,10 +90,26 @@ describe("Checkbox", () => { expect(onFocus).toBeCalled(); }); - it('should work correctly with "isRequired" prop', () => { - const {container} = render(Option); + it("should have required attribute when isRequired with native validationBehavior", () => { + const {container} = render( + + Option + , + ); + + expect(container.querySelector("input")).toHaveAttribute("required"); + expect(container.querySelector("input")).not.toHaveAttribute("aria-required"); + }); - expect(container.querySelector("input")?.required).toBe(true); + it("should have aria-required attribute when isRequired with aria validationBehavior", () => { + const {container} = render( + + Option + , + ); + + expect(container.querySelector("input")).not.toHaveAttribute("required"); + expect(container.querySelector("input")).toHaveAttribute("aria-required", "true"); }); it("should work correctly with controlled value", () => { @@ -127,4 +144,129 @@ describe("Checkbox", () => { expect(onChange).toBeCalled(); }); + + describe("validation", () => { + let user; + + beforeEach(() => { + user = userEvent.setup(); + }); + + describe("validationBehavior=native", () => { + it("supports isRequired", async () => { + const {getByRole, getByTestId} = render( +
+ + Terms and conditions + +
, + ); + + const checkbox = getByRole("checkbox") as HTMLInputElement; + + expect(checkbox).toHaveAttribute("required"); + expect(checkbox).not.toHaveAttribute("aria-required"); + expect(checkbox.validity.valid).toBe(false); + + act(() => { + (getByTestId("form") as HTMLFormElement).checkValidity(); + }); + + await user.click(checkbox); + expect(checkbox.validity.valid).toBe(true); + }); + }); + + describe("validationBehavior=aria", () => { + it("supports validate function", async () => { + const {getByRole} = render( + (!v ? "You must accept the terms." : null)} + validationBehavior="aria" + value="terms" + > + Terms and conditions + , + ); + + const checkbox = getByRole("checkbox") as HTMLInputElement; + + expect(checkbox.validity.valid).toBe(true); + + await user.click(checkbox); + expect(checkbox.validity.valid).toBe(true); + }); + }); + }); +}); + +describe("Checkbox with React Hook Form", () => { + let checkbox1: HTMLInputElement; + let checkbox2: HTMLInputElement; + let checkbox3: HTMLInputElement; + let submitButton: HTMLButtonElement; + let onSubmit: () => void; + + beforeEach(() => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + withDefaultValue: true, + withoutDefaultValue: false, + requiredField: false, + }, + }), + ); + + const { + handleSubmit, + register, + formState: {errors}, + } = result.current; + + onSubmit = jest.fn(); + + render( +
+ + + + {errors.requiredField && This field is required} + + , + ); + + checkbox1 = document.querySelector("input[name=withDefaultValue]")!; + checkbox2 = document.querySelector("input[name=withoutDefaultValue]")!; + checkbox3 = document.querySelector("input[name=requiredField]")!; + submitButton = document.querySelector("button")!; + }); + + it("should work with defaultValues", () => { + expect(checkbox1.checked).toBe(true); + expect(checkbox2.checked).toBe(false); + expect(checkbox3.checked).toBe(false); + }); + + it("should not submit form when required field is empty", async () => { + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(0); + }); + + it("should submit form when required field is not empty", async () => { + act(() => { + checkbox3.click(); + }); + + expect(checkbox3.checked).toBe(true); + + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/components/checkbox/package.json b/packages/components/checkbox/package.json index c3672373e3..4f1bd8597a 100644 --- a/packages/components/checkbox/package.json +++ b/packages/components/checkbox/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/checkbox", - "version": "2.0.29", + "version": "2.1.0", "description": "Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.", "keywords": [ "checkbox" @@ -44,15 +44,15 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/use-callback-ref": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/checkbox": "^3.14.1", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/checkbox": "^3.6.3", - "@react-stately/toggle": "^3.7.2", - "@react-types/checkbox": "^3.7.1", - "@react-types/shared": "^3.22.1" + "@react-aria/checkbox": "3.14.1", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/checkbox": "3.6.3", + "@react-stately/toggle": "3.7.2", + "@react-types/checkbox": "3.7.1", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/chip": "workspace:*", @@ -67,4 +67,4 @@ "react-hook-form": "^7.51.3" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/checkbox/src/checkbox.tsx b/packages/components/checkbox/src/checkbox.tsx index dea6db536e..9ca4ee6e2a 100644 --- a/packages/components/checkbox/src/checkbox.tsx +++ b/packages/components/checkbox/src/checkbox.tsx @@ -26,7 +26,7 @@ const Checkbox = forwardRef<"input", CheckboxProps>((props, ref) => { return ( - + {clonedIcon} diff --git a/packages/components/checkbox/src/use-checkbox-group.ts b/packages/components/checkbox/src/use-checkbox-group.ts index 93ac34d013..c351cc93e6 100644 --- a/packages/components/checkbox/src/use-checkbox-group.ts +++ b/packages/components/checkbox/src/use-checkbox-group.ts @@ -1,10 +1,10 @@ import type {CheckboxGroupSlots, SlotsToClasses} from "@nextui-org/theme"; import type {AriaCheckboxGroupProps} from "@react-types/checkbox"; import type {Orientation} from "@react-types/shared"; -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import type {ReactRef} from "@nextui-org/react-utils"; import type {CheckboxGroupProps} from "@react-types/checkbox"; +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {useCallback, useMemo} from "react"; import {chain, mergeProps} from "@react-aria/utils"; import {checkboxGroup} from "@nextui-org/theme"; @@ -48,7 +48,7 @@ interface Props extends HTMLNextUIProps<"div"> { } export type UseCheckboxGroupProps = Omit & - Omit & + AriaCheckboxGroupProps & Partial< Pick< CheckboxProps, @@ -65,9 +65,12 @@ export type ContextType = { lineThrough?: CheckboxProps["lineThrough"]; isDisabled?: CheckboxProps["isDisabled"]; disableAnimation?: CheckboxProps["disableAnimation"]; + validationBehavior?: CheckboxProps["validationBehavior"]; }; export function useCheckboxGroup(props: UseCheckboxGroupProps) { + const globalContext = useProviderContext(); + const { as, ref, @@ -85,7 +88,8 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { orientation = "vertical", lineThrough = false, isDisabled = false, - disableAnimation = false, + validationBehavior = globalContext?.validationBehavior ?? "aria", + disableAnimation = globalContext?.disableAnimation ?? false, isReadOnly, isRequired, onValueChange, @@ -110,7 +114,7 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { isRequired, isReadOnly, orientation, - validationBehavior: "native", + validationBehavior, isInvalid: validationState === "invalid" || isInvalidProp, onChange: chain(props.onChange, onValueChange), }; @@ -125,6 +129,7 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { onValueChange, isInvalidProp, validationState, + validationBehavior, otherProps["aria-label"], otherProps, ]); @@ -136,22 +141,20 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { groupProps, descriptionProps, errorMessageProps, - isInvalid: isAriaInvalid, validationErrors, validationDetails, } = useReactAriaCheckboxGroup(checkboxGroupProps, groupState); - let isInvalid = checkboxGroupProps.isInvalid || isAriaInvalid; - const context = useMemo( () => ({ size, color, radius, lineThrough, - isInvalid, + isInvalid: groupState.isInvalid, isDisabled, disableAnimation, + validationBehavior, groupState, }), [ @@ -161,18 +164,18 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { lineThrough, isDisabled, disableAnimation, - isInvalid, - groupState?.value, - groupState?.isDisabled, - groupState?.isReadOnly, - groupState?.isInvalid, - groupState?.isSelected, + validationBehavior, + groupState.value, + groupState.isDisabled, + groupState.isReadOnly, + groupState.isInvalid, + groupState.isSelected, ], ); const slots = useMemo( - () => checkboxGroup({isRequired, isInvalid, disableAnimation}), - [isRequired, isInvalid, disableAnimation], + () => checkboxGroup({isRequired, isInvalid: groupState.isInvalid, disableAnimation}), + [isRequired, groupState.isInvalid, , disableAnimation], ); const baseStyles = clsx(classNames?.base, className); @@ -233,10 +236,10 @@ export function useCheckboxGroup(props: UseCheckboxGroupProps) { label, context, description, - isInvalid, + isInvalid: groupState.isInvalid, errorMessage: typeof errorMessage === "function" - ? errorMessage({isInvalid, validationErrors, validationDetails}) + ? errorMessage({isInvalid: groupState.isInvalid, validationErrors, validationDetails}) : errorMessage || validationErrors?.join(" "), getGroupProps, getLabelProps, diff --git a/packages/components/checkbox/src/use-checkbox.ts b/packages/components/checkbox/src/use-checkbox.ts index b5e0171d5b..402488c89d 100644 --- a/packages/components/checkbox/src/use-checkbox.ts +++ b/packages/components/checkbox/src/use-checkbox.ts @@ -1,7 +1,7 @@ import type {CheckboxVariantProps, CheckboxSlots, SlotsToClasses} from "@nextui-org/theme"; import type {AriaCheckboxProps} from "@react-types/checkbox"; -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {ReactNode, Ref, useCallback, useId, useState} from "react"; import {useMemo, useRef} from "react"; import {useToggleState} from "@react-stately/toggle"; @@ -68,10 +68,11 @@ interface Props extends Omit, keyof CheckboxVariantProp } export type UseCheckboxProps = Omit & - Omit & + Omit & CheckboxVariantProps; export function useCheckbox(props: UseCheckboxProps = {}) { + const globalContext = useProviderContext(); const groupContext = useCheckboxGroupContext(); const isInGroup = !!groupContext; @@ -86,15 +87,16 @@ export function useCheckbox(props: UseCheckboxProps = {}) { isReadOnly: isReadOnlyProp = false, autoFocus = false, isSelected: isSelectedProp, - validationState, size = groupContext?.size ?? "md", color = groupContext?.color ?? "primary", radius = groupContext?.radius, lineThrough = groupContext?.lineThrough ?? false, isDisabled: isDisabledProp = groupContext?.isDisabled ?? false, - disableAnimation = groupContext?.disableAnimation ?? false, + disableAnimation = groupContext?.disableAnimation ?? globalContext?.disableAnimation ?? false, + validationState, isInvalid = validationState ? validationState === "invalid" : groupContext?.isInvalid ?? false, isIndeterminate = false, + validationBehavior = groupContext?.validationBehavior ?? "aria", defaultSelected, classNames, className, @@ -144,6 +146,7 @@ export function useCheckbox(props: UseCheckboxProps = {}) { children, autoFocus, defaultSelected, + validationBehavior, isIndeterminate, isRequired, isInvalid, @@ -166,6 +169,7 @@ export function useCheckbox(props: UseCheckboxProps = {}) { isReadOnlyProp, isSelectedProp, defaultSelected, + validationBehavior, otherProps["aria-label"], otherProps["aria-labelledby"], onValueChange, @@ -181,22 +185,9 @@ export function useCheckbox(props: UseCheckboxProps = {}) { isPressed: isPressedKeyboard, } = isInGroup ? // eslint-disable-next-line - useReactAriaCheckboxGroupItem( - { - ...ariaCheckboxProps, - isInvalid, - validationBehavior: "native", - }, - groupContext.groupState, - inputRef, - ) + useReactAriaCheckboxGroupItem({...ariaCheckboxProps}, groupContext.groupState, inputRef) : // eslint-disable-next-line - useReactAriaCheckbox( - {...ariaCheckboxProps, validationBehavior: "native"}, - // eslint-disable-next-line - toggleState, - inputRef, - ); + useReactAriaCheckbox({...ariaCheckboxProps}, toggleState, inputRef); const isInteractionDisabled = isDisabled || isReadOnly; @@ -219,10 +210,6 @@ export function useCheckbox(props: UseCheckboxProps = {}) { const pressed = isInteractionDisabled ? false : isPressed || isPressedKeyboard; - if (isRequired) { - inputProps.required = true; - } - const {hoverProps, isHovered} = useHover({ isDisabled: inputProps.disabled, }); @@ -334,9 +321,9 @@ export function useCheckbox(props: UseCheckboxProps = {}) { const getIconProps = useCallback( () => ({ - isSelected: isSelected, - isIndeterminate: !!isIndeterminate, - disableAnimation: !!disableAnimation, + isSelected, + isIndeterminate, + disableAnimation, className: slots.icon({class: classNames?.icon}), } as CheckboxIconProps), [slots, classNames?.icon, isSelected, isIndeterminate, disableAnimation], diff --git a/packages/components/checkbox/stories/checkbox-group.stories.tsx b/packages/components/checkbox/stories/checkbox-group.stories.tsx index 131e8ee342..34ad88f04b 100644 --- a/packages/components/checkbox/stories/checkbox-group.stories.tsx +++ b/packages/components/checkbox/stories/checkbox-group.stories.tsx @@ -39,6 +39,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, } as Meta; diff --git a/packages/components/checkbox/stories/checkbox.stories.tsx b/packages/components/checkbox/stories/checkbox.stories.tsx index abd840770b..aa60c33c0d 100644 --- a/packages/components/checkbox/stories/checkbox.stories.tsx +++ b/packages/components/checkbox/stories/checkbox.stories.tsx @@ -39,6 +39,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, } as Meta; @@ -71,8 +77,14 @@ const FormTemplate = (args: CheckboxProps) => {
{ - alert(`Submitted value: ${e.target["check"].value}`); e.preventDefault(); + const checkbox = e.target["check"] as HTMLInputElement; + + if (checkbox.checked) { + alert(`Submitted value: ${checkbox.value}`); + } else { + alert("Checkbox is not checked"); + } }} > diff --git a/packages/components/chip/package.json b/packages/components/chip/package.json index 9a2575f325..adee99c070 100644 --- a/packages/components/chip/package.json +++ b/packages/components/chip/package.json @@ -43,10 +43,10 @@ "@nextui-org/shared-icons": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-types/checkbox": "^3.7.1" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-types/checkbox": "3.7.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -57,4 +57,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/code/CHANGELOG.md b/packages/components/code/CHANGELOG.md index a21ad53be4..34dd190fbf 100644 --- a/packages/components/code/CHANGELOG.md +++ b/packages/components/code/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/code +## 2.0.28 + +### Patch Changes + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.0.27 ### Patch Changes diff --git a/packages/components/code/package.json b/packages/components/code/package.json index 4e2e5d2542..04f63ec8ec 100644 --- a/packages/components/code/package.json +++ b/packages/components/code/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/code", - "version": "2.0.27", + "version": "2.0.28", "description": "Code is a component used to display inline code.", "keywords": [ "code" @@ -50,4 +50,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/date-input/CHANGELOG.md b/packages/components/date-input/CHANGELOG.md index 3c6eb212ec..6494f33363 100644 --- a/packages/components/date-input/CHANGELOG.md +++ b/packages/components/date-input/CHANGELOG.md @@ -1,5 +1,19 @@ # @nextui-org/date-input +## 2.1.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2889](https://github.com/nextui-org/nextui/pull/2889) [`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Update React Aria packages + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3014](https://github.com/nextui-org/nextui/pull/3014) [`20ba81948`](https://github.com/nextui-org/nextui/commit/20ba81948d86ccc7ea4269cceb06e04899903b0e) Thanks [@winchesHe](https://github.com/winchesHe)! - add the correct peerDep version + ## 2.0.3 ### Patch Changes diff --git a/packages/components/date-input/__tests__/date-input.test.tsx b/packages/components/date-input/__tests__/date-input.test.tsx index aa48657e87..62dd7d6bbf 100644 --- a/packages/components/date-input/__tests__/date-input.test.tsx +++ b/packages/components/date-input/__tests__/date-input.test.tsx @@ -63,6 +63,7 @@ describe("DateInput", () => { date.compare(new CalendarDate(1980, 1, 8)) <= 0 ); }} + name="date" />, ); @@ -70,9 +71,7 @@ describe("DateInput", () => { await user.tab(); }); - await act(async () => { - await user.keyboard("01011980"); - }); + await user.keyboard("01011980"); expect(tree.getByText("Date unavailable.")).toBeInTheDocument(); }); diff --git a/packages/components/date-input/package.json b/packages/components/date-input/package.json index ac708f65b6..1f85ae8961 100644 --- a/packages/components/date-input/package.json +++ b/packages/components/date-input/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/date-input", - "version": "2.0.3", + "version": "2.1.0", "description": "A date input allows users to enter and edit date and time values using a keyboard.", "keywords": [ "date-field" @@ -34,8 +34,8 @@ "postpack": "clean-package restore" }, "peerDependencies": { - "@nextui-org/system": ">=2.0.0", - "@nextui-org/theme": ">=2.0.0", + "@nextui-org/system": ">=2.1.0", + "@nextui-org/theme": ">=2.2.0", "react": ">=18", "react-dom": ">=18" }, @@ -43,12 +43,12 @@ "@nextui-org/react-utils": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@internationalized/date": "^3.5.2", - "@react-aria/datepicker": "^3.9.3", - "@react-aria/i18n": "^3.8.4", - "@react-stately/datepicker": "^3.9.2", - "@react-types/datepicker": "^3.7.2", - "@react-types/shared": "3.21.0", - "@react-aria/utils": "^3.21.1" + "@react-aria/datepicker": "3.9.3", + "@react-aria/i18n": "3.10.2", + "@react-stately/datepicker": "3.9.2", + "@react-types/datepicker": "3.7.2", + "@react-types/shared": "3.22.1", + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/system": "workspace:*", @@ -60,4 +60,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/date-input/src/date-input-group.tsx b/packages/components/date-input/src/date-input-group.tsx index f87c2b731d..cecd813fdf 100644 --- a/packages/components/date-input/src/date-input-group.tsx +++ b/packages/components/date-input/src/date-input-group.tsx @@ -75,7 +75,7 @@ export const DateInputGroup = forwardRef<"div", DateInputGroupProps>((props, ref return (
- {errorMessage ? ( + {isInvalid && errorMessage ? (
{errorMessage}
) : description ? (
{description}
diff --git a/packages/components/date-input/src/use-date-input.ts b/packages/components/date-input/src/use-date-input.ts index f250153eb7..1ecbe7d8e4 100644 --- a/packages/components/date-input/src/use-date-input.ts +++ b/packages/components/date-input/src/use-date-input.ts @@ -112,9 +112,9 @@ export type UseDateInputProps = Props & AriaDateFieldProps; export function useDateInput(originalProps: UseDateInputProps) { - const [props, variantProps] = mapPropsVariants(originalProps, dateInput.variantKeys); + const globalContext = useProviderContext(); - const providerContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, dateInput.variantKeys); const { ref, @@ -132,11 +132,11 @@ export function useDateInput(originalProps: UseDateInputPro fieldProps: fieldPropsProp, errorMessageProps: errorMessagePropsProp, descriptionProps: descriptionPropsProp, - validationBehavior, + validationBehavior = globalContext?.validationBehavior ?? "aria", shouldForceLeadingZeros = true, - minValue = providerContext?.defaultDates?.minDate ?? new CalendarDate(1900, 1, 1), - maxValue = providerContext?.defaultDates?.maxDate ?? new CalendarDate(2099, 12, 31), - createCalendar: createCalendarProp = providerContext?.createCalendar ?? null, + minValue = globalContext?.defaultDates?.minDate ?? new CalendarDate(1900, 1, 1), + maxValue = globalContext?.defaultDates?.maxDate ?? new CalendarDate(2099, 12, 31), + createCalendar: createCalendarProp = globalContext?.createCalendar ?? null, isInvalid: isInvalidProp = validationState ? validationState === "invalid" : false, errorMessage, } = props; @@ -144,6 +144,8 @@ export function useDateInput(originalProps: UseDateInputPro const domRef = useDOMRef(ref); const inputRef = useDOMRef(inputRefProp); + const disableAnimation = originalProps.disableAnimation ?? globalContext?.disableAnimation; + const {locale} = useLocale(); const state = useDateFieldState({ @@ -193,10 +195,11 @@ export function useDateInput(originalProps: UseDateInputPro () => dateInput({ ...variantProps, + disableAnimation, labelPlacement, className, }), - [objectToDeps(variantProps), labelPlacement, className], + [objectToDeps(variantProps), disableAnimation, labelPlacement, className], ); const getLabelProps: PropGetter = (props) => { diff --git a/packages/components/date-input/src/use-time-input.ts b/packages/components/date-input/src/use-time-input.ts index 4f15ec5723..5adfe588d7 100644 --- a/packages/components/date-input/src/use-time-input.ts +++ b/packages/components/date-input/src/use-time-input.ts @@ -6,7 +6,7 @@ import type {DateInputGroupProps} from "./date-input-group"; import {useLocale} from "@react-aria/i18n"; import {mergeProps} from "@react-aria/utils"; -import {PropGetter} from "@nextui-org/system"; +import {PropGetter, useProviderContext} from "@nextui-org/system"; import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system"; import {useDOMRef} from "@nextui-org/react-utils"; import {useTimeField as useAriaTimeField} from "@react-aria/datepicker"; @@ -70,9 +70,11 @@ interface Props extends NextUIBaseProps { export type UseTimeInputProps = Props & DateInputVariantProps & - Omit, "validationBehavior">; + AriaTimeFieldProps; export function useTimeInput(originalProps: UseTimeInputProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, dateInput.variantKeys); const { @@ -91,7 +93,7 @@ export function useTimeInput(originalProps: UseTimeInputPro fieldProps: fieldPropsProp, errorMessageProps: errorMessagePropsProp, descriptionProps: descriptionPropsProp, - // validationBehavior = "native", TODO: Uncomment this one we support `native` and `aria` validations + validationBehavior = globalContext?.validationBehavior ?? "aria", shouldForceLeadingZeros = true, minValue, maxValue, @@ -104,12 +106,15 @@ export function useTimeInput(originalProps: UseTimeInputPro const {locale} = useLocale(); + const disableAnimation = originalProps.disableAnimation ?? globalContext?.disableAnimation; + const state = useTimeFieldState({ ...originalProps, label, locale, minValue, maxValue, + validationBehavior, isInvalid: isInvalidProp, shouldForceLeadingZeros, }); @@ -123,11 +128,7 @@ export function useTimeInput(originalProps: UseTimeInputPro descriptionProps, errorMessageProps, isInvalid: ariaIsInvalid, - } = useAriaTimeField( - {...originalProps, label, validationBehavior: "native", inputRef}, - state, - domRef, - ); + } = useAriaTimeField({...originalProps, label, validationBehavior, inputRef}, state, domRef); const baseStyles = clsx(classNames?.base, className); @@ -150,10 +151,11 @@ export function useTimeInput(originalProps: UseTimeInputPro () => dateInput({ ...variantProps, + disableAnimation, labelPlacement, className, }), - [objectToDeps(variantProps), labelPlacement, className], + [objectToDeps(variantProps), labelPlacement, disableAnimation, className], ); const getLabelProps: PropGetter = (props) => { diff --git a/packages/components/date-input/stories/date-input.stories.tsx b/packages/components/date-input/stories/date-input.stories.tsx index a23e4e211b..87c2b74697 100644 --- a/packages/components/date-input/stories/date-input.stories.tsx +++ b/packages/components/date-input/stories/date-input.stories.tsx @@ -1,6 +1,6 @@ import React from "react"; import {Meta} from "@storybook/react"; -import {dateInput} from "@nextui-org/theme"; +import {dateInput, button} from "@nextui-org/theme"; import { CalendarDate, DateValue, @@ -55,6 +55,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, } as Meta; @@ -67,6 +73,21 @@ const Template = (args: DateInputProps) => ( ); +const FormTemplate = (args: DateInputProps) => ( + { + e.preventDefault(); + alert(`Submitted: ${e.target["date"].value}`); + }} + > + + + +); + const LabelPlacementTemplate = (args: DateInputProps) => (
@@ -152,7 +173,7 @@ export const Default = { }; export const Required = { - render: Template, + render: FormTemplate, args: { ...defaultProps, isRequired: true, @@ -337,3 +358,35 @@ export const HourCycle = { granularity: "minute", }, }; + +export const UnavailableDates = { + render: FormTemplate, + + args: { + ...defaultProps, + label: "Appointment date (Unavailable: Jan 1 - Jan 8, 2024)", + isDateUnavailable: (date) => { + return ( + date.compare(new CalendarDate(2024, 1, 1)) >= 0 && + date.compare(new CalendarDate(2024, 1, 8)) <= 0 + ); + }, + }, +}; + +export const WithValidation = { + render: FormTemplate, + + args: { + ...defaultProps, + validate: (value) => { + if (!value) { + return "Please enter a date"; + } + if (value.year < 2024) { + return "Please select a date in the year 2024 or later"; + } + }, + label: "Date (Year 2024 or later)", + }, +}; diff --git a/packages/components/date-input/stories/time-input.stories.tsx b/packages/components/date-input/stories/time-input.stories.tsx index 86ba657bdc..16f0190bf2 100644 --- a/packages/components/date-input/stories/time-input.stories.tsx +++ b/packages/components/date-input/stories/time-input.stories.tsx @@ -1,6 +1,6 @@ import React from "react"; import {Meta} from "@storybook/react"; -import {dateInput} from "@nextui-org/theme"; +import {dateInput, button} from "@nextui-org/theme"; import {ClockCircleLinearIcon} from "@nextui-org/shared-icons"; import { parseAbsoluteToLocal, @@ -51,6 +51,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, } as Meta; @@ -61,12 +67,20 @@ const defaultProps = { const Template = (args: TimeInputProps) => ; -export const Default = { - render: Template, - args: { - ...defaultProps, - }, -}; +const FormTemplate = (args: TimeInputProps) => ( +
{ + e.preventDefault(); + alert(`Submitted: ${e.target["time"].value}`); + }} + > + + + +); const LabelPlacementTemplate = (args: TimeInputProps) => (
@@ -126,8 +140,15 @@ const GranularityTemplate = (args: TimeInputProps) => { ); }; -export const Required = { +export const Default = { render: Template, + args: { + ...defaultProps, + }, +}; + +export const Required = { + render: FormTemplate, args: { ...defaultProps, isRequired: true, @@ -282,3 +303,19 @@ export const HourCycle = { granularity: "minute", }, }; +export const WithValidation = { + render: FormTemplate, + + args: { + ...defaultProps, + validate: (value) => { + if (!value) { + return "Please enter a time"; + } + if (value.hour < 9) { + return "Please select a time at 9 A.M. or later"; + } + }, + label: "Time (9 A.M. or later)", + }, +}; diff --git a/packages/components/date-picker/CHANGELOG.md b/packages/components/date-picker/CHANGELOG.md index 61b4390db5..c261c0e469 100644 --- a/packages/components/date-picker/CHANGELOG.md +++ b/packages/components/date-picker/CHANGELOG.md @@ -1,5 +1,34 @@ # @nextui-org/date-picker +## 2.1.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2889](https://github.com/nextui-org/nextui/pull/2889) [`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Update React Aria packages + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#3011](https://github.com/nextui-org/nextui/pull/3011) [`ca8554ccf`](https://github.com/nextui-org/nextui/commit/ca8554ccff143c49aea535b98e4ffdbcd0040a26) Thanks [@wingkwong](https://github.com/wingkwong)! - add missing ref to input wrapper (#3008) + +- [#2845](https://github.com/nextui-org/nextui/pull/2845) [`6bbd234aa`](https://github.com/nextui-org/nextui/commit/6bbd234aa23fa594a191e39265296c3be09fda7f) Thanks [@chirokas](https://github.com/chirokas)! - Fix calendar header controlled state on DatePicker. + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#2908](https://github.com/nextui-org/nextui/pull/2908) [`2a2a0692c`](https://github.com/nextui-org/nextui/commit/2a2a0692ca81ea575d2328d933d775ccbd43ac1c) Thanks [@wingkwong](https://github.com/wingkwong)! - keep date picker style consistent for different variants (#2901) + +- [#3014](https://github.com/nextui-org/nextui/pull/3014) [`20ba81948`](https://github.com/nextui-org/nextui/commit/20ba81948d86ccc7ea4269cceb06e04899903b0e) Thanks [@winchesHe](https://github.com/winchesHe)! - add the correct peerDep version + +- Updated dependencies [[`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002), [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`bf68c91b9`](https://github.com/nextui-org/nextui/commit/bf68c91b9a5be2014830859b0be2127d657ba90f), [`c83ff382b`](https://github.com/nextui-org/nextui/commit/c83ff382b9e5deaa08ed7e64eee484cc4904704d), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a), [`20ba81948`](https://github.com/nextui-org/nextui/commit/20ba81948d86ccc7ea4269cceb06e04899903b0e)]: + - @nextui-org/calendar@2.0.5 + - @nextui-org/date-input@2.1.0 + - @nextui-org/popover@2.1.22 + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/button@2.0.32 + ## 2.0.7 ### Patch Changes diff --git a/packages/components/date-picker/__tests__/date-picker.test.tsx b/packages/components/date-picker/__tests__/date-picker.test.tsx index d4d7c20971..3bb62626a0 100644 --- a/packages/components/date-picker/__tests__/date-picker.test.tsx +++ b/packages/components/date-picker/__tests__/date-picker.test.tsx @@ -458,4 +458,153 @@ describe("DatePicker", () => { expect(getTextValue(combobox)).toBe("2/4/2019"); // uncontrolled }); }); + + describe("Month and Year Picker", () => { + const onHeaderExpandedChangeSpy = jest.fn(); + + afterEach(() => { + onHeaderExpandedChangeSpy.mockClear(); + }); + + it("should show the month and year picker (uncontrolled)", () => { + const {getByRole} = render( + , + ); + + const button = getByRole("button"); + + triggerPress(button); + + const dialog = getByRole("dialog"); + const header = document.querySelector(`button[data-slot="header"]`)!; + + expect(dialog).toBeVisible(); + expect(onHeaderExpandedChangeSpy).not.toHaveBeenCalled(); + + triggerPress(header); + + const month = getByRole("button", {name: "April"}); + const year = getByRole("button", {name: "2024"}); + + expect(month).toHaveAttribute("data-value", "4"); + expect(year).toHaveAttribute("data-value", "2024"); + expect(onHeaderExpandedChangeSpy).toHaveBeenCalledTimes(1); + expect(onHeaderExpandedChangeSpy).toHaveBeenCalledWith(true); + + triggerPress(button); + + expect(dialog).not.toBeInTheDocument(); + expect(onHeaderExpandedChangeSpy).toHaveBeenCalledTimes(2); + expect(onHeaderExpandedChangeSpy).toHaveBeenCalledWith(false); + }); + + it("should show the month and year picker (controlled)", () => { + const {getByRole} = render( + , + ); + + const button = getByRole("button"); + + triggerPress(button); + + const dialog = getByRole("dialog"); + const month = getByRole("button", {name: "April"}); + const year = getByRole("button", {name: "2024"}); + + expect(dialog).toBeVisible(); + expect(month).toHaveAttribute("data-value", "4"); + expect(year).toHaveAttribute("data-value", "2024"); + expect(onHeaderExpandedChangeSpy).not.toHaveBeenCalled(); + + triggerPress(button); + + expect(dialog).not.toBeInTheDocument(); + expect(onHeaderExpandedChangeSpy).not.toHaveBeenCalled(); + }); + + it("CalendarBottomContent should render correctly", () => { + const {getByRole, getByTestId} = render( + } + label="Date" + />, + ); + + const button = getByRole("button"); + + triggerPress(button); + + let dialog = getByRole("dialog"); + let calendarBottomContent = getByTestId("calendar-bottom-content"); + const header = document.querySelector(`button[data-slot="header"]`)!; + + expect(dialog).toBeVisible(); + expect(calendarBottomContent).toBeVisible(); + + triggerPress(header); + + expect(dialog).toBeVisible(); + expect(calendarBottomContent).not.toBeInTheDocument(); + + triggerPress(button); // close date picker + + expect(dialog).not.toBeInTheDocument(); + expect(calendarBottomContent).not.toBeInTheDocument(); + + triggerPress(button); + + dialog = getByRole("dialog"); + calendarBottomContent = getByTestId("calendar-bottom-content"); + + expect(dialog).toBeVisible(); + expect(calendarBottomContent).toBeVisible(); + }); + + it("should close listbox by clicking another datepicker", async () => { + const {getByRole, getAllByRole} = render( + <> + + + , + ); + + const dateButtons = getAllByRole("button"); + + expect(dateButtons[0]).not.toBeNull(); + + expect(dateButtons[1]).not.toBeNull(); + + // open the datepicker dialog by clicking datepicker button in the first datepicker + triggerPress(dateButtons[0]); + + let dialog = getByRole("dialog"); + + // assert that the first datepicker dialog is open + expect(dialog).toBeVisible(); + + // close the datepicker dialog by clicking the second datepicker + triggerPress(dateButtons[1]); + + dialog = getByRole("dialog"); + + // assert that the second datepicker dialog is open + expect(dialog).toBeVisible(); + }); + }); }); diff --git a/packages/components/date-picker/package.json b/packages/components/date-picker/package.json index fedad88c22..2ebbfc86c5 100644 --- a/packages/components/date-picker/package.json +++ b/packages/components/date-picker/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/date-picker", - "version": "2.0.7", + "version": "2.1.0", "description": "A date picker combines a DateInput and a Calendar popover to allow users to enter or select a date and time value.", "keywords": [ "date-picker" @@ -34,8 +34,8 @@ "postpack": "clean-package restore" }, "peerDependencies": { - "@nextui-org/system": ">=2.0.0", - "@nextui-org/theme": ">=2.0.0", + "@nextui-org/system": ">=2.1.0", + "@nextui-org/theme": ">=2.2.0", "react": ">=18", "react-dom": ">=18" }, @@ -47,15 +47,16 @@ "@nextui-org/button": "workspace:*", "@nextui-org/date-input": "workspace:*", "@nextui-org/shared-icons": "workspace:*", - "@react-stately/overlays": "^3.6.3", - "@react-stately/utils": "^3.8.0", + "@nextui-org/aria-utils": "workspace:*", + "@react-stately/overlays": "3.6.5", + "@react-stately/utils": "3.9.1", "@internationalized/date": "^3.5.2", - "@react-aria/datepicker": "^3.9.3", - "@react-aria/i18n": "^3.8.4", - "@react-stately/datepicker": "^3.9.2", - "@react-types/datepicker": "^3.7.2", - "@react-types/shared": "3.21.0", - "@react-aria/utils": "^3.21.1" + "@react-aria/datepicker": "3.9.3", + "@react-aria/i18n": "3.10.2", + "@react-stately/datepicker": "3.9.2", + "@react-types/datepicker": "3.7.2", + "@react-types/shared": "3.22.1", + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/system": "workspace:*", @@ -67,4 +68,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/date-picker/src/date-range-picker-field.tsx b/packages/components/date-picker/src/date-range-picker-field.tsx index 7e9ee28c53..dc9119532c 100644 --- a/packages/components/date-picker/src/date-range-picker-field.tsx +++ b/packages/components/date-picker/src/date-range-picker-field.tsx @@ -44,7 +44,6 @@ function DateRangePickerField( let state = useDateFieldState({ ...otherProps, locale, - validationBehavior: "native", createCalendar: !createCalendarProp || typeof createCalendarProp !== "function" ? createCalendar @@ -74,7 +73,7 @@ function DateRangePickerField( state={state} /> ))} - + ); } diff --git a/packages/components/date-picker/src/use-date-picker-base.ts b/packages/components/date-picker/src/use-date-picker-base.ts index 9df18d552a..da129c2cc2 100644 --- a/packages/components/date-picker/src/use-date-picker-base.ts +++ b/packages/components/date-picker/src/use-date-picker-base.ts @@ -7,13 +7,14 @@ import type {PopoverProps} from "@nextui-org/popover"; import type {ReactNode} from "react"; import type {ValueBase} from "@react-types/shared"; +import {dataAttr} from "@nextui-org/shared-utils"; import {dateInput, DatePickerVariantProps} from "@nextui-org/theme"; -import {useState} from "react"; -import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system"; +import {useCallback} from "react"; +import {HTMLNextUIProps, mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {mergeProps} from "@react-aria/utils"; import {useDOMRef} from "@nextui-org/react-utils"; -import {dataAttr} from "@nextui-org/shared-utils"; import {useLocalizedStringFormatter} from "@react-aria/i18n"; +import {useControlledState} from "@react-stately/utils"; import intlMessages from "../intl/messages"; @@ -109,12 +110,12 @@ export type UseDatePickerBaseProps = Props & DateInputProps, Variants | "ref" | "createCalendar" | "startContent" | "endContent" | "inputRef" > & - Omit, keyof ValueBase | "validate" | "validationBehavior">; + Omit, keyof ValueBase | "validate">; export function useDatePickerBase(originalProps: UseDatePickerBaseProps) { - const [props, variantProps] = mapPropsVariants(originalProps, dateInput.variantKeys); + const globalContext = useProviderContext(); - const [isCalendarHeaderExpanded, setIsCalendarHeaderExpanded] = useState(false); + const [props, variantProps] = mapPropsVariants(originalProps, dateInput.variantKeys); const { as, @@ -128,7 +129,7 @@ export function useDatePickerBase(originalProps: UseDatePic description, startContent, validationState, - // validationBehavior, TODO: Uncomment this one we support `native` and `aria` validations + validationBehavior, visibleMonths = 1, pageBehavior = "visible", calendarWidth = 256, @@ -144,8 +145,27 @@ export function useDatePickerBase(originalProps: UseDatePic createCalendar, } = props; + const { + isHeaderExpanded, + isHeaderDefaultExpanded, + onHeaderExpandedChange, + ...restUserCalendarProps + } = userCalendarProps; + + const handleHeaderExpandedChange = useCallback( + (isExpanded: boolean | undefined) => { + onHeaderExpandedChange?.(isExpanded || false); + }, + [onHeaderExpandedChange], + ); + + const [isCalendarHeaderExpanded, setIsCalendarHeaderExpanded] = useControlledState< + boolean | undefined + >(isHeaderExpanded, isHeaderDefaultExpanded ?? false, handleHeaderExpandedChange); + const domRef = useDOMRef(ref); - const disableAnimation = originalProps.disableAnimation ?? false; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; let stringFormatter = useLocalizedStringFormatter(intlMessages) as any; @@ -191,17 +211,12 @@ export function useDatePickerBase(originalProps: UseDatePic pageBehavior, isDateUnavailable, showMonthAndYearPickers, + isHeaderExpanded: isCalendarHeaderExpanded, onHeaderExpandedChange: setIsCalendarHeaderExpanded, - color: - (originalProps.variant === "bordered" || originalProps.variant === "underlined") && - isDefaultColor - ? "foreground" - : isDefaultColor - ? "primary" - : originalProps.color, + color: isDefaultColor ? "primary" : originalProps.color, disableAnimation, }, - userCalendarProps, + restUserCalendarProps, ), }; @@ -216,6 +231,7 @@ export function useDatePickerBase(originalProps: UseDatePic shouldForceLeadingZeros, isInvalid, errorMessage, + validationBehavior, "data-invalid": dataAttr(originalProps?.isInvalid), } as DateInputProps; @@ -227,6 +243,7 @@ export function useDatePickerBase(originalProps: UseDatePic placeholderValue: timePlaceholder, hourCycle: props.hourCycle, hideTimeZone: props.hideTimeZone, + validationBehavior, } as TimeInputProps; const popoverProps: PopoverProps = { @@ -250,6 +267,12 @@ export function useDatePickerBase(originalProps: UseDatePic "data-slot": "selector-icon", }; + const onClose = () => { + if (isHeaderExpanded === undefined) { + setIsCalendarHeaderExpanded(false); + } + }; + return { domRef, endContent, @@ -273,6 +296,7 @@ export function useDatePickerBase(originalProps: UseDatePic userTimeInputProps, selectorButtonProps, selectorIconProps, + onClose, }; } diff --git a/packages/components/date-picker/src/use-date-picker.ts b/packages/components/date-picker/src/use-date-picker.ts index fa5b97bfcf..8d4957bdf2 100644 --- a/packages/components/date-picker/src/use-date-picker.ts +++ b/packages/components/date-picker/src/use-date-picker.ts @@ -8,12 +8,14 @@ import type {UseDatePickerBaseProps} from "./use-date-picker-base"; import type {DOMAttributes} from "@nextui-org/system"; import type {DatePickerSlots, SlotsToClasses} from "@nextui-org/theme"; +import {useProviderContext} from "@nextui-org/system"; import {useMemo} from "react"; import {datePicker} from "@nextui-org/theme"; import {useDatePickerState} from "@react-stately/datepicker"; import {AriaDatePickerProps, useDatePicker as useAriaDatePicker} from "@react-aria/datepicker"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {mergeProps} from "@react-aria/utils"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; import {useDatePickerBase} from "./use-date-picker-base"; @@ -54,6 +56,11 @@ export function useDatePicker({ classNames, ...originalProps }: UseDatePickerProps) { + const globalContext = useProviderContext(); + + const validationBehavior = + originalProps.validationBehavior ?? globalContext?.validationBehavior ?? "aria"; + const { domRef, endContent, @@ -74,11 +81,18 @@ export function useDatePicker({ userTimeInputProps, selectorButtonProps, selectorIconProps, - } = useDatePickerBase(originalProps); + onClose, + } = useDatePickerBase({...originalProps, validationBehavior}); let state: DatePickerState = useDatePickerState({ ...originalProps, + validationBehavior, shouldCloseOnSelect: () => !state.hasTime, + onOpenChange: (isOpen) => { + if (!isOpen) { + onClose(); + } + }, }); const baseStyles = clsx(classNames?.base, className); @@ -101,7 +115,7 @@ export function useDatePicker({ calendarProps: ariaCalendarProps, descriptionProps, errorMessageProps, - } = useAriaDatePicker(originalProps, state, domRef); + } = useAriaDatePicker({...originalProps, validationBehavior}, state, domRef); // Time field values originalProps.maxValue && "hour" in originalProps.maxValue ? originalProps.maxValue : null; @@ -166,6 +180,9 @@ export function useDatePicker({ ), }), }, + shouldCloseOnInteractOutside: popoverProps?.shouldCloseOnInteractOutside + ? popoverProps.shouldCloseOnInteractOutside + : (element: Element) => ariaShouldCloseOnInteractOutside(element, domRef, state), }; }; diff --git a/packages/components/date-picker/src/use-date-range-picker.ts b/packages/components/date-picker/src/use-date-range-picker.ts index 7851b31a77..42df170d40 100644 --- a/packages/components/date-picker/src/use-date-range-picker.ts +++ b/packages/components/date-picker/src/use-date-range-picker.ts @@ -14,12 +14,14 @@ import type {DateInputGroupProps} from "@nextui-org/date-input"; import type {DateRangePickerSlots, SlotsToClasses} from "@nextui-org/theme"; import type {DateInputProps} from "@nextui-org/date-input"; +import {useProviderContext} from "@nextui-org/system"; import {useMemo, useRef} from "react"; import {useDateRangePickerState} from "@react-stately/datepicker"; import {useDateRangePicker as useAriaDateRangePicker} from "@react-aria/datepicker"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {mergeProps} from "@react-aria/utils"; import {dateRangePicker, dateInput} from "@nextui-org/theme"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; import {useDatePickerBase} from "./use-date-picker-base"; interface Props @@ -57,7 +59,7 @@ export type UseDateRangePickerProps = Props & AriaDateRa export function useDateRangePicker({ as, - isInvalid, + isInvalid: isInvalidProp, description, startContent, endContent, @@ -67,6 +69,11 @@ export function useDateRangePicker({ classNames, ...originalProps }: UseDateRangePickerProps) { + const globalContext = useProviderContext(); + + const validationBehavior = + originalProps.validationBehavior ?? globalContext?.validationBehavior ?? "aria"; + const { domRef, slotsProps, @@ -86,10 +93,11 @@ export function useDateRangePicker({ hasMultipleMonths, selectorButtonProps, selectorIconProps, - } = useDatePickerBase(originalProps); + } = useDatePickerBase({...originalProps, validationBehavior}); let state: DateRangePickerState = useDateRangePickerState({ ...originalProps, + validationBehavior, shouldCloseOnSelect: () => !state.hasTime, }); @@ -107,7 +115,10 @@ export function useDateRangePicker({ validationErrors, descriptionProps, errorMessageProps, - } = useAriaDateRangePicker(originalProps, state, domRef); + isInvalid: isAriaInvalid, + } = useAriaDateRangePicker({...originalProps, validationBehavior}, state, domRef); + + const isInvalid = isInvalidProp || isAriaInvalid; const slots = useMemo( () => @@ -205,6 +216,9 @@ export function useDateRangePicker({ ), }), }, + shouldCloseOnInteractOutside: popoverProps?.shouldCloseOnInteractOutside + ? popoverProps.shouldCloseOnInteractOutside + : (element: Element) => ariaShouldCloseOnInteractOutside(element, domRef, state), } as PopoverProps; }; @@ -315,6 +329,7 @@ export function useDateRangePicker({ const getInputWrapperProps = (props = {}) => { return { + ref: domRef, ...props, ...groupProps, "data-slot": "input-wrapper", diff --git a/packages/components/date-picker/stories/date-picker.stories.tsx b/packages/components/date-picker/stories/date-picker.stories.tsx index 0db7446cd8..1dac4ee286 100644 --- a/packages/components/date-picker/stories/date-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-picker.stories.tsx @@ -1,6 +1,6 @@ import React from "react"; import {Meta} from "@storybook/react"; -import {dateInput} from "@nextui-org/theme"; +import {dateInput, button} from "@nextui-org/theme"; import { DateValue, getLocalTimeZone, @@ -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"; @@ -59,6 +59,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, decorators: [ (Story) => ( @@ -77,6 +83,21 @@ const defaultProps = { const Template = (args: DatePickerProps) => ; +const FormTemplate = (args: DatePickerProps) => ( +
{ + e.preventDefault(); + alert(`Submitted: ${e.target["date"].value}`); + }} + > + + + +); + const LabelPlacementTemplate = (args: DatePickerProps) => (
@@ -331,7 +352,7 @@ export const Controlled = { }; export const Required = { - render: Template, + render: FormTemplate, args: { ...defaultProps, isRequired: true, @@ -499,3 +520,20 @@ export const Presets = { ...defaultProps, }, }; + +export const WithValidation = { + render: FormTemplate, + + args: { + ...defaultProps, + validate: (value) => { + if (!value) { + return "Please enter a date"; + } + if (value.year < 2024) { + return "Please select a date in the year 2024 or later"; + } + }, + label: "Date (Year 2024 or later)", + }, +}; 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..f24d1d4385 100644 --- a/packages/components/date-picker/stories/date-range-picker.stories.tsx +++ b/packages/components/date-picker/stories/date-range-picker.stories.tsx @@ -1,6 +1,6 @@ import React from "react"; import {Meta} from "@storybook/react"; -import {dateInput} from "@nextui-org/theme"; +import {dateInput, button} from "@nextui-org/theme"; import { endOfMonth, endOfWeek, @@ -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"; @@ -61,6 +61,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, decorators: [ (Story) => ( @@ -78,6 +84,23 @@ const defaultProps = { const Template = (args: DateRangePickerProps) => ; +const FormTemplate = (args: DateRangePickerProps) => ( +
{ + e.preventDefault(); + alert( + `Submitted: start -> ${e.target["start-date"].value} end -> ${e.target["end-date"].value}`, + ); + }} + > + + + +); + const LabelPlacementTemplate = (args: DateRangePickerProps) => (
@@ -398,7 +421,7 @@ export const Controlled = { }; export const Required = { - render: Template, + render: FormTemplate, args: { ...defaultProps, isRequired: true, @@ -579,3 +602,22 @@ export const Presets = { visibleMonths: 2, }, }; + +export const WithValidation = { + render: FormTemplate, + + args: { + ...defaultProps, + validate: (value) => { + if (!value || !value.start || !value.end) { + return "Please enter a valid date range"; + } + const {start, end} = value; + + if (start.year < 2024 || end.year < 2024) { + return "Both start and end dates must be in the year 2024 or later"; + } + }, + label: "Date Range (Year 2024 or later)", + }, +}; diff --git a/packages/components/divider/CHANGELOG.md b/packages/components/divider/CHANGELOG.md index e4d3f1b777..1b3907f7e5 100644 --- a/packages/components/divider/CHANGELOG.md +++ b/packages/components/divider/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/divider +## 2.0.28 + +### Patch Changes + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.0.27 ### Patch Changes diff --git a/packages/components/divider/package.json b/packages/components/divider/package.json index b92d921405..69f2990e2b 100644 --- a/packages/components/divider/package.json +++ b/packages/components/divider/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/divider", - "version": "2.0.27", + "version": "2.0.28", "description": ". A separator is a visual divider between two groups of content", "keywords": [ "divider" @@ -42,7 +42,7 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-rsc-utils": "workspace:*", "@nextui-org/system-rsc": "workspace:*", - "@react-types/shared": "^3.22.1" + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -51,4 +51,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/dropdown/CHANGELOG.md b/packages/components/dropdown/CHANGELOG.md index 05a28366b0..b33c22baa3 100644 --- a/packages/components/dropdown/CHANGELOG.md +++ b/packages/components/dropdown/CHANGELOG.md @@ -1,5 +1,22 @@ # @nextui-org/dropdown +## 2.1.24 + +### Patch Changes + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2970](https://github.com/nextui-org/nextui/pull/2970) [`7df2c71ec`](https://github.com/nextui-org/nextui/commit/7df2c71ecc5f06d60807b6b3502d3a118080a0d5) Thanks [@wingkwong](https://github.com/wingkwong)! - Focus on the first item when pressing Space / Enter key on dropdown menu open (#2863) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531), [`7df2c71ec`](https://github.com/nextui-org/nextui/commit/7df2c71ecc5f06d60807b6b3502d3a118080a0d5), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/popover@2.1.22 + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/menu@2.0.23 + ## 2.1.23 ### Patch Changes diff --git a/packages/components/dropdown/__tests__/dropdown.test.tsx b/packages/components/dropdown/__tests__/dropdown.test.tsx index 3080321f5b..ee1ada513d 100644 --- a/packages/components/dropdown/__tests__/dropdown.test.tsx +++ b/packages/components/dropdown/__tests__/dropdown.test.tsx @@ -1,7 +1,8 @@ import * as React from "react"; -import {act, render} from "@testing-library/react"; +import {act, render, fireEvent} from "@testing-library/react"; import {Button} from "@nextui-org/button"; import userEvent from "@testing-library/user-event"; +import {keyCodes} from "@nextui-org/test-utils"; import {User} from "@nextui-org/user"; import {Image} from "@nextui-org/image"; import {Avatar} from "@nextui-org/avatar"; @@ -537,4 +538,145 @@ describe("Dropdown", () => { spy.mockRestore(); }); + + it("should close listbox by clicking another dropdown", async () => { + const wrapper = render( + <> + + + + + + New file + Copy link + Edit file + + Delete file + + + + + + + + + New file + Copy link + Edit file + + Delete file + + + + , + ); + + const dropdown = wrapper.getByTestId("dropdown"); + + const dropdown2 = wrapper.getByTestId("dropdown2"); + + expect(dropdown).not.toBeNull(); + + expect(dropdown2).not.toBeNull(); + + // open the dropdown listbox by clicking dropdownor button in the first dropdown + await act(async () => { + await userEvent.click(dropdown); + }); + + // assert that the first dropdown listbox is open + expect(dropdown).toHaveAttribute("aria-expanded", "true"); + + // assert that the second dropdown listbox is close + expect(dropdown2).toHaveAttribute("aria-expanded", "false"); + + // close the dropdown listbox by clicking the second dropdown + await act(async () => { + await userEvent.click(dropdown2); + }); + + // assert that the first dropdown listbox is closed + expect(dropdown).toHaveAttribute("aria-expanded", "false"); + + // assert that the second dropdown listbox is open + expect(dropdown2).toHaveAttribute("aria-expanded", "true"); + }); +}); + +describe("Keyboard interactions", () => { + it("should focus on the first item on keyDown (Enter)", async () => { + const wrapper = render( + + + + + + New file + Copy link + Edit file + + Delete file + + + , + ); + + let triggerButton = wrapper.getByTestId("trigger-test"); + + act(() => { + triggerButton.focus(); + }); + + expect(triggerButton).toHaveFocus(); + + fireEvent.keyDown(triggerButton, {key: "Enter", charCode: keyCodes.Enter}); + + let menu = wrapper.queryByRole("menu"); + + expect(menu).toBeTruthy(); + + let menuItems = wrapper.getAllByRole("menuitemradio"); + + expect(menuItems.length).toBe(4); + + expect(menuItems[0]).toHaveFocus(); + }); + + it("should focus on the first item on keyDown (Space)", async () => { + const wrapper = render( + + + + + + New file + Copy link + Edit file + + Delete file + + + , + ); + + let triggerButton = wrapper.getByTestId("trigger-test"); + + act(() => { + triggerButton.focus(); + }); + + expect(triggerButton).toHaveFocus(); + + fireEvent.keyDown(triggerButton, {key: " ", charCode: keyCodes.Space}); + + let menu = wrapper.queryByRole("menu"); + + expect(menu).toBeTruthy(); + + let menuItems = wrapper.getAllByRole("menuitemradio"); + + expect(menuItems.length).toBe(4); + + expect(menuItems[0]).toHaveFocus(); + }); }); diff --git a/packages/components/dropdown/package.json b/packages/components/dropdown/package.json index fc0f766ffc..0eb9555ccd 100644 --- a/packages/components/dropdown/package.json +++ b/packages/components/dropdown/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/dropdown", - "version": "2.1.23", + "version": "2.1.24", "description": "A dropdown displays a list of actions or options that a user can choose.", "keywords": [ "dropdown" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -45,11 +45,12 @@ "@nextui-org/popover": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/menu": "^3.13.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/menu": "^3.6.1", - "@react-aria/focus": "^3.16.2", - "@react-types/menu": "^3.9.7" + "@nextui-org/aria-utils": "workspace:*", + "@react-aria/menu": "3.13.1", + "@react-aria/utils": "3.23.2", + "@react-stately/menu": "3.6.1", + "@react-aria/focus": "3.16.2", + "@react-types/menu": "3.9.7" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -59,10 +60,11 @@ "@nextui-org/user": "workspace:*", "@nextui-org/image": "workspace:*", "@nextui-org/shared-icons": "workspace:*", + "@nextui-org/test-utils": "workspace:*", "framer-motion": "^11.0.22", "clean-package": "2.2.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/dropdown/src/dropdown-menu.tsx b/packages/components/dropdown/src/dropdown-menu.tsx index db3811bd6f..11617934c6 100644 --- a/packages/components/dropdown/src/dropdown-menu.tsx +++ b/packages/components/dropdown/src/dropdown-menu.tsx @@ -6,7 +6,7 @@ import {ForwardedRef, ReactElement, Ref} from "react"; import {useDropdownContext} from "./dropdown-context"; -interface Props extends Omit, "menuProps"> {} +interface Props extends Omit, "menuProps"> {} function DropdownMenu(props: Props, ref: ForwardedRef) { const {getMenuProps} = useDropdownContext(); @@ -20,10 +20,10 @@ function DropdownMenu(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type DropdownMenuProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(DropdownMenu) as ( +export default forwardRef(DropdownMenu) as ( props: DropdownMenuProps, ) => ReactElement; diff --git a/packages/components/dropdown/src/use-dropdown.ts b/packages/components/dropdown/src/use-dropdown.ts index 55fb9071b1..0124669b95 100644 --- a/packages/components/dropdown/src/use-dropdown.ts +++ b/packages/components/dropdown/src/use-dropdown.ts @@ -1,13 +1,14 @@ -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import type {PopoverProps} from "@nextui-org/popover"; import type {MenuTriggerType} from "@react-types/menu"; import type {Ref} from "react"; +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {useMenuTriggerState} from "@react-stately/menu"; import {useMenuTrigger} from "@react-aria/menu"; import {dropdown} from "@nextui-org/theme"; import {clsx} from "@nextui-org/shared-utils"; import {ReactRef, mergeRefs} from "@nextui-org/react-utils"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; import {useMemo, useRef} from "react"; import {mergeProps} from "@react-aria/utils"; import {MenuProps} from "@nextui-org/menu"; @@ -41,6 +42,8 @@ interface Props extends HTMLNextUIProps<"div"> { export type UseDropdownProps = Props & Omit; export function useDropdown(props: UseDropdownProps) { + const globalContext = useProviderContext(); + const { as, triggerRef: triggerRefProp, @@ -54,7 +57,7 @@ export function useDropdown(props: UseDropdownProps) { closeOnSelect = true, shouldBlockScroll = true, classNames: classNamesProp, - disableAnimation = false, + disableAnimation = globalContext?.disableAnimation ?? false, onClose, className, ...otherProps @@ -102,21 +105,28 @@ export function useDropdown(props: UseDropdownProps) { } }; - const getPopoverProps: PropGetter = (props = {}) => ({ - state, - placement, - ref: popoverRef, - disableAnimation, - shouldBlockScroll, - scrollRef: menuRef, - triggerRef: menuTriggerRef, - ...mergeProps(otherProps, props), - classNames: { - ...classNamesProp, - ...props.classNames, - content: clsx(classNames, classNamesProp?.content, props.className), - }, - }); + const getPopoverProps: PropGetter = (props = {}) => { + const popoverProps = mergeProps(otherProps, props); + + return { + state, + placement, + ref: popoverRef, + disableAnimation, + shouldBlockScroll, + scrollRef: menuRef, + triggerRef: menuTriggerRef, + ...popoverProps, + classNames: { + ...classNamesProp, + ...props.classNames, + content: clsx(classNames, classNamesProp?.content, props.className), + }, + shouldCloseOnInteractOutside: popoverProps?.shouldCloseOnInteractOutside + ? popoverProps.shouldCloseOnInteractOutside + : (element: Element) => ariaShouldCloseOnInteractOutside(element, triggerRef, state), + }; + }; const getMenuTriggerProps: PropGetter = ( originalProps = {}, @@ -124,7 +134,7 @@ export function useDropdown(props: UseDropdownProps) { ) => { // These props are not needed for the menu trigger since it is handled by the popover trigger. // eslint-disable-next-line @typescript-eslint/no-unused-vars - const {onKeyDown, onPress, onPressStart, ...otherMenuTriggerProps} = menuTriggerProps; + const {onPress, onPressStart, ...otherMenuTriggerProps} = menuTriggerProps; return { ...mergeProps(otherMenuTriggerProps, {isDisabled}, originalProps), @@ -132,7 +142,7 @@ export function useDropdown(props: UseDropdownProps) { }; }; - const getMenuProps = ( + const getMenuProps = ( props?: Partial>, _ref: Ref | null | undefined = null, ) => { diff --git a/packages/components/dropdown/stories/dropdown.stories.tsx b/packages/components/dropdown/stories/dropdown.stories.tsx index 7a74ef096a..20455396d7 100644 --- a/packages/components/dropdown/stories/dropdown.stories.tsx +++ b/packages/components/dropdown/stories/dropdown.stories.tsx @@ -116,7 +116,6 @@ const defaultProps = { offset: 7, isDisabled: false, defaultOpen: false, - disableAnimation: false, }; const items = [ diff --git a/packages/components/image/CHANGELOG.md b/packages/components/image/CHANGELOG.md index 76ba8c67b6..af5bf620d3 100644 --- a/packages/components/image/CHANGELOG.md +++ b/packages/components/image/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/image +## 2.0.28 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.27 ### Patch Changes diff --git a/packages/components/image/package.json b/packages/components/image/package.json index 9ed834d9fe..685b199dd0 100644 --- a/packages/components/image/package.json +++ b/packages/components/image/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/image", - "version": "2.0.27", + "version": "2.0.28", "description": "A simple image component", "keywords": [ "image" @@ -52,4 +52,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/image/src/use-image.ts b/packages/components/image/src/use-image.ts index 7e516d9823..7f6c3d4842 100644 --- a/packages/components/image/src/use-image.ts +++ b/packages/components/image/src/use-image.ts @@ -1,7 +1,12 @@ import type {ImageVariantProps, SlotsToClasses, ImageSlots} from "@nextui-org/theme"; import {ImgHTMLAttributes, useCallback} from "react"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {image} from "@nextui-org/theme"; import {useDOMRef} from "@nextui-org/react-utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -70,6 +75,8 @@ interface Props extends HTMLNextUIProps<"img"> { export type UseImageProps = Props & ImageVariantProps; export function useImage(originalProps: UseImageProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, image.variantKeys); const { @@ -103,6 +110,9 @@ export function useImage(originalProps: UseImageProps) { crossOrigin, }); + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; + const isImgLoaded = imageStatus === "loaded" && !isLoadingProp; const isLoading = imageStatus === "loading" || isLoadingProp; const isZoomed = originalProps.isZoomed; @@ -128,9 +138,10 @@ export function useImage(originalProps: UseImageProps) { () => image({ ...variantProps, + disableAnimation, showSkeleton, }), - [objectToDeps(variantProps), showSkeleton], + [objectToDeps(variantProps), disableAnimation, showSkeleton], ); const baseStyles = clsx(className, classNames?.img); diff --git a/packages/components/image/stories/image.stories.tsx b/packages/components/image/stories/image.stories.tsx index 2961dcd607..96fa31d293 100644 --- a/packages/components/image/stories/image.stories.tsx +++ b/packages/components/image/stories/image.stories.tsx @@ -30,6 +30,11 @@ export default { type: "boolean", }, }, + disableAnimation: { + control: { + type: "boolean", + }, + }, showSkeleton: { control: { disable: true, diff --git a/packages/components/input/CHANGELOG.md b/packages/components/input/CHANGELOG.md index 1163fb1228..da97ccb899 100644 --- a/packages/components/input/CHANGELOG.md +++ b/packages/components/input/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/input +## 2.2.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.1.21 ### Patch Changes diff --git a/packages/components/input/__tests__/input.test.tsx b/packages/components/input/__tests__/input.test.tsx index d9ab83d694..13de8cf841 100644 --- a/packages/components/input/__tests__/input.test.tsx +++ b/packages/components/input/__tests__/input.test.tsx @@ -1,6 +1,7 @@ import * as React from "react"; -import {render} from "@testing-library/react"; +import {render, renderHook, fireEvent} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import {useForm} from "react-hook-form"; import {Input} from "../src"; @@ -36,10 +37,17 @@ describe("Input", () => { expect(container.querySelector("input")).toHaveAttribute("disabled"); }); - it("should have required attribute when isRequired", () => { - const {container} = render(); + it("should have required attribute when isRequired with native validationBehavior", () => { + const {container} = render(); expect(container.querySelector("input")).toHaveAttribute("required"); + expect(container.querySelector("input")).not.toHaveAttribute("aria-required"); + }); + + it("should have aria-required attribute when isRequired with aria validationBehavior", () => { + const {container} = render(); + + expect(container.querySelector("input")).not.toHaveAttribute("required"); expect(container.querySelector("input")).toHaveAttribute("aria-required", "true"); }); @@ -146,3 +154,78 @@ describe("Input", () => { expect(onClear).toHaveBeenCalledTimes(1); }); }); + +describe("Input with React Hook Form", () => { + let input1: HTMLInputElement; + let input2: HTMLInputElement; + let input3: HTMLInputElement; + let submitButton: HTMLButtonElement; + let onSubmit: () => void; + + beforeEach(() => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + withDefaultValue: "wkw", + withoutDefaultValue: "", + requiredField: "", + }, + }), + ); + + const { + handleSubmit, + register, + formState: {errors}, + } = result.current; + + onSubmit = jest.fn(); + + render( +
+ + + + {errors.requiredField && This field is required} + +
, + ); + + input1 = document.querySelector("input[name=withDefaultValue]")!; + input2 = document.querySelector("input[name=withoutDefaultValue]")!; + input3 = document.querySelector("input[name=requiredField]")!; + submitButton = document.querySelector("button")!; + }); + + it("should work with defaultValues", () => { + expect(input1).toHaveValue("wkw"); + expect(input2).toHaveValue(""); + expect(input3).toHaveValue(""); + }); + + it("should not submit form when required field is empty", async () => { + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(0); + }); + + it("should submit form when required field is not empty", async () => { + fireEvent.change(input3, {target: {value: "updated"}}); + + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/components/input/package.json b/packages/components/input/package.json index 7b5dffcc62..4f61664745 100644 --- a/packages/components/input/package.json +++ b/packages/components/input/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/input", - "version": "2.1.21", + "version": "2.2.0", "description": "The input component is designed for capturing user input within a text field.", "keywords": [ "input" @@ -44,13 +44,13 @@ "@nextui-org/shared-icons": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/textfield": "^3.14.3", - "@react-aria/utils": "^3.23.2", - "@react-stately/utils": "^3.9.1", - "@react-types/shared": "^3.22.1", - "@react-types/textfield": "^3.9.1", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/textfield": "3.14.3", + "@react-aria/utils": "3.23.2", + "@react-stately/utils": "3.9.1", + "@react-types/shared": "3.22.1", + "@react-types/textfield": "3.9.1", "react-textarea-autosize": "^8.5.3" }, "devDependencies": { @@ -62,4 +62,4 @@ "react-hook-form": "^7.51.3" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/input/src/use-input.ts b/packages/components/input/src/use-input.ts index 32b61841b3..4902d3279c 100644 --- a/packages/components/input/src/use-input.ts +++ b/packages/components/input/src/use-input.ts @@ -1,7 +1,12 @@ import type {InputVariantProps, SlotsToClasses, InputSlots} from "@nextui-org/theme"; import type {AriaTextFieldOptions} from "@react-aria/textfield"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; import {AriaTextFieldProps} from "@react-types/textfield"; import {useFocusRing} from "@react-aria/focus"; @@ -81,11 +86,13 @@ export interface Props["autoCapitalize"]; export type UseInputProps = - Props & Omit & InputVariantProps; + Props & Omit & InputVariantProps; export function useInput( originalProps: UseInputProps, ) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, input.variantKeys); const { @@ -104,6 +111,7 @@ export function useInput {}, ...otherProps @@ -120,6 +128,9 @@ export function useInput(ref); const baseDomRef = useDOMRef(baseRef); const inputWrapperRef = useDOMRef(wrapperRef); @@ -164,13 +175,13 @@ export function useInput ( diff --git a/packages/components/input/stories/textarea.stories.tsx b/packages/components/input/stories/textarea.stories.tsx index 9c75c2445d..fc0799be94 100644 --- a/packages/components/input/stories/textarea.stories.tsx +++ b/packages/components/input/stories/textarea.stories.tsx @@ -52,6 +52,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, decorators: [ (Story) => ( diff --git a/packages/components/kbd/CHANGELOG.md b/packages/components/kbd/CHANGELOG.md index e74d64534d..dd863d22d3 100644 --- a/packages/components/kbd/CHANGELOG.md +++ b/packages/components/kbd/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/kbd +## 2.0.29 + +### Patch Changes + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.0.28 ### Patch Changes diff --git a/packages/components/kbd/package.json b/packages/components/kbd/package.json index fe1e3a5aee..a794374bd2 100644 --- a/packages/components/kbd/package.json +++ b/packages/components/kbd/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/kbd", - "version": "2.0.28", + "version": "2.0.29", "description": "The keyboard key components indicates which key or set of keys used to execute a specificv action", "keywords": [ "kbd" @@ -42,7 +42,7 @@ "@nextui-org/system-rsc": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/utils": "^3.23.2" + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -51,4 +51,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/link/CHANGELOG.md b/packages/components/link/CHANGELOG.md index 49fb4dd033..5a8e4b1717 100644 --- a/packages/components/link/CHANGELOG.md +++ b/packages/components/link/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/link +## 2.0.30 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.29 ### Patch Changes diff --git a/packages/components/link/package.json b/packages/components/link/package.json index 242368ded5..d09b7a3dde 100644 --- a/packages/components/link/package.json +++ b/packages/components/link/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/link", - "version": "2.0.29", + "version": "2.0.30", "description": "Links allow users to click their way from page to page. This component is styled to resemble a hyperlink and semantically renders an <a>", "keywords": [ "link" @@ -44,10 +44,10 @@ "@nextui-org/shared-icons": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-aria-link": "workspace:*", - "@react-aria/link": "^3.6.5", - "@react-aria/utils": "^3.23.2", - "@react-aria/focus": "^3.16.2", - "@react-types/link": "^3.5.3" + "@react-aria/link": "3.6.5", + "@react-aria/utils": "3.23.2", + "@react-aria/focus": "3.16.2", + "@react-types/link": "3.5.3" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -57,4 +57,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/link/src/use-link.ts b/packages/components/link/src/use-link.ts index 9082dcacf7..bc1617c923 100644 --- a/packages/components/link/src/use-link.ts +++ b/packages/components/link/src/use-link.ts @@ -3,7 +3,12 @@ import type {LinkVariantProps} from "@nextui-org/theme"; import {link} from "@nextui-org/theme"; import {useAriaLink} from "@nextui-org/use-aria-link"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useDOMRef} from "@nextui-org/react-utils"; import {useFocusRing} from "@react-aria/focus"; import {dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -36,6 +41,8 @@ interface Props extends HTMLNextUIProps<"a">, LinkVariantProps { export type UseLinkProps = Props & AriaLinkProps; export function useLink(originalProps: UseLinkProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, link.variantKeys); const { @@ -57,6 +64,8 @@ export function useLink(originalProps: UseLinkProps) { const Component = as || "a"; const domRef = useDOMRef(ref); + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; const {linkProps} = useAriaLink( { @@ -85,9 +94,10 @@ export function useLink(originalProps: UseLinkProps) { () => link({ ...variantProps, + disableAnimation, className, }), - [objectToDeps(variantProps), className], + [objectToDeps(variantProps), disableAnimation, className], ); const getLinkProps: PropGetter = useCallback(() => { diff --git a/packages/components/listbox/CHANGELOG.md b/packages/components/listbox/CHANGELOG.md index cd490c9364..a2f520baed 100644 --- a/packages/components/listbox/CHANGELOG.md +++ b/packages/components/listbox/CHANGELOG.md @@ -1,5 +1,17 @@ # @nextui-org/listbox +## 2.1.20 + +### Patch Changes + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e)]: + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/divider@2.0.28 + ## 2.1.19 ### Patch Changes diff --git a/packages/components/listbox/package.json b/packages/components/listbox/package.json index 5bb5bce855..05c6ab95c9 100644 --- a/packages/components/listbox/package.json +++ b/packages/components/listbox/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/listbox", - "version": "2.1.19", + "version": "2.1.20", "description": "A listbox displays a list of options and allows a user to select one or more of them.", "keywords": [ "listbox" @@ -45,13 +45,13 @@ "@nextui-org/divider": "workspace:*", "@nextui-org/aria-utils": "workspace:*", "@nextui-org/use-is-mobile": "workspace:*", - "@react-aria/utils": "^3.23.2", - "@react-aria/listbox": "^3.11.5", - "@react-stately/list": "^3.10.3", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-types/menu": "^3.9.7", - "@react-types/shared": "^3.22.1" + "@react-aria/utils": "3.23.2", + "@react-aria/listbox": "3.11.5", + "@react-stately/list": "3.10.3", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-types/menu": "3.9.7", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -66,4 +66,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/listbox/src/listbox.tsx b/packages/components/listbox/src/listbox.tsx index 855ef169ca..ffc0fa6f82 100644 --- a/packages/components/listbox/src/listbox.tsx +++ b/packages/components/listbox/src/listbox.tsx @@ -76,7 +76,7 @@ function Listbox(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type ListboxProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(Listbox) as (props: ListboxProps) => ReactElement; +export default forwardRef(Listbox) as (props: ListboxProps) => ReactElement; diff --git a/packages/components/listbox/src/use-listbox-item.ts b/packages/components/listbox/src/use-listbox-item.ts index 0b13d13e15..ac23c21aed 100644 --- a/packages/components/listbox/src/use-listbox-item.ts +++ b/packages/components/listbox/src/use-listbox-item.ts @@ -2,7 +2,12 @@ import type {ListboxItemBaseProps} from "./base/listbox-item-base"; import {useMemo, useRef, useCallback} from "react"; import {listboxItem} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useFocusRing} from "@react-aria/focus"; import {Node} from "@react-types/shared"; import {filterDOMProps} from "@nextui-org/react-utils"; @@ -22,6 +27,8 @@ export type UseListboxItemProps = Props & Omit, keyof Props>; export function useListboxItem(originalProps: UseListboxItemProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, listboxItem.variantKeys); const { @@ -44,7 +51,8 @@ export function useListboxItem(originalProps: UseListboxItemPr ...otherProps } = props; - const disableAnimation = originalProps.disableAnimation; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const domRef = useRef(null); diff --git a/packages/components/listbox/src/use-listbox.ts b/packages/components/listbox/src/use-listbox.ts index 92dcf04ade..6a59d6aeb9 100644 --- a/packages/components/listbox/src/use-listbox.ts +++ b/packages/components/listbox/src/use-listbox.ts @@ -1,7 +1,7 @@ import type {KeyboardDelegate} from "@react-types/shared"; import {AriaListBoxProps, useListBox as useAriaListbox} from "@react-aria/listbox"; -import {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; import {listbox, ListboxVariantProps, ListboxSlots, SlotsToClasses} from "@nextui-org/theme"; import {ListState, useListState} from "@react-stately/list"; import {filterDOMProps, ReactRef, useDOMRef} from "@nextui-org/react-utils"; @@ -97,6 +97,8 @@ interface Props extends Omit, "children"> { export type UseListboxProps = Props & AriaListBoxOptions & ListboxVariantProps; export function useListbox(props: UseListboxProps) { + const globalContext = useProviderContext(); + const { ref, as, @@ -106,7 +108,7 @@ export function useListbox(props: UseListboxProps) { onAction, children, onSelectionChange, - disableAnimation, + disableAnimation = globalContext?.disableAnimation ?? false, itemClasses, className, topContent, diff --git a/packages/components/menu/CHANGELOG.md b/packages/components/menu/CHANGELOG.md index 16d96d8d5d..b347c30b78 100644 --- a/packages/components/menu/CHANGELOG.md +++ b/packages/components/menu/CHANGELOG.md @@ -1,5 +1,20 @@ # @nextui-org/menu +## 2.0.23 + +### Patch Changes + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2970](https://github.com/nextui-org/nextui/pull/2970) [`7df2c71ec`](https://github.com/nextui-org/nextui/commit/7df2c71ecc5f06d60807b6b3502d3a118080a0d5) Thanks [@wingkwong](https://github.com/wingkwong)! - Focus on the first item when pressing Space / Enter key on dropdown menu open (#2863) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`f24a97311`](https://github.com/nextui-org/nextui/commit/f24a97311ab4dd16dafb56d35fe7c6db81798129)]: + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/use-aria-menu@2.0.3 + - @nextui-org/divider@2.0.28 + ## 2.0.22 ### Patch Changes diff --git a/packages/components/menu/package.json b/packages/components/menu/package.json index 4d3dabcc83..3bf337b451 100644 --- a/packages/components/menu/package.json +++ b/packages/components/menu/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/menu", - "version": "2.0.22", + "version": "2.0.23", "description": "A menu displays a list of options and allows a user to select one or more of them.", "keywords": [ "menu" @@ -46,14 +46,14 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-aria-menu": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/menu": "^3.13.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/menu": "^3.6.1", - "@react-stately/tree": "^3.7.6", - "@react-types/menu": "^3.9.7", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/menu": "3.13.1", + "@react-aria/utils": "3.23.2", + "@react-stately/menu": "3.6.1", + "@react-stately/tree": "3.7.6", + "@react-types/menu": "3.9.7", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -65,4 +65,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/menu/src/menu.tsx b/packages/components/menu/src/menu.tsx index 2e7bab14f2..318b86baa1 100644 --- a/packages/components/menu/src/menu.tsx +++ b/packages/components/menu/src/menu.tsx @@ -71,9 +71,9 @@ function Menu(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type MenuProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(Menu) as (props: MenuProps) => ReactElement; +export default forwardRef(Menu) as (props: MenuProps) => ReactElement; Menu.displayName = "NextUI.Menu"; diff --git a/packages/components/menu/src/use-menu-item.ts b/packages/components/menu/src/use-menu-item.ts index 922ca2daad..360b101e27 100644 --- a/packages/components/menu/src/use-menu-item.ts +++ b/packages/components/menu/src/use-menu-item.ts @@ -3,7 +3,12 @@ import type {Node} from "@react-types/shared"; import {useMemo, useRef, useCallback} from "react"; import {menuItem} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useFocusRing} from "@react-aria/focus"; import {TreeState} from "@react-stately/tree"; import {clsx, dataAttr, objectToDeps, removeEvents} from "@nextui-org/shared-utils"; @@ -21,6 +26,8 @@ export type UseMenuItemProps = Props & Omit, keyof Props>; export function useMenuItem(originalProps: UseMenuItemProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, menuItem.variantKeys); const { @@ -50,7 +57,8 @@ export function useMenuItem(originalProps: UseMenuItemProps ...otherProps } = props; - const disableAnimation = originalProps.disableAnimation; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const domRef = useRef(null); diff --git a/packages/components/menu/src/use-menu.ts b/packages/components/menu/src/use-menu.ts index fa1dd7e847..82d8d7afeb 100644 --- a/packages/components/menu/src/use-menu.ts +++ b/packages/components/menu/src/use-menu.ts @@ -1,5 +1,4 @@ -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; - +import {useProviderContext, type HTMLNextUIProps, type PropGetter} from "@nextui-org/system"; import {AriaMenuProps} from "@react-types/menu"; import {AriaMenuOptions} from "@react-aria/menu"; import {useAriaMenu} from "@nextui-org/use-aria-menu"; @@ -90,13 +89,15 @@ export type UseMenuProps = Props & MenuVariantProps; export function useMenu(props: UseMenuProps) { + const globalContext = useProviderContext(); + const { as, ref, variant, color, children, - disableAnimation, + disableAnimation = globalContext?.disableAnimation ?? false, onAction, closeOnSelect, itemClasses, @@ -118,11 +119,11 @@ export function useMenu(props: UseMenuProps) { const domRef = useDOMRef(ref); const shouldFilterDOMProps = typeof Component === "string"; - const innerState = useTreeState({...otherProps, children}); + const innerState = useTreeState({...otherProps, ...userMenuProps, children}); const state = propState || innerState; - const {menuProps} = useAriaMenu(otherProps, state, domRef); + const {menuProps} = useAriaMenu({...otherProps, ...userMenuProps}, state, domRef); const slots = useMemo(() => menu({className}), [className]); const baseStyles = clsx(classNames?.base, className); @@ -143,9 +144,7 @@ export function useMenu(props: UseMenuProps) { return { "data-slot": "list", className: slots.list({class: classNames?.list}), - ...userMenuProps, ...menuProps, - ...props, }; }; diff --git a/packages/components/modal/CHANGELOG.md b/packages/components/modal/CHANGELOG.md index b1497bb6e9..3d764923e1 100644 --- a/packages/components/modal/CHANGELOG.md +++ b/packages/components/modal/CHANGELOG.md @@ -1,5 +1,16 @@ # @nextui-org/modal +## 2.0.34 + +### Patch Changes + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/framer-utils@2.0.19 + ## 2.0.33 ### Patch Changes diff --git a/packages/components/modal/package.json b/packages/components/modal/package.json index 1564446ce8..1d257147be 100644 --- a/packages/components/modal/package.json +++ b/packages/components/modal/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/modal", - "version": "2.0.33", + "version": "2.0.34", "description": "Displays a dialog with a custom content that requires attention or provides additional information.", "keywords": [ "modal" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -48,13 +48,13 @@ "@nextui-org/react-utils": "workspace:*", "@nextui-org/shared-icons": "workspace:*", "@nextui-org/use-aria-modal-overlay": "workspace:*", - "@react-aria/dialog": "^3.5.12", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/overlays": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/overlays": "^3.6.5", - "@react-types/overlays": "^3.8.5" + "@react-aria/dialog": "3.5.12", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/overlays": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-stately/overlays": "3.6.5", + "@react-types/overlays": "3.8.5" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -70,4 +70,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/modal/src/modal.tsx b/packages/components/modal/src/modal.tsx index 323aa53089..1aa2ceb64b 100644 --- a/packages/components/modal/src/modal.tsx +++ b/packages/components/modal/src/modal.tsx @@ -17,7 +17,11 @@ const Modal = forwardRef<"div", ModalProps>((props, ref) => { const {children, ...otherProps} = props; const context = useModal({...otherProps, ref}); - const overlay = {children}; + const overlay = ( + + {children} + + ); return ( diff --git a/packages/components/modal/src/use-modal.ts b/packages/components/modal/src/use-modal.ts index 4638de68e8..66b6f7be63 100644 --- a/packages/components/modal/src/use-modal.ts +++ b/packages/components/modal/src/use-modal.ts @@ -5,7 +5,12 @@ import {AriaModalOverlayProps} from "@react-aria/overlays"; import {useAriaModalOverlay} from "@nextui-org/use-aria-modal-overlay"; import {useCallback, useId, useRef, useState, useMemo, ReactNode} from "react"; import {modal} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useAriaButton} from "@nextui-org/use-aria-button"; import {useFocusRing} from "@react-aria/focus"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -74,6 +79,8 @@ interface Props extends HTMLNextUIProps<"section"> { export type UseModalProps = Props & OverlayTriggerProps & AriaModalOverlayProps & ModalVariantProps; export function useModal(originalProps: UseModalProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, modal.variantKeys); const { @@ -81,7 +88,7 @@ export function useModal(originalProps: UseModalProps) { as, className, classNames, - disableAnimation = false, + isOpen, defaultOpen, onOpenChange, @@ -104,6 +111,9 @@ export function useModal(originalProps: UseModalProps) { const [headerMounted, setHeaderMounted] = useState(false); const [bodyMounted, setBodyMounted] = useState(false); + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; + const dialogId = useId(); const headerId = useId(); const bodyId = useId(); @@ -139,8 +149,9 @@ export function useModal(originalProps: UseModalProps) { () => modal({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableAnimation], ); const getDialogProps: PropGetter = (props = {}, ref = null) => ({ diff --git a/packages/components/modal/stories/modal.stories.tsx b/packages/components/modal/stories/modal.stories.tsx index 0a824fea8b..eb4c8fb2bb 100644 --- a/packages/components/modal/stories/modal.stories.tsx +++ b/packages/components/modal/stories/modal.stories.tsx @@ -74,7 +74,6 @@ export default { const defaultProps = { ...modal.defaultVariants, - disableAnimation: false, isDismissable: true, isKeyboardDismissDisabled: false, }; diff --git a/packages/components/navbar/CHANGELOG.md b/packages/components/navbar/CHANGELOG.md index ef49d1dc4b..b9b217ce7c 100644 --- a/packages/components/navbar/CHANGELOG.md +++ b/packages/components/navbar/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/navbar +## 2.0.31 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`fa26ce02f`](https://github.com/nextui-org/nextui/commit/fa26ce02fd84b25fc29925c93462d15de542b847), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/use-scroll-position@2.0.6 + - @nextui-org/framer-utils@2.0.19 + ## 2.0.30 ### Patch Changes diff --git a/packages/components/navbar/package.json b/packages/components/navbar/package.json index d81b493c2f..e700f49a13 100644 --- a/packages/components/navbar/package.json +++ b/packages/components/navbar/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/navbar", - "version": "2.0.30", + "version": "2.0.31", "description": "A responsive navigation header positioned on top side of your page that includes support for branding, links, navigation, collapse and more.", "keywords": [ "navbar" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -46,12 +46,12 @@ "@nextui-org/framer-utils": "workspace:*", "@nextui-org/use-aria-toggle-button": "workspace:*", "@nextui-org/use-scroll-position": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/overlays": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/toggle": "^3.7.2", - "@react-stately/utils": "^3.9.1", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/overlays": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-stately/toggle": "3.7.2", + "@react-stately/utils": "3.9.1", "react-remove-scroll": "^2.5.6" }, "devDependencies": { @@ -69,4 +69,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/navbar/src/use-navbar.ts b/packages/components/navbar/src/use-navbar.ts index 07e071fc5b..796a25ea11 100644 --- a/packages/components/navbar/src/use-navbar.ts +++ b/packages/components/navbar/src/use-navbar.ts @@ -1,6 +1,11 @@ import type {NavbarVariantProps, SlotsToClasses, NavbarSlots} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {navbar} from "@nextui-org/theme"; import {useDOMRef} from "@nextui-org/react-utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -86,6 +91,8 @@ interface Props extends HTMLNextUIProps<"nav"> { export type UseNavbarProps = Props & NavbarVariantProps; export function useNavbar(originalProps: UseNavbarProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, navbar.variantKeys); const { @@ -106,6 +113,8 @@ export function useNavbar(originalProps: UseNavbarProps) { } = props; const Component = as || "nav"; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const domRef = useDOMRef(ref); @@ -159,9 +168,10 @@ export function useNavbar(originalProps: UseNavbarProps) { () => navbar({ ...variantProps, + disableAnimation, hideOnScroll: shouldHideOnScroll, }), - [objectToDeps(variantProps), shouldHideOnScroll], + [objectToDeps(variantProps), disableAnimation, shouldHideOnScroll], ); const baseStyles = clsx(classNames?.base, className); @@ -206,7 +216,7 @@ export function useNavbar(originalProps: UseNavbarProps) { domRef, height, isHidden, - disableAnimation: originalProps.disableAnimation ?? false, + disableAnimation, shouldHideOnScroll, isMenuOpen, classNames, diff --git a/packages/components/pagination/CHANGELOG.md b/packages/components/pagination/CHANGELOG.md index 5cfc3bb1f0..7db790be2f 100644 --- a/packages/components/pagination/CHANGELOG.md +++ b/packages/components/pagination/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/pagination +## 2.0.31 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.30 ### Patch Changes diff --git a/packages/components/pagination/package.json b/packages/components/pagination/package.json index 20de2869d0..1560fe7697 100644 --- a/packages/components/pagination/package.json +++ b/packages/components/pagination/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/pagination", - "version": "2.0.30", + "version": "2.0.31", "description": "The Pagination component allows you to display active page and navigate between multiple pages.", "keywords": [ "pagination" @@ -44,10 +44,10 @@ "@nextui-org/react-utils": "workspace:*", "@nextui-org/shared-icons": "workspace:*", "@nextui-org/use-pagination": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/i18n": "^3.10.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", + "@react-aria/focus": "3.16.2", + "@react-aria/i18n": "3.10.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", "scroll-into-view-if-needed": "3.0.10" }, "devDependencies": { @@ -58,4 +58,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/pagination/src/use-pagination.ts b/packages/components/pagination/src/use-pagination.ts index 52b64cccfc..b59df5852a 100644 --- a/packages/components/pagination/src/use-pagination.ts +++ b/packages/components/pagination/src/use-pagination.ts @@ -10,7 +10,7 @@ import { PaginationItemType, } from "@nextui-org/use-pagination"; import {useEffect, useRef, useMemo} from "react"; -import {mapPropsVariants} from "@nextui-org/system"; +import {mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {usePagination as useBasePagination} from "@nextui-org/use-pagination"; import scrollIntoView from "scroll-into-view-if-needed"; import {pagination} from "@nextui-org/theme"; @@ -163,6 +163,8 @@ export type UsePaginationProps = Props & UseBasePaginationProps & PaginationVari export const CURSOR_TRANSITION_TIMEOUT = 300; // in ms export function usePagination(originalProps: UsePaginationProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, pagination.variantKeys); const { @@ -196,6 +198,10 @@ export function usePagination(originalProps: UsePaginationProps) { const isRTL = direction === "rtl"; + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; + const disableCursorAnimation = originalProps?.disableCursorAnimation ?? disableAnimation ?? false; + function getItemsRefMap() { if (!itemsRef.current) { // Initialize the Map on first usage. @@ -275,14 +281,14 @@ export function usePagination(originalProps: UsePaginationProps) { const activePageRef = useRef(activePage); useEffect(() => { - if (activePage && !originalProps.disableAnimation) { + if (activePage && !disableAnimation) { scrollTo(activePage, activePage === activePageRef.current); } activePageRef.current = activePage; }, [ activePage, - originalProps.disableAnimation, - originalProps.disableCursorAnimation, + disableAnimation, + disableCursorAnimation, originalProps.dotsJump, originalProps.isCompact, originalProps.showControls, @@ -292,10 +298,9 @@ export function usePagination(originalProps: UsePaginationProps) { () => pagination({ ...variantProps, - disableCursorAnimation: - originalProps.disableCursorAnimation || originalProps.disableAnimation, + disableCursorAnimation: disableCursorAnimation || disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableCursorAnimation, disableAnimation], ); const baseStyles = clsx(classNames?.base, className); @@ -400,8 +405,8 @@ export function usePagination(originalProps: UsePaginationProps) { range, activePage, getItemRef, - disableCursorAnimation: originalProps.disableCursorAnimation, - disableAnimation: originalProps.disableAnimation, + disableAnimation, + disableCursorAnimation, setPage, onPrevious, onNext, 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/popover/CHANGELOG.md b/packages/components/popover/CHANGELOG.md index 9c3f272354..d1d602416b 100644 --- a/packages/components/popover/CHANGELOG.md +++ b/packages/components/popover/CHANGELOG.md @@ -1,5 +1,18 @@ # @nextui-org/popover +## 2.1.22 + +### Patch Changes + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/button@2.0.32 + - @nextui-org/framer-utils@2.0.19 + ## 2.1.21 ### Patch Changes diff --git a/packages/components/popover/__tests__/popover.test.tsx b/packages/components/popover/__tests__/popover.test.tsx index 5a09b2c8f6..2ab4e75da9 100644 --- a/packages/components/popover/__tests__/popover.test.tsx +++ b/packages/components/popover/__tests__/popover.test.tsx @@ -1,5 +1,6 @@ import * as React from "react"; import {render, fireEvent, act} from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import {Button} from "@nextui-org/button"; import {Popover, PopoverContent, PopoverTrigger} from "../src"; @@ -159,4 +160,57 @@ describe("Popover", () => { expect(onClose).toHaveBeenCalledTimes(1); }); + + it("should close listbox by clicking another popover", async () => { + const wrapper = render( + <> + + + + + +

This is the content of the popover.

+
+
+ + + + + +

This is the content of the popover.

+
+
+ , + ); + + const popover = wrapper.getByTestId("popover"); + + const popover2 = wrapper.getByTestId("popover2"); + + expect(popover).not.toBeNull(); + + expect(popover2).not.toBeNull(); + + // open the popover by clicking popover in the first popover + await act(async () => { + await userEvent.click(popover); + }); + + // assert that the first popover is open + expect(popover).toHaveAttribute("aria-expanded", "true"); + + // assert that the second popover is close + expect(popover2).toHaveAttribute("aria-expanded", "false"); + + // close the popover by clicking the second popover + await act(async () => { + await userEvent.click(popover2); + }); + + // assert that the first popover is closed + expect(popover).toHaveAttribute("aria-expanded", "false"); + + // assert that the second popover is open + expect(popover2).toHaveAttribute("aria-expanded", "true"); + }); }); diff --git a/packages/components/popover/package.json b/packages/components/popover/package.json index f01e935bdc..03ec401aac 100644 --- a/packages/components/popover/package.json +++ b/packages/components/popover/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/popover", - "version": "2.1.21", + "version": "2.1.22", "description": "A popover is an overlay element positioned relative to a trigger.", "keywords": [ "popover" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -48,14 +48,14 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/dialog": "^3.5.12", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/overlays": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/overlays": "^3.6.5", - "@react-types/button": "^3.9.2", - "@react-types/overlays": "^3.8.5", + "@react-aria/dialog": "3.5.12", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/overlays": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-stately/overlays": "3.6.5", + "@react-types/button": "3.9.2", + "@react-types/overlays": "3.8.5", "react-remove-scroll": "^2.5.6" }, "devDependencies": { @@ -69,4 +69,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/popover/src/popover.tsx b/packages/components/popover/src/popover.tsx index 3465d884b3..c44888f487 100644 --- a/packages/components/popover/src/popover.tsx +++ b/packages/components/popover/src/popover.tsx @@ -20,7 +20,11 @@ const Popover = forwardRef<"div", PopoverProps>((props, ref) => { const [trigger, content] = Children.toArray(children); - const overlay = {content}; + const overlay = ( + + {content} + + ); return ( diff --git a/packages/components/popover/src/use-aria-popover.ts b/packages/components/popover/src/use-aria-popover.ts index 40a3937ea9..d1b3ea1a01 100644 --- a/packages/components/popover/src/use-aria-popover.ts +++ b/packages/components/popover/src/use-aria-popover.ts @@ -10,6 +10,7 @@ import {OverlayPlacement, ariaHideOutside, toReactAriaPlacement} from "@nextui-o import {OverlayTriggerState} from "@react-stately/overlays"; import {mergeProps} from "@react-aria/utils"; import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; export interface Props { /** @@ -64,7 +65,7 @@ export function useReactAriaPopover( ...otherProps } = props; - const isNonModal = isNonModalProp || true; + const isNonModal = isNonModalProp ?? true; const {overlayProps, underlayProps} = useOverlay( { @@ -75,12 +76,7 @@ export function useReactAriaPopover( isKeyboardDismissDisabled, shouldCloseOnInteractOutside: shouldCloseOnInteractOutside ? shouldCloseOnInteractOutside - : (element) => { - // Don't close if the click is within the trigger or the popover itself - let trigger = triggerRef?.current; - - return !trigger || !trigger.contains(element); - }, + : (element: Element) => ariaShouldCloseOnInteractOutside(element, triggerRef, state), }, popoverRef, ); diff --git a/packages/components/popover/src/use-popover.ts b/packages/components/popover/src/use-popover.ts index 5e8fa86c52..b1ea300088 100644 --- a/packages/components/popover/src/use-popover.ts +++ b/packages/components/popover/src/use-popover.ts @@ -8,7 +8,12 @@ import {OverlayTriggerState, useOverlayTriggerState} from "@react-stately/overla import {useFocusRing} from "@react-aria/focus"; import {ariaHideOutside, useOverlayTrigger} from "@react-aria/overlays"; import {OverlayTriggerProps} from "@react-types/overlays"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {getArrowPlacement, getShouldUseAxisPlacement} from "@nextui-org/aria-utils"; import {popover} from "@nextui-org/theme"; import {mergeProps, mergeRefs} from "@react-aria/utils"; @@ -82,6 +87,8 @@ export type UsePopoverProps = Props & PopoverVariantProps; export function usePopover(originalProps: UsePopoverProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, popover.variantKeys); const { @@ -127,7 +134,8 @@ export function usePopover(originalProps: UsePopoverProps) { const dialogRef = useRef(null); const triggerRef = triggerRefProp || domTriggerRef; - const disableAnimation = originalProps.disableAnimation ?? false; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const innerState = useOverlayTriggerState({ isOpen: isOpenProp, diff --git a/packages/components/popover/stories/popover.stories.tsx b/packages/components/popover/stories/popover.stories.tsx index 52ef8dcc17..38e7dab459 100644 --- a/packages/components/popover/stories/popover.stories.tsx +++ b/packages/components/popover/stories/popover.stories.tsx @@ -106,7 +106,6 @@ const defaultProps = { placement: "top", offset: 7, defaultOpen: false, - disableAnimation: false, }; const content = ( @@ -122,7 +121,7 @@ const Template = (args: PopoverProps) => { return ( - + {content} @@ -133,7 +132,7 @@ const WithTitlePropsTemplate = (args: PopoverProps) => { return ( - + {(titleProps) => ( diff --git a/packages/components/progress/CHANGELOG.md b/packages/components/progress/CHANGELOG.md index 5930bbe043..d23f7da31a 100644 --- a/packages/components/progress/CHANGELOG.md +++ b/packages/components/progress/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/progress +## 2.0.29 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.28 ### Patch Changes diff --git a/packages/components/progress/package.json b/packages/components/progress/package.json index 6d9b363ac3..5c23ea6ffe 100644 --- a/packages/components/progress/package.json +++ b/packages/components/progress/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/progress", - "version": "2.0.28", + "version": "2.0.29", "description": "Progress bars show either determinate or indeterminate progress of an operation over time.", "keywords": [ "progress" @@ -43,10 +43,10 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/use-is-mounted": "workspace:*", - "@react-aria/i18n": "^3.10.2", - "@react-aria/progress": "^3.4.11", - "@react-aria/utils": "^3.23.2", - "@react-types/progress": "^3.5.2" + "@react-aria/i18n": "3.10.2", + "@react-aria/progress": "3.4.11", + "@react-aria/utils": "3.23.2", + "@react-types/progress": "3.5.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -58,4 +58,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/progress/src/use-circular-progress.ts b/packages/components/progress/src/use-circular-progress.ts index 3e8160463c..6f803f4fe9 100644 --- a/packages/components/progress/src/use-circular-progress.ts +++ b/packages/components/progress/src/use-circular-progress.ts @@ -6,7 +6,7 @@ import type { import type {PropGetter} from "@nextui-org/system"; import type {AriaProgressBarProps} from "@react-types/progress"; -import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system"; +import {HTMLNextUIProps, mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {circularProgress} from "@nextui-org/theme"; import {useDOMRef} from "@nextui-org/react-utils"; import {clampPercentage, clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -53,6 +53,7 @@ export interface Props extends HTMLNextUIProps<"div"> { export type UseCircularProgressProps = Props & AriaProgressBarProps & CircularProgressVariantProps; export function useCircularProgress(originalProps: UseCircularProgressProps) { + const globalContext = useProviderContext(); const [props, variantProps] = mapPropsVariants(originalProps, circularProgress.variantKeys); const { @@ -86,6 +87,8 @@ export function useCircularProgress(originalProps: UseCircularProgressProps) { // default isIndeterminate to true const isIndeterminate = (originalProps.isIndeterminate ?? true) && value === undefined; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const {progressBarProps, labelProps} = useAriaProgress({ id, @@ -104,12 +107,13 @@ export function useCircularProgress(originalProps: UseCircularProgressProps) { () => circularProgress({ ...variantProps, + disableAnimation, isIndeterminate, }), - [objectToDeps(variantProps), isIndeterminate], + [objectToDeps(variantProps), disableAnimation, isIndeterminate], ); - const selfMounted = originalProps.disableAnimation ? true : isMounted; + const selfMounted = disableAnimation ? true : isMounted; const center = 16; const strokeWidth = strokeWidthProp || (originalProps.size === "sm" ? 2 : 3); diff --git a/packages/components/progress/src/use-progress.ts b/packages/components/progress/src/use-progress.ts index fd91cf4a48..dfe1dcc95c 100644 --- a/packages/components/progress/src/use-progress.ts +++ b/packages/components/progress/src/use-progress.ts @@ -2,7 +2,7 @@ import type {ProgressVariantProps, SlotsToClasses, ProgressSlots} from "@nextui- import type {PropGetter} from "@nextui-org/system"; import type {AriaProgressBarProps} from "@react-types/progress"; -import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system"; +import {HTMLNextUIProps, mapPropsVariants, useProviderContext} from "@nextui-org/system"; import {progress} from "@nextui-org/theme"; import {useDOMRef} from "@nextui-org/react-utils"; import {clampPercentage, clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; @@ -44,6 +44,7 @@ interface Props extends HTMLNextUIProps<"div"> { export type UseProgressProps = Props & AriaProgressBarProps & ProgressVariantProps; export function useProgress(originalProps: UseProgressProps) { + const globalContext = useProviderContext(); const [props, variantProps] = mapPropsVariants(originalProps, progress.variantKeys); const { @@ -73,8 +74,12 @@ export function useProgress(originalProps: UseProgressProps) { rerender: true, delay: 100, }); + const isIndeterminate = originalProps.isIndeterminate; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; + const {progressBarProps, labelProps} = useAriaProgress({ id, label, @@ -92,11 +97,12 @@ export function useProgress(originalProps: UseProgressProps) { () => progress({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableAnimation], ); - const selfMounted = originalProps.disableAnimation ? true : isMounted; + const selfMounted = disableAnimation ? true : isMounted; // Calculate the width of the progress bar as a percentage const percentage = useMemo( diff --git a/packages/components/radio/CHANGELOG.md b/packages/components/radio/CHANGELOG.md index a2d9f3e2a4..7242cdbc0d 100644 --- a/packages/components/radio/CHANGELOG.md +++ b/packages/components/radio/CHANGELOG.md @@ -1,5 +1,17 @@ # @nextui-org/radio +## 2.1.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3013](https://github.com/nextui-org/nextui/pull/3013) [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85) Thanks [@kosmotema](https://github.com/kosmotema)! - make the VisuallyHidden `elementType` as span when the default parent element accepts only phrasing elements + ## 2.0.28 ### Patch Changes diff --git a/packages/components/radio/__tests__/radio.test.tsx b/packages/components/radio/__tests__/radio.test.tsx index d88c87a5ab..b89b2b7963 100644 --- a/packages/components/radio/__tests__/radio.test.tsx +++ b/packages/components/radio/__tests__/radio.test.tsx @@ -146,7 +146,7 @@ describe("Radio", () => { it('should work correctly with "isRequired" prop', () => { const {getByRole, getAllByRole} = render( - + Option 1 Option 2 , @@ -204,11 +204,11 @@ describe("validation", () => { beforeAll(() => { user = userEvent.setup(); }); - describe("validationBehavior=native (default)", () => { + describe("validationBehavior=native", () => { it("supports isRequired", async () => { const {getAllByRole, getByRole, getByTestId} = render(
- + Dogs Cats Dragons @@ -246,4 +246,39 @@ describe("validation", () => { expect(group).not.toHaveAttribute("aria-describedby"); }); }); + + describe("validationBehavior=aria", () => { + it("supports validate function", async () => { + const {getAllByRole, getByRole} = render( + (v === "dragons" ? "Too scary" : null)} + validationBehavior="aria" + > + Dogs + Cats + Dragons + , + ); + + const group = getByRole("radiogroup"); + + expect(group).toHaveAttribute("aria-describedby"); + expect(group).toHaveAttribute("aria-invalid", "true"); + expect( + document.getElementById(group.getAttribute("aria-describedby") as string), + ).toHaveTextContent("Too scary"); + + const radios = getAllByRole("radio") as HTMLInputElement[]; + + for (let input of radios) { + expect(input.validity.valid).toBe(true); + } + + await user.click(radios[0]); + expect(group).not.toHaveAttribute("aria-describedby"); + expect(group).not.toHaveAttribute("aria-invalid"); + }); + }); }); diff --git a/packages/components/radio/package.json b/packages/components/radio/package.json index 03adde6eb1..32e834f0f6 100644 --- a/packages/components/radio/package.json +++ b/packages/components/radio/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/radio", - "version": "2.0.28", + "version": "2.1.0", "description": "Radios allow users to select a single option from a list of mutually exclusive options.", "keywords": [ "radio" @@ -42,14 +42,14 @@ "dependencies": { "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/radio": "^3.10.2", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/radio": "^3.10.2", - "@react-types/radio": "^3.7.1", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/radio": "3.10.2", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/radio": "3.10.2", + "@react-types/radio": "3.7.1", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -60,4 +60,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/radio/src/radio.tsx b/packages/components/radio/src/radio.tsx index 42b108f959..a72bc656dd 100644 --- a/packages/components/radio/src/radio.tsx +++ b/packages/components/radio/src/radio.tsx @@ -22,7 +22,7 @@ const Radio = forwardRef<"input", RadioProps>((props, ref) => { return ( - + diff --git a/packages/components/radio/src/use-radio-group.ts b/packages/components/radio/src/use-radio-group.ts index f096bbb846..da6ce27a2b 100644 --- a/packages/components/radio/src/use-radio-group.ts +++ b/packages/components/radio/src/use-radio-group.ts @@ -7,7 +7,7 @@ import {radioGroup} from "@nextui-org/theme"; import {useCallback, useMemo} from "react"; import {RadioGroupState, useRadioGroupState} from "@react-stately/radio"; import {useRadioGroup as useReactAriaRadioGroup} from "@react-aria/radio"; -import {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; import {filterDOMProps, useDOMRef} from "@nextui-org/react-utils"; import {clsx, safeAriaLabel} from "@nextui-org/shared-utils"; import {mergeProps} from "@react-aria/utils"; @@ -47,7 +47,7 @@ interface Props extends Omit, "onChange"> { } export type UseRadioGroupProps = Omit & - Omit & + Omit & Partial>; export type ContextType = { @@ -62,6 +62,8 @@ export type ContextType = { }; export function useRadioGroup(props: UseRadioGroupProps) { + const globalContext = useProviderContext(); + const { as, ref, @@ -72,10 +74,11 @@ export function useRadioGroup(props: UseRadioGroupProps) { name, isInvalid: isInvalidProp, validationState, + validationBehavior = globalContext?.validationBehavior ?? "aria", size = "md", color = "primary", isDisabled = false, - disableAnimation = false, + disableAnimation = globalContext?.disableAnimation ?? false, orientation = "vertical", isRequired = false, isReadOnly, @@ -102,7 +105,7 @@ export function useRadioGroup(props: UseRadioGroupProps) { isReadOnly, isInvalid: validationState === "invalid" || isInvalidProp, orientation, - validationBehavior: "native", + validationBehavior, onChange: onValueChange, }; }, [ @@ -114,6 +117,7 @@ export function useRadioGroup(props: UseRadioGroupProps) { isReadOnly, isInvalidProp, validationState, + validationBehavior, orientation, onValueChange, ]); @@ -130,7 +134,7 @@ export function useRadioGroup(props: UseRadioGroupProps) { validationDetails, } = useReactAriaRadioGroup(otherPropsWithOrientation, groupState); - const isInvalid = otherPropsWithOrientation.isInvalid || isAriaInvalid; + const isInvalid = otherPropsWithOrientation.isInvalid || isAriaInvalid || groupState.isInvalid; const context: ContextType = useMemo( () => ({ @@ -152,11 +156,11 @@ export function useRadioGroup(props: UseRadioGroupProps) { onChange, disableAnimation, groupState.name, - groupState?.isDisabled, - groupState?.isReadOnly, - groupState?.isRequired, - groupState?.selectedValue, - groupState?.lastFocusedValue, + groupState.isDisabled, + groupState.isReadOnly, + groupState.isRequired, + groupState.selectedValue, + groupState.lastFocusedValue, ], ); diff --git a/packages/components/radio/src/use-radio.ts b/packages/components/radio/src/use-radio.ts index abe8e3dfb6..85a6117589 100644 --- a/packages/components/radio/src/use-radio.ts +++ b/packages/components/radio/src/use-radio.ts @@ -7,7 +7,7 @@ import {useFocusRing} from "@react-aria/focus"; import {useHover, usePress} from "@react-aria/interactions"; import {radio} from "@nextui-org/theme"; import {useRadio as useReactAriaRadio} from "@react-aria/radio"; -import {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; +import {HTMLNextUIProps, PropGetter, useProviderContext} from "@nextui-org/system"; import {__DEV__, warn, clsx, dataAttr} from "@nextui-org/shared-utils"; import {useDOMRef} from "@nextui-org/react-utils"; import {chain, mergeProps} from "@react-aria/utils"; @@ -51,6 +51,7 @@ export type UseRadioProps = Omit & RadioVariantProps; export function useRadio(props: UseRadioProps) { + const globalContext = useProviderContext(); const groupContext = useRadioGroupContext(); const { @@ -64,7 +65,7 @@ export function useRadio(props: UseRadioProps) { size = groupContext?.size ?? "md", color = groupContext?.color ?? "primary", isDisabled: isDisabledProp = groupContext?.isDisabled ?? false, - disableAnimation = groupContext?.disableAnimation ?? false, + disableAnimation = groupContext?.disableAnimation ?? globalContext?.disableAnimation ?? false, onChange = groupContext?.onChange, autoFocus = false, className, diff --git a/packages/components/radio/stories/radio.stories.tsx b/packages/components/radio/stories/radio.stories.tsx index b2631f617c..a279d61fbd 100644 --- a/packages/components/radio/stories/radio.stories.tsx +++ b/packages/components/radio/stories/radio.stories.tsx @@ -37,6 +37,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, } as Meta; diff --git a/packages/components/ripple/CHANGELOG.md b/packages/components/ripple/CHANGELOG.md index a2a15f1c93..adbfd7746f 100644 --- a/packages/components/ripple/CHANGELOG.md +++ b/packages/components/ripple/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/ripple +## 2.0.29 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.28 ### Patch Changes diff --git a/packages/components/ripple/package.json b/packages/components/ripple/package.json index 2bf322a25b..5f8a3cd581 100644 --- a/packages/components/ripple/package.json +++ b/packages/components/ripple/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/ripple", - "version": "2.0.28", + "version": "2.0.29", "description": "A simple implementation to display a ripple animation when the source component is clicked", "keywords": [ "ripple" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -53,4 +53,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/ripple/src/ripple.tsx b/packages/components/ripple/src/ripple.tsx index 28467789dd..92caacc451 100644 --- a/packages/components/ripple/src/ripple.tsx +++ b/packages/components/ripple/src/ripple.tsx @@ -1,9 +1,10 @@ -import {FC} from "react"; -import {AnimatePresence, HTMLMotionProps, m, LazyMotion, domAnimation} from "framer-motion"; -import {HTMLNextUIProps} from "@nextui-org/system"; -import {clamp} from "@nextui-org/shared-utils"; +import type {RippleType} from "./use-ripple"; +import type {FC} from "react"; +import type {HTMLMotionProps} from "framer-motion"; +import type {HTMLNextUIProps} from "@nextui-org/system"; -import {RippleType} from "./use-ripple"; +import {AnimatePresence, m, LazyMotion, domAnimation} from "framer-motion"; +import {clamp} from "@nextui-org/shared-utils"; export interface RippleProps extends HTMLNextUIProps<"span"> { ripples: RippleType[]; @@ -22,38 +23,36 @@ const Ripple: FC = (props) => { const duration = clamp(0.01 * ripple.size, 0.2, ripple.size > 100 ? 0.75 : 0.5); return ( - - <> - - { - onClear(ripple.key); - }} - {...motionProps} - /> - - - + + + { + onClear(ripple.key); + }} + {...motionProps} + /> + + ); })} diff --git a/packages/components/scroll-shadow/package.json b/packages/components/scroll-shadow/package.json index ba76cd874a..bf2113b370 100644 --- a/packages/components/scroll-shadow/package.json +++ b/packages/components/scroll-shadow/package.json @@ -53,4 +53,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/select/CHANGELOG.md b/packages/components/select/CHANGELOG.md index 691343f755..7069293938 100644 --- a/packages/components/select/CHANGELOG.md +++ b/packages/components/select/CHANGELOG.md @@ -1,5 +1,33 @@ # @nextui-org/select +## 2.2.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2937](https://github.com/nextui-org/nextui/pull/2937) [`a2133009f`](https://github.com/nextui-org/nextui/commit/a2133009f73aa728a0e5deeb9b742aa1defd4de2) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix onSelectionChange can handle number (#2926) + +- [#3081](https://github.com/nextui-org/nextui/pull/3081) [`31bfaebe2`](https://github.com/nextui-org/nextui/commit/31bfaebe2c53b0a3b9d18c65db4089e6044fe9dc) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix: display placeholder text when unselected for controlled (#3062) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3013](https://github.com/nextui-org/nextui/pull/3013) [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85) Thanks [@kosmotema](https://github.com/kosmotema)! - make the VisuallyHidden `elementType` as span when the default parent element accepts only phrasing elements + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531), [`a2133009f`](https://github.com/nextui-org/nextui/commit/a2133009f73aa728a0e5deeb9b742aa1defd4de2), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a)]: + - @nextui-org/popover@2.1.22 + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/listbox@2.1.20 + - @nextui-org/use-aria-multiselect@2.2.0 + - @nextui-org/scroll-shadow@2.1.16 + - @nextui-org/spinner@2.0.29 + ## 2.1.27 ### Patch Changes diff --git a/packages/components/select/__tests__/select.test.tsx b/packages/components/select/__tests__/select.test.tsx index 654a58e484..75431dccbb 100644 --- a/packages/components/select/__tests__/select.test.tsx +++ b/packages/components/select/__tests__/select.test.tsx @@ -1,29 +1,30 @@ import * as React from "react"; -import {act, render} from "@testing-library/react"; +import {render, renderHook, act} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import {useForm} from "react-hook-form"; import {Select, SelectItem, SelectSection, type SelectProps} from "../src"; import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter} from "../../modal/src"; type Item = { label: string; - value: string; + id: string; }; const itemsData: Item[] = [ - {label: "Cat", value: "cat"}, - {label: "Dog", value: "dog"}, - {label: "Elephant", value: "elephant"}, - {label: "Lion", value: "lion"}, - {label: "Tiger", value: "tiger"}, - {label: "Giraffe", value: "giraffe"}, - {label: "Dolphin", value: "dolphin"}, - {label: "Penguin", value: "penguin"}, - {label: "Zebra", value: "zebra"}, - {label: "Shark", value: "shark"}, - {label: "Whale", value: "whale"}, - {label: "Otter", value: "otter"}, - {label: "Crocodile", value: "crocodile"}, + {label: "Cat", id: "cat"}, + {label: "Dog", id: "dog"}, + {label: "Elephant", id: "elephant"}, + {label: "Lion", id: "lion"}, + {label: "Tiger", id: "tiger"}, + {label: "Giraffe", id: "giraffe"}, + {label: "Dolphin", id: "dolphin"}, + {label: "Penguin", id: "penguin"}, + {label: "Zebra", id: "zebra"}, + {label: "Shark", id: "shark"}, + {label: "Whale", id: "whale"}, + {label: "Otter", id: "otter"}, + {label: "Crocodile", id: "crocodile"}, ]; const itemsSectionData = [ @@ -48,18 +49,18 @@ const itemsSectionData = [ ]; describe("Select", () => { + let user; + + beforeEach(() => { + user = userEvent.setup(); + }); + it("should render correctly", () => { const wrapper = render( , ); @@ -71,15 +72,9 @@ describe("Select", () => { render( , ); expect(ref.current).not.toBeNull(); @@ -88,7 +83,7 @@ describe("Select", () => { it("should render correctly (dynamic)", () => { const wrapper = render( , ); @@ -99,17 +94,11 @@ describe("Select", () => { const wrapper = render( , ); @@ -146,18 +135,11 @@ describe("Select", () => { aria-label="Favorite Animal" label="Favorite Animal" selectionMode="single" - value="penguin" onSelectionChange={onSelectionChange} > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark , ); @@ -170,7 +152,7 @@ describe("Select", () => { expect(listboxItems.length).toBe(3); await act(async () => { - await userEvent.click(listboxItems[1]); + await user.click(listboxItems[1]); expect(onSelectionChange).toBeCalledTimes(1); }); @@ -188,15 +170,9 @@ describe("Select", () => { selectionMode="multiple" onSelectionChange={onSelectionChange} > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark , ); @@ -209,8 +185,8 @@ describe("Select", () => { expect(listboxItems.length).toBe(3); await act(async () => { - await userEvent.click(listboxItems[1]); - await userEvent.click(listboxItems[2]); + await user.click(listboxItems[1]); + await user.click(listboxItems[2]); expect(onSelectionChange).toBeCalledTimes(2); }); @@ -231,15 +207,9 @@ describe("Select", () => { placeholder={placeholder} renderValue={renderValue} > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark ); }; @@ -255,13 +225,13 @@ describe("Select", () => { const select = wrapper.getByTestId("render-selected-item-test"); await act(async () => { - await userEvent.click(select); + await user.click(select); }); const listboxItems = wrapper.getAllByRole("option"); await act(async () => { - await userEvent.click(listboxItems[0]); + await user.click(listboxItems[0]); }); expect(select).toHaveTextContent("Penguin"); @@ -285,15 +255,9 @@ describe("Select", () => { data-testid="close-when-clicking-outside-test" label="Favorite Animal" > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark , ); @@ -301,7 +265,7 @@ describe("Select", () => { // open the select dropdown await act(async () => { - await userEvent.click(select); + await user.click(select); }); // assert that the select is open @@ -309,7 +273,7 @@ describe("Select", () => { // click outside the select component await act(async () => { - await userEvent.click(document.body); + await user.click(document.body); }); // assert that the select is closed @@ -327,15 +291,9 @@ describe("Select", () => { data-testid="close-when-clicking-outside-test" label="Favorite Animal" > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark Modal footer @@ -347,7 +305,7 @@ describe("Select", () => { // open the select dropdown await act(async () => { - await userEvent.click(select); + await user.click(select); }); // assert that the select is open @@ -355,7 +313,7 @@ describe("Select", () => { // click outside the select component await act(async () => { - await userEvent.click(document.body); + await user.click(document.body); }); // assert that the select is closed @@ -372,28 +330,312 @@ describe("Select", () => { data-testid="test-select" label="Favorite Animal" selectionMode="single" - value="penguin" onSelectionChange={onSelectionChange} > - - Penguin - - - Zebra - - - Shark - + Penguin + Zebra + Shark , ); const select = wrapper.getByTestId("test-select"); await act(async () => { - await userEvent.click(document.body); - await userEvent.tab(); - await userEvent.type(select, "z", {skipClick: true}); + await user.click(document.body); + await user.tab(); + await user.type(select, "z", {skipClick: true}); expect(onSelectionChange).toBeCalledTimes(0); }); }); + + it("onSelectionChange should be called with a Set of item ids upon selection", async () => { + const itemsWithId = [ + {id: 1, value: "penguin"}, + {id: 2, value: "zebra"}, + {id: 3, value: "shark"}, + ]; + + const onSelectionChangeId = jest.fn(); + const wrapperWithId = render( + , + ); + + const listbox = wrapperWithId.getByRole("listbox"); + + expect(listbox).toBeInTheDocument(); + + // Select item and check the correct ID is passed to the callback + await act(async () => { + await user.click(wrapperWithId.getByRole("option", {name: itemsWithId[0].value})); + }); + expect(onSelectionChangeId).toHaveBeenCalled(); + let selectionArg = onSelectionChangeId.mock.calls[0][0]; + + expect([...selectionArg]).toEqual([itemsWithId[0].id]); + + await act(async () => { + await user.click(wrapperWithId.getByRole("option", {name: itemsWithId[1].value})); + }); + expect(onSelectionChangeId).toHaveBeenCalledTimes(2); + selectionArg = onSelectionChangeId.mock.calls[1][0]; + expect([...selectionArg]).toEqual([itemsWithId[1].id]); + }); + + it("onSelectionChange should be called with a Set of item keys upon selection", async () => { + const itemsWithKey = [ + {key: 1, value: "penguin"}, + {key: 2, value: "zebra"}, + {key: 3, value: "shark"}, + ]; + + const onSelectionChangeKey = jest.fn(); + const wrapperWithKey = render( + , + ); + + const listbox = wrapperWithKey.getByRole("listbox"); + + expect(listbox).toBeInTheDocument(); + + // Select item and check the correct key is passed to the callback + await act(async () => { + await user.click(wrapperWithKey.getByRole("option", {name: itemsWithKey[0].value})); + }); + expect(onSelectionChangeKey).toHaveBeenCalled(); + let selectionArg = onSelectionChangeKey.mock.calls[0][0]; + + expect([...selectionArg]).toEqual([itemsWithKey[0].key]); + + await act(async () => { + await user.click(wrapperWithKey.getByRole("option", {name: itemsWithKey[1].value})); + }); + expect(onSelectionChangeKey).toHaveBeenCalledTimes(2); + selectionArg = onSelectionChangeKey.mock.calls[1][0]; + expect([...selectionArg]).toEqual([itemsWithKey[1].key]); + }); + + it("should display selected items as comma-separated string inside the select", async () => { + const wrapper = render( + , + ); + const select = wrapper.getByTestId("test-select"); + const displayedText = select?.textContent?.trim(); + + expect(displayedText).toBe("Penguin, Zebra"); + }); + + it("should close listbox by clicking another select", async () => { + const wrapper = render( + <> + + + , + ); + + const select = wrapper.getByTestId("select"); + + const select2 = wrapper.getByTestId("select2"); + + expect(select).not.toBeNull(); + + expect(select2).not.toBeNull(); + + // open the select listbox by clicking selector button in the first select + await act(async () => { + await userEvent.click(select); + }); + + // assert that the first select listbox is open + expect(select).toHaveAttribute("aria-expanded", "true"); + + // assert that the second select listbox is close + expect(select2).toHaveAttribute("aria-expanded", "false"); + + // close the select listbox by clicking the second select + await act(async () => { + await userEvent.click(select2); + }); + + // assert that the first select listbox is closed + expect(select).toHaveAttribute("aria-expanded", "false"); + + // assert that the second select listbox is open + expect(select2).toHaveAttribute("aria-expanded", "true"); + }); + + it("should display placeholder text when unselected", async () => { + const wrapper = render( + , + ); + + const select = wrapper.getByTestId("test-select"); + + expect(select).toHaveTextContent("Select an animal"); + }); + + it("should display placeholder text when unselected (controlled)", async () => { + const onSelectionChange = jest.fn(); + const wrapper = render( + , + ); + + const select = wrapper.getByTestId("test-select"); + + expect(select).toHaveTextContent("Select an animal"); + }); +}); + +describe("Select with React Hook Form", () => { + let select1: HTMLElement; + let select2: HTMLElement; + let select3: HTMLElement; + let submitButton: HTMLButtonElement; + let wrapper: any; + let onSubmit: () => void; + + beforeEach(() => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + withDefaultValue: "cat", + withoutDefaultValue: "", + requiredField: "", + }, + }), + ); + + const { + register, + formState: {errors}, + handleSubmit, + } = result.current; + + onSubmit = jest.fn(); + + wrapper = render( + + + + + + + + {errors.requiredField && This field is required} + + , + ); + + select1 = wrapper.getByTestId("select-1"); + select2 = wrapper.getByTestId("select-2"); + select3 = wrapper.getByTestId("select-3"); + submitButton = wrapper.getByTestId("submit-button"); + }); + + it("should work with defaultValues", () => { + expect(select1).toHaveTextContent("Cat"); + expect(select2).toHaveTextContent(""); + expect(select3).toHaveTextContent(""); + }); + + it("should not submit form when required field is empty", async () => { + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(0); + }); + + it("should submit form when required field is not empty", async () => { + const user = userEvent.setup(); + + await user.click(select3); + + expect(select3).toHaveAttribute("aria-expanded", "true"); + + let listboxItems = wrapper.getAllByRole("option"); + + await user.click(listboxItems[1]); + + expect(select3).toHaveTextContent("Dog"); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/components/select/package.json b/packages/components/select/package.json index f7ee446f55..1bf659ed20 100644 --- a/packages/components/select/package.json +++ b/packages/components/select/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/select", - "version": "2.1.27", + "version": "2.2.0", "description": "A select displays a collapsible list of options and allows a user to select one of them.", "keywords": [ "select" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -52,12 +52,12 @@ "@nextui-org/use-aria-button": "workspace:*", "@nextui-org/use-aria-multiselect": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/form": "^3.0.3", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/form": "3.0.3", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -69,11 +69,12 @@ "@nextui-org/stories-utils": "workspace:*", "@nextui-org/use-infinite-scroll": "workspace:*", "framer-motion": "^11.0.28", - "@react-aria/i18n": "^3.10.2", - "@react-stately/data": "^3.11.2", + "@react-aria/i18n": "3.10.2", + "@react-stately/data": "3.11.2", "clean-package": "2.2.0", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-hook-form": "^7.51.3" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/select/src/select.tsx b/packages/components/select/src/select.tsx index bda7b15277..83aa324c9c 100644 --- a/packages/components/select/src/select.tsx +++ b/packages/components/select/src/select.tsx @@ -75,7 +75,7 @@ function Select(props: Props, ref: ForwardedRef { - if (!state.selectedItems) return placeholder; + if (!state.selectedItems?.length) return placeholder; if (renderValue && typeof renderValue === "function") { const mappedItems = [...state.selectedItems].map((item) => ({ @@ -105,13 +105,7 @@ function Select(props: Props, ref: ForwardedRef state.isOpen ? ( - + @@ -129,10 +123,10 @@ function Select(props: Props, ref: ForwardedRef {startContent} - - {renderSelectedItem} - {state.selectedItems && ,} - + {renderSelectedItem} + {endContent && state.selectedItems && ( + , + )} {endContent}
{renderIndicator} @@ -144,9 +138,9 @@ function Select(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type SelectProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(Select) as (props: SelectProps) => ReactElement; +export default forwardRef(Select) as (props: SelectProps) => ReactElement; Select.displayName = "NextUI.Select"; diff --git a/packages/components/select/src/use-select.ts b/packages/components/select/src/use-select.ts index b81fe7ed13..35600017ba 100644 --- a/packages/components/select/src/use-select.ts +++ b/packages/components/select/src/use-select.ts @@ -1,7 +1,13 @@ import type {SelectSlots, SelectVariantProps, SlotsToClasses} from "@nextui-org/theme"; import type {HiddenSelectProps} from "./hidden-select"; -import {DOMAttributes, HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + DOMAttributes, + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {select} from "@nextui-org/theme"; import {ReactRef, useDOMRef, filterDOMProps} from "@nextui-org/react-utils"; import {useMemo, useCallback, useRef, Key, ReactNode, useEffect} from "react"; @@ -21,6 +27,7 @@ import { } from "@nextui-org/use-aria-multiselect"; import {SpinnerProps} from "@nextui-org/spinner"; import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; +import {ariaShouldCloseOnInteractOutside} from "@nextui-org/aria-utils"; import {CollectionChildren} from "@react-types/shared"; export type SelectedItemProps = { @@ -137,8 +144,12 @@ export type UseSelectProps = Omit, keyof MultiSelectProps> & SelectVariantProps; export function useSelect(originalProps: UseSelectProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, select.variantKeys); - const disableAnimation = originalProps.disableAnimation ?? false; + + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const { ref, @@ -220,8 +231,8 @@ export function useSelect(originalProps: UseSelectProps) { selectionMode, disallowEmptySelection, children: children as CollectionChildren, - isRequired: originalProps?.isRequired, - isDisabled: originalProps?.isDisabled, + isRequired: originalProps.isRequired, + isDisabled: originalProps.isDisabled, defaultOpen, onOpenChange: (open) => { onOpenChange?.(open); @@ -247,7 +258,7 @@ export function useSelect(originalProps: UseSelectProps) { state = { ...state, - ...(originalProps?.isDisabled && { + ...(originalProps.isDisabled && { disabledKeys: new Set([...state.collection.getKeys()]), }), }; @@ -272,7 +283,7 @@ export function useSelect(originalProps: UseSelectProps) { validationErrors, validationDetails, } = useMultiSelect( - {...props, disallowEmptySelection, isDisabled: originalProps?.isDisabled}, + {...props, disallowEmptySelection, isDisabled: originalProps.isDisabled}, state, triggerRef, ); @@ -282,7 +293,7 @@ export function useSelect(originalProps: UseSelectProps) { const {isPressed, buttonProps} = useAriaButton(triggerProps, triggerRef); const {focusProps, isFocused, isFocusVisible} = useFocusRing(); - const {isHovered, hoverProps} = useHover({isDisabled: originalProps?.isDisabled}); + const {isHovered, hoverProps} = useHover({isDisabled: originalProps.isDisabled}); const labelPlacement = useMemo(() => { if ((!originalProps.labelPlacement || originalProps.labelPlacement === "inside") && !label) { @@ -317,9 +328,10 @@ export function useSelect(originalProps: UseSelectProps) { ...variantProps, isInvalid, labelPlacement, + disableAnimation, className, }), - [objectToDeps(variantProps), isInvalid, labelPlacement, className], + [objectToDeps(variantProps), isInvalid, labelPlacement, disableAnimation, className], ); // scroll the listbox to the selected item @@ -489,6 +501,8 @@ export function useSelect(originalProps: UseSelectProps) { const getPopoverProps = useCallback( (props: DOMAttributes = {}) => { + const popoverProps = mergeProps(slotsProps.popoverProps, props); + return { state, triggerRef, @@ -501,12 +515,15 @@ export function useSelect(originalProps: UseSelectProps) { class: clsx(classNames?.popoverContent, props.className), }), }, - ...mergeProps(slotsProps.popoverProps, props), + ...popoverProps, offset: state.selectedItems && state.selectedItems.length > 0 ? // forces the popover to update its position when the selected items change state.selectedItems.length * 0.00000001 + (slotsProps.popoverProps?.offset || 0) : slotsProps.popoverProps?.offset, + shouldCloseOnInteractOutside: popoverProps?.shouldCloseOnInteractOutside + ? popoverProps.shouldCloseOnInteractOutside + : (element: Element) => ariaShouldCloseOnInteractOutside(element, triggerRef, state), } as PopoverProps; }, [ @@ -613,6 +630,7 @@ export function useSelect(originalProps: UseSelectProps) { isDisabled: originalProps?.isDisabled, isRequired: originalProps?.isRequired, name: originalProps?.name, + // TODO: Future enhancement to support "aria" validation behavior. validationBehavior: "native", }); diff --git a/packages/components/select/stories/select.stories.tsx b/packages/components/select/stories/select.stories.tsx index 26dd2710b8..a30224cf3e 100644 --- a/packages/components/select/stories/select.stories.tsx +++ b/packages/components/select/stories/select.stories.tsx @@ -2,6 +2,7 @@ import type {ValidationResult} from "@react-types/shared"; import React, {ChangeEvent} from "react"; +import {useForm} from "react-hook-form"; import {Meta} from "@storybook/react"; import {select, button} from "@nextui-org/theme"; import {PetBoldIcon, SelectorIcon} from "@nextui-org/shared-icons"; @@ -585,6 +586,47 @@ const AsyncLoadingTemplate = ({color, variant, ...args}: SelectProps) = ); }; +const WithReactHookFormTemplate = (args: SelectProps) => { + const { + register, + formState: {errors}, + handleSubmit, + } = useForm({ + defaultValues: { + withDefaultValue: "cat", + withoutDefaultValue: "", + requiredField: "", + }, + }); + + const onSubmit = (data: any) => { + // eslint-disable-next-line no-console + console.log(data); + alert("Submitted value: " + JSON.stringify(data)); + }; + + return ( +
+ + + + + + + {errors.requiredField && This field is required} + +
+ ); +}; + export const Default = { render: MirrorTemplate, @@ -631,23 +673,15 @@ export const DisabledOptions = { }, }; -export const WithDescription = { - render: MirrorTemplate, - - args: { - ...defaultProps, - description: "Select your favorite animal", - }, -}; - -export const WithoutLabel = { +export const IsInvalid = { render: Template, args: { ...defaultProps, - label: null, - "aria-label": "Select an animal", - placeholder: "Select an animal", + isInvalid: true, + variant: "bordered", + defaultSelectedKeys: ["dog"], + errorMessage: "Please select a valid animal", }, }; @@ -675,6 +709,26 @@ export const StartContent = { }, }; +export const WithDescription = { + render: MirrorTemplate, + + args: { + ...defaultProps, + description: "Select your favorite animal", + }, +}; + +export const WithoutLabel = { + render: Template, + + args: { + ...defaultProps, + label: null, + "aria-label": "Select an animal", + placeholder: "Select an animal", + }, +}; + export const WithoutScrollShadow = { render: Template, @@ -726,15 +780,62 @@ export const WithErrorMessageFunction = { }, }; -export const IsInvalid = { - render: Template, +export const WithChips = { + render: CustomItemsTemplate, args: { ...defaultProps, - isInvalid: true, variant: "bordered", - defaultSelectedKeys: ["dog"], - errorMessage: "Please select a valid animal", + selectionMode: "multiple", + isMultiline: true, + labelPlacement: "outside", + classNames: { + base: "max-w-xs", + trigger: "min-h-12 py-2", + }, + renderValue: (items: SelectedItems) => { + return ( +
+ {items.map((item) => ( + {item.data?.name} + ))} +
+ ); + }, + }, +}; + +export const WithSections = { + render: WithSectionsTemplate, + + args: { + ...defaultProps, + }, +}; + +export const WithCustomSectionsStyles = { + render: WithCustomSectionsStylesTemplate, + + args: { + ...defaultProps, + }, +}; + +export const WithAriaLabel = { + render: WithAriaLabelTemplate, + + args: { + ...defaultProps, + label: "Select an animal 🐹", + "aria-label": "Select an animal", + }, +}; + +export const WithReactHookForm = { + render: WithReactHookFormTemplate, + + args: { + ...defaultProps, }, }; @@ -808,57 +909,6 @@ export const CustomRenderValue = { }, }; -export const WithChips = { - render: CustomItemsTemplate, - - args: { - ...defaultProps, - variant: "bordered", - selectionMode: "multiple", - isMultiline: true, - labelPlacement: "outside", - classNames: { - base: "max-w-xs", - trigger: "min-h-12 py-2", - }, - renderValue: (items: SelectedItems) => { - return ( -
- {items.map((item) => ( - {item.data?.name} - ))} -
- ); - }, - }, -}; - -export const WithSections = { - render: WithSectionsTemplate, - - args: { - ...defaultProps, - }, -}; - -export const WithCustomSectionsStyles = { - render: WithCustomSectionsStylesTemplate, - - args: { - ...defaultProps, - }, -}; - -export const WithAriaLabel = { - render: WithAriaLabelTemplate, - - args: { - ...defaultProps, - label: "Select an animal 🐹", - "aria-label": "Select an animal", - }, -}; - export const CustomStyles = { render: CustomStylesTemplate, diff --git a/packages/components/skeleton/CHANGELOG.md b/packages/components/skeleton/CHANGELOG.md index fb251659f4..dd3bf4ae6a 100644 --- a/packages/components/skeleton/CHANGELOG.md +++ b/packages/components/skeleton/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/skeleton +## 2.0.28 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + ## 2.0.27 ### Patch Changes diff --git a/packages/components/skeleton/package.json b/packages/components/skeleton/package.json index 69e1ef90eb..4aea3e6a12 100644 --- a/packages/components/skeleton/package.json +++ b/packages/components/skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/skeleton", - "version": "2.0.27", + "version": "2.0.28", "description": "Skeleton is used to display the loading state of some component.", "keywords": [ "skeleton" @@ -36,10 +36,10 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "@nextui-org/theme": ">=2.1.0" + "@nextui-org/theme": ">=2.1.0", + "@nextui-org/system": ">=2.0.0" }, "dependencies": { - "@nextui-org/system-rsc": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*" }, @@ -47,9 +47,10 @@ "@nextui-org/theme": "workspace:*", "@nextui-org/card": "workspace:*", "@nextui-org/button": "workspace:*", + "@nextui-org/system": "workspace:*", "clean-package": "2.2.0", "react": "^18.0.0", "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/skeleton/src/skeleton.tsx b/packages/components/skeleton/src/skeleton.tsx index 7294435a02..bbeb114335 100644 --- a/packages/components/skeleton/src/skeleton.tsx +++ b/packages/components/skeleton/src/skeleton.tsx @@ -1,4 +1,4 @@ -import {forwardRef} from "@nextui-org/system-rsc"; +import {forwardRef} from "@nextui-org/system"; import {UseSkeletonProps, useSkeleton} from "./use-skeleton"; diff --git a/packages/components/skeleton/src/use-skeleton.ts b/packages/components/skeleton/src/use-skeleton.ts index ace986d004..c1636981c7 100644 --- a/packages/components/skeleton/src/use-skeleton.ts +++ b/packages/components/skeleton/src/use-skeleton.ts @@ -1,10 +1,11 @@ import type {SkeletonVariantProps, SkeletonSlots, SlotsToClasses} from "@nextui-org/theme"; -import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system-rsc"; +import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; -import {mapPropsVariants} from "@nextui-org/system-rsc"; +import {mapPropsVariants} from "@nextui-org/system"; import {skeleton} from "@nextui-org/theme"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {useMemo, Ref} from "react"; +import {useProviderContext} from "@nextui-org/system"; interface Props extends HTMLNextUIProps<"div"> { /** @@ -34,18 +35,24 @@ interface Props extends HTMLNextUIProps<"div"> { export type UseSkeletonProps = Props & SkeletonVariantProps; export function useSkeleton(originalProps: UseSkeletonProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, skeleton.variantKeys); const {as, children, isLoaded = false, className, classNames, ...otherProps} = props; const Component = as || "div"; + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; + const slots = useMemo( () => skeleton({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps), children], + [objectToDeps(variantProps), disableAnimation, children], ); const baseStyles = clsx(classNames?.base, className); diff --git a/packages/components/skeleton/tsup.config.ts b/packages/components/skeleton/tsup.config.ts index e7ef417947..3e2bcff6cc 100644 --- a/packages/components/skeleton/tsup.config.ts +++ b/packages/components/skeleton/tsup.config.ts @@ -4,4 +4,5 @@ export default defineConfig({ clean: true, target: "es2019", format: ["cjs", "esm"], + banner: {js: '"use client";'}, }); diff --git a/packages/components/slider/CHANGELOG.md b/packages/components/slider/CHANGELOG.md index 96f07ffa80..79a1930fbc 100644 --- a/packages/components/slider/CHANGELOG.md +++ b/packages/components/slider/CHANGELOG.md @@ -1,5 +1,16 @@ # @nextui-org/slider +## 2.2.10 + +### Patch Changes + +- [#3017](https://github.com/nextui-org/nextui/pull/3017) [`5329de42d`](https://github.com/nextui-org/nextui/commit/5329de42d2152424bfb5d5ebbf08a68d557f147f) Thanks [@wingkwong](https://github.com/wingkwong)! - calculate the correct value on mark click (#2980) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/tooltip@2.0.34 + ## 2.2.9 ### Patch Changes diff --git a/packages/components/slider/__tests__/slider.test.tsx b/packages/components/slider/__tests__/slider.test.tsx index 9507e3e80e..0100c814c2 100644 --- a/packages/components/slider/__tests__/slider.test.tsx +++ b/packages/components/slider/__tests__/slider.test.tsx @@ -213,4 +213,161 @@ describe("Slider", () => { expect(setValues).toStrictEqual([[15, 25]]); }); + + it("should supports hideThumb", async function () { + const {container} = render(); + + const track = container.querySelector("[data-slot='track']"); + + expect(track).toHaveAttribute("data-thumb-hidden", "true"); + }); + + it("should supports marks", async function () { + const {container} = render( + , + ); + + const marks = container.querySelectorAll("[data-slot='mark']"); + + expect(marks).toHaveLength(3); + }); + + it("should supports marks with hideThumb", async function () { + const {container} = render( + , + ); + + const track = container.querySelector("[data-slot='track']"); + + expect(track).toHaveAttribute("data-thumb-hidden", "true"); + + const marks = container.querySelectorAll("[data-slot='mark']"); + + expect(marks).toHaveLength(3); + }); + + it("should move thumb after clicking mark (single thumb)", async function () { + const {getByRole, container} = render( + , + ); + + const marks = container.querySelectorAll("[data-slot='mark']"); + + expect(marks).toHaveLength(3); + + await act(async () => { + await userEvent.click(marks[1]); + }); + + const slider = getByRole("slider"); + + expect(slider).toHaveProperty("value", "0.5"); + expect(slider).toHaveAttribute("aria-valuetext", "0.5"); + }); + + it("should move thumb after clicking mark (left and right thumbs)", async function () { + const {getAllByRole, container} = render( + , + ); + + const marks = container.querySelectorAll("[data-slot='mark']"); + + expect(marks).toHaveLength(3); + + await act(async () => { + await userEvent.click(marks[1]); + }); + + const [leftSlider, rightSlider] = getAllByRole("slider"); + + expect(leftSlider).toHaveProperty("value", "0.5"); + expect(leftSlider).toHaveAttribute("aria-valuetext", "0.5"); + + expect(rightSlider).toHaveProperty("value", "0.8"); + expect(rightSlider).toHaveAttribute("aria-valuetext", "0.8"); + }); }); diff --git a/packages/components/slider/package.json b/packages/components/slider/package.json index 9ceb5f0a04..9899c2d971 100644 --- a/packages/components/slider/package.json +++ b/packages/components/slider/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/slider", - "version": "2.2.9", + "version": "2.2.10", "description": "A slider allows a user to select one or more values within a range.", "keywords": [ "slider" @@ -43,13 +43,13 @@ "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", "@nextui-org/tooltip": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/i18n": "^3.10.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/slider": "^3.7.6", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/slider": "^3.5.2" + "@react-aria/focus": "3.16.2", + "@react-aria/i18n": "3.10.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/slider": "3.7.6", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/slider": "3.5.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -61,4 +61,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/slider/src/use-slider.ts b/packages/components/slider/src/use-slider.ts index 4aa15ced80..aef600c01e 100644 --- a/packages/components/slider/src/use-slider.ts +++ b/packages/components/slider/src/use-slider.ts @@ -1,6 +1,12 @@ import type {SliderSlots, SliderVariantProps, SlotsToClasses} from "@nextui-org/theme"; -import {DOMAttributes, HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + DOMAttributes, + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {slider} from "@nextui-org/theme"; import {ReactRef, useDOMRef, filterDOMProps} from "@nextui-org/react-utils"; import {useSliderState} from "@react-stately/slider"; @@ -133,6 +139,8 @@ export type UseSliderProps = Omit> & SliderVariantProps; export function useSlider(originalProps: UseSliderProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, slider.variantKeys); const { @@ -167,6 +175,8 @@ export function useSlider(originalProps: UseSliderProps) { const Component = as || "div"; const shouldFilterDOMProps = typeof Component === "string"; + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; const domRef = useDOMRef(ref); const trackRef = useRef(null); @@ -231,11 +241,12 @@ export function useSlider(originalProps: UseSliderProps) { slider({ ...variantProps, hasMarks, + disableAnimation, hasSingleThumb, isVertical, className, }), - [objectToDeps(variantProps), isVertical, hasSingleThumb, hasMarks, className], + [objectToDeps(variantProps), isVertical, disableAnimation, hasSingleThumb, hasMarks, className], ); const [startOffset, endOffset] = [ @@ -378,6 +389,30 @@ export function useSlider(originalProps: UseSliderProps) { style: { [isVertical ? "bottom" : direction === "rtl" ? "right" : "left"]: `${percent * 100}%`, }, + // avoid `onDownTrack` is being called since when you click the mark, + // `onDownTrack` will calculate the percent based on the position you click + // the calculated value will be set instead of the actual value defined in `marks` + onMouseDown: (e: React.MouseEvent) => e.stopPropagation(), + onPointerDown: (e: React.PointerEvent) => e.stopPropagation(), + onClick: (e: any) => { + e.stopPropagation(); + if (state.values.length === 1) { + state.setThumbPercent(0, percent); + } else { + const leftThumbVal = state.values[0]; + const rightThumbVal = state.values[1]; + + if (mark.value < leftThumbVal) { + state.setThumbPercent(0, percent); + } else if (mark.value > rightThumbVal) { + state.setThumbPercent(1, percent); + } else if (Math.abs(mark.value - leftThumbVal) < Math.abs(mark.value - rightThumbVal)) { + state.setThumbPercent(0, percent); + } else { + state.setThumbPercent(1, percent); + } + } + }, }; }; diff --git a/packages/components/slider/stories/slider.stories.tsx b/packages/components/slider/stories/slider.stories.tsx index 1d7abee816..64022e7995 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"; @@ -315,7 +315,24 @@ export const ThumbHidden = { "aria-label": "Player progress", color: "foreground", hideThumb: true, - defaultValue: 20, + maxValue: 1, + minValue: 0, + step: 0.1, + marks: [ + { + value: 0.2, + label: "20%", + }, + { + value: 0.5, + label: "50%", + }, + { + value: 0.8, + label: "80%", + }, + ], + defaultValue: 0.2, }, }; diff --git a/packages/components/snippet/CHANGELOG.md b/packages/components/snippet/CHANGELOG.md index 0db8ff5d92..d0d7aaadfa 100644 --- a/packages/components/snippet/CHANGELOG.md +++ b/packages/components/snippet/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/snippet +## 2.0.36 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/button@2.0.32 + - @nextui-org/tooltip@2.0.34 + ## 2.0.35 ### Patch Changes diff --git a/packages/components/snippet/package.json b/packages/components/snippet/package.json index 42dd602c32..ae00a45bf2 100644 --- a/packages/components/snippet/package.json +++ b/packages/components/snippet/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/snippet", - "version": "2.0.35", + "version": "2.0.36", "description": "Display a snippet of copyable code for the command line.", "keywords": [ "snippet" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -47,8 +47,8 @@ "@nextui-org/shared-icons": "workspace:*", "@nextui-org/use-clipboard": "workspace:*", "@nextui-org/tooltip": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/utils": "^3.23.2" + "@react-aria/focus": "3.16.2", + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -58,4 +58,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/snippet/src/use-snippet.ts b/packages/components/snippet/src/use-snippet.ts index c0ab8200ed..e45740452a 100644 --- a/packages/components/snippet/src/use-snippet.ts +++ b/packages/components/snippet/src/use-snippet.ts @@ -1,7 +1,12 @@ import type {SnippetVariantProps, SnippetSlots, SlotsToClasses} from "@nextui-org/theme"; import {snippet} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {useDOMRef, filterDOMProps} from "@nextui-org/react-utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {ReactRef} from "@nextui-org/react-utils"; @@ -116,6 +121,8 @@ export interface UseSnippetProps extends Omit, Snippe } export function useSnippet(originalProps: UseSnippetProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, snippet.variantKeys); const { @@ -142,6 +149,8 @@ export function useSnippet(originalProps: UseSnippetProps) { const Component = as || "div"; const shouldFilterDOMProps = typeof Component === "string"; + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; const tooltipProps: Partial = { offset: 15, @@ -167,8 +176,9 @@ export function useSnippet(originalProps: UseSnippetProps) { () => snippet({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableAnimation], ); const symbolBefore = useMemo(() => { diff --git a/packages/components/spacer/CHANGELOG.md b/packages/components/spacer/CHANGELOG.md index 5efd527c1a..869df55783 100644 --- a/packages/components/spacer/CHANGELOG.md +++ b/packages/components/spacer/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/spacer +## 2.0.28 + +### Patch Changes + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.0.27 ### Patch Changes diff --git a/packages/components/spacer/package.json b/packages/components/spacer/package.json index 0bb73d110a..f1ad9fb93a 100644 --- a/packages/components/spacer/package.json +++ b/packages/components/spacer/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/spacer", - "version": "2.0.27", + "version": "2.0.28", "description": "A flexible spacer component designed to create consistent spacing and maintain alignment in your layout.", "keywords": [ "spacer" @@ -50,4 +50,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/spinner/CHANGELOG.md b/packages/components/spinner/CHANGELOG.md index cb81829d06..8797cee25a 100644 --- a/packages/components/spinner/CHANGELOG.md +++ b/packages/components/spinner/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/spinner +## 2.0.29 + +### Patch Changes + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.0.28 ### Patch Changes diff --git a/packages/components/spinner/package.json b/packages/components/spinner/package.json index 0878894e78..21c7b0ea7a 100644 --- a/packages/components/spinner/package.json +++ b/packages/components/spinner/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/spinner", - "version": "2.0.28", + "version": "2.0.29", "description": "Loaders express an unspecified wait time or display the length of a process.", "keywords": [ "loading", @@ -52,4 +52,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/switch/CHANGELOG.md b/packages/components/switch/CHANGELOG.md index 00c5b69eb1..beb3c994a2 100644 --- a/packages/components/switch/CHANGELOG.md +++ b/packages/components/switch/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/switch +## 2.0.29 + +### Patch Changes + +- [#2924](https://github.com/nextui-org/nextui/pull/2924) [`9acf3eada`](https://github.com/nextui-org/nextui/commit/9acf3eada03af911dba42198a83e8ea4b453a93a) Thanks [@wingkwong](https://github.com/wingkwong)! - Fixed react-hook-form uncontrolled switch component + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#3013](https://github.com/nextui-org/nextui/pull/3013) [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85) Thanks [@kosmotema](https://github.com/kosmotema)! - make the VisuallyHidden `elementType` as span when the default parent element accepts only phrasing elements + ## 2.0.28 ### Patch Changes diff --git a/packages/components/switch/__tests__/switch.test.tsx b/packages/components/switch/__tests__/switch.test.tsx index 870ef8933c..d9b76e9092 100644 --- a/packages/components/switch/__tests__/switch.test.tsx +++ b/packages/components/switch/__tests__/switch.test.tsx @@ -1,5 +1,7 @@ import * as React from "react"; -import {act, render} from "@testing-library/react"; +import {render, renderHook, act} from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import {useForm} from "react-hook-form"; import {Switch} from "../src"; @@ -11,7 +13,7 @@ describe("Switch", () => { }); it("ref should be forwarded", () => { - const ref = React.createRef(); + const ref = React.createRef(); render(); expect(ref.current).not.toBeNull(); @@ -198,3 +200,74 @@ describe("Switch", () => { expect(wrapper.getByTestId("end-icon")).toBeInTheDocument(); }); }); + +describe("Switch with React Hook Form", () => { + let switch1: HTMLInputElement; + let switch2: HTMLInputElement; + let switch3: HTMLInputElement; + let submitButton: HTMLButtonElement; + let onSubmit: () => void; + + beforeEach(() => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + defaultTrue: true, + defaultFalse: false, + requiredField: false, + }, + }), + ); + + const { + register, + formState: {errors}, + handleSubmit, + } = result.current; + + onSubmit = jest.fn(); + + render( +
+ By default this switch is true + By default this switch is false + This switch is required + {errors.requiredField && This switch is required} + +
, + ); + + switch1 = document.querySelector("input[name=defaultTrue]")!; + switch2 = document.querySelector("input[name=defaultFalse]")!; + switch3 = document.querySelector("input[name=requiredField]")!; + submitButton = document.querySelector("button")!; + }); + + it("should work with defaultValues", () => { + expect(switch1.checked).toBe(true); + expect(switch2.checked).toBe(false); + expect(switch3.checked).toBe(false); + }); + + it("should not submit form when required field is empty", async () => { + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(0); + }); + + it("should submit form when required field is not empty", async () => { + act(() => { + switch3.click(); + }); + + expect(switch3.checked).toBe(true); + + const user = userEvent.setup(); + + await user.click(submitButton); + + expect(onSubmit).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/components/switch/package.json b/packages/components/switch/package.json index 98c276a4aa..8f15a90907 100644 --- a/packages/components/switch/package.json +++ b/packages/components/switch/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/switch", - "version": "2.0.28", + "version": "2.0.29", "description": "A switch is similar to a checkbox, but represents on/off values as opposed to selection.", "keywords": [ "switch" @@ -42,13 +42,14 @@ "dependencies": { "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/switch": "^3.6.2", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/toggle": "^3.7.2", - "@react-types/shared": "^3.22.1" + "@nextui-org/use-safe-layout-effect": "workspace:*", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/switch": "3.6.2", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/toggle": "3.7.2", + "@react-types/shared": "3.22.1" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -56,7 +57,8 @@ "@nextui-org/shared-icons": "workspace:*", "clean-package": "2.2.0", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-hook-form": "^7.51.3" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/switch/src/switch.tsx b/packages/components/switch/src/switch.tsx index bee6a80bae..d60428bfcd 100644 --- a/packages/components/switch/src/switch.tsx +++ b/packages/components/switch/src/switch.tsx @@ -35,7 +35,7 @@ const Switch = forwardRef<"input", SwitchProps>((props, ref) => { return ( - + diff --git a/packages/components/switch/src/use-switch.ts b/packages/components/switch/src/use-switch.ts index aa209fbc00..59ad998229 100644 --- a/packages/components/switch/src/use-switch.ts +++ b/packages/components/switch/src/use-switch.ts @@ -1,15 +1,15 @@ import type {ToggleVariantProps, ToggleSlots, SlotsToClasses} from "@nextui-org/theme"; -import type {FocusableRef} from "@react-types/shared"; import type {AriaSwitchProps} from "@react-aria/switch"; import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system"; import {ReactNode, Ref, useCallback, useId, useRef, useState} from "react"; -import {mapPropsVariants} from "@nextui-org/system"; +import {mapPropsVariants, useProviderContext} from "@nextui-org/system"; +import {mergeRefs} from "@nextui-org/react-utils"; +import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect"; import {useHover, usePress} from "@react-aria/interactions"; import {toggle} from "@nextui-org/theme"; import {chain, mergeProps} from "@react-aria/utils"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; -import {useFocusableRef} from "@nextui-org/react-utils"; import {useSwitch as useReactAriaSwitch} from "@react-aria/switch"; import {useMemo} from "react"; import {useToggleState} from "@react-stately/toggle"; @@ -27,7 +27,7 @@ interface Props extends HTMLNextUIProps<"input"> { /** * Ref to the DOM node. */ - ref?: Ref; + ref?: Ref; /** * The label of the switch. */ @@ -76,6 +76,8 @@ export type UseSwitchProps = Omit & ToggleVariantProps; export function useSwitch(originalProps: UseSwitchProps = {}) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, toggle.variantKeys); const { @@ -100,8 +102,12 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { const Component = as || "label"; - const inputRef = useRef(null); - const domRef = useFocusableRef(ref as FocusableRef, inputRef); + const domRef = useRef(null); + + const inputRef = useRef(null); + + const disableAnimation = + originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false; const labelId = useId(); @@ -139,6 +145,16 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { const state = useToggleState(ariaSwitchProps); + // if we use `react-hook-form`, it will set the switch value using the ref in register + // i.e. setting ref.current.checked to true or false which is uncontrolled + // hence, sync the state with `ref.current.checked` + useSafeLayoutEffect(() => { + if (!inputRef.current) return; + const isInputRefChecked = !!inputRef.current.checked; + + state.setSelected(isInputRefChecked); + }, [inputRef.current]); + const { inputProps, isPressed: isPressedKeyboard, @@ -177,8 +193,9 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { () => toggle({ ...variantProps, + disableAnimation, }), - [objectToDeps(variantProps)], + [objectToDeps(variantProps), disableAnimation], ); const baseStyles = clsx(classNames?.base, className); @@ -212,7 +229,7 @@ export function useSwitch(originalProps: UseSwitchProps = {}) { const getInputProps: PropGetter = (props = {}) => { return { ...mergeProps(inputProps, focusProps, props), - ref: inputRef, + ref: mergeRefs(inputRef, ref), id: inputProps.id, onChange: chain(onChange, inputProps.onChange), }; diff --git a/packages/components/switch/stories/switch.stories.tsx b/packages/components/switch/stories/switch.stories.tsx index 33a753a70b..408762247f 100644 --- a/packages/components/switch/stories/switch.stories.tsx +++ b/packages/components/switch/stories/switch.stories.tsx @@ -5,6 +5,8 @@ import {toggle} from "@nextui-org/theme"; import {VisuallyHidden} from "@react-aria/visually-hidden"; import {SunFilledIcon, MoonFilledIcon} from "@nextui-org/shared-icons"; import {clsx} from "@nextui-org/shared-utils"; +import {button} from "@nextui-org/theme"; +import {useForm} from "react-hook-form"; import {Switch, SwitchProps, SwitchThumbIconProps, useSwitch} from "../src"; @@ -131,6 +133,44 @@ const CustomWithHooksTemplate = (args: SwitchProps) => { ); }; +const WithReactHookFormTemplate = (args: SwitchProps) => { + const { + register, + formState: {errors}, + handleSubmit, + } = useForm({ + defaultValues: { + defaultTrue: true, + defaultFalse: false, + requiredField: false, + }, + }); + + const onSubmit = (data: any) => { + // eslint-disable-next-line no-console + console.log(data); + alert("Submitted value: " + JSON.stringify(data)); + }; + + return ( +
+ + By default this switch is true + + + By default this switch is false + + + This switch is required + + {errors.requiredField && This switch is required} + +
+ ); +}; + export const Default = { args: { ...defaultProps, @@ -181,6 +221,14 @@ export const WithIcons = { }, }; +export const WithReactHookForm = { + render: WithReactHookFormTemplate, + + args: { + ...defaultProps, + }, +}; + export const Controlled = { render: ControlledTemplate, diff --git a/packages/components/table/CHANGELOG.md b/packages/components/table/CHANGELOG.md index c69abca6b5..12067ed1ef 100644 --- a/packages/components/table/CHANGELOG.md +++ b/packages/components/table/CHANGELOG.md @@ -1,5 +1,19 @@ # @nextui-org/table +## 2.0.34 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#2866](https://github.com/nextui-org/nextui/pull/2866) [`5f5ad7a1d`](https://github.com/nextui-org/nextui/commit/5f5ad7a1ddae1fb30be4a25e6aad4f87666ee1ce) Thanks [@sapkra](https://github.com/sapkra)! - Add missing export of `TableRowProps` type (#2584) + +- [#3020](https://github.com/nextui-org/nextui/pull/3020) [`9d63259ee`](https://github.com/nextui-org/nextui/commit/9d63259eeacfe8b8714629e612a20e00293f0a43) Thanks [@wingkwong](https://github.com/wingkwong)! - set `onKeyDownCapture` to `undefined` so that users can type with spaces in input or textarea inside a table component (#1968) + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a), [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85)]: + - @nextui-org/checkbox@2.1.0 + - @nextui-org/spacer@2.0.28 + ## 2.0.33 ### Patch Changes diff --git a/packages/components/table/package.json b/packages/components/table/package.json index 34c84474f6..59ff88a4c9 100644 --- a/packages/components/table/package.json +++ b/packages/components/table/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/table", - "version": "2.0.33", + "version": "2.0.34", "description": "Tables are used to display tabular data using rows and columns. ", "keywords": [ "table" @@ -45,15 +45,15 @@ "@nextui-org/shared-icons": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/spacer": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/table": "^3.13.5", - "@react-aria/utils": "^3.23.2", - "@react-aria/visually-hidden": "^3.8.10", - "@react-stately/table": "^3.11.6", - "@react-stately/virtualizer": "^3.6.8", - "@react-types/grid": "^3.2.4", - "@react-types/table": "^3.9.3" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/table": "3.13.5", + "@react-aria/utils": "3.23.2", + "@react-aria/visually-hidden": "3.8.10", + "@react-stately/table": "3.11.6", + "@react-stately/virtualizer": "3.6.8", + "@react-types/grid": "3.2.4", + "@react-types/table": "3.9.3" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -65,11 +65,11 @@ "@nextui-org/tooltip": "workspace:*", "@nextui-org/use-infinite-scroll": "workspace:*", "@nextui-org/user": "workspace:*", - "@react-stately/data": "^3.11.2", + "@react-stately/data": "3.11.2", "clean-package": "2.2.0", "react": "^18.0.0", "react-dom": "^18.0.0", "swr": "^2.2.1" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/table/src/index.ts b/packages/components/table/src/index.ts index 8bbe716b23..2d85ff6564 100644 --- a/packages/components/table/src/index.ts +++ b/packages/components/table/src/index.ts @@ -19,4 +19,10 @@ export {default as Table} from "./table"; // export base components export {TableBody, TableCell, TableColumn, TableHeader, TableRow} from "./base"; -export type {TableBodyProps, TableCellProps, TableColumnProps, TableHeaderProps} from "./base"; +export type { + TableBodyProps, + TableCellProps, + TableColumnProps, + TableHeaderProps, + TableRowProps, +} from "./base"; diff --git a/packages/components/table/src/use-table.ts b/packages/components/table/src/use-table.ts index b40b4c0250..56f48b01d4 100644 --- a/packages/components/table/src/use-table.ts +++ b/packages/components/table/src/use-table.ts @@ -12,7 +12,12 @@ import type {TableCollection} from "@react-types/table"; import {ReactNode, Key, useCallback} from "react"; import {useTableState} from "@react-stately/table"; import {AriaTableProps, useTable as useReactAriaTable} from "@react-aria/table"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {table} from "@nextui-org/theme"; import {useDOMRef, filterDOMProps} from "@nextui-org/react-utils"; import {mergeProps} from "@react-aria/utils"; @@ -140,6 +145,8 @@ export type ValuesType = { }; export function useTable(originalProps: UseTableProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, table.variantKeys); const { @@ -151,7 +158,7 @@ export function useTable(originalProps: UseTableProps) { classNames, layoutNode, removeWrapper = false, - disableAnimation = false, + disableAnimation = globalContext?.disableAnimation ?? false, selectionMode = "none", topContentPlacement = "inside", bottomContentPlacement = "inside", @@ -262,6 +269,9 @@ export function useTable(originalProps: UseTableProps) { }), props, ), + // avoid typeahead debounce wait for input / textarea + // so that typing with space won't be blocked + onKeyDownCapture: undefined, ref: domRef, className: slots.table({class: clsx(classNames?.table, props?.className)}), }), diff --git a/packages/components/tabs/CHANGELOG.md b/packages/components/tabs/CHANGELOG.md index 59b1d3bcad..888d62b82f 100644 --- a/packages/components/tabs/CHANGELOG.md +++ b/packages/components/tabs/CHANGELOG.md @@ -1,5 +1,21 @@ # @nextui-org/tabs +## 2.0.30 + +### Patch Changes + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2973](https://github.com/nextui-org/nextui/pull/2973) [`e34c5e307`](https://github.com/nextui-org/nextui/commit/e34c5e307d1dfa2747e7fd77752983ad30a7e3eb) Thanks [@wingkwong](https://github.com/wingkwong)! - Add `destroyInactiveTabPanel` prop for Tabs component (#1562) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#2725](https://github.com/nextui-org/nextui/pull/2725) [`8048dcc0c`](https://github.com/nextui-org/nextui/commit/8048dcc0c37455d2574c93d6f9fa505a936aedb5) Thanks [@ericfabreu](https://github.com/ericfabreu)! - Fix 'Tap to click' behavior on macOS for Accordion and Tab + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/framer-utils@2.0.19 + ## 2.0.29 ### Patch Changes diff --git a/packages/components/tabs/__tests__/tabs.test.tsx b/packages/components/tabs/__tests__/tabs.test.tsx index a5117c5d6a..ff87798522 100644 --- a/packages/components/tabs/__tests__/tabs.test.tsx +++ b/packages/components/tabs/__tests__/tabs.test.tsx @@ -318,4 +318,40 @@ describe("Tabs", () => { expect(tabWrapper).toHaveAttribute("data-placement", "top"); expect(tabWrapper).toHaveAttribute("data-vertical", "horizontal"); }); + + test("should destory inactive tab panels", () => { + const {container} = render( + + +
Content 1
+
+ +
Content 2
+
+ +
Content 3
+
+
, + ); + + expect(container.querySelectorAll("[data-slot='panel']")).toHaveLength(1); + }); + + test("should destory inactive tab panels", () => { + const {container} = render( + + +
Content 1
+
+ +
Content 2
+
+ +
Content 3
+
+
, + ); + + expect(container.querySelectorAll("[data-slot='panel']")).toHaveLength(3); + }); }); diff --git a/packages/components/tabs/package.json b/packages/components/tabs/package.json index 62f43b3383..91d0f2b91d 100644 --- a/packages/components/tabs/package.json +++ b/packages/components/tabs/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/tabs", - "version": "2.0.29", + "version": "2.0.30", "description": "Tabs organize content into multiple sections and allow users to navigate between them.", "keywords": [ "tabs" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -47,13 +47,13 @@ "@nextui-org/framer-utils": "workspace:*", "@nextui-org/use-is-mounted": "workspace:*", "@nextui-org/use-update-effect": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/tabs": "^3.8.5", - "@react-aria/utils": "^3.23.2", - "@react-stately/tabs": "^3.6.4", - "@react-types/shared": "^3.22.1", - "@react-types/tabs": "^3.3.5", + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/tabs": "3.8.5", + "@react-aria/utils": "3.23.2", + "@react-stately/tabs": "3.6.4", + "@react-types/shared": "3.22.1", + "@react-types/tabs": "3.3.5", "scroll-into-view-if-needed": "3.0.10" }, "devDependencies": { @@ -71,4 +71,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/tabs/src/tab-panel.tsx b/packages/components/tabs/src/tab-panel.tsx index 58968cf97f..fe51d65efb 100644 --- a/packages/components/tabs/src/tab-panel.tsx +++ b/packages/components/tabs/src/tab-panel.tsx @@ -1,5 +1,6 @@ import type {AriaTabPanelProps} from "@react-aria/tabs"; +import {Key} from "@react-types/shared"; import {forwardRef, HTMLNextUIProps} from "@nextui-org/system"; import {useDOMRef} from "@nextui-org/react-utils"; import {clsx} from "@nextui-org/shared-utils"; @@ -10,6 +11,15 @@ import {useFocusRing} from "@react-aria/focus"; import {ValuesType} from "./use-tabs"; interface Props extends HTMLNextUIProps<"div"> { + /** + * Whether to destroy inactive tab panel when switching tabs. + * Inactive tab panels are inert and cannot be interacted with. + */ + destroyInactiveTabPanel: boolean; + /** + * The current tab key. + */ + tabKey: Key; /** * The tab list state. */ @@ -30,12 +40,15 @@ export type TabPanelProps = Props & AriaTabPanelProps; * @internal */ const TabPanel = forwardRef<"div", TabPanelProps>((props, ref) => { - const {as, state, className, slots, classNames, ...otherProps} = props; + const {as, tabKey, destroyInactiveTabPanel, state, className, slots, classNames, ...otherProps} = + props; const Component = as || "div"; + const domRef = useDOMRef(ref); const {tabPanelProps} = useTabPanel(props, state, domRef); + const {focusProps, isFocused, isFocusVisible} = useFocusRing(); const selectedItem = state.selectedItem; @@ -44,7 +57,9 @@ const TabPanel = forwardRef<"div", TabPanelProps>((props, ref) => { const tabPanelStyles = clsx(classNames?.panel, className, selectedItem?.props?.className); - if (!content) { + const isSelected = tabKey === selectedItem?.key; + + if (!content || (!isSelected && destroyInactiveTabPanel)) { return null; } @@ -53,7 +68,9 @@ const TabPanel = forwardRef<"div", TabPanelProps>((props, ref) => { ref={domRef} data-focus={isFocused} data-focus-visible={isFocusVisible} - {...mergeProps(tabPanelProps, focusProps, otherProps)} + data-inert={!isSelected ? "true" : undefined} + inert={!isSelected ? "true" : undefined} + {...(isSelected && mergeProps(tabPanelProps, focusProps, otherProps))} className={slots.panel?.({class: tabPanelStyles})} data-slot="panel" > diff --git a/packages/components/tabs/src/tab.tsx b/packages/components/tabs/src/tab.tsx index 3f3da0974b..6c68507f50 100644 --- a/packages/components/tabs/src/tab.tsx +++ b/packages/components/tabs/src/tab.tsx @@ -111,11 +111,11 @@ const Tab = forwardRef<"button", TabItemProps>((props, ref) => { enabled: shouldFilterDOMProps, omitPropNames: new Set(["title"]), }), + {onClick: handleClick}, )} className={slots.tab?.({class: tabStyles})} title={otherProps?.titleValue} type={Component === "button" ? "button" : undefined} - onClick={handleClick} > {isSelected && !disableAnimation && !disableCursorAnimation && isMounted ? ( diff --git a/packages/components/tabs/src/tabs.tsx b/packages/components/tabs/src/tabs.tsx index a4c618ac82..c03e7c8525 100644 --- a/packages/components/tabs/src/tabs.tsx +++ b/packages/components/tabs/src/tabs.tsx @@ -9,7 +9,15 @@ import TabPanel from "./tab-panel"; interface Props extends UseTabsProps {} function Tabs(props: Props, ref: ForwardedRef) { - const {Component, values, state, getBaseProps, getTabListProps, getWrapperProps} = useTabs({ + const { + Component, + values, + state, + destroyInactiveTabPanel, + getBaseProps, + getTabListProps, + getWrapperProps, + } = useTabs({ ...props, ref, }); @@ -41,12 +49,18 @@ function Tabs(props: Props, ref: ForwardedRef{tabs} : tabs}
- + {[...state.collection].map((item) => { + return ( + + ); + })} ); @@ -57,9 +71,9 @@ function Tabs(props: Props, ref: ForwardedRef = Props & {ref?: Ref}; +export type TabsProps = Props & {ref?: Ref}; // forwardRef doesn't support generic parameters, so cast the result to the correct type -export default forwardRef(Tabs) as (props: TabsProps) => ReactElement; +export default forwardRef(Tabs) as (props: TabsProps) => ReactElement; Tabs.displayName = "NextUI.Tabs"; diff --git a/packages/components/tabs/src/use-tabs.ts b/packages/components/tabs/src/use-tabs.ts index aaea6f4c12..beb7de2fd3 100644 --- a/packages/components/tabs/src/use-tabs.ts +++ b/packages/components/tabs/src/use-tabs.ts @@ -1,6 +1,11 @@ import type {TabsVariantProps, SlotsToClasses, TabsSlots, TabsReturnType} from "@nextui-org/theme"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {tabs} from "@nextui-org/theme"; import {useDOMRef} from "@nextui-org/react-utils"; import {clsx, objectToDeps} from "@nextui-org/shared-utils"; @@ -57,6 +62,11 @@ export interface Props extends Omit { * @default false */ isVertical?: boolean; + /** + * Whether to destroy inactive tab panel when switching tabs. Inactive tab panels are inert and cannot be interacted with. + * @default true + */ + destroyInactiveTabPanel?: boolean; } export type UseTabsProps = Props & @@ -78,6 +88,8 @@ export type ValuesType = { }; export function useTabs(originalProps: UseTabsProps) { + const globalContext = useProviderContext(); + const [props, variantProps] = mapPropsVariants(originalProps, tabs.variantKeys); const { @@ -90,6 +102,7 @@ export function useTabs(originalProps: UseTabsProps) { motionProps, isVertical = false, shouldSelectOnPressUp = true, + destroyInactiveTabPanel = true, ...otherProps } = props; @@ -98,6 +111,9 @@ export function useTabs(originalProps: UseTabsProps) { const domRef = useDOMRef(ref); + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; + const state = useTabListState({ children: children as CollectionChildren, ...otherProps, @@ -109,9 +125,10 @@ export function useTabs(originalProps: UseTabsProps) { tabs({ ...variantProps, className, + disableAnimation, ...(isVertical ? {placement: "start"} : {}), }), - [objectToDeps(variantProps), className, isVertical], + [objectToDeps(variantProps), className, disableAnimation, isVertical], ); const baseStyles = clsx(classNames?.base, className); @@ -122,20 +139,20 @@ export function useTabs(originalProps: UseTabsProps) { slots, classNames, motionProps, + disableAnimation, listRef: domRef, shouldSelectOnPressUp, disableCursorAnimation, isDisabled: originalProps?.isDisabled, - disableAnimation: originalProps?.disableAnimation, }), [ state, slots, domRef, motionProps, + disableAnimation, disableCursorAnimation, shouldSelectOnPressUp, - originalProps?.disableAnimation, originalProps?.isDisabled, classNames, ], @@ -182,6 +199,7 @@ export function useTabs(originalProps: UseTabsProps) { domRef, state, values, + destroyInactiveTabPanel, getBaseProps, getTabListProps, getWrapperProps, diff --git a/packages/components/tooltip/CHANGELOG.md b/packages/components/tooltip/CHANGELOG.md index ae672fc2b4..a4dc503359 100644 --- a/packages/components/tooltip/CHANGELOG.md +++ b/packages/components/tooltip/CHANGELOG.md @@ -1,5 +1,15 @@ # @nextui-org/tooltip +## 2.0.34 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/aria-utils@2.0.19 + - @nextui-org/framer-utils@2.0.19 + ## 2.0.33 ### Patch Changes diff --git a/packages/components/tooltip/package.json b/packages/components/tooltip/package.json index 5368ee8fc2..0aaaae210c 100644 --- a/packages/components/tooltip/package.json +++ b/packages/components/tooltip/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/tooltip", - "version": "2.0.33", + "version": "2.0.34", "description": "A React Component for rendering dynamically positioned Tooltips", "keywords": [ "tooltip" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0", + "framer-motion": ">=10.17.0", "@nextui-org/theme": ">=2.1.0", "@nextui-org/system": ">=2.0.0" }, @@ -46,13 +46,13 @@ "@nextui-org/aria-utils": "workspace:*", "@nextui-org/framer-utils": "workspace:*", "@nextui-org/use-safe-layout-effect": "workspace:*", - "@react-aria/interactions": "^3.21.1", - "@react-aria/overlays": "^3.21.1", - "@react-aria/tooltip": "^3.7.2", - "@react-aria/utils": "^3.23.2", - "@react-stately/tooltip": "^3.4.7", - "@react-types/overlays": "^3.8.5", - "@react-types/tooltip": "^3.4.7" + "@react-aria/interactions": "3.21.1", + "@react-aria/overlays": "3.21.1", + "@react-aria/tooltip": "3.7.2", + "@react-aria/utils": "3.23.2", + "@react-stately/tooltip": "3.4.7", + "@react-types/overlays": "3.8.5", + "@react-types/tooltip": "3.4.7" }, "devDependencies": { "@nextui-org/button": "workspace:*", @@ -64,4 +64,4 @@ "react-dom": "^18.2.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/components/tooltip/src/use-tooltip.ts b/packages/components/tooltip/src/use-tooltip.ts index 2fec41b92f..bbee183ef3 100644 --- a/packages/components/tooltip/src/use-tooltip.ts +++ b/packages/components/tooltip/src/use-tooltip.ts @@ -9,7 +9,12 @@ import {useTooltipTriggerState} from "@react-stately/tooltip"; import {mergeProps} from "@react-aria/utils"; import {useTooltip as useReactAriaTooltip, useTooltipTrigger} from "@react-aria/tooltip"; import {useOverlayPosition, useOverlay, AriaOverlayProps} from "@react-aria/overlays"; -import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; +import { + HTMLNextUIProps, + mapPropsVariants, + PropGetter, + useProviderContext, +} from "@nextui-org/system"; import {popover} from "@nextui-org/theme"; import {clsx, dataAttr, objectToDeps} from "@nextui-org/shared-utils"; import {ReactRef, mergeRefs} from "@nextui-org/react-utils"; @@ -87,6 +92,7 @@ export type UseTooltipProps = Props & PopoverVariantProps; export function useTooltip(originalProps: UseTooltipProps) { + const globalContext = useProviderContext(); const [props, variantProps] = mapPropsVariants(originalProps, popover.variantKeys); const { @@ -122,6 +128,9 @@ export function useTooltip(originalProps: UseTooltipProps) { const Component = as || "div"; + const disableAnimation = + originalProps?.disableAnimation ?? globalContext?.disableAnimation ?? false; + const state = useTooltipTriggerState({ delay, closeDelay, @@ -203,11 +212,18 @@ export function useTooltip(originalProps: UseTooltipProps) { () => popover({ ...variantProps, + disableAnimation, radius: originalProps?.radius ?? "md", size: originalProps?.size ?? "md", shadow: originalProps?.shadow ?? "sm", }), - [objectToDeps(variantProps), originalProps?.radius, originalProps?.size, originalProps?.shadow], + [ + objectToDeps(variantProps), + disableAnimation, + originalProps?.radius, + originalProps?.size, + originalProps?.shadow, + ], ); const getTriggerProps = useCallback( @@ -269,7 +285,7 @@ export function useTooltip(originalProps: UseTooltipProps) { showArrow, portalContainer, placement: placementProp, - disableAnimation: originalProps?.disableAnimation, + disableAnimation, isDisabled, motionProps, getTooltipContentProps, diff --git a/packages/components/tooltip/stories/tooltip.stories.tsx b/packages/components/tooltip/stories/tooltip.stories.tsx index 771409882f..69658e10ef 100644 --- a/packages/components/tooltip/stories/tooltip.stories.tsx +++ b/packages/components/tooltip/stories/tooltip.stories.tsx @@ -104,7 +104,6 @@ const defaultProps = { offset: 7, defaultOpen: false, isDisabled: false, - disableAnimation: false, content: "I am a tooltip", children: , }; diff --git a/packages/components/user/CHANGELOG.md b/packages/components/user/CHANGELOG.md index 27d187fd14..e0f52c2618 100644 --- a/packages/components/user/CHANGELOG.md +++ b/packages/components/user/CHANGELOG.md @@ -1,5 +1,12 @@ # @nextui-org/user +## 2.0.29 + +### Patch Changes + +- Updated dependencies [[`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2)]: + - @nextui-org/avatar@2.0.28 + ## 2.0.28 ### Patch Changes diff --git a/packages/components/user/package.json b/packages/components/user/package.json index 198a11464a..64360b2528 100644 --- a/packages/components/user/package.json +++ b/packages/components/user/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/user", - "version": "2.0.28", + "version": "2.0.29", "description": "Flexible User Profile Component.", "keywords": [ "user" @@ -43,8 +43,8 @@ "@nextui-org/avatar": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/focus": "^3.16.2", - "@react-aria/utils": "^3.23.2" + "@react-aria/focus": "3.16.2", + "@react-aria/utils": "3.23.2" }, "devDependencies": { "@nextui-org/theme": "workspace:*", @@ -55,4 +55,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/core/react/CHANGELOG.md b/packages/core/react/CHANGELOG.md index 6b695a648c..07b3a938f7 100644 --- a/packages/core/react/CHANGELOG.md +++ b/packages/core/react/CHANGELOG.md @@ -1,5 +1,58 @@ # @nextui-org/react +## 2.4.0 + +### Minor Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +### Patch Changes + +- Updated dependencies [[`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002), [`76f4dd8e7`](https://github.com/nextui-org/nextui/commit/76f4dd8e76d4c0047953fc8a5b8b65614d7375f0), [`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`77e85e665`](https://github.com/nextui-org/nextui/commit/77e85e665c94a188db46ec08c61c872e5d8e7d8c), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9), [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e), [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531), [`a2133009f`](https://github.com/nextui-org/nextui/commit/a2133009f73aa728a0e5deeb9b742aa1defd4de2), [`10497f1a9`](https://github.com/nextui-org/nextui/commit/10497f1a97c0ecfec1cf699a1cd407be48553754), [`7df2c71ec`](https://github.com/nextui-org/nextui/commit/7df2c71ecc5f06d60807b6b3502d3a118080a0d5), [`ad08010fe`](https://github.com/nextui-org/nextui/commit/ad08010fe58438c8d8f264ee1671b53d6638a724), [`bf68c91b9`](https://github.com/nextui-org/nextui/commit/bf68c91b9a5be2014830859b0be2127d657ba90f), [`e34c5e307`](https://github.com/nextui-org/nextui/commit/e34c5e307d1dfa2747e7fd77752983ad30a7e3eb), [`ca8554ccf`](https://github.com/nextui-org/nextui/commit/ca8554ccff143c49aea535b98e4ffdbcd0040a26), [`c83ff382b`](https://github.com/nextui-org/nextui/commit/c83ff382b9e5deaa08ed7e64eee484cc4904704d), [`5329de42d`](https://github.com/nextui-org/nextui/commit/5329de42d2152424bfb5d5ebbf08a68d557f147f), [`6bbd234aa`](https://github.com/nextui-org/nextui/commit/6bbd234aa23fa594a191e39265296c3be09fda7f), [`31bfaebe2`](https://github.com/nextui-org/nextui/commit/31bfaebe2c53b0a3b9d18c65db4089e6044fe9dc), [`9acf3eada`](https://github.com/nextui-org/nextui/commit/9acf3eada03af911dba42198a83e8ea4b453a93a), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`8048dcc0c`](https://github.com/nextui-org/nextui/commit/8048dcc0c37455d2574c93d6f9fa505a936aedb5), [`5f735a989`](https://github.com/nextui-org/nextui/commit/5f735a989297dd5b5b82eba84b91ddbdb33cd3c4), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a), [`2a2a0692c`](https://github.com/nextui-org/nextui/commit/2a2a0692ca81ea575d2328d933d775ccbd43ac1c), [`06ecd213c`](https://github.com/nextui-org/nextui/commit/06ecd213cf85db2dfaa5fc26c1fed62dcb5fbc85), [`5f5ad7a1d`](https://github.com/nextui-org/nextui/commit/5f5ad7a1ddae1fb30be4a25e6aad4f87666ee1ce), [`9d63259ee`](https://github.com/nextui-org/nextui/commit/9d63259eeacfe8b8714629e612a20e00293f0a43), [`20ba81948`](https://github.com/nextui-org/nextui/commit/20ba81948d86ccc7ea4269cceb06e04899903b0e), [`0108d06d3`](https://github.com/nextui-org/nextui/commit/0108d06d3b4167b28d4be03813d883d9d4d7d9f6), [`8988981c5`](https://github.com/nextui-org/nextui/commit/8988981c532b35577b0efa3a68496499973acb33), [`648edad77`](https://github.com/nextui-org/nextui/commit/648edad77ea6265baecbc8cd7d1caee5a983da7c)]: + - @nextui-org/autocomplete@2.1.0 + - @nextui-org/calendar@2.0.5 + - @nextui-org/date-input@2.1.0 + - @nextui-org/date-picker@2.1.0 + - @nextui-org/theme@2.2.4 + - @nextui-org/system@2.2.0 + - @nextui-org/modal@2.0.34 + - @nextui-org/popover@2.1.22 + - @nextui-org/dropdown@2.1.24 + - @nextui-org/select@2.2.0 + - @nextui-org/listbox@2.1.20 + - @nextui-org/menu@2.0.23 + - @nextui-org/tabs@2.0.30 + - @nextui-org/accordion@2.0.33 + - @nextui-org/slider@2.2.10 + - @nextui-org/switch@2.0.29 + - @nextui-org/avatar@2.0.28 + - @nextui-org/badge@2.0.28 + - @nextui-org/breadcrumbs@2.0.8 + - @nextui-org/button@2.0.32 + - @nextui-org/card@2.0.29 + - @nextui-org/checkbox@2.1.0 + - @nextui-org/image@2.0.28 + - @nextui-org/input@2.2.0 + - @nextui-org/link@2.0.30 + - @nextui-org/navbar@2.0.31 + - @nextui-org/pagination@2.0.31 + - @nextui-org/progress@2.0.29 + - @nextui-org/radio@2.1.0 + - @nextui-org/ripple@2.0.29 + - @nextui-org/skeleton@2.0.28 + - @nextui-org/snippet@2.0.36 + - @nextui-org/table@2.0.34 + - @nextui-org/tooltip@2.0.34 + - @nextui-org/framer-utils@2.0.19 + - @nextui-org/chip@2.0.28 + - @nextui-org/code@2.0.28 + - @nextui-org/divider@2.0.28 + - @nextui-org/kbd@2.0.29 + - @nextui-org/scroll-shadow@2.1.16 + - @nextui-org/spacer@2.0.28 + - @nextui-org/spinner@2.0.29 + - @nextui-org/user@2.0.29 + ## 2.3.6 ### Patch Changes diff --git a/packages/core/react/README.md b/packages/core/react/README.md index c0faf61916..0d4a2bb4e3 100644 --- a/packages/core/react/README.md +++ b/packages/core/react/README.md @@ -36,7 +36,18 @@ Visit
https://nextu Visit [https://nextui.org/docs](https://nextui.org/docs) to view the full documentation. -### Community +## Storybook + +Visit [https://storybook.nextui.org](https://storybook.nextui.org/) to view the storybook for all components. + +## Canary Release + +Canary versions are available after every merge into `canary` branch. You can install the packages with the tag `canary` in npm to use the latest changes before the next production release. + +- [Documentation](https://canary.nextui.org/docs) +- [Storybook](https://canary-storybook.nextui.org) + +## Community We're excited to see the community adopt NextUI, raise issues, and provide feedback. Whether it's a feature request, bug report, or a project to showcase, please get involved! diff --git a/packages/core/react/package.json b/packages/core/react/package.json index 6b62819492..4cf929f16e 100644 --- a/packages/core/react/package.json +++ b/packages/core/react/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/react", - "version": "2.3.6", + "version": "2.4.0", "description": "🚀 Beautiful and modern React UI library.", "author": "Junior Garcia ", "homepage": "https://nextui.org", @@ -84,12 +84,12 @@ "@nextui-org/date-input": "workspace:*", "@nextui-org/date-picker": "workspace:*", "@nextui-org/framer-utils": "workspace:*", - "@react-aria/visually-hidden": "^3.8.10" + "@react-aria/visually-hidden": "3.8.10" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0" + "framer-motion": ">=10.17.0" }, "devDependencies": { "react": "^18.0.0", @@ -97,4 +97,4 @@ "clean-package": "2.2.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/core/system-rsc/CHANGELOG.md b/packages/core/system-rsc/CHANGELOG.md index ad48259aa8..4b851de3a7 100644 --- a/packages/core/system-rsc/CHANGELOG.md +++ b/packages/core/system-rsc/CHANGELOG.md @@ -1,5 +1,13 @@ # @nextui-org/system-rsc +## 2.1.2 + +### Patch Changes + +- [#2915](https://github.com/nextui-org/nextui/pull/2915) [`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - The `cn` utility was moved the `theme` package and updated to support NextUI custom classes. + +- [#3018](https://github.com/nextui-org/nextui/pull/3018) [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9) Thanks [@wingkwong](https://github.com/wingkwong)! - fix incorrect tailwind classnames + ## 2.1.1 ### Patch Changes diff --git a/packages/core/system-rsc/package.json b/packages/core/system-rsc/package.json index f95e1f4dcd..85c2cbfde7 100644 --- a/packages/core/system-rsc/package.json +++ b/packages/core/system-rsc/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/system-rsc", - "version": "2.1.1", + "version": "2.1.2", "description": "NextUI system primitives compatibles with RSC imports", "keywords": [ "system-rsc" @@ -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", @@ -58,4 +57,4 @@ "esm" ] } -} +} \ No newline at end of file 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..99fc967faa 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: { @@ -14,7 +15,7 @@ const card = tv({ "flex-col", "relative", "overflow-hidden", - "height-auto", + "h-auto", "outline-none", "text-foreground", "box-border", @@ -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/CHANGELOG.md b/packages/core/system/CHANGELOG.md index 544ab3dd0d..6ad35226c1 100644 --- a/packages/core/system/CHANGELOG.md +++ b/packages/core/system/CHANGELOG.md @@ -1,5 +1,20 @@ # @nextui-org/system +## 2.2.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2915](https://github.com/nextui-org/nextui/pull/2915) [`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - The `cn` utility was moved the `theme` package and updated to support NextUI custom classes. + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9)]: + - @nextui-org/system-rsc@2.1.2 + ## 2.1.2 ### Patch Changes diff --git a/packages/core/system/package.json b/packages/core/system/package.json index 1b6a16870b..78b6682136 100644 --- a/packages/core/system/package.json +++ b/packages/core/system/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/system", - "version": "2.1.2", + "version": "2.2.0", "description": "NextUI system primitives", "keywords": [ "system" @@ -35,12 +35,14 @@ }, "peerDependencies": { "react": ">=18", - "react-dom": ">=18" + "react-dom": ">=18", + "framer-motion": ">=10.17.0" }, "devDependencies": { - "clean-package": "2.2.0", "react": "^18.0.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "clean-package": "2.2.0", + "framer-motion": "^11.0.22" }, "clean-package": "../../../clean-package.config.json", "tsup": { @@ -54,10 +56,10 @@ "dependencies": { "@nextui-org/system-rsc": "workspace:*", "@nextui-org/react-utils": "workspace:*", - "@react-aria/i18n": "^3.10.2", + "@react-aria/i18n": "3.10.2", "@internationalized/date": "^3.5.2", - "@react-aria/overlays": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/utils": "^3.9.1" + "@react-aria/overlays": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-stately/utils": "3.9.1" } -} +} \ No newline at end of file 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/system/src/provider-context.ts b/packages/core/system/src/provider-context.ts index 39b6c60905..575b545999 100644 --- a/packages/core/system/src/provider-context.ts +++ b/packages/core/system/src/provider-context.ts @@ -4,6 +4,28 @@ import type {CalendarDate, Calendar} from "@internationalized/date"; import {createContext} from "@nextui-org/react-utils"; export type ProviderContextProps = { + /** + * Whether to disable animations in the whole application. + * + * @default false + */ + disableAnimation?: boolean; + /** + * Whether to disable the ripple effect in the whole application. + * If `disableAnimation` is set to `true`, this prop will be ignored. + * + * @default false + */ + disableRipple?: boolean; + /** + * Whether to use native HTML form validation to prevent form submission + * when the value is missing or invalid, or mark the field as required + * or invalid via ARIA. + * @see https://react-spectrum.adobe.com/react-aria/forms.html + * + * @default "aria" + */ + validationBehavior?: "aria" | "native"; /** * The default dates range that can be selected in the calendar. */ @@ -12,7 +34,7 @@ export type ProviderContextProps = { * The minimum date that can be selected in the calendar. * @see https://react-spectrum.adobe.com/internationalized/date/CalendarDate.html * - * @default new CalendarDate(1900, 1, 1) + * @default CalendarDate(1900, 1, 1) * */ minDate?: CalendarDate; diff --git a/packages/core/system/src/provider.tsx b/packages/core/system/src/provider.tsx index d4931c23ea..fa648c1a42 100644 --- a/packages/core/system/src/provider.tsx +++ b/packages/core/system/src/provider.tsx @@ -6,6 +6,7 @@ import {RouterProvider} from "@react-aria/utils"; import {OverlayProvider} from "@react-aria/overlays"; import {useMemo} from "react"; import {CalendarDate} from "@internationalized/date"; +import {MotionGlobalConfig} from "framer-motion"; import {ProviderContext} from "./provider-context"; @@ -13,6 +14,14 @@ export interface NextUIProviderProps extends Omit, ProviderContextProps { children: React.ReactNode; + /** + * Controls whether `framer-motion` animations are skipped within the application. + * This property is automatically enabled (`true`) when the `disableAnimation` prop is set to `true`, + * effectively skipping all `framer-motion` animations. To retain `framer-motion` animations while + * using the `disableAnimation` prop for other purposes, set this to `false`. However, note that + * animations in NextUI Components are still omitted if the `disableAnimation` prop is `true`. + */ + skipFramerMotionAnimations?: boolean; /** * The locale to apply to the children. * @default "en-US" @@ -28,6 +37,10 @@ export interface NextUIProviderProps export const NextUIProvider: React.FC = ({ children, navigate, + disableAnimation = false, + disableRipple = false, + skipFramerMotionAnimations = disableAnimation, + validationBehavior = "aria", locale = "en-US", defaultDates = { minDate: new CalendarDate(1900, 1, 1), @@ -42,10 +55,26 @@ export const NextUIProvider: React.FC = ({ contents = {contents}; } - const context = useMemo( - () => ({createCalendar, defaultDates}), - [createCalendar, defaultDates?.maxDate, defaultDates?.minDate], - ); + const context = useMemo(() => { + if (disableAnimation && skipFramerMotionAnimations) { + MotionGlobalConfig.skipAnimations = true; + } + + return { + createCalendar, + defaultDates, + disableAnimation, + disableRipple, + validationBehavior, + }; + }, [ + createCalendar, + defaultDates?.maxDate, + defaultDates?.minDate, + disableAnimation, + disableRipple, + validationBehavior, + ]); return ( diff --git a/packages/core/theme/CHANGELOG.md b/packages/core/theme/CHANGELOG.md index dcc6469d69..1dca568c2d 100644 --- a/packages/core/theme/CHANGELOG.md +++ b/packages/core/theme/CHANGELOG.md @@ -1,5 +1,33 @@ # @nextui-org/theme +## 2.2.4 + +### Patch Changes + +- [#2883](https://github.com/nextui-org/nextui/pull/2883) [`76f4dd8e7`](https://github.com/nextui-org/nextui/commit/76f4dd8e76d4c0047953fc8a5b8b65614d7375f0) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise slider styles (#2880) + +- [#2915](https://github.com/nextui-org/nextui/pull/2915) [`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - The `cn` utility was moved the `theme` package and updated to support NextUI custom classes. + +- [#3010](https://github.com/nextui-org/nextui/pull/3010) [`77e85e665`](https://github.com/nextui-org/nextui/commit/77e85e665c94a188db46ec08c61c872e5d8e7d8c) Thanks [@wingkwong](https://github.com/wingkwong)! - revise isInvalid input styles (#3007) + +- [#3018](https://github.com/nextui-org/nextui/pull/3018) [`1109baea6`](https://github.com/nextui-org/nextui/commit/1109baea6ac6aa3feb2be90ef065f61b2c2a06a9) Thanks [@wingkwong](https://github.com/wingkwong)! - fix incorrect tailwind classnames + +- [#2990](https://github.com/nextui-org/nextui/pull/2990) [`ad08010fe`](https://github.com/nextui-org/nextui/commit/ad08010fe58438c8d8f264ee1671b53d6638a724) Thanks [@wingkwong](https://github.com/wingkwong)! - remove unnecessary origin-bottom in button + +- [#2973](https://github.com/nextui-org/nextui/pull/2973) [`e34c5e307`](https://github.com/nextui-org/nextui/commit/e34c5e307d1dfa2747e7fd77752983ad30a7e3eb) Thanks [@wingkwong](https://github.com/wingkwong)! - Add `destroyInactiveTabPanel` prop for Tabs component (#1562) + +- [#2906](https://github.com/nextui-org/nextui/pull/2906) [`c83ff382b`](https://github.com/nextui-org/nextui/commit/c83ff382b9e5deaa08ed7e64eee484cc4904704d) Thanks [@ShrinidhiUpadhyaya](https://github.com/ShrinidhiUpadhyaya)! - Fixed hiding of unavailable dates in RangeCalendar (#2890) + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- [#2972](https://github.com/nextui-org/nextui/pull/2972) [`5f735a989`](https://github.com/nextui-org/nextui/commit/5f735a989297dd5b5b82eba84b91ddbdb33cd3c4) Thanks [@wingkwong](https://github.com/wingkwong)! - add pointer-events-none to skeleton base (#1714) + +- [#2949](https://github.com/nextui-org/nextui/pull/2949) [`0108d06d3`](https://github.com/nextui-org/nextui/commit/0108d06d3b4167b28d4be03813d883d9d4d7d9f6) Thanks [@novsource](https://github.com/novsource)! - Removed scrolling display during month change animation (#2945) + +- [#2986](https://github.com/nextui-org/nextui/pull/2986) [`8988981c5`](https://github.com/nextui-org/nextui/commit/8988981c532b35577b0efa3a68496499973acb33) Thanks [@wingkwong](https://github.com/wingkwong)! - set overflow visible after skeleton loaded (#2125) + +- [#2781](https://github.com/nextui-org/nextui/pull/2781) [`648edad77`](https://github.com/nextui-org/nextui/commit/648edad77ea6265baecbc8cd7d1caee5a983da7c) Thanks [@mrbadri](https://github.com/mrbadri)! - Fixed incorrect margin on labels for RTL required inputs. (#2780) + ## 2.2.3 ### Patch Changes diff --git a/packages/core/theme/package.json b/packages/core/theme/package.json index c020814a37..737a1c7c10 100644 --- a/packages/core/theme/package.json +++ b/packages/core/theme/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/theme", - "version": "2.2.3", + "version": "2.2.4", "description": "The default theme for NextUI components", "keywords": [ "theme", @@ -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" @@ -79,4 +81,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/core/theme/src/components/accordion.ts b/packages/core/theme/src/components/accordion.ts index 7c3b25425c..8a3e4ad1a3 100644 --- a/packages/core/theme/src/components/accordion.ts +++ b/packages/core/theme/src/components/accordion.ts @@ -119,7 +119,6 @@ const accordionItem = tv({ radius: "lg", isDisabled: false, hideIndicator: false, - disableAnimation: false, disableIndicatorAnimation: false, }, }); diff --git a/packages/core/theme/src/components/autocomplete.ts b/packages/core/theme/src/components/autocomplete.ts index bc0fe92a14..87ebe45e2d 100644 --- a/packages/core/theme/src/components/autocomplete.ts +++ b/packages/core/theme/src/components/autocomplete.ts @@ -46,7 +46,6 @@ const autocomplete = tv({ }, }, defaultVariants: { - disableAnimation: false, isClearable: true, disableSelectorIconRotation: false, }, diff --git a/packages/core/theme/src/components/avatar.ts b/packages/core/theme/src/components/avatar.ts index 16bf99f6f8..d69fc1686c 100644 --- a/packages/core/theme/src/components/avatar.ts +++ b/packages/core/theme/src/components/avatar.ts @@ -124,6 +124,13 @@ const avatar = tv({ base: "m-0 data-[hover=true]:translate-x-0", }, }, + disableAnimation: { + true: { + base: "transition-none", + img: "transition-none", + }, + false: {}, + }, }, defaultVariants: { size: "md", @@ -188,7 +195,7 @@ const avatar = tv({ */ const avatarGroup = tv({ slots: { - base: "flex items-center justify-center h-auto w-max-content", + base: "flex items-center justify-center h-auto w-max", count: "hover:-translate-x-0", }, variants: { diff --git a/packages/core/theme/src/components/badge.ts b/packages/core/theme/src/components/badge.ts index 6d8d56252a..cbe04a515a 100644 --- a/packages/core/theme/src/components/badge.ts +++ b/packages/core/theme/src/components/badge.ts @@ -111,7 +111,6 @@ const badge = tv({ shape: "rectangle", placement: "top-right", showOutline: true, - disableAnimation: false, isInvisible: false, }, compoundVariants: [ diff --git a/packages/core/theme/src/components/breadcrumbs.ts b/packages/core/theme/src/components/breadcrumbs.ts index c923e793ea..b5b2e902b1 100644 --- a/packages/core/theme/src/components/breadcrumbs.ts +++ b/packages/core/theme/src/components/breadcrumbs.ts @@ -108,7 +108,6 @@ const breadcrumbItem = tv({ color: "foreground", underline: "hover", isDisabled: false, - disableAnimation: false, }, compoundVariants: [ // isCurrent && color diff --git a/packages/core/theme/src/components/button.ts b/packages/core/theme/src/components/button.ts index bb5a2f1f01..ea7f48a041 100644 --- a/packages/core/theme/src/components/button.ts +++ b/packages/core/theme/src/components/button.ts @@ -37,6 +37,7 @@ const button = tv({ "subpixel-antialiased", "overflow-hidden", "tap-highlight-transparent", + "data-[pressed=true]:scale-[0.97]", // focus ring ...dataFocusVisibleClasses, ], @@ -85,8 +86,7 @@ const button = tv({ }, disableAnimation: { true: "!transition-none", - false: - "data-[pressed=true]:scale-[0.97] transition-transform-colors-opacity motion-reduce:transition-none", + false: "transition-transform-colors-opacity motion-reduce:transition-none", }, }, defaultVariants: { @@ -96,7 +96,6 @@ const button = tv({ fullWidth: false, isDisabled: false, isInGroup: false, - disableAnimation: false, }, compoundVariants: [ // solid / color diff --git a/packages/core/theme/src/components/calendar.ts b/packages/core/theme/src/components/calendar.ts index 5ebf37514f..115915f7cb 100644 --- a/packages/core/theme/src/components/calendar.ts +++ b/packages/core/theme/src/components/calendar.ts @@ -13,7 +13,7 @@ const calendar = tv({ prevButton: [], nextButton: [], headerWrapper: [ - "px-4 py-2 flex items-center justify-between gap-2 bg-content1", + "px-4 py-2 flex items-center justify-between gap-2 bg-content1 overflow-hidden", "[&_.chevron-icon]:flex-none", // month/year picker wrapper "after:content-['']", @@ -106,7 +106,7 @@ const calendar = tv({ }, hideDisabledDates: { true: { - cellButton: "data-[disabled=true]:opacity-0", + cellButton: "data-[disabled=true]:data-[outside-month=true]:opacity-0", }, false: {}, }, @@ -154,7 +154,6 @@ const calendar = tv({ showShadow: false, hideDisabledDates: false, showMonthAndYearPickers: false, - disableAnimation: false, }, compoundVariants: [ // !isRange & colors --> Calendar diff --git a/packages/core/theme/src/components/card.ts b/packages/core/theme/src/components/card.ts index 8a25416de9..04b86032a1 100644 --- a/packages/core/theme/src/components/card.ts +++ b/packages/core/theme/src/components/card.ts @@ -24,7 +24,7 @@ const card = tv({ "flex-col", "relative", "overflow-hidden", - "height-auto", + "h-auto", "outline-none", "text-foreground", "box-border", @@ -149,7 +149,6 @@ const card = tv({ compoundVariants: [ { isPressable: true, - disableAnimation: false, class: "data-[pressed=true]:scale-[0.97] tap-highlight-transparent", }, ], @@ -160,7 +159,6 @@ const card = tv({ isHoverable: false, isPressable: false, isDisabled: false, - disableAnimation: false, isFooterBlurred: false, }, }); diff --git a/packages/core/theme/src/components/checkbox.ts b/packages/core/theme/src/components/checkbox.ts index 672b65c3c7..ef1d80b915 100644 --- a/packages/core/theme/src/components/checkbox.ts +++ b/packages/core/theme/src/components/checkbox.ts @@ -189,7 +189,6 @@ const checkbox = tv({ size: "md", isDisabled: false, lineThrough: false, - disableAnimation: false, }, }); @@ -235,7 +234,6 @@ const checkboxGroup = tv({ defaultVariants: { isInvalid: false, isRequired: false, - disableAnimation: false, }, }); diff --git a/packages/core/theme/src/components/circular-progress.ts b/packages/core/theme/src/components/circular-progress.ts index 9ea622d329..604a4f8bbe 100644 --- a/packages/core/theme/src/components/circular-progress.ts +++ b/packages/core/theme/src/components/circular-progress.ts @@ -90,7 +90,6 @@ const circularProgress = tv({ color: "primary", size: "md", isDisabled: false, - disableAnimation: false, }, compoundVariants: [ // disableAnimation && !isIndeterminate diff --git a/packages/core/theme/src/components/date-input.ts b/packages/core/theme/src/components/date-input.ts index 6ebf0c0bef..1036491eec 100644 --- a/packages/core/theme/src/components/date-input.ts +++ b/packages/core/theme/src/components/date-input.ts @@ -218,7 +218,6 @@ const dateInput = tv({ fullWidth: true, labelPlacement: "inside", isDisabled: false, - disableAnimation: false, }, compoundVariants: [ // flat & color diff --git a/packages/core/theme/src/components/dropdown.ts b/packages/core/theme/src/components/dropdown.ts index ccf2fc8eee..77945ef744 100644 --- a/packages/core/theme/src/components/dropdown.ts +++ b/packages/core/theme/src/components/dropdown.ts @@ -124,7 +124,6 @@ const dropdownItem = tv({ defaultVariants: { variant: "solid", color: "default", - disableAnimation: false, }, compoundVariants: [ // solid / color diff --git a/packages/core/theme/src/components/image.ts b/packages/core/theme/src/components/image.ts index b54bcb5eb4..dcfba8c320 100644 --- a/packages/core/theme/src/components/image.ts +++ b/packages/core/theme/src/components/image.ts @@ -71,11 +71,32 @@ const image = tv({ }, showSkeleton: { true: { + wrapper: ["group", "relative", "overflow-hidden", "bg-content3 dark:bg-content2"], + img: "opacity-0", + }, + }, + disableAnimation: { + true: { + img: "transition-none", + }, + false: { + img: "transition-transform-opacity motion-reduce:transition-none !duration-300", + }, + }, + }, + defaultVariants: { + radius: "lg", + shadow: "none", + isZoomed: false, + isBlurred: false, + showSkeleton: false, + }, + compoundVariants: [ + { + showSkeleton: true, + disableAnimation: false, + class: { wrapper: [ - "group", - "relative", - "overflow-hidden", - "bg-content3 dark:bg-content2", // before "before:opacity-100", "before:absolute", @@ -97,26 +118,9 @@ const image = tv({ "after:bg-content3", "dark:after:bg-content2", ], - img: "opacity-0", - }, - }, - disableAnimation: { - true: { - img: "transition-none", - }, - false: { - img: "transition-transform-opacity motion-reduce:transition-none !duration-300", }, }, - }, - defaultVariants: { - radius: "lg", - shadow: "none", - isZoomed: false, - isBlurred: false, - showSkeleton: false, - disableAnimation: false, - }, + ], compoundSlots: [ { slots: ["wrapper", "img", "blurredImg", "zoomedWrapper"], diff --git a/packages/core/theme/src/components/input.ts b/packages/core/theme/src/components/input.ts index 7ac7b19e82..889f9a76c3 100644 --- a/packages/core/theme/src/components/input.ts +++ b/packages/core/theme/src/components/input.ts @@ -206,7 +206,8 @@ const input = tv({ }, isRequired: { true: { - label: "after:content-['*'] after:text-danger after:ml-0.5", + label: + "after:content-['*'] after:text-danger after:ml-0.5 rtl:after:ml-[unset] rtl:after:mr-0.5", }, }, isMultiline: { @@ -244,7 +245,6 @@ const input = tv({ labelPlacement: "inside", isDisabled: false, isMultiline: false, - disableAnimation: false, }, compoundVariants: [ // flat & color @@ -535,9 +535,9 @@ const input = tv({ variant: "flat", class: { inputWrapper: [ - "bg-danger-50", - "data-[hover=true]:bg-danger-100", - "group-data-[focus=true]:bg-danger-50", + "!bg-danger-50", + "data-[hover=true]:!bg-danger-100", + "group-data-[focus=true]:!bg-danger-50", ], }, }, @@ -545,14 +545,14 @@ const input = tv({ isInvalid: true, variant: "bordered", class: { - inputWrapper: "!border-danger group-data-[focus=true]:border-danger", + inputWrapper: "!border-danger group-data-[focus=true]:!border-danger", }, }, { isInvalid: true, variant: "underlined", class: { - inputWrapper: "after:bg-danger", + inputWrapper: "after:!bg-danger", }, }, // size & labelPlacement diff --git a/packages/core/theme/src/components/link.ts b/packages/core/theme/src/components/link.ts index 680c9319c7..69f16a5f27 100644 --- a/packages/core/theme/src/components/link.ts +++ b/packages/core/theme/src/components/link.ts @@ -101,7 +101,6 @@ const link = tv({ isBlock: false, underline: "none", isDisabled: false, - disableAnimation: false, }, }); diff --git a/packages/core/theme/src/components/menu.ts b/packages/core/theme/src/components/menu.ts index 65bac23b2a..96dba231dc 100644 --- a/packages/core/theme/src/components/menu.ts +++ b/packages/core/theme/src/components/menu.ts @@ -148,7 +148,6 @@ const menuItem = tv({ defaultVariants: { variant: "solid", color: "default", - disableAnimation: false, showDivider: false, }, compoundVariants: [ diff --git a/packages/core/theme/src/components/modal.ts b/packages/core/theme/src/components/modal.ts index f4869c038f..94c74be5c4 100644 --- a/packages/core/theme/src/components/modal.ts +++ b/packages/core/theme/src/components/modal.ts @@ -32,16 +32,6 @@ const modal = tv({ "z-50", "overflow-x-auto", "justify-center", - // mobile animation vars - "[--scale-enter:100%]", - "[--scale-exit:100%]", - "[--slide-enter:0px]", - "[--slide-exit:80px]", - // tablet/desktop animation vars - "sm:[--scale-enter:100%]", - "sm:[--scale-exit:103%]", - "sm:[--slide-enter:0px]", - "sm:[--slide-exit:0px]", ], base: [ "flex", @@ -175,6 +165,22 @@ const modal = tv({ base: "my-16", }, }, + disableAnimation: { + false: { + wrapper: [ + // mobile animation vars + "[--scale-enter:100%]", + "[--scale-exit:100%]", + "[--slide-enter:0px]", + "[--slide-exit:80px]", + // tablet/desktop animation vars + "sm:[--scale-enter:100%]", + "sm:[--scale-exit:103%]", + "sm:[--slide-enter:0px]", + "sm:[--slide-exit:0px]", + ], + }, + }, }, defaultVariants: { size: "md", diff --git a/packages/core/theme/src/components/pagination.ts b/packages/core/theme/src/components/pagination.ts index 644e6ddccf..50fb6c0653 100644 --- a/packages/core/theme/src/components/pagination.ts +++ b/packages/core/theme/src/components/pagination.ts @@ -154,7 +154,6 @@ const pagination = tv({ isCompact: false, isDisabled: false, showShadow: false, - disableAnimation: false, disableCursorAnimation: false, }, compoundVariants: [ diff --git a/packages/core/theme/src/components/popover.ts b/packages/core/theme/src/components/popover.ts index 5ebbdb90a1..f09f76e83e 100644 --- a/packages/core/theme/src/components/popover.ts +++ b/packages/core/theme/src/components/popover.ts @@ -175,7 +175,6 @@ const popover = tv({ size: "md", shadow: "md", backdrop: "transparent", - disableAnimation: false, triggerScaleOnOpen: true, }, compoundVariants: [ diff --git a/packages/core/theme/src/components/progress.ts b/packages/core/theme/src/components/progress.ts index 347d0cfad1..cef7fb35ff 100644 --- a/packages/core/theme/src/components/progress.ts +++ b/packages/core/theme/src/components/progress.ts @@ -119,7 +119,6 @@ const progress = tv( isStriped: false, isIndeterminate: false, isDisabled: false, - disableAnimation: false, }, compoundVariants: [ // disableAnimation && !isIndeterminate diff --git a/packages/core/theme/src/components/radio.ts b/packages/core/theme/src/components/radio.ts index 305b28263f..3ac379b212 100644 --- a/packages/core/theme/src/components/radio.ts +++ b/packages/core/theme/src/components/radio.ts @@ -141,7 +141,6 @@ const radio = tv({ size: "md", isDisabled: false, isInvalid: false, - disableAnimation: false, }, }); @@ -187,7 +186,6 @@ const radioGroup = tv({ defaultVariants: { isInvalid: false, isRequired: false, - disableAnimation: false, }, }); diff --git a/packages/core/theme/src/components/select.ts b/packages/core/theme/src/components/select.ts index f9e11f42b8..24a95b3dc0 100644 --- a/packages/core/theme/src/components/select.ts +++ b/packages/core/theme/src/components/select.ts @@ -208,7 +208,6 @@ const select = tv({ fullWidth: true, isDisabled: false, isMultiline: false, - disableAnimation: false, disableSelectorIconRotation: false, }, compoundVariants: [ diff --git a/packages/core/theme/src/components/skeleton.ts b/packages/core/theme/src/components/skeleton.ts index e5a0825a7c..f2340af0e7 100644 --- a/packages/core/theme/src/components/skeleton.ts +++ b/packages/core/theme/src/components/skeleton.ts @@ -19,6 +19,7 @@ const skeleton = tv({ "relative", "overflow-hidden", "bg-content3 dark:bg-content2", + "pointer-events-none", // before "before:opacity-100", "before:absolute", @@ -40,6 +41,7 @@ const skeleton = tv({ "after:bg-content3", "dark:after:bg-content2", // state + "data-[loaded=true]:overflow-visible", "data-[loaded=true]:!bg-transparent", "data-[loaded=true]:before:opacity-0 data-[loaded=true]:before:animate-none", "data-[loaded=true]:after:opacity-0", @@ -58,9 +60,7 @@ const skeleton = tv({ }, }, }, - defaultVariants: { - disableAnimation: false, - }, + defaultVariants: {}, }); export type SkeletonVariantProps = VariantProps; diff --git a/packages/core/theme/src/components/slider.ts b/packages/core/theme/src/components/slider.ts index e21b870a29..3811564678 100644 --- a/packages/core/theme/src/components/slider.ts +++ b/packages/core/theme/src/components/slider.ts @@ -166,6 +166,7 @@ const slider = tv({ hasMarks: { true: { base: "mb-5", + mark: "cursor-pointer", }, false: {}, }, @@ -185,7 +186,7 @@ const slider = tv({ hideThumb: { true: { thumb: "sr-only", - track: "overflow-hidden cursor-pointer", + track: "cursor-pointer", }, }, hasSingleThumb: { @@ -266,7 +267,7 @@ const slider = tv({ isVertical: false, class: { track: - "h-1 my-[calc((theme(spacing.5)-theme(spacing.1))/2)] data-[thumb-hidden=false]:border-x-[calc(theme(spacing.5)/2)]", + "h-1 my-[calc((theme(spacing.5)-theme(spacing.1))/2)] border-x-[calc(theme(spacing.5)/2)]", }, }, { @@ -274,7 +275,7 @@ const slider = tv({ isVertical: false, class: { track: - "h-3 my-[calc((theme(spacing.6)-theme(spacing.3))/2)] data-[thumb-hidden=false]:border-x-[calc(theme(spacing.6)/2)]", + "h-3 my-[calc((theme(spacing.6)-theme(spacing.3))/2)] border-x-[calc(theme(spacing.6)/2)]", }, }, { @@ -282,7 +283,7 @@ const slider = tv({ isVertical: false, class: { track: - "h-7 my-[calc((theme(spacing.7)-theme(spacing.5))/2)] data-[thumb-hidden=false]:border-x-[calc(theme(spacing.7)/2)]", + "h-7 my-[calc((theme(spacing.7)-theme(spacing.5))/2)] border-x-[calc(theme(spacing.7)/2)]", }, }, // size && isVertical @@ -291,7 +292,7 @@ const slider = tv({ isVertical: true, class: { track: - "w-1 mx-[calc((theme(spacing.5)-theme(spacing.1))/2)] data-[thumb-hidden=false]:border-y-[calc(theme(spacing.5)/2)]", + "w-1 mx-[calc((theme(spacing.5)-theme(spacing.1))/2)] border-y-[calc(theme(spacing.5)/2)]", }, }, { @@ -299,7 +300,7 @@ const slider = tv({ isVertical: true, class: { track: - "w-3 mx-[calc((theme(spacing.6)-theme(spacing.3))/2)] data-[thumb-hidden=false]:border-y-[calc(theme(spacing.6)/2)]", + "w-3 mx-[calc((theme(spacing.6)-theme(spacing.3))/2)] border-y-[calc(theme(spacing.6)/2)]", }, }, { @@ -307,7 +308,7 @@ const slider = tv({ isVertical: true, class: { track: - "w-7 mx-[calc((theme(spacing.7)-theme(spacing.5))/2)] data-[thumb-hidden=false]:border-y-[calc(theme(spacing.7)/2)]", + "w-7 mx-[calc((theme(spacing.7)-theme(spacing.5))/2)] border-y-[calc(theme(spacing.7)/2)]", }, }, // color && !isVertical && hasSingleThumb @@ -417,7 +418,6 @@ const slider = tv({ hideThumb: false, isDisabled: false, disableThumbScale: false, - disableAnimation: false, showOutline: false, }, }); diff --git a/packages/core/theme/src/components/snippet.ts b/packages/core/theme/src/components/snippet.ts index 53142f9f9c..f3e3cc91e7 100644 --- a/packages/core/theme/src/components/snippet.ts +++ b/packages/core/theme/src/components/snippet.ts @@ -100,7 +100,6 @@ const snippet = tv({ variant: "flat", size: "md", fullWidth: false, - disableAnimation: false, }, compoundVariants: [ // solid - shadow / color diff --git a/packages/core/theme/src/components/table.ts b/packages/core/theme/src/components/table.ts index 3fa5b8552b..6c990a4ebf 100644 --- a/packages/core/theme/src/components/table.ts +++ b/packages/core/theme/src/components/table.ts @@ -259,7 +259,6 @@ const table = tv({ hideHeader: false, isStriped: false, fullWidth: true, - disableAnimation: false, }, }); diff --git a/packages/core/theme/src/components/tabs.ts b/packages/core/theme/src/components/tabs.ts index 3e126deb7f..70432a33c6 100644 --- a/packages/core/theme/src/components/tabs.ts +++ b/packages/core/theme/src/components/tabs.ts @@ -68,6 +68,7 @@ const tabs = tv({ "py-3", "px-1", "outline-none", + "data-[inert=true]:hidden", // focus ring ...dataFocusVisibleClasses, ], @@ -183,7 +184,6 @@ const tabs = tv({ size: "md", fullWidth: false, isDisabled: false, - disableAnimation: false, }, compoundVariants: [ /** diff --git a/packages/core/theme/src/components/toggle.ts b/packages/core/theme/src/components/toggle.ts index e2f9e1ecd5..1afa716cc1 100644 --- a/packages/core/theme/src/components/toggle.ts +++ b/packages/core/theme/src/components/toggle.ts @@ -166,7 +166,6 @@ const toggle = tv({ color: "primary", size: "md", isDisabled: false, - disableAnimation: false, }, compoundVariants: [ { 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/packages/hooks/use-aria-accordion-item/package.json b/packages/hooks/use-aria-accordion-item/package.json index 752b071c8f..2ff05e9ce1 100644 --- a/packages/hooks/use-aria-accordion-item/package.json +++ b/packages/hooks/use-aria-accordion-item/package.json @@ -34,10 +34,10 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/button": "^3.9.3", - "@react-aria/focus": "^3.16.2", - "@react-stately/tree": "^3.7.6", - "@react-types/shared": "^3.22.1" + "@react-aria/button": "3.9.3", + "@react-aria/focus": "3.16.2", + "@react-stately/tree": "3.7.6", + "@react-types/shared": "3.22.1" }, "peerDependencies": { "react": ">=18" @@ -55,4 +55,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-accordion/package.json b/packages/hooks/use-aria-accordion/package.json index a3d3db5cf3..ab73b0bc8d 100644 --- a/packages/hooks/use-aria-accordion/package.json +++ b/packages/hooks/use-aria-accordion/package.json @@ -34,13 +34,13 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/button": "^3.9.3", - "@react-aria/focus": "^3.16.2", - "@react-aria/selection": "^3.17.5", - "@react-aria/utils": "^3.23.2", - "@react-stately/tree": "^3.7.6", + "@react-aria/button": "3.9.3", + "@react-aria/focus": "3.16.2", + "@react-aria/selection": "3.17.5", + "@react-aria/utils": "3.23.2", + "@react-stately/tree": "3.7.6", "@react-types/accordion": "3.0.0-alpha.19", - "@react-types/shared": "^3.22.1" + "@react-types/shared": "3.22.1" }, "peerDependencies": { "react": ">=18" @@ -58,4 +58,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-button/package.json b/packages/hooks/use-aria-button/package.json index aaa28ca8da..54b64570c5 100644 --- a/packages/hooks/use-aria-button/package.json +++ b/packages/hooks/use-aria-button/package.json @@ -37,11 +37,11 @@ "react": ">=18" }, "dependencies": { - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-types/button": "^3.9.2", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-types/button": "3.9.2", + "@react-types/shared": "3.22.1" }, "devDependencies": { "clean-package": "2.2.0", @@ -56,4 +56,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-link/package.json b/packages/hooks/use-aria-link/package.json index 6e4a67f401..a52137be76 100644 --- a/packages/hooks/use-aria-link/package.json +++ b/packages/hooks/use-aria-link/package.json @@ -37,11 +37,11 @@ "react": ">=18" }, "dependencies": { - "@react-aria/focus": "^3.16.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-types/link": "^3.5.3", - "@react-types/shared": "^3.22.1" + "@react-aria/focus": "3.16.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-types/link": "3.5.3", + "@react-types/shared": "3.22.1" }, "devDependencies": { "clean-package": "2.2.0", @@ -56,4 +56,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-menu/CHANGELOG.md b/packages/hooks/use-aria-menu/CHANGELOG.md index 429bfb76d9..f14a33deee 100644 --- a/packages/hooks/use-aria-menu/CHANGELOG.md +++ b/packages/hooks/use-aria-menu/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/use-aria-menu +## 2.0.3 + +### Patch Changes + +- [#3064](https://github.com/nextui-org/nextui/pull/3064) [`f24a97311`](https://github.com/nextui-org/nextui/commit/f24a97311ab4dd16dafb56d35fe7c6db81798129) Thanks [@Gaic4o](https://github.com/Gaic4o)! - Fixed a type error in the onKeyDown event handler for the menu component + ## 2.0.2 ### Patch Changes diff --git a/packages/hooks/use-aria-menu/package.json b/packages/hooks/use-aria-menu/package.json index b90b8ad2d9..44a5dbe0ae 100644 --- a/packages/hooks/use-aria-menu/package.json +++ b/packages/hooks/use-aria-menu/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/use-aria-menu", - "version": "2.0.2", + "version": "2.0.3", "description": "React-aria useMenu hooks with custom implementations", "keywords": [ "use-aria-menu" @@ -38,15 +38,15 @@ "react-dom": ">=18" }, "dependencies": { - "@react-aria/utils": "^3.23.2", - "@react-types/shared": "^3.22.1", - "@react-aria/menu": "^3.13.1", - "@react-aria/interactions": "^3.21.1", - "@react-stately/tree": "^3.7.6", - "@react-aria/i18n": "^3.10.2", - "@react-aria/selection": "^3.17.5", - "@react-stately/collections": "^3.10.5", - "@react-types/menu": "^3.9.7" + "@react-aria/utils": "3.23.2", + "@react-types/shared": "3.22.1", + "@react-aria/menu": "3.13.1", + "@react-aria/interactions": "3.21.1", + "@react-stately/tree": "3.7.6", + "@react-aria/i18n": "3.10.2", + "@react-aria/selection": "3.17.5", + "@react-stately/collections": "3.10.5", + "@react-types/menu": "3.9.7" }, "devDependencies": { "clean-package": "2.2.0", @@ -61,4 +61,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-menu/src/use-menu.ts b/packages/hooks/use-aria-menu/src/use-menu.ts index 2dad54f1aa..ff551c1341 100644 --- a/packages/hooks/use-aria-menu/src/use-menu.ts +++ b/packages/hooks/use-aria-menu/src/use-menu.ts @@ -2,7 +2,7 @@ import {AriaMenuProps} from "@react-types/menu"; import {DOMAttributes, Key, KeyboardDelegate, KeyboardEvents} from "@react-types/shared"; import {filterDOMProps, mergeProps} from "@react-aria/utils"; -import {RefObject} from "react"; +import {RefObject, KeyboardEvent as ReactKeyboardEvent} from "react"; import {TreeState} from "@react-stately/tree"; import {useSelectableList} from "@react-aria/selection"; @@ -46,7 +46,6 @@ export function useMenu( console.warn("An aria-label or aria-labelledby prop is required for accessibility."); } - // @ts-ignore let domProps = filterDOMProps(props, {labelable: true}); let {listProps} = useSelectableList({ ...otherProps, @@ -70,12 +69,12 @@ export function useMenu( { role: "menu", ...listProps, - // @ts-ignore - onKeyDown: (e) => { + onKeyDown: (event: ReactKeyboardEvent) => { // don't clear the menu selected keys if the user is presses escape since escape closes the menu - if (e.key !== "Escape") { - // @ts-ignore - listProps.onKeyDown(e); + if (event.key !== "Escape") { + if (listProps.onKeyDown) { + listProps.onKeyDown(event); + } } }, }, diff --git a/packages/hooks/use-aria-modal-overlay/package.json b/packages/hooks/use-aria-modal-overlay/package.json index 777ce77108..79a76a5d97 100644 --- a/packages/hooks/use-aria-modal-overlay/package.json +++ b/packages/hooks/use-aria-modal-overlay/package.json @@ -34,10 +34,10 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/overlays": "^3.21.1", - "@react-aria/utils": "^3.23.2", - "@react-stately/overlays": "^3.6.5", - "@react-types/shared": "^3.22.1" + "@react-aria/overlays": "3.21.1", + "@react-aria/utils": "3.23.2", + "@react-stately/overlays": "3.6.5", + "@react-types/shared": "3.22.1" }, "peerDependencies": { "react": ">=18", @@ -57,4 +57,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-multiselect/CHANGELOG.md b/packages/hooks/use-aria-multiselect/CHANGELOG.md index 3bdc9a7a71..327c735617 100644 --- a/packages/hooks/use-aria-multiselect/CHANGELOG.md +++ b/packages/hooks/use-aria-multiselect/CHANGELOG.md @@ -1,5 +1,17 @@ # @nextui-org/use-aria-multiselect +## 2.2.0 + +### Minor Changes + +- [#2987](https://github.com/nextui-org/nextui/pull/2987) [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Change validationBehavior from native to aria by default, with the option to change via props. + +### Patch Changes + +- [#2953](https://github.com/nextui-org/nextui/pull/2953) [`c8f792ccd`](https://github.com/nextui-org/nextui/commit/c8f792ccd78a80000e6f5b15e6f22cac947fd531) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix update type definition to prevent primitive values as items (#2938) + +- [#2937](https://github.com/nextui-org/nextui/pull/2937) [`a2133009f`](https://github.com/nextui-org/nextui/commit/a2133009f73aa728a0e5deeb9b742aa1defd4de2) Thanks [@ryo-manba](https://github.com/ryo-manba)! - Fix onSelectionChange can handle number (#2926) + ## 2.1.5 ### Patch Changes diff --git a/packages/hooks/use-aria-multiselect/package.json b/packages/hooks/use-aria-multiselect/package.json index 3c5edceb2e..6d3c4bf063 100644 --- a/packages/hooks/use-aria-multiselect/package.json +++ b/packages/hooks/use-aria-multiselect/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/use-aria-multiselect", - "version": "2.1.5", + "version": "2.2.0", "description": "Provides the behavior and accessibility implementation for a multi-select component.", "keywords": [ "use-aria-multiselect" @@ -34,20 +34,20 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/i18n": "^3.10.2", - "@react-aria/interactions": "^3.21.1", - "@react-aria/label": "^3.7.6", - "@react-aria/listbox": "^3.11.5", - "@react-aria/menu": "^3.13.1", - "@react-aria/selection": "^3.17.5", - "@react-aria/utils": "^3.23.2", - "@react-stately/form": "^3.0.1", - "@react-stately/list": "^3.10.3", - "@react-stately/menu": "^3.6.1", - "@react-types/button": "^3.9.2", - "@react-types/overlays": "^3.8.5", - "@react-types/select": "^3.9.2", - "@react-types/shared": "^3.22.1" + "@react-aria/i18n": "3.10.2", + "@react-aria/interactions": "3.21.1", + "@react-aria/label": "3.7.6", + "@react-aria/listbox": "3.11.5", + "@react-aria/menu": "3.13.1", + "@react-aria/selection": "3.17.5", + "@react-aria/utils": "3.23.2", + "@react-stately/form": "3.0.1", + "@react-stately/list": "3.10.3", + "@react-stately/menu": "3.6.1", + "@react-types/button": "3.9.2", + "@react-types/overlays": "3.8.5", + "@react-types/select": "3.9.2", + "@react-types/shared": "3.22.1" }, "peerDependencies": { "react": ">=18", @@ -67,4 +67,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-aria-multiselect/src/use-multiselect-list-state.ts b/packages/hooks/use-aria-multiselect/src/use-multiselect-list-state.ts index f031020215..3384d27d05 100644 --- a/packages/hooks/use-aria-multiselect/src/use-multiselect-list-state.ts +++ b/packages/hooks/use-aria-multiselect/src/use-multiselect-list-state.ts @@ -32,7 +32,7 @@ export function useMultiSelectListState( if (!props.isLoading && selectedKeys.size !== 0) { return Array.from(selectedKeys) .filter(Boolean) - .filter((key) => !collection.getItem(`${key}`)); + .filter((key) => !collection.getItem(key)); } return []; @@ -42,7 +42,7 @@ export function useMultiSelectListState( selectedKeys.size !== 0 ? Array.from(selectedKeys) .map((key) => { - return collection.getItem(`${key}`); + return collection.getItem(key); }) // Remove undefined values when some keys are not present in the collection .filter(Boolean) diff --git a/packages/hooks/use-aria-multiselect/src/use-multiselect-state.ts b/packages/hooks/use-aria-multiselect/src/use-multiselect-state.ts index aec0c3468d..8e2d5535c6 100644 --- a/packages/hooks/use-aria-multiselect/src/use-multiselect-state.ts +++ b/packages/hooks/use-aria-multiselect/src/use-multiselect-state.ts @@ -76,6 +76,7 @@ export function useMultiSelectState(props: MultiSelectProps): M const validationState = useFormValidationState({ ...props, + // TODO: Future enhancement to support "aria" validation behavior. validationBehavior: "native", // @ts-ignore value: listState.selectedKeys, diff --git a/packages/hooks/use-aria-toggle-button/package.json b/packages/hooks/use-aria-toggle-button/package.json index 36370c5418..857687d8ad 100644 --- a/packages/hooks/use-aria-toggle-button/package.json +++ b/packages/hooks/use-aria-toggle-button/package.json @@ -38,10 +38,10 @@ }, "dependencies": { "@nextui-org/use-aria-button": "workspace:*", - "@react-aria/utils": "^3.23.2", - "@react-stately/toggle": "^3.7.2", - "@react-types/button": "^3.9.2", - "@react-types/shared": "^3.22.1" + "@react-aria/utils": "3.23.2", + "@react-stately/toggle": "3.7.2", + "@react-types/button": "3.9.2", + "@react-types/shared": "3.22.1" }, "devDependencies": { "clean-package": "2.2.0", @@ -56,4 +56,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-callback-ref/package.json b/packages/hooks/use-callback-ref/package.json index 972a5f5ac7..b494fc9b1f 100644 --- a/packages/hooks/use-callback-ref/package.json +++ b/packages/hooks/use-callback-ref/package.json @@ -52,4 +52,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-clipboard/package.json b/packages/hooks/use-clipboard/package.json index dbcc0324ad..b7587f38dc 100644 --- a/packages/hooks/use-clipboard/package.json +++ b/packages/hooks/use-clipboard/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-data-scroll-overflow/package.json b/packages/hooks/use-data-scroll-overflow/package.json index c1067ea653..59c2ec2d1a 100644 --- a/packages/hooks/use-data-scroll-overflow/package.json +++ b/packages/hooks/use-data-scroll-overflow/package.json @@ -52,4 +52,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-disclosure/package.json b/packages/hooks/use-disclosure/package.json index 3b63dfbbe7..bcb531b107 100644 --- a/packages/hooks/use-disclosure/package.json +++ b/packages/hooks/use-disclosure/package.json @@ -38,8 +38,8 @@ }, "dependencies": { "@nextui-org/use-callback-ref": "workspace:*", - "@react-aria/utils": "^3.23.2", - "@react-stately/utils": "^3.9.1" + "@react-aria/utils": "3.23.2", + "@react-stately/utils": "3.9.1" }, "devDependencies": { "clean-package": "2.2.0", @@ -54,4 +54,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-image/package.json b/packages/hooks/use-image/package.json index f042ade156..0a03813994 100644 --- a/packages/hooks/use-image/package.json +++ b/packages/hooks/use-image/package.json @@ -52,4 +52,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-infinite-scroll/package.json b/packages/hooks/use-infinite-scroll/package.json index f7bafe3133..1ff2d5ca36 100644 --- a/packages/hooks/use-infinite-scroll/package.json +++ b/packages/hooks/use-infinite-scroll/package.json @@ -53,4 +53,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-intersection-observer/CHANGELOG.md b/packages/hooks/use-intersection-observer/CHANGELOG.md index 0ffd62ac63..81e44c2206 100644 --- a/packages/hooks/use-intersection-observer/CHANGELOG.md +++ b/packages/hooks/use-intersection-observer/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/use-intersection-observer +## 2.0.2 + +### Patch Changes + +- [#2889](https://github.com/nextui-org/nextui/pull/2889) [`aba1716ed`](https://github.com/nextui-org/nextui/commit/aba1716edc2a85c94e6baeb4acc481f67589d002) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Update React Aria packages + ## 2.0.1 ### Patch Changes diff --git a/packages/hooks/use-intersection-observer/package.json b/packages/hooks/use-intersection-observer/package.json index 414921d827..5710e78ccd 100644 --- a/packages/hooks/use-intersection-observer/package.json +++ b/packages/hooks/use-intersection-observer/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/use-intersection-observer", - "version": "2.0.1", + "version": "2.0.2", "description": "Tracks the intersection of a DOM element and its containing element or the viewport.", "keywords": [ "use-intersection-observer" @@ -34,10 +34,10 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/utils": "^3.21.1", - "@react-aria/ssr": "^3.8.0", - "@react-types/shared": "3.21.0", - "@react-aria/interactions": "^3.19.1" + "@react-aria/utils": "3.23.2", + "@react-aria/ssr": "3.9.2", + "@react-types/shared": "3.22.1", + "@react-aria/interactions": "3.21.1" }, "peerDependencies": { "react": ">=18" @@ -55,4 +55,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-is-mobile/package.json b/packages/hooks/use-is-mobile/package.json index 63a3a3143b..bc67aabd39 100644 --- a/packages/hooks/use-is-mobile/package.json +++ b/packages/hooks/use-is-mobile/package.json @@ -34,7 +34,7 @@ "postpack": "clean-package restore" }, "dependencies": { - "@react-aria/ssr": "^3.9.2" + "@react-aria/ssr": "3.9.2" }, "peerDependencies": { "react": ">=18" @@ -52,4 +52,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-is-mounted/package.json b/packages/hooks/use-is-mounted/package.json index 8e81803511..1d47708bd9 100644 --- a/packages/hooks/use-is-mounted/package.json +++ b/packages/hooks/use-is-mounted/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-measure/package.json b/packages/hooks/use-measure/package.json index 8910076f6e..7396063a27 100644 --- a/packages/hooks/use-measure/package.json +++ b/packages/hooks/use-measure/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-pagination/package.json b/packages/hooks/use-pagination/package.json index 8d7a5f4892..0ac696104c 100644 --- a/packages/hooks/use-pagination/package.json +++ b/packages/hooks/use-pagination/package.json @@ -35,7 +35,7 @@ }, "dependencies": { "@nextui-org/shared-utils": "workspace:*", - "@react-aria/i18n": "^3.10.2" + "@react-aria/i18n": "3.10.2" }, "peerDependencies": { "react": ">=18" @@ -53,4 +53,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-pagination/src/index.ts b/packages/hooks/use-pagination/src/index.ts index 03659eb767..3f4ff8a130 100644 --- a/packages/hooks/use-pagination/src/index.ts +++ b/packages/hooks/use-pagination/src/index.ts @@ -82,7 +82,7 @@ export function usePagination(props: UsePaginationProps) { onChangeActivePage(pageNumber); } }, - [total, activePage], + [total, activePage, onChangeActivePage], ); const next = () => (isRTL ? setPage(activePage - 1) : setPage(activePage + 1)); diff --git a/packages/hooks/use-real-shape/package.json b/packages/hooks/use-real-shape/package.json index b0eeab16e9..f1797b4ac4 100644 --- a/packages/hooks/use-real-shape/package.json +++ b/packages/hooks/use-real-shape/package.json @@ -52,4 +52,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-ref-state/package.json b/packages/hooks/use-ref-state/package.json index 07ba654996..65bd70995d 100644 --- a/packages/hooks/use-ref-state/package.json +++ b/packages/hooks/use-ref-state/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-resize/package.json b/packages/hooks/use-resize/package.json index 2ccee90b8d..02c771bb4e 100644 --- a/packages/hooks/use-resize/package.json +++ b/packages/hooks/use-resize/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-safe-layout-effect/package.json b/packages/hooks/use-safe-layout-effect/package.json index 90edfc4ee0..73964ccf71 100644 --- a/packages/hooks/use-safe-layout-effect/package.json +++ b/packages/hooks/use-safe-layout-effect/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-scroll-position/CHANGELOG.md b/packages/hooks/use-scroll-position/CHANGELOG.md index fa93be393c..0a6828957d 100644 --- a/packages/hooks/use-scroll-position/CHANGELOG.md +++ b/packages/hooks/use-scroll-position/CHANGELOG.md @@ -1,5 +1,11 @@ # @nextui-org/use-scroll-position +## 2.0.6 + +### Patch Changes + +- [#3049](https://github.com/nextui-org/nextui/pull/3049) [`fa26ce02f`](https://github.com/nextui-org/nextui/commit/fa26ce02fd84b25fc29925c93462d15de542b847) Thanks [@Gaic4o](https://github.com/Gaic4o)! - WHAT: Refactored the useScrollPosition hook to improve performance and stability by using useCallback for the handler function and useRef for throttleTimeout. + ## 2.0.5 ### Patch Changes diff --git a/packages/hooks/use-scroll-position/package.json b/packages/hooks/use-scroll-position/package.json index acbaf8efd4..2beb0c93a4 100644 --- a/packages/hooks/use-scroll-position/package.json +++ b/packages/hooks/use-scroll-position/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/use-scroll-position", - "version": "2.0.5", + "version": "2.0.6", "description": "Provides the logic to control the scroll over an element", "keywords": [ "use-scroll-position" @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-scroll-position/src/index.ts b/packages/hooks/use-scroll-position/src/index.ts index ca1ed163a8..8a85280829 100644 --- a/packages/hooks/use-scroll-position/src/index.ts +++ b/packages/hooks/use-scroll-position/src/index.ts @@ -1,8 +1,8 @@ -import {useRef, useEffect} from "react"; +import {useRef, useEffect, useCallback} from "react"; const isBrowser = typeof window !== "undefined"; -export type ScrollValue = {x: any; y: any}; +export type ScrollValue = {x: number; y: number}; function getScrollPosition(element: HTMLElement | undefined | null): ScrollValue { if (!isBrowser) return {x: 0, y: 0}; @@ -41,9 +41,9 @@ export const useScrollPosition = (props: UseScrollPositionOptions): ScrollValue isEnabled ? getScrollPosition(elementRef?.current) : {x: 0, y: 0}, ); - let throttleTimeout: ReturnType | null = null; + const throttleTimeout = useRef | null>(null); - const handler = () => { + const handler = useCallback(() => { const currPos = getScrollPosition(elementRef?.current); if (typeof callback === "function") { @@ -51,16 +51,16 @@ export const useScrollPosition = (props: UseScrollPositionOptions): ScrollValue } position.current = currPos; - throttleTimeout = null; - }; + throttleTimeout.current = null; + }, [callback, elementRef]); useEffect(() => { if (!isEnabled) return; const handleScroll = () => { if (delay) { - if (throttleTimeout === null) { - throttleTimeout = setTimeout(handler, delay); + if (throttleTimeout.current === null) { + throttleTimeout.current = setTimeout(handler, delay); } } else { handler(); @@ -71,8 +71,13 @@ export const useScrollPosition = (props: UseScrollPositionOptions): ScrollValue target.addEventListener("scroll", handleScroll); - return () => target.removeEventListener("scroll", handleScroll); - }, [elementRef?.current, delay, isEnabled]); + return () => { + target.removeEventListener("scroll", handleScroll); + if (throttleTimeout.current) { + clearTimeout(throttleTimeout.current); + } + }; + }, [elementRef?.current, delay, handler, isEnabled]); return position.current; }; diff --git a/packages/hooks/use-ssr/package.json b/packages/hooks/use-ssr/package.json index c02610bc32..0929ea97a8 100644 --- a/packages/hooks/use-ssr/package.json +++ b/packages/hooks/use-ssr/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/hooks/use-update-effect/package.json b/packages/hooks/use-update-effect/package.json index 6c37ef40ae..9f92c7d6ab 100644 --- a/packages/hooks/use-update-effect/package.json +++ b/packages/hooks/use-update-effect/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/storybook/.storybook/preview.tsx b/packages/storybook/.storybook/preview.tsx index 91bfd76799..ce502c4407 100644 --- a/packages/storybook/.storybook/preview.tsx +++ b/packages/storybook/.storybook/preview.tsx @@ -6,13 +6,13 @@ import type {Preview} from "@storybook/react"; import "./style.css"; const decorators: Preview["decorators"] = [ - (Story, {globals: {locale}}) => { + (Story, {globals: {locale, disableAnimation}}) => { const direction = // @ts-ignore locale && new Intl.Locale(locale)?.textInfo?.direction === "rtl" ? "rtl" : undefined; return ( - +
@@ -115,6 +115,17 @@ const globalTypes: Preview["globalTypes"] = { })), }, }, + disableAnimation: { + name: "Disable Animation", + description: "Disable all animations in the stories", + toolbar: { + icon: "photodrag", + items: [ + {value: true, title: "True"}, + {value: false, title: "False"}, + ], + }, + }, }; const preview: Preview = { diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 767fab477c..098421a155 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -61,4 +61,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/storybook/tailwind.config.js b/packages/storybook/tailwind.config.js index 8ce6de50d2..fd291b0649 100644 --- a/packages/storybook/tailwind.config.js +++ b/packages/storybook/tailwind.config.js @@ -13,6 +13,7 @@ module.exports = { darkMode: "class", plugins: [ nextui({ + addCommonColors: true, themes: { // "dark": { // extend: "dark", // <- inherit default values from dark theme diff --git a/packages/utilities/aria-utils/CHANGELOG.md b/packages/utilities/aria-utils/CHANGELOG.md index 3f7307f5d2..f313d00b6a 100644 --- a/packages/utilities/aria-utils/CHANGELOG.md +++ b/packages/utilities/aria-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @nextui-org/aria-utils +## 2.0.19 + +### Patch Changes + +- [#2854](https://github.com/nextui-org/nextui/pull/2854) [`3b14c21e0`](https://github.com/nextui-org/nextui/commit/3b14c21e02fedf15d7d22e911109dac60c4e780e) Thanks [@wingkwong](https://github.com/wingkwong)! - Revise popover-based focus behaviours (#2849, #2834, #2779, #2962, #2872, #2974, #1920, #1287, #3060) + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a)]: + - @nextui-org/system@2.2.0 + ## 2.0.18 ### Patch Changes diff --git a/packages/utilities/aria-utils/package.json b/packages/utilities/aria-utils/package.json index c7e1e90d1f..5cfe86db9c 100644 --- a/packages/utilities/aria-utils/package.json +++ b/packages/utilities/aria-utils/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/aria-utils", - "version": "2.0.18", + "version": "2.0.19", "description": "A package for managing @react-aria nextui utils.", "keywords": [ "aria-utils" @@ -41,10 +41,11 @@ "@nextui-org/system": "workspace:*", "@nextui-org/shared-utils": "workspace:*", "@nextui-org/react-rsc-utils": "workspace:*", - "@react-aria/utils": "^3.23.2", - "@react-stately/collections": "^3.10.5", - "@react-types/overlays": "^3.8.5", - "@react-types/shared": "^3.22.1" + "@react-aria/utils": "3.23.2", + "@react-stately/collections": "3.10.5", + "@react-stately/overlays": "3.6.5", + "@react-types/overlays": "3.8.5", + "@react-types/shared": "3.22.1" }, "devDependencies": { "clean-package": "2.2.0", @@ -52,4 +53,4 @@ "react-dom": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/utilities/aria-utils/src/index.ts b/packages/utilities/aria-utils/src/index.ts index 8e70c1b183..6b8d27ad3f 100644 --- a/packages/utilities/aria-utils/src/index.ts +++ b/packages/utilities/aria-utils/src/index.ts @@ -7,6 +7,7 @@ export {isNonContiguousSelectionModifier, isCtrlKeyPressed} from "./utils"; export { ariaHideOutside, + ariaShouldCloseOnInteractOutside, getTransformOrigins, toReactAriaPlacement, toOverlayPlacement, diff --git a/packages/utilities/aria-utils/src/overlays/ariaShouldCloseOnInteractOutside.ts b/packages/utilities/aria-utils/src/overlays/ariaShouldCloseOnInteractOutside.ts new file mode 100644 index 0000000000..0be005b516 --- /dev/null +++ b/packages/utilities/aria-utils/src/overlays/ariaShouldCloseOnInteractOutside.ts @@ -0,0 +1,41 @@ +import {MutableRefObject, RefObject} from "react"; + +/** + * Used to handle the outside interaction for popover-based components + * e.g. dropdown, datepicker, date-range-picker, popover, select, autocomplete etc + * @param element - the element outside of the popover ref, originally from `shouldCloseOnInteractOutside` + * @param ref - The popover ref object that will interact outside with + * @param state - The popover state from the target component + * @param shouldFocus - a mutable ref boolean object to control the focus state + * (used in input-based component such as autocomplete) + * @returns - a boolean value which is same as shouldCloseOnInteractOutside + */ +export const ariaShouldCloseOnInteractOutside = ( + element: Element, + ref: RefObject, + state: any, + shouldFocus?: MutableRefObject, +) => { + let trigger = ref?.current; + + // check if the click is on the underlay + const clickOnUnderlay = element?.children?.[0]?.getAttribute("role") === "dialog" ?? false; + + // if interacting outside the component + if (!trigger || !trigger.contains(element)) { + // blur the component (e.g. autocomplete) + if (shouldFocus) shouldFocus.current = false; + // if the click is not on the underlay, + // trigger the state close to prevent from opening multiple popovers at the same time + // e.g. open dropdown1 -> click dropdown2 (dropdown1 should be closed and dropdown2 should be open) + if (!clickOnUnderlay) state.close(); + } else { + // otherwise the component (e.g. autocomplete) should keep focused + if (shouldFocus) shouldFocus.current = true; + } + + // if the click is on the underlay, + // clicking the overlay should close the popover instead of closing the modal + // otherwise, allow interaction with other elements + return clickOnUnderlay; +}; diff --git a/packages/utilities/aria-utils/src/overlays/index.ts b/packages/utilities/aria-utils/src/overlays/index.ts index ccf839f2d9..6999e8b90c 100644 --- a/packages/utilities/aria-utils/src/overlays/index.ts +++ b/packages/utilities/aria-utils/src/overlays/index.ts @@ -9,3 +9,4 @@ export { } from "./utils"; export {ariaHideOutside} from "./ariaHideOutside"; +export {ariaShouldCloseOnInteractOutside} from "./ariaShouldCloseOnInteractOutside"; diff --git a/packages/utilities/framer-utils/CHANGELOG.md b/packages/utilities/framer-utils/CHANGELOG.md index abb5e0dd97..f4a77da3a8 100644 --- a/packages/utilities/framer-utils/CHANGELOG.md +++ b/packages/utilities/framer-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @nextui-org/framer-utils +## 2.0.19 + +### Patch Changes + +- [#2929](https://github.com/nextui-org/nextui/pull/2929) [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2) Thanks [@jrgarciadev](https://github.com/jrgarciadev)! - Add support for disabling the animations globally. + +- Updated dependencies [[`e3afa4789`](https://github.com/nextui-org/nextui/commit/e3afa4789a1ac0fa929b2acaca5bd9c520567ab8), [`422770cc6`](https://github.com/nextui-org/nextui/commit/422770cc6bcdd1d4c51257654ab718f3c19d6cb2), [`540aa2124`](https://github.com/nextui-org/nextui/commit/540aa2124b45b65a40e73f5aea2b90405fe1fe9a)]: + - @nextui-org/system@2.2.0 + ## 2.0.18 ### Patch Changes diff --git a/packages/utilities/framer-utils/package.json b/packages/utilities/framer-utils/package.json index f1eb15c822..96e1d25b3f 100644 --- a/packages/utilities/framer-utils/package.json +++ b/packages/utilities/framer-utils/package.json @@ -1,6 +1,6 @@ { "name": "@nextui-org/framer-utils", - "version": "2.0.18", + "version": "2.0.19", "description": "A set of framer motion transitions for react", "keywords": [ "framer-utils" @@ -36,7 +36,7 @@ "peerDependencies": { "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=4.0.0" + "framer-motion": ">=10.17.0" }, "dependencies": { "@nextui-org/system": "workspace:*", @@ -50,4 +50,4 @@ "framer-motion": "^11.0.22" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/utilities/framer-utils/src/transition-utils.ts b/packages/utilities/framer-utils/src/transition-utils.ts index bab9937548..78a07da9aa 100644 --- a/packages/utilities/framer-utils/src/transition-utils.ts +++ b/packages/utilities/framer-utils/src/transition-utils.ts @@ -59,15 +59,15 @@ export const TRANSITION_VARIANTS: Variants = { transition: { type: "spring", bounce: 0, - duration: 0.3, + duration: 0.2, }, }, exit: { - transform: "scale(0.6)", + transform: "scale(0.85)", opacity: 0, transition: { type: "easeOut", - duration: 0.2, + duration: 0.15, }, }, }, diff --git a/packages/utilities/react-rsc-utils/package.json b/packages/utilities/react-rsc-utils/package.json index 1eb416db81..f0f4d69c8d 100644 --- a/packages/utilities/react-rsc-utils/package.json +++ b/packages/utilities/react-rsc-utils/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/utilities/react-utils/package.json b/packages/utilities/react-utils/package.json index ba5305a281..be54543666 100644 --- a/packages/utilities/react-utils/package.json +++ b/packages/utilities/react-utils/package.json @@ -45,4 +45,4 @@ "react": "^18.0.0" }, "clean-package": "../../../clean-package.config.json" -} +} \ No newline at end of file diff --git a/packages/utilities/shared-icons/package.json b/packages/utilities/shared-icons/package.json index 1b5d5dc563..c0bc325001 100644 --- a/packages/utilities/shared-icons/package.json +++ b/packages/utilities/shared-icons/package.json @@ -49,4 +49,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/utilities/shared-utils/package.json b/packages/utilities/shared-utils/package.json index c6a948f8cc..72eee17481 100644 --- a/packages/utilities/shared-utils/package.json +++ b/packages/utilities/shared-utils/package.json @@ -45,4 +45,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/utilities/stories-utils/package.json b/packages/utilities/stories-utils/package.json index 6e778890ed..648b412830 100644 --- a/packages/utilities/stories-utils/package.json +++ b/packages/utilities/stories-utils/package.json @@ -45,4 +45,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/packages/utilities/test-utils/package.json b/packages/utilities/test-utils/package.json index f1eed2e012..fd83741aa1 100644 --- a/packages/utilities/test-utils/package.json +++ b/packages/utilities/test-utils/package.json @@ -45,4 +45,4 @@ "esm" ] } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ba354aba0..8163b07a45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,7 +58,7 @@ importers: specifier: ^3.4.4 version: 3.5.3(react@18.2.0) '@react-types/shared': - specifier: ^3.22.0 + specifier: ^3.22.1 version: 3.22.1(react@18.2.0) '@storybook/react': specifier: ^7.4.6 @@ -337,46 +337,46 @@ importers: specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.4)(@types/react@18.2.8)(react-dom@18.2.0)(react@18.2.0) '@react-aria/focus': - specifier: ^3.14.3 + specifier: ^3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/i18n': - specifier: ^3.8.4 + specifier: ^3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.19.1 + specifier: ^3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/selection': - specifier: ^3.17.1 + specifier: ^3.17.5 version: 3.17.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/ssr': - specifier: ^3.8.0 + specifier: ^3.9.2 version: 3.9.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.21.1 + specifier: ^3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/virtualizer': - specifier: ^3.9.4 + specifier: ^3.9.10 version: 3.9.10(react-dom@18.2.0)(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.6 + specifier: ^3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/data': - specifier: ^3.10.3 + specifier: ^3.11.2 version: 3.11.2(react@18.2.0) '@react-stately/layout': - specifier: ^3.13.3 + specifier: ^3.13.7 version: 3.13.7(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.3 + specifier: ^3.7.6 version: 3.7.6(react@18.2.0) '@rehooks/local-storage': specifier: ^2.4.5 version: 2.4.5(react@18.2.0) '@vercel/analytics': - specifier: ^1.1.1 + specifier: ^1.2.2 version: 1.2.2(next@13.5.1)(react@18.2.0) canvas-confetti: - specifier: ^1.9.0 + specifier: ^1.9.2 version: 1.9.2 cmdk: specifier: ^0.2.0 @@ -391,8 +391,8 @@ importers: specifier: ^2.30.0 version: 2.30.0 framer-motion: - specifier: ^11.0.22 - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: ^11.1.7 + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) github-slugger: specifier: ^2.0.0 version: 2.0.0 @@ -524,7 +524,7 @@ importers: specifier: ^13.4.12 version: 13.5.6 '@react-types/shared': - specifier: ^3.22.0 + specifier: ^3.22.1 version: 3.22.1(react@18.2.0) '@tailwindcss/typography': specifier: ^0.5.9 @@ -632,25 +632,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-aria-accordion '@react-aria/button': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-types/accordion': specifier: 3.0.0-alpha.19 version: 3.0.0-alpha.19(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -723,31 +723,31 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/combobox': - specifier: ^3.8.4 + specifier: 3.8.4 version: 3.8.4(react-dom@18.2.0)(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/combobox': - specifier: ^3.8.2 + specifier: 3.8.2 version: 3.8.2(react@18.2.0) '@react-types/combobox': - specifier: ^3.10.1 + specifier: 3.10.1 version: 3.10.1(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -769,7 +769,7 @@ importers: specifier: workspace:* version: link:../../hooks/use-infinite-scroll '@react-stately/data': - specifier: ^3.11.0 + specifier: 3.11.2 version: 3.11.2(react@18.2.0) clean-package: specifier: 2.2.0 @@ -783,6 +783,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.51.3 + version: 7.51.3(react@18.2.0) packages/components/avatar: dependencies: @@ -796,13 +799,13 @@ importers: specifier: workspace:* version: link:../../hooks/use-image '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) devDependencies: '@nextui-org/shared-icons': @@ -835,9 +838,6 @@ importers: '@nextui-org/shared-utils': specifier: workspace:* version: link:../../utilities/shared-utils - '@nextui-org/system-rsc': - specifier: workspace:* - version: link:../../core/system-rsc devDependencies: '@nextui-org/avatar': specifier: workspace:* @@ -848,6 +848,9 @@ importers: '@nextui-org/switch': specifier: workspace:* version: link:../switch + '@nextui-org/system': + specifier: workspace:* + version: link:../../core/system '@nextui-org/theme': specifier: workspace:* version: link:../../core/theme @@ -873,19 +876,19 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/breadcrumbs': - specifier: ^3.5.11 + specifier: 3.5.11 version: 3.5.11(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/breadcrumbs': - specifier: ^3.7.3 + specifier: 3.7.3 version: 3.7.3(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/button': @@ -931,26 +934,26 @@ importers: specifier: workspace:* version: link:../../hooks/use-aria-button '@react-aria/button': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/button': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) framer-motion: - specifier: '>=4.0.0' - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: '>=10.17.0' + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) devDependencies: '@nextui-org/shared-icons': specifier: workspace:* @@ -995,38 +998,38 @@ importers: specifier: workspace:* version: link:../../hooks/use-aria-button '@react-aria/calendar': - specifier: 3.5.1 - version: 3.5.1(react-dom@18.2.0)(react@18.2.0) + specifier: 3.5.6 + version: 3.5.6(react-dom@18.2.0)(react@18.2.0) '@react-aria/focus': - specifier: ^3.14.3 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/i18n': - specifier: ^3.8.4 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.19.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.21.1 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.6 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/calendar': - specifier: 3.4.1 - version: 3.4.1(react@18.2.0) + specifier: 3.4.4 + version: 3.4.4(react@18.2.0) '@react-stately/utils': - specifier: ^3.8.0 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) '@react-types/button': - specifier: ^3.9.0 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/calendar': - specifier: 3.4.1 - version: 3.4.1(react@18.2.0) + specifier: 3.4.4 + version: 3.4.4(react@18.2.0) '@react-types/shared': - specifier: 3.21.0 - version: 3.21.0(react@18.2.0) + specifier: 3.22.1 + version: 3.22.1(react@18.2.0) '@types/lodash.debounce': specifier: ^4.0.7 version: 4.0.9 @@ -1077,23 +1080,23 @@ importers: specifier: workspace:* version: link:../../hooks/use-aria-button '@react-aria/button': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) framer-motion: - specifier: '>=4.0.0' - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: '>=10.17.0' + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) devDependencies: '@nextui-org/avatar': specifier: workspace:* @@ -1141,31 +1144,31 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/checkbox': - specifier: ^3.14.1 + specifier: 3.14.1 version: 3.14.1(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/checkbox': - specifier: ^3.6.3 + specifier: 3.6.3 version: 3.6.3(react@18.2.0) '@react-stately/toggle': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-types/checkbox': - specifier: ^3.7.1 + specifier: 3.7.1 version: 3.7.1(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/chip': @@ -1211,16 +1214,16 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/checkbox': - specifier: ^3.7.1 + specifier: 3.7.1 version: 3.7.1(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -1279,23 +1282,23 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/datepicker': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react-dom@18.2.0)(react@18.2.0) '@react-aria/i18n': - specifier: ^3.8.4 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.21.1 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/datepicker': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/datepicker': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-types/shared': - specifier: 3.21.0 - version: 3.21.0(react@18.2.0) + specifier: 3.22.1 + version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/shared-icons': specifier: workspace:* @@ -1324,6 +1327,9 @@ importers: '@internationalized/date': specifier: ^3.5.2 version: 3.5.2 + '@nextui-org/aria-utils': + specifier: workspace:* + version: link:../../utilities/aria-utils '@nextui-org/button': specifier: workspace:* version: link:../button @@ -1346,29 +1352,29 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/datepicker': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react-dom@18.2.0)(react@18.2.0) '@react-aria/i18n': - specifier: ^3.8.4 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.21.1 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/datepicker': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-stately/overlays': - specifier: ^3.6.3 + specifier: 3.6.5 version: 3.6.5(react@18.2.0) '@react-stately/utils': - specifier: ^3.8.0 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) '@react-types/datepicker': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-types/shared': - specifier: 3.21.0 - version: 3.21.0(react@18.2.0) + specifier: 3.22.1 + version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/radio': specifier: workspace:* @@ -1404,7 +1410,7 @@ importers: specifier: workspace:* version: link:../../core/system-rsc '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/theme': @@ -1422,6 +1428,9 @@ importers: packages/components/dropdown: dependencies: + '@nextui-org/aria-utils': + specifier: workspace:* + version: link:../../utilities/aria-utils '@nextui-org/menu': specifier: workspace:* version: link:../menu @@ -1435,19 +1444,19 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/menu': - specifier: ^3.13.1 + specifier: 3.13.1 version: 3.13.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/menu': - specifier: ^3.6.1 + specifier: 3.6.1 version: 3.6.1(react@18.2.0) '@react-types/menu': - specifier: ^3.9.7 + specifier: 3.9.7 version: 3.9.7(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -1465,6 +1474,9 @@ importers: '@nextui-org/system': specifier: workspace:* version: link:../../core/system + '@nextui-org/test-utils': + specifier: workspace:* + version: link:../../utilities/test-utils '@nextui-org/theme': specifier: workspace:* version: link:../../core/theme @@ -1527,25 +1539,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/textfield': - specifier: ^3.14.3 + specifier: 3.14.3 version: 3.14.3(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/utils': - specifier: ^3.9.1 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) '@react-types/textfield': - specifier: ^3.9.1 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) react-textarea-autosize: specifier: ^8.5.3 @@ -1582,7 +1594,7 @@ importers: specifier: workspace:* version: link:../../core/system-rsc '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) devDependencies: '@nextui-org/theme': @@ -1613,16 +1625,16 @@ importers: specifier: workspace:* version: link:../../hooks/use-aria-link '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/link': - specifier: ^3.6.5 + specifier: 3.6.5 version: 3.6.5(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/link': - specifier: ^3.5.3 + specifier: 3.5.3 version: 3.5.3(react@18.2.0) devDependencies: '@nextui-org/system': @@ -1659,25 +1671,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-is-mobile '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/listbox': - specifier: ^3.11.5 + specifier: 3.11.5 version: 3.11.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/list': - specifier: ^3.10.3 + specifier: 3.10.3 version: 3.10.3(react@18.2.0) '@react-types/menu': - specifier: ^3.9.7 + specifier: 3.9.7 version: 3.9.7(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -1732,28 +1744,28 @@ importers: specifier: workspace:* version: link:../../hooks/use-is-mobile '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/menu': - specifier: ^3.13.1 + specifier: 3.13.1 version: 3.13.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/menu': - specifier: ^3.6.1 + specifier: 3.6.1 version: 3.6.1(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-types/menu': - specifier: ^3.9.7 + specifier: 3.9.7 version: 3.9.7(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/shared-icons': @@ -1802,25 +1814,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-disclosure '@react-aria/dialog': - specifier: ^3.5.12 + specifier: 3.5.12 version: 3.5.12(react-dom@18.2.0)(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/overlays': - specifier: ^3.6.5 + specifier: 3.6.5 version: 3.6.5(react@18.2.0) '@react-types/overlays': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react@18.2.0) devDependencies: '@nextui-org/button': @@ -1875,26 +1887,26 @@ importers: specifier: workspace:* version: link:../../hooks/use-scroll-position '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/toggle': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-stately/utils': - specifier: ^3.9.1 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) framer-motion: - specifier: '>=4.0.0' - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: '>=10.17.0' + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) react-remove-scroll: specifier: ^2.5.6 version: 2.5.9(@types/react@18.2.8)(react@18.2.0) @@ -1951,16 +1963,16 @@ importers: specifier: workspace:* version: link:../../hooks/use-pagination '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) scroll-into-view-if-needed: specifier: 3.0.10 @@ -2006,28 +2018,28 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/dialog': - specifier: ^3.5.12 + specifier: 3.5.12 version: 3.5.12(react-dom@18.2.0)(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/overlays': - specifier: ^3.6.5 + specifier: 3.6.5 version: 3.6.5(react@18.2.0) '@react-types/button': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/overlays': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react@18.2.0) react-remove-scroll: specifier: ^2.5.6 @@ -2070,16 +2082,16 @@ importers: specifier: workspace:* version: link:../../hooks/use-is-mounted '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/progress': - specifier: ^3.4.11 + specifier: 3.4.11 version: 3.4.11(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/progress': - specifier: ^3.5.2 + specifier: 3.5.2 version: 3.5.2(react@18.2.0) devDependencies: '@nextui-org/card': @@ -2113,28 +2125,28 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/radio': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/radio': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-types/radio': - specifier: ^3.7.1 + specifier: 3.7.1 version: 3.7.1(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/button': @@ -2251,22 +2263,22 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/form': - specifier: ^3.0.3 + specifier: 3.0.3 version: 3.0.3(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/avatar': @@ -2294,10 +2306,10 @@ importers: specifier: workspace:* version: link:../../hooks/use-infinite-scroll '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-stately/data': - specifier: ^3.11.2 + specifier: 3.11.2 version: 3.11.2(react@18.2.0) clean-package: specifier: 2.2.0 @@ -2311,6 +2323,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.51.3 + version: 7.51.3(react@18.2.0) packages/components/skeleton: dependencies: @@ -2320,9 +2335,6 @@ importers: '@nextui-org/shared-utils': specifier: workspace:* version: link:../../utilities/shared-utils - '@nextui-org/system-rsc': - specifier: workspace:* - version: link:../../core/system-rsc devDependencies: '@nextui-org/button': specifier: workspace:* @@ -2330,6 +2342,9 @@ importers: '@nextui-org/card': specifier: workspace:* version: link:../card + '@nextui-org/system': + specifier: workspace:* + version: link:../../core/system '@nextui-org/theme': specifier: workspace:* version: link:../../core/theme @@ -2355,25 +2370,25 @@ importers: specifier: workspace:* version: link:../tooltip '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/slider': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/slider': - specifier: ^3.5.2 + specifier: 3.5.2 version: 3.5.2(react@18.2.0) devDependencies: '@nextui-org/shared-icons': @@ -2419,14 +2434,14 @@ importers: specifier: workspace:* version: link:../../hooks/use-clipboard '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) framer-motion: - specifier: '>=4.0.0' - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: '>=10.17.0' + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) devDependencies: '@nextui-org/system': specifier: workspace:* @@ -2502,26 +2517,29 @@ importers: '@nextui-org/shared-utils': specifier: workspace:* version: link:../../utilities/shared-utils + '@nextui-org/use-safe-layout-effect': + specifier: workspace:* + version: link:../../hooks/use-safe-layout-effect '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/switch': - specifier: ^3.6.2 + specifier: 3.6.2 version: 3.6.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/toggle': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: '@nextui-org/shared-icons': @@ -2542,6 +2560,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) + react-hook-form: + specifier: ^7.51.3 + version: 7.51.3(react@18.2.0) packages/components/table: dependencies: @@ -2561,31 +2582,31 @@ importers: specifier: workspace:* version: link:../spacer '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/table': - specifier: ^3.13.5 + specifier: 3.13.5 version: 3.13.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) '@react-stately/table': - specifier: ^3.11.6 + specifier: 3.11.6 version: 3.11.6(react@18.2.0) '@react-stately/virtualizer': - specifier: ^3.6.8 + specifier: 3.6.8 version: 3.6.8(react@18.2.0) '@react-types/grid': - specifier: ^3.2.4 + specifier: 3.2.4 version: 3.2.4(react@18.2.0) '@react-types/table': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) devDependencies: '@nextui-org/button': @@ -2616,7 +2637,7 @@ importers: specifier: workspace:* version: link:../user '@react-stately/data': - specifier: ^3.11.2 + specifier: 3.11.2 version: 3.11.2(react@18.2.0) clean-package: specifier: 2.2.0 @@ -2652,25 +2673,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-update-effect '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/tabs': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/tabs': - specifier: ^3.6.4 + specifier: 3.6.4 version: 3.6.4(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) '@react-types/tabs': - specifier: ^3.3.5 + specifier: 3.3.5 version: 3.3.5(react@18.2.0) scroll-into-view-if-needed: specifier: 3.0.10 @@ -2731,25 +2752,25 @@ importers: specifier: workspace:* version: link:../../hooks/use-safe-layout-effect '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/tooltip': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/tooltip': - specifier: ^3.4.7 + specifier: 3.4.7 version: 3.4.7(react@18.2.0) '@react-types/overlays': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react@18.2.0) '@react-types/tooltip': - specifier: ^3.4.7 + specifier: 3.4.7 version: 3.4.7(react@18.2.0) devDependencies: '@nextui-org/button': @@ -2786,10 +2807,10 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) devDependencies: '@nextui-org/link': @@ -2943,11 +2964,11 @@ importers: specifier: workspace:* version: link:../../components/user '@react-aria/visually-hidden': - specifier: ^3.8.10 + specifier: 3.8.10 version: 3.8.10(react@18.2.0) framer-motion: - specifier: '>=4.0.0' - version: 11.0.28(react-dom@18.2.0)(react@18.2.0) + specifier: '>=10.17.0' + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) devDependencies: clean-package: specifier: 2.2.0 @@ -2971,21 +2992,24 @@ importers: specifier: workspace:* version: link:../system-rsc '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/utils': - specifier: ^3.9.1 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) devDependencies: clean-package: specifier: 2.2.0 version: 2.2.0 + framer-motion: + specifier: ^11.0.22 + version: 11.1.7(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -3020,6 +3044,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 +3074,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) @@ -3082,25 +3112,25 @@ importers: packages/hooks/use-aria-accordion: dependencies: '@react-aria/button': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/selection': - specifier: ^3.17.5 + specifier: 3.17.5 version: 3.17.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-types/accordion': specifier: 3.0.0-alpha.19 version: 3.0.0-alpha.19(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3113,16 +3143,16 @@ importers: packages/hooks/use-aria-accordion-item: dependencies: '@react-aria/button': - specifier: ^3.9.3 + specifier: 3.9.3 version: 3.9.3(react@18.2.0) '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3135,19 +3165,19 @@ importers: packages/hooks/use-aria-button: dependencies: '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/button': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3160,19 +3190,19 @@ importers: packages/hooks/use-aria-link: dependencies: '@react-aria/focus': - specifier: ^3.16.2 + specifier: 3.16.2 version: 3.16.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/link': - specifier: ^3.5.3 + specifier: 3.5.3 version: 3.5.3(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3185,31 +3215,31 @@ importers: packages/hooks/use-aria-menu: dependencies: '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/menu': - specifier: ^3.13.1 + specifier: 3.13.1 version: 3.13.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/selection': - specifier: ^3.17.5 + specifier: 3.17.5 version: 3.17.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/collections': - specifier: ^3.10.5 + specifier: 3.10.5 version: 3.10.5(react@18.2.0) '@react-stately/tree': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-types/menu': - specifier: ^3.9.7 + specifier: 3.9.7 version: 3.9.7(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) react-dom: specifier: ^18.2.0 @@ -3225,16 +3255,16 @@ importers: packages/hooks/use-aria-modal-overlay: dependencies: '@react-aria/overlays': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/overlays': - specifier: ^3.6.5 + specifier: 3.6.5 version: 3.6.5(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3250,46 +3280,46 @@ importers: packages/hooks/use-aria-multiselect: dependencies: '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) '@react-aria/interactions': - specifier: ^3.21.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/label': - specifier: ^3.7.6 + specifier: 3.7.6 version: 3.7.6(react@18.2.0) '@react-aria/listbox': - specifier: ^3.11.5 + specifier: 3.11.5 version: 3.11.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/menu': - specifier: ^3.13.1 + specifier: 3.13.1 version: 3.13.1(react-dom@18.2.0)(react@18.2.0) '@react-aria/selection': - specifier: ^3.17.5 + specifier: 3.17.5 version: 3.17.5(react-dom@18.2.0)(react@18.2.0) '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/form': - specifier: ^3.0.1 + specifier: 3.0.1 version: 3.0.1(react@18.2.0) '@react-stately/list': - specifier: ^3.10.3 + specifier: 3.10.3 version: 3.10.3(react@18.2.0) '@react-stately/menu': - specifier: ^3.6.1 + specifier: 3.6.1 version: 3.6.1(react@18.2.0) '@react-types/button': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/overlays': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react@18.2.0) '@react-types/select': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3308,16 +3338,16 @@ importers: specifier: workspace:* version: link:../use-aria-button '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/toggle': - specifier: ^3.7.2 + specifier: 3.7.2 version: 3.7.2(react@18.2.0) '@react-types/button': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -3368,10 +3398,10 @@ importers: specifier: workspace:* version: link:../use-callback-ref '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/utils': - specifier: ^3.9.1 + specifier: 3.9.1 version: 3.9.1(react@18.2.0) devDependencies: clean-package: @@ -3413,17 +3443,17 @@ importers: packages/hooks/use-intersection-observer: dependencies: '@react-aria/interactions': - specifier: ^3.19.1 + specifier: 3.21.1 version: 3.21.1(react@18.2.0) '@react-aria/ssr': - specifier: ^3.8.0 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) '@react-aria/utils': - specifier: ^3.21.1 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-types/shared': - specifier: 3.21.0 - version: 3.21.0(react@18.2.0) + specifier: 3.22.1 + version: 3.22.1(react@18.2.0) devDependencies: clean-package: specifier: 2.2.0 @@ -3435,7 +3465,7 @@ importers: packages/hooks/use-is-mobile: dependencies: '@react-aria/ssr': - specifier: ^3.9.2 + specifier: 3.9.2 version: 3.9.2(react@18.2.0) devDependencies: clean-package: @@ -3469,7 +3499,7 @@ importers: specifier: workspace:* version: link:../../utilities/shared-utils '@react-aria/i18n': - specifier: ^3.10.2 + specifier: 3.10.2 version: 3.10.2(react@18.2.0) devDependencies: clean-package: @@ -3622,16 +3652,19 @@ importers: specifier: workspace:* version: link:../../core/system '@react-aria/utils': - specifier: ^3.23.2 + specifier: 3.23.2 version: 3.23.2(react@18.2.0) '@react-stately/collections': - specifier: ^3.10.5 + specifier: 3.10.5 version: 3.10.5(react@18.2.0) + '@react-stately/overlays': + specifier: 3.6.5 + version: 3.6.5(react@18.2.0) '@react-types/overlays': - specifier: ^3.8.5 + specifier: 3.8.5 version: 3.8.5(react@18.2.0) '@react-types/shared': - specifier: ^3.22.1 + specifier: 3.22.1 version: 3.22.1(react@18.2.0) devDependencies: clean-package: @@ -5946,10 +5979,6 @@ packages: peerDependencies: '@effect-ts/otel-node': '*' peerDependenciesMeta: - '@effect-ts/core': - optional: true - '@effect-ts/otel': - optional: true '@effect-ts/otel-node': optional: true dependencies: @@ -9616,8 +9645,8 @@ packages: react: 18.2.0 dev: false - /@react-aria/calendar@3.5.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3gGiI2arrGQtlPD9633l00TR4y5dj9IMFapEiCDuwVwNSCsnH8aiz/emg+3hGFq86QoyvkFBvnKmezJIVKfPkA==} + /@react-aria/calendar@3.5.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PA0Ur5WcODMn7t2gCUvq61YktkB+WlSZjzDr5kcY3sdl53ZjiyqCa2hYgrb6R0J859LVJXAp+5Qaproz8g1oLA==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -9627,9 +9656,9 @@ packages: '@react-aria/interactions': 3.21.1(react@18.2.0) '@react-aria/live-announcer': 3.3.2 '@react-aria/utils': 3.23.2(react@18.2.0) - '@react-stately/calendar': 3.4.1(react@18.2.0) + '@react-stately/calendar': 3.4.4(react@18.2.0) '@react-types/button': 3.9.2(react@18.2.0) - '@react-types/calendar': 3.4.1(react@18.2.0) + '@react-types/calendar': 3.4.4(react@18.2.0) '@react-types/shared': 3.22.1(react@18.2.0) '@swc/helpers': 0.5.9 react: 18.2.0 @@ -10166,15 +10195,14 @@ packages: react: 18.2.0 dev: false - /@react-stately/calendar@3.4.1(react@18.2.0): - resolution: {integrity: sha512-XKCdrXNA7/ukZ842EeDZfLqYUQDv/x5RoAVkzTbp++3U/MLM1XZXsqj+5xVlQfJiWpQzM9L6ySjxzzgepJDeuw==} + /@react-stately/calendar@3.4.4(react@18.2.0): + resolution: {integrity: sha512-f9ZOd096gGGD+3LmU1gkmfqytGyQtrgi+Qjn+70GbM2Jy65pwOR4I9YrobbmeAFov5Tff13mQEa0yqWvbcDLZQ==} peerDependencies: react: ^18.2.0 dependencies: '@internationalized/date': 3.5.2 '@react-stately/utils': 3.9.1(react@18.2.0) - '@react-types/calendar': 3.4.1(react@18.2.0) - '@react-types/datepicker': 3.7.2(react@18.2.0) + '@react-types/calendar': 3.4.4(react@18.2.0) '@react-types/shared': 3.22.1(react@18.2.0) '@swc/helpers': 0.5.9 react: 18.2.0 @@ -10487,16 +10515,6 @@ packages: react: 18.2.0 dev: false - /@react-types/calendar@3.4.1(react@18.2.0): - resolution: {integrity: sha512-tiCkHi6IQtYcVoAESG79eUBWDXoo8NImo+Mj8WAWpo1lOA3SV1W2PpeXkoRNqtloilQ0aYcmsaJJUhciQG4ndg==} - peerDependencies: - react: ^18.2.0 - dependencies: - '@internationalized/date': 3.5.2 - '@react-types/shared': 3.22.1(react@18.2.0) - react: 18.2.0 - dev: false - /@react-types/calendar@3.4.4(react@18.2.0): resolution: {integrity: sha512-hV1Thmb/AES5OmfPvvmyjSkmsEULjiDfA7Yyy70L/YKuSNKb7Su+Bf2VnZuDW3ec+GxO4JJNlpJ0AkbphWBvcg==} peerDependencies: @@ -10619,14 +10637,6 @@ packages: react: 18.2.0 dev: false - /@react-types/shared@3.21.0(react@18.2.0): - resolution: {integrity: sha512-wJA2cUF8dP4LkuNUt9Vh2kkfiQb2NLnV2pPXxVnKJZ7d4x2/7VPccN+LYPnH8m0X3+rt50cxWuPKQmjxSsCFOg==} - peerDependencies: - react: ^18.2.0 - dependencies: - react: 18.2.0 - dev: false - /@react-types/shared@3.22.1(react@18.2.0): resolution: {integrity: sha512-PCpa+Vo6BKnRMuOEzy5zAZ3/H5tnQg1e80khMhK2xys0j6ZqzkgQC+fHMNZ7VDFNLqqNMj/o0eVeSBDh2POjkw==} peerDependencies: @@ -17374,6 +17384,25 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) tslib: 2.6.2 + dev: true + + /framer-motion@11.1.7(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cW11Pu53eDAXUEhv5hEiWuIXWhfkbV32PlgVISn7jRdcAiVrJ1S03YQQ0/DzoswGYYwKi4qYmHHjCzAH52eSdQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.2.0 + react-dom: ^18.2.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.6.2 /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} @@ -22443,9 +22472,6 @@ packages: resolution: {integrity: sha512-W+gxAq7aQ9dJIg/XLKGcRT0cvnStFAQHPaI0pvD0U2l6IVLueUAm3nwN7lkY62zZNmlvNx6jNtE4wlbS+CyqSg==} engines: {node: '>= 12.0.0'} hasBin: true - peerDependenciesMeta: - '@parcel/core': - optional: true dependencies: '@parcel/config-default': 2.12.0(@parcel/core@2.12.0)(typescript@4.9.5) '@parcel/core': 2.12.0 diff --git a/scripts/fix-rap.ts b/scripts/fix-rap.ts new file mode 100644 index 0000000000..a57f3bbca6 --- /dev/null +++ b/scripts/fix-rap.ts @@ -0,0 +1,42 @@ +import glob from 'glob'; +import { resolve } from 'path'; +import { readFileSync, writeFileSync } from 'fs'; + +const fixVersions = (packageData: any) => { + ['dependencies', 'devDependencies'].forEach(depType => { + if (packageData[depType]) { + Object.keys(packageData[depType]).forEach(key => { + if (key.match(/^@react-(aria|stately|types)/)) { + packageData[depType][key] = packageData[depType][key].replace('^', ''); + } + }); + } + }); +}; + +const processPackageFiles = (path: string) => { + const filePaths = glob.sync(resolve(path, '**/package.json'), { + ignore: '**/node_modules/**', + }); + + for (const filePath of filePaths) { + try { + const packageData = JSON.parse(readFileSync(filePath, 'utf8')); + fixVersions(packageData); + writeFileSync(filePath, JSON.stringify(packageData, null, 2)); + console.log(`✅ Fixed versions in ${filePath}`); + } catch (error) { + console.error(`Error occurred while processing ${filePath}:`, error.message); + } + } +}; + +const main = async () => { + const dirs = [resolve('app/docs'), resolve('packages')]; + for (const dir of dirs) { + processPackageFiles(dir); + } +}; + + +main().catch(console.error); \ No newline at end of file