diff --git a/package.json b/package.json index abccebce..4a6c560d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ ], "repository": "github:fullstackhouse/plasmic-utils", "devDependencies": { - "@headlessui/react": "^2.1.8", + "@headlessui/react": "^2.2.0", "@hookform/resolvers": "^3.3.4", "@plasmicapp/loader-nextjs": "^1.0.415", "@plasmicapp/react-web": "^0.2.374", @@ -58,7 +58,7 @@ "zod": "^3.22.4" }, "peerDependencies": { - "@headlessui/react": "^1.7.18", + "@headlessui/react": "^2.2.0", "@hookform/resolvers": "^3.3.4", "@plasmicapp/react-web": "^0.2.327", "@radix-ui/react-dialog": "^1.0.5", diff --git a/src/code-components/Combobox/Combobox.module.css b/src/code-components/Combobox/Combobox.module.css index 263bddf9..73a6204c 100644 --- a/src/code-components/Combobox/Combobox.module.css +++ b/src/code-components/Combobox/Combobox.module.css @@ -24,22 +24,26 @@ transform: rotate(180deg) translateY(5px); } -/* Clip-path to get rid of overflowing box shadow on input */ +/* + * `.positioner` is the element Headless UI portals + positions via Floating UI. + * It also gets an inline `max-width: px` from Floating UI's `size` + * middleware on every reposition, so all author-facing sizing (width via the + * `menuWidth` prop, max-width via `optionsClassName`) lives on the inner + * `.options` wrapper, which Floating UI never touches. + */ +.positioner { + transition: opacity 0.1s ease-in; +} + +.positioner[data-closed] { + opacity: 0; +} + .options { list-style: none; scrollbar-width: thin; user-select: none; - transition: opacity 0.1s ease-in; - position: absolute; - top: 100%; margin: 0; + /* Clip the top edge so the box-shadow does not bleed up over the input. */ clip-path: inset(0 -48px -48px -48px); } - -.options:global(.hide-start) { - opacity: 1; -} - -.options:global(.hide-end) { - opacity: 0; -} diff --git a/src/code-components/Combobox/Combobox.register.ts b/src/code-components/Combobox/Combobox.register.ts index f7c2277d..a2aff004 100644 --- a/src/code-components/Combobox/Combobox.register.ts +++ b/src/code-components/Combobox/Combobox.register.ts @@ -5,10 +5,10 @@ export function registerCombobox( plasmic: PlasmicLoader, modulePath = "@fullstackhouse/plasmic-utils/dist", ) { - const activeSelector = "[data-headlessui-state*=active]"; - const selectedSelector = "[aria-selected=true]"; + const activeSelector = "[data-focus]"; + const selectedSelector = "[data-selected]"; const highlightSelector = "[data-highlight=true]"; - const disabledSelector = "[aria-disabled=true]"; + const disabledSelector = "[data-disabled]"; plasmic.registerComponent(Combobox, { name: "RawCombobox", @@ -48,6 +48,27 @@ export function registerCombobox( ], }, disabled: { type: "boolean", defaultValue: false }, + menuPlacement: { + type: "choice", + options: [ + "bottom start", + "bottom end", + "bottom", + "top start", + "top end", + "top", + ], + defaultValue: "bottom start", + description: + "Where the dropdown menu opens relative to the input. The menu is portalled and kept on-screen, so it may be wider than the input.", + }, + menuWidth: { + type: "choice", + options: ["input", "fit"], + defaultValue: "input", + description: + "'input' matches the input width; 'fit' grows to the widest option (long labels don't wrap), never narrower than the input, capped to stay on screen.", + }, "aria-label": "string", "aria-labelledby": "string", placeholder: { type: "string", defaultValue: "Select" }, diff --git a/src/code-components/Combobox/Combobox.tsx b/src/code-components/Combobox/Combobox.tsx index 8f391adf..c1d300fe 100644 --- a/src/code-components/Combobox/Combobox.tsx +++ b/src/code-components/Combobox/Combobox.tsx @@ -1,5 +1,11 @@ -import { Combobox as HeadlessCombobox, Transition } from "@headlessui/react"; -import { useState, useRef, Fragment, ReactNode } from "react"; +import { + Combobox as HeadlessCombobox, + ComboboxButton, + ComboboxInput, + ComboboxOption, + ComboboxOptions, +} from "@headlessui/react"; +import { Fragment, useRef, useState, ReactNode } from "react"; import { MemoDataProvider } from "../MemoDataProvider/MemoDataProvider"; import styles from "./Combobox.module.css"; @@ -22,6 +28,31 @@ interface ComboboxProps { options?: ComboboxOption[]; optionLeftIcon?: ReactNode; disabled?: boolean; + /** + * Placement of the dropdown menu relative to the input, forwarded to Headless + * UI's `anchor` prop. The menu is portalled to the document body and kept in + * the viewport by Floating UI, so it can be wider than the input without being + * clipped by an overflow ancestor. Defaults to `"bottom start"`. + */ + menuPlacement?: + | "top" + | "top start" + | "top end" + | "bottom" + | "bottom start" + | "bottom end"; + /** + * How wide the dropdown menu is. + * - `"input"` (default): exactly as wide as the input. + * - `"fit"`: grows to the widest option (so long labels don't wrap), never + * narrower than the input. + * + * `width` / `min-width` are applied inline so they win over any `width` left in + * `optionsClassName`. `max-width` is left entirely to CSS — set your own cap on + * the options element via `optionsClassName` if you want one. Without a cap the + * menu is still kept inside the viewport by Floating UI. + */ + menuWidth?: "input" | "fit"; "aria-label"?: string; "aria-labelledby"?: string; placeholder?: string; @@ -68,6 +99,8 @@ export function Combobox({ options, optionLeftIcon, disabled, + menuPlacement = "bottom start", + menuWidth = "input", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, placeholder, @@ -90,7 +123,15 @@ export function Combobox({ arrowIconClassName, }: ComboboxProps) { const [query, setQuery] = useState(""); - const arrowIconRef = useRef(null); + const buttonRef = useRef(null); + + // `width` / `min-width` are inline so they beat any leftover `width` on + // `optionsClassName` (e.g. a `width: 100%` from the design tool). `max-width` + // is deliberately left to CSS so it stays overridable via `optionsClassName`. + const optionsStyle = + menuWidth === "fit" + ? { width: "max-content", minWidth: "var(--input-width)" } + : { width: "var(--input-width)" }; const optionGroups = groupOptions(options ?? []); const visibleOptionGroups = filterOptionGroupsByQuery(optionGroups, query); @@ -103,169 +144,165 @@ export function Combobox({ return (
onChange?.(option ? option.value : null)} + onChange={(option: ComboboxOption | null) => + onChange?.(option ? option.value : null) + } + onClose={() => setQuery("")} > - {({ open }) => { - const openMenu = () => { - arrowIconRef.current?.click(); - }; - - return ( -
-
- {leftIcon} -
- { - setQuery(event.target.value); - }} - // Open menu whenever input is focused or clicked - // (The latter is still needed. When clicking on an input that's already focused, e.g. after selecting an option, - // user may want to select another option). - // - // P.S. Once `immediate` prop gets released in a stable version - // ( https://github.com/tailwindlabs/headlessui/discussions/1236 ) - // we may simply switch to that. - onClick={() => { - if (!open) { - openMenu(); - } - }} - onFocus={(event) => { - if (!open) { - openMenu(); - event.currentTarget.select(); - event.currentTarget.setSelectionRange( - 0, - event.currentTarget.value.length, - ); - } - }} - displayValue={(option: ComboboxOption | null) => { - return option ? (option.label ?? String(option.value)) : ""; - }} - className={[inputClassName, styles.input].join(" ")} - placeholder={placeholder} - autoComplete="off" - spellCheck="false" - aria-labelledby={ariaLabelledBy} - aria-label={ariaLabel} - // Hide 1password autocomplete - // ( https://1password.community/discussion/117501/as-a-web-developer-how-can-i-disable-1password-filling-for-a-specific-field/p4 ) - data-1p-ignore - /> -
- {rightIcon} -
- ( +
+
+ {leftIcon} +
+ { + setQuery(event.target.value); + }} + // `immediate` opens the menu on focus. We still handle click so + // that clicking an already-focused input (e.g. right after + // selecting an option) reopens the menu. + onClick={() => { + if (!open) { + buttonRef.current?.click(); + } + }} + onFocus={(event) => { + event.currentTarget.select(); + event.currentTarget.setSelectionRange( + 0, + event.currentTarget.value.length, + ); + }} + displayValue={(option: ComboboxOption | null) => { + return option ? (option.label ?? String(option.value)) : ""; + }} + className={[inputClassName, styles.input].join(" ")} + placeholder={placeholder} + autoComplete="off" + spellCheck="false" + aria-labelledby={ariaLabelledBy} + aria-label={ariaLabel} + // Hide 1password autocomplete + // ( https://1password.community/discussion/117501/as-a-web-developer-how-can-i-disable-1password-filling-for-a-specific-field/p4 ) + data-1p-ignore + /> +
+ {rightIcon} +
+ + - - - - - setQuery("")} + + + + + {/* + * Inner wrapper carries all sizing + look. Headless UI's `anchor` + * writes an inline `max-width: px` (Floating UI `size` + * middleware) onto the positioned element on every reposition, + * which would override a caller's `max-width`. That element is the + * `ComboboxOptions` above; this wrapper is untouched by it. + */} +
- - {visibleOptionGroups.length === 0 ? ( -

{emptyOptionText}

- ) : ( - limitedOptionGroups.map(({ name, options }) => ( - - {name && ( - - - - )} - {options.map((option, optionIndex) => ( - - {optionLeftIcon ? ( - {emptyOptionText}

+ ) : ( + limitedOptionGroups.map(({ name, options }) => ( + + {name && ( +
+ +
+ )} + {options.map((option, optionIndex) => ( + + {optionLeftIcon ? ( + +
+ {optionLeftIcon} +
+
+ ) : null} +
+

+ +

+ {option.description ? ( +

-

- {optionLeftIcon} -
- - ) : null} -
-

- {option.description ? ( -

- -

- ) : null} -
- - ))} - - )) - )} - {overLimit && ( -

{typeToSearchText}

- )} - {footer &&
{footer}
} - - -
- ); - }} + ) : null} +
+ + ))} + + )) + )} + {overLimit && ( +

{typeToSearchText}

+ )} + {footer &&
{footer}
} +
+ +
+ )}
); diff --git a/yarn.lock b/yarn.lock index 73df7583..3003bdc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -437,15 +437,16 @@ resolved "https://registry.yarnpkg.com/@fullstackhouse/react-ssr-prepass/-/react-ssr-prepass-2.0.11.tgz#669a6d100282b13506a71a7cc5356817fdbed486" integrity sha512-MJiupFz2sxQYqipaN+TcOyJvNZuQ4bcuwO4ZvM0HInc2mgHxrUApWvRZmhipEKJdPG+rPmIMLYx9ec7nl9nA4g== -"@headlessui/react@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-2.1.8.tgz#b0e254fdf8b1f65dbf28de444f70d0a9b61705dd" - integrity sha512-uajqVkAcVG/wHwG9Fh5PFMcFpf2VxM4vNRNKxRjuK009kePVur8LkuuygHfIE+2uZ7z7GnlTtYsyUe6glPpTLg== +"@headlessui/react@^2.2.0": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-2.2.10.tgz#ad69258abcb9efea27a4b37e06e8c9cafd39ca63" + integrity sha512-5pVLNK9wlpxTUTy9GpgbX/SdcRh+HBnPktjM2wbiLTH4p+2EPHBO1aoSryUCuKUIItdDWO9ITlhUL8UnUN/oIA== dependencies: "@floating-ui/react" "^0.26.16" - "@react-aria/focus" "^3.17.1" - "@react-aria/interactions" "^3.21.3" - "@tanstack/react-virtual" "^3.8.1" + "@react-aria/focus" "^3.20.2" + "@react-aria/interactions" "^3.25.0" + "@tanstack/react-virtual" "^3.13.9" + use-sync-external-store "^1.5.0" "@hookform/resolvers@^3.3.4": version "3.9.0" @@ -516,6 +517,13 @@ dependencies: mute-stream "^1.0.0" +"@internationalized/date@^3.12.3": + version "3.12.3" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.12.3.tgz#e11d14dbf12bf45057dd5996bac77f7c2f89ff9c" + integrity sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q== + dependencies: + "@swc/helpers" "^0.5.0" + "@internationalized/date@^3.5.4": version "3.5.4" resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.5.4.tgz#49ba11634fd4350b7a9308e297032267b4063c44" @@ -538,6 +546,20 @@ dependencies: "@swc/helpers" "^0.5.0" +"@internationalized/number@^3.6.7": + version "3.6.7" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.6.7.tgz#5a0a8fa413b5f8679a59dcf37e2a74dc508b8371" + integrity sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg== + dependencies: + "@swc/helpers" "^0.5.0" + +"@internationalized/string@^3.2.10": + version "3.2.10" + resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.2.10.tgz#c38bd59a69509a41bf7422f1b2fc3af7468e4322" + integrity sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA== + dependencies: + "@swc/helpers" "^0.5.0" + "@internationalized/string@^3.2.3": version "3.2.3" resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.2.3.tgz#b0a8379e779a69e7874979714e27f2ae86761d3c" @@ -1097,6 +1119,14 @@ "@swc/helpers" "^0.5.0" clsx "^2.0.0" +"@react-aria/focus@^3.20.2": + version "3.22.1" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.22.1.tgz#7e7ba6a2250135728eb68ffa21e422ec25a16800" + integrity sha512-CPxtkyrBi/HYY5P3lE/57sQ6qfa0lN8E55TOm89H0kNGv0lKt+/0zP7lWERzBjRr5IxBVrQX4gFEowBN52LPaA== + dependencies: + "@swc/helpers" "^0.5.0" + react-aria "^3.48.0" + "@react-aria/form@^3.0.5": version "3.0.5" resolved "https://registry.yarnpkg.com/@react-aria/form/-/form-3.0.5.tgz#abaf6ac005dc3f98760ac74fdb6524ad189399d6" @@ -1132,6 +1162,15 @@ "@react-types/shared" "^3.23.1" "@swc/helpers" "^0.5.0" +"@react-aria/interactions@^3.25.0": + version "3.28.1" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.28.1.tgz#852294ae63f6a7d8aeeba3a1e7343daa8cd8507c" + integrity sha512-Bqb+HrD5I5MHS2SKBhISYqo2SW8Y2dfzgF/Y1lIJq7xqLxheo9vzxPGEHhz+XzkgGfoqEJx8A6a3C7uiqS3HWA== + dependencies: + "@react-types/shared" "^3.34.0" + "@swc/helpers" "^0.5.0" + react-aria "^3.48.0" + "@react-aria/label@^3.7.8": version "3.7.8" resolved "https://registry.yarnpkg.com/@react-aria/label/-/label-3.7.8.tgz#69f1c184836b04445fcedce78db9fd939a0570ea" @@ -1438,6 +1477,11 @@ resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.23.1.tgz#2f23c81d819d0ef376df3cd4c944be4d6bce84c3" integrity sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw== +"@react-types/shared@^3.34.0", "@react-types/shared@^3.36.1": + version "3.36.1" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.36.1.tgz#edb8081e3872ae68a4eb4a65e62233d0754ec186" + integrity sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag== + "@react-types/switch@^3.5.3": version "3.5.3" resolved "https://registry.yarnpkg.com/@react-types/switch/-/switch-3.5.3.tgz#2a5faaf513e03972df3077e4ff5ef21738239d7c" @@ -1555,17 +1599,17 @@ dependencies: tslib "^2.4.0" -"@tanstack/react-virtual@^3.8.1": - version "3.10.8" - resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz#bf4b06f157ed298644a96ab7efc1a2b01ab36e3c" - integrity sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA== +"@tanstack/react-virtual@^3.13.9": + version "3.14.10" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.14.10.tgz#371cb86ea221302768331981b6a0c11875262354" + integrity sha512-SRyoUbdFMRHuYXMijV5H4ZarQWpXkj3iANq8OFre+pybeVap8ZJjZ3Nz9bVjx4d8PfobVUQUdKyyyHYk3E+djw== dependencies: - "@tanstack/virtual-core" "3.10.8" + "@tanstack/virtual-core" "3.17.8" -"@tanstack/virtual-core@3.10.8": - version "3.10.8" - resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz#975446a667755222f62884c19e5c3c66d959b8b4" - integrity sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA== +"@tanstack/virtual-core@3.17.8": + version "3.17.8" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.17.8.tgz#3a801d8b0569a67b7490d3fe64c83758d18a230c" + integrity sha512-BfEvehNpOT75r5Ksc5xW6NZuXujTfb7nlSEyVu4XHG3gdxNg1KqXruWbDewXOUaUYIo4oRbSfkjIajz4MAT8tA== "@testing-library/dom@^10.4.0": version "10.4.0" @@ -1914,6 +1958,13 @@ aria-hidden@^1.1.1: dependencies: tslib "^2.0.0" +aria-hidden@^1.2.3: + version "1.2.6" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.6.tgz#73051c9b088114c795b1ea414e9c0fff874ffc1a" + integrity sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA== + dependencies: + tslib "^2.0.0" + aria-query@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" @@ -4333,6 +4384,21 @@ quill@~2.0.2: parchment "^3.0.0" quill-delta "^5.1.0" +react-aria@^3.48.0: + version "3.51.0" + resolved "https://registry.yarnpkg.com/react-aria/-/react-aria-3.51.0.tgz#b8129df769eda51a0f92d256fddcc105badf6ed7" + integrity sha512-AyWLw0XR38cFPwBu/ErgGaVrc5dupLEKmRlMXTGvFKOtbaGRQ2+yQJkjVhpdHhoRhU4+G+tJDFeHDTS8tK3bfQ== + dependencies: + "@internationalized/date" "^3.12.3" + "@internationalized/number" "^3.6.7" + "@internationalized/string" "^3.2.10" + "@react-types/shared" "^3.36.1" + "@swc/helpers" "^0.5.0" + aria-hidden "^1.2.3" + clsx "^2.0.0" + react-stately "3.49.0" + use-sync-external-store "^1.6.0" + react-dom@^18: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" @@ -4408,6 +4474,18 @@ react-remove-scroll@2.5.7: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-stately@3.49.0: + version "3.49.0" + resolved "https://registry.yarnpkg.com/react-stately/-/react-stately-3.49.0.tgz#bc7fda7d0245e2a1239a8f0451950b27639d0e17" + integrity sha512-13iNq2KzBrRAzxRc+n53hgROfIistiYY/sPtIhCw1qUB7/kmo+X1xEU2uiS5zcCIrc55AUPwoHqOIIpKWSwB9A== + dependencies: + "@internationalized/date" "^3.12.3" + "@internationalized/number" "^3.6.7" + "@internationalized/string" "^3.2.10" + "@react-types/shared" "^3.36.1" + "@swc/helpers" "^0.5.0" + use-sync-external-store "^1.6.0" + react-style-singleton@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" @@ -4713,7 +4791,16 @@ strict-event-emitter@^0.5.1: resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4793,7 +4880,14 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5131,6 +5225,11 @@ use-sync-external-store@^1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== +use-sync-external-store@^1.5.0, use-sync-external-store@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + valtio@^1.6.3: version "1.13.2" resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.13.2.tgz#e31d452d5da3550935417670aafd34d832dc7241" @@ -5299,7 +5398,7 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -5317,6 +5416,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"