diff options
| -rw-r--r-- | apps/web/app/dashboard/tags/page.tsx | 16 | ||||
| -rw-r--r-- | apps/web/components/dashboard/bookmarks/TagsEditor.tsx | 233 | ||||
| -rw-r--r-- | apps/web/package.json | 1 | ||||
| -rw-r--r-- | packages/trpc/routers/tags.ts | 29 | ||||
| -rw-r--r-- | pnpm-lock.yaml | 200 |
5 files changed, 348 insertions, 131 deletions
diff --git a/apps/web/app/dashboard/tags/page.tsx b/apps/web/app/dashboard/tags/page.tsx index 08acd968..dec11527 100644 --- a/apps/web/app/dashboard/tags/page.tsx +++ b/apps/web/app/dashboard/tags/page.tsx @@ -1,11 +1,8 @@ import Link from "next/link"; import { redirect } from "next/navigation"; import { Separator } from "@/components/ui/separator"; +import { api } from "@/server/api/client"; import { getServerAuthSession } from "@/server/auth"; -import { count, eq } from "drizzle-orm"; - -import { db } from "@hoarder/db"; -import { bookmarkTags, tagsOnBookmarks } from "@hoarder/db/schema"; function TagPill({ name, count }: { name: string; count: number }) { return ( @@ -24,16 +21,7 @@ export default async function TagsPage() { redirect("/"); } - let tags = await db - .select({ - id: tagsOnBookmarks.tagId, - name: bookmarkTags.name, - count: count(), - }) - .from(tagsOnBookmarks) - .where(eq(bookmarkTags.userId, session.user.id)) - .groupBy(tagsOnBookmarks.tagId) - .innerJoin(bookmarkTags, eq(bookmarkTags.id, tagsOnBookmarks.tagId)); + let tags = (await api.tags.list()).tags; // Sort tags by usage desc tags = tags.sort((a, b) => b.count - a.count); diff --git a/apps/web/components/dashboard/bookmarks/TagsEditor.tsx b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx index 12c0dcd0..38f01bdd 100644 --- a/apps/web/components/dashboard/bookmarks/TagsEditor.tsx +++ b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx @@ -1,134 +1,137 @@ -import type { KeyboardEvent } from "react"; -import { useEffect, useState } from "react"; -import { Input } from "@/components/ui/input"; +import type { ActionMeta } from "react-select"; import { toast } from "@/components/ui/use-toast"; import { api } from "@/lib/trpc"; import { cn } from "@/lib/utils"; -import { Sparkles, X } from "lucide-react"; +import { Sparkles } from "lucide-react"; +import CreateableSelect from "react-select/creatable"; import type { ZBookmark } from "@hoarder/trpc/types/bookmarks"; import type { ZAttachedByEnum } from "@hoarder/trpc/types/tags"; interface EditableTag { attachedBy: ZAttachedByEnum; - id?: string; - name: string; -} - -function TagAddInput({ addTag }: { addTag: (tag: string) => void }) { - const onKeyUp = (e: KeyboardEvent<HTMLInputElement>) => { - if (e.key === "Enter") { - addTag(e.currentTarget.value); - e.currentTarget.value = ""; - } - }; - return ( - <Input - onKeyUp={onKeyUp} - className="h-8 w-full border-none bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0" - /> - ); -} - -function TagPill({ - tag, - deleteCB, -}: { - tag: { attachedBy: ZAttachedByEnum; id?: string; name: string }; - deleteCB: () => void; -}) { - const isAttachedByAI = tag.attachedBy == "ai"; - return ( - <div - className={cn( - "flex min-h-8 space-x-1 rounded px-2", - isAttachedByAI - ? "bg-gradient-to-tr from-purple-500 to-purple-400 text-white" - : "bg-gray-200", - )} - > - {isAttachedByAI && <Sparkles className="m-auto size-4" />} - <p className="m-auto">{tag.name}</p> - <button className="m-auto size-4" onClick={deleteCB}> - <X className="size-4" /> - </button> - </div> - ); + value?: string; + label: string; } export function TagsEditor({ bookmark }: { bookmark: ZBookmark }) { - const [tags, setTags] = useState<Map<string, EditableTag>>(new Map()); - useEffect(() => { - const m = new Map<string, EditableTag>(); - for (const t of bookmark.tags) { - m.set(t.name, { attachedBy: t.attachedBy, id: t.id, name: t.name }); - } - setTags(m); - }, [bookmark.tags]); - const bookmarkInvalidationFunction = api.useUtils().bookmarks.getBookmark.invalidate; - const { mutate } = api.bookmarks.updateTags.useMutation({ - onSuccess: () => { - toast({ - description: "Tags has been updated!", - }); - bookmarkInvalidationFunction({ bookmarkId: bookmark.id }); - // TODO(bug) Invalidate the tag views as well - }, - onError: () => { - toast({ - variant: "destructive", - title: "Something went wrong", - description: "There was a problem with your request.", - }); - }, - }); + const { mutate, isPending: isMutating } = + api.bookmarks.updateTags.useMutation({ + onSuccess: () => { + toast({ + description: "Tags has been updated!", + }); + bookmarkInvalidationFunction({ bookmarkId: bookmark.id }); + // TODO(bug) Invalidate the tag views as well + }, + onError: () => { + toast({ + variant: "destructive", + title: "Something went wrong", + description: "There was a problem with your request.", + }); + }, + }); + + const { data: existingTags, isLoading: isExistingTagsLoading } = + api.tags.list.useQuery(); + + const onChange = ( + _option: readonly EditableTag[], + actionMeta: ActionMeta<EditableTag>, + ) => { + switch (actionMeta.action) { + case "remove-value": { + if (actionMeta.removedValue.value) { + mutate({ + bookmarkId: bookmark.id, + attach: [], + detach: [{ tagId: actionMeta.removedValue.value }], + }); + } + break; + } + case "create-option": { + mutate({ + bookmarkId: bookmark.id, + attach: [{ tag: actionMeta.option.label }], + detach: [], + }); + break; + } + case "select-option": { + if (actionMeta.option) { + mutate({ + bookmarkId: bookmark.id, + attach: [ + { tag: actionMeta.option.label, tagId: actionMeta.option?.value }, + ], + detach: [], + }); + } + break; + } + } + }; return ( - <div className="flex flex-wrap gap-2 rounded border p-2"> - {[...tags.values()].map((t) => ( - <TagPill - key={t.name} - tag={t} - deleteCB={() => { - setTags((m) => { - const newMap = new Map(m); - newMap.delete(t.name); - if (t.id) { - mutate({ - bookmarkId: bookmark.id, - attach: [], - detach: [{ tagId: t.id }], - }); - } - return newMap; - }); - }} - /> - ))} - <div className="flex-1"> - <TagAddInput - addTag={(val) => { - setTags((m) => { - if (m.has(val)) { - // Tag already exists - // Do nothing - return m; - } - const newMap = new Map(m); - newMap.set(val, { attachedBy: "human", name: val }); - mutate({ - bookmarkId: bookmark.id, - attach: [{ tag: val }], - detach: [], - }); - return newMap; - }); - }} - /> - </div> - </div> + <CreateableSelect + onChange={onChange} + options={ + existingTags?.tags.map((t) => ({ + label: t.name, + value: t.id, + attachedBy: "human" as const, + })) ?? [] + } + value={bookmark.tags.map((t) => ({ + label: t.name, + value: t.id, + attachedBy: t.attachedBy, + }))} + isMulti + closeMenuOnSelect={false} + isClearable={false} + isLoading={isExistingTagsLoading || isMutating} + styles={{ + multiValueRemove: () => ({ + "background-color": "transparent", + }), + valueContainer: (styles) => ({ + ...styles, + padding: "0.5rem", + }), + }} + components={{ + MultiValueContainer: ({ children, data }) => ( + <div + className={cn( + "flex min-h-8 space-x-1 rounded px-2", + (data as { attachedBy: string }).attachedBy == "ai" + ? "bg-gradient-to-tr from-purple-500 to-purple-400 text-white" + : "bg-gray-200", + )} + > + {children} + </div> + ), + MultiValueLabel: ({ children, data }) => ( + <div className="m-auto flex gap-2"> + {(data as { attachedBy: string }).attachedBy == "ai" && ( + <Sparkles className="m-auto size-4" /> + )} + {children} + </div> + ), + }} + classNames={{ + multiValueRemove: () => "my-auto", + valueContainer: () => "gap-2", + menuList: () => "text-sm", + }} + /> ); } diff --git a/apps/web/package.json b/apps/web/package.json index bfe9a5a2..4b0b5e0d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -51,6 +51,7 @@ "react-hook-form": "^7.50.1", "react-markdown": "^9.0.1", "react-masonry-css": "^1.0.16", + "react-select": "^5.8.0", "superjson": "^2.2.1", "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", diff --git a/packages/trpc/routers/tags.ts b/packages/trpc/routers/tags.ts index af11f34c..61b052d9 100644 --- a/packages/trpc/routers/tags.ts +++ b/packages/trpc/routers/tags.ts @@ -1,5 +1,5 @@ import { experimental_trpcMiddleware, TRPCError } from "@trpc/server"; -import { and, eq } from "drizzle-orm"; +import { and, count, eq } from "drizzle-orm"; import { z } from "zod"; import { bookmarks, bookmarkTags, tagsOnBookmarks } from "@hoarder/db/schema"; @@ -93,7 +93,32 @@ export const tagsAppRouter = router({ return { id: res[0].id, name: res[0].name, - bookmarks: res.flatMap((t) => t.bookmarkId ? [t.bookmarkId] : []), + bookmarks: res.flatMap((t) => (t.bookmarkId ? [t.bookmarkId] : [])), }; }), + list: authedProcedure + .output( + z.object({ + tags: z.array( + z.object({ + id: z.string(), + name: z.string(), + count: z.number(), + }), + ), + }), + ) + .query(async ({ ctx }) => { + const tags = await ctx.db + .select({ + id: tagsOnBookmarks.tagId, + name: bookmarkTags.name, + count: count(), + }) + .from(tagsOnBookmarks) + .where(eq(bookmarkTags.userId, ctx.user.id)) + .groupBy(tagsOnBookmarks.tagId) + .innerJoin(bookmarkTags, eq(bookmarkTags.id, tagsOnBookmarks.tagId)); + return { tags }; + }), }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75e23664..72229bc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -372,6 +372,9 @@ importers: react-masonry-css: specifier: ^1.0.16 version: 1.0.16(react@18.2.0) + react-select: + specifier: ^5.8.0 + version: 5.8.0(@types/react@18.2.58)(react-dom@18.2.0)(react@18.2.0) superjson: specifier: ^2.2.1 version: 2.2.1 @@ -2529,6 +2532,95 @@ packages: react: 18.2.0 dev: false + /@emotion/babel-plugin@11.11.0: + resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} + dependencies: + '@babel/helper-module-imports': 7.22.15 + '@babel/runtime': 7.23.9 + '@emotion/hash': 0.9.1 + '@emotion/memoize': 0.8.1 + '@emotion/serialize': 1.1.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + dev: false + + /@emotion/cache@11.11.0: + resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==} + dependencies: + '@emotion/memoize': 0.8.1 + '@emotion/sheet': 1.2.2 + '@emotion/utils': 1.2.1 + '@emotion/weak-memoize': 0.3.1 + stylis: 4.2.0 + dev: false + + /@emotion/hash@0.9.1: + resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} + dev: false + + /@emotion/memoize@0.8.1: + resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} + dev: false + + /@emotion/react@11.11.4(@types/react@18.2.58)(react@18.2.0): + resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.9 + '@emotion/babel-plugin': 11.11.0 + '@emotion/cache': 11.11.0 + '@emotion/serialize': 1.1.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/utils': 1.2.1 + '@emotion/weak-memoize': 0.3.1 + '@types/react': 18.2.58 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + dev: false + + /@emotion/serialize@1.1.3: + resolution: {integrity: sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==} + dependencies: + '@emotion/hash': 0.9.1 + '@emotion/memoize': 0.8.1 + '@emotion/unitless': 0.8.1 + '@emotion/utils': 1.2.1 + csstype: 3.1.3 + dev: false + + /@emotion/sheet@1.2.2: + resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} + dev: false + + /@emotion/unitless@0.8.1: + resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + dev: false + + /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): + resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + dev: false + + /@emotion/utils@1.2.1: + resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} + dev: false + + /@emotion/weak-memoize@0.3.1: + resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} + dev: false + /@esbuild-kit/core-utils@3.3.2: resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} dependencies: @@ -5888,6 +5980,10 @@ packages: dependencies: undici-types: 5.26.5 + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + dev: false + /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} @@ -5896,6 +5992,12 @@ packages: dependencies: '@types/react': 18.2.58 + /@types/react-transition-group@4.4.10: + resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} + dependencies: + '@types/react': 18.2.58 + dev: false + /@types/react@18.2.58: resolution: {integrity: sha512-TaGvMNhxvG2Q0K0aYxiKfNDS5m5ZsoIBBbtfUorxdH4NGSXIlYvZxLJI+9Dd3KjeB3780bciLyAb7ylO8pLhPw==} dependencies: @@ -6909,6 +7011,15 @@ packages: webpack: 5.90.3 dev: false + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.23.9 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + dev: false + /babel-plugin-polyfill-corejs2@0.4.8(@babel/core@7.23.9): resolution: {integrity: sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==} peerDependencies: @@ -7830,6 +7941,10 @@ packages: - supports-color dev: false + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: false + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} requiresBuild: true @@ -7885,6 +8000,17 @@ packages: parse-json: 4.0.0 dev: false + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: false + /cosmiconfig@9.0.0(typescript@5.3.3): resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -8314,6 +8440,13 @@ packages: dependencies: esutils: 2.0.3 + /dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dependencies: + '@babel/runtime': 7.23.9 + csstype: 3.1.3 + dev: false + /dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -9912,6 +10045,10 @@ packages: pkg-dir: 4.2.0 dev: false + /find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + dev: false + /find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -10492,6 +10629,12 @@ packages: source-map: 0.7.4 dev: false + /hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + /hosted-git-info@3.0.8: resolution: {integrity: sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==} engines: {node: '>=10'} @@ -14770,6 +14913,27 @@ packages: react: 18.2.0 dev: false + /react-select@5.8.0(@types/react@18.2.58)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.23.9 + '@emotion/cache': 11.11.0 + '@emotion/react': 11.11.4(@types/react@18.2.58)(react@18.2.0) + '@floating-ui/dom': 1.6.3 + '@types/react-transition-group': 4.4.10 + memoize-one: 6.0.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.58)(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + dev: false + /react-shallow-renderer@16.15.0(react@18.2.0): resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} peerDependencies: @@ -14797,6 +14961,20 @@ packages: tslib: 2.6.2 dev: false + /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + dependencies: + '@babel/runtime': 7.23.9 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -15846,6 +16024,10 @@ packages: react: 18.2.0 dev: false + /stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + dev: false + /sucrase@3.34.0: resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} engines: {node: '>=8'} @@ -16745,6 +16927,19 @@ packages: react: 18.2.0 dev: false + /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.58)(react@18.2.0): + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.58 + react: 18.2.0 + dev: false + /use-latest-callback@0.1.9(react@18.2.0): resolution: {integrity: sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==} peerDependencies: @@ -17577,6 +17772,11 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: false + /yaml@2.4.0: resolution: {integrity: sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==} engines: {node: '>= 14'} |
