Improve Combobox component performance (#3697)
This PR improves the performance of the `Combobox` component. This is a similar implementation as: - https://github.com/tailwindlabs/headlessui/pull/3685 - https://github.com/tailwindlabs/headlessui/pull/3688 Before this PR, the `Combobox` component is built in a way where all the state lives in the `Combobox` itself. If state changes, everything re-renders and re-computes the necessary derived state. However, if you have a 1000 items, then every time the active item changes, all 1000 items have to re-render. To solve this, we can move the state outside of the `Combobox` component, and "subscribe" to state changes using the `useSlice` hook introduced in https://github.com/tailwindlabs/headlessui/pull/3684. This will allow us to subscribe to a slice of the state, and only re-render if the computed slice actually changes. If the active item changes, only 3 things will happen: 1. The `ComboboxOptions` will re-render and have an updated `aria-activedescendant` 2. The `ComboboxOption` that _was_ active, will re-render and the `data-focus` attribute wil be removed. 3. The `ComboboxOption` that is now active, will re-render and the `data-focus` attribute wil be added. The `Combobox` component already has a `virtual` option if you want to render many many more items. This is a bit of a different model where all the options are passed in via an array instead of rendering all `ComboboxOption` components immediately. Because of this, I didn't want to batch the registration of the options as part of this PR (similar to what we do in the `Menu` and `Listbox`) because it behaves differently compared to what mode you are using (virtual or not). Since not all components will be rendered, batching the registration until everything is registered doesn't really make sense in the general case. However, it does make sense in non-virtual mode. But because of this difference, I didn't want to implement this as part of this PR and increase the complexity of the PR even more. Instead I will follow up with more PRs with more improvements. But the key improvement of looking at the slice of the data is what makes the biggest impact. This also means that we can do another release once this is merged. Last but not least, recently we fixed a bug where the `Combobox` in `virtual` mode could crash if you search for an item that doesn't exist. To solve it, we implemented a workaround in: - https://github.com/tailwindlabs/headlessui/pull/3678 Which used a double `requestAnimationFrame` to delay the scrolling to the item. While this solved this issue, this also caused visual flicker when holding down your arrow keys. I also fixed it in this PR by introducing `patch-package` and work around the issue in the `@tanstack/virtual-core` package itself. More info: 96f4da70b16d5cf259643 Before: https://github.com/user-attachments/assets/132520d3-b4d6-42f9-9152-57427de20639 After: https://github.com/user-attachments/assets/41f198fe-9326-42d1-a09f-077b60a9f65d ## Test plan 1. All tests still pass 2. Tested this in the browser with a 1000 items. In the videos below the only thing I'm doing is holding down the `ArrowDown` key. Before: https://github.com/user-attachments/assets/945692a3-96e6-4ac7-bee0-36a1fd89172b After: https://github.com/user-attachments/assets/98a151d0-16cc-4823-811c-fcee0019937a
This commit is contained in:
Generated
+437
-51
@@ -7,6 +7,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "headlessui",
|
"name": "headlessui",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
"jest": "26",
|
"jest": "26",
|
||||||
"lint-staged": "^12.2.1",
|
"lint-staged": "^12.2.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
@@ -2638,12 +2640,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-virtual": {
|
"node_modules/@tanstack/react-virtual": {
|
||||||
"version": "3.11.1",
|
"version": "3.13.6",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz",
|
||||||
"integrity": "sha512-orn2QNe5tF6SqjucHJ6cKTKcRDe3GG7bcYqPNn72Yejj7noECdzgAyRfGt2pGDPemhYim3d1HIR/dgruCnLfUA==",
|
"integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/virtual-core": "3.10.9"
|
"@tanstack/virtual-core": "3.13.6"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -2654,22 +2656,30 @@
|
|||||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-virtual/node_modules/@tanstack/virtual-core": {
|
"node_modules/@tanstack/virtual-core": {
|
||||||
"version": "3.10.9",
|
"version": "3.13.6",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz",
|
||||||
"integrity": "sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==",
|
"integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/virtual-core": {
|
"node_modules/@tanstack/vue-virtual": {
|
||||||
"version": "3.0.0-beta.60",
|
"version": "3.13.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.6.tgz",
|
||||||
|
"integrity": "sha512-GYdZ3SJBQPzgxhuCE2fvpiH46qzHiVx5XzBSdtESgiqh4poj8UgckjGWYEhxaBbcVt1oLzh1m3Ql4TyH32TOzQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/virtual-core": "3.13.6"
|
||||||
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^2.7.0 || ^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
@@ -3141,6 +3151,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-core": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
@@ -3151,6 +3162,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-dom": {
|
"node_modules/@vue/compiler-dom": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.2.37",
|
"@vue/compiler-core": "3.2.37",
|
||||||
@@ -3218,6 +3230,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@vue/compiler-ssr": {
|
"node_modules/@vue/compiler-ssr": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.2.37",
|
"@vue/compiler-dom": "3.2.37",
|
||||||
@@ -3238,6 +3251,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@vue/reactivity-transform": {
|
"node_modules/@vue/reactivity-transform": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
@@ -3330,8 +3344,16 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@yarnpkg/lockfile": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/abab": {
|
"node_modules/abab": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -3577,6 +3599,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/at-least-node": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/atob": {
|
"node_modules/atob": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -3858,13 +3890,50 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2",
|
"call-bind-apply-helpers": "^1.0.0",
|
||||||
"get-intrinsic": "^1.2.1",
|
"es-define-property": "^1.0.0",
|
||||||
"set-function-length": "^1.1.1"
|
"get-intrinsic": "^1.2.4",
|
||||||
|
"set-function-length": "^1.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bind-apply-helpers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/call-bound": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -4405,16 +4474,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/define-data-property": {
|
"node_modules/define-data-property": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.1",
|
"es-define-property": "^1.0.0",
|
||||||
"gopd": "^1.0.1",
|
"es-errors": "^1.3.0",
|
||||||
"has-property-descriptors": "^1.0.0"
|
"gopd": "^1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/define-properties": {
|
"node_modules/define-properties": {
|
||||||
@@ -4529,6 +4603,21 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dunder-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"gopd": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -4681,6 +4770,26 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-define-property": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-errors": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-get-iterator": {
|
"node_modules/es-get-iterator": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -4700,6 +4809,19 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-object-atoms": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-set-tostringtag": {
|
"node_modules/es-set-tostringtag": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -5364,6 +5486,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-yarn-workspace-root": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"micromatch": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/flatpickr": {
|
"node_modules/flatpickr": {
|
||||||
"version": "4.6.13",
|
"version": "4.6.13",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -5458,6 +5590,32 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "9.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||||
|
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"at-least-node": "^1.0.0",
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-extra/node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -5523,14 +5681,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.2",
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
|
"es-define-property": "^1.0.1",
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"es-object-atoms": "^1.1.1",
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
"has-proto": "^1.0.1",
|
"get-proto": "^1.0.1",
|
||||||
"has-symbols": "^1.0.3",
|
"gopd": "^1.2.0",
|
||||||
"hasown": "^2.0.0"
|
"has-symbols": "^1.1.0",
|
||||||
|
"hasown": "^2.0.2",
|
||||||
|
"math-intrinsics": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -5544,6 +5713,20 @@
|
|||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dunder-proto": "^1.0.1",
|
||||||
|
"es-object-atoms": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-stream": {
|
"node_modules/get-stream": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -5641,11 +5824,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/gopd": {
|
"node_modules/gopd": {
|
||||||
"version": "1.0.1",
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"engines": {
|
||||||
"get-intrinsic": "^1.1.3"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -5678,11 +5863,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-property-descriptors": {
|
"node_modules/has-property-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.2"
|
"es-define-property": "^1.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -5700,7 +5887,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/has-symbols": {
|
"node_modules/has-symbols": {
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -5761,7 +5950,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -6126,7 +6317,6 @@
|
|||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"is-docker": "cli.js"
|
"is-docker": "cli.js"
|
||||||
},
|
},
|
||||||
@@ -6372,7 +6562,6 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-docker": "^2.0.0"
|
"is-docker": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -7289,6 +7478,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-stable-stringify": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.8",
|
||||||
|
"call-bound": "^1.0.3",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
|
"jsonify": "^0.0.1",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -7305,6 +7514,39 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile/node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jsonify": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Public Domain",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/kind-of": {
|
"node_modules/kind-of": {
|
||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -7316,6 +7558,16 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/klaw-sync": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.1.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/kleur": {
|
"node_modules/kleur": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -7820,6 +8072,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sourcemap-codec": "^1.4.8"
|
"sourcemap-codec": "^1.4.8"
|
||||||
@@ -7932,6 +8185,16 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/math-intrinsics": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/memorystream": {
|
"node_modules/memorystream": {
|
||||||
"version": "0.3.1",
|
"version": "0.3.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -8600,6 +8863,23 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/open": {
|
||||||
|
"version": "7.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
|
||||||
|
"integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-docker": "^2.0.0",
|
||||||
|
"is-wsl": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/opencollective-postinstall": {
|
"node_modules/opencollective-postinstall": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -8608,6 +8888,16 @@
|
|||||||
"opencollective-postinstall": "index.js"
|
"opencollective-postinstall": "index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-each-series": {
|
"node_modules/p-each-series": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -8715,6 +9005,90 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/patch-package": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@yarnpkg/lockfile": "^1.1.0",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"ci-info": "^3.7.0",
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"find-yarn-workspace-root": "^2.0.0",
|
||||||
|
"fs-extra": "^9.0.0",
|
||||||
|
"json-stable-stringify": "^1.0.2",
|
||||||
|
"klaw-sync": "^6.0.0",
|
||||||
|
"minimist": "^1.2.6",
|
||||||
|
"open": "^7.4.2",
|
||||||
|
"rimraf": "^2.6.3",
|
||||||
|
"semver": "^7.5.3",
|
||||||
|
"slash": "^2.0.0",
|
||||||
|
"tmp": "^0.0.33",
|
||||||
|
"yaml": "^2.2.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"patch-package": "index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14",
|
||||||
|
"npm": ">5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/ci-info": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/sibiraj-s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/slash": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/yaml": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -10029,14 +10403,18 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.1.1",
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.1",
|
"define-data-property": "^1.1.4",
|
||||||
"get-intrinsic": "^1.2.1",
|
"es-errors": "^1.3.0",
|
||||||
|
"function-bind": "^1.1.2",
|
||||||
|
"get-intrinsic": "^1.2.4",
|
||||||
"gopd": "^1.0.1",
|
"gopd": "^1.0.1",
|
||||||
"has-property-descriptors": "^1.0.0"
|
"has-property-descriptors": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -10586,6 +10964,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
|
"dev": true,
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -10628,6 +11007,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/sourcemap-codec": {
|
"node_modules/sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/spdx-correct": {
|
"node_modules/spdx-correct": {
|
||||||
@@ -11163,6 +11543,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tmp": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"os-tmpdir": "~1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmpl": {
|
"node_modules/tmpl": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -11997,7 +12390,7 @@
|
|||||||
"@floating-ui/react": "^0.26.16",
|
"@floating-ui/react": "^0.26.16",
|
||||||
"@react-aria/focus": "^3.17.1",
|
"@react-aria/focus": "^3.17.1",
|
||||||
"@react-aria/interactions": "^3.21.3",
|
"@react-aria/interactions": "^3.21.3",
|
||||||
"@tanstack/react-virtual": "^3.11.1",
|
"@tanstack/react-virtual": "^3.13.6",
|
||||||
"use-sync-external-store": "^1.5.0"
|
"use-sync-external-store": "^1.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -12056,7 +12449,7 @@
|
|||||||
"version": "1.7.22",
|
"version": "1.7.22",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/vue-virtual": "3.0.0-beta.60"
|
"@tanstack/vue-virtual": "3.13.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/vue": "8.0.0",
|
"@testing-library/vue": "8.0.0",
|
||||||
@@ -12070,20 +12463,6 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@tanstack/vue-virtual": {
|
|
||||||
"version": "3.0.0-beta.60",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@tanstack/virtual-core": "3.0.0-beta.60"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^2.7.0 || ^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages/@headlessui-vue/node_modules/@testing-library/dom": {
|
"packages/@headlessui-vue/node_modules/@testing-library/dom": {
|
||||||
"version": "9.3.3",
|
"version": "9.3.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -12121,6 +12500,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@vue/compiler-sfc": {
|
"packages/@headlessui-vue/node_modules/@vue/compiler-sfc": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.16.4",
|
"@babel/parser": "^7.16.4",
|
||||||
@@ -12137,6 +12517,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@vue/reactivity": {
|
"packages/@headlessui-vue/node_modules/@vue/reactivity": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.2.37"
|
"@vue/shared": "3.2.37"
|
||||||
@@ -12144,6 +12525,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@vue/runtime-core": {
|
"packages/@headlessui-vue/node_modules/@vue/runtime-core": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.2.37",
|
"@vue/reactivity": "3.2.37",
|
||||||
@@ -12152,6 +12534,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@vue/runtime-dom": {
|
"packages/@headlessui-vue/node_modules/@vue/runtime-dom": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/runtime-core": "3.2.37",
|
"@vue/runtime-core": "3.2.37",
|
||||||
@@ -12161,6 +12544,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/@vue/server-renderer": {
|
"packages/@headlessui-vue/node_modules/@vue/server-renderer": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.2.37",
|
"@vue/compiler-ssr": "3.2.37",
|
||||||
@@ -12201,6 +12585,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/csstype": {
|
"packages/@headlessui-vue/node_modules/csstype": {
|
||||||
"version": "2.6.21",
|
"version": "2.6.21",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/pretty-format": {
|
"packages/@headlessui-vue/node_modules/pretty-format": {
|
||||||
@@ -12218,6 +12603,7 @@
|
|||||||
},
|
},
|
||||||
"packages/@headlessui-vue/node_modules/vue": {
|
"packages/@headlessui-vue/node_modules/vue": {
|
||||||
"version": "3.2.37",
|
"version": "3.2.37",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.2.37",
|
"@vue/compiler-dom": "3.2.37",
|
||||||
|
|||||||
+3
-1
@@ -25,7 +25,8 @@
|
|||||||
"lint-types": "CI=true npm run lint-types --workspaces --if-present",
|
"lint-types": "CI=true npm run lint-types --workspaces --if-present",
|
||||||
"release-channel": "node ./scripts/release-channel.js",
|
"release-channel": "node ./scripts/release-channel.js",
|
||||||
"release-notes": "node ./scripts/release-notes.js",
|
"release-notes": "node ./scripts/release-notes.js",
|
||||||
"package-path": "node ./scripts/package-path.js"
|
"package-path": "node ./scripts/package-path.js",
|
||||||
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
"jest": "26",
|
"jest": "26",
|
||||||
"lint-staged": "^12.2.1",
|
"lint-staged": "^12.2.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-organize-imports": "^3.2.4",
|
"prettier-plugin-organize-imports": "^3.2.4",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Improve `Listbox` component performance ([#3688](https://github.com/tailwindlabs/headlessui/pull/3688))
|
- Improve `Listbox` component performance ([#3688](https://github.com/tailwindlabs/headlessui/pull/3688))
|
||||||
- Open `Menu` and `Listbox` on `mousedown` ([#3689](https://github.com/tailwindlabs/headlessui/pull/3689))
|
- Open `Menu` and `Listbox` on `mousedown` ([#3689](https://github.com/tailwindlabs/headlessui/pull/3689))
|
||||||
- Fix `Transition` component from incorrectly exposing the `Closing` state ([#3696](https://github.com/tailwindlabs/headlessui/pull/3696))
|
- Fix `Transition` component from incorrectly exposing the `Closing` state ([#3696](https://github.com/tailwindlabs/headlessui/pull/3696))
|
||||||
|
- Improve `Combobox` component performance ([#3697](https://github.com/tailwindlabs/headlessui/pull/3697))
|
||||||
|
|
||||||
## [2.2.1] - 2025-04-04
|
## [2.2.1] - 2025-04-04
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"@floating-ui/react": "^0.26.16",
|
"@floating-ui/react": "^0.26.16",
|
||||||
"@react-aria/focus": "^3.17.1",
|
"@react-aria/focus": "^3.17.1",
|
||||||
"@react-aria/interactions": "^3.21.3",
|
"@react-aria/interactions": "^3.21.3",
|
||||||
"@tanstack/react-virtual": "^3.11.1",
|
"@tanstack/react-virtual": "^3.13.6",
|
||||||
"use-sync-external-store": "^1.5.0"
|
"use-sync-external-store": "^1.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createContext, useContext, useMemo } from 'react'
|
||||||
|
import { ComboboxMachine } from './combobox-machine'
|
||||||
|
|
||||||
|
export const ComboboxContext = createContext<ComboboxMachine<unknown> | null>(null)
|
||||||
|
export function useComboboxMachineContext<T>(component: string) {
|
||||||
|
let context = useContext(ComboboxContext)
|
||||||
|
if (context === null) {
|
||||||
|
let err = new Error(`<${component} /> is missing a parent <Combobox /> component.`)
|
||||||
|
if (Error.captureStackTrace) Error.captureStackTrace(err, useComboboxMachine)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
return context as ComboboxMachine<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useComboboxMachine({
|
||||||
|
virtual = null,
|
||||||
|
__demoMode = false,
|
||||||
|
}: Parameters<typeof ComboboxMachine.new>[0] = {}) {
|
||||||
|
return useMemo(() => ComboboxMachine.new({ virtual, __demoMode }), [])
|
||||||
|
}
|
||||||
@@ -0,0 +1,617 @@
|
|||||||
|
import { Machine } from '../../machine'
|
||||||
|
import type { EnsureArray } from '../../types'
|
||||||
|
import { Focus, calculateActiveIndex } from '../../utils/calculate-active-index'
|
||||||
|
import { sortByDomNode } from '../../utils/focus-management'
|
||||||
|
import { match } from '../../utils/match'
|
||||||
|
|
||||||
|
interface MutableRefObject<T> {
|
||||||
|
current: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ComboboxState {
|
||||||
|
Open,
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ValueMode {
|
||||||
|
Single,
|
||||||
|
Multi,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ActivationTrigger {
|
||||||
|
Pointer,
|
||||||
|
Focus,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComboboxOptionDataRef<T> = MutableRefObject<{
|
||||||
|
disabled: boolean
|
||||||
|
value: T
|
||||||
|
domRef: MutableRefObject<HTMLElement | null>
|
||||||
|
order: number | null
|
||||||
|
}>
|
||||||
|
|
||||||
|
export interface State<T> {
|
||||||
|
dataRef: MutableRefObject<{
|
||||||
|
value: unknown
|
||||||
|
defaultValue: unknown
|
||||||
|
disabled: boolean
|
||||||
|
invalid: boolean
|
||||||
|
mode: ValueMode
|
||||||
|
immediate: boolean
|
||||||
|
onChange: (value: T) => void
|
||||||
|
onClose?: () => void
|
||||||
|
compare(a: unknown, z: unknown): boolean
|
||||||
|
isSelected(value: unknown): boolean
|
||||||
|
|
||||||
|
virtual: { options: T[]; disabled: (value: T) => boolean } | null
|
||||||
|
calculateIndex(value: unknown): number
|
||||||
|
|
||||||
|
__demoMode: boolean
|
||||||
|
|
||||||
|
optionsPropsRef: MutableRefObject<{
|
||||||
|
static: boolean
|
||||||
|
hold: boolean
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
|
||||||
|
virtual: { options: T[]; disabled: (value: unknown) => boolean } | null
|
||||||
|
|
||||||
|
comboboxState: ComboboxState
|
||||||
|
|
||||||
|
defaultToFirstOption: boolean
|
||||||
|
|
||||||
|
options: { id: string; dataRef: ComboboxOptionDataRef<T> }[]
|
||||||
|
activeOptionIndex: number | null
|
||||||
|
activationTrigger: ActivationTrigger
|
||||||
|
|
||||||
|
isTyping: boolean
|
||||||
|
|
||||||
|
inputElement: HTMLInputElement | null
|
||||||
|
buttonElement: HTMLButtonElement | null
|
||||||
|
optionsElement: HTMLElement | null
|
||||||
|
|
||||||
|
__demoMode: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ActionTypes {
|
||||||
|
OpenCombobox,
|
||||||
|
CloseCombobox,
|
||||||
|
|
||||||
|
GoToOption,
|
||||||
|
SetTyping,
|
||||||
|
|
||||||
|
RegisterOption,
|
||||||
|
UnregisterOption,
|
||||||
|
|
||||||
|
DefaultToFirstOption,
|
||||||
|
|
||||||
|
SetActivationTrigger,
|
||||||
|
|
||||||
|
UpdateVirtualConfiguration,
|
||||||
|
|
||||||
|
SetInputElement,
|
||||||
|
SetButtonElement,
|
||||||
|
SetOptionsElement,
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustOrderedState<T>(
|
||||||
|
state: State<T>,
|
||||||
|
adjustment: (options: State<T>['options']) => State<T>['options'] = (i) => i
|
||||||
|
) {
|
||||||
|
let currentActiveOption =
|
||||||
|
state.activeOptionIndex !== null ? state.options[state.activeOptionIndex] : null
|
||||||
|
|
||||||
|
let list = adjustment(state.options.slice())
|
||||||
|
let sortedOptions =
|
||||||
|
list.length > 0 && list[0].dataRef.current.order !== null
|
||||||
|
? // Prefer sorting based on the `order`
|
||||||
|
list.sort((a, z) => a.dataRef.current.order! - z.dataRef.current.order!)
|
||||||
|
: // Fallback to much slower DOM order
|
||||||
|
sortByDomNode(list, (option) => option.dataRef.current.domRef.current)
|
||||||
|
|
||||||
|
// If we inserted an option before the current active option then the active option index
|
||||||
|
// would be wrong. To fix this, we will re-lookup the correct index.
|
||||||
|
let adjustedActiveOptionIndex = currentActiveOption
|
||||||
|
? sortedOptions.indexOf(currentActiveOption)
|
||||||
|
: null
|
||||||
|
|
||||||
|
// Reset to `null` in case the currentActiveOption was removed.
|
||||||
|
if (adjustedActiveOptionIndex === -1) {
|
||||||
|
adjustedActiveOptionIndex = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
options: sortedOptions,
|
||||||
|
activeOptionIndex: adjustedActiveOptionIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Actions<T> =
|
||||||
|
| { type: ActionTypes.CloseCombobox }
|
||||||
|
| { type: ActionTypes.OpenCombobox }
|
||||||
|
| {
|
||||||
|
type: ActionTypes.GoToOption
|
||||||
|
focus: Focus.Specific
|
||||||
|
idx: number
|
||||||
|
trigger?: ActivationTrigger
|
||||||
|
}
|
||||||
|
| { type: ActionTypes.SetTyping; isTyping: boolean }
|
||||||
|
| {
|
||||||
|
type: ActionTypes.GoToOption
|
||||||
|
focus: Exclude<Focus, Focus.Specific>
|
||||||
|
trigger?: ActivationTrigger
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: ActionTypes.RegisterOption
|
||||||
|
payload: { id: string; dataRef: ComboboxOptionDataRef<T> }
|
||||||
|
}
|
||||||
|
| { type: ActionTypes.UnregisterOption; id: string }
|
||||||
|
| { type: ActionTypes.DefaultToFirstOption; value: boolean }
|
||||||
|
| { type: ActionTypes.SetActivationTrigger; trigger: ActivationTrigger }
|
||||||
|
| {
|
||||||
|
type: ActionTypes.UpdateVirtualConfiguration
|
||||||
|
options: T[]
|
||||||
|
disabled: ((value: any) => boolean) | null
|
||||||
|
}
|
||||||
|
| { type: ActionTypes.SetInputElement; element: HTMLInputElement | null }
|
||||||
|
| { type: ActionTypes.SetButtonElement; element: HTMLButtonElement | null }
|
||||||
|
| { type: ActionTypes.SetOptionsElement; element: HTMLElement | null }
|
||||||
|
|
||||||
|
let reducers: {
|
||||||
|
[P in ActionTypes]: <T>(state: State<T>, action: Extract<Actions<T>, { type: P }>) => State<T>
|
||||||
|
} = {
|
||||||
|
[ActionTypes.CloseCombobox](state) {
|
||||||
|
if (state.dataRef.current?.disabled) return state
|
||||||
|
if (state.comboboxState === ComboboxState.Closed) return state
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeOptionIndex: null,
|
||||||
|
comboboxState: ComboboxState.Closed,
|
||||||
|
|
||||||
|
isTyping: false,
|
||||||
|
|
||||||
|
// Clear the last known activation trigger
|
||||||
|
// This is because if a user interacts with the combobox using a mouse
|
||||||
|
// resulting in it closing we might incorrectly handle the next interaction
|
||||||
|
// for example, not scrolling to the active option in a virtual list
|
||||||
|
activationTrigger: ActivationTrigger.Other,
|
||||||
|
|
||||||
|
__demoMode: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.OpenCombobox](state) {
|
||||||
|
if (state.dataRef.current?.disabled) return state
|
||||||
|
if (state.comboboxState === ComboboxState.Open) return state
|
||||||
|
|
||||||
|
// Check if we have a selected value that we can make active
|
||||||
|
if (state.dataRef.current?.value) {
|
||||||
|
let idx = state.dataRef.current.calculateIndex(state.dataRef.current.value)
|
||||||
|
if (idx !== -1) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeOptionIndex: idx,
|
||||||
|
comboboxState: ComboboxState.Open,
|
||||||
|
__demoMode: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...state, comboboxState: ComboboxState.Open, __demoMode: false }
|
||||||
|
},
|
||||||
|
[ActionTypes.SetTyping](state, action) {
|
||||||
|
if (state.isTyping === action.isTyping) return state
|
||||||
|
return { ...state, isTyping: action.isTyping }
|
||||||
|
},
|
||||||
|
[ActionTypes.GoToOption](state, action) {
|
||||||
|
if (state.dataRef.current?.disabled) return state
|
||||||
|
if (
|
||||||
|
state.optionsElement &&
|
||||||
|
!state.dataRef.current?.optionsPropsRef.current.static &&
|
||||||
|
state.comboboxState === ComboboxState.Closed
|
||||||
|
) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.virtual) {
|
||||||
|
let { options, disabled } = state.virtual
|
||||||
|
let activeOptionIndex =
|
||||||
|
action.focus === Focus.Specific
|
||||||
|
? action.idx
|
||||||
|
: calculateActiveIndex(action, {
|
||||||
|
resolveItems: () => options,
|
||||||
|
resolveActiveIndex: () =>
|
||||||
|
state.activeOptionIndex ?? options.findIndex((option) => !disabled(option)) ?? null,
|
||||||
|
resolveDisabled: disabled,
|
||||||
|
resolveId() {
|
||||||
|
throw new Error('Function not implemented.')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
let activationTrigger = action.trigger ?? ActivationTrigger.Other
|
||||||
|
|
||||||
|
if (
|
||||||
|
state.activeOptionIndex === activeOptionIndex &&
|
||||||
|
state.activationTrigger === activationTrigger
|
||||||
|
) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeOptionIndex,
|
||||||
|
activationTrigger,
|
||||||
|
isTyping: false,
|
||||||
|
__demoMode: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let adjustedState = adjustOrderedState(state)
|
||||||
|
|
||||||
|
// It's possible that the activeOptionIndex is set to `null` internally, but
|
||||||
|
// this means that we will fallback to the first non-disabled option by default.
|
||||||
|
// We have to take this into account.
|
||||||
|
if (adjustedState.activeOptionIndex === null) {
|
||||||
|
let localActiveOptionIndex = adjustedState.options.findIndex(
|
||||||
|
(option) => !option.dataRef.current.disabled
|
||||||
|
)
|
||||||
|
|
||||||
|
if (localActiveOptionIndex !== -1) {
|
||||||
|
adjustedState.activeOptionIndex = localActiveOptionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeOptionIndex =
|
||||||
|
action.focus === Focus.Specific
|
||||||
|
? action.idx
|
||||||
|
: calculateActiveIndex(action, {
|
||||||
|
resolveItems: () => adjustedState.options,
|
||||||
|
resolveActiveIndex: () => adjustedState.activeOptionIndex,
|
||||||
|
resolveId: (item) => item.id,
|
||||||
|
resolveDisabled: (item) => item.dataRef.current.disabled,
|
||||||
|
})
|
||||||
|
let activationTrigger = action.trigger ?? ActivationTrigger.Other
|
||||||
|
|
||||||
|
if (
|
||||||
|
state.activeOptionIndex === activeOptionIndex &&
|
||||||
|
state.activationTrigger === activationTrigger
|
||||||
|
) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...adjustedState,
|
||||||
|
isTyping: false,
|
||||||
|
activeOptionIndex,
|
||||||
|
activationTrigger,
|
||||||
|
__demoMode: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.RegisterOption]: (state, action) => {
|
||||||
|
if (state.dataRef.current?.virtual) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
options: [...state.options, action.payload],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = action.payload
|
||||||
|
|
||||||
|
let adjustedState = adjustOrderedState(state, (options) => {
|
||||||
|
options.push(option)
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
|
||||||
|
// Check if we need to make the newly registered option active.
|
||||||
|
if (state.activeOptionIndex === null) {
|
||||||
|
if (state.dataRef.current.isSelected?.(action.payload.dataRef.current.value)) {
|
||||||
|
adjustedState.activeOptionIndex = adjustedState.options.indexOf(option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextState = {
|
||||||
|
...state,
|
||||||
|
...adjustedState,
|
||||||
|
activationTrigger: ActivationTrigger.Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.dataRef.current?.__demoMode && state.dataRef.current.value === undefined) {
|
||||||
|
nextState.activeOptionIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextState
|
||||||
|
},
|
||||||
|
[ActionTypes.UnregisterOption]: (state, action) => {
|
||||||
|
if (state.dataRef.current?.virtual) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
options: state.options.filter((option) => option.id !== action.id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let adjustedState = adjustOrderedState(state, (options) => {
|
||||||
|
let idx = options.findIndex((option) => option.id === action.id)
|
||||||
|
if (idx !== -1) options.splice(idx, 1)
|
||||||
|
return options
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...adjustedState,
|
||||||
|
activationTrigger: ActivationTrigger.Other,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.DefaultToFirstOption]: (state, action) => {
|
||||||
|
if (state.defaultToFirstOption === action.value) return state
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
defaultToFirstOption: action.value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.SetActivationTrigger]: (state, action) => {
|
||||||
|
if (state.activationTrigger === action.trigger) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activationTrigger: action.trigger,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.UpdateVirtualConfiguration]: (state, action) => {
|
||||||
|
if (state.virtual === null) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
virtual: { options: action.options, disabled: action.disabled ?? (() => false) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.virtual.options === action.options && state.virtual.disabled === action.disabled) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
let adjustedActiveOptionIndex = state.activeOptionIndex
|
||||||
|
if (state.activeOptionIndex !== null) {
|
||||||
|
let idx = action.options.indexOf(state.virtual.options[state.activeOptionIndex])
|
||||||
|
if (idx !== -1) {
|
||||||
|
adjustedActiveOptionIndex = idx
|
||||||
|
} else {
|
||||||
|
adjustedActiveOptionIndex = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeOptionIndex: adjustedActiveOptionIndex,
|
||||||
|
virtual: { options: action.options, disabled: action.disabled ?? (() => false) },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ActionTypes.SetInputElement]: (state, action) => {
|
||||||
|
if (state.inputElement === action.element) return state
|
||||||
|
return { ...state, inputElement: action.element }
|
||||||
|
},
|
||||||
|
[ActionTypes.SetButtonElement]: (state, action) => {
|
||||||
|
if (state.buttonElement === action.element) return state
|
||||||
|
return { ...state, buttonElement: action.element }
|
||||||
|
},
|
||||||
|
[ActionTypes.SetOptionsElement]: (state, action) => {
|
||||||
|
if (state.optionsElement === action.element) return state
|
||||||
|
return { ...state, optionsElement: action.element }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComboboxMachine<T> extends Machine<State<T>, Actions<T>> {
|
||||||
|
static new<T, TMultiple extends boolean | undefined>({
|
||||||
|
virtual = null,
|
||||||
|
__demoMode = false,
|
||||||
|
}: {
|
||||||
|
virtual?: {
|
||||||
|
options: TMultiple extends true ? EnsureArray<NoInfer<T>> : NoInfer<T>[]
|
||||||
|
disabled?: (
|
||||||
|
value: TMultiple extends true ? EnsureArray<NoInfer<T>>[number] : NoInfer<T>
|
||||||
|
) => boolean
|
||||||
|
} | null
|
||||||
|
__demoMode?: boolean
|
||||||
|
} = {}) {
|
||||||
|
return new ComboboxMachine({
|
||||||
|
// @ts-expect-error TODO: Re-structure such that we don't need to ignore this
|
||||||
|
dataRef: { current: {} },
|
||||||
|
comboboxState: __demoMode ? ComboboxState.Open : ComboboxState.Closed,
|
||||||
|
isTyping: false,
|
||||||
|
options: [],
|
||||||
|
// @ts-expect-error TODO: Ensure we use the correct type
|
||||||
|
virtual: virtual
|
||||||
|
? { options: virtual.options, disabled: virtual.disabled ?? (() => false) }
|
||||||
|
: null,
|
||||||
|
activeOptionIndex: null,
|
||||||
|
activationTrigger: ActivationTrigger.Other,
|
||||||
|
inputElement: null,
|
||||||
|
buttonElement: null,
|
||||||
|
optionsElement: null,
|
||||||
|
__demoMode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
actions = {
|
||||||
|
onChange: (newValue: T) => {
|
||||||
|
let { onChange, compare, mode, value } = this.state.dataRef.current
|
||||||
|
|
||||||
|
return match(mode, {
|
||||||
|
[ValueMode.Single]: () => {
|
||||||
|
return onChange?.(newValue)
|
||||||
|
},
|
||||||
|
[ValueMode.Multi]: () => {
|
||||||
|
let copy = (value as T[]).slice()
|
||||||
|
|
||||||
|
let idx = copy.findIndex((item) => compare(item, newValue))
|
||||||
|
if (idx === -1) {
|
||||||
|
copy.push(newValue)
|
||||||
|
} else {
|
||||||
|
copy.splice(idx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return onChange?.(copy as T)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
registerOption: (id: string, dataRef: ComboboxOptionDataRef<T>) => {
|
||||||
|
this.send({ type: ActionTypes.RegisterOption, payload: { id, dataRef } })
|
||||||
|
return () => {
|
||||||
|
// When we are unregistering the currently active option, then we also have to make sure to
|
||||||
|
// reset the `defaultToFirstOption` flag, so that visually something is selected and the next
|
||||||
|
// time you press a key on your keyboard it will go to the proper next or previous option in
|
||||||
|
// the list.
|
||||||
|
//
|
||||||
|
// Since this was the active option and it could have been anywhere in the list, resetting to
|
||||||
|
// the very first option seems like a fine default. We _could_ be smarter about this by going
|
||||||
|
// to the previous / next item in list if we know the direction of the keyboard navigation,
|
||||||
|
// but that might be too complex/confusing from an end users perspective.
|
||||||
|
if (
|
||||||
|
this.state.activeOptionIndex ===
|
||||||
|
this.state.dataRef.current.calculateIndex(dataRef.current.value)
|
||||||
|
) {
|
||||||
|
this.send({ type: ActionTypes.DefaultToFirstOption, value: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send({ type: ActionTypes.UnregisterOption, id })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToOption: (
|
||||||
|
focus: { focus: Focus.Specific; idx: number } | { focus: Exclude<Focus, Focus.Specific> },
|
||||||
|
trigger?: ActivationTrigger
|
||||||
|
) => {
|
||||||
|
this.send({ type: ActionTypes.DefaultToFirstOption, value: false })
|
||||||
|
return this.send({ type: ActionTypes.GoToOption, ...focus, trigger })
|
||||||
|
},
|
||||||
|
setIsTyping: (isTyping: boolean) => {
|
||||||
|
this.send({ type: ActionTypes.SetTyping, isTyping })
|
||||||
|
},
|
||||||
|
closeCombobox: () => {
|
||||||
|
this.send({ type: ActionTypes.CloseCombobox })
|
||||||
|
this.send({ type: ActionTypes.DefaultToFirstOption, value: false })
|
||||||
|
this.state.dataRef.current.onClose?.()
|
||||||
|
},
|
||||||
|
openCombobox: () => {
|
||||||
|
this.send({ type: ActionTypes.OpenCombobox })
|
||||||
|
this.send({ type: ActionTypes.DefaultToFirstOption, value: true })
|
||||||
|
},
|
||||||
|
setActivationTrigger: (trigger: ActivationTrigger) => {
|
||||||
|
this.send({ type: ActionTypes.SetActivationTrigger, trigger })
|
||||||
|
},
|
||||||
|
selectActiveOption: () => {
|
||||||
|
let activeOptionIndex = this.selectors.activeOptionIndex(this.state)
|
||||||
|
if (activeOptionIndex === null) return
|
||||||
|
|
||||||
|
this.actions.setIsTyping(false)
|
||||||
|
|
||||||
|
if (this.state.virtual) {
|
||||||
|
this.actions.onChange(this.state.virtual.options[activeOptionIndex])
|
||||||
|
} else {
|
||||||
|
let { dataRef } = this.state.options[activeOptionIndex]
|
||||||
|
this.actions.onChange(dataRef.current.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// It could happen that the `activeOptionIndex` stored in state is actually null, but we are
|
||||||
|
// getting the fallback active option back instead.
|
||||||
|
this.actions.goToOption({ focus: Focus.Specific, idx: activeOptionIndex })
|
||||||
|
},
|
||||||
|
setInputElement: (element: HTMLInputElement | null) => {
|
||||||
|
this.send({ type: ActionTypes.SetInputElement, element })
|
||||||
|
},
|
||||||
|
setButtonElement: (element: HTMLButtonElement | null) => {
|
||||||
|
this.send({ type: ActionTypes.SetButtonElement, element })
|
||||||
|
},
|
||||||
|
setOptionsElement: (element: HTMLElement | null) => {
|
||||||
|
this.send({ type: ActionTypes.SetOptionsElement, element })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
selectors = {
|
||||||
|
activeDescendantId: (state: State<T>) => {
|
||||||
|
let activeOptionIndex = this.selectors.activeOptionIndex(state)
|
||||||
|
if (activeOptionIndex === null) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.virtual) {
|
||||||
|
return state.options[activeOptionIndex]?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.options.find((option) => {
|
||||||
|
return (
|
||||||
|
!option.dataRef.current.disabled &&
|
||||||
|
state.dataRef.current.compare(
|
||||||
|
option.dataRef.current.value,
|
||||||
|
state.virtual!.options[activeOptionIndex]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})?.id
|
||||||
|
},
|
||||||
|
|
||||||
|
activeOptionIndex: (state: State<T>) => {
|
||||||
|
if (
|
||||||
|
state.defaultToFirstOption &&
|
||||||
|
state.activeOptionIndex === null &&
|
||||||
|
(state.virtual ? state.virtual.options.length > 0 : state.options.length > 0)
|
||||||
|
) {
|
||||||
|
if (state.virtual) {
|
||||||
|
let { options, disabled } = state.virtual
|
||||||
|
let activeOptionIndex = options.findIndex((option) => !(disabled?.(option) ?? false))
|
||||||
|
|
||||||
|
if (activeOptionIndex !== -1) {
|
||||||
|
return activeOptionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeOptionIndex = state.options.findIndex((option) => {
|
||||||
|
return !option.dataRef.current.disabled
|
||||||
|
})
|
||||||
|
|
||||||
|
if (activeOptionIndex !== -1) {
|
||||||
|
return activeOptionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.activeOptionIndex
|
||||||
|
},
|
||||||
|
|
||||||
|
activeOption: (state: State<T>) => {
|
||||||
|
let activeOptionIndex = this.selectors.activeOptionIndex(state)
|
||||||
|
return activeOptionIndex === null
|
||||||
|
? null
|
||||||
|
: state.virtual
|
||||||
|
? state.virtual.options[activeOptionIndex ?? 0]
|
||||||
|
: state.options[activeOptionIndex]?.dataRef.current.value ?? null
|
||||||
|
},
|
||||||
|
|
||||||
|
isActive: (state: State<T>, value: T, id: string) => {
|
||||||
|
let activeOptionIndex = this.selectors.activeOptionIndex(state)
|
||||||
|
if (activeOptionIndex === null) return false
|
||||||
|
|
||||||
|
if (state.virtual) {
|
||||||
|
return activeOptionIndex === state.dataRef.current.calculateIndex(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.options[activeOptionIndex]?.id === id
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldScrollIntoView: (state: State<T>, value: T, id: string): boolean => {
|
||||||
|
if (state.virtual) return false
|
||||||
|
if (state.__demoMode) return false
|
||||||
|
if (state.comboboxState !== ComboboxState.Open) return false
|
||||||
|
if (state.activationTrigger === ActivationTrigger.Pointer) return false
|
||||||
|
|
||||||
|
let active = this.selectors.isActive(state, value, id)
|
||||||
|
if (!active) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce(state: Readonly<State<T>>, action: Actions<T>): State<T> {
|
||||||
|
return match(action.type, reducers, state, action) as State<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,6 @@
|
|||||||
"vue": "3.2.37"
|
"vue": "3.2.37"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/vue-virtual": "3.0.0-beta.60"
|
"@tanstack/vue-virtual": "3.13.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -151,6 +151,8 @@ let VirtualProvider = defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
let virtualizer = useVirtualizer<HTMLDivElement, HTMLLIElement>(
|
let virtualizer = useVirtualizer<HTMLDivElement, HTMLLIElement>(
|
||||||
|
// @ts-expect-error TODO: Drop when using `pnpm` and `@tanstack/virtual-vue`
|
||||||
|
// has been rolled back to the older version.
|
||||||
computed(() => {
|
computed(() => {
|
||||||
return {
|
return {
|
||||||
scrollPaddingStart: padding.value.start,
|
scrollPaddingStart: padding.value.start,
|
||||||
@@ -173,6 +175,8 @@ let VirtualProvider = defineComponent({
|
|||||||
baseKey.value += 1
|
baseKey.value += 1
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// @ts-expect-error TODO: Drop when using `pnpm` and `@tanstack/virtual-vue`
|
||||||
|
// has been rolled back to the older version.
|
||||||
provide(VirtualContext, api.virtual.value ? virtualizer : null)
|
provide(VirtualContext, api.virtual.value ? virtualizer : null)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
diff --git a/node_modules/@tanstack/virtual-core/dist/cjs/index.cjs b/node_modules/@tanstack/virtual-core/dist/cjs/index.cjs
|
||||||
|
index dd4e77f..127987e 100644
|
||||||
|
--- a/node_modules/@tanstack/virtual-core/dist/cjs/index.cjs
|
||||||
|
+++ b/node_modules/@tanstack/virtual-core/dist/cjs/index.cjs
|
||||||
|
@@ -665,9 +665,9 @@ class Virtualizer {
|
||||||
|
this.options.getItemKey(index)
|
||||||
|
);
|
||||||
|
if (elementInDOM) {
|
||||||
|
- const [latestOffset] = utils.notUndefined(
|
||||||
|
- this.getOffsetForIndex(index, align)
|
||||||
|
- );
|
||||||
|
+ const result = this.getOffsetForIndex(index, align)
|
||||||
|
+ if (!result) return
|
||||||
|
+ const [latestOffset] = result
|
||||||
|
if (!utils.approxEqual(latestOffset, this.getScrollOffset())) {
|
||||||
|
this.scrollToIndex(index, { align, behavior });
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/@tanstack/virtual-core/dist/esm/index.js b/node_modules/@tanstack/virtual-core/dist/esm/index.js
|
||||||
|
index 8da519d..8c78af8 100644
|
||||||
|
--- a/node_modules/@tanstack/virtual-core/dist/esm/index.js
|
||||||
|
+++ b/node_modules/@tanstack/virtual-core/dist/esm/index.js
|
||||||
|
@@ -663,9 +663,9 @@ class Virtualizer {
|
||||||
|
this.options.getItemKey(index)
|
||||||
|
);
|
||||||
|
if (elementInDOM) {
|
||||||
|
- const [latestOffset] = notUndefined(
|
||||||
|
- this.getOffsetForIndex(index, align)
|
||||||
|
- );
|
||||||
|
+ const result = this.getOffsetForIndex(index, align)
|
||||||
|
+ if (!result) return
|
||||||
|
+ const [latestOffset] = result
|
||||||
|
if (!approxEqual(latestOffset, this.getScrollOffset())) {
|
||||||
|
this.scrollToIndex(index, { align, behavior });
|
||||||
|
}
|
||||||
|
diff --git a/node_modules/@tanstack/virtual-core/src/index.ts b/node_modules/@tanstack/virtual-core/src/index.ts
|
||||||
|
index 3a0c446..4a9e792 100644
|
||||||
|
--- a/node_modules/@tanstack/virtual-core/src/index.ts
|
||||||
|
+++ b/node_modules/@tanstack/virtual-core/src/index.ts
|
||||||
|
@@ -1003,9 +1003,9 @@ export class Virtualizer<
|
||||||
|
)
|
||||||
|
|
||||||
|
if (elementInDOM) {
|
||||||
|
- const [latestOffset] = notUndefined(
|
||||||
|
- this.getOffsetForIndex(index, align),
|
||||||
|
- )
|
||||||
|
+ const result = this.getOffsetForIndex(index, align)
|
||||||
|
+ if (!result) return
|
||||||
|
+ const [latestOffset] = result
|
||||||
|
|
||||||
|
if (!approxEqual(latestOffset, this.getScrollOffset())) {
|
||||||
|
this.scrollToIndex(index, { align, behavior })
|
||||||
@@ -75,7 +75,7 @@ export default function Home() {
|
|||||||
<Combobox.Options
|
<Combobox.Options
|
||||||
transition
|
transition
|
||||||
anchor="bottom start"
|
anchor="bottom start"
|
||||||
className="focus:outline-hidden data-closed:opacity-0 w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-1000 [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5"
|
className="focus:outline-hidden data-closed:opacity-0 w-[calc(var(--input-width)+var(--button-width))] overflow-auto rounded-md bg-white py-1 text-base leading-6 shadow-lg transition duration-300 [--anchor-gap:--spacing(1)] [--anchor-max-height:--spacing(60)] sm:text-sm sm:leading-5"
|
||||||
>
|
>
|
||||||
{countries.map((country) => (
|
{countries.map((country) => (
|
||||||
<Combobox.Option
|
<Combobox.Option
|
||||||
|
|||||||
Reference in New Issue
Block a user