Skip to content

Commit 2b9b95b

Browse files
renovate[bot]dimaMachina
andauthoredSep 1, 2024··
fix(deps): update dependency @headlessui/react to v2 (#3037)
* fix(deps): update dependency @headlessui/react to v2 * more * more * more * prettier * remove popper * upd lock * more * migrate navbar * lint * add changeset * Update .changeset/clever-timers-impress.md --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Dimitri POSTOLOV <dmytropostolov@gmail.com>
1 parent 7d54c71 commit 2b9b95b

File tree

14 files changed

+300
-139
lines changed

14 files changed

+300
-139
lines changed
 

‎.changeset/clever-timers-impress.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'nextra-theme-docs': patch
3+
'nextra': patch
4+
---
5+
6+
migrate to `@headlessui/react` v2

‎.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ module.exports = {
121121
{
122122
files: '**/*.{ts,tsx,cts,mts}',
123123
extends: [
124+
'plugin:deprecation/recommended'
124125
// TODO: fix errors
125126
// 'plugin:@typescript-eslint/recommended-requiring-type-checking'
126127
],

‎package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"@typescript-eslint/parser": "8.0.0",
3131
"eslint": "9.8.0",
3232
"eslint-config-prettier": "9.1.0",
33+
"eslint-plugin-deprecation": "3.0.0",
3334
"eslint-plugin-import": "2.29.1",
3435
"eslint-plugin-react": "7.35.0",
3536
"eslint-plugin-react-hooks": "5.1.0-rc-14a4699f-20240725",
@@ -54,7 +55,8 @@
5455
},
5556
"patchedDependencies": {
5657
"@changesets/assemble-release-plan@6.0.3": "patches/@changesets__assemble-release-plan@6.0.3.patch",
57-
"tsup@8.2.4": "patches/tsup.patch"
58+
"tsup@8.2.4": "patches/tsup.patch",
59+
"eslint-plugin-deprecation@3.0.0": "patches/eslint-plugin-deprecation@3.0.0.patch"
5860
}
5961
}
6062
}

‎packages/nextra-theme-docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"react-dom": ">=16.13.1"
3737
},
3838
"dependencies": {
39-
"@headlessui/react": "^1.7.17",
39+
"@headlessui/react": "^2.1.2",
4040
"@popperjs/core": "^2.11.8",
4141
"clsx": "^2.0.0",
4242
"escape-string-regexp": "^5.0.0",

‎packages/nextra-theme-docs/src/components/navbar.tsx

+19-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Menu, Transition } from '@headlessui/react'
1+
import {
2+
MenuItem as _MenuItem,
3+
Menu,
4+
MenuButton,
5+
MenuItems
6+
} from '@headlessui/react'
27
import cn from 'clsx'
38
// eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop
49
import NextLink from 'next/link'
@@ -42,31 +47,31 @@ function NavbarMenu({
4247

4348
return (
4449
<Menu as="div" className="_relative">
45-
<Menu.Button
50+
<MenuButton
4651
className={cn(
4752
classes.link,
4853
classes.inactive,
4954
'max-md:_hidden _items-center _whitespace-nowrap _rounded _flex _gap-1'
5055
)}
5156
>
5257
{children}
53-
</Menu.Button>
54-
<Transition
55-
leave="_transition-opacity"
56-
leaveFrom="_opacity-100"
57-
leaveTo="_opacity-0"
58-
as={Menu.Items}
59-
className="_absolute _right-0 _z-20 _mt-1 _max-h-64 _min-w-full _overflow-auto _rounded-md _ring-1 _ring-black/5 _bg-white _py-1 _text-sm _shadow-lg dark:_ring-white/20 dark:_bg-neutral-800"
58+
</MenuButton>
59+
<MenuItems
60+
transition
61+
className={cn(
62+
'_transition-opacity data-[closed]:_opacity-0 data-[open]:_opacity-100',
63+
'_absolute _right-0 _z-20 _mt-1 _max-h-64 _min-w-full _overflow-auto _rounded-md _ring-1 _ring-black/5 _bg-white _py-1 _text-sm _shadow-lg dark:_ring-white/20 dark:_bg-neutral-800'
64+
)}
6065
>
6166
{entries.map(([key, item]) => (
62-
<Menu.Item key={key}>
63-
{({ active }) => (
67+
<_MenuItem key={key}>
68+
{({ focus }) => (
6469
<Anchor
6570
href={item.href || routes[key]?.route || menu.route + '/' + key}
6671
className={cn(
6772
'_relative _w-full _select-none _whitespace-nowrap hover:_text-gray-900 dark:hover:_text-gray-100 _inline-block',
6873
'_py-1.5 _transition-colors ltr:_pl-3 ltr:_pr-9 rtl:_pr-3 rtl:_pl-9',
69-
active
74+
focus
7075
? '_text-gray-900 dark:_text-gray-100'
7176
: '_text-gray-600 dark:_text-gray-400'
7277
)}
@@ -75,9 +80,9 @@ function NavbarMenu({
7580
{item.title || key}
7681
</Anchor>
7782
)}
78-
</Menu.Item>
83+
</_MenuItem>
7984
))}
80-
</Transition>
85+
</MenuItems>
8186
</Menu>
8287
)
8388
}

‎packages/nextra-theme-docs/src/components/search.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Transition } from '@headlessui/react'
1+
import { Transition, TransitionChild } from '@headlessui/react'
22
import cn from 'clsx'
33
// eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop
44
import NextLink from 'next/link'
@@ -159,7 +159,6 @@ export function Search({
159159
const icon = (
160160
<Transition
161161
show={mounted && (!show || Boolean(value))}
162-
as={Fragment}
163162
enter="_transition-opacity"
164163
enterFrom="_opacity-0"
165164
enterTo="_opacity-100"
@@ -264,7 +263,7 @@ export function Search({
264263
<Transition
265264
show={renderList}
266265
// Transition.Child is required here, otherwise popup will be still present in DOM after focus out
267-
as={Transition.Child}
266+
as={TransitionChild}
268267
leave="_transition-opacity _duration-100"
269268
leaveFrom="_opacity-100"
270269
leaveTo="_opacity-0"
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Listbox, Transition } from '@headlessui/react'
1+
import {
2+
Listbox,
3+
ListboxButton,
4+
ListboxOption,
5+
ListboxOptions
6+
} from '@headlessui/react'
27
import cn from 'clsx'
3-
import { useMounted } from 'nextra/hooks'
48
import { CheckIcon } from 'nextra/icons'
5-
import type { ReactElement, ReactNode } from 'react'
6-
import { createPortal } from 'react-dom'
7-
import { usePopper } from '../utils'
9+
import type { ReactElement } from 'react'
810

911
interface MenuOption {
1012
key: string
@@ -26,28 +28,10 @@ export function Select({
2628
title,
2729
className
2830
}: MenuProps): ReactElement {
29-
const [trigger, container] = usePopper({
30-
strategy: 'fixed',
31-
placement: 'top-start',
32-
modifiers: [
33-
{ name: 'offset', options: { offset: [0, 10] } },
34-
{
35-
name: 'sameWidth',
36-
enabled: true,
37-
fn({ state }) {
38-
state.styles.popper.minWidth = `${state.rects.reference.width}px`
39-
},
40-
phase: 'beforeWrite',
41-
requires: ['computeStyles']
42-
}
43-
]
44-
})
45-
4631
return (
4732
<Listbox value={selected} onChange={onChange}>
4833
{({ open }) => (
49-
<Listbox.Button
50-
ref={trigger}
34+
<ListboxButton
5135
title={title}
5236
className={cn(
5337
'_h-7 _rounded-md _px-2 _text-left _text-xs _font-medium _text-gray-600 _transition-colors dark:_text-gray-400',
@@ -58,48 +42,39 @@ export function Select({
5842
)}
5943
>
6044
{selected.name}
61-
<Portal>
62-
<Transition
63-
ref={container}
64-
show={open}
65-
as={Listbox.Options}
66-
className="_z-20 _max-h-64 _overflow-auto _rounded-md _ring-1 _ring-black/5 _bg-white _py-1 _text-sm _shadow-lg dark:_ring-white/20 dark:_bg-neutral-800"
67-
leave="_transition-opacity"
68-
leaveFrom="_opacity-100"
69-
leaveTo="_opacity-0"
70-
>
71-
{options.map(option => (
72-
<Listbox.Option
73-
key={option.key}
74-
value={option}
75-
className={({ active }) =>
76-
cn(
77-
active
78-
? '_bg-primary-50 _text-primary-600 dark:_bg-primary-500/10'
79-
: '_text-gray-800 dark:_text-gray-100',
80-
'_relative _cursor-pointer _whitespace-nowrap _py-1.5',
81-
'_transition-colors ltr:_pl-3 ltr:_pr-9 rtl:_pr-3 rtl:_pl-9'
82-
)
83-
}
84-
>
85-
{option.name}
86-
{option.key === selected.key && (
87-
<span className="_absolute _inset-y-0 _flex _items-center ltr:_right-3 rtl:_left-3">
88-
<CheckIcon />
89-
</span>
90-
)}
91-
</Listbox.Option>
92-
))}
93-
</Transition>
94-
</Portal>
95-
</Listbox.Button>
45+
<ListboxOptions
46+
transition
47+
anchor={{
48+
to: 'top start',
49+
gap: 10
50+
}}
51+
className="_transition-opacity data-[closed]:_opacity-0 data-[open]:_opacity-100 _min-w-[--button-width] _z-20 _max-h-64 _overflow-auto _rounded-md _ring-1 _ring-black/5 _bg-white _py-1 _text-sm _shadow-lg dark:_ring-white/20 dark:_bg-neutral-800"
52+
>
53+
{options.map(option => (
54+
<ListboxOption
55+
key={option.key}
56+
value={option}
57+
className={({ focus }) =>
58+
cn(
59+
focus
60+
? '_bg-primary-50 _text-primary-600 dark:_bg-primary-500/10'
61+
: '_text-gray-800 dark:_text-gray-100',
62+
'_relative _cursor-pointer _whitespace-nowrap _py-1.5',
63+
'_transition-colors ltr:_pl-3 ltr:_pr-9 rtl:_pr-3 rtl:_pl-9'
64+
)
65+
}
66+
>
67+
{option.name}
68+
{option.key === selected.key && (
69+
<span className="_absolute _inset-y-0 _flex _items-center ltr:_right-3 rtl:_left-3">
70+
<CheckIcon />
71+
</span>
72+
)}
73+
</ListboxOption>
74+
))}
75+
</ListboxOptions>
76+
</ListboxButton>
9677
)}
9778
</Listbox>
9879
)
9980
}
100-
101-
function Portal(props: { children: ReactNode }): ReactElement | null {
102-
const mounted = useMounted()
103-
if (!mounted) return null
104-
return createPortal(props.children, document.body)
105-
}

‎packages/nextra-theme-docs/src/schemas.ts

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const themeSchema = /* @__PURE__ */ (() =>
135135
}))()
136136

137137
export const publicThemeSchema = /* @__PURE__ */ (() =>
138+
// eslint-disable-next-line deprecation/deprecation -- fixme
138139
themeSchema.deepPartial().extend({
139140
// to have `locale` and `text` as required properties
140141
i18n: i18nSchema.optional()

‎packages/nextra/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
"react-dom": ">=16.13.1"
118118
},
119119
"dependencies": {
120-
"@headlessui/react": "^1.7.17",
120+
"@headlessui/react": "^2.1.2",
121121
"@mdx-js/mdx": "^3.0.0",
122122
"@mdx-js/react": "^3.0.0",
123123
"@napi-rs/simple-git": "^0.1.9",

‎packages/nextra/src/client/components/tabs.tsx

+40-37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { Tab as HeadlessTab } from '@headlessui/react'
1+
import {
2+
Tab as HeadlessTab,
3+
TabGroup,
4+
TabList,
5+
TabPanel,
6+
TabPanels
7+
} from '@headlessui/react'
28
import cn from 'clsx'
39
import type { ComponentProps, ReactElement, ReactNode } from 'react'
410
import { useCallback, useEffect, useState } from 'react'
@@ -75,43 +81,40 @@ function Tabs_({
7581
}, []) // eslint-disable-line react-hooks/exhaustive-deps -- only on mount
7682

7783
return (
78-
<HeadlessTab.Group
84+
<TabGroup
7985
selectedIndex={selectedIndex}
8086
defaultIndex={defaultIndex}
8187
onChange={handleChange}
88+
className="nextra-scrollbar _overflow-x-auto"
89+
tabIndex={-1} // disables focus in Firefox
8290
>
83-
<div
84-
className={cn('nextra-scrollbar _overflow-x-auto')}
85-
tabIndex={-1} // disables focus in Firefox
86-
>
87-
<HeadlessTab.List className="_mt-4 _flex _w-max _min-w-full _gap-2 _border-b _border-gray-200 _pb-px dark:_border-neutral-800">
88-
{items.map((item, index) => {
89-
const disabled = isTabObjectItem(item) && item.disabled
90-
return (
91-
<HeadlessTab
92-
key={index}
93-
disabled={disabled}
94-
className={({ selected }) =>
95-
cn(
96-
'_ring-inset',
97-
'_rounded-t _p-2 _font-medium _leading-5 _transition-colors',
98-
'_-mb-0.5 _select-none _border-b-2',
99-
selected
100-
? '_border-primary-500 _text-primary-600'
101-
: '_border-transparent _text-gray-600 hover:_border-gray-200 hover:_text-black dark:_text-gray-200 dark:hover:_border-neutral-800 dark:hover:_text-white',
102-
disabled &&
103-
'_pointer-events-none _text-gray-400 dark:_text-neutral-600'
104-
)
105-
}
106-
>
107-
{isTabObjectItem(item) ? item.label : item}
108-
</HeadlessTab>
109-
)
110-
})}
111-
</HeadlessTab.List>
112-
</div>
113-
<HeadlessTab.Panels>{children}</HeadlessTab.Panels>
114-
</HeadlessTab.Group>
91+
<TabList className="_mt-4 _flex _w-max _min-w-full _gap-2 _border-b _border-gray-200 _pb-px dark:_border-neutral-800">
92+
{items.map((item, index) => {
93+
const disabled = isTabObjectItem(item) && item.disabled
94+
return (
95+
<HeadlessTab
96+
key={index}
97+
disabled={disabled}
98+
className={({ selected }) =>
99+
cn(
100+
'_ring-inset',
101+
'_rounded-t _p-2 _font-medium _leading-5 _transition-colors',
102+
'_-mb-0.5 _select-none _border-b-2',
103+
selected
104+
? '_border-primary-500 _text-primary-600'
105+
: '_border-transparent _text-gray-600 hover:_border-gray-200 hover:_text-black dark:_text-gray-200 dark:hover:_border-neutral-800 dark:hover:_text-white',
106+
disabled &&
107+
'_pointer-events-none _text-gray-400 dark:_text-neutral-600'
108+
)
109+
}
110+
>
111+
{isTabObjectItem(item) ? item.label : item}
112+
</HeadlessTab>
113+
)
114+
})}
115+
</TabList>
116+
<TabPanels>{children}</TabPanels>
117+
</TabGroup>
115118
)
116119
}
117120

@@ -120,11 +123,11 @@ function Tab({
120123
// For SEO display all the Panel in the DOM and set `display: none;` for those that are not selected
121124
unmount = false,
122125
...props
123-
}: Omit<ComponentProps<typeof HeadlessTab.Panel>, 'static'>): ReactElement {
126+
}: Omit<ComponentProps<typeof TabPanel>, 'static'>): ReactElement {
124127
return (
125-
<HeadlessTab.Panel {...props} unmount={unmount} className="_rounded _mt-6">
128+
<TabPanel {...props} unmount={unmount} className="_rounded _mt-6">
126129
{children}
127-
</HeadlessTab.Panel>
130+
</TabPanel>
128131
)
129132
}
130133

‎packages/nextra/src/server/rehype-plugins/rehype.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const DEFAULT_REHYPE_PRETTY_CODE_OPTIONS: RehypePrettyCodeOptions = {
3131
block: 'plaintext'
3232
},
3333
getHighlighter(opts) {
34+
// eslint-disable-next-line deprecation/deprecation -- TODO: remove deprecation error
3435
return getHighlighter({
3536
...opts,
3637
// Without `getHighlighter` option ```mdx lang is not highlighted...

‎packages/nextra/src/server/schemas.ts

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const mathJaxOptionsSchema = z
6666
*/
6767
config: z.custom<MathJax3Config>()
6868
})
69+
// eslint-disable-next-line deprecation/deprecation -- fixme
6970
.deepPartial()
7071
.optional()
7172

@@ -107,6 +108,7 @@ export const nextraConfigSchema = z
107108
rehypePrettyCodeOptions: z.custom<RehypePrettyCodeOptions>()
108109
})
109110
})
111+
// eslint-disable-next-line deprecation/deprecation -- fixme
110112
.deepPartial()
111113
.extend({ theme: z.string() })
112114

@@ -159,6 +161,7 @@ const itemSchema = linkItemSchema
159161
title: titleSchema,
160162
type: z.enum(['page', 'doc'])
161163
})
164+
// eslint-disable-next-line deprecation/deprecation -- fixme
162165
.deepPartial()
163166

164167
export const metaSchema = z
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
diff --git a/dist/rules/deprecation.js b/dist/rules/deprecation.js
2+
index 35ba448f4c4407794f4d4999bf2287350667e089..5525ef8774ff3b52b525b9edafdb2de8d0561bf7 100644
3+
--- a/dist/rules/deprecation.js
4+
+++ b/dist/rules/deprecation.js
5+
@@ -62,8 +62,7 @@ function createRuleForIdentifier(context) {
6+
return;
7+
}
8+
// - Inside an import
9+
- const isInsideImport = context
10+
- .getAncestors()
11+
+ const isInsideImport = context.sourceCode.getAncestors(id)
12+
.some((anc) => anc.type.includes('Import'));
13+
if (isInsideImport) {
14+
return;
15+
@@ -82,8 +81,8 @@ function createRuleForIdentifier(context) {
16+
}
17+
};
18+
}
19+
-function getParent(context) {
20+
- const ancestors = context.getAncestors();
21+
+function getParent(context, id) {
22+
+ const ancestors = context.sourceCode.getAncestors(id)
23+
return ancestors.length > 0 ? ancestors[ancestors.length - 1] : undefined;
24+
}
25+
// Unfortunately need to keep some state because identifiers like foo in
26+
@@ -91,7 +90,7 @@ function getParent(context) {
27+
let lastProcessedDuplicateName;
28+
function isDeclaration(id, context) {
29+
var _a, _b;
30+
- const parent = getParent(context);
31+
+ const parent = getParent(context, id);
32+
switch (parent === null || parent === void 0 ? void 0 : parent.type) {
33+
case 'TSEnumDeclaration':
34+
case 'TSInterfaceDeclaration':
35+
@@ -216,7 +215,7 @@ function getSymbol(id, services, tc) {
36+
return symbol;
37+
}
38+
function getCallExpression(context, id) {
39+
- const ancestors = context.getAncestors();
40+
+ const ancestors = context.sourceCode.getAncestors(id)
41+
let callee = id;
42+
let parent = ancestors.length > 0 ? ancestors[ancestors.length - 1] : undefined;
43+
if (parent && parent.type === 'MemberExpression' && parent.property === id) {

‎pnpm-lock.yaml

+139-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.