mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
* chore: org name change (#4596) * chore: update brand name (#4600) * fix(calendar): function components cannot be given refs (#4614) * docs(modal): fix small typos and add clarifying language (#4629) * chore(deps): bump RA versions (#4611) * chore(deps): bump RA versions * chore(deps): bump @internationalized/date * chore(docs): update RA versions * chore(docs): update versions * chore(docs): use string type * chore(deps): update @react-types versions * refactor(docs): undo version change since they will be removed in another PR * feat: tailwind variants upgrade (#4386) * feat: tailwind variants upgrade * chore: restore npmrc * chore: adjust pkgs * fix: versions * fix: lock file * chore(changeset): update package name * chore(deps): use fixed version * fix(test): incorrect package name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * feat: add fn win alt keys (#4638) * feat: add new keys * feat: add new keys * chore: update docs & storybook as well --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(use-image): load images after props change (#4523) * fix(use-image): load image after props change * chore(changeset): add changeset * refactor(use-image): remove unused props * feat(use-image): add test case * fix(use-image): apply useCallback to load & remove status check * chore(changeset): update package name * feat: global labelPlacement prop (#4346) * feat: adding the support for labelPlacement globally * chore: reafctoring * chore: updating the dependency * chore(changeset): update package name * chore: adding Marcus's suggestions --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * fix(form): use native as default validation behavior (#4425) * fix(form): use native as default validation behavior * docs(form): delete explicit validationBehavior=native * test(form): adjusted form test validation behaviors * chore(form): adjusted stories with forms * chore(changeset): changed form default validation behavior to native * chore(changeset): removed packages with only test changes * chore(changeset): change to patch * chore(changeset): update package name * refactor(docs): update package name * refactor(docs): update to heroui --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * feat(spinner): new spinner variants (#4555) * refactor(spinner): add default variant * feature(spinner): add gradient variant * feature(spinner): add dots variant * feature(spinner): add dots-blink variant * feature(spinner): add spinner-bars * chore(spinner): add variants storybook * chore: adding variants to docs * chore: simplyfying the styles and modifying docs * chore: nits * chore: updating the dots and dots-blink animation * chore: nits * chore: adding Marcus' suggestions * chore: adding Marcus's suggestions * chore: adding junior's suggestions --------- Co-authored-by: Maharshi Alpesh <maharshialpesh@gmail.com> * fix: rename wrapper to tab wrapper (#4636) * fix: rename wrapper to tab wrapper * docs: update * docs: update * docs: update * fix: rename wrapper to tab wrapper * refactor: remove feature request from issue template (#4661) * refactor(.github): remove feature request template * refactor(.github): add a link to redirect to discussion (feature request category) * docs(table): include TS examples to show Selection type usage (#4793) * fix(listbox): unexpected scrollShadow on virtualized listbox (#4784) * fix(listbox): add scroll height & scroll top to listbox * fix(use-data-scroll-overflow): handle scrollHeight & scrollTop in virtualization * chore(changeset): add changeset * refactor(theme): replace left & right by start & end to support RTL (#4782) * fix(date-picker): deprecate dateInputClassNames (#4780) * chore(date-picker): add missing slots comments * fix(date-picker): remove dateInputClassNames * fix(date-picker): use classNames instead of dateInputClassNames * chore(docs): add missing attributes * fix(date-picker): use classNames instead of dateInputClassNames * feat(changeset): add changeset * fix(docs): broken type * refactor(navbar): remove dropdown menu width (#4757) * refactor: remove dropdown menu width * refactor: shorter description * refactor: rename instances of NextUI to Hero UI (#4645) * docs: use the correct org for `img.shields.io` license in README * docs: update opencollective org name * docs: use correct org name in site footer * docs: update image urls for heroui pro sections * docs: update laravel installation keywords in route config * docs: add `heroui` tag to `Introducing HeroUI` blog post * fix: use correct names in `plop/components/src` templates * chore: add empty changeset * fix: revert image urls back to `nextuipro.nyc3.cdn.digitaloceanspaces...` * chore: undo footer change * chore: update incorrect brand name * chore(docs): nextui -> heroui --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * fix(input): missing clear button with file input type (#4599) * fix(theme): sync with input theme on labelPlacement (#4597) * fix(theme): sync with input theme on labelPlacement * chore(select): revise width for labelPlacement * chore(changeset): add changeset * test(input): input interaction tests (#4579) * test(input): user interaction tests * test(input): missing act wrappers --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(calendar): rtl navigation (#4565) * fix(calendar): rtl navigation * chore(changeset): fixed reverse behavior of NextButton and PrevButton in the RTL calendar * chore(changeset): update package name * refactor(calendar): prefer isRTL and use className in theme package instead * chore(changeset): add theme package as well * chore(calendar): add min theme package to 2.4.7 --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * refactor: remove unnecessary className passing to tv and make naming consistent (#4558) * refactor: remove unnecessary className passing to tv * refactor(button): move styles to getButtonProps * refactor: rename classNames to styles to keep the naming consistent * fix: deprecation warning triggered by internal onClick (#4557) * fix(use-aria-link): onClick deprecation warning * fix(use-aria-button): onClick deprecation warning * feat(changeset): add changeset * fix(use-aria-button): incorrect prop name * chore(changeset): update package name * ci: add pkg pr new (#4540) * ci: add pkg pr new * ci: add pkg pr new * chore(workflow): update repo name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * chore(docs): remove shouldBlockScroll prop in Tooltip page (#4539) * fix(use-pagination): controlled page after delay (#4536) * fix(use-pagination): add page to dependency for scrollTo * feat(changeset): add changeset * chore(changeset): update package name * fix(tooltip): accessing element.ref was removed in React 19 issue (#4531) * fix(tooltip): accessing element.ref was removed in React 19 issue * chore(changeset): update package name * fix: correctly dismissable default value (#4524) * fix: correctly dismissable default value * fix: correctly dismissable default value * chore(changeset): update package name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * fix(theme): input height in innerWrapper in Select (#4512) * fix(select): fix input height #4321 * chore(select): changed package name in changeset to theme * chore(select): updated changeset message * chore(changeset): update package name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * fix: inert value in next15 (#4491) * feat: add post install * feat: add postinstall * feat: add postinstall * fix: type * fix: type * fix: next version * chore(changeset): update package name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * refactor: remove cursor-hit in hiddenInputClasses (#4474) * refactor: remove cursor-hit in hiddenInputClasses * Create lazy-ants-exercise.md * chore(changeset): update package name --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * feat(table): virtualization (#4285) * feat: baseline virtualization for table * merge branch canary * fix: table layout * fix: calc header height w layouteffect to offset padding * Merge branch 'canary' into feat/eng-1633-virtualization-for-table * chore: remove unused files and comments * chore: add missing package * feat: add shouldVirtualize conditional to render virtualized-table * feat: update docs for table * feat: use wrapper to support theme styles * chore: add changeset * chore(changeset): update package name * chore(deps): pnpm-lock.yaml * fix(table): outdated package name * chore(changeset): add issue number * fix(deps): keep the version consistent with other components * fix(table): incorrect displayName * refactor(table): use VirtualizedTemplate * chore(deps): bump `@tanstack/react-virtua` * chore(deps): typecheck issue * fix(table): do not use any type * chore: remove auto virtualization --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * feat(toast): introduce Toast component (#4437) * feat: initial commit * chore: adding the animation * chore: nits * chore: fixes and adding draft1 of stories * chore: adding the docs draft * chore: adding the swiping interaction for toast removal * chore: adding the tests * fix: improving the progress bar logix * chore: refactoring and refining the animations * fix: making the animations compatible with the positons * chore: fixing the styles * chore: modifying the animations * chore: improving the animations * chore: adding the decorator to the story-book * chore: fixing the animations and positions * fix: handle expand region on touch * feat: adding the promises support * chore: updating the styles * chore: improving styles * chore: styles correction * fix: adding junior's suggestions * chore: correcting styles * fix: fixing the timer behavior * chore: adding the spinner to the toast * chore: full width for mobile * chore: modifying styles * chore: fixing the positions on smaller devices * chore: adding story with description * chore: adding credits for sonner * fix: adding junior's suggestions * chore: adding the exit animation * fix: adding junior's suggestions * chore: improving the swipe animations * fix: fixing the swipe animations on touch * chore: adding tests * chore: adding swipe threshild and initial position variable * fix: fixing autoclose in timeout * chore: modifying the docs * chore: fixing the conflict * chore: adding marcus' suggestions * chore: adding the bottom animations * chore: modying docs * chore: removing nextui references * chore: adding info about the provider * chore: updating the docs * chore: versions in package.json * chore: nits * chore: adding junior's suggestions * chore: nits * fix: applying junior's suggestions * chore: adding junior's suggestions * chore: using domMax * fix: adding Marcus's suggestions * chore: add global toast props and custom close icon * chore: adding the defaultTimout provider prop * chore: modifying defaultTimeout * chore: nits * fix: adding Marcus' suggestions * chore: fixing bg * chore(deps): bump RA deps * fix: fixing the color discrepancy due to the timer * chore: moving the kapan ai to the left side * refactor(toast): update author * chore: nit * chore: improvements * chore: updating the solid variant --------- Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix(docs): correct Tab usage example (#4821) * chore(docs): add note itemHeight for virtualization (#4822) * chore(docs): add note itemHeight for virtualization * fix: format * fix(docs): fix horizontal scrolling example in scroll-shadow (#4820) * refactor: update author in package.json (#4800) * feat(button): export PressEvent for onPress event typing (#4819) * fix(docs): failed to install dependencies in StackBlitz (#4639) * chore(Docs): remove step 2 from "Using use-theme-hook" (#4797) * fix(docs): incorrect code Modal placement (#4652) * docs: update DatePicker example to remove "time" label as time selection is not supported in this example (#4443) * feat(button): export PressEvent for onPress event typing * revert unnecessary changes * chore: format --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> Co-authored-by: Praharsh Bhatt <30700808+praharshbhatt@users.noreply.github.com> * fix(listbox): pass missing press events to usePress (#4812) * fix(listbox): pass missing press events to usePress * feat(listbox): add test case for press event * chore(changeset): add changeset * fix(checkbox): inherit stroke in CheckboxIcon (#4811) * fix: `SelectItem`, `ListboxItem`, and `AutocompleteItem` not to accept `value` props (#4653) * fix(select): `SelectItem` does not accept value props * refactor: do not use the index as `key` * Update .changeset/light-hairs-draw.md * chore: remove unnecessary `value` props * chore: update changeset * refactor: remove unnecessary value prop --------- Co-authored-by: WK Wong <wingkwong.code@gmail.com> * fix: pkg package scope (#4823) * fix: pkg package scope * fix: pkg package scope * fix: pkg package scope * fix(theme): border radius in Table when isMultiSelectable (#4808) * fix(theme): border radius in Table when isMultiSelectable * chore(theme): added changeset (#4807) * chore: removing the kapa ai for toast doc page (#4833) * fix(accordion): add data-slot attributes to accordion (#4832) * fix(accordion): add data-slot attributes to accordion * chore --------- Co-authored-by: Hovannes Markarian <hovannes.markarian@socrate.fr> Co-authored-by: աӄա <wingkwong.code@gmail.com> * chore(docs): update versions (#4836) * docs(themes): adding theme generator (#4626) * chore: adding xylish's contributions + modifying styles * chore: nextui to heroui * chore: colors in theme generator * chore: radiuses, disable-opacity * chore: fixing the configuration box styles * chore: adding the showcase elemtents * chore: modifying styles * chore: adding the fonts * chore: adding the scaling * chore: removing the calendar * feat: adding the border-width * chore: modifying style for mobile * chore: modifying the styles * chore: removing the NextUI references + small bug fix * chore: adding coderabits reviews * fix: borderWidth not getting applied on breadcrumbs and input * chore: rebasing * chore: modifying the styles * chore: updating the styles for the smaller devices * chore: refactoring * chore: improvements * chore: making the fonts workable * chore: making the fonts workable * chore: modifying the swatch according to the theme * chore: adding the default selected template * chore: modifying mobile styles * chore: fixing the popover * chore: nit * fix: fixing the select styles * chore: modifying the mobile styles * chore: modifying the styles * fix: adding junior's suggestions * fix: fixing the breadcrumb * fix: adding junior's suggestions * feat: introduce NumberInput (#4475) * feat(number-field): init structure * feat(deps): add `@nextui-org/button` & `@react-types/button` * feat(theme): export number-field * feat(number-field): storybook init structure * feat(number-field): add NumberFieldHorizontalStepper * feat(number-field): add NumberFieldHorizontalStepper * feat(theme): init number field theme * feat(number-field): number-field draft * refactor(number-field): revise stepper icons * feat(shared-icons): add ChevronLeftIcon * feat(theme): stepperButton styles * feat(theme): number-field styles * fix(number-field): label layout * feat(number-field): vertical stepper wrapper * feat(number-field): use-number-field (wip) * feat(number-field): add data-direction * feat(theme): center the text if it is horizontal stepper * feat(number-field): add HorizontalStepper * feat(number-field): add HideStepper * chore(number-field): revise minValue & defaultValue * feat(docs): init number field structure * fix(theme): outside-left styles * refactor(theme): remove labelPlacement styles * refactor(number-field): remove labelContent logic * refactor(number-field): remove labelPlacement args * feat(number-field): helper text * feat(number-field): revise number field stories * feat(number-field): description * refactor(number-field): revise number field stories * feat(theme): numberFieldLabelClasses * fix(number-field): incorrect button props * fix(number-field): typing issue on stepper buttons * chore(number-field): add aria-label * refactor(number-field): merge props * fix(number-field): pass originalProps instead * chore(number-field): revise Required story args * feat(number-field): add WithStepValue & WithWheelDisabled & revise stories * chore(number-field): add label to Required * feat(docs): number-field doc page * fix(number-field): typing issue * fix(number-field): test cases * fix(number-field): user.keyboard & defaultValue * fix(number-field): should work with defaultValues * chore(number-field): add type: number * chore(number-field): remove hidden related code * fix(number-field): numeric value * chore(changeset): add changeset * feat(deps): add "@nextui-org/number-field" to docs * feat(react): export `@nextui-org/number-field` * feat(changeset): add @nextui-org/react * feat(docs): number-field examples * chore(number-field): use text instead * refactor(number-field): remove unnecessary filled-within * fix(number-field): test case * chore(number-field): remove aria-label for stepper buttons * feat(docs): add incrementAriaLabel & decrementAriaLabel to NumberField * chore(number-field): reorder WithFormatOptions * fix(deps): update number-field's peerDependencies & dependencies * feat(number-field): hidden input for holding numeric vaule * fix(docs): number field title * feat(docs): add format options to number field * chore(docs): revise number field content * chore(number-field): add type to useDOMRef * fix(number-field): clear button * fix(theme): clear button styles * refactor(theme): stepper button styles * chore(number-field): accept stepperButton class * fix(theme): helper wrapper padding * feat(deps): add `@react-aria/i18n` * fix(number-field): use locale from `@react-aria/i18n` * fix(deps): dependency order * fix(docs): incorrect command * chore(docs): remove type=number * chore(theme): add padding to stepper wrapper * fix(number-field): avoid resetting value * fix(number-field): storybook * chore(docs): remove custom impl * chore(docs): update docs code & content * chore(number-field): migrate to heroui * chore(number-field): migrate to heroui * chore(number-field): migrate to heroui * chore: rename to number input * fix(number-input): incorrect import * chore(docs): rename to number input * chore: change to number input * refactor(number-input): change label to amount * fix(docs): use heroui commands * chore(changeset): update package name * refactor(number-input): remove steps * refactor: remove helper text * feat(number-input): label placement * refactor(number-input): rename stepper * fix(theme): isClearable * feat(docs): add label placements * refactor(docs): update number-input content * fix(docs): incorrect file * feat(docs): add lablePlacement * refactor(docs): remove labelPlacement & startContent * refactor(docs): remove helperText * refactor(docs): remove helperText * refactor(docs): revise description * feat(number-input): add data-slot for stepper-wrapper * fix(number-input): test cases * fix(docs): unexpected change * refactor(number-input): update outdated info * fix(docs): coderabbitai comments * refactor: remove validationState * fix(docs): typo * chore(deps): remove unnecessary dep * chore(deps): bump RA versions * chore(number-input): apply latest labelPlacement change * refactor(number-input): update author * refactor(number-input): revise stepper wrapper alignment * refactor(number-input): stepper button styles * chore(number-input): add disableRipple * fix(theme): increase stepper button click area * fix(number-input): sync latest validationBehavior changes * fix(number-input): pass validationBehavior to useAriaNumberInput * chore(docs): add import react * chore(number-input): remove HorizontalStepper story * chore(number-input): enable ripple * fix(number-input): remove number type * refactor(theme): follow input clear button styles * feat(theme): add color for stepperButton * fix(theme): revise stepperButton size for outside & outside-left cases * fix(number-input): typo * chore(docs): update description for wheel * chore(theme): change opacity when pressed * chore(number-input): add disableRipple * Update .changeset/witty-flies-reflect.md * fix(theme): add hover opacity effect --------- Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * chore(docs): revised tags in doc routes for 2.7.0 (#4777) * chore(docs): remove last version update tags * chore(docs): add updated tag for 2.7.0 * chore(docs): updated table * chore(docs): update search meta * chore(docs): update github info * Merge branch 'canary' into docs/eng-2003 * chore(docs): update routes.json * chore(docs): update meta info * chore: improve theme builder * v2.7.0 * chore: v2.7.0 combined changeset * fix: changeset * fix: peer deps * feat: toast api improved * chore: toast styles improved * fix: toast styles * chore: toast width style changed * fix: changeset release * fix: changeset peerdeps * chore: toast styles improved * refactor(pagination): rtl (#4843) * refactor(pagination): rtl * chore(changeset): add changeset * feat: new spinner variant * fix(docs): popover shouldBlockScroll default value (#4851) * fix(select): select scroll content will close immediately when popover on click (#4849) * chore(select): update select deps * fix(select): select scroll content will close immediately when popover on click * chore(select): add .changeset file * chore(changeset): add issue number --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> * feat(calendar): add firstDayOfWeek (#4852) * feat(calendar): add firstDayOfWeek * feat(docs): add firstDayOfWeek in Calendar docs * feat(calendar): add firstDayOfWeek to range calendar * feat(docs): add firstDayOfWeek to API table * feat: add firstDayOfWeek to date picker & date range picker * feat(docs): add firstDayOfWeek * feat(changeset): add changeset * feat: add firstDayOfWeek option in storybook * feat(docs): export firstDayOfWeek * chore(docs): update title * chore: spinner variants updated * feat: v2.7.0 blog * ci(changesets): version packages (#4601) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * chore: manual release * fix(toast): fixing maxVisibleToasts, solid variant promise, promise timer (#4881) * fix: maxVisibleToasts functionality * chore: adding the changeset * fix: starting the toast timer when the promise is resolved * fix: spinner invisible in solid variants * fix(toast): shouldShowTimeoutProgess typo (#4858) * fix(toast): shouldShowTimeoutProgess typo * fix(toast): shouldShowTimeoutProgess typo * fix(select): pass form prop to hidden-select (#4854) * fix(select): pass form prop to hidden-select * chore(select): changeset * fix(spinner): Attempted to call useProviderContext() from the server (#4904) * fix(spinner): add banner in tsup.config.ts * chore(changeset): add changeset * chore(docs): update breaking changes in blog * chore(docs): handled in PR4905 * fix(number-input): onChange event in number input (#4907) * fix(number-input): onChange event in number input * chore(number-input): remove duplicate test case * fix(breadcrumb): broken start & end content (#4921) (#4922) * refactor: build process (#4909) * chore: update component description * chore: exclude stories-utils & test-utils * chore(utilities): remove unused info * chore(deps): pnpm-lock.yaml * fix: incorrect system & theme version in peerDependencies (#4901) * fix: incorrect system & theme version in peerDependencies * fix: incorrect system & theme version in peerDependencies * chore(changeset): add changeset * fix(changeset): remove `@heroui/stories-utils` and `@heroui/test-utils` from ignore list (#4939) * fix(docs): include .npmrc in sandbox (#4951) * fix(input): esc key to clear input value (#4892) * chore: theme generator credits * fix: blog date and spinner default variant * fix: #4850 Solve Pressing ESC doesn't clear input value * fix: #4850 code review change * fix: undo changes in apps/docs/content/blog/v2.7.0.mdx and add a test case for my changes * fix: run through the test cases successfully * fix: change md content * fix: using isClearable not clear the value * fix: add number-input clearable esc clear * fix: edit review problem * fix: delete unless file * chore(changeset): update changeset * fix: add inputProps.onKeyDown * fix: pressing ESC key in a read-only input not clear --------- Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> Co-authored-by: աӄա <wingkwong.code@gmail.com> * ci(changesets): version packages (#4896) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> * v2.7.4 * chore(deps): bump turbo version (#4958) * chore(deps): bump turbo version * chore(root): update turbo.json * fix(docs): typecheck errors * fix(docs): typecheck errors * fix(deps): bump react-aria versions (#4998) * fix(deps): bump react-aria versions * chore(changeset): add changeset * chore(hooks): sync with RA's useButton * fix(use-aria-button): support aria-current * fix(docs): remove priority prop * fix(toast): remove priority queue & animation * fix(toast): remove deprecated logic * fix(popover): avoid passing preventFocusOnPress to non heroui button * fix: sync with UsePopover * fix(popover): prevent submenus from closing on scroll * fix(tests): test cases related to press refactoring * fix(toast): toast should appear above overlay and adding regionProps to ToastProvider (#5001) * fix: toast should be above the modal * fix: renaming the loadingIcon to loadingComponent * chore: adding changeset * chore: fixing conflicts * chore: adding the region props * fix: adding Marcus' suggestions * fix: marcus' suggestions * Revert "fix: renaming the loadingIcon to loadingComponent" This reverts commit 4c6bf32765542ba8fe4d862005e6f0ac75c5e49a. * chore: adding marcus' suggestions * fix(number-stepper): stepper button pseudo height (#4968) * fix(progress): incorrect size in indeterminate storybook (#4967) * fix: shouldShowTimeoutProgress typo (#4961) * chore(workflows): update runner (#4960) * fix: a small eye icon for password input in edge(#4927) (#4950) * fix(progress): add RTL support to the progress component (#4911) * fix(progress): add RTL support to the progress component (#4908) * docs(progress): add changeset notes * refactor: resolved code review notes * refactor: removed unnecessary change * refactor: undo unnecessary change * fix: build * fix(toast): unexpected toast animation (#5003) * fix(deps): use RA beta toast * chore(toast): remove non-existing wrapUpdate * chore(changeset): add changeset * ci(changesets): version packages (#5002) Co-authored-by: Junior Garcia <jrgarciadev@gmail.com> --------- Co-authored-by: աӄա <wingkwong.code@gmail.com> Co-authored-by: millmason <jmsoper@protonmail.com> Co-authored-by: winches <329487092@qq.com> Co-authored-by: Maharshi Alpesh <maharshialpesh@gmail.com> Co-authored-by: Peterl561 <76144929+Peterl561@users.noreply.github.com> Co-authored-by: Paul Ebose <49006567+plbstl@users.noreply.github.com> Co-authored-by: Zarin <thesharifi.maruf@gmail.com> Co-authored-by: Shrinidhi Upadhyaya <shrinidhiupadhyaya1195@gmail.com> Co-authored-by: Avan <layouwen@gmail.com> Co-authored-by: Vincentius Roger Kuswara <vincentiusrkuswara@gmail.com> Co-authored-by: Ryo Matsukawa <76232929+ryo-manba@users.noreply.github.com> Co-authored-by: Praharsh Bhatt <30700808+praharshbhatt@users.noreply.github.com> Co-authored-by: Adrian Szarapow <63786007+Adee1499@users.noreply.github.com> Co-authored-by: Hova25 <75216176+Hova25@users.noreply.github.com> Co-authored-by: Hovannes Markarian <hovannes.markarian@socrate.fr> Co-authored-by: Tsuki <76603360+sudongyuer@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Steve Mosley <steve@starter4ten.com> Co-authored-by: LinYongLu <88522000+linyonglu@users.noreply.github.com> Co-authored-by: Waleed Asender <waleed.asender@mobasher.sa>
576 lines
17 KiB
TypeScript
576 lines
17 KiB
TypeScript
import type {InputVariantProps, SlotsToClasses, InputSlots} from "@heroui/theme";
|
|
import type {AriaTextFieldOptions} from "@react-aria/textfield";
|
|
|
|
import {
|
|
HTMLHeroUIProps,
|
|
mapPropsVariants,
|
|
PropGetter,
|
|
useLabelPlacement,
|
|
useProviderContext,
|
|
} from "@heroui/system";
|
|
import {useSafeLayoutEffect} from "@heroui/use-safe-layout-effect";
|
|
import {AriaTextFieldProps} from "@react-types/textfield";
|
|
import {useFocusRing} from "@react-aria/focus";
|
|
import {input} from "@heroui/theme";
|
|
import {useDOMRef, filterDOMProps} from "@heroui/react-utils";
|
|
import {useFocusWithin, useHover, usePress} from "@react-aria/interactions";
|
|
import {clsx, dataAttr, isEmpty, objectToDeps, safeAriaLabel} from "@heroui/shared-utils";
|
|
import {useControlledState} from "@react-stately/utils";
|
|
import {useMemo, Ref, useCallback, useState} from "react";
|
|
import {chain, mergeProps} from "@react-aria/utils";
|
|
import {useTextField} from "@react-aria/textfield";
|
|
import {FormContext, useSlottedContext} from "@heroui/form";
|
|
|
|
export interface Props<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>
|
|
extends Omit<HTMLHeroUIProps<"input">, keyof InputVariantProps> {
|
|
/**
|
|
* Ref to the DOM node.
|
|
*/
|
|
ref?: Ref<T>;
|
|
/**
|
|
* Ref to the container DOM node.
|
|
*/
|
|
baseRef?: Ref<HTMLDivElement>;
|
|
/**
|
|
* Ref to the input wrapper DOM node.
|
|
* This is the element that wraps the input label and the innerWrapper when the labelPlacement="inside"
|
|
* and the input has start/end content.
|
|
*/
|
|
wrapperRef?: Ref<HTMLDivElement>;
|
|
/**
|
|
* Ref to the input inner wrapper DOM node.
|
|
* This is the element that wraps the input and the start/end content when passed.
|
|
*/
|
|
innerWrapperRef?: Ref<HTMLDivElement>;
|
|
/**
|
|
* Element to be rendered in the left side of the input.
|
|
*/
|
|
startContent?: React.ReactNode;
|
|
/**
|
|
* Element to be rendered in the right side of the input.
|
|
* if you pass this prop and the `onClear` prop, the passed element
|
|
* will have the clear button props and it will be rendered instead of the
|
|
* default clear button.
|
|
*/
|
|
endContent?: React.ReactNode;
|
|
/**
|
|
* Classname or List of classes to change the classNames of the element.
|
|
* if `className` is passed, it will be added to the base slot.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* <Input classNames={{
|
|
* base:"base-classes",
|
|
* label: "label-classes",
|
|
* mainWrapper: "main-wrapper-classes",
|
|
* inputWrapper: "input-wrapper-classes",
|
|
* innerWrapper: "inner-wrapper-classes",
|
|
* input: "input-classes",
|
|
* clearButton: "clear-button-classes",
|
|
* helperWrapper: "helper-wrapper-classes",
|
|
* description: "description-classes",
|
|
* errorMessage: "error-message-classes",
|
|
* }} />
|
|
* ```
|
|
*/
|
|
classNames?: SlotsToClasses<InputSlots>;
|
|
/**
|
|
* Callback fired when the value is cleared.
|
|
* if you pass this prop, the clear button will be shown.
|
|
*/
|
|
onClear?: () => void;
|
|
/**
|
|
* React aria onChange event.
|
|
*/
|
|
onValueChange?: (value: string) => void;
|
|
}
|
|
|
|
type AutoCapitalize = AriaTextFieldOptions<"input">["autoCapitalize"];
|
|
|
|
export type UseInputProps<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement> =
|
|
Props<T> & Omit<AriaTextFieldProps, "onChange"> & InputVariantProps;
|
|
|
|
export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTMLInputElement>(
|
|
originalProps: UseInputProps<T>,
|
|
) {
|
|
const globalContext = useProviderContext();
|
|
const {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
|
|
|
|
const [props, variantProps] = mapPropsVariants(originalProps, input.variantKeys);
|
|
|
|
const {
|
|
ref,
|
|
as,
|
|
type,
|
|
label,
|
|
baseRef,
|
|
wrapperRef,
|
|
description,
|
|
className,
|
|
classNames,
|
|
autoFocus,
|
|
startContent,
|
|
endContent,
|
|
onClear,
|
|
onChange,
|
|
validationState,
|
|
validationBehavior = formValidationBehavior ?? globalContext?.validationBehavior ?? "native",
|
|
innerWrapperRef: innerWrapperRefProp,
|
|
onValueChange = () => {},
|
|
...otherProps
|
|
} = props;
|
|
|
|
const handleValueChange = useCallback(
|
|
(value: string | undefined) => {
|
|
onValueChange(value ?? "");
|
|
},
|
|
[onValueChange],
|
|
);
|
|
|
|
const [isFocusWithin, setFocusWithin] = useState(false);
|
|
|
|
const Component = as || "div";
|
|
|
|
const disableAnimation =
|
|
originalProps.disableAnimation ?? globalContext?.disableAnimation ?? false;
|
|
|
|
const domRef = useDOMRef<T>(ref);
|
|
|
|
const baseDomRef = useDOMRef<HTMLDivElement>(baseRef);
|
|
const inputWrapperRef = useDOMRef<HTMLDivElement>(wrapperRef);
|
|
const innerWrapperRef = useDOMRef<HTMLDivElement>(innerWrapperRefProp);
|
|
|
|
const [inputValue, setInputValue] = useControlledState<string | undefined>(
|
|
props.value,
|
|
props.defaultValue ?? "",
|
|
handleValueChange,
|
|
);
|
|
|
|
const isFileTypeInput = type === "file";
|
|
const hasUploadedFiles = ((domRef?.current as HTMLInputElement)?.files?.length ?? 0) > 0;
|
|
const isFilledByDefault = ["date", "time", "month", "week", "range"].includes(type!);
|
|
const isFilled = !isEmpty(inputValue) || isFilledByDefault || hasUploadedFiles;
|
|
const isFilledWithin = isFilled || isFocusWithin;
|
|
const isHiddenType = type === "hidden";
|
|
const isMultiline = originalProps.isMultiline;
|
|
|
|
const baseStyles = clsx(classNames?.base, className, isFilled ? "is-filled" : "");
|
|
|
|
const handleClear = useCallback(() => {
|
|
if (isFileTypeInput) {
|
|
(domRef.current as HTMLInputElement).value = "";
|
|
} else {
|
|
setInputValue("");
|
|
}
|
|
|
|
onClear?.();
|
|
domRef.current?.focus();
|
|
}, [setInputValue, onClear, isFileTypeInput]);
|
|
|
|
// if we use `react-hook-form`, it will set the input value using the ref in register
|
|
// i.e. setting ref.current.value to something which is uncontrolled
|
|
// hence, sync the state with `ref.current.value`
|
|
useSafeLayoutEffect(() => {
|
|
if (!domRef.current) return;
|
|
|
|
setInputValue(domRef.current.value);
|
|
}, [domRef.current]);
|
|
|
|
const {
|
|
labelProps,
|
|
inputProps,
|
|
isInvalid: isAriaInvalid,
|
|
validationErrors,
|
|
validationDetails,
|
|
descriptionProps,
|
|
errorMessageProps,
|
|
} = useTextField<any>(
|
|
{
|
|
...originalProps,
|
|
validationBehavior,
|
|
autoCapitalize: originalProps.autoCapitalize as AutoCapitalize,
|
|
value: inputValue,
|
|
"aria-label": safeAriaLabel(
|
|
originalProps["aria-label"],
|
|
originalProps.label,
|
|
originalProps.placeholder,
|
|
),
|
|
inputElementType: isMultiline ? "textarea" : "input",
|
|
onChange: setInputValue,
|
|
},
|
|
domRef,
|
|
);
|
|
|
|
if (isFileTypeInput) {
|
|
// for input[type="file"], we don't need `value` and `onChange` from `useTextField`
|
|
// otherwise, the default value with empty string will block the first attempt of file upload
|
|
// hence, remove `value` and `onChange` attribute here
|
|
delete inputProps.value;
|
|
delete inputProps.onChange;
|
|
}
|
|
|
|
const {isFocusVisible, isFocused, focusProps} = useFocusRing({
|
|
autoFocus,
|
|
isTextInput: true,
|
|
});
|
|
|
|
const {isHovered, hoverProps} = useHover({isDisabled: !!originalProps?.isDisabled});
|
|
|
|
const {isHovered: isLabelHovered, hoverProps: labelHoverProps} = useHover({
|
|
isDisabled: !!originalProps?.isDisabled,
|
|
});
|
|
|
|
const {focusProps: clearFocusProps, isFocusVisible: isClearButtonFocusVisible} = useFocusRing();
|
|
|
|
const {focusWithinProps} = useFocusWithin({
|
|
onFocusWithinChange: setFocusWithin,
|
|
});
|
|
|
|
const {pressProps: clearPressProps} = usePress({
|
|
isDisabled: !!originalProps?.isDisabled || !!originalProps?.isReadOnly,
|
|
onPress: handleClear,
|
|
});
|
|
|
|
const isInvalid = validationState === "invalid" || isAriaInvalid;
|
|
|
|
const labelPlacement = useLabelPlacement({
|
|
labelPlacement: originalProps.labelPlacement,
|
|
label,
|
|
});
|
|
|
|
const errorMessage =
|
|
typeof props.errorMessage === "function"
|
|
? props.errorMessage({isInvalid, validationErrors, validationDetails})
|
|
: props.errorMessage || validationErrors?.join(" ");
|
|
const isClearable = !!onClear || originalProps.isClearable;
|
|
const hasElements = !!label || !!description || !!errorMessage;
|
|
const hasPlaceholder = !!props.placeholder;
|
|
const hasLabel = !!label;
|
|
const hasHelper = !!description || !!errorMessage;
|
|
const shouldLabelBeOutside = labelPlacement === "outside" || labelPlacement === "outside-left";
|
|
const shouldLabelBeInside = labelPlacement === "inside";
|
|
const isPlaceholderShown = domRef.current
|
|
? (!domRef.current.value || domRef.current.value === "" || !inputValue || inputValue === "") &&
|
|
hasPlaceholder
|
|
: false;
|
|
const isOutsideLeft = labelPlacement === "outside-left";
|
|
|
|
const hasStartContent = !!startContent;
|
|
const isLabelOutside = shouldLabelBeOutside
|
|
? labelPlacement === "outside-left" ||
|
|
hasPlaceholder ||
|
|
(labelPlacement === "outside" && hasStartContent)
|
|
: false;
|
|
const isLabelOutsideAsPlaceholder =
|
|
labelPlacement === "outside" && !hasPlaceholder && !hasStartContent;
|
|
|
|
const slots = useMemo(
|
|
() =>
|
|
input({
|
|
...variantProps,
|
|
isInvalid,
|
|
labelPlacement,
|
|
isClearable,
|
|
disableAnimation,
|
|
}),
|
|
[
|
|
objectToDeps(variantProps),
|
|
isInvalid,
|
|
labelPlacement,
|
|
isClearable,
|
|
hasStartContent,
|
|
disableAnimation,
|
|
],
|
|
);
|
|
|
|
const getBaseProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
ref: baseDomRef,
|
|
className: slots.base({class: baseStyles}),
|
|
"data-slot": "base",
|
|
"data-filled": dataAttr(
|
|
isFilled || hasPlaceholder || hasStartContent || isPlaceholderShown || isFileTypeInput,
|
|
),
|
|
"data-filled-within": dataAttr(
|
|
isFilledWithin ||
|
|
hasPlaceholder ||
|
|
hasStartContent ||
|
|
isPlaceholderShown ||
|
|
isFileTypeInput,
|
|
),
|
|
"data-focus-within": dataAttr(isFocusWithin),
|
|
"data-focus-visible": dataAttr(isFocusVisible),
|
|
"data-readonly": dataAttr(originalProps.isReadOnly),
|
|
"data-focus": dataAttr(isFocused),
|
|
"data-hover": dataAttr(isHovered || isLabelHovered),
|
|
"data-required": dataAttr(originalProps.isRequired),
|
|
"data-invalid": dataAttr(isInvalid),
|
|
"data-disabled": dataAttr(originalProps.isDisabled),
|
|
"data-has-elements": dataAttr(hasElements),
|
|
"data-has-helper": dataAttr(hasHelper),
|
|
"data-has-label": dataAttr(hasLabel),
|
|
"data-has-value": dataAttr(!isPlaceholderShown),
|
|
"data-hidden": dataAttr(isHiddenType),
|
|
...focusWithinProps,
|
|
...props,
|
|
};
|
|
},
|
|
[
|
|
slots,
|
|
baseStyles,
|
|
isFilled,
|
|
isFocused,
|
|
isHovered,
|
|
isLabelHovered,
|
|
isInvalid,
|
|
hasHelper,
|
|
hasLabel,
|
|
hasElements,
|
|
isPlaceholderShown,
|
|
hasStartContent,
|
|
isFocusWithin,
|
|
isFocusVisible,
|
|
isFilledWithin,
|
|
hasPlaceholder,
|
|
focusWithinProps,
|
|
isHiddenType,
|
|
originalProps.isReadOnly,
|
|
originalProps.isRequired,
|
|
originalProps.isDisabled,
|
|
],
|
|
);
|
|
|
|
const getLabelProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
"data-slot": "label",
|
|
className: slots.label({class: classNames?.label}),
|
|
...mergeProps(labelProps, labelHoverProps, props),
|
|
};
|
|
},
|
|
[slots, isLabelHovered, labelProps, classNames?.label],
|
|
);
|
|
|
|
const handleKeyDown = useCallback(
|
|
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (
|
|
e.key === "Escape" &&
|
|
inputValue &&
|
|
(isClearable || onClear) &&
|
|
!originalProps.isReadOnly
|
|
) {
|
|
setInputValue("");
|
|
onClear?.();
|
|
}
|
|
},
|
|
[inputValue, setInputValue, onClear, isClearable, originalProps.isReadOnly],
|
|
);
|
|
|
|
const getInputProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
"data-slot": "input",
|
|
"data-filled": dataAttr(isFilled),
|
|
"data-filled-within": dataAttr(isFilledWithin),
|
|
"data-has-start-content": dataAttr(hasStartContent),
|
|
"data-has-end-content": dataAttr(!!endContent),
|
|
className: slots.input({
|
|
class: clsx(
|
|
classNames?.input,
|
|
isFilled ? "is-filled" : "",
|
|
isMultiline ? "pe-0" : "",
|
|
type === "password" ? "[&::-ms-reveal]:hidden" : "",
|
|
),
|
|
}),
|
|
...mergeProps(
|
|
focusProps,
|
|
inputProps,
|
|
filterDOMProps(otherProps, {
|
|
enabled: true,
|
|
labelable: true,
|
|
omitEventNames: new Set(Object.keys(inputProps)),
|
|
}),
|
|
props,
|
|
),
|
|
"aria-readonly": dataAttr(originalProps.isReadOnly),
|
|
onChange: chain(inputProps.onChange, onChange),
|
|
onKeyDown: chain(inputProps.onKeyDown, props.onKeyDown, handleKeyDown),
|
|
ref: domRef,
|
|
};
|
|
},
|
|
[
|
|
slots,
|
|
inputValue,
|
|
focusProps,
|
|
inputProps,
|
|
otherProps,
|
|
isFilled,
|
|
isFilledWithin,
|
|
hasStartContent,
|
|
endContent,
|
|
classNames?.input,
|
|
originalProps.isReadOnly,
|
|
originalProps.isRequired,
|
|
onChange,
|
|
handleKeyDown,
|
|
],
|
|
);
|
|
|
|
const getInputWrapperProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
ref: inputWrapperRef,
|
|
"data-slot": "input-wrapper",
|
|
"data-hover": dataAttr(isHovered || isLabelHovered),
|
|
"data-focus-visible": dataAttr(isFocusVisible),
|
|
"data-focus": dataAttr(isFocused),
|
|
className: slots.inputWrapper({
|
|
class: clsx(classNames?.inputWrapper, isFilled ? "is-filled" : ""),
|
|
}),
|
|
...mergeProps(props, hoverProps),
|
|
onClick: (e) => {
|
|
if (domRef.current && e.currentTarget === e.target) {
|
|
domRef.current.focus();
|
|
}
|
|
},
|
|
style: {
|
|
cursor: "text",
|
|
...props.style,
|
|
},
|
|
};
|
|
},
|
|
[
|
|
slots,
|
|
isHovered,
|
|
isLabelHovered,
|
|
isFocusVisible,
|
|
isFocused,
|
|
inputValue,
|
|
classNames?.inputWrapper,
|
|
],
|
|
);
|
|
|
|
const getInnerWrapperProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
ref: innerWrapperRef,
|
|
"data-slot": "inner-wrapper",
|
|
onClick: (e) => {
|
|
if (domRef.current && e.currentTarget === e.target) {
|
|
domRef.current.focus();
|
|
}
|
|
},
|
|
className: slots.innerWrapper({
|
|
class: clsx(classNames?.innerWrapper, props?.className),
|
|
}),
|
|
};
|
|
},
|
|
[slots, classNames?.innerWrapper],
|
|
);
|
|
|
|
const getMainWrapperProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
"data-slot": "main-wrapper",
|
|
className: slots.mainWrapper({
|
|
class: clsx(classNames?.mainWrapper, props?.className),
|
|
}),
|
|
};
|
|
},
|
|
[slots, classNames?.mainWrapper],
|
|
);
|
|
|
|
const getHelperWrapperProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
"data-slot": "helper-wrapper",
|
|
className: slots.helperWrapper({
|
|
class: clsx(classNames?.helperWrapper, props?.className),
|
|
}),
|
|
};
|
|
},
|
|
[slots, classNames?.helperWrapper],
|
|
);
|
|
|
|
const getDescriptionProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
...descriptionProps,
|
|
"data-slot": "description",
|
|
className: slots.description({class: clsx(classNames?.description, props?.className)}),
|
|
};
|
|
},
|
|
[slots, classNames?.description],
|
|
);
|
|
|
|
const getErrorMessageProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
...errorMessageProps,
|
|
"data-slot": "error-message",
|
|
className: slots.errorMessage({class: clsx(classNames?.errorMessage, props?.className)}),
|
|
};
|
|
},
|
|
[slots, errorMessageProps, classNames?.errorMessage],
|
|
);
|
|
|
|
const getClearButtonProps: PropGetter = useCallback(
|
|
(props = {}) => {
|
|
return {
|
|
...props,
|
|
type: "button",
|
|
tabIndex: -1,
|
|
disabled: originalProps.isDisabled,
|
|
"aria-label": "clear input",
|
|
"data-slot": "clear-button",
|
|
"data-focus-visible": dataAttr(isClearButtonFocusVisible),
|
|
className: slots.clearButton({
|
|
class: clsx(classNames?.clearButton, props?.className),
|
|
}),
|
|
...mergeProps(clearPressProps, clearFocusProps),
|
|
};
|
|
},
|
|
[slots, isClearButtonFocusVisible, clearPressProps, clearFocusProps, classNames?.clearButton],
|
|
);
|
|
|
|
return {
|
|
Component,
|
|
classNames,
|
|
domRef,
|
|
label,
|
|
description,
|
|
startContent,
|
|
endContent,
|
|
labelPlacement,
|
|
isClearable,
|
|
hasHelper,
|
|
hasStartContent,
|
|
isLabelOutside,
|
|
isOutsideLeft,
|
|
isLabelOutsideAsPlaceholder,
|
|
shouldLabelBeOutside,
|
|
shouldLabelBeInside,
|
|
hasPlaceholder,
|
|
isInvalid,
|
|
errorMessage,
|
|
getBaseProps,
|
|
getLabelProps,
|
|
getInputProps,
|
|
getMainWrapperProps,
|
|
getInputWrapperProps,
|
|
getInnerWrapperProps,
|
|
getHelperWrapperProps,
|
|
getDescriptionProps,
|
|
getErrorMessageProps,
|
|
getClearButtonProps,
|
|
};
|
|
}
|
|
|
|
export type UseInputReturn = ReturnType<typeof useInput>;
|