From b8c214eebb56b1cc87b42e2ef6c265b6f919f7ae Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 20 Feb 2023 12:26:17 -0500 Subject: [PATCH] Make React types more compatible with other libraries (#2282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Export explicit props types * wip * wip * wip * wip dialog types * wip * Fix build * Upgrade esbuild * Add aliased types for ComponentLabel and ComponentDescription * Update lockfile * Update changelog * Update exported prop type names * Make onChange optional * Update tests * Use `never` in CleanProps Using a branded type doesn’t work properly with unions * Fix types * wip * work on types * wip * wip * Tweak types in render helpers * Fix CS * Fix changelog * Tweak render prop types for combobox * Update hidden props type name * remove unused type * Tweak types * Update TypeScript version --- package.json | 4 +- packages/@headlessui-react/CHANGELOG.md | 5 + .../src/components/combobox/combobox.test.tsx | 5 +- .../src/components/combobox/combobox.tsx | 175 ++++++++--- .../components/description/description.tsx | 25 +- .../src/components/dialog/dialog.tsx | 129 +++++++-- .../components/disclosure/disclosure.test.tsx | 2 +- .../src/components/disclosure/disclosure.tsx | 72 +++-- .../src/components/focus-trap/focus-trap.tsx | 251 ++++++++-------- .../src/components/label/label.tsx | 28 +- .../src/components/listbox/listbox.tsx | 161 ++++++++--- .../src/components/menu/menu.test.tsx | 2 +- .../src/components/menu/menu.tsx | 104 +++++-- .../src/components/popover/popover.test.tsx | 2 +- .../src/components/popover/popover.tsx | 116 ++++++-- .../src/components/portal/portal.tsx | 40 ++- .../components/radio-group/radio-group.tsx | 88 ++++-- .../src/components/switch/switch.tsx | 66 +++-- .../src/components/tabs/tabs.tsx | 121 ++++++-- .../src/components/transitions/transition.tsx | 55 +++- .../@headlessui-react/src/internal/hidden.tsx | 23 +- packages/@headlessui-react/src/types.ts | 14 +- .../@headlessui-react/src/utils/render.ts | 22 +- packages/@headlessui-react/tsconfig.json | 2 +- .../pages/popover/popover.tsx | 2 +- yarn.lock | 272 +++++++++--------- 26 files changed, 1226 insertions(+), 560 deletions(-) diff --git a/package.json b/package.json index ed0d5c1730..13dfe2b4dd 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@swc/jest": "^0.2.17", "@testing-library/jest-dom": "^5.16.4", "@types/node": "^14.14.22", - "esbuild": "^0.14.11", + "esbuild": "^0.17.8", "fast-glob": "^3.2.11", "husky": "^4.3.8", "jest": "26", @@ -51,6 +51,6 @@ "prettier-plugin-tailwindcss": "^0.1.4", "rimraf": "^3.0.2", "tslib": "^2.3.1", - "typescript": "^4.5.4" + "typescript": "^4.9.5" } } diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 85e823dc38..76b634e17b 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add explicit props types for every component ([#2282](https://github.com/tailwindlabs/headlessui/pull/2282)) + ### Fixed - Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290)) - Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293)) +- Make React types more compatible with other libraries ([#2282](https://github.com/tailwindlabs/headlessui/pull/2282)) ## [1.7.11] - 2023-02-15 diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index 62b56953f7..3fc8957555 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -424,7 +424,10 @@ describe('Rendering', () => { render( - value.name} /> + value.name} + onChange={NOOP} + /> {data.map((person) => ( diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index df6e89338e..69513abbc6 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -31,7 +31,15 @@ import { useTreeWalker } from '../../hooks/use-tree-walker' import { calculateActiveIndex, Focus } from '../../utils/calculate-active-index' import { disposables } from '../../utils/disposables' -import { forwardRefWithAs, render, compact, PropsForFeatures, Features } from '../../utils/render' +import { + forwardRefWithAs, + render, + compact, + PropsForFeatures, + Features, + HasDisplayName, + RefProp, +} from '../../utils/render' import { isDisabledReactIssue7711 } from '../../utils/bugs' import { match } from '../../utils/match' import { objectToFormEntries } from '../../utils/form' @@ -313,12 +321,12 @@ function stateReducer(state: StateDefinition, action: Actions) { // --- let DEFAULT_COMBOBOX_TAG = Fragment -interface ComboboxRenderPropArg { +interface ComboboxRenderPropArg { open: boolean disabled: boolean activeIndex: number | null - activeOption: T | null - value: T + activeOption: TActive | null + value: TValue } type O = 'value' | 'defaultValue' | 'nullable' | 'multiple' | 'onChange' | 'by' @@ -336,7 +344,7 @@ type ComboboxValueProps< multiple: true onChange?(value: EnsureArray): void by?: ByComparator - } & Props>, O>) + } & Props, TValue>, O>) | ({ value?: TValue | null defaultValue?: TValue | null @@ -352,7 +360,7 @@ type ComboboxValueProps< multiple: true onChange?(value: EnsureArray): void by?: ByComparator ? U : TValue> - } & Expand>, O>>) + } & Expand, TValue>, O>>) | ({ value?: TValue nullable?: false @@ -364,7 +372,7 @@ type ComboboxValueProps< { nullable?: TNullable; multiple?: TMultiple } > -type ComboboxProps< +export type ComboboxProps< TValue, TNullable extends boolean | undefined, TMultiple extends boolean | undefined, @@ -678,7 +686,6 @@ function ComboboxFn ) } -let ComboboxRoot = forwardRefWithAs(ComboboxFn) // --- @@ -697,23 +704,27 @@ type InputPropsWeControl = | 'onChange' | 'displayValue' -let Input = forwardRefWithAs(function Input< +export type ComboboxInputProps = Props< + TTag, + InputRenderPropArg, + InputPropsWeControl +> & { + displayValue?(item: TType): string + onChange?(event: React.ChangeEvent): void +} + +function InputFn< TTag extends ElementType = typeof DEFAULT_INPUT_TAG, // TODO: One day we will be able to infer this type from the generic in Combobox itself. // But today is not that day.. TType = Parameters[0]['value'] ->( - props: Props & { - displayValue?(item: TType): string - onChange(event: React.ChangeEvent): void - }, - ref: Ref -) { +>(props: ComboboxInputProps, ref: Ref) { let internalId = useId() let { id = `headlessui-combobox-input-${internalId}`, onChange, displayValue, + // @ts-ignore: We know this MAY NOT exist for a given tag but we only care when it _does_ exist. type = 'text', ...theirProps } = props @@ -988,7 +999,7 @@ let Input = forwardRefWithAs(function Input< defaultTag: DEFAULT_INPUT_TAG, name: 'Combobox.Input', }) -}) +} // --- @@ -999,7 +1010,7 @@ interface ButtonRenderPropArg { value: any } type ButtonPropsWeControl = - | 'type' + // | 'type' // While we do "control" this prop we allow it to be overridden | 'tabIndex' | 'aria-haspopup' | 'aria-controls' @@ -1009,8 +1020,14 @@ type ButtonPropsWeControl = | 'onClick' | 'onKeyDown' -let Button = forwardRefWithAs(function Button( - props: Props, +export type ComboboxButtonProps = Props< + TTag, + ButtonRenderPropArg, + ButtonPropsWeControl +> + +function ButtonFn( + props: ComboboxButtonProps, ref: Ref ) { let data = useData('Combobox.Button') @@ -1105,7 +1122,7 @@ let Button = forwardRefWithAs(function Button( - props: Props, +export type ComboboxLabelProps = Props< + TTag, + LabelRenderPropArg, + LabelPropsWeControl +> + +function LabelFn( + props: ComboboxLabelProps, ref: Ref ) { let internalId = useId() @@ -1144,7 +1167,7 @@ let Label = forwardRefWithAs(function Label( - props: Props & - PropsForFeatures & { - hold?: boolean - }, +export type ComboboxOptionsProps = Props< + TTag, + OptionsRenderPropArg, + OptionsPropsWeControl +> & + PropsForFeatures & { + hold?: boolean + } + +function OptionsFn( + props: ComboboxOptionsProps, ref: Ref ) { let internalId = useId() @@ -1226,7 +1253,7 @@ let Options = forwardRefWithAs(function Options< visible, name: 'Combobox.Options', }) -}) +} // --- @@ -1238,18 +1265,21 @@ interface OptionRenderPropArg { } type ComboboxOptionPropsWeControl = 'role' | 'tabIndex' | 'aria-disabled' | 'aria-selected' -let Option = forwardRefWithAs(function Option< +export type ComboboxOptionProps = Props< + TTag, + OptionRenderPropArg, + ComboboxOptionPropsWeControl | 'value' +> & { + disabled?: boolean + value: TType +} + +function OptionFn< TTag extends ElementType = typeof DEFAULT_OPTION_TAG, // TODO: One day we will be able to infer this type from the generic in Combobox itself. // But today is not that day.. TType = Parameters[0]['value'] ->( - props: Props & { - disabled?: boolean - value: TType - }, - ref: Ref -) { +>(props: ComboboxOptionProps, ref: Ref) { let internalId = useId() let { id = `headlessui-combobox-option-${internalId}`, @@ -1296,7 +1326,13 @@ let Option = forwardRefWithAs(function Option< internalOptionRef.current?.scrollIntoView?.({ block: 'nearest' }) }) return d.dispose - }, [internalOptionRef, active, data.comboboxState, data.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data.activeOptionIndex]) + }, [ + internalOptionRef, + active, + data.comboboxState, + data.activationTrigger, + /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data.activeOptionIndex, + ]) let handleClick = useEvent((event: { preventDefault: Function }) => { if (disabled) return event.preventDefault() @@ -1379,8 +1415,63 @@ let Option = forwardRefWithAs(function Option< defaultTag: DEFAULT_OPTION_TAG, name: 'Combobox.Option', }) -}) +} // --- +interface ComponentCombobox extends HasDisplayName { + ( + props: ComboboxProps & RefProp + ): JSX.Element + ( + props: ComboboxProps & RefProp + ): JSX.Element + ( + props: ComboboxProps & RefProp + ): JSX.Element + ( + props: ComboboxProps & RefProp + ): JSX.Element +} + +interface ComponentComboboxButton extends HasDisplayName { + ( + props: ComboboxButtonProps & RefProp + ): JSX.Element +} + +interface ComponentComboboxInput extends HasDisplayName { + ( + props: ComboboxInputProps & RefProp + ): JSX.Element +} + +interface ComponentComboboxLabel extends HasDisplayName { + ( + props: ComboboxLabelProps & RefProp + ): JSX.Element +} + +interface ComponentComboboxOptions extends HasDisplayName { + ( + props: ComboboxOptionsProps & RefProp + ): JSX.Element +} + +interface ComponentComboboxOption extends HasDisplayName { + < + TTag extends ElementType = typeof DEFAULT_OPTION_TAG, + TType = Parameters[0]['value'] + >( + props: ComboboxOptionProps & RefProp + ): JSX.Element +} + +let ComboboxRoot = forwardRefWithAs(ComboboxFn) as unknown as ComponentCombobox +let Button = forwardRefWithAs(ButtonFn) as unknown as ComponentComboboxButton +let Input = forwardRefWithAs(InputFn) as unknown as ComponentComboboxInput +let Label = forwardRefWithAs(LabelFn) as unknown as ComponentComboboxLabel +let Options = forwardRefWithAs(OptionsFn) as unknown as ComponentComboboxOptions +let Option = forwardRefWithAs(OptionFn) as unknown as ComponentComboboxOption + export let Combobox = Object.assign(ComboboxRoot, { Input, Button, Label, Options, Option }) diff --git a/packages/@headlessui-react/src/components/description/description.tsx b/packages/@headlessui-react/src/components/description/description.tsx index 1924fbabc3..84bcd66656 100644 --- a/packages/@headlessui-react/src/components/description/description.tsx +++ b/packages/@headlessui-react/src/components/description/description.tsx @@ -12,7 +12,7 @@ import React, { import { Props } from '../../types' import { useId } from '../../hooks/use-id' -import { forwardRefWithAs, render } from '../../utils/render' +import { forwardRefWithAs, HasDisplayName, RefProp, render } from '../../utils/render' import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' import { useSyncRefs } from '../../hooks/use-sync-refs' import { useEvent } from '../../hooks/use-event' @@ -89,9 +89,13 @@ export function useDescriptions(): [ let DEFAULT_DESCRIPTION_TAG = 'p' as const -export let Description = forwardRefWithAs(function Description< - TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG ->(props: Props, ref: Ref) { +export type DescriptionProps = + Props + +function DescriptionFn( + props: DescriptionProps, + ref: Ref +) { let internalId = useId() let { id = `headlessui-description-${internalId}`, ...theirProps } = props let context = useDescriptionContext() @@ -108,4 +112,17 @@ export let Description = forwardRefWithAs(function Description< defaultTag: DEFAULT_DESCRIPTION_TAG, name: context.name || 'Description', }) +} + +// --- +export interface ComponentDescription extends HasDisplayName { + ( + props: DescriptionProps & RefProp + ): JSX.Element +} + +let DescriptionRoot = forwardRefWithAs(DescriptionFn) as unknown as ComponentDescription + +export let Description = Object.assign(DescriptionRoot, { + // }) diff --git a/packages/@headlessui-react/src/components/dialog/dialog.tsx b/packages/@headlessui-react/src/components/dialog/dialog.tsx index a33b490dde..7c810602ae 100644 --- a/packages/@headlessui-react/src/components/dialog/dialog.tsx +++ b/packages/@headlessui-react/src/components/dialog/dialog.tsx @@ -20,7 +20,14 @@ import React, { import { Props } from '../../types' import { match } from '../../utils/match' -import { forwardRefWithAs, render, Features, PropsForFeatures } from '../../utils/render' +import { + forwardRefWithAs, + render, + Features, + PropsForFeatures, + HasDisplayName, + RefProp, +} from '../../utils/render' import { useSyncRefs } from '../../hooks/use-sync-refs' import { Keys } from '../keyboard' import { isDisabledReactIssue7711 } from '../../utils/bugs' @@ -28,7 +35,7 @@ import { useId } from '../../hooks/use-id' import { FocusTrap } from '../../components/focus-trap/focus-trap' import { Portal } from '../../components/portal/portal' import { ForcePortalRoot } from '../../internal/portal-force-root' -import { Description, useDescriptions } from '../description/description' +import { ComponentDescription, Description, useDescriptions } from '../description/description' import { useOpenClosed, State } from '../../internal/open-closed' import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete' import { StackProvider, StackMessage } from '../../internal/stack-context' @@ -115,16 +122,20 @@ type DialogPropsWeControl = 'role' | 'aria-modal' | 'aria-describedby' | 'aria-l let DialogRenderFeatures = Features.RenderStrategy | Features.Static -let DialogRoot = forwardRefWithAs(function Dialog< - TTag extends ElementType = typeof DEFAULT_DIALOG_TAG ->( - props: Props & - PropsForFeatures & { - open?: boolean - onClose(value: boolean): void - initialFocus?: MutableRefObject - __demoMode?: boolean - }, +export type DialogProps = Props< + TTag, + DialogRenderPropArg, + DialogPropsWeControl +> & + PropsForFeatures & { + open?: boolean + onClose(value: boolean): void + initialFocus?: MutableRefObject + __demoMode?: boolean + } + +function DialogFn( + props: DialogProps, ref: Ref ) { let internalId = useId() @@ -394,7 +405,7 @@ let DialogRoot = forwardRefWithAs(function Dialog< ) -}) +} // --- @@ -404,9 +415,16 @@ interface OverlayRenderPropArg { } type OverlayPropsWeControl = 'aria-hidden' | 'onClick' -let Overlay = forwardRefWithAs(function Overlay< - TTag extends ElementType = typeof DEFAULT_OVERLAY_TAG ->(props: Props, ref: Ref) { +export type DialogOverlayProps = Props< + TTag, + OverlayRenderPropArg, + OverlayPropsWeControl +> + +function OverlayFn( + props: DialogOverlayProps, + ref: Ref +) { let internalId = useId() let { id = `headlessui-dialog-overlay-${internalId}`, ...theirProps } = props let [{ dialogState, close }] = useDialogContext('Dialog.Overlay') @@ -439,7 +457,7 @@ let Overlay = forwardRefWithAs(function Overlay< defaultTag: DEFAULT_OVERLAY_TAG, name: 'Dialog.Overlay', }) -}) +} // --- @@ -449,9 +467,16 @@ interface BackdropRenderPropArg { } type BackdropPropsWeControl = 'aria-hidden' | 'onClick' -let Backdrop = forwardRefWithAs(function Backdrop< - TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG ->(props: Props, ref: Ref) { +export type DialogBackdropProps = Props< + TTag, + BackdropRenderPropArg, + BackdropPropsWeControl +> + +function BackdropFn( + props: DialogBackdropProps, + ref: Ref +) { let internalId = useId() let { id = `headlessui-dialog-backdrop-${internalId}`, ...theirProps } = props let [{ dialogState }, state] = useDialogContext('Dialog.Backdrop') @@ -489,7 +514,7 @@ let Backdrop = forwardRefWithAs(function Backdrop< ) -}) +} // --- @@ -498,8 +523,10 @@ interface PanelRenderPropArg { open: boolean } -let Panel = forwardRefWithAs(function Panel( - props: Props, +export type DialogPanelProps = Props + +function PanelFn( + props: DialogPanelProps, ref: Ref ) { let internalId = useId() @@ -531,7 +558,7 @@ let Panel = forwardRefWithAs(function Panel( - props: Props, +export type DialogTitleProps = Props + +function TitleFn( + props: DialogTitleProps, ref: Ref ) { let internalId = useId() @@ -569,8 +598,52 @@ let Title = forwardRefWithAs(function Title( + props: DialogProps & RefProp + ): JSX.Element +} + +interface ComponentDialogBackdrop extends HasDisplayName { + ( + props: DialogBackdropProps & RefProp + ): JSX.Element +} + +interface ComponentDialogPanel extends HasDisplayName { + ( + props: DialogPanelProps & RefProp + ): JSX.Element +} + +interface ComponentDialogOverlay extends HasDisplayName { + ( + props: DialogOverlayProps & RefProp + ): JSX.Element +} + +interface ComponentDialogTitle extends HasDisplayName { + ( + props: DialogTitleProps & RefProp + ): JSX.Element +} + +interface ComponentDialogDescription extends ComponentDescription {} + +let DialogRoot = forwardRefWithAs(DialogFn) as unknown as ComponentDialog +let Backdrop = forwardRefWithAs(BackdropFn) as unknown as ComponentDialogBackdrop +let Panel = forwardRefWithAs(PanelFn) as unknown as ComponentDialogPanel +let Overlay = forwardRefWithAs(OverlayFn) as unknown as ComponentDialogOverlay +let Title = forwardRefWithAs(TitleFn) as unknown as ComponentDialogTitle + +export let Dialog = Object.assign(DialogRoot, { + Backdrop, + Panel, + Overlay, + Title, + Description: Description as ComponentDialogDescription, +}) diff --git a/packages/@headlessui-react/src/components/disclosure/disclosure.test.tsx b/packages/@headlessui-react/src/components/disclosure/disclosure.test.tsx index ea0091b27e..a38ec66a9a 100644 --- a/packages/@headlessui-react/src/components/disclosure/disclosure.test.tsx +++ b/packages/@headlessui-react/src/components/disclosure/disclosure.test.tsx @@ -36,7 +36,7 @@ describe('Safe guards', () => { ])( 'should error when we are using a <%s /> without a parent ', suppressConsoleLogs((name, Component) => { - expect(() => render(createElement(Component))).toThrowError( + expect(() => render(createElement(Component))).toThrowError( `<${name} /> is missing a parent component.` ) }) diff --git a/packages/@headlessui-react/src/components/disclosure/disclosure.tsx b/packages/@headlessui-react/src/components/disclosure/disclosure.tsx index 5b2884cbe0..cbebe24246 100644 --- a/packages/@headlessui-react/src/components/disclosure/disclosure.tsx +++ b/packages/@headlessui-react/src/components/disclosure/disclosure.tsx @@ -20,7 +20,14 @@ import React, { import { Props } from '../../types' import { match } from '../../utils/match' -import { forwardRefWithAs, render, Features, PropsForFeatures } from '../../utils/render' +import { + forwardRefWithAs, + render, + Features, + PropsForFeatures, + HasDisplayName, + RefProp, +} from '../../utils/render' import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs' import { useId } from '../../hooks/use-id' import { Keys } from '../keyboard' @@ -149,12 +156,12 @@ interface DisclosureRenderPropArg { close(focusableElement?: HTMLElement | MutableRefObject): void } -let DisclosureRoot = forwardRefWithAs(function Disclosure< - TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG ->( - props: Props & { - defaultOpen?: boolean - }, +export type DisclosureProps = Props & { + defaultOpen?: boolean +} + +function DisclosureFn( + props: DisclosureProps, ref: Ref ) { let { defaultOpen = false, ...theirProps } = props @@ -232,7 +239,7 @@ let DisclosureRoot = forwardRefWithAs(function Disclosure< ) -}) +} // --- @@ -240,10 +247,18 @@ let DEFAULT_BUTTON_TAG = 'button' as const interface ButtonRenderPropArg { open: boolean } -type ButtonPropsWeControl = 'type' | 'aria-expanded' | 'aria-controls' | 'onKeyDown' | 'onClick' - -let Button = forwardRefWithAs(function Button( - props: Props, +type ButtonPropsWeControl = + // | 'type' // We allow this to be overridden + 'aria-expanded' | 'aria-controls' | 'onKeyDown' | 'onClick' + +export type DisclosureButtonProps = Props< + TTag, + ButtonRenderPropArg, + ButtonPropsWeControl +> + +function ButtonFn( + props: DisclosureButtonProps, ref: Ref ) { let internalId = useId() @@ -340,7 +355,7 @@ let Button = forwardRefWithAs(function Button( - props: Props & PropsForFeatures, +export type DisclosurePanelProps = Props & + PropsForFeatures + +function PanelFn( + props: DisclosurePanelProps, ref: Ref ) { let internalId = useId() @@ -404,8 +422,30 @@ let Panel = forwardRefWithAs(function Panel ) -}) +} // --- +interface ComponentDisclosure extends HasDisplayName { + ( + props: DisclosureProps & RefProp + ): JSX.Element +} + +interface ComponentDisclosureButton extends HasDisplayName { + ( + props: DisclosureButtonProps & RefProp + ): JSX.Element +} + +interface ComponentDisclosurePanel extends HasDisplayName { + ( + props: DisclosurePanelProps & RefProp + ): JSX.Element +} + +let DisclosureRoot = forwardRefWithAs(DisclosureFn) as unknown as ComponentDisclosure +let Button = forwardRefWithAs(ButtonFn) as unknown as ComponentDisclosureButton +let Panel = forwardRefWithAs(PanelFn) as unknown as ComponentDisclosurePanel + export let Disclosure = Object.assign(DisclosureRoot, { Button, Panel }) diff --git a/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx b/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx index df13b4312a..4455597313 100644 --- a/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx +++ b/packages/@headlessui-react/src/components/focus-trap/focus-trap.tsx @@ -10,7 +10,7 @@ import React, { } from 'react' import { Props } from '../../types' -import { forwardRefWithAs, render } from '../../utils/render' +import { forwardRefWithAs, HasDisplayName, RefProp, render } from '../../utils/render' import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete' import { useSyncRefs } from '../../hooks/use-sync-refs' import { Features as HiddenFeatures, Hidden } from '../../internal/hidden' @@ -47,133 +47,148 @@ enum Features { All = InitialFocus | TabLock | FocusLock | RestoreFocus, } -export let FocusTrap = Object.assign( - forwardRefWithAs(function FocusTrap( - props: Props & { - initialFocus?: MutableRefObject - features?: Features - containers?: MutableRefObject>> - }, - ref: Ref - ) { - let container = useRef(null) - let focusTrapRef = useSyncRefs(container, ref) - let { initialFocus, containers, features = Features.All, ...theirProps } = props - - if (!useServerHandoffComplete()) { - features = Features.None - } +export type FocusTrapProps = Props & { + initialFocus?: MutableRefObject + features?: Features + containers?: MutableRefObject>> +} - let ownerDocument = useOwnerDocument(container) - - useRestoreFocus({ ownerDocument }, Boolean(features & Features.RestoreFocus)) - let previousActiveElement = useInitialFocus( - { ownerDocument, container, initialFocus }, - Boolean(features & Features.InitialFocus) - ) - useFocusLock( - { ownerDocument, container, containers, previousActiveElement }, - Boolean(features & Features.FocusLock) - ) - - let direction = useTabDirection() - let handleFocus = useEvent((e: ReactFocusEvent) => { - let el = container.current as HTMLElement - if (!el) return - - // TODO: Cleanup once we are using real browser tests - let wrapper = process.env.NODE_ENV === 'test' ? microTask : (cb: Function) => cb() - wrapper(() => { - match(direction.current, { - [TabDirection.Forwards]: () => { - focusIn(el, Focus.First, { skipElements: [e.relatedTarget as HTMLElement] }) - }, - [TabDirection.Backwards]: () => { - focusIn(el, Focus.Last, { skipElements: [e.relatedTarget as HTMLElement] }) - }, - }) +function FocusTrapFn( + props: FocusTrapProps, + ref: Ref +) { + let container = useRef(null) + let focusTrapRef = useSyncRefs(container, ref) + let { initialFocus, containers, features = Features.All, ...theirProps } = props + + if (!useServerHandoffComplete()) { + features = Features.None + } + + let ownerDocument = useOwnerDocument(container) + + useRestoreFocus({ ownerDocument }, Boolean(features & Features.RestoreFocus)) + let previousActiveElement = useInitialFocus( + { ownerDocument, container, initialFocus }, + Boolean(features & Features.InitialFocus) + ) + useFocusLock( + { ownerDocument, container, containers, previousActiveElement }, + Boolean(features & Features.FocusLock) + ) + + let direction = useTabDirection() + let handleFocus = useEvent((e: ReactFocusEvent) => { + let el = container.current as HTMLElement + if (!el) return + + // TODO: Cleanup once we are using real browser tests + let wrapper = process.env.NODE_ENV === 'test' ? microTask : (cb: Function) => cb() + wrapper(() => { + match(direction.current, { + [TabDirection.Forwards]: () => { + focusIn(el, Focus.First, { skipElements: [e.relatedTarget as HTMLElement] }) + }, + [TabDirection.Backwards]: () => { + focusIn(el, Focus.Last, { skipElements: [e.relatedTarget as HTMLElement] }) + }, }) }) + }) + + let d = useDisposables() + let recentlyUsedTabKey = useRef(false) + let ourProps = { + ref: focusTrapRef, + onKeyDown(e: KeyboardEvent) { + if (e.key == 'Tab') { + recentlyUsedTabKey.current = true + d.requestAnimationFrame(() => { + recentlyUsedTabKey.current = false + }) + } + }, + onBlur(e: ReactFocusEvent) { + let allContainers = new Set(containers?.current) + allContainers.add(container) - let d = useDisposables() - let recentlyUsedTabKey = useRef(false) - let ourProps = { - ref: focusTrapRef, - onKeyDown(e: KeyboardEvent) { - if (e.key == 'Tab') { - recentlyUsedTabKey.current = true - d.requestAnimationFrame(() => { - recentlyUsedTabKey.current = false - }) - } - }, - onBlur(e: ReactFocusEvent) { - let allContainers = new Set(containers?.current) - allContainers.add(container) + let relatedTarget = e.relatedTarget + if (!(relatedTarget instanceof HTMLElement)) return - let relatedTarget = e.relatedTarget - if (!(relatedTarget instanceof HTMLElement)) return + // Known guards, leave them alone! + if (relatedTarget.dataset.headlessuiFocusGuard === 'true') { + return + } - // Known guards, leave them alone! - if (relatedTarget.dataset.headlessuiFocusGuard === 'true') { - return + // Blur is triggered due to focus on relatedTarget, and the relatedTarget is not inside any + // of the dialog containers. In other words, let's move focus back in! + if (!contains(allContainers, relatedTarget)) { + // Was the blur invoke via the keyboard? Redirect to the next in line. + if (recentlyUsedTabKey.current) { + focusIn( + container.current as HTMLElement, + match(direction.current, { + [TabDirection.Forwards]: () => Focus.Next, + [TabDirection.Backwards]: () => Focus.Previous, + }) | Focus.WrapAround, + { relativeTo: e.target as HTMLElement } + ) } - // Blur is triggered due to focus on relatedTarget, and the relatedTarget is not inside any - // of the dialog containers. In other words, let's move focus back in! - if (!contains(allContainers, relatedTarget)) { - // Was the blur invoke via the keyboard? Redirect to the next in line. - if (recentlyUsedTabKey.current) { - focusIn( - container.current as HTMLElement, - match(direction.current, { - [TabDirection.Forwards]: () => Focus.Next, - [TabDirection.Backwards]: () => Focus.Previous, - }) | Focus.WrapAround, - { relativeTo: e.target as HTMLElement } - ) - } - - // It was invoke via something else (e.g.: click, programmatically, ...). Redirect to the - // previous active item in the FocusTrap - else if (e.target instanceof HTMLElement) { - focusElement(e.target) - } + // It was invoke via something else (e.g.: click, programmatically, ...). Redirect to the + // previous active item in the FocusTrap + else if (e.target instanceof HTMLElement) { + focusElement(e.target) } - }, - } + } + }, + } + + return ( + <> + {Boolean(features & Features.TabLock) && ( + + )} + {render({ + ourProps, + theirProps, + defaultTag: DEFAULT_FOCUS_TRAP_TAG, + name: 'FocusTrap', + })} + {Boolean(features & Features.TabLock) && ( + + )} + + ) +} + +// --- + +interface ComponentFocusTrap extends HasDisplayName { + ( + props: FocusTrapProps & RefProp + ): JSX.Element +} + +let FocusTrapRoot = forwardRefWithAs(FocusTrapFn) as unknown as ComponentFocusTrap + +export let FocusTrap = Object.assign(FocusTrapRoot, { + features: Features, +}) - return ( - <> - {Boolean(features & Features.TabLock) && ( - - )} - {render({ - ourProps, - theirProps, - defaultTag: DEFAULT_FOCUS_TRAP_TAG, - name: 'FocusTrap', - })} - {Boolean(features & Features.TabLock) && ( - - )} - - ) - }), - { features: Features } -) +// --- function useRestoreFocus({ ownerDocument }: { ownerDocument: Document | null }, enabled: boolean) { let restoreElement = useRef(null) diff --git a/packages/@headlessui-react/src/components/label/label.tsx b/packages/@headlessui-react/src/components/label/label.tsx index 2873449b90..97df90487a 100644 --- a/packages/@headlessui-react/src/components/label/label.tsx +++ b/packages/@headlessui-react/src/components/label/label.tsx @@ -12,7 +12,7 @@ import React, { import { Props } from '../../types' import { useId } from '../../hooks/use-id' -import { forwardRefWithAs, render } from '../../utils/render' +import { forwardRefWithAs, HasDisplayName, RefProp, render } from '../../utils/render' import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' import { useSyncRefs } from '../../hooks/use-sync-refs' import { useEvent } from '../../hooks/use-event' @@ -80,12 +80,12 @@ export function useLabels(): [string | undefined, (props: LabelProviderProps) => let DEFAULT_LABEL_TAG = 'label' as const -export let Label = forwardRefWithAs(function Label< - TTag extends ElementType = typeof DEFAULT_LABEL_TAG ->( - props: Props & { - passive?: boolean - }, +export type LabelProps = Props & { + passive?: boolean +} + +function LabelFn( + props: LabelProps, ref: Ref ) { let internalId = useId() @@ -114,4 +114,18 @@ export let Label = forwardRefWithAs(function Label< defaultTag: DEFAULT_LABEL_TAG, name: context.name || 'Label', }) +} + +// --- + +export interface ComponentLabel extends HasDisplayName { + ( + props: LabelProps & RefProp + ): JSX.Element +} + +let LabelRoot = forwardRefWithAs(LabelFn) as unknown as ComponentLabel + +export let Label = Object.assign(LabelRoot, { + // }) diff --git a/packages/@headlessui-react/src/components/listbox/listbox.tsx b/packages/@headlessui-react/src/components/listbox/listbox.tsx index 081fbde5d5..12a3923c04 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox.tsx +++ b/packages/@headlessui-react/src/components/listbox/listbox.tsx @@ -23,7 +23,15 @@ import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' import { useComputed } from '../../hooks/use-computed' import { useSyncRefs } from '../../hooks/use-sync-refs' import { EnsureArray, Props } from '../../types' -import { Features, forwardRefWithAs, PropsForFeatures, render, compact } from '../../utils/render' +import { + Features, + forwardRefWithAs, + PropsForFeatures, + render, + compact, + HasDisplayName, + RefProp, +} from '../../utils/render' import { match } from '../../utils/match' import { disposables } from '../../utils/disposables' import { Keys } from '../keyboard' @@ -324,27 +332,26 @@ interface ListboxRenderPropArg { value: T } -let ListboxRoot = forwardRefWithAs(function Listbox< +export type ListboxProps = Props< + TTag, + ListboxRenderPropArg, + 'value' | 'defaultValue' | 'onChange' | 'by' | 'disabled' | 'horizontal' | 'name' | 'multiple' +> & { + value?: TType + defaultValue?: TType + onChange?(value: TType): void + by?: (keyof TActualType & string) | ((a: TActualType, z: TActualType) => boolean) + disabled?: boolean + horizontal?: boolean + name?: string + multiple?: boolean +} + +function ListboxFn< TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG, TType = string, TActualType = TType extends (infer U)[] ? U : TType ->( - props: Props< - TTag, - ListboxRenderPropArg, - 'value' | 'defaultValue' | 'onChange' | 'by' | 'disabled' | 'horizontal' | 'name' | 'multiple' - > & { - value?: TType - defaultValue?: TType - onChange?(value: TType): void - by?: (keyof TActualType & string) | ((a: TActualType, z: TActualType) => boolean) - disabled?: boolean - horizontal?: boolean - name?: string - multiple?: boolean - }, - ref: Ref -) { +>(props: ListboxProps, ref: Ref) { let { value: controlledValue, defaultValue, @@ -568,7 +575,7 @@ let ListboxRoot = forwardRefWithAs(function Listbox< ) -}) +} // --- @@ -579,7 +586,7 @@ interface ButtonRenderPropArg { value: any } type ButtonPropsWeControl = - | 'type' + // | 'type' // We allow this to be overridden | 'aria-haspopup' | 'aria-controls' | 'aria-expanded' @@ -588,8 +595,14 @@ type ButtonPropsWeControl = | 'onKeyDown' | 'onClick' -let Button = forwardRefWithAs(function Button( - props: Props, +export type ListboxButtonProps = Props< + TTag, + ButtonRenderPropArg, + ButtonPropsWeControl +> + +function ButtonFn( + props: ListboxButtonProps, ref: Ref ) { let internalId = useId() @@ -681,7 +694,7 @@ let Button = forwardRefWithAs(function Button( - props: Props, +export type ListboxLabelProps = Props< + TTag, + LabelRenderPropArg, + LabelPropsWeControl +> + +function LabelFn( + props: ListboxLabelProps, ref: Ref ) { let internalId = useId() @@ -719,7 +738,7 @@ let Label = forwardRefWithAs(function Label( - props: Props & - PropsForFeatures, +export type ListboxOptionsProps = Props< + TTag, + OptionsRenderPropArg, + OptionsPropsWeControl +> & + PropsForFeatures + +function OptionsFn( + props: ListboxOptionsProps, ref: Ref ) { let internalId = useId() @@ -873,7 +896,7 @@ let Options = forwardRefWithAs(function Options< visible, name: 'Listbox.Options', }) -}) +} // --- @@ -894,18 +917,21 @@ type ListboxOptionPropsWeControl = | 'onMouseMove' | 'onFocus' -let Option = forwardRefWithAs(function Option< +export type ListboxOptionProps = Props< + TTag, + OptionRenderPropArg, + ListboxOptionPropsWeControl | 'value' +> & { + disabled?: boolean + value: TType +} + +function OptionFn< TTag extends ElementType = typeof DEFAULT_OPTION_TAG, // TODO: One day we will be able to infer this type from the generic in Listbox itself. // But today is not that day.. TType = Parameters[0]['value'] ->( - props: Props & { - disabled?: boolean - value: TType - }, - ref: Ref -) { +>(props: ListboxOptionProps, ref: Ref) { let internalId = useId() let { id = `headlessui-listbox-option-${internalId}`, @@ -940,7 +966,13 @@ let Option = forwardRefWithAs(function Option< internalOptionRef.current?.scrollIntoView?.({ block: 'nearest' }) }) return d.dispose - }, [internalOptionRef, active, data.listboxState, data.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data.activeOptionIndex]) + }, [ + internalOptionRef, + active, + data.listboxState, + data.activationTrigger, + /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ data.activeOptionIndex, + ]) useIsoMorphicEffect(() => actions.registerOption(id, bag), [bag, id]) @@ -1008,8 +1040,51 @@ let Option = forwardRefWithAs(function Option< defaultTag: DEFAULT_OPTION_TAG, name: 'Listbox.Option', }) -}) +} // --- +interface ComponentListbox extends HasDisplayName { + < + TTag extends ElementType = typeof DEFAULT_LISTBOX_TAG, + TType = string, + TActualType = TType extends (infer U)[] ? U : TType + >( + props: ListboxProps & RefProp + ): JSX.Element +} + +interface ComponentListboxButton extends HasDisplayName { + ( + props: ListboxButtonProps & RefProp + ): JSX.Element +} + +interface ComponentListboxLabel extends HasDisplayName { + ( + props: ListboxLabelProps & RefProp + ): JSX.Element +} + +interface ComponentListboxOptions extends HasDisplayName { + ( + props: ListboxOptionsProps & RefProp + ): JSX.Element +} + +interface ComponentListboxOption extends HasDisplayName { + < + TTag extends ElementType = typeof DEFAULT_OPTION_TAG, + TType = Parameters[0]['value'] + >( + props: ListboxOptionProps & RefProp + ): JSX.Element +} + +let ListboxRoot = forwardRefWithAs(ListboxFn) as unknown as ComponentListbox +let Button = forwardRefWithAs(ButtonFn) as unknown as ComponentListboxButton +let Label = forwardRefWithAs(LabelFn) as unknown as ComponentListboxLabel +let Options = forwardRefWithAs(OptionsFn) as unknown as ComponentListboxOptions +let Option = forwardRefWithAs(OptionFn) as unknown as ComponentListboxOption + export let Listbox = Object.assign(ListboxRoot, { Button, Label, Options, Option }) diff --git a/packages/@headlessui-react/src/components/menu/menu.test.tsx b/packages/@headlessui-react/src/components/menu/menu.test.tsx index 10cfe4ce3a..4409a8eb7f 100644 --- a/packages/@headlessui-react/src/components/menu/menu.test.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.test.tsx @@ -50,7 +50,7 @@ describe('Safe guards', () => { ])( 'should error when we are using a <%s /> without a parent ', suppressConsoleLogs((name, Component) => { - expect(() => render(createElement(Component))).toThrowError( + expect(() => render(createElement(Component))).toThrowError( `<${name} /> is missing a parent component.` ) }) diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index cca078ec2d..2245090752 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -20,7 +20,14 @@ import React, { import { Props } from '../../types' import { match } from '../../utils/match' -import { forwardRefWithAs, render, Features, PropsForFeatures } from '../../utils/render' +import { + forwardRefWithAs, + render, + Features, + PropsForFeatures, + HasDisplayName, + RefProp, +} from '../../utils/render' import { disposables } from '../../utils/disposables' import { useDisposables } from '../../hooks/use-disposables' import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' @@ -231,8 +238,10 @@ interface MenuRenderPropArg { close: () => void } -let MenuRoot = forwardRefWithAs(function Menu( - props: Props, +export type MenuProps = Props + +function MenuFn( + props: MenuProps, ref: Ref ) { let reducerBag = useReducer(stateReducer, { @@ -291,7 +300,7 @@ let MenuRoot = forwardRefWithAs(function Menu ) -}) +} // --- @@ -300,15 +309,17 @@ interface ButtonRenderPropArg { open: boolean } type ButtonPropsWeControl = - | 'type' - | 'aria-haspopup' - | 'aria-controls' - | 'aria-expanded' - | 'onKeyDown' - | 'onClick' + // | 'type' // We allow this to be overridden + 'aria-haspopup' | 'aria-controls' | 'aria-expanded' | 'onKeyDown' | 'onClick' + +export type MenuButtonProps = Props< + TTag, + ButtonRenderPropArg, + ButtonPropsWeControl +> -let Button = forwardRefWithAs(function Button( - props: Props, +function ButtonFn( + props: MenuButtonProps, ref: Ref ) { let internalId = useId() @@ -386,7 +397,7 @@ let Button = forwardRefWithAs(function Button( - props: Props & - PropsForFeatures, +export type MenuItemsProps = Props< + TTag, + ItemsRenderPropArg, + ItemsPropsWeControl +> & + PropsForFeatures + +function ItemsFn( + props: MenuItemsProps, ref: Ref ) { let internalId = useId() @@ -559,7 +576,7 @@ let Items = forwardRefWithAs(function Items( - props: Props & { - disabled?: boolean - }, +export type MenuItemProps = Props< + TTag, + ItemRenderPropArg, + MenuItemPropsWeControl +> & { + disabled?: boolean +} + +function ItemFn( + props: MenuItemProps, ref: Ref ) { let internalId = useId() @@ -601,7 +624,13 @@ let Item = forwardRefWithAs(function Item({ disabled, domRef: internalItemRef }) @@ -683,8 +712,37 @@ let Item = forwardRefWithAs(function Item( + props: MenuProps & RefProp + ): JSX.Element +} + +interface ComponentMenuButton extends HasDisplayName { + ( + props: MenuButtonProps & RefProp + ): JSX.Element +} + +interface ComponentMenuItems extends HasDisplayName { + ( + props: MenuItemsProps & RefProp + ): JSX.Element +} + +interface ComponentMenuItem extends HasDisplayName { + ( + props: MenuItemProps & RefProp + ): JSX.Element +} + +let MenuRoot = forwardRefWithAs(MenuFn) as unknown as ComponentMenu +let Button = forwardRefWithAs(ButtonFn) as unknown as ComponentMenuButton +let Items = forwardRefWithAs(ItemsFn) as unknown as ComponentMenuItems +let Item = forwardRefWithAs(ItemFn) as unknown as ComponentMenuItem + export let Menu = Object.assign(MenuRoot, { Button, Items, Item }) diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index 4f6c755dca..444f7abe5e 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -41,7 +41,7 @@ describe('Safe guards', () => { ])( 'should error when we are using a <%s /> without a parent ', suppressConsoleLogs((name, Component) => { - expect(() => render(createElement(Component))).toThrowError( + expect(() => render(createElement(Component))).toThrowError( `<${name} /> is missing a parent component.` ) }) diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index 96d9ad6e1b..3a11217db8 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -22,7 +22,14 @@ import React, { import { Props } from '../../types' import { match } from '../../utils/match' -import { forwardRefWithAs, render, Features, PropsForFeatures } from '../../utils/render' +import { + forwardRefWithAs, + render, + Features, + PropsForFeatures, + HasDisplayName, + RefProp, +} from '../../utils/render' import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs' import { useId } from '../../hooks/use-id' import { Keys } from '../keyboard' @@ -191,9 +198,12 @@ interface PopoverRenderPropArg { ): void } -let PopoverRoot = forwardRefWithAs(function Popover< - TTag extends ElementType = typeof DEFAULT_POPOVER_TAG ->(props: Props, ref: Ref) { +export type PopoverProps = Props + +function PopoverFn( + props: PopoverProps, + ref: Ref +) { let internalPopoverRef = useRef(null) let popoverRef = useSyncRefs( ref, @@ -367,7 +377,7 @@ let PopoverRoot = forwardRefWithAs(function Popover< ) -}) +} // --- @@ -375,10 +385,18 @@ let DEFAULT_BUTTON_TAG = 'button' as const interface ButtonRenderPropArg { open: boolean } -type ButtonPropsWeControl = 'type' | 'aria-expanded' | 'aria-controls' | 'onKeyDown' | 'onClick' - -let Button = forwardRefWithAs(function Button( - props: Props, +type ButtonPropsWeControl = + // | 'type' // We allow this to be overridden + 'aria-expanded' | 'aria-controls' | 'onKeyDown' | 'onClick' + +export type PopoverButtonProps = Props< + TTag, + ButtonRenderPropArg, + ButtonPropsWeControl +> + +function ButtonFn( + props: PopoverButtonProps, ref: Ref ) { let internalId = useId() @@ -594,7 +612,7 @@ let Button = forwardRefWithAs(function Button ) -}) +} // --- @@ -606,11 +624,15 @@ type OverlayPropsWeControl = 'aria-hidden' | 'onClick' let OverlayRenderFeatures = Features.RenderStrategy | Features.Static -let Overlay = forwardRefWithAs(function Overlay< - TTag extends ElementType = typeof DEFAULT_OVERLAY_TAG ->( - props: Props & - PropsForFeatures, +export type PopoverOverlayProps = Props< + TTag, + OverlayRenderPropArg, + OverlayPropsWeControl +> & + PropsForFeatures + +function OverlayFn( + props: PopoverOverlayProps, ref: Ref ) { let internalId = useId() @@ -653,7 +675,7 @@ let Overlay = forwardRefWithAs(function Overlay< visible, name: 'Popover.Overlay', }) -}) +} // --- @@ -666,11 +688,17 @@ type PanelPropsWeControl = 'onKeyDown' let PanelRenderFeatures = Features.RenderStrategy | Features.Static -let Panel = forwardRefWithAs(function Panel( - props: Props & - PropsForFeatures & { - focus?: boolean - }, +export type PopoverPanelProps = Props< + TTag, + PanelRenderPropArg, + PanelPropsWeControl +> & + PropsForFeatures & { + focus?: boolean + } + +function PanelFn( + props: PopoverPanelProps, ref: Ref ) { let internalId = useId() @@ -886,15 +914,17 @@ let Panel = forwardRefWithAs(function Panel ) -}) +} // --- let DEFAULT_GROUP_TAG = 'div' as const interface GroupRenderPropArg {} -let Group = forwardRefWithAs(function Group( - props: Props, +export type PopoverGroupProps = Props + +function GroupFn( + props: PopoverGroupProps, ref: Ref ) { let internalGroupRef = useRef(null) @@ -966,8 +996,44 @@ let Group = forwardRefWithAs(function Group ) -}) +} // --- +interface ComponentPopover extends HasDisplayName { + ( + props: PopoverProps & RefProp + ): JSX.Element +} + +interface ComponentPopoverButton extends HasDisplayName { + ( + props: PopoverButtonProps & RefProp + ): JSX.Element +} + +interface ComponentPopoverOverlay extends HasDisplayName { + ( + props: PopoverOverlayProps & RefProp + ): JSX.Element +} + +interface ComponentPopoverPanel extends HasDisplayName { + ( + props: PopoverPanelProps & RefProp + ): JSX.Element +} + +interface ComponentPopoverGroup extends HasDisplayName { + ( + props: PopoverGroupProps & RefProp + ): JSX.Element +} + +let PopoverRoot = forwardRefWithAs(PopoverFn) as unknown as ComponentPopover +let Button = forwardRefWithAs(ButtonFn) as unknown as ComponentPopoverButton +let Overlay = forwardRefWithAs(OverlayFn) as unknown as ComponentPopoverOverlay +let Panel = forwardRefWithAs(PanelFn) as unknown as ComponentPopoverPanel +let Group = forwardRefWithAs(GroupFn) as unknown as ComponentPopoverGroup + export let Popover = Object.assign(PopoverRoot, { Button, Overlay, Panel, Group }) diff --git a/packages/@headlessui-react/src/components/portal/portal.tsx b/packages/@headlessui-react/src/components/portal/portal.tsx index 3a375615fd..f7fe9177f5 100644 --- a/packages/@headlessui-react/src/components/portal/portal.tsx +++ b/packages/@headlessui-react/src/components/portal/portal.tsx @@ -14,7 +14,7 @@ import React, { import { createPortal } from 'react-dom' import { Props } from '../../types' -import { forwardRefWithAs, render } from '../../utils/render' +import { forwardRefWithAs, RefProp, HasDisplayName, render } from '../../utils/render' import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' import { usePortalRoot } from '../../internal/portal-force-root' import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete' @@ -68,9 +68,12 @@ function usePortalTarget(ref: MutableRefObject): HTMLElement let DEFAULT_PORTAL_TAG = Fragment interface PortalRenderPropArg {} -let PortalRoot = forwardRefWithAs(function Portal< - TTag extends ElementType = typeof DEFAULT_PORTAL_TAG ->(props: Props, ref: Ref) { +export type PortalProps = Props + +function PortalFn( + props: PortalProps, + ref: Ref +) { let theirProps = props let internalPortalRootRef = useRef(null) let portalRef = useSyncRefs( @@ -133,7 +136,7 @@ let PortalRoot = forwardRefWithAs(function Portal< }), element ) -}) +} // --- @@ -142,10 +145,12 @@ interface GroupRenderPropArg {} let PortalGroupContext = createContext | null>(null) -let Group = forwardRefWithAs(function Group( - props: Props & { - target: MutableRefObject - }, +export type PortalGroupProps = Props & { + target: MutableRefObject +} + +function GroupFn( + props: PortalGroupProps, ref: Ref ) { let { target, ...theirProps } = props @@ -163,8 +168,23 @@ let Group = forwardRefWithAs(function Group ) -}) +} // --- +interface ComponentPortal extends HasDisplayName { + ( + props: PortalProps & RefProp + ): JSX.Element +} + +interface ComponentPortalGroup extends HasDisplayName { + ( + props: PortalGroupProps & RefProp + ): JSX.Element +} + +let PortalRoot = forwardRefWithAs(PortalFn) as unknown as ComponentPortal +let Group = forwardRefWithAs(GroupFn) as unknown as ComponentPortalGroup + export let Portal = Object.assign(PortalRoot, { Group }) diff --git a/packages/@headlessui-react/src/components/radio-group/radio-group.tsx b/packages/@headlessui-react/src/components/radio-group/radio-group.tsx index c1af35c08d..dc977aaa7d 100644 --- a/packages/@headlessui-react/src/components/radio-group/radio-group.tsx +++ b/packages/@headlessui-react/src/components/radio-group/radio-group.tsx @@ -16,15 +16,19 @@ import React, { } from 'react' import { Props, Expand } from '../../types' -import { forwardRefWithAs, render, compact } from '../../utils/render' +import { forwardRefWithAs, render, compact, RefProp, HasDisplayName } from '../../utils/render' import { useId } from '../../hooks/use-id' import { match } from '../../utils/match' import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect' import { Keys } from '../../components/keyboard' import { focusIn, Focus, FocusResult, sortByDomNode } from '../../utils/focus-management' import { useFlags } from '../../hooks/use-flags' -import { Label, useLabels } from '../../components/label/label' -import { Description, useDescriptions } from '../../components/description/description' +import { ComponentLabel, Label, useLabels } from '../../components/label/label' +import { + ComponentDescription, + Description, + useDescriptions, +} from '../../components/description/description' import { useTreeWalker } from '../../hooks/use-tree-walker' import { useSyncRefs } from '../../hooks/use-sync-refs' import { Hidden, Features as HiddenFeatures } from '../../internal/hidden' @@ -133,22 +137,21 @@ interface RadioGroupRenderPropArg { } type RadioGroupPropsWeControl = 'role' | 'aria-labelledby' | 'aria-describedby' -let RadioGroupRoot = forwardRefWithAs(function RadioGroup< - TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG, - TType = string ->( - props: Props< - TTag, - RadioGroupRenderPropArg, - RadioGroupPropsWeControl | 'value' | 'defaultValue' | 'onChange' | 'disabled' | 'name' | 'by' - > & { - value?: TType - defaultValue?: TType - onChange?(value: TType): void - by?: (keyof TType & string) | ((a: TType, z: TType) => boolean) - disabled?: boolean - name?: string - }, +export type RadioGroupProps = Props< + TTag, + RadioGroupRenderPropArg, + RadioGroupPropsWeControl | 'value' | 'defaultValue' | 'onChange' | 'disabled' | 'name' | 'by' +> & { + value?: TType + defaultValue?: TType + onChange?(value: TType): void + by?: (keyof TType & string) | ((a: TType, z: TType) => boolean) + disabled?: boolean + name?: string +} + +function RadioGroupFn( + props: RadioGroupProps, ref: Ref ) { let internalId = useId() @@ -356,7 +359,7 @@ let RadioGroupRoot = forwardRefWithAs(function RadioGroup< ) -}) +} // --- @@ -380,18 +383,21 @@ type RadioPropsWeControl = | 'role' | 'tabIndex' -let Option = forwardRefWithAs(function Option< +export type RadioOptionProps = Props< + TTag, + OptionRenderPropArg, + RadioPropsWeControl | 'value' | 'disabled' +> & { + value: TType + disabled?: boolean +} + +function OptionFn< TTag extends ElementType = typeof DEFAULT_OPTION_TAG, // TODO: One day we will be able to infer this type from the generic in RadioGroup itself. // But today is not that day.. TType = Parameters[0]['value'] ->( - props: Props & { - value: TType - disabled?: boolean - }, - ref: Ref -) { +>(props: RadioOptionProps, ref: Ref) { let internalId = useId() let { id = `headlessui-radiogroup-option-${internalId}`, @@ -471,8 +477,30 @@ let Option = forwardRefWithAs(function Option< ) -}) +} // --- -export let RadioGroup = Object.assign(RadioGroupRoot, { Option, Label, Description }) +interface ComponentRadioGroup extends HasDisplayName { + ( + props: RadioGroupProps & RefProp + ): JSX.Element +} + +interface ComponentRadioOption extends HasDisplayName { + ( + props: RadioOptionProps & RefProp + ): JSX.Element +} + +interface ComponentRadioLabel extends ComponentLabel {} +interface ComponentRadioDescription extends ComponentDescription {} + +let RadioGroupRoot = forwardRefWithAs(RadioGroupFn) as unknown as ComponentRadioGroup +let Option = forwardRefWithAs(OptionFn) as unknown as ComponentRadioOption + +export let RadioGroup = Object.assign(RadioGroupRoot, { + Option, + Label: Label as ComponentRadioLabel, + Description: Description as ComponentRadioDescription, +}) diff --git a/packages/@headlessui-react/src/components/switch/switch.tsx b/packages/@headlessui-react/src/components/switch/switch.tsx index 71e6b31ed0..031a29cce2 100644 --- a/packages/@headlessui-react/src/components/switch/switch.tsx +++ b/packages/@headlessui-react/src/components/switch/switch.tsx @@ -15,12 +15,12 @@ import React, { } from 'react' import { Props } from '../../types' -import { forwardRefWithAs, render, compact } from '../../utils/render' +import { forwardRefWithAs, render, compact, HasDisplayName, RefProp } from '../../utils/render' import { useId } from '../../hooks/use-id' import { Keys } from '../keyboard' import { isDisabledReactIssue7711 } from '../../utils/bugs' -import { Label, useLabels } from '../label/label' -import { Description, useDescriptions } from '../description/description' +import { ComponentLabel, Label, useLabels } from '../label/label' +import { ComponentDescription, Description, useDescriptions } from '../description/description' import { useResolveButtonType } from '../../hooks/use-resolve-button-type' import { useSyncRefs } from '../../hooks/use-sync-refs' import { Hidden, Features as HiddenFeatures } from '../../internal/hidden' @@ -43,7 +43,11 @@ GroupContext.displayName = 'GroupContext' let DEFAULT_GROUP_TAG = Fragment -function Group(props: Props) { +export type SwitchGroupProps = Props + +function GroupFn( + props: SwitchGroupProps +) { let [switchElement, setSwitchElement] = useState(null) let [labelledby, LabelProvider] = useLabels() let [describedby, DescriptionProvider] = useDescriptions() @@ -97,20 +101,20 @@ type SwitchPropsWeControl = | 'onKeyUp' | 'onKeyPress' -let SwitchRoot = forwardRefWithAs(function Switch< - TTag extends ElementType = typeof DEFAULT_SWITCH_TAG ->( - props: Props< - TTag, - SwitchRenderPropArg, - SwitchPropsWeControl | 'checked' | 'defaultChecked' | 'onChange' | 'name' | 'value' - > & { - checked?: boolean - defaultChecked?: boolean - onChange?(checked: boolean): void - name?: string - value?: string - }, +export type SwitchProps = Props< + TTag, + SwitchRenderPropArg, + SwitchPropsWeControl | 'checked' | 'defaultChecked' | 'onChange' | 'name' | 'value' +> & { + checked?: boolean + defaultChecked?: boolean + onChange?(checked: boolean): void + name?: string + value?: string +} + +function SwitchFn( + props: SwitchProps, ref: Ref ) { let internalId = useId() @@ -196,8 +200,30 @@ let SwitchRoot = forwardRefWithAs(function Switch< {render({ ourProps, theirProps, slot, defaultTag: DEFAULT_SWITCH_TAG, name: 'Switch' })} ) -}) +} // --- -export let Switch = Object.assign(SwitchRoot, { Group, Label, Description }) +interface ComponentSwitch extends HasDisplayName { + ( + props: SwitchProps & RefProp + ): JSX.Element +} + +interface ComponentSwitchGroup extends HasDisplayName { + ( + props: SwitchGroupProps & RefProp + ): JSX.Element +} + +interface ComponentSwitchLabel extends ComponentLabel {} +interface ComponentSwitchDescription extends ComponentDescription {} + +let SwitchRoot = forwardRefWithAs(SwitchFn) as unknown as ComponentSwitch +let Group = GroupFn as unknown as ComponentSwitchGroup + +export let Switch = Object.assign(SwitchRoot, { + Group, + Label: Label as ComponentSwitchLabel, + Description: Description as ComponentSwitchDescription, +}) diff --git a/packages/@headlessui-react/src/components/tabs/tabs.tsx b/packages/@headlessui-react/src/components/tabs/tabs.tsx index 6c27793ce6..57ead334bb 100644 --- a/packages/@headlessui-react/src/components/tabs/tabs.tsx +++ b/packages/@headlessui-react/src/components/tabs/tabs.tsx @@ -15,7 +15,14 @@ import React, { } from 'react' import { Props } from '../../types' -import { render, Features, PropsForFeatures, forwardRefWithAs } from '../../utils/render' +import { + render, + Features, + PropsForFeatures, + forwardRefWithAs, + RefProp, + HasDisplayName, +} from '../../utils/render' import { useId } from '../../hooks/use-id' import { match } from '../../utils/match' import { Keys } from '../../components/keyboard' @@ -202,14 +209,16 @@ interface TabsRenderPropArg { selectedIndex: number } -let Tabs = forwardRefWithAs(function Tabs( - props: Props & { - defaultIndex?: number - onChange?: (index: number) => void - selectedIndex?: number - vertical?: boolean - manual?: boolean - }, +export type TabGroupProps = Props & { + defaultIndex?: number + onChange?: (index: number) => void + selectedIndex?: number + vertical?: boolean + manual?: boolean +} + +function GroupFn( + props: TabGroupProps, ref: Ref ) { let { @@ -312,7 +321,7 @@ let Tabs = forwardRefWithAs(function Tabs ) -}) +} // --- @@ -322,8 +331,16 @@ interface ListRenderPropArg { } type ListPropsWeControl = 'role' | 'aria-orientation' -let List = forwardRefWithAs(function List( - props: Props & {}, +export type TabListProps = Props< + TTag, + ListRenderPropArg, + ListPropsWeControl +> & { + // +} + +function ListFn( + props: TabListProps, ref: Ref ) { let { orientation, selectedIndex } = useData('Tab.List') @@ -345,7 +362,7 @@ let List = forwardRefWithAs(function List = Props< + TTag, + TabRenderPropArg, + TabPropsWeControl +> & { + // +} -let TabRoot = forwardRefWithAs(function Tab( - props: Props, +function TabFn( + props: TabProps, ref: Ref ) { let internalId = useId() @@ -476,7 +503,7 @@ let TabRoot = forwardRefWithAs(function Tab( - props: Props, +export type TabPanelsProps = Props + +function PanelsFn( + props: TabPanelsProps, ref: Ref ) { let { selectedIndex } = useData('Tab.Panels') @@ -504,7 +533,7 @@ let Panels = forwardRefWithAs(function Panels( - props: Props & - PropsForFeatures, +export type TabPanelProps = Props< + TTag, + PanelRenderPropArg, + PanelPropsWeControl +> & + PropsForFeatures + +function PanelFn( + props: TabPanelProps, ref: Ref ) { let internalId = useId() @@ -560,8 +595,44 @@ let Panel = forwardRefWithAs(function Panel( + props: TabProps & RefProp + ): JSX.Element +} + +interface ComponentTabGroup extends HasDisplayName { + ( + props: TabGroupProps & RefProp + ): JSX.Element +} + +interface ComponentTabList extends HasDisplayName { + ( + props: TabListProps & RefProp + ): JSX.Element +} + +interface ComponentTabPanels extends HasDisplayName { + ( + props: TabPanelsProps & RefProp + ): JSX.Element +} + +interface ComponentTabPanel extends HasDisplayName { + ( + props: TabPanelProps & RefProp + ): JSX.Element +} + +let TabRoot = forwardRefWithAs(TabFn) as unknown as ComponentTab +let Group = forwardRefWithAs(GroupFn) as unknown as ComponentTabGroup +let List = forwardRefWithAs(ListFn) as unknown as ComponentTabList +let Panels = forwardRefWithAs(PanelsFn) as unknown as ComponentTabPanels +let Panel = forwardRefWithAs(PanelFn) as unknown as ComponentTabPanel + +export let Tab = Object.assign(TabRoot, { Group, List, Panels, Panel }) diff --git a/packages/@headlessui-react/src/components/transitions/transition.tsx b/packages/@headlessui-react/src/components/transitions/transition.tsx index b0bfae479d..79dbde1cb6 100644 --- a/packages/@headlessui-react/src/components/transitions/transition.tsx +++ b/packages/@headlessui-react/src/components/transitions/transition.tsx @@ -16,9 +16,11 @@ import { Props, ReactTag } from '../../types' import { Features, forwardRefWithAs, + HasDisplayName, PropsForFeatures, render, RenderStrategy, + RefProp, } from '../../utils/render' import { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed' import { match } from '../../utils/match' @@ -72,7 +74,10 @@ export interface TransitionEvents { afterLeave?: () => void } -type TransitionChildProps = Props & +export type TransitionChildProps = Omit< + Props, + 'ref' +> & PropsForFeatures & TransitionClasses & TransitionEvents & { appear?: boolean } @@ -272,9 +277,10 @@ let DEFAULT_TRANSITION_CHILD_TAG = 'div' as const type TransitionChildRenderPropArg = MutableRefObject let TransitionChildRenderFeatures = Features.RenderStrategy -let TransitionChild = forwardRefWithAs(function TransitionChild< - TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG ->(props: TransitionChildProps, ref: Ref) { +function TransitionChildFn( + props: TransitionChildProps, + ref: Ref +) { let { // Event "handlers" beforeEnter, @@ -458,11 +464,17 @@ let TransitionChild = forwardRefWithAs(function TransitionChild< ) -}) +} + +export type TransitionRootProps = TransitionChildProps & { + show?: boolean + appear?: boolean +} -let TransitionRoot = forwardRefWithAs(function Transition< - TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG ->(props: TransitionChildProps & { show?: boolean; appear?: boolean }, ref: Ref) { +function TransitionRootFn( + props: TransitionRootProps, + ref: Ref +) { // @ts-expect-error let { show, appear = false, unmount, ...theirProps } = props as typeof props let internalTransitionRef = useRef(null) @@ -549,11 +561,12 @@ let TransitionRoot = forwardRefWithAs(function Transition< ) -}) +} -let Child = forwardRefWithAs(function Child< - TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG ->(props: TransitionChildProps, ref: MutableRefObject) { +function ChildFn( + props: TransitionChildProps, + ref: MutableRefObject +) { let hasTransitionContext = useContext(TransitionContext) !== null let hasOpenClosedContext = useOpenClosed() !== null @@ -566,6 +579,22 @@ let Child = forwardRefWithAs(function Child< )} ) -}) +} + +interface ComponentTransitionRoot extends HasDisplayName { + ( + props: TransitionRootProps & RefProp + ): JSX.Element +} + +interface ComponentTransitionChild extends HasDisplayName { + ( + props: TransitionChildProps & RefProp + ): JSX.Element +} + +let TransitionRoot = forwardRefWithAs(TransitionRootFn) as unknown as ComponentTransitionRoot +let TransitionChild = forwardRefWithAs(TransitionChildFn) as unknown as ComponentTransitionChild +let Child = forwardRefWithAs(ChildFn) as unknown as ComponentTransitionChild export let Transition = Object.assign(TransitionRoot, { Child, Root: TransitionRoot }) diff --git a/packages/@headlessui-react/src/internal/hidden.tsx b/packages/@headlessui-react/src/internal/hidden.tsx index 398775c026..96a981e0ae 100644 --- a/packages/@headlessui-react/src/internal/hidden.tsx +++ b/packages/@headlessui-react/src/internal/hidden.tsx @@ -1,6 +1,6 @@ import { ElementType, Ref } from 'react' import { Props } from '../types' -import { forwardRefWithAs, render } from '../utils/render' +import { forwardRefWithAs, render, HasDisplayName, RefProp } from '../utils/render' let DEFAULT_VISUALLY_HIDDEN_TAG = 'div' as const @@ -15,9 +15,14 @@ export enum Features { Hidden = 1 << 2, } -export let Hidden = forwardRefWithAs(function VisuallyHidden< - TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDEN_TAG ->(props: Props & { features?: Features }, ref: Ref) { +export type HiddenProps = Props & { + features?: Features +} + +function VisuallyHidden( + props: HiddenProps, + ref: Ref +) { let { features = Features.None, ...theirProps } = props let ourProps = { ref, @@ -46,4 +51,12 @@ export let Hidden = forwardRefWithAs(function VisuallyHidden< defaultTag: DEFAULT_VISUALLY_HIDDEN_TAG, name: 'Hidden', }) -}) +} + +interface ComponentHidden extends HasDisplayName { + ( + props: HiddenProps & RefProp + ): JSX.Element +} + +export let Hidden = forwardRefWithAs(VisuallyHidden) as unknown as ComponentHidden diff --git a/packages/@headlessui-react/src/types.ts b/packages/@headlessui-react/src/types.ts index 3b246f09ae..caf933cbbe 100644 --- a/packages/@headlessui-react/src/types.ts +++ b/packages/@headlessui-react/src/types.ts @@ -11,18 +11,16 @@ export type __ = typeof __ export type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never export type PropsOf = TTag extends React.ElementType - ? React.ComponentProps + ? Omit, 'ref'> : never type PropsWeControl = 'as' | 'children' | 'refName' | 'className' // Resolve the props of the component, but ensure to omit certain props that we control -type CleanProps< - TTag extends ReactTag, - TOmitableProps extends PropertyKey = __ -> = TOmitableProps extends __ - ? Omit, PropsWeControl> - : Omit, TOmitableProps | PropsWeControl> +type CleanProps = Omit< + PropsOf, + TOmitableProps | PropsWeControl +> // Add certain props that we control type OurProps = { @@ -50,7 +48,7 @@ type ClassNameOverride = export type Props< TTag extends ReactTag, TSlot = {}, - TOmitableProps extends PropertyKey = __ + TOmitableProps extends PropertyKey = never > = CleanProps & OurProps & ClassNameOverride type Without = { [P in Exclude]?: never } diff --git a/packages/@headlessui-react/src/utils/render.ts b/packages/@headlessui-react/src/utils/render.ts index 61dea1c334..4d1293354b 100644 --- a/packages/@headlessui-react/src/utils/render.ts +++ b/packages/@headlessui-react/src/utils/render.ts @@ -8,6 +8,7 @@ import { // Types ElementType, ReactElement, + Ref, } from 'react' import { Props, XOR, __, Expand } from '../types' import { classNames } from './class-names' @@ -56,7 +57,9 @@ export function render & PropsForFeatures> + ourProps: Expand & PropsForFeatures> & { + ref?: Ref + } theirProps: Expand> slot?: TSlot defaultTag: ElementType @@ -122,8 +125,8 @@ function _render( | ReactElement[] // Allow for className to be a function with the slot as the contents - if (rest.className && typeof rest.className === 'function') { - ;(rest as any).className = rest.className(slot) + if ('className' in rest && rest.className && typeof rest.className === 'function') { + rest.className = rest.className(slot) } let dataAttributes: Record = {} @@ -170,6 +173,7 @@ function _render( } // Merge class name prop in SSR + // @ts-ignore We know that the props may not have className. It'll be undefined then which is fine. let newClassName = classNames(resolvedChildren.props?.className, rest.className) let classNameProps = newClassName ? { className: newClassName } : {} @@ -178,7 +182,7 @@ function _render( Object.assign( {}, // Filter out undefined values so that they don't override the existing values - mergeProps(resolvedChildren.props, compact(omit(rest, ['ref']))), + mergeProps(resolvedChildren.props as any, compact(omit(rest, ['ref']))), dataAttributes, refRelatedProps, mergeRefs((resolvedChildren as any).ref, refRelatedProps.ref), @@ -273,6 +277,14 @@ function mergeProps(...listOfProps: Props[]) { return target } +export type HasDisplayName = { + displayName: string +} + +export type RefProp = T extends (props: any, ref: Ref) => any + ? { ref?: Ref } + : never + /** * This is a hack, but basically we want to keep the full 'API' of the component, but we do want to * wrap it in a forwardRef so that we _can_ passthrough the ref @@ -294,7 +306,7 @@ export function compact>(object: T) { } function omit>(object: T, keysToOmit: string[] = []) { - let clone = Object.assign({}, object) + let clone = Object.assign({}, object) as T for (let key of keysToOmit) { if (key in clone) delete clone[key] } diff --git a/packages/@headlessui-react/tsconfig.json b/packages/@headlessui-react/tsconfig.json index 68bf66cb70..c7e78c2c4a 100644 --- a/packages/@headlessui-react/tsconfig.json +++ b/packages/@headlessui-react/tsconfig.json @@ -19,7 +19,7 @@ "@headlessui/react": ["src"], "*": ["src/*", "node_modules/*"] }, - "jsx": "preserve", + "jsx": "react", "esModuleInterop": true, "target": "ESNext", "allowJs": true, diff --git a/packages/playground-react/pages/popover/popover.tsx b/packages/playground-react/pages/popover/popover.tsx index 788d3f4468..40f8abe234 100644 --- a/packages/playground-react/pages/popover/popover.tsx +++ b/packages/playground-react/pages/popover/popover.tsx @@ -6,9 +6,9 @@ let Button = forwardRef( (props: React.ComponentProps<'button'>, ref: React.MutableRefObject) => { return ( ) } diff --git a/yarn.lock b/yarn.lock index 76dc0fd787..4c145a53ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -326,11 +326,121 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== +"@esbuild/android-arm64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.8.tgz#b3d5b65a3b2e073a6c7ee36b1f3c30c8f000315b" + integrity sha512-oa/N5j6v1svZQs7EIRPqR8f+Bf8g6HBDjD/xHC02radE/NjKHK7oQmtmLxPs1iVwYyvE+Kolo6lbpfEQ9xnhxQ== + +"@esbuild/android-arm@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.8.tgz#c41e496af541e175369d48164d0cf01a5f656cf6" + integrity sha512-0/rb91GYKhrtbeglJXOhAv9RuYimgI8h623TplY2X+vA4EXnk3Zj1fXZreJ0J3OJJu1bwmb0W7g+2cT/d8/l/w== + +"@esbuild/android-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.8.tgz#080fa67c29be77f5a3ca5ee4cc78d5bf927e3a3b" + integrity sha512-bTliMLqD7pTOoPg4zZkXqCDuzIUguEWLpeqkNfC41ODBHwoUgZ2w5JBeYimv4oP6TDVocoYmEhZrCLQTrH89bg== + +"@esbuild/darwin-arm64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.8.tgz#053622bf9a82f43d5c075b7818e02618f7b4a397" + integrity sha512-ghAbV3ia2zybEefXRRm7+lx8J/rnupZT0gp9CaGy/3iolEXkJ6LYRq4IpQVI9zR97ID80KJVoUlo3LSeA/sMAg== + +"@esbuild/darwin-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.8.tgz#8a1aadb358d537d8efad817bb1a5bff91b84734b" + integrity sha512-n5WOpyvZ9TIdv2V1K3/iIkkJeKmUpKaCTdun9buhGRWfH//osmUjlv4Z5mmWdPWind/VGcVxTHtLfLCOohsOXw== + +"@esbuild/freebsd-arm64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.8.tgz#e6738d0081ba0721a5c6c674e84c6e7fcea61989" + integrity sha512-a/SATTaOhPIPFWvHZDoZYgxaZRVHn0/LX1fHLGfZ6C13JqFUZ3K6SMD6/HCtwOQ8HnsNaEeokdiDSFLuizqv5A== + +"@esbuild/freebsd-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.8.tgz#1855e562f2b730f4483f6e94086e9e2597feb4c3" + integrity sha512-xpFJb08dfXr5+rZc4E+ooZmayBW6R3q59daCpKZ/cDU96/kvDM+vkYzNeTJCGd8rtO6fHWMq5Rcv/1cY6p6/0Q== + +"@esbuild/linux-arm64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.8.tgz#481da38952721a3fdb77c17a36ceaacc4270b5c5" + integrity sha512-v3iwDQuDljLTxpsqQDl3fl/yihjPAyOguxuloON9kFHYwopeJEf1BkDXODzYyXEI19gisEsQlG1bM65YqKSIww== + +"@esbuild/linux-arm@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.8.tgz#18127072b270bb6321c6d11be20bfd30e0d6ad17" + integrity sha512-6Ij8gfuGszcEwZpi5jQIJCVIACLS8Tz2chnEBfYjlmMzVsfqBP1iGmHQPp7JSnZg5xxK9tjCc+pJ2WtAmPRFVA== + +"@esbuild/linux-ia32@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.8.tgz#ee400af7b3bc69e8ca2e593ca35156ffb9abd54f" + integrity sha512-8svILYKhE5XetuFk/B6raFYIyIqydQi+GngEXJgdPdI7OMKUbSd7uzR02wSY4kb53xBrClLkhH4Xs8P61Q2BaA== + "@esbuild/linux-loong64@0.14.54": version "0.14.54" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== +"@esbuild/linux-loong64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.8.tgz#8c509d8a454693d39824b83b3f66c400872fce82" + integrity sha512-B6FyMeRJeV0NpyEOYlm5qtQfxbdlgmiGdD+QsipzKfFky0K5HW5Td6dyK3L3ypu1eY4kOmo7wW0o94SBqlqBSA== + +"@esbuild/linux-mips64el@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.8.tgz#f2b0d36e63fb26bc3f95b203b6a80638292101ca" + integrity sha512-CCb67RKahNobjm/eeEqeD/oJfJlrWyw29fgiyB6vcgyq97YAf3gCOuP6qMShYSPXgnlZe/i4a8WFHBw6N8bYAA== + +"@esbuild/linux-ppc64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.8.tgz#1e628be003e036e90423716028cc884fe5ba25bd" + integrity sha512-bytLJOi55y55+mGSdgwZ5qBm0K9WOCh0rx+vavVPx+gqLLhxtSFU0XbeYy/dsAAD6xECGEv4IQeFILaSS2auXw== + +"@esbuild/linux-riscv64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.8.tgz#419a815cb4c3fb9f1b78ef5295f5b48b8bf6427a" + integrity sha512-2YpRyQJmKVBEHSBLa8kBAtbhucaclb6ex4wchfY0Tj3Kg39kpjeJ9vhRU7x4mUpq8ISLXRXH1L0dBYjAeqzZAw== + +"@esbuild/linux-s390x@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.8.tgz#291c49ae5c3d11d226352755c0835911fe1a9e5c" + integrity sha512-QgbNY/V3IFXvNf11SS6exkpVcX0LJcob+0RWCgV9OiDAmVElnxciHIisoSix9uzYzScPmS6dJFbZULdSAEkQVw== + +"@esbuild/linux-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.8.tgz#03199d91c76faf80bd54104f5cbf0a489bc39f6a" + integrity sha512-mM/9S0SbAFDBc4OPoyP6SEOo5324LpUxdpeIUUSrSTOfhHU9hEfqRngmKgqILqwx/0DVJBzeNW7HmLEWp9vcOA== + +"@esbuild/netbsd-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.8.tgz#b436d767e1b21852f9ed212e2bb57f77203b0ae2" + integrity sha512-eKUYcWaWTaYr9zbj8GertdVtlt1DTS1gNBWov+iQfWuWyuu59YN6gSEJvFzC5ESJ4kMcKR0uqWThKUn5o8We6Q== + +"@esbuild/openbsd-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.8.tgz#d1481d8539e21d4729cd04a0450a26c2c8789e89" + integrity sha512-Vc9J4dXOboDyMXKD0eCeW0SIeEzr8K9oTHJU+Ci1mZc5njPfhKAqkRt3B/fUNU7dP+mRyralPu8QUkiaQn7iIg== + +"@esbuild/sunos-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.8.tgz#2cfb8126e079b2c00fd1bf095541e9f5c47877e4" + integrity sha512-0xvOTNuPXI7ft1LYUgiaXtpCEjp90RuBBYovdd2lqAFxje4sEucurg30M1WIm03+3jxByd3mfo+VUmPtRSVuOw== + +"@esbuild/win32-arm64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.8.tgz#7c6ecfd097ca23b82119753bf7072bbaefe51e3a" + integrity sha512-G0JQwUI5WdEFEnYNKzklxtBheCPkuDdu1YrtRrjuQv30WsYbkkoixKxLLv8qhJmNI+ATEWquZe/N0d0rpr55Mg== + +"@esbuild/win32-ia32@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.8.tgz#cffec63c3cb0ef8563a04df4e09fa71056171d00" + integrity sha512-Fqy63515xl20OHGFykjJsMnoIWS+38fqfg88ClvPXyDbLtgXal2DTlhb1TfTX34qWi3u4I7Cq563QcHpqgLx8w== + +"@esbuild/win32-x64@0.17.8": + version "0.17.8" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.8.tgz#200a0965cf654ac28b971358ecdca9cc5b44c335" + integrity sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg== + "@heroicons/react@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" @@ -2040,201 +2150,101 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -esbuild-android-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz#b868bbd9955a92309c69df628d8dd1945478b45c" - integrity sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ== - esbuild-android-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== -esbuild-android-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz#e7d6430555e8e9c505fd87266bbc709f25f1825c" - integrity sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ== - esbuild-android-arm64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== -esbuild-darwin-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz#4dc7484127564e89b4445c0a560a3cb50b3d68e1" - integrity sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g== - esbuild-darwin-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== -esbuild-darwin-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz#469e59c665f84a8ed323166624c5e7b9b2d22ac1" - integrity sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ== - esbuild-darwin-arm64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== -esbuild-freebsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz#895df03bf5f87094a56c9a5815bf92e591903d70" - integrity sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA== - esbuild-freebsd-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== -esbuild-freebsd-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz#0b72a41a6b8655e9a8c5608f2ec1afdcf6958441" - integrity sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA== - esbuild-freebsd-arm64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== -esbuild-linux-32@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz#43b8ba3803b0bbe7f051869c6a8bf6de1e95de28" - integrity sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw== - esbuild-linux-32@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== -esbuild-linux-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz#dc8072097327ecfadba1735562824ce8c05dd0bd" - integrity sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg== - esbuild-linux-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== -esbuild-linux-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz#c52b58cbe948426b1559910f521b0a3f396f10b8" - integrity sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ== - esbuild-linux-arm64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== -esbuild-linux-arm@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz#df869dbd67d4ee3a04b3c7273b6bd2b233e78a18" - integrity sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw== - esbuild-linux-arm@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== -esbuild-linux-mips64le@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz#a2b646d9df368b01aa970a7b8968be6dd6b01d19" - integrity sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A== - esbuild-linux-mips64le@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== -esbuild-linux-ppc64le@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz#9a21af766a0292578a3009c7408b8509cac7cefd" - integrity sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA== - esbuild-linux-ppc64le@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== -esbuild-linux-riscv64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz#344a27f91568056a5903ad5841b447e00e78d740" - integrity sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg== - esbuild-linux-riscv64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== -esbuild-linux-s390x@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz#73a7309bd648a07ef58f069658f989a5096130db" - integrity sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg== - esbuild-linux-s390x@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== -esbuild-netbsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz#482a587cdbd18a6c264a05136596927deb46c30a" - integrity sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q== - esbuild-netbsd-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== -esbuild-openbsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz#e99f8cdc63f1628747b63edd124d53cf7796468d" - integrity sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw== - esbuild-openbsd-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== -esbuild-sunos-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz#8611d825bcb8239c78d57452e83253a71942f45c" - integrity sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg== - esbuild-sunos-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== -esbuild-windows-32@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz#c06374206d4d92dd31d4fda299b09f51a35e82f6" - integrity sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw== - esbuild-windows-32@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== -esbuild-windows-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz#756631c1d301dfc0d1a887deed2459ce4079582f" - integrity sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg== - esbuild-windows-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== -esbuild-windows-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz#ad7e187193dcd18768b16065a950f4441d7173f4" - integrity sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg== - esbuild-windows-arm64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" @@ -2245,32 +2255,6 @@ esbuild@^0.11.18: resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8" integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q== -esbuild@^0.14.11: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.27.tgz#41fe0f1b6b68b9f77cac025009bc54bb96e616f1" - integrity sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q== - optionalDependencies: - esbuild-android-64 "0.14.27" - esbuild-android-arm64 "0.14.27" - esbuild-darwin-64 "0.14.27" - esbuild-darwin-arm64 "0.14.27" - esbuild-freebsd-64 "0.14.27" - esbuild-freebsd-arm64 "0.14.27" - esbuild-linux-32 "0.14.27" - esbuild-linux-64 "0.14.27" - esbuild-linux-arm "0.14.27" - esbuild-linux-arm64 "0.14.27" - esbuild-linux-mips64le "0.14.27" - esbuild-linux-ppc64le "0.14.27" - esbuild-linux-riscv64 "0.14.27" - esbuild-linux-s390x "0.14.27" - esbuild-netbsd-64 "0.14.27" - esbuild-openbsd-64 "0.14.27" - esbuild-sunos-64 "0.14.27" - esbuild-windows-32 "0.14.27" - esbuild-windows-64 "0.14.27" - esbuild-windows-arm64 "0.14.27" - esbuild@^0.14.47: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2" @@ -2298,6 +2282,34 @@ esbuild@^0.14.47: esbuild-windows-64 "0.14.54" esbuild-windows-arm64 "0.14.54" +esbuild@^0.17.8: + version "0.17.8" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.8.tgz#f7f799abc7cdce3f0f2e3e0c01f120d4d55193b4" + integrity sha512-g24ybC3fWhZddZK6R3uD2iF/RIPnRpwJAqLov6ouX3hMbY4+tKolP0VMF3zuIYCaXun+yHwS5IPQ91N2BT191g== + optionalDependencies: + "@esbuild/android-arm" "0.17.8" + "@esbuild/android-arm64" "0.17.8" + "@esbuild/android-x64" "0.17.8" + "@esbuild/darwin-arm64" "0.17.8" + "@esbuild/darwin-x64" "0.17.8" + "@esbuild/freebsd-arm64" "0.17.8" + "@esbuild/freebsd-x64" "0.17.8" + "@esbuild/linux-arm" "0.17.8" + "@esbuild/linux-arm64" "0.17.8" + "@esbuild/linux-ia32" "0.17.8" + "@esbuild/linux-loong64" "0.17.8" + "@esbuild/linux-mips64el" "0.17.8" + "@esbuild/linux-ppc64" "0.17.8" + "@esbuild/linux-riscv64" "0.17.8" + "@esbuild/linux-s390x" "0.17.8" + "@esbuild/linux-x64" "0.17.8" + "@esbuild/netbsd-x64" "0.17.8" + "@esbuild/openbsd-x64" "0.17.8" + "@esbuild/sunos-x64" "0.17.8" + "@esbuild/win32-arm64" "0.17.8" + "@esbuild/win32-ia32" "0.17.8" + "@esbuild/win32-x64" "0.17.8" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5524,10 +5536,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.5.4: - version "4.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" - integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== unbox-primitive@^1.0.1: version "1.0.1"