diff --git a/.husky/pre-commit b/.husky/pre-commit index bfc028b7..fac4de58 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,2 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -yarn test +cd packages/react +yarn run lint \ No newline at end of file diff --git a/apps/smithy/.storybook/main.ts b/apps/smithy/.storybook/main.ts index 030eb214..092e540e 100644 --- a/apps/smithy/.storybook/main.ts +++ b/apps/smithy/.storybook/main.ts @@ -1,7 +1,14 @@ import path from "path"; // SEE: https://storybook.js.org/docs/react/faq#how-do-i-fix-module-resolution-in-special-environments -const getAbsolutePath = (packageName) => +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +// Utility to resolve the absolute path of a given package. Storybook requires absolute +// addon paths when the config file is executed in an ESM context (Node ≥ 18 / SB 8). +// We can still rely on Node's `require.resolve` by creating a CommonJS `require` +// function that is bound to this ESM module. +const getAbsolutePath = (packageName: string) => path.dirname(require.resolve(path.join(packageName, "package.json"))); /** @type { import('@storybook/react-vite').StorybookConfig } */ diff --git a/apps/smithy/.storybook/preview.jsx b/apps/smithy/.storybook/preview.jsx deleted file mode 100644 index eb229bc0..00000000 --- a/apps/smithy/.storybook/preview.jsx +++ /dev/null @@ -1,89 +0,0 @@ -import "./global.css"; - -import * as React from "react"; -import { useEffect } from "react"; -import { Provider } from "@frigade/react"; - -// LOCAL DEV -// const FRIGADE_API_KEY = -// "api_public_0FaxqVs527bAVQsFK4RcuJYjVqHeC5U7CGJLfsVXRE36eAKiLjwAEugZYeFijCI2"; -// const API_URL = "https://localhost:3443/v1/public"; - -// FRIGADE DEV -const FRIGADE_API_KEY = - "api_public_GY6O5JS99XTL2HAXU0D6OQHYQ7I706P5I9C9I7CEZFNFUFRARD2DVDSMFW3YT3SV"; -const API_URL = "https://api.frigade.com"; - -// const userId = `jonathan_livingston_smeagol`; -const userId = `jonathan_livingston_smeagol_${Math.random() - .toString(36) - .substring(2, 9)}`; - -const preview = { - parameters: { - actions: { argTypesRegex: "^on[A-Z].*" }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - }, - decorators: [ - (Story) => { - const [delayedUserId, setDelayedUserId] = React.useState(undefined); - - useEffect(() => { - setTimeout(() => { - setDelayedUserId(userId); - }, 1000); - }, []); - - return ( - - - - ); - }, - ], -}; - -export default preview; diff --git a/apps/smithy/.storybook/preview.tsx b/apps/smithy/.storybook/preview.tsx index 16d6f4fb..f839e164 100644 --- a/apps/smithy/.storybook/preview.tsx +++ b/apps/smithy/.storybook/preview.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { FrigadeJS, Provider } from "@frigade/react"; +import { FrigadeJS, Provider, useFlow } from "@frigade/react"; import "./global.css"; @@ -35,9 +35,26 @@ const preview = { apiKey={FRIGADE_API_KEY} apiUrl={FRIGADE_API_URL} defaultCollection={true} - // generateGuestId={false} + // generateGuestId={true} userId={FRIGADE_USER_ID} // syncOnWindowUpdates={false} + + // theme={{ + // colors: { + // primary: { + // surface: "pink", + // active: { + // surface: "teal", + // }, + // hover: { + // surface: "orange", + // }, + // }, + // }, + // }} + + // themeSelector="#storybook-root" + userProperties={{ firstName: "Jonathan", lastName: "Livingston", diff --git a/apps/smithy/package.json b/apps/smithy/package.json index dc09e534..10651e20 100644 --- a/apps/smithy/package.json +++ b/apps/smithy/package.json @@ -12,33 +12,33 @@ }, "dependencies": { "@frigade/react": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "react": "^19.0.0", + "react-dom": "^19.0.0" }, "devDependencies": { - "@storybook/addon-docs": "^8.2.9", - "@storybook/addon-essentials": "^8.2.9", - "@storybook/addon-interactions": "^8.2.9", - "@storybook/addon-links": "^8.2.9", - "@storybook/blocks": "^8.2.9", - "@storybook/cli": "^8.2.9", - "@storybook/react": "^8.2.9", - "@storybook/react-vite": "^8.2.9", - "@storybook/test": "^8.2.9", - "@storybook/test-runner": "^0.19.1", - "@types/react": "^18.2.34", - "@types/react-dom": "^18.2.14", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", + "@storybook/addon-docs": "^8.5.3", + "@storybook/addon-essentials": "^8.5.3", + "@storybook/addon-interactions": "^8.5.3", + "@storybook/addon-links": "^8.5.3", + "@storybook/blocks": "^8.5.3", + "@storybook/cli": "^8.5.3", + "@storybook/react": "^8.5.3", + "@storybook/react-vite": "^8.5.3", + "@storybook/test": "^8.5.3", + "@storybook/test-runner": "^0.21.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", + "eslint": "^8.56.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "eslint-plugin-storybook": "^0.8.0", - "playwright": "^1.47.1", + "eslint-plugin-react-refresh": "^0.4.5", + "eslint-plugin-storybook": "^0.11.2", + "playwright": "^1.55.1", "prop-types": "^15.8.1", - "storybook": "^8.2.9", + "storybook": "^8.5.3", "typescript": "^4.9.4", - "vite": "^4.5.5" + "vite": "^4.5.14" } } diff --git a/apps/smithy/src/stories/Announcement/Announcement.stories.tsx b/apps/smithy/src/stories/Announcement/Announcement.stories.tsx index c47b0d06..1e99149e 100644 --- a/apps/smithy/src/stories/Announcement/Announcement.stories.tsx +++ b/apps/smithy/src/stories/Announcement/Announcement.stories.tsx @@ -1,4 +1,5 @@ -import { Announcement, useFlow, useFrigade } from "@frigade/react"; +import { Announcement, Tour, useFlow, useFrigade } from "@frigade/react"; +import { useEffect } from "react"; export default { title: "Components/Announcement", @@ -8,7 +9,7 @@ export default { export const Default = { args: { dismissible: true, - flowId: "flow_8Ybz7lMK", + flowId: "flow_vLivpwoH", modal: true, onDismiss: () => console.log("Dismissed"), }, @@ -24,6 +25,25 @@ export const TestReset = { const { frigade } = useFrigade(); const { flow } = useFlow(args.flowId); + useEffect(() => { + frigade.on("step.start", (event, flow, previousFlow, step) => { + console.log("step.start", event, flow.id, step?.id); + }); + frigade.on("step.complete", (event, flow, previousFlow, step) => { + console.log("step.complete", event, flow.id, step?.id); + }); + + frigade.on("flow.start", (event, flow) => { + console.log("flow.start", event, flow.id); + }); + frigade.on("flow.complete", (event, flow) => { + console.log("flow.complete", event, flow.id); + }); + frigade.on("flow.skip", (event, flow) => { + console.log("flow.skip", event, flow.id); + }); + }, []); + return (
@@ -52,8 +72,10 @@ export const ModalCollisions = { return (
- + + + + +
Tour anchor
); }, diff --git a/apps/smithy/src/stories/Checklist/Checklist.stories.tsx b/apps/smithy/src/stories/Checklist/Checklist.stories.tsx index 913f4dde..cbfe2758 100644 --- a/apps/smithy/src/stories/Checklist/Checklist.stories.tsx +++ b/apps/smithy/src/stories/Checklist/Checklist.stories.tsx @@ -64,7 +64,6 @@ export const Collapsible = { test: TestStep, }, width: "500px", - forceMount: true, dismissible: true, }, }; diff --git a/apps/smithy/src/stories/Checklist/ChecklistInteractionTests.stories.tsx b/apps/smithy/src/stories/Checklist/ChecklistInteractionTests.stories.tsx new file mode 100644 index 00000000..be732623 --- /dev/null +++ b/apps/smithy/src/stories/Checklist/ChecklistInteractionTests.stories.tsx @@ -0,0 +1,74 @@ +import { Checklist, useFlow } from "@frigade/react"; +import { type Meta, type StoryObj } from "@storybook/react"; +import { expect, userEvent, within } from "@storybook/test"; + +type ChecklistStory = StoryObj; + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +const StoryMeta: Meta = { + title: "Components/Checklist", + component: Checklist.Collapsible, +}; + +export default StoryMeta; + +export const InteractionTests: ChecklistStory = { + args: { + flowId: "flow_K2dmIlteW8eGbGoo", + }, + decorators: [ + (Story, { args }) => { + const { flow } = useFlow(args.flowId); + + return ( + <> + + + + ); + }, + ], + + play: async ({ step, canvasElement }) => { + await sleep(1000); + const canvas = within(canvasElement); + const checklistElement = document.querySelector(".fr-checklist"); + + await step("Test interactions", async () => { + // test that checklistElement is in the document + expect(checklistElement).toBeInTheDocument(); + + const lastHeader = canvas.getByText("Donec id diam lectus"); + let checkboxes = document.querySelectorAll(".fr-field-radio-value"); + + // expectd lastCheckbox to not have any children + expect(checkboxes[checkboxes.length - 1]?.childNodes.length).toBe(0); + // click last header + await userEvent.click(lastHeader); + // Click the primary button that says "Testing" + await userEvent.click(canvas.getByText("Testing")); + await sleep(100); + + checkboxes = document.querySelectorAll(".fr-field-radio-value"); + // expect lastCheckbox to have a child (it is checked) + expect( + checkboxes[checkboxes.length - 1]?.childNodes.length + ).toBeGreaterThanOrEqual(1); + await sleep(1000); + expect(checklistElement).toBeInTheDocument(); + + await userEvent.click(canvas.getByText("Reset Flow")); + }); + }, +}; diff --git a/apps/smithy/src/stories/Checklist/Floating.stories.tsx b/apps/smithy/src/stories/Checklist/Floating.stories.tsx new file mode 100644 index 00000000..fc42d3fe --- /dev/null +++ b/apps/smithy/src/stories/Checklist/Floating.stories.tsx @@ -0,0 +1,21 @@ +import { Box, Checklist } from "@frigade/react"; +import { type Meta } from "@storybook/react"; + +export default { + title: "Components/Checklist/Floating", + component: Checklist.Floating, + decorators: [ + (Story) => ( + + + + ), + ], +} as Meta; + +export const Default = { + args: { + //children: , + flowId: "flow_K2dmIlteW8eGbGoo", + }, +}; diff --git a/apps/smithy/src/stories/Collection/Collection.stories.tsx b/apps/smithy/src/stories/Collection/Collection.stories.tsx index 45ef5ff4..11d1248e 100644 --- a/apps/smithy/src/stories/Collection/Collection.stories.tsx +++ b/apps/smithy/src/stories/Collection/Collection.stories.tsx @@ -176,7 +176,7 @@ export const MultipleAnnouncements = { {/* Storybook X & Y Announcements */} - + {/* */} ), ], diff --git a/apps/smithy/src/stories/Collection/CollectionInteractionTests.stories.tsx b/apps/smithy/src/stories/Collection/CollectionInteractionTests.stories.tsx index a8e1eaf0..5d9e2bef 100644 --- a/apps/smithy/src/stories/Collection/CollectionInteractionTests.stories.tsx +++ b/apps/smithy/src/stories/Collection/CollectionInteractionTests.stories.tsx @@ -112,7 +112,7 @@ export const InteractionTests: CollectionStory = { "Test that the default collection can render url-based announcement", async () => { const canvas = within(document.body); - await sleep(300); + await sleep(500); await expect(canvas.queryByRole("dialog")).not.toBeInTheDocument(); await userEvent.click( @@ -151,6 +151,8 @@ export const InteractionTests: CollectionStory = { // expect banner to contain the text "Banner title" await expect(banner.getByText("Banner title")).toBeInTheDocument(); + await sleep(200); + // Click button with Primary CTA await userEvent.click( banner.getByRole("button", { diff --git a/apps/smithy/src/stories/Form/Form.stories.tsx b/apps/smithy/src/stories/Form/Form.stories.tsx index f96df805..ca316668 100644 --- a/apps/smithy/src/stories/Form/Form.stories.tsx +++ b/apps/smithy/src/stories/Form/Form.stories.tsx @@ -5,7 +5,7 @@ import { type FormFieldProps, SelectField, } from "@frigade/react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; export default { title: "Components/Form", @@ -32,7 +32,7 @@ export const Default = { fieldTypes: { customTest: CustomStep, }, - as: Dialog, + // as: Dialog, variables: { testVar: "hello world", }, @@ -84,6 +84,7 @@ export const CustomForm = { console.log("Secondary"); return true; }, + onComplete: (e, properties) => console.log("Complete", e, properties), }, }; @@ -114,11 +115,20 @@ export const VariablesInForms = { const [testVar, setTestVar] = useState("test1"); const [otherVar, setOtherVar] = useState("test2"); - // setTimeout to simulate async data fetching - setTimeout(() => { - setTestVar("updated var 1"); - setOtherVar("updated var 2"); - }, 1000); + useEffect(() => { + // setTimeout to simulate async data fetching + setTimeout(() => { + setTestVar("updated var 1"); + setOtherVar("updated var 2"); + }, 1000); + + // update it again after 2000ms + setTimeout(() => { + setTestVar("updated var 1 again"); + setOtherVar("updated var 2 again"); + }, 2000); + }, []); + ``; return (
diff --git a/apps/smithy/src/stories/Form/FormInteractionTests.stories.tsx b/apps/smithy/src/stories/Form/FormInteractionTests.stories.tsx index 8e0be290..8fe58ed1 100644 --- a/apps/smithy/src/stories/Form/FormInteractionTests.stories.tsx +++ b/apps/smithy/src/stories/Form/FormInteractionTests.stories.tsx @@ -1,6 +1,6 @@ import { Form, useFlow } from "@frigade/react"; import { type Meta, type StoryObj } from "@storybook/react"; -import { expect, userEvent, within } from "@storybook/test"; +import { expect, userEvent, waitFor, within } from "@storybook/test"; type FormStory = StoryObj; @@ -44,19 +44,35 @@ export const InteractionTests: FormStory = { play: async ({ step }) => { await step("Test linear flow of Form", async () => { const canvas = within(document.body); - await sleep(500); + await canvas.findByText("This is page 1"); await userEvent.click(canvas.getByText("Continue to page 2")); await userEvent.click(canvas.getByText("Next")); + await canvas.findByText("This is page 2"); await userEvent.click(canvas.getByText("Next")); + await canvas.findByText("This is page 3"); - await sleep(100); - await expect(canvas.getByText("Finish").closest("button")).toBeDisabled(); + + const finishButton = await canvas.findByRole("button", { + name: "Finish", + }); + + await waitFor(async () => { + await expect(finishButton).toBeDisabled(); + }); + await userEvent.click(canvas.getByText("Radio 1")); - await userEvent.click(canvas.getByText("Finish")); - await sleep(500); - await expect(document.querySelector(".fr-form")).not.toBeInTheDocument(); + await userEvent.click(finishButton); + + await waitFor(async () => { + await expect( + document.querySelector(".fr-form") + ).not.toBeInTheDocument(); + }); + + await sleep(1000); + await userEvent.click(canvas.getByText("Reset Flow")); await sleep(1000); }); @@ -74,6 +90,49 @@ export const InteractionTests: FormStory = { await expect(document.querySelector(".fr-form")).not.toBeInTheDocument(); await userEvent.click(canvas.getByText("Reset Flow")); await sleep(1000); + await userEvent.click(canvas.getByText("Reset Flow")); + await sleep(1000); }); + + // test navigating back and forth 3 times between step 1 and 2 + await step( + "Test navigating back and forth 3 times between step 1 and 2", + async () => { + const canvas = within(document.body); + + for (let i = 0; i < 3; i++) { + await canvas.findByText( + "This is page 1", + {}, + { timeout: 5000, interval: 500 } + ); + await userEvent.click(canvas.getByText("Continue to page 2")); + await userEvent.click(canvas.getByText("Next")); + await canvas.findByText( + "This is page 2", + {}, + { timeout: 5000, interval: 500 } + ); + await userEvent.click(canvas.getByText("Back")); + } + + // now finish the flow + await canvas.findByText("This is page 1"); + await userEvent.click(canvas.getByText("Continue to page 2")); + await userEvent.click(canvas.getByText("Next")); + await canvas.findByText("This is page 2"); + await userEvent.click(canvas.getByText("Next")); + await canvas.findByText("This is page 3"); + await userEvent.click(canvas.getByText("Radio 1")); + await userEvent.click(canvas.getByText("Finish")); + await sleep(500); + await expect( + document.querySelector(".fr-form") + ).not.toBeInTheDocument(); + + await userEvent.click(canvas.getByText("Reset Flow")); + await sleep(1000); + } + ); }, }; diff --git a/apps/smithy/src/stories/Hint/Hint.stories.tsx b/apps/smithy/src/stories/Hint/Hint.stories.tsx index b5aee7ff..9de8a467 100644 --- a/apps/smithy/src/stories/Hint/Hint.stories.tsx +++ b/apps/smithy/src/stories/Hint/Hint.stories.tsx @@ -12,7 +12,7 @@ export const Default = { alignOffset: 0, anchor: "#tooltip-storybook-0", children: Hello, - defaultOpen: false, + defaultOpen: true, side: "left", sideOffset: 0, }, diff --git a/apps/smithy/src/stories/Popover/Popover.stories.tsx b/apps/smithy/src/stories/Popover/Popover.stories.tsx new file mode 100644 index 00000000..055f5f38 --- /dev/null +++ b/apps/smithy/src/stories/Popover/Popover.stories.tsx @@ -0,0 +1,75 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { Box, Button, Card, FloatingUI, Popover } from "@frigade/react"; + +const meta: Meta = { + title: "Design System/Popover", + parameters: { + layout: "centered", + }, +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + Popover.Trigger + + + + Popover.Content + + + + + ), +}; + +export const Nested: Story = { + render: () => ( + + + + + Popover.Trigger + + + + Popover.Content + + + Nested Popover.Trigger + + + + Nested Popover.Content + + + + + + + + + ), +}; + +export const AutoScroll: Story = { + render: () => ( + + + + Popover.Trigger + + + + Popover.Content + + + + + ), +}; diff --git a/apps/smithy/src/stories/Progress/Progress.stories.tsx b/apps/smithy/src/stories/Progress/Progress.stories.tsx index ff75c678..a269ba6f 100644 --- a/apps/smithy/src/stories/Progress/Progress.stories.tsx +++ b/apps/smithy/src/stories/Progress/Progress.stories.tsx @@ -1,4 +1,4 @@ -import { Flex, Progress } from "@frigade/react"; +import { Flex, Progress, Text } from "@frigade/react"; export default { title: "Design System/Progress", @@ -13,9 +13,30 @@ export const Default = { decorators: [ (_, { args }) => ( - - - + + Progress.Bar + + + + Progress.Dots + + + + Progress.Segments + + + + Progress.Ring + + + + + ), ], diff --git a/apps/smithy/src/stories/Survey/Survey.stories.tsx b/apps/smithy/src/stories/Survey/Survey.stories.tsx index 8a6a91b3..6b03ca28 100644 --- a/apps/smithy/src/stories/Survey/Survey.stories.tsx +++ b/apps/smithy/src/stories/Survey/Survey.stories.tsx @@ -8,13 +8,21 @@ export default { export const NPS = { args: { + flowId: "flow_SJjL59eSt9A112vJ", dismissible: true, + options: [ + { label: "😞", value: "0" }, + { label: "😕", value: "1" }, + { label: "😐", value: "2" }, + { label: "🙂", value: "3" }, + { label: "😍", value: "4" }, + ], }, decorators: [ (_: StoryFn, options: StoryContext) => { return ( - + Other elements on the page { @@ -24,7 +32,7 @@ export const NPS = { Change url { - // const [open, setOpen] = useState(true); + const [, setForceRender] = useState(Math.random()); + const [open, setOpen] = useState(true); // const { flow } = useFlow("flow_U63A5pndRrvCwxNs"); const lateAnchorRef = useRef(null); useEffect(() => { - // @ts-expect-error Shush TypeScript, it's a debug ref in a story, it's fine - lateAnchorRef.current = ( - - { - // no-op - }} - /> - - ); + setTimeout(() => { + // @ts-expect-error Shush TypeScript, it's a debug ref in a story, it's fine + lateAnchorRef.current = ( + + { + // no-op + }} + /> + + ); + + setForceRender(Math.random()); + }, 1000); }, []); return ( @@ -77,11 +84,12 @@ export const Default = { Second anchor setOpen(false)} {...(options.args as TourProps)} /> @@ -92,7 +100,7 @@ export const Default = { style={{ background: "lightgreen", width: "200px", - marginTop: "1400px", // Add significant margin to force scrolling + marginTop: "200vh", // Add significant margin to force scrolling }} > Scroll to see this anchor @@ -102,3 +110,120 @@ export const Default = { }, ], }; + +export const AnchorNotFound = { + args: { + autoScroll: true, + flowId: "flow_U63A5pndRrvCwxNs", + defaultOpen: true, + }, + decorators: [ + (_: StoryFn, options: StoryContext) => { + return ( + <> + + + { + // no-op + }} + /> + + + + + ); + }, + ], +}; + +export const WithScrollContainer = { + args: { + autoScroll: true, + container: "#tour-scroll-container", + flowId: "flow_U63A5pndRrvCwxNs", + modal: false, + spotlight: false, + variables: { + firstName: "Euronymous Bosch", + }, + }, + decorators: [ + (_: StoryFn, options: StoryContext) => { + return ( + <> + + + + + { + // no-op + }} + /> + + + + Second anchor + + + + Scroll to see this anchor + + + + + + + ); + }, + ], +}; diff --git a/apps/smithy/src/stories/Tour/TourInteractionTests.stories.tsx b/apps/smithy/src/stories/Tour/TourInteractionTests.stories.tsx index 84335c2c..5503ddfb 100644 --- a/apps/smithy/src/stories/Tour/TourInteractionTests.stories.tsx +++ b/apps/smithy/src/stories/Tour/TourInteractionTests.stories.tsx @@ -16,7 +16,7 @@ const StoryMeta: Meta = { export default StoryMeta; -export const InteractionTests: TourStory = { +export const AnchorFoundInteractionTests: TourStory = { args: { flowId: "flow_U63A5pndRrvCwxNs", }, @@ -24,6 +24,7 @@ export const InteractionTests: TourStory = { decorators: [ (Story, { args }) => { const { flow } = useFlow(args.flowId); + args.flow = flow; const lateAnchorRef = useRef(null); @@ -52,7 +53,7 @@ export const InteractionTests: TourStory = { + + + ); + }, + ], + + play: async ({ step, args }) => { + console.log("args", args); + + await step("Test Anchor Not Found", async () => { + // Wait for flow to be defined + await waitFor(() => { + expect(args.flow).toBeDefined(); + }); + + // Verify the flow is started + expect(args.flow?.isStarted).toBe(false); + }); + }, +}; diff --git a/apps/smithy/src/stories/hooks/useAutoScroll.stories.tsx b/apps/smithy/src/stories/hooks/useAutoScroll.stories.tsx new file mode 100644 index 00000000..3d3b55c4 --- /dev/null +++ b/apps/smithy/src/stories/hooks/useAutoScroll.stories.tsx @@ -0,0 +1,26 @@ +import { useState } from "react"; +import { Box, Button, Flex, useAutoScroll } from "@frigade/react"; + +export default { + title: "Hooks/useAutoScroll", +}; + +export const Default = { + args: { + scrollOptions: true, + }, + + decorators: [ + () => { + const [scrollRef, setScrollRef] = useState(); + + useAutoScroll(scrollRef); + + return ( + + Scroll to this element + + ); + }, + ], +}; diff --git a/apps/smithy/src/stories/hooks/useModal.stories.tsx b/apps/smithy/src/stories/hooks/useModal.stories.tsx index c7697bee..c825bcab 100644 --- a/apps/smithy/src/stories/hooks/useModal.stories.tsx +++ b/apps/smithy/src/stories/hooks/useModal.stories.tsx @@ -8,8 +8,8 @@ export function Default() { return (
Anchor
- + {/* */}
); diff --git a/apps/smithy/vite.config.ts b/apps/smithy/vite.config.ts index 4bc92dd1..d3d4a811 100644 --- a/apps/smithy/vite.config.ts +++ b/apps/smithy/vite.config.ts @@ -9,6 +9,6 @@ export default defineConfig({ alias: { "@": path.resolve(__dirname, "../../packages/react/src"), }, - conditions: ["storybook"], + conditions: ["smithy"], }, }); diff --git a/package.json b/package.json index bfde3df1..4444783d 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "devDependencies": { "@changesets/cli": "^2.22.0", "eslint": "^7.32.0", + "eslint-plugin-react-hooks": "^5.1.0", + "husky": "^9.1.7", "prettier": "^2.5.1", "rimraf": "^5.0.0", "turbo": "^1.10.14" diff --git a/packages/js-api/CHANGELOG.md b/packages/js-api/CHANGELOG.md index b0ccb97f..f853612d 100644 --- a/packages/js-api/CHANGELOG.md +++ b/packages/js-api/CHANGELOG.md @@ -1,5 +1,108 @@ # @frigade/js +## 0.9.5 + +### Patch Changes + +- 8496a7f: Fixes an issue where navigation change events were not firing in Firefox + +## 0.9.4 + +### Patch Changes + +- e1688af: - Fixes a bug where `frigade.on('step.compelte')` could sometimes be called for already completed steps in flows. + - Fixes an issue with using Storybook with Frigade components + +## 0.9.3 + +### Patch Changes + +- b3d3cc6: Fixes an edge case where in some cases, the error `Frigade has not yet been initialized` can occur. + +## 0.9.2 + +### Patch Changes + +- 78b0672: Fixes an issue where collections in some cases would not respect cool offs + +## 0.9.1 + +### Patch Changes + +- 2113e9d: Fixes a typescript issue with a required field for `__refreshIntervalInMS` + +## 0.9.0 + +### Minor Changes + +- 32e8384: Performance improvement that updates the behavior of `syncOnWindowUpdates` to debounce identical calls more aggressively + +## 0.8.1 + +### Patch Changes + +- 3cd8d0b: Adds version number to the `Flow` model + +## 0.8.0 + +### Minor Changes + +- 9bdfcb3: Adds a new and improved API for handling state changes which allows you to filter events based on flow and step completion for the user. The API is available through `frigade.on(...)` + +## 0.7.24 + +### Patch Changes + +- 2860866: Bug fixes for onDismiss, onOpenChange, and URL-based Collection targeting + +## 0.7.23 + +### Patch Changes + +- 0a6c80d: Fixes an issue where `generateGuestId` if used with the `useFrigade()` could cause components not to load + +## 0.7.22 + +### Patch Changes + +- e8de2d9: Adds a `try/catch` to localStorage getter/setter + +## 0.7.21 + +### Patch Changes + +- 7d96719: Fixes a minor bug where multiple flow back and forth navigations can result in a 2 page form to finish prematurely + +## 0.7.20 + +### Patch Changes + +- 51b6444: Fixes an issue where variables are sometimes not applied when using useFlow() with the same Flow ID in a component + +## 0.7.19 + +### Patch Changes + +- 2982a19: Fixes an issue where variables are sometimes not applied when using useFlow() with the same Flow ID in a component + +## 0.7.18 + +### Patch Changes + +- 177f6bb: Fixes an edge cases where variables used in form fields do not update in some cases + +## 0.7.17 + +### Patch Changes + +- 6c8748e: Contains a series of minor bug fixes related to tours and variables + +## 0.7.16 + +### Patch Changes + +- 96a7862: Fixes an issue where checklists complete when finishing the last step + ## 0.7.15 ### Patch Changes diff --git a/packages/js-api/package.json b/packages/js-api/package.json index e01c667e..2ee00d3f 100644 --- a/packages/js-api/package.json +++ b/packages/js-api/package.json @@ -1,6 +1,6 @@ { "name": "@frigade/js", - "version": "0.7.15", + "version": "0.9.5", "description": "The official Javascript SDK for Frigade.", "type": "module", "main": "./dist/index.js", diff --git a/packages/js-api/prepublish.mjs b/packages/js-api/prepublish.mjs index d01892ba..b0f21401 100644 --- a/packages/js-api/prepublish.mjs +++ b/packages/js-api/prepublish.mjs @@ -1,16 +1,17 @@ -import {writeFileSync} from 'fs'; -import {fileURLToPath} from 'url'; -import {dirname, resolve} from 'path'; -import packageJson from './package.json' assert {type: 'json'}; +import { writeFileSync, readFileSync } from 'fs' +import { fileURLToPath } from 'url' +import { dirname, resolve } from 'path' + +const packageJson = JSON.parse(readFileSync('./package.json', 'utf-8')) // Mimic __dirname in ES modules -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) // Define the path to the version file -const versionFilePath = resolve(__dirname, 'src/version.ts'); +const versionFilePath = resolve(__dirname, 'src/version.ts') // Write the version to the file -writeFileSync(versionFilePath, `export const SDK_VERSION = '${packageJson.version}';`, 'utf8'); +writeFileSync(versionFilePath, `export const SDK_VERSION = '${packageJson.version}';`, 'utf8') console.log(`Successfully wrote version number`) diff --git a/packages/js-api/src/core/collections.ts b/packages/js-api/src/core/collections.ts index 55ee5868..cd77e0e7 100644 --- a/packages/js-api/src/core/collections.ts +++ b/packages/js-api/src/core/collections.ts @@ -90,15 +90,15 @@ export class Collections { for (const [collectionId, collection] of previousCollections) { const newCollection = newCollections.get(collectionId) - if (collection.flows.length !== newCollection.flows.length) { + if (collection?.flows?.length !== newCollection?.flows?.length) { this.hasChanges = true break } for (let i = 0; i < collection.flows.length; i++) { if ( - collection.flows[i].flowId !== newCollection.flows[i].flowId || - collection.flows[i].visible !== newCollection.flows[i].visible + collection?.flows[i]?.flowId !== newCollection?.flows[i]?.flowId || + collection?.flows[i]?.visible !== newCollection?.flows[i]?.visible ) { this.hasChanges = true break diff --git a/packages/js-api/src/core/flow.ts b/packages/js-api/src/core/flow.ts index a6bb5c03..66df41a2 100644 --- a/packages/js-api/src/core/flow.ts +++ b/packages/js-api/src/core/flow.ts @@ -30,6 +30,10 @@ export class Flow extends Fetchable { * The Flow's ID. */ public id: string + /** + * The version of the Flow. + */ + public version: number /** * The Flow's component ID. */ @@ -125,7 +129,7 @@ export class Flow extends Fetchable { this.subtitle = statefulFlow?.data?.subtitle this.type = statefulFlow?.data?.type this.props = statefulFlow?.data?.props ?? {} - + this.version = statefulFlow?.version this.isCompleted = statefulFlow.$state.completed this.isStarted = statefulFlow.$state.started this.isSkipped = statefulFlow.$state.skipped @@ -143,7 +147,9 @@ export class Flow extends Fetchable { }) if (this.getGlobalState().variables[this.id]) { - this.applyVariables(this.getGlobalState().variables[this.id] ?? {}) + this.applyVariablesInternal(this.getGlobalState().variables[this.id] ?? {}) + } else { + this.applyVariablesInternal({}) } } @@ -167,9 +173,6 @@ export class Flow extends Fetchable { */ private init() { const statefulFlow = this.getStatefulFlow() - - this.resyncState() - const newSteps = new Map() statefulFlow.data.steps.forEach((step, index) => { @@ -230,6 +233,11 @@ export class Flow extends Fetchable { } const isLastStep = this.getCurrentStepOrder() + 1 === this.getNumberOfAvailableSteps() + const isLastIncompleteStep = + Array.from(this.steps.values()).filter( + (step) => step.$state.visible && !step.$state.completed && !step.$state.skipped + ).length === 1 && isLastStep + if (optimistic) { const copy = clone(this.getGlobalState().flowStates[this.id]) @@ -249,7 +257,8 @@ export class Flow extends Fetchable { copy.$state.currentStepIndex = nextStepIndex copy.data.steps[nextStepIndex].$state.started = true } - } else { + } + if (isLastIncompleteStep) { copy.$state.completed = true copy.$state.visible = false } @@ -257,7 +266,7 @@ export class Flow extends Fetchable { this.getGlobalState().flowStates[this.id] = copy this.resyncState() - if (isLastStep) { + if (isLastIncompleteStep) { this.optimisticallyMarkFlowCompleted() } } @@ -267,7 +276,7 @@ export class Flow extends Fetchable { properties, thisStep.id ) - if (isLastStep) { + if (isLastIncompleteStep) { await this.sendFlowStateToAPI(COMPLETED_FLOW, properties) } } @@ -333,14 +342,8 @@ export class Flow extends Fetchable { newSteps.set(step.id, stepObj as FlowStep) }) this.steps = newSteps - // Check if empty object - if ( - this.getGlobalState().variables && - this.getGlobalState().variables[this.id] && - Object.keys(this.getGlobalState().variables[this.id]).length > 0 - ) { - this.applyVariables(this.getGlobalState().variables[this.id]) - } + + this.resyncState() } /** @@ -499,6 +502,7 @@ export class Flow extends Fetchable { /** * @ignore + * @deprecated Use `frigade.on('flow.complete' | 'flow.skip' | 'flow.restart' | 'flow.start', handler)` instead. */ public onStateChange(handler: (flow: Flow, previousFlow: Flow) => void) { const wrapperHandler = (flow: Flow, previousFlow: Flow) => { @@ -534,17 +538,13 @@ export class Flow extends Fetchable { * @param variables A record of variables to apply to the flow. */ public applyVariables(variables: Record) { - // Check if variables have changed - - if (this.getGlobalState().variables[this.id]) { - // If they have changed, reset the flow's content so variables can be replaced again. - if (JSON.stringify(this.getGlobalState().variables[this.id]) !== JSON.stringify(variables)) { - this.getGlobalState().variables[this.id] = variables - this.reload() - return - } - } + this.applyVariablesInternal(variables, true) + } + /** + * @ignore + */ + private applyVariablesInternal(variables: Record, resyncState: boolean = false) { // Replace ${variable} with the value of the variable const replaceVariables = (str: string) => { const matches = str.match(/\${(.*?)}/g) @@ -586,6 +586,7 @@ export class Flow extends Fetchable { if (this.steps) { this.steps.forEach((step) => { try { + console applyVariablesToStep(step) } catch (e) { // ignore any failures @@ -593,7 +594,14 @@ export class Flow extends Fetchable { }) } - this.getGlobalState().variables[this.id] = variables + this.getGlobalState().variables[this.id] = { + ...this.getGlobalState().variables[this.id], + ...variables, + } + + if (resyncState) { + this.resyncState() + } } /** @@ -613,6 +621,8 @@ export class Flow extends Fetchable { copy.$state.started = true copy.$state.visible = false this.getGlobalState().flowStates[this.id] = copy + // Temporarily disable collections until server round trip has finished + this.getGlobalState().collections.ingestCollectionsData(new Map()) this.resyncState() } @@ -623,6 +633,8 @@ export class Flow extends Fetchable { const copy = clone(this.getGlobalState().flowStates[this.id]) copy.$state.started = true this.getGlobalState().flowStates[this.id] = copy + // Temporarily disable collections until server round trip has finished + this.getGlobalState().collections.ingestCollectionsData(new Map()) this.resyncState() } @@ -668,6 +680,8 @@ export class Flow extends Fetchable { copy.$state.skipped = true copy.$state.visible = false this.getGlobalState().flowStates[this.id] = copy + // Temporarily disable collections until server round trip has finished + this.getGlobalState().collections.ingestCollectionsData(new Map()) this.resyncState() } diff --git a/packages/js-api/src/core/frigade.ts b/packages/js-api/src/core/frigade.ts index 915b4f6c..c3dd8a9e 100644 --- a/packages/js-api/src/core/frigade.ts +++ b/packages/js-api/src/core/frigade.ts @@ -1,6 +1,10 @@ import { + DEFAULT_REFRESH_INTERVAL_IN_MS, + FlowChangeEvent, + FlowChangeEventHandler, FlowStateDTO, FlowStates, + FlowStep, FrigadeConfig, PropertyPayload, SessionDTO, @@ -38,47 +42,101 @@ export class Frigade extends Fetchable { * @ignore */ private hasFailed = false + /** + * @ignore + */ + private lastSessionDTO?: SessionDTO + /** + * @ignore + */ + private eventHandlers: Map = new Map() + /** + * @ignore + */ + private refreshTimeout: ReturnType | null = null + /** + * @ignore + */ + private queuedRefresh: boolean = false /** * @ignore */ - private visibilityChangeHandler = async () => { + private visibilityChangeHandler = () => { if (document.visibilityState === 'visible') { - await this.refreshStateFromAPI() + this.queueRefreshStateFromAPI() } } + /** + * @ignore + */ + private queueRefreshStateFromAPI() { + this.queuedRefresh = true + if (this.refreshTimeout) { + return + } + + this.refreshTimeout = setTimeout(async () => { + if (this.queuedRefresh) { + this.getGlobalState().currentUrl = window.location.href + await this.refreshStateFromAPI() + this.queuedRefresh = false + } + this.refreshTimeout = null + }, this.getGlobalState().config.__refreshIntervalInMS ?? DEFAULT_REFRESH_INTERVAL_IN_MS) + } + constructor(apiKey: string, config?: FrigadeConfig) { super({ apiKey, ...config, }) - this.init(this.config) - if (isWeb() && this.config.syncOnWindowUpdates !== false) { - document.addEventListener('visibilitychange', this.visibilityChangeHandler) - // @ts-ignore - if (window.navigation) { + this.init(this.config).then(() => { + if (isWeb() && this.config.syncOnWindowUpdates !== false) { + document.addEventListener('visibilitychange', this.visibilityChangeHandler) // @ts-ignore - window.navigation.addEventListener('navigate', async (event) => { - try { - if (this.getGlobalState().currentUrl === event.destination.url) { - return - } - // if the new base url is the same but with a hashtag, don't refresh the state as the page has not changed. - if ( - event.destination.url && - this.getGlobalState().currentUrl && - event.destination.url.split('#')[0] === this.getGlobalState().currentUrl.split('#')[0] - ) { - return - } - - this.getGlobalState().currentUrl = event.destination.url - this.refreshStateFromAPI(true) - } catch (e) {} - }) + if (window.navigation) { + // @ts-ignore + window.navigation.addEventListener('navigate', async (event) => { + try { + if (this.getGlobalState().currentUrl === event.destination.url) { + return + } + // if the new base url is the same but with a hashtag, don't refresh the state as the page has not changed. + if ( + event.destination.url && + this.getGlobalState().currentUrl && + event.destination.url.split('#')[0] === + this.getGlobalState().currentUrl.split('#')[0] + ) { + return + } + this.queueRefreshStateFromAPI() + } catch (e) {} + }) + } else { + // For browsers like Safari and Firefox, window.navigation is not supported. + // Instead we use a mutation observer to detect changes to the URL. + new MutationObserver(() => { + try { + if (this.getGlobalState().currentUrl === location.href) { + return + } + // if the new base url is the same but with a hashtag, don't refresh the state as the page has not changed. + if ( + location.href && + this.getGlobalState().currentUrl && + location.href.split('#')[0] === this.getGlobalState().currentUrl.split('#')[0] + ) { + return + } + this.queueRefreshStateFromAPI() + } catch (e) {} + }).observe(document, { subtree: true, childList: true }) + } } - } + }) } /** @@ -112,7 +170,7 @@ export class Frigade extends Fetchable { ...config, }) - if (!this.config.userId && this.config.generateGuestId === false) { + if (!this.config.userId) { return } @@ -146,13 +204,19 @@ export class Frigade extends Fetchable { * @param properties */ public async identify(userId: string, properties?: PropertyPayload): Promise { - await this.updateConfig({ ...this.config, userId }) + if (userId !== this.config.userId) { + await this.updateConfig({ ...this.config, userId }) + await this.reload() + } + await this.initIfNeeded() - await this.session({ + const isNewSession = await this.session({ userId: this.config.userId, userProperties: properties, }) - await this.resync() + if (isNewSession) { + await this.resync() + } } /** @@ -217,17 +281,32 @@ export class Frigade extends Fetchable { * @ignore */ private async session(sessionDTO: SessionDTO) { + const lastSessionDTO = this.lastSessionDTO + + if (lastSessionDTO && JSON.stringify(lastSessionDTO) === JSON.stringify(sessionDTO)) { + return false + } + await this.fetch('/v1/public/sessions', { method: 'POST', body: JSON.stringify(sessionDTO), }) + + this.lastSessionDTO = sessionDTO + + return true } /** * @ignore */ public isReady(): boolean { - return Boolean(this.config.__instanceId && this.config.apiKey && this.initPromise) + return Boolean( + this.config.__instanceId && + this.config.apiKey && + this.initPromise && + frigadeGlobalState[getGlobalStateKey(this.config)] + ) } /** @@ -235,6 +314,13 @@ export class Frigade extends Fetchable { * @param flowId */ public async getFlow(flowId: string) { + if ( + this.getConfig().generateGuestId === false && + this.getConfig().userId && + this.getConfig().userId.startsWith(GUEST_PREFIX) + ) { + return undefined + } await this.initIfNeeded() return this.getFlowSync(flowId) @@ -249,6 +335,12 @@ export class Frigade extends Fetchable { public async getFlows() { await this.initIfNeeded() + if ( + this.config.generateGuestId === false && + this.getConfig().userId?.startsWith(GUEST_PREFIX) + ) { + return [] + } return this.flows } @@ -333,10 +425,14 @@ export class Frigade extends Fetchable { } this.initPromise = null await this.init(this.config) + this.triggerAllLegacyEventHandlers() this.triggerAllEventHandlers() } - private triggerAllEventHandlers() { + /** + * @ignore + */ + private triggerAllLegacyEventHandlers() { this.flows.forEach((flow) => { this.getGlobalState().onFlowStateChangeHandlers.forEach((handler) => { const lastFlow = this.getGlobalState().previousFlows.get(flow.id) @@ -346,12 +442,20 @@ export class Frigade extends Fetchable { }) } + private triggerAllEventHandlers() { + this.flows.forEach((flow) => { + const lastFlow = this.getGlobalState().previousFlows.get(flow.id) + this.triggerEventHandlers(flow.rawData, lastFlow?.rawData) + }) + } + private async resync() { await this.refreshStateFromAPI() } /** * Event handler that captures all changes that happen to the state of the Flows. + * @deprecated Use `frigade.on` instead. * @param handler */ public async onStateChange(handler: (flow: Flow, previousFlow?: Flow) => void) { @@ -359,6 +463,27 @@ export class Frigade extends Fetchable { this.getGlobalState().onFlowStateChangeHandlers.push(handler) } + /** + * Event handler that captures all changes that happen to the state of the Flows. Use `flow.any` to capture all events. + * @param event `flow.any` | `flow.complete` | `flow.restart` | `flow.skip` | `flow.start` | `step.complete` | `step.skip` | `step.reset` | `step.start` + * @param handler + */ + public async on(event: FlowChangeEvent, handler: FlowChangeEventHandler) { + this.eventHandlers.set(event, [...(this.eventHandlers.get(event) ?? []), handler]) + } + + /** + * Removes the given handler. + * @param event `flow.any` | `flow.complete` | `flow.restart` | `flow.skip` | `flow.start` | `step.complete` | `step.skip` | `step.reset` | `step.start` + * @param handler + */ + public async off(event: FlowChangeEvent, handler: FlowChangeEventHandler) { + this.eventHandlers.set( + event, + (this.eventHandlers.get(event) ?? []).filter((h) => h !== handler) + ) + } + /** * Returns true if the JS SDK failed to connect to the Frigade API. */ @@ -390,7 +515,7 @@ export class Frigade extends Fetchable { /** * @ignore */ - private async refreshStateFromAPI(cancelPendingRequests: boolean = false): Promise { + private async refreshStateFromAPI(): Promise { const globalStateKey = getGlobalStateKey(this.config) if (!frigadeGlobalState[globalStateKey]) { @@ -402,6 +527,7 @@ export class Frigade extends Fetchable { const previousState = target[key] as StatefulFlow const newState = value as StatefulFlow if (JSON.stringify(previousState) !== JSON.stringify(newState)) { + that.triggerDeprecatedEventHandlers(newState, previousState) that.triggerEventHandlers(newState, previousState) } } @@ -434,8 +560,7 @@ export class Frigade extends Fetchable { } frigadeGlobalState[globalStateKey].refreshStateFromAPI = async ( - overrideFlowStateRaw?: FlowStates, - cancelPendingRequests?: boolean + overrideFlowStateRaw?: FlowStates ) => { if (this.config.__readOnly) { return @@ -443,18 +568,14 @@ export class Frigade extends Fetchable { const flowStateRaw: FlowStates = overrideFlowStateRaw ? overrideFlowStateRaw - : await this.fetch( - '/v1/public/flowStates', - { - method: 'POST', - body: JSON.stringify({ - userId: this.getGlobalState().config.userId, - groupId: this.getGlobalState().config.groupId, - context: getContext(this.getGlobalState()), - } as FlowStateDTO), - }, - cancelPendingRequests - ) + : await this.fetch('/v1/public/flowStates', { + method: 'POST', + body: JSON.stringify({ + userId: this.getGlobalState().config.userId, + groupId: this.getGlobalState().config.groupId, + context: getContext(this.getGlobalState()), + } as FlowStateDTO), + }) const collectionsData: CollectionsList = new Map() @@ -482,6 +603,7 @@ export class Frigade extends Fetchable { // Necessary check to prevent race condition between flow state and collection state !overrideFlowStateRaw ) { + this.triggerAllLegacyEventHandlers() this.triggerAllEventHandlers() } } @@ -511,7 +633,7 @@ export class Frigade extends Fetchable { } } - await frigadeGlobalState[globalStateKey].refreshStateFromAPI(undefined, cancelPendingRequests) + await frigadeGlobalState[globalStateKey].refreshStateFromAPI(undefined) } /** @@ -555,7 +677,10 @@ export class Frigade extends Fetchable { /** * @ignore */ - private async triggerEventHandlers(newState: StatefulFlow, previousState?: StatefulFlow) { + private async triggerDeprecatedEventHandlers( + newState: StatefulFlow, + previousState?: StatefulFlow + ) { if (newState) { this.flows.forEach((flow) => { if (flow.id == previousState.flowSlug) { @@ -570,6 +695,116 @@ export class Frigade extends Fetchable { } } + /** + * @ignore + */ + private triggerEventHandlers(newState: StatefulFlow, previousState?: StatefulFlow) { + if (newState) { + for (const flow of this.flows) { + if (flow.id == newState.flowSlug) { + const lastFlow = this.getGlobalState().previousFlows.get(flow.id) + flow.resyncState(newState) + for (const [event, handlers] of this.eventHandlers.entries()) { + switch (event) { + case 'flow.complete': + if (newState.$state.completed && previousState?.$state.completed === false) { + handlers.forEach((handler) => handler(event, flow, lastFlow)) + } + break + case 'flow.restart': + if (!newState.$state.started && previousState?.$state.started === true) { + handlers.forEach((handler) => handler(event, flow, lastFlow)) + } + break + case 'flow.skip': + if (newState.$state.skipped && previousState?.$state.skipped === false) { + handlers.forEach((handler) => handler(event, flow, lastFlow)) + } + break + case 'flow.start': + if (newState.$state.started && previousState?.$state.started === false) { + handlers.forEach((handler) => handler(event, flow, lastFlow)) + } + break + case 'step.complete': + for (const step of newState.data.steps ?? []) { + const previousStep = previousState?.data.steps.find( + (previousStepState) => previousStepState.id === step.id + ) + if ( + step.$state.completed && + previousStep && + previousStep.$state.completed !== step.$state.completed + ) { + handlers.forEach((handler) => + handler(event, flow, lastFlow, flow.steps.get(step.id)) + ) + } + } + break + case 'step.reset': + for (const step of newState.data.steps ?? []) { + const previousStep = previousState?.data.steps.find( + (previousStepState) => previousStepState.id === step.id + ) + if ( + step.$state.started == false && + !step.$state.lastActionAt && + previousStep && + previousStep?.$state.started && + previousStep?.$state.lastActionAt + ) { + handlers.forEach((handler) => + handler(event, flow, lastFlow, flow.steps.get(step.id)) + ) + } + } + break + case 'step.skip': + for (const step of newState.data.steps ?? []) { + const previousStep = previousState?.data.steps.find( + (previousStepState) => previousStepState.id === step.id + ) + if ( + step.$state.skipped && + previousStep && + previousStep.$state.skipped !== step.$state.skipped + ) { + handlers.forEach((handler) => + handler(event, flow, lastFlow, flow.steps.get(step.id)) + ) + } + } + break + case 'step.start': + for (const step of newState.data.steps ?? []) { + const previousStep = previousState?.data.steps.find( + (previousStepState) => previousStepState.id === step.id + ) + if ( + step.$state.started && + previousStep && + previousStep.$state.started === false + ) { + handlers.forEach((handler) => + handler(event, flow, lastFlow, flow.steps.get(step.id)) + ) + } + } + break + case 'flow.any': + if (JSON.stringify(newState) !== JSON.stringify(previousState ?? {})) { + handlers.forEach((handler) => handler(event, flow, lastFlow)) + } + break + } + } + this.getGlobalState().previousFlows.set(flow.id, cloneFlow(flow)) + } + } + } + } + /** * @ignore */ diff --git a/packages/js-api/src/core/types.ts b/packages/js-api/src/core/types.ts index 74310237..96fb48cc 100644 --- a/packages/js-api/src/core/types.ts +++ b/packages/js-api/src/core/types.ts @@ -1,5 +1,7 @@ import type { Flow } from './flow' +export const DEFAULT_REFRESH_INTERVAL_IN_MS = 100 + export enum TriggerType { MANUAL = 'MANUAL', AUTOMATIC = 'AUTOMATIC', @@ -24,6 +26,36 @@ export type StepAction = | 'step.start' | false +export type FlowChangeEvent = + | 'flow.any' + | 'flow.complete' + | 'flow.restart' + | 'flow.skip' + | 'flow.start' + | 'step.complete' + | 'step.skip' + | 'step.reset' + | 'step.start' + +export type FlowChangeEventHandler = ( + /** + * The event that triggered the handler. + */ + event: FlowChangeEvent, + /** + * The Flow that triggered the event. + */ + flow: Flow, + /** + * The previous Flow that triggered the event. + */ + previousFlow?: Flow, + /** + * The step that triggered the event. Only applicable for `step.complete`, `step.skip`, `step.reset`, `step.start` events. + */ + step?: FlowStep +) => void + export type PropertyPayload = Record export interface FlowStep extends StatefulStep { @@ -201,7 +233,7 @@ export interface FlowStep extends StatefulStep { [x: string | number | symbol]: unknown /** - * Marks the step started. + * Marks the step started. This will move the current step index to the given step. */ start: (properties?: PropertyPayload) => Promise @@ -223,6 +255,7 @@ export interface FlowStep extends StatefulStep { /** * Event handler called when this step's state changes. + * @deprecated Use `frigade.on('step.complete' | 'step.skip' | 'step.reset' | 'step.start', handler)` instead. */ onStateChange: (callback: (step: FlowStep, previousStep?: FlowStep) => void) => void @@ -306,6 +339,11 @@ export interface FrigadeConfig { * @ignore Internal use only. */ __platformName?: string + + /** + * @ignore Internal use only. + */ + __refreshIntervalInMS?: number } export interface StatefulStep { @@ -340,6 +378,7 @@ export interface StatefulFlow { flowSlug: string flowName: string flowType: FlowType + version: number /** * Contains the raw data of the Flow as defined in the YAML config as well as the user's state of each step. */ diff --git a/packages/js-api/src/index.ts b/packages/js-api/src/index.ts index 5ff7b946..a246a157 100644 --- a/packages/js-api/src/index.ts +++ b/packages/js-api/src/index.ts @@ -8,6 +8,8 @@ export { type StatefulFlow, TriggerType, PropertyPayload, + FlowChangeEvent, + FlowChangeEventHandler, } from './core/types' export type { EnrichedCollection, diff --git a/packages/js-api/src/shared/fetchable.ts b/packages/js-api/src/shared/fetchable.ts index bbc76e3e..8d79f2ba 100644 --- a/packages/js-api/src/shared/fetchable.ts +++ b/packages/js-api/src/shared/fetchable.ts @@ -1,5 +1,5 @@ import { generateGuestId, getEmptyResponse, getHeaders, gracefulFetch } from './utils' -import { FrigadeConfig } from '../core/types' +import { DEFAULT_REFRESH_INTERVAL_IN_MS, FrigadeConfig } from '../core/types' import { frigadeGlobalState, FrigadeGlobalState, getGlobalStateKey } from './state' export class Fetchable { @@ -9,13 +9,11 @@ export class Fetchable { userId: generateGuestId(), __instanceId: Math.random().toString(12).substring(4), generateGuestId: true, + __refreshIntervalInMS: DEFAULT_REFRESH_INTERVAL_IN_MS, } constructor(config: FrigadeConfig) { const filteredConfig = Object.fromEntries(Object.entries(config).filter(([_, v]) => v != null)) - if (!config.userId && config.generateGuestId === false) { - delete this.config.userId - } this.config = { ...this.config, ...filteredConfig, @@ -25,24 +23,16 @@ export class Fetchable { /** * @ignore */ - public async fetch( - path: string, - options?: Record, - cancelPendingRequests: boolean = false - ) { + public async fetch(path: string, options?: Record) { if (this.config.__readOnly) { return getEmptyResponse() } - return gracefulFetch( - this.getAPIUrl(path), - { - keepalive: true, - ...(options ?? {}), - ...getHeaders(this.config), - }, - cancelPendingRequests - ) + return gracefulFetch(this.getAPIUrl(path), { + keepalive: true, + ...(options ?? {}), + ...getHeaders(this.config), + }) } private getAPIUrl(path: string) { diff --git a/packages/js-api/src/shared/state.ts b/packages/js-api/src/shared/state.ts index 86f0409f..c224def1 100644 --- a/packages/js-api/src/shared/state.ts +++ b/packages/js-api/src/shared/state.ts @@ -1,4 +1,4 @@ -import { FlowStates, FlowStep, FrigadeConfig, StatefulFlow } from '../core/types' +import { FlowStates, FlowStep, FrigadeConfig, SessionDTO, StatefulFlow } from '../core/types' import { Flow } from '../core/flow' import type { Collections } from '../core/collections' diff --git a/packages/js-api/src/shared/utils.ts b/packages/js-api/src/shared/utils.ts index ee8a43af..f2d90b98 100644 --- a/packages/js-api/src/shared/utils.ts +++ b/packages/js-api/src/shared/utils.ts @@ -43,14 +43,23 @@ export function getHeaders(config: FrigadeConfig) { function getLocalStorage(key: string) { if (isWeb()) { - return window.localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${key}`) + try { + return window.localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${key}`) + } catch (e) { + console.debug('Error getting localStorage:', e) + return null + } } return null } function setLocalStorage(key: string, value: string) { if (isWeb()) { - window.localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${key}`, value) + try { + window.localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${key}`, value) + } catch (e) { + console.debug('Error setting localStorage:', e) + } } } @@ -109,13 +118,9 @@ class CallQueue { } } -const callQueue = new CallQueue() +globalThis.callQueue = new CallQueue() -export async function gracefulFetch( - url: string, - options: any, - cancelPendingRequests: boolean = false -) { +export async function gracefulFetch(url: string, options: any) { if (typeof globalThis.fetch !== 'function') { return getEmptyResponse( "- Attempted to call fetch() in an environment that doesn't support it." @@ -127,8 +132,8 @@ export async function gracefulFetch( const isWebPostRequest = isWeb() && options && options.body && options.method === 'POST' - if (isWebPostRequest && !cancelPendingRequests) { - const cachedCall = callQueue.hasIdenticalRecentCall(lastCallDataKey) + if (isWebPostRequest) { + const cachedCall = globalThis.callQueue.hasIdenticalRecentCall(lastCallDataKey) if (cachedCall != null && cachedCall.response != null) { const cachedResponse = await cachedCall.response @@ -139,14 +144,10 @@ export async function gracefulFetch( if (!response) { try { - if (cancelPendingRequests) { - callQueue.cancelAllPendingRequests() - } - const pendingResponse = fetch(url, options) if (isWebPostRequest) { - callQueue.push( + globalThis.callQueue.push( lastCallDataKey, // @ts-ignore pendingResponse.then((res) => res.clone()).catch(() => getEmptyResponse()) @@ -154,7 +155,7 @@ export async function gracefulFetch( } response = await pendingResponse - if (isWebPostRequest && !callQueue.hasCall(lastCallDataKey)) { + if (isWebPostRequest && !globalThis.callQueue.hasCall(lastCallDataKey)) { response = getEmptyResponse(REDUNDANT_CALL_MESSAGE) } } catch (error) { diff --git a/packages/js-api/src/version.ts b/packages/js-api/src/version.ts index b04a322c..4e1ec270 100644 --- a/packages/js-api/src/version.ts +++ b/packages/js-api/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '0.7.15'; \ No newline at end of file +export const SDK_VERSION = '0.9.5'; \ No newline at end of file diff --git a/packages/js-api/test/frigade.int.test.ts b/packages/js-api/test/frigade.int.test.ts index 1147a515..ea1985da 100644 --- a/packages/js-api/test/frigade.int.test.ts +++ b/packages/js-api/test/frigade.int.test.ts @@ -1,13 +1,12 @@ -import { FlowStep, Frigade } from '../src' +import { FlowStep, FlowType, Frigade } from '../src' import { getRandomID } from './util' import { Flow } from '../src/.' -import { FlowType } from '@frigade/reactv1' - const testAPIKey = 'api_public_3MPLH7NJ9L0U963XKW7BPE2IT137GC6L742JLC2XCT6NOIYSI4QUI9I1RA3ZOGIL' const testFlowId = 'flow_yJfjksFrs5QEH0c8' const testFlowIdWithTargeting = 'flow_61YBPQek' const testFlowStepId = 'checklist-step-one' +const testFlowStepId2 = 'checklist-step-two' jest.retryTimes(2, { logErrorsBeforeRetry: false }) describe('Basic Checklist integration test', () => { @@ -24,7 +23,7 @@ describe('Basic Checklist integration test', () => { }) let flows = await frigade.getFlows() expect(flows.length).toEqual(0) - expect(frigade.isReady()).toBeFalsy() + expect(frigade.isReady()).toBeTruthy() await frigade.identify(getRandomID()) flows = await frigade.getFlows() expect(flows.length).toBeGreaterThan(0) @@ -43,6 +42,7 @@ describe('Basic Checklist integration test', () => { expect(step.id).toBeDefined() expect(step.title).toBeDefined() }) + expect(flow.version).toBeDefined() }) test('read and set flow state', async () => { @@ -66,6 +66,7 @@ describe('Basic Checklist integration test', () => { flowSlug: 'some-flow', flowName: 'Some flow', flowType: FlowType.CHECKLIST, + version: 1, data: { steps: [ { @@ -124,6 +125,7 @@ describe('Basic Checklist integration test', () => { [madeUpFlowId]: { flowSlug: 'some-flow', flowName: 'Some flow', + version: 1, flowType: FlowType.CHECKLIST, data: { steps: [ @@ -179,6 +181,7 @@ describe('Basic Checklist integration test', () => { [madeUpFlowId]: { flowSlug: 'some-flow', flowName: 'Some flow', + version: 1, flowType: FlowType.CHECKLIST, data: { steps: [ @@ -413,8 +416,8 @@ describe('Basic Checklist integration test', () => { const flow = await frigade.getFlow(testFlowId) expect(flow).toBeDefined() expect(flow.getStepByIndex(0)).toBeDefined() - expect(flow.getStepByIndex(0).subtitle).toContain('${email}') - expect(flow.getStepByIndex(0).subtitle).toContain('${name}') + expect(flow.getStepByIndex(0).subtitle).not.toContain('${email}') + expect(flow.getStepByIndex(0).subtitle).not.toContain('${name}') const step = flow.steps.get(testFlowStepId) const customVariables = { name: 'John Doe', @@ -458,6 +461,247 @@ describe('Basic Checklist integration test', () => { expect(flow.isCompleted).toBeFalsy() expect(flow.isVisible).toBeTruthy() }) + test('on() event handler for flow.complete', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, _step?: FlowStep) => { + expect(event).toBe('flow.complete') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + } + ) + frigade.on('flow.complete', callback) + + expect(flow.isCompleted).toBeFalsy() + await flow.complete() + expect(flow.isCompleted).toBeTruthy() + expect(callback).toHaveBeenCalledTimes(1) + }) + + test('on() event handler for flow.start', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, _step?: FlowStep) => { + expect(event).toBe('flow.start') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + } + ) + frigade.on('flow.start', callback) + + const step = flow.steps.get(testFlowStepId) + expect(step).toBeDefined() + + await step.start() + expect(callback).toHaveBeenCalledTimes(1) + }) + + test('on() event handler for step.start', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn((event: string, flow: Flow, _previousFlow?: Flow, step?: FlowStep) => { + expect(event).toBe('step.start') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + expect(step).toBeDefined() + expect(step.id).toEqual(testFlowStepId) + }) + frigade.on('step.start', callback) + + const step = flow.steps.get(testFlowStepId) + expect(step).toBeDefined() + if (step) { + await step.start() + expect(callback).toHaveBeenCalledTimes(1) + } + + frigade.off('step.start', callback) + + const callback2 = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, step?: FlowStep) => { + expect(event).toBe('step.start') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + expect(step).toBeDefined() + expect(step.id).toEqual(testFlowStepId2) // Assuming testFlowStepId2 is the ID of step 2 + } + ) + frigade.on('step.start', callback2) + + const step1 = flow.steps.get(testFlowStepId) + expect(step1).toBeDefined() + if (step1) { + await step1.complete() + const step2 = flow.steps.get(testFlowStepId2) // Assuming testFlowStepId2 is the ID of step 2 + expect(step2).toBeDefined() + if (step2) { + await step2.start() + expect(callback2).toHaveBeenCalledTimes(1) + } + } + + frigade.off('step.start', callback2) + }) + + test('on() event handler for step.complete', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn((event: string, flow: Flow, _previousFlow?: Flow, step?: FlowStep) => { + expect(event).toBe('step.complete') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + expect(step).toBeDefined() + expect(step.id).toEqual(testFlowStepId) + }) + frigade.on('step.complete', callback) + + if (flow) { + const step = flow.steps.get(testFlowStepId) + expect(step).toBeDefined() + if (step) { + await step.complete() + expect(callback).toHaveBeenCalledTimes(1) + } + } + }) + + test('on() event handler for flow.skip', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, _step?: FlowStep) => { + expect(event).toBe('flow.skip') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + } + ) + frigade.on('flow.skip', callback) + + if (flow) { + await flow.skip() + expect(callback).toHaveBeenCalledTimes(1) + } + }) + + test('on() event handler for step.reset', async () => { + const userId = getRandomID() + const frigade = new Frigade(testAPIKey, { + userId, + }) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + + const callback = jest.fn((event: string, flow: Flow, _previousFlow?: Flow, step?: FlowStep) => { + expect(event).toBe('step.reset') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + expect(step).toBeDefined() + expect(step.id).toEqual(testFlowStepId) + }) + frigade.on('step.reset', callback) + + if (flow) { + const step = flow.steps.get(testFlowStepId) + expect(step).toBeDefined() + if (step) { + await step.complete() + await step.reset() + expect(callback).toHaveBeenCalledTimes(1) + } + } + }) + + test('on() event handler for steps completion should fire expected number of times', async () => { + const userId = getRandomID() + // First instance to complete the flow + const frigade = new Frigade(testAPIKey, { + userId, + }) + const callback = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, _step?: FlowStep) => { + expect(event).toBe('step.complete') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + } + ) + frigade.on('step.complete', callback) + await frigade.identify(userId) + const flow = await frigade.getFlow(testFlowId) + expect(flow).toBeDefined() + expect(callback).toHaveBeenCalledTimes(0) + + // Complete the flow first + expect(flow.steps.get(testFlowStepId).$state.completed).toBeFalsy() + await flow.steps.get(testFlowStepId).complete() + expect(flow.steps.get(testFlowStepId).$state.completed).toBeTruthy() + await flow.complete() + expect(callback).toHaveBeenCalledTimes(1) + + // Create a new Frigade instance with the same userId + const newFrigade = new Frigade(testAPIKey, { + userId, + }) + await newFrigade.identify(userId) + + // Register event handler after flow is already completed + const newCallback = jest.fn( + (event: string, flow: Flow, _previousFlow?: Flow, _step?: FlowStep) => { + expect(event).toBe('step.complete') + expect(flow).toBeDefined() + expect(flow.id).toEqual(testFlowId) + } + ) + + // Register the handler on the new instance - it should not fire for the already completed flow + newFrigade.on('step.complete', newCallback) + + // Verify the callback wasn't called + expect(newCallback).toHaveBeenCalledTimes(0) + + // Get the flow from the new instance and verify it's still completed + const newFlow = await newFrigade.getFlow(testFlowId) + expect(newFlow.isCompleted).toBeTruthy() + + // Reset and complete the flow step again to verify the handler works for new completions + await newFlow.restart() + expect(newFlow.steps.get(testFlowStepId).$state.completed).toBeFalsy() + await newFlow.steps.get(testFlowStepId).complete() + expect(newFlow.steps.get(testFlowStepId).$state.completed).toBeTruthy() + expect(newCallback).toHaveBeenCalledTimes(1) + }) }) describe('Advanced Checklist integration test', () => { diff --git a/packages/react/.eslintrc.cjs b/packages/react/.eslintrc.cjs index 8882f245..e8cc42a8 100644 --- a/packages/react/.eslintrc.cjs +++ b/packages/react/.eslintrc.cjs @@ -1,6 +1,6 @@ /* eslint-env node */ module.exports = { - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'prettier'], ignorePatterns: ['**/dist/*'], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint', 'react-refresh'], diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index fee4fede..345c9433 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,293 @@ # @frigade/react +## 2.10.1 + +### Patch Changes + +- d600eee: Fixes an issue related to form type select/multiple + +## 2.10.0 + +### Minor Changes + +- 11f71d9: Fixes an issue with Frigade and Vite + +## 2.9.6 + +### Patch Changes + +- 8496a7f: Fixes an issue where navigation change events were not firing in Firefox +- Updated dependencies [8496a7f] + - @frigade/js@0.9.5 + +## 2.9.5 + +### Patch Changes + +- 75a5475: Adds an additional null check to Flows served through collections + +## 2.9.4 + +### Patch Changes + +- e1688af: - Fixes a bug where `frigade.on('step.compelte')` could sometimes be called for already completed steps in flows. + - Fixes an issue with using Storybook with Frigade components +- Updated dependencies [e1688af] + - @frigade/js@0.9.4 + +## 2.9.3 + +### Patch Changes + +- b3d3cc6: Fixes an edge case where in some cases, the error `Frigade has not yet been initialized` can occur. +- Updated dependencies [b3d3cc6] + - @frigade/js@0.9.3 + +## 2.9.2 + +### Patch Changes + +- 78b0672: Fixes an issue where collections in some cases would not respect cool offs +- Updated dependencies [78b0672] + - @frigade/js@0.9.2 + +## 2.9.1 + +### Patch Changes + +- 2113e9d: Fixes a typescript issue with a required field for `__refreshIntervalInMS` +- Updated dependencies [2113e9d] + - @frigade/js@0.9.1 + +## 2.9.0 + +### Minor Changes + +- 32e8384: Performance improvement that updates the behavior of `syncOnWindowUpdates` to debounce identical calls more aggressively + +### Patch Changes + +- Updated dependencies [32e8384] + - @frigade/js@0.9.0 + +## 2.8.3 + +### Patch Changes + +- 4063bb5: Fix dependencies for Flow onComplete + +## 2.8.2 + +### Patch Changes + +- 6ac6815: Add themeSelector prop to Provider + +## 2.8.1 + +### Patch Changes + +- 187f0c8: Misc fixes for data-step-id and part props + +## 2.8.0 + +### Minor Changes + +- 8e6f67b: Add Checklist.Floating component, Popover and Progress.Ring primitives, and useFloating and useAutoScroll hooks + +## 2.7.6 + +### Patch Changes + +- b9bc059: Export ClientPortal + +## 2.7.5 + +### Patch Changes + +- 51c7b3d: Upgrade dependencies for React 19 support + +## 2.7.4 + +### Patch Changes + +- 50e19ba: Adds support for React 19 + +## 2.7.3 + +### Patch Changes + +- d05a923: Fixes an issue where Frigade will re-render when minor fields are changed in the provider + +## 2.7.2 + +### Patch Changes + +- 8225ecd: Updates the behavior of `` such that Flows are only marked started when a valid selector is found. Also adds the ability to disable/enable scroll when using spotlight in tours. + +## 2.7.1 + +### Patch Changes + +- 3cd8d0b: Adds version number to the `Flow` model +- Updated dependencies [3cd8d0b] + - @frigade/js@0.8.1 + +## 2.7.0 + +### Minor Changes + +- 9bdfcb3: Adds a new and improved API for handling state changes which allows you to filter events based on flow and step completion for the user. The API is available through `frigade.on(...)` + +### Patch Changes + +- Updated dependencies [9bdfcb3] + - @frigade/js@0.8.0 + +## 2.6.8 + +### Patch Changes + +- 2860866: Bug fixes for onDismiss, onOpenChange, and URL-based Collection targeting +- Updated dependencies [2860866] + - @frigade/js@0.7.24 + +## 2.6.7 + +### Patch Changes + +- 0a6c80d: Fixes an issue where `generateGuestId` if used with the `useFrigade()` could cause components not to load +- Updated dependencies [0a6c80d] + - @frigade/js@0.7.23 + +## 2.6.6 + +### Patch Changes + +- 991ebe2: Adds the ability to override the default scoring (options) on the `Survey.NPS` component + +## 2.6.5 + +### Patch Changes + +- 3808781: Fix issue with ESM import of use-sync-external-store. + +## 2.6.4 + +### Patch Changes + +- 3855645: Fixes an issue with React 17 related to useSyncExternalStore + +## 2.6.3 + +### Patch Changes + +- 51c62d6: Show error logs when hooks are used outside the context of the `Frigade.Provider` + +## 2.6.2 + +### Patch Changes + +- 32d72bc: Fixes an issue where `flow.restart()` followed by `flow.start()` could cause Dialog flows not to show up + +## 2.6.1 + +### Patch Changes + +- cce59b6: Fixes a series of inconsistencies in dismissible behaviors across components + +## 2.6.0 + +### Minor Changes + +- ae705d1: Refactored useModal. Modal collision detection should behave more consistently with SSR / caching. + +## 2.5.29 + +### Patch Changes + +- b59ede2: Fix Button active styles + +## 2.5.28 + +### Patch Changes + +- 25e7a98: Add video props to Announcement, Card, Collapsible + +## 2.5.27 + +### Patch Changes + +- 76343a0: Allow video HTML props to be passed into Tour + +## 2.5.26 + +### Patch Changes + +- e8de2d9: Adds a `try/catch` to localStorage getter/setter +- Updated dependencies [e8de2d9] + - @frigade/js@0.7.22 + +## 2.5.25 + +### Patch Changes + +- e9053ec: Partially de-flake Form tests +- 1eef79d: Fix dismissible prop in Tour + +## 2.5.24 + +### Patch Changes + +- 595957b: Fixes an issue where form state triggers on step change even if no fields are dirty + +## 2.5.23 + +### Patch Changes + +- 7d96719: Fixes a minor bug where multiple flow back and forth navigations can result in a 2 page form to finish prematurely +- Updated dependencies [7d96719] + - @frigade/js@0.7.21 + +## 2.5.22 + +### Patch Changes + +- 51b6444: Fixes an issue where variables are sometimes not applied when using useFlow() with the same Flow ID in a component +- Updated dependencies [51b6444] + - @frigade/js@0.7.20 + +## 2.5.21 + +### Patch Changes + +- 2982a19: Fixes an issue where variables are sometimes not applied when using useFlow() with the same Flow ID in a component +- Updated dependencies [2982a19] + - @frigade/js@0.7.19 + +## 2.5.20 + +### Patch Changes + +- 177f6bb: Fixes an edge cases where variables used in form fields do not update in some cases +- Updated dependencies [177f6bb] + - @frigade/js@0.7.18 + +## 2.5.19 + +### Patch Changes + +- 6c8748e: Contains a series of minor bug fixes related to tours and variables +- Updated dependencies [6c8748e] + - @frigade/js@0.7.17 + +## 2.5.18 + +### Patch Changes + +- 96a7862: Fixes an issue where checklists complete when finishing the last step +- Updated dependencies [96a7862] + - @frigade/js@0.7.16 + ## 2.5.17 ### Patch Changes diff --git a/packages/react/package.json b/packages/react/package.json index 6d008739..66b996dd 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@frigade/react", - "version": "2.5.17", + "version": "2.10.1", "description": "Build better product onboarding, faster.", "type": "module", "main": "./dist/index.js", @@ -29,7 +29,7 @@ ], "exports": { ".": { - "storybook": "./src/index.ts", + "smithy": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" @@ -45,12 +45,13 @@ "lint": "eslint --fix --ext .ts,.tsx .", "build": "yarn clean && tsup", "local-release": "tsup", - "prepublish": "node prepublish.mjs" + "prepublish": "node prepublish.mjs", + "prepare": "cd ../../ && husky" }, "dependencies": { - "@emotion/react": "^11.11.4", + "@emotion/react": "^11.14.0", "@floating-ui/react": "^0.26.22", - "@frigade/js": "^0.7.15", + "@frigade/js": "^0.9.5", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.5", @@ -58,16 +59,18 @@ "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-select": "^2.0.0", "clsx": "^2.0.0", - "dompurify": "^3.1.3", + "dompurify": "^3.2.4", "embla-carousel-react": "^8.1.3", "jsdom": "^23.0.1", "known-css-properties": "^0.29.0", "react-hook-form": "^7.49.3", - "react-remove-scroll": "^2.5.10" + "react-remove-scroll": "^2.5.10", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "react": "17 - 18", - "react-dom": "17 - 18" + "@emotion/react": "^11.14.0", + "react": "17 - 19", + "react-dom": "17 - 19" }, "devDependencies": { "@types/dompurify": "^3.0.5", @@ -75,6 +78,7 @@ "@types/node": "^20.10.5", "@types/react": "^18.2.34", "@types/react-dom": "^18.2.14", + "@types/use-sync-external-store": "^0.0.6", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", diff --git a/packages/react/prepublish.mjs b/packages/react/prepublish.mjs index d01892ba..4b49eb3e 100644 --- a/packages/react/prepublish.mjs +++ b/packages/react/prepublish.mjs @@ -1,7 +1,9 @@ -import {writeFileSync} from 'fs'; +import {writeFileSync, readFileSync} from 'fs'; import {fileURLToPath} from 'url'; import {dirname, resolve} from 'path'; -import packageJson from './package.json' assert {type: 'json'}; + +const packageJson = JSON.parse(readFileSync('./package.json', 'utf-8')); + // Mimic __dirname in ES modules const __filename = fileURLToPath(import.meta.url); diff --git a/packages/react/src/components/Announcement/index.tsx b/packages/react/src/components/Announcement/index.tsx index 87a75674..1222aba6 100644 --- a/packages/react/src/components/Announcement/index.tsx +++ b/packages/react/src/components/Announcement/index.tsx @@ -2,6 +2,8 @@ import { Dialog, type DialogProps } from '@/components/Dialog' import { Flex } from '@/components/Flex' import { Flow, type FlowPropsWithoutChildren } from '@/components/Flow' +import { getVideoProps } from '@/components/Media/videoProps' + export interface AnnouncementProps extends FlowPropsWithoutChildren, DialogProps { /** * @ignore @@ -17,27 +19,9 @@ export interface AnnouncementProps extends FlowPropsWithoutChildren, DialogProps defaultOpen?: boolean } -const flowPropNames = [ - 'dismissible', - 'flowId', - 'forceMount', - 'onComplete', - 'onDismiss', - 'onPrimary', - 'onSecondary', - 'variables', -] - -export function Announcement({ flowId, part, ...props }: AnnouncementProps) { - const flowProps = Object.fromEntries( - Object.entries(props).filter(([k]) => flowPropNames.some((name) => k === name)) - ) - const dialogProps = Object.fromEntries( - Object.entries(props).filter(([k]) => flowPropNames.indexOf(k) === -1) - ) - +export function Announcement({ flowId, ...props }: AnnouncementProps) { return ( - + {({ flow, handleDismiss, @@ -46,81 +30,70 @@ export function Announcement({ flowId, part, ...props }: AnnouncementProps) { parentProps: { containerProps, dismissible }, step, }) => { - const stepProps = step.props ?? {} - const primaryButtonTitle = step.primaryButton?.title ?? step.primaryButtonTitle const secondaryButtonTitle = step.secondaryButton?.title ?? step.secondaryButtonTitle const disabled = step.$state.blocked + const { videoProps } = getVideoProps(step.props ?? {}) + return ( { - if (props.dismissible === false) { - e.preventDefault() - return - } - if (typeof props.onEscapeKeyDown === 'function') { - props.onEscapeKeyDown(e) - } - - if (!e.defaultPrevented) { - handleDismiss(e) - } - }} > - - {dismissible && } - - {step.title} - {step.subtitle} - + {dismissible && } + + {step.title} + {step.subtitle} + - + - + - button': { - flexBasis: '50%', - flexGrow: 1, - }, - }} - gap={3} - part="announcement-footer" - > - {secondaryButtonTitle && ( - - )} - {primaryButtonTitle && ( - - )} - - + button': { + flexBasis: '50%', + flexGrow: 1, + }, + }} + gap={3} + part="announcement-footer" + > + {secondaryButtonTitle && ( + + )} + {primaryButtonTitle && ( + + )} + ) }} diff --git a/packages/react/src/components/Banner/index.tsx b/packages/react/src/components/Banner/index.tsx index 7cc9d1b6..25a17f19 100644 --- a/packages/react/src/components/Banner/index.tsx +++ b/packages/react/src/components/Banner/index.tsx @@ -5,18 +5,16 @@ import { Box } from '@/components/Box' export interface BannerProps extends FlowPropsWithoutChildren {} -export function Banner({ dismissible, flowId, part, ...props }: BannerProps) { +export function Banner({ flowId, ...props }: BannerProps) { return ( {({ handleDismiss, handlePrimary, handleSecondary, - parentProps: { containerProps }, + parentProps: { containerProps, dismissible }, step, }) => { - const stepProps = step.props ?? {} - const primaryButtonTitle = step.primaryButton?.title ?? step.primaryButtonTitle const secondaryButtonTitle = step.secondaryButton?.title ?? step.secondaryButtonTitle @@ -27,14 +25,14 @@ export function Banner({ dismissible, flowId, part, ...props }: BannerProps) { alignItems="center" aria-label="Banner" borderWidth="md" + data-step-id={step.id} display="flex" flexDirection="row" gap={3} justifyContent="flex-start" - part={['banner', part]} + part="banner" role="complementary" {...containerProps} - {...stepProps} > {step.imageUri && ( - + diff --git a/packages/react/src/components/Card/index.tsx b/packages/react/src/components/Card/index.tsx index 7d52a707..37eeaa45 100644 --- a/packages/react/src/components/Card/index.tsx +++ b/packages/react/src/components/Card/index.tsx @@ -1,6 +1,6 @@ import type { ForwardRefExoticComponent, RefAttributes } from 'react' import * as React from 'react' -import { EmotionJSX } from '@emotion/react/types/jsx-namespace' +import type { JSX } from '@emotion/react/jsx-runtime' import type { BoxProps } from '@/components/Box' import { Button, type ButtonProps } from '@/components/Button' @@ -24,14 +24,14 @@ export interface CardComponent extends ForwardRefExoticComponent< Omit & RefAttributes > { - Dismiss: (props: ButtonProps) => EmotionJSX.Element - Header: (props: CardHeaderProps) => EmotionJSX.Element - Footer: (props: BoxProps) => EmotionJSX.Element - Media: (props: MediaProps) => EmotionJSX.Element - Primary: (props: ButtonProps) => EmotionJSX.Element - Secondary: (props: ButtonProps) => EmotionJSX.Element - Subtitle: (props: TextProps) => EmotionJSX.Element - Title: (props: TextProps) => EmotionJSX.Element + Dismiss: (props: ButtonProps) => JSX.Element + Header: (props: CardHeaderProps) => JSX.Element + Footer: (props: BoxProps) => JSX.Element + Media: (props: MediaProps) => JSX.Element + Primary: (props: ButtonProps) => JSX.Element + Secondary: (props: ButtonProps) => JSX.Element + Subtitle: (props: TextProps) => JSX.Element + Title: (props: TextProps) => JSX.Element } export interface CardProps extends FlowPropsWithoutChildren { @@ -86,6 +86,14 @@ Card.Footer = ({ children, part, ...props }: BoxProps) => { } Card.Header = ({ dismissible, handleDismiss, part, subtitle, title, ...props }) => { + if ( + !dismissible && + (title == null || title?.length === 0) && + (subtitle == null || subtitle?.length === 0) + ) { + return null + } + return ( {checked && ( diff --git a/packages/react/src/components/Checklist/CarouselEmblaWrapper.tsx b/packages/react/src/components/Checklist/CarouselEmblaWrapper.tsx index 1cd0e4d6..25c215f1 100644 --- a/packages/react/src/components/Checklist/CarouselEmblaWrapper.tsx +++ b/packages/react/src/components/Checklist/CarouselEmblaWrapper.tsx @@ -69,7 +69,7 @@ export function CarouselEmblaWrapper({ setHasNext(emblaApi.canScrollNext()) setHasPrev(emblaApi.canScrollPrev()) }) - }, [emblaApi]) + }, [emblaApi, hasNext, hasPrev]) useEffect(() => { // check if hasCompletedInitialSort. If not then sort the steps from not completed not skipped to completed/skipped @@ -91,7 +91,7 @@ export function CarouselEmblaWrapper({ setStepOrder(steps.map((step) => step.id)) } } - }, []) + }, [flow.steps, sort, stepOrder]) const completedSteps = flow.getNumberOfCompletedSteps() const availableSteps = flow.getNumberOfAvailableSteps() diff --git a/packages/react/src/components/Checklist/CarouselStep.tsx b/packages/react/src/components/Checklist/CarouselStep.tsx index 2ebff38a..04da823d 100644 --- a/packages/react/src/components/Checklist/CarouselStep.tsx +++ b/packages/react/src/components/Checklist/CarouselStep.tsx @@ -42,6 +42,7 @@ export function CarouselStep({ onPrimary, onSecondary, step }: CarouselStepProps flexBasis: '25%', }, }} + data-step-id={step.id} flex="0 0 30%" gap="2" p="4" diff --git a/packages/react/src/components/Checklist/Collapsible.tsx b/packages/react/src/components/Checklist/Collapsible.tsx index ec70abd3..b93f23bd 100644 --- a/packages/react/src/components/Checklist/Collapsible.tsx +++ b/packages/react/src/components/Checklist/Collapsible.tsx @@ -1,4 +1,4 @@ -import { EmotionJSX } from '@emotion/react/types/jsx-namespace' +import type { JSX } from '@emotion/react/jsx-runtime' import { createContext, type Dispatch, type SetStateAction, useContext, useState } from 'react' import { Card } from '@/components/Card' @@ -10,12 +10,14 @@ import { Text } from '@/components/Text' import { type StepHandlerProp, useStepHandlers } from '@/hooks/useStepHandlers' +import { getVideoProps } from '@/components/Media/videoProps' + export interface CollapsibleStepProps extends FlowChildrenProps { onOpenChange: (isOpening: boolean) => void open: boolean } -export type StepTypes = Record EmotionJSX.Element> +export type StepTypes = Record JSX.Element> export interface CollapsibleContextType { onPrimary: StepHandlerProp @@ -71,8 +73,15 @@ function DefaultCollapsibleStep({ const disabled = blocked ? true : false + const { videoProps } = getVideoProps(stepProps) + return ( - + {subtitle} @@ -136,7 +146,6 @@ function StepWrapper({ flow, step, ...props }: FlowChildrenProps) { } export function Collapsible({ - dismissible, flowId, onPrimary, onSecondary, @@ -174,19 +183,21 @@ export function Collapsible({ const currentSteps = flow.getNumberOfCompletedSteps() const availableSteps = flow.getNumberOfAvailableSteps() + // Note: Ignore merged props from step here, Checklist steps don't control flow dismissibility + const dismissible = props.dismissible || !!flow?.props?.dismissible + return ( <> - + - - + + {currentSteps}/{availableSteps} diff --git a/packages/react/src/components/Checklist/CollapsibleStep.tsx b/packages/react/src/components/Checklist/CollapsibleStep.tsx index 25525fca..4796fab5 100644 --- a/packages/react/src/components/Checklist/CollapsibleStep.tsx +++ b/packages/react/src/components/Checklist/CollapsibleStep.tsx @@ -49,6 +49,7 @@ function CheckIndicator({ checked = false }) { borderStyle="solid" borderColor="neutral.border" borderRadius="100%" + marginRight="2" padding="0" part="field-radio-value" position="relative" @@ -100,7 +101,7 @@ export function Content({ children }) { It creates a flex gap at the top of this column, which animates smoothly. Other forms of whitespace like margin or padding? Not so smooth! */} - + {children} @@ -139,7 +140,7 @@ export function Trigger({ isCompleted, isBlocked, title }) { - - - - {title} - - + + + {title} + >() + + function handlePointerEnter() { + clearTimeout(pointerLeaveTimeout.current) + } + + function handlePointerLeave() { + clearTimeout(pointerLeaveTimeout.current) + + if (openStepId != null) { + pointerLeaveTimeout.current = setTimeout(() => setOpenStepId(null), 300) + } + } + + function resetOpenStep(isOpen: boolean) { + if (!isOpen && openStepId != null) { + setOpenStepId(null) + } + } + + return ( + + {({ flow }) => { + const currentSteps = flow.getNumberOfCompletedSteps() + const availableSteps = flow.getNumberOfAvailableSteps() + + const anchorContent = children ?? ( + + + {flow.title} + + + + ) + + return ( + + + {anchorContent} + + + + + {Array.from(flow.steps.values()).map((step) => ( + + ))} + + + + + ) + }} + + ) +} diff --git a/packages/react/src/components/Checklist/FloatingStep.tsx b/packages/react/src/components/Checklist/FloatingStep.tsx new file mode 100644 index 00000000..2d71bbef --- /dev/null +++ b/packages/react/src/components/Checklist/FloatingStep.tsx @@ -0,0 +1,108 @@ +import { useRef } from 'react' +import * as Popover from '@/components/Popover' + +import { Card } from '@/components/Card' +import { CheckIndicator } from '@/components/CheckIndicator' +import { Flex } from '@/components/Flex' +import { getVideoProps } from '@/components/Media/videoProps' +import { Text } from '@/components/Text' + +import { useStepHandlers } from '@/hooks/useStepHandlers' + +import { floatingTransitionCSS } from '@/components/Checklist/Floating.styles' + +export function FloatingStep({ onPrimary, onSecondary, openStepId, setOpenStepId, step }) { + const anchorPointerEnterTimeout = useRef>() + const { handlePrimary, handleSecondary } = useStepHandlers(step, { onPrimary, onSecondary }) + + const isStepOpen = openStepId === step.id + + async function wrappedHandlePrimary(...args: Parameters) { + const primaryReturnValue = await handlePrimary(...args) + + if (primaryReturnValue) { + setOpenStepId(null) + } + } + + async function wrappedHandleSecondary(...args: Parameters) { + const secondaryReturnValue = await handleSecondary(...args) + + if (secondaryReturnValue) { + setOpenStepId(null) + } + } + + function handlePointerEnter() { + clearTimeout(anchorPointerEnterTimeout.current) + + if (!isStepOpen) { + anchorPointerEnterTimeout.current = setTimeout(() => setOpenStepId(step.id), 300) + } + } + + function handlePointerLeave() { + clearTimeout(anchorPointerEnterTimeout.current) + } + + const primaryButtonTitle = step.primaryButton?.title ?? step.primaryButtonTitle + const secondaryButtonTitle = step.secondaryButton?.title ?? step.secondaryButtonTitle + + const { videoProps } = getVideoProps(step.props ?? {}) + + return ( + + + {step.title} + + + + + + + + + + + + + + + ) +} diff --git a/packages/react/src/components/Checklist/index.tsx b/packages/react/src/components/Checklist/index.tsx index 31f4fd3f..bdb192a4 100644 --- a/packages/react/src/components/Checklist/index.tsx +++ b/packages/react/src/components/Checklist/index.tsx @@ -2,3 +2,4 @@ export { Carousel } from './Carousel' export { Collapsible, type CollapsibleProps, type CollapsibleStepProps } from './Collapsible' // eslint-disable-next-line react-refresh/only-export-components export * as CollapsibleStep from './CollapsibleStep' +export { Floating } from './Floating' diff --git a/packages/react/src/components/ClientPortal/ClientPortal.tsx b/packages/react/src/components/ClientPortal/ClientPortal.tsx new file mode 100644 index 00000000..019a9714 --- /dev/null +++ b/packages/react/src/components/ClientPortal/ClientPortal.tsx @@ -0,0 +1,10 @@ +import { useClientPortal } from '@/hooks/useClientPortal' + +export interface ClientPortalProps { + children?: React.ReactNode + container?: Parameters[1] +} + +export function ClientPortal({ children, container = 'body' }: ClientPortalProps) { + return useClientPortal(children, container) +} diff --git a/packages/react/src/components/ClientPortal/index.tsx b/packages/react/src/components/ClientPortal/index.tsx new file mode 100644 index 00000000..7e2ca2f9 --- /dev/null +++ b/packages/react/src/components/ClientPortal/index.tsx @@ -0,0 +1 @@ +export { ClientPortal, type ClientPortalProps } from './ClientPortal' diff --git a/packages/react/src/components/Collection/index.tsx b/packages/react/src/components/Collection/index.tsx index 565d742f..2a487b71 100644 --- a/packages/react/src/components/Collection/index.tsx +++ b/packages/react/src/components/Collection/index.tsx @@ -1,4 +1,4 @@ -import { EmotionJSX } from '@emotion/react/types/jsx-namespace' +import type { JSX } from '@emotion/react/jsx-runtime' import { Announcement } from '@/components/Announcement' import { Banner } from '@/components/Banner' @@ -21,7 +21,7 @@ export interface CollectionProps extends BoxProps { collectionId: string /** - * A map of variables to pass to Flows this Collection. + * A map of variables to pass to Flows in this Collection. * Example: * ```tsx * variables={{ @@ -48,7 +48,7 @@ export function Collection({ collectionId, part, variables = {}, ...props }: Col const { currentFlow } = useCollection(collectionId) - const FlowComponent: EmotionJSX.ElementType = flowTypeMap[currentFlow?.rawData?.flowType] ?? null + const FlowComponent: JSX.ElementType = flowTypeMap[currentFlow?.rawData?.flowType] ?? null if (currentFlow == null || FlowComponent == null) { return null diff --git a/packages/react/src/components/Flow/FlowProps.ts b/packages/react/src/components/Flow/FlowProps.ts index aff33e5d..2365e734 100644 --- a/packages/react/src/components/Flow/FlowProps.ts +++ b/packages/react/src/components/Flow/FlowProps.ts @@ -10,6 +10,26 @@ import type { StepHandler, StepHandlerProp } from '@/hooks/useStepHandlers' export interface BoxPropsWithoutChildren extends Omit {} export interface FlowPropsWithoutChildren extends BoxPropsWithoutChildren { + /** + * Whether to automatically mark the Flow started (i.e. in progress) when the Flow is eligible to be shown. + * You will need to call `flow.start()` or `step.start()` from the parent component if you set this to `false`. Most components should not need to override this behavior. + * + * Defaults to `true`. + */ + autoStart?: boolean + /** + * Optional component to wrap the child components in, e.g. `as={Dialog}` will render the Flow in a modal Dialog. Defaults to `Box`. + */ + as?: React.ElementType + /** + * Emotion CSS prop to apply to the component. See [Theming documentation](https://docs.frigade.com/v2/sdk/styling/css-overrides) for more information. + * + * Example usage: + * ``` + * + * ``` + */ + css?: React.Attributes['css'] /** * Whether the Flow is dismissible or not * @@ -38,12 +58,12 @@ export interface FlowPropsWithoutChildren extends BoxPropsWithoutChildren { onDismiss?: FlowHandlerProp /** * Handler for when primary button is clicked. - * If this function a promise that evaluates to `false`, the step will not be automatically completed when clicked. + * If this function returns false or a promise that resolves to `false`, the step will not be automatically completed when clicked. */ onPrimary?: StepHandlerProp /** * Handler for when secondary button is clicked. - * If this function a promise that evaluates to `false`, the step will not be automatically completed when clicked. + * If this function returns false or a promise that resolves to `false`, the step will not be automatically completed when clicked. */ onSecondary?: StepHandlerProp /** @@ -51,21 +71,6 @@ export interface FlowPropsWithoutChildren extends BoxPropsWithoutChildren { * For instance, you can use `title: Hello, ${name}!` in the Flow configuration and pass `variables={{name: 'John'}}` to customize the copy. */ variables?: Record - - /** - * Optional component to wrap the child components in, e.g. `as={Dialog}` will render the Flow in a modal Dialog. Defaults to `Box`. - */ - as?: React.ElementType - - /** - * Emotion CSS prop to apply to the component. See [Theming documentation](https://docs.frigade.com/v2/sdk/styling/css-overrides) for more information. - * - * Example usage: - * ``` - * - * ``` - */ - css?: React.Attributes['css'] } export interface FlowProps extends FlowPropsWithoutChildren { @@ -76,6 +81,7 @@ export interface FlowProps extends FlowPropsWithoutChildren { } type ParentProps = { + as: FlowPropsWithoutChildren['as'] containerProps: Record dismissible: boolean flowId: string diff --git a/packages/react/src/components/Flow/index.tsx b/packages/react/src/components/Flow/index.tsx index d68bad6c..e8f589bc 100644 --- a/packages/react/src/components/Flow/index.tsx +++ b/packages/react/src/components/Flow/index.tsx @@ -6,9 +6,10 @@ import { Box } from '@/components/Box' import { useFlow } from '@/hooks/useFlow' import { useFlowHandlers } from '@/hooks/useFlowHandlers' import { useStepHandlers } from '@/hooks/useStepHandlers' -import { useModal } from '@/hooks/useModal' +import { useCheckForModalCollision } from '@/hooks/useModal' import type { FlowProps } from '@/components/Flow/FlowProps' +import { getVideoProps } from '@/components/Media/videoProps' // import { FrigadeContext } from '@/components/Provider' export type { @@ -17,8 +18,13 @@ export type { FlowPropsWithoutChildren, } from '@/components/Flow/FlowProps' +function isDialog(component) { + return typeof component === 'function' && component.displayName === 'Dialog' +} + export function Flow({ as, + autoStart = true, children, flowId, onComplete, @@ -26,7 +32,6 @@ export function Flow({ onPrimary, onSecondary, variables, - ...props }: FlowProps) { // const [hasProcessedRules, setHasProcessedRules] = useState(false) @@ -35,19 +40,25 @@ export function Flow({ variables, }) + const step = flow?.getCurrentStep() + + const initialStepProps = step?.props ?? {} + + // Discard video props when merging step props onto top-level container + const { otherProps: stepProps } = getVideoProps(initialStepProps) + const { - dismissible = false, + dismissible = isDialog(as) ? true : false, forceMount = false, ...mergedProps } = { ...props, ...(flow?.props ?? {}), + ...(flow?.rawData?.flowType === FlowType.CHECKLIST ? {} : stepProps), } // const { hasInitialized, registerComponent, unregisterComponent } = useContext(FrigadeContext) - const step = flow?.getCurrentStep() - const { handleDismiss } = useFlowHandlers(flow, { onComplete, onDismiss, @@ -60,10 +71,25 @@ export function Flow({ const isModal = mergedProps?.modal || - (typeof as === 'function' && as?.displayName === 'Dialog') || + isDialog(as) || [FlowType.ANNOUNCEMENT, FlowType.TOUR].includes(flow?.rawData?.flowType) - const { isCurrentModal } = useModal(flow, isModal) + const { hasModalCollision } = useCheckForModalCollision(flow, isModal) + + function handleEscapeKeyDown(e) { + if (dismissible === false) { + e.preventDefault() + return + } + + if (typeof props.onEscapeKeyDown === 'function') { + props.onEscapeKeyDown(e) + } + + if (!e.defaultPrevented) { + handleDismiss(e) + } + } // useEffect(() => { // return () => { @@ -89,11 +115,7 @@ export function Flow({ // } // }) - if (!flow.isVisible && !shouldForceMount) { - return null - } - - if (!shouldForceMount && !isCurrentModal) { + if ((!flow.isVisible || hasModalCollision) && !shouldForceMount) { return null } @@ -101,17 +123,21 @@ export function Flow({ // return null // } - if (shouldForceMount || (!flow.isCompleted && !flow.isSkipped)) { + if (shouldForceMount || (!flow.isCompleted && !flow.isSkipped && autoStart)) { step?.start() } const ContainerElement = as === null ? Fragment : as ?? Box - const containerProps = { + const containerProps: Record = { ...mergedProps, 'data-flow-id': flow.id, } + if (isDialog(as)) { + containerProps.onEscapeKeyDown = handleEscapeKeyDown + } + return ( {children({ @@ -120,6 +146,7 @@ export function Flow({ handlePrimary, handleSecondary, parentProps: { + as, dismissible, flowId, variables, diff --git a/packages/react/src/components/Form/FormStep.tsx b/packages/react/src/components/Form/FormStep.tsx index a9fbecc1..0a251c5f 100644 --- a/packages/react/src/components/Form/FormStep.tsx +++ b/packages/react/src/components/Form/FormStep.tsx @@ -92,7 +92,17 @@ export function FormStep({ }, {}) ) } - }, [fieldDatas]) + }, [fieldDatas, formContext]) + + useEffect(() => { + if ( + Object.keys(formContext.formState.dirtyFields).some((field) => + fieldDatas.some((data) => data.id === field) + ) + ) { + formContext.trigger() + } + }, [fieldDatas, formContext, step.id]) const [isSubmitting, setIsSubmitting] = useState(false) @@ -100,8 +110,6 @@ export function FormStep({ const { control, handleSubmit } = formContext - const stepProps = step.props ?? {} - function onPrimarySubmit(properties: PropertyPayload, e: SyntheticEvent) { setIsSubmitting(true) handlePrimary(e, properties, __readOnly === true).then(() => setIsSubmitting(false)) @@ -128,10 +136,10 @@ export function FormStep({ useEffect(() => { formContext.clearErrors() - }, [step]) + }, [formContext, step]) return ( - + <> - + ) } diff --git a/packages/react/src/components/Form/fields/BaseField.tsx b/packages/react/src/components/Form/fields/BaseField.tsx index 94238904..60f815be 100644 --- a/packages/react/src/components/Form/fields/BaseField.tsx +++ b/packages/react/src/components/Form/fields/BaseField.tsx @@ -8,7 +8,7 @@ import * as styles from './BaseField.styles' export interface FieldProps { value: string onChange: (value: string) => void - [key: string]: any + [key: string]: unknown } interface BaseFieldProps extends FormFieldProps { diff --git a/packages/react/src/components/Form/fields/CheckboxField.tsx b/packages/react/src/components/Form/fields/CheckboxField.tsx index b8460b59..0ff8c409 100644 --- a/packages/react/src/components/Form/fields/CheckboxField.tsx +++ b/packages/react/src/components/Form/fields/CheckboxField.tsx @@ -11,7 +11,7 @@ import * as baseStyles from '@/components/Form/fields/BaseField.styles' export function CheckboxField(props: FormFieldProps) { const { field: { onChange, value }, - fieldData: { options, label, id = [] }, + fieldData: { label, id = [] }, } = props return ( @@ -32,7 +32,7 @@ export function CheckboxField(props: FormFieldProps) { justifyContent="center" alignItems="center" display="flex" - // @ts-ignore + // @ts-expect-error :hover type isn't in style props yet backgroundColor:hover="neutral.900" part="field-checkbox" id={id as string} diff --git a/packages/react/src/components/Form/fields/SelectMultipleField.tsx b/packages/react/src/components/Form/fields/SelectMultipleField.tsx index a64c9cdc..b9915bd2 100644 --- a/packages/react/src/components/Form/fields/SelectMultipleField.tsx +++ b/packages/react/src/components/Form/fields/SelectMultipleField.tsx @@ -15,16 +15,15 @@ export function SelectMultipleField(props: FormFieldProps) { fieldData: { options = [] }, } = props - const [valueArray, setValueArray] = React.useState([]) + // Ensure we always work with an array of strings coming from RHF + const valueArray: string[] = Array.isArray(value) ? value : [] - function setValueInArray(value: string) { - let updatedValueArray = [] - if (valueArray.includes(value)) { - updatedValueArray = [...valueArray.filter((v) => v !== value)] - } else { - updatedValueArray = [...valueArray, value] - } - setValueArray(updatedValueArray) + function toggleValue(optionValue: string) { + const updatedValueArray = valueArray.includes(optionValue) + ? valueArray.filter((v) => v !== optionValue) + : [...valueArray, optionValue] + + // Let react-hook-form handle the new value onChange(updatedValueArray) } @@ -32,11 +31,11 @@ export function SelectMultipleField(props: FormFieldProps) { {() => ( - {options.map(({ label, value }) => ( + {options.map(({ label, value: optionValue }) => ( setValueInArray(value)} - key={value} + key={optionValue} + checked={valueArray.includes(optionValue)} + onCheckedChange={() => toggleValue(optionValue)} asChild > // allow any other custom properties - [key: string]: any + [key: string]: unknown } // TODO: Wire UseControllerReturn into this type @@ -108,7 +109,7 @@ export function Form({ fieldTypes = {}, flowId, part, ...props }: FormProps) { const mergedFieldTypes = Object.assign({}, defaultFieldTypes, fieldTypes) return ( - + {(childProps) => } ) diff --git a/packages/react/src/components/Hint/index.tsx b/packages/react/src/components/Hint/index.tsx index ba76d890..b3ae1491 100644 --- a/packages/react/src/components/Hint/index.tsx +++ b/packages/react/src/components/Hint/index.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useRef, useState } from 'react' import { Box, type BoxProps } from '@/components/Box' import { Overlay } from '@/components/Overlay' @@ -6,7 +6,9 @@ import { Ping } from '@/components/Ping' import { Spotlight } from '@/components/Spotlight' import { getPingPosition } from '@/components/Hint/getPingPosition' -import { useFloatingHint } from '@/components/Hint/useFloatingHint' +import { useAutoScroll } from '@/hooks/useAutoScroll' +import { useFloating } from '@/hooks/useFloating' +import { useVisibility } from '@/hooks/useVisibility' export type AlignValue = 'after' | 'before' | 'center' | 'end' | 'start' export type SideValue = 'bottom' | 'left' | 'right' | 'top' @@ -16,9 +18,12 @@ export interface HintProps extends BoxProps { align?: AlignValue alignOffset?: number anchor: string + autoScroll?: ScrollIntoViewOptions | boolean children?: React.ReactNode defaultOpen?: boolean + lockScroll?: boolean modal?: boolean + onMount?: () => void onOpenChange?: (open: boolean) => void open?: boolean side?: SideValue @@ -30,9 +35,13 @@ export function Hint({ align = 'center', alignOffset = 0, anchor, + autoScroll = false, children, + css = {}, defaultOpen = true, + lockScroll = true, modal = false, + onMount, onOpenChange = () => {}, open, part, @@ -47,35 +56,55 @@ export function Hint({ // Defer to controlled open prop, otherwise manage open state internally const canonicalOpen = open ?? internalOpen - const { getFloatingProps, getReferenceProps, floatingStyles, placement, refs } = useFloatingHint({ - align, - alignOffset, - anchor, - onOpenChange: (newOpen) => { - onOpenChange(newOpen) - - if (open == null) { - setInteralOpen(newOpen) - } - }, - open: canonicalOpen, - side, - sideOffset, - }) + const { getFloatingProps, getReferenceProps, floatingStyles, placement, refs, status } = + useFloating({ + align, + alignOffset, + anchor, + onOpenChange: (newOpen) => { + onOpenChange(newOpen) + + if (open == null) { + setInteralOpen(newOpen) + } + }, + open: canonicalOpen, + side, + sideOffset, + }) const [finalSide, finalAlign] = placement.split('-') const referenceProps = getReferenceProps() - if (refs.reference.current == null) { + const { isVisible } = useVisibility(refs.reference.current as Element | null) + const isMounted = useRef(false) + + useAutoScroll(refs.reference.current as Element, autoScroll) + + const shouldMount = refs.reference.current !== null && isVisible + + if (!shouldMount) { + isMounted.current = false return null + } else if (isMounted.current === false) { + isMounted.current = true + onMount?.() } return ( <> - {spotlight && canonicalOpen && } - {modal && !spotlight && canonicalOpen && } + {spotlight && canonicalOpen && } + {modal && !spotlight && canonicalOpen && } { + if (typeof anchor !== 'string') { + return + } + try { const element = document.querySelector(anchor) if (element != null) { + console.debug(`[frigade] Found anchor: ${anchor}`) setAnchorElement(element) + } else { + console.debug(`[frigade] No anchor found for selector: ${anchor}`) } } catch (invalidSelector) { - /* no-op */ + console.debug(`[frigade] Invalid selector for anchor: ${anchor}`) } }, [anchor]) useEffect(() => { + if (typeof anchor !== 'string') { + return + } + const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type !== 'childList') { @@ -54,6 +65,7 @@ export function useMutationAwareAnchor(anchor: string) { const maybeAnchor = checkElementForAnchor(node as Element, anchor) if (maybeAnchor != null) { + console.debug(`[frigade] Found anchor: ${anchor}`) setAnchorElement(maybeAnchor) break } @@ -67,6 +79,7 @@ export function useMutationAwareAnchor(anchor: string) { const maybeAnchor = checkElementForAnchor(node as Element, anchor) if (maybeAnchor != null) { + console.debug(`[frigade] Removed anchor: ${anchor}`) setAnchorElement(null) break } diff --git a/packages/react/src/components/Media/Media.tsx b/packages/react/src/components/Media/Media.tsx index f23ae7a8..820839fb 100644 --- a/packages/react/src/components/Media/Media.tsx +++ b/packages/react/src/components/Media/Media.tsx @@ -1,9 +1,7 @@ import { Image } from './Image' -import { Video } from './Video' -import { BoxProps } from '@/components/Box' +import { Video, type VideoProps } from './Video' -export interface MediaProps extends BoxProps { - src: string +export interface MediaProps extends VideoProps { type?: 'image' | 'video' } diff --git a/packages/react/src/components/Media/Video.tsx b/packages/react/src/components/Media/Video.tsx index 9d63cf49..ff7bcfb4 100644 --- a/packages/react/src/components/Media/Video.tsx +++ b/packages/react/src/components/Media/Video.tsx @@ -1,5 +1,8 @@ +import * as React from 'react' import { Box, BoxProps } from '@/components/Box' +import type { VideoPropName } from '@/components/Media/videoProps' + function getVideoEmbedSrc(videoUri: string) { if (videoUri.includes('youtube')) { const videoId = videoUri.split('v=')[1]?.split('&')[0] @@ -26,17 +29,52 @@ function getVideoEmbedSrc(videoUri: string) { return null } -export interface VideoProps extends BoxProps { - src: string -} +export interface VideoProps + extends BoxProps, + Pick, VideoPropName> {} -export function Video({ part, src, ...props }: VideoProps) { +export function Video({ + autoPlay, + controls, + controlsList, + crossOrigin, + disablePictureInPicture, + disableRemotePlayback, + loop, + muted, + playsInline, + poster, + preload, + part, + src, + ...props +}: VideoProps) { const videoEmbedSrc = getVideoEmbedSrc(src) if (!videoEmbedSrc) { // Check if it's a url that ends in .mp4 if (src?.endsWith('.mp4')) { - return + return ( + + ) } console.error( @@ -47,7 +85,7 @@ export function Video({ part, src, ...props }: VideoProps) { return ( ) { + const videoProps = {} + const otherProps = {} + + for (const [propName, propValue] of Object.entries(props)) { + if (VIDEO_PROP_NAMES.some((name) => name === propName)) { + videoProps[propName] = propValue + } else { + otherProps[propName] = propValue + } + } + + return { + otherProps, + videoProps, + } +} diff --git a/packages/react/src/components/Overlay/index.tsx b/packages/react/src/components/Overlay/index.tsx index 67c35283..6235616a 100644 --- a/packages/react/src/components/Overlay/index.tsx +++ b/packages/react/src/components/Overlay/index.tsx @@ -1,40 +1,60 @@ import * as React from 'react' +import { keyframes } from '@emotion/react' import { Box, type BoxProps } from '@/components/Box' import { RemoveScroll } from 'react-remove-scroll' +import { useEffect, useState } from 'react' -export interface OverlayProps extends BoxProps {} - -// export function Overlay({ children, part, ...props }: OverlayProps) { -// return ( -// -// -// {children} -// -// -// ) -// } +export interface OverlayProps extends BoxProps { + lockScroll?: boolean +} function OverlayWithRef( - { children, part, ...props }: OverlayProps, + { children, lockScroll = true, part, opacity = 0.5, ...props }: OverlayProps, ref: React.ForwardedRef ) { + const fadeIn = keyframes` + from { + opacity: 0; + } + to { + opacity: ${opacity} + } + ` + + const [hasScrolled, setHasScrolled] = useState(false) + + useEffect(() => { + if (!lockScroll) { + const handleScroll = () => { + if (!hasScrolled) { + setHasScrolled(true) + window.removeEventListener('scroll', handleScroll) + } + } + + window.addEventListener('scroll', handleScroll) + + return () => { + window.removeEventListener('scroll', handleScroll) + } + } + }, [lockScroll, hasScrolled]) + + if (!lockScroll && hasScrolled) { + return <>{children} + } + return ( - + {children} diff --git a/packages/react/src/components/Ping/index.tsx b/packages/react/src/components/Ping/index.tsx index faa27045..24cb7efc 100644 --- a/packages/react/src/components/Ping/index.tsx +++ b/packages/react/src/components/Ping/index.tsx @@ -1,4 +1,5 @@ import { keyframes } from '@emotion/react' +import { useEffect, useState } from 'react' import { Box, type BoxProps } from '@/components/Box' @@ -21,7 +22,13 @@ export interface PingProps extends BoxProps { clickable?: boolean } -export function Ping({ clickable = false, part = '', ...props }: PingProps) { +export function Ping({ clickable = false, part = '', style = {}, ...props }: PingProps) { + const [hasMounted, setHasMounted] = useState(false) + + useEffect(() => { + setHasMounted(true) + }, []) + return ( + + {children} + + + ) +} diff --git a/packages/react/src/components/Popover/Root.tsx b/packages/react/src/components/Popover/Root.tsx new file mode 100644 index 00000000..0067c65b --- /dev/null +++ b/packages/react/src/components/Popover/Root.tsx @@ -0,0 +1,87 @@ +import { createContext, type Dispatch, type SetStateAction, useState } from 'react' +import { useFloatingNodeId } from '@floating-ui/react' + +import { Spotlight } from '@/components/Spotlight' +import { Overlay } from '@/components/Overlay' + +import { useAutoScroll } from '@/hooks/useAutoScroll' +import { type FloatingProps, type FloatingReturn, useFloating } from '@/hooks/useFloating' + +export interface PopoverContextValue { + floating?: FloatingReturn + floatingNodeId: string | null + isOpen: boolean + setIsOpen: Dispatch> +} + +export const PopoverContext = createContext({ + floatingNodeId: null, + isOpen: false, + setIsOpen: () => {}, +}) + +export interface PopoverRootProps extends FloatingProps { + autoScroll?: ScrollIntoViewOptions | boolean + children?: React.ReactNode + defaultOpen?: boolean + modal?: boolean + spotlight?: boolean +} + +export function Root({ + align = 'center', + alignOffset = 0, + anchor, + autoScroll = false, + children, + defaultOpen = false, + modal = false, + onOpenChange = () => {}, + open, + side = 'bottom', + sideOffset = 0, + spotlight = false, + ...floatingProps +}: PopoverRootProps) { + const [internalOpen, setInternalOpen] = useState(defaultOpen) + + // Defer to controlled open prop, otherwise manage open state internally + const canonicalOpen = open ?? internalOpen + const floatingNodeId = useFloatingNodeId() + + const floating = useFloating({ + align, + alignOffset, + anchor, + nodeId: floatingNodeId, + onOpenChange: (newOpen) => { + onOpenChange(newOpen) + if (open == null) { + setInternalOpen(newOpen) + } + }, + open: canonicalOpen, + side, + sideOffset, + ...floatingProps, + }) + + const { refs } = floating + + useAutoScroll(refs.reference.current as Element, autoScroll) + + return ( + + {spotlight && canonicalOpen && } + {modal && !spotlight && canonicalOpen && } + {children} + + ) +} diff --git a/packages/react/src/components/Popover/Trigger.tsx b/packages/react/src/components/Popover/Trigger.tsx new file mode 100644 index 00000000..bd7e9500 --- /dev/null +++ b/packages/react/src/components/Popover/Trigger.tsx @@ -0,0 +1,24 @@ +import { useContext } from 'react' +import { Box, type BoxProps } from '@/components/Box' +import { PopoverContext } from './Root' + +export interface PopoverTriggerProps extends BoxProps {} + +export function Trigger({ children, part, ...props }: BoxProps) { + const { + floating: { getReferenceProps, refs }, + setIsOpen, + } = useContext(PopoverContext) + + return ( + setIsOpen((prev) => !prev)} + part={['popover-trigger', part]} + {...props} + {...(getReferenceProps?.() ?? {})} + > + {children} + + ) +} diff --git a/packages/react/src/components/Popover/index.tsx b/packages/react/src/components/Popover/index.tsx new file mode 100644 index 00000000..1c7efc85 --- /dev/null +++ b/packages/react/src/components/Popover/index.tsx @@ -0,0 +1,6 @@ +export { Root } from './Root' +export { Content } from './Content' +export { Trigger } from './Trigger' +export type { PopoverRootProps } from './Root' +export type { PopoverContentProps } from './Content' +export type { PopoverTriggerProps } from './Trigger' diff --git a/packages/react/src/components/Progress/Ring.tsx b/packages/react/src/components/Progress/Ring.tsx new file mode 100644 index 00000000..fb88a319 --- /dev/null +++ b/packages/react/src/components/Progress/Ring.tsx @@ -0,0 +1,88 @@ +import { Box } from '@/components/Box' +import { Text } from '@/components/Text' + +import { theme } from '@/shared/theme' + +import type { ProgressProps } from './ProgressProps' + +export function Ring({ + css, + current, + height = '48px', + showLabel = false, + strokeWidth = '8px', + total, + width = '48px', + ...props +}: ProgressProps) { + if (total == 1) { + return null + } + + const progressPercent = total > 0 ? Math.min(Math.round((current / total) * 100) / 100, 1) : 0 + + // TODO: Configurable size + return ( + + + + {showLabel && ( + + {progressPercent * 100} + + )} + + ) +} diff --git a/packages/react/src/components/Progress/index.tsx b/packages/react/src/components/Progress/index.tsx index 57d3722a..1805da16 100644 --- a/packages/react/src/components/Progress/index.tsx +++ b/packages/react/src/components/Progress/index.tsx @@ -1,5 +1,6 @@ export { Bar } from './Bar' export { Dots } from './Dots' export { Fraction } from './Fraction' +export { Ring } from './Ring' export { Segments } from './Segments' export type { ProgressProps } from './ProgressProps' diff --git a/packages/react/src/components/Provider/FrigadeContext.ts b/packages/react/src/components/Provider/FrigadeContext.ts index 7c03a59b..a04ec85e 100644 --- a/packages/react/src/components/Provider/FrigadeContext.ts +++ b/packages/react/src/components/Provider/FrigadeContext.ts @@ -1,9 +1,11 @@ import type { CollectionsRegistryCallback, Frigade } from '@frigade/js' -import { createContext } from 'react' +import { createContext, type Dispatch, type SetStateAction } from 'react' import type { ProviderProps } from './Provider' export interface ProviderContext extends Omit { + currentModal: string | null + setCurrentModal: Dispatch> frigade?: Frigade hasInitialized: boolean registerComponent: (flowId: string, callback?: CollectionsRegistryCallback) => void @@ -13,6 +15,8 @@ export interface ProviderContext extends Omit({ apiKey: '', + currentModal: null, + setCurrentModal: () => {}, navigate: () => {}, hasInitialized: false, registerComponent: () => {}, diff --git a/packages/react/src/components/Provider/ImagePreloader.tsx b/packages/react/src/components/Provider/ImagePreloader.tsx index c66c1ed3..7e0b1236 100644 --- a/packages/react/src/components/Provider/ImagePreloader.tsx +++ b/packages/react/src/components/Provider/ImagePreloader.tsx @@ -16,7 +16,7 @@ export function ImagePreloader() { }) }) })() - }, []) + }, [frigade]) return null } diff --git a/packages/react/src/components/Provider/Provider.tsx b/packages/react/src/components/Provider/Provider.tsx index 68472de0..64bce88a 100644 --- a/packages/react/src/components/Provider/Provider.tsx +++ b/packages/react/src/components/Provider/Provider.tsx @@ -89,6 +89,11 @@ export interface ProviderProps { */ theme?: Theme + /** + * CSS selector to scope Frigade CSS variables into. Defaults to `:root`. + */ + themeSelector?: string + /** * The user ID of the user who is interacting with Frigade. If not provided, Frigade will generate a random guest ID and persist it in local storage. */ @@ -137,12 +142,14 @@ export function Provider({ defaultCollection = true, navigate, theme, + themeSelector = ':root', ...props }: ProviderProps) { const themeOverrides = theme ? createThemeVariables(theme) : {} const registeredComponents = useRef(new Map()) const intervalRef = useRef() const [hasInitialized, setHasInitialized] = useState(false) + const [currentModal, setCurrentModal] = useState(null) const frigade = useMemo(() => { setHasInitialized(false) @@ -162,6 +169,7 @@ export function Provider({ __platformVersion: SDK_VERSION, __platformName: 'React', } as FrigadeConfig) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.userId, props.groupId, props.apiKey]) useEffect(() => { @@ -170,7 +178,7 @@ export function Provider({ config.__flowStateOverrides = props.__flowStateOverrides frigade.reload(config) } - }, [props.__flowStateOverrides]) + }, [frigade, props.__flowStateOverrides]) function batchRegistration() { const batchedFlowIds = [...registeredComponents.current.entries()].map(([flowId, options]) => [ @@ -235,11 +243,13 @@ export function Provider({ return () => { frigade.destroy() } - }, []) + }, [frigade]) return ( - + {defaultCollection && } {children} diff --git a/packages/react/src/components/Spotlight/index.tsx b/packages/react/src/components/Spotlight/index.tsx index a0ed1f2e..133e790d 100644 --- a/packages/react/src/components/Spotlight/index.tsx +++ b/packages/react/src/components/Spotlight/index.tsx @@ -55,9 +55,16 @@ function getComputedRadius(element: ReferenceElement) { export interface SpotlightProps extends OverlayProps { anchor: string + lockScroll?: boolean } -export function Spotlight({ anchor, part, style = {}, ...props }: SpotlightProps) { +export function Spotlight({ + anchor, + lockScroll = true, + part, + style = {}, + ...props +}: SpotlightProps) { const [clipPathCoords, setClipPathCoords] = useState({ maxX: 0, maxY: 0, @@ -105,10 +112,11 @@ export function Spotlight({ anchor, part, style = {}, ...props }: SpotlightProps } catch (invalidSelector) { /* no-op */ } - }, [anchor]) + }, [anchor, refs]) return ( ({ label: `${i}`, value: `${i}` })) + const npsOptions = options || defaultOptions + return (
( + + ), ...fieldTypes, }} modal={false} @@ -33,11 +80,21 @@ export function NPS({ as = Dialog, flowId, fieldTypes, part, ...props }: FormPro // Hides the submit button on the first page ...(!flow || flow.getCurrentStepIndex() == 0 ? { '.fr-form-step-footer': { display: 'none' } } - : {}), + : { + '.fr-card-header': { + // Heuristic to prevent width jumpiness between first and second step + minWidth: npsOptions.length * 51, + }, + }), '.fr-form': { padding: '20px', + '@media (max-width: 660px)': { + minWidth: '100%', + }, + }, + '.fr-nps-field': { '@media (min-width: 660px)': { - minWidth: '600px', + minWidth: 'fit-content', }, minWidth: '100%', }, @@ -47,6 +104,9 @@ export function NPS({ as = Dialog, flowId, fieldTypes, part, ...props }: FormPro gap: '1', }, }, + '.fr-nps': { + maxWidth: 'min-content', + }, ...((props.css as object) ?? {}), }, }} diff --git a/packages/react/src/components/Survey/NPSField.tsx b/packages/react/src/components/Survey/NPSField.tsx index cb77ef1f..e7ec0df0 100644 --- a/packages/react/src/components/Survey/NPSField.tsx +++ b/packages/react/src/components/Survey/NPSField.tsx @@ -3,18 +3,29 @@ import { Flex } from '@/components/Flex' import { FormFieldProps } from '@/components/Form' import { Text } from '@/components/Text' -export function NPSField({ field, fieldData, submit }: FormFieldProps) { - const buttons = [...Array(11)].map((_, i) => { - const Component = field.value === i ? Button.Primary : Button.Secondary +export function NPSField({ + field, + fieldData, + submit, + options, + positiveLabel, + negativeLabel, +}: FormFieldProps & { + options: { label: string; value: string }[] + positiveLabel?: string + negativeLabel?: string +}) { + const buttons = options.map((option) => { + const Component = field.value === option.value ? Button.Primary : Button.Secondary return ( { - field.onChange(i) + field.onChange(option.value) submit() }} - title={`${i}`} + title={option.label} css={{ '.fr-button-title': { fontSize: '15px', @@ -41,10 +52,10 @@ export function NPSField({ field, fieldData, submit }: FormFieldProps) { - {fieldData.negativeLabel ?? `Not likely at all`} + {fieldData.negativeLabel ?? negativeLabel} - {fieldData.positiveLabel ?? `Extremely likely`} + {fieldData.positiveLabel ?? positiveLabel} diff --git a/packages/react/src/components/Survey/index.tsx b/packages/react/src/components/Survey/index.tsx index 51342d4c..3bebe146 100644 --- a/packages/react/src/components/Survey/index.tsx +++ b/packages/react/src/components/Survey/index.tsx @@ -1,2 +1,2 @@ // eslint-disable-next-line react-refresh/only-export-components -- NPS is a valid component name -export { NPS } from './NPS' +export { NPS, type NPSProps } from './NPS' diff --git a/packages/react/src/components/Tooltip/index.tsx b/packages/react/src/components/Tooltip/index.tsx index bd7531d3..959947c1 100644 --- a/packages/react/src/components/Tooltip/index.tsx +++ b/packages/react/src/components/Tooltip/index.tsx @@ -123,7 +123,7 @@ export function Tooltip({ } else { console.debug(`[frigade] Tooltip: No anchor found for query: ${anchor}`) } - }, [anchor]) + }, [anchor, anchorRef]) useEffect(() => { function checkElementForAnchor(element: Element) { @@ -193,7 +193,7 @@ export function Tooltip({ observer.observe(document.querySelector('body'), { childList: true, subtree: true }) return () => observer.disconnect() - }, []) + }, [anchor, anchorRef]) useEffect(() => { const { scrollX, scrollY } = window diff --git a/packages/react/src/components/Tour/Tour.tsx b/packages/react/src/components/Tour/Tour.tsx index 1dd030da..dbc0d775 100644 --- a/packages/react/src/components/Tour/Tour.tsx +++ b/packages/react/src/components/Tour/Tour.tsx @@ -1,17 +1,10 @@ +import { Box } from '@/components/Box' +import { ClientPortal, type ClientPortalProps } from '@/components/ClientPortal/ClientPortal' import { Flow, type FlowPropsWithoutChildren } from '@/components/Flow' import { AlignValue, SideValue, type HintProps } from '@/components/Hint' import { TourStep } from '@/components/Tour/TourStep' -import { useClientPortal } from '@/hooks/useClientPortal' - export interface TourProps extends FlowPropsWithoutChildren, Omit { - /** - * Whether the tour should be completed by the end-user in sequential order. - * If `false`, all steps will be rendered at once. - * Defaults to `true`, which means only one step will be rendered at a time in sequential order. - */ - sequential?: boolean - /** * The alignment of the tooltip relative to the anchor. * Possible values: `after`, `before`, `center`, `end`, `start`. @@ -21,10 +14,24 @@ export interface TourProps extends FlowPropsWithoutChildren, Omit) { + return ( + + + {children} + + + ) +} + +export function Tour({ as, flowId, ...props }: TourProps) { const { onDismiss, onPrimary, onSecondary } = props - return useClientPortal( - - {({ flow, handleDismiss, parentProps: { containerProps }, step }) => { + return ( + + {({ flow, handleDismiss, parentProps, step }) => { + const { + align = 'after', + alignOffset = 0, + autoScroll = false, + container = 'body', + defaultOpen, + lockScroll = true, + modal, + onOpenChange, + open, + part, + sequential = true, + side = 'bottom', + sideOffset = 0, + spotlight, + zIndex = 9999, + ...containerProps + } = parentProps.containerProps as Partial + + const { dismissible } = parentProps + const sequentialStepProps = { align, alignOffset, + autoScroll, dismissible, flow, handleDismiss, modal, onPrimary, onSecondary, + onOpenChange, + open, part, side, sideOffset, spotlight, step, - zIndex: step.props?.zIndex ?? containerProps?.zIndex ?? zIndex, + zIndex, ...(step.props ?? {}), } if (sequential) { return ( - + + + ) } // TODO: Only render spotlight if current step // TODO: Only render modal overlay once - return Array.from(flow.steps.values()) + const tourSteps = Array.from(flow.steps.values()) .filter((currentStep) => { const { blocked, completed, skipped, visible } = currentStep.$state @@ -125,7 +171,7 @@ export function Tour({ const shouldShowSpotlight = spotlight && currentStep.id === step.id - const currentStepZIndex = currentStep.props?.zIndex ?? containerProps?.zIndex ?? zIndex + const currentStepZIndex = currentStep.props?.zIndex ?? zIndex const nonSequentialStepProps = { align, @@ -135,6 +181,8 @@ export function Tour({ handleDismiss: handleDismissStep, onPrimary, onSecondary, + onOpenChange, + open, part, side, sideOffset, @@ -161,13 +209,26 @@ export function Tour({ }} defaultOpen={(defaultOpen || shouldShowSpotlight) ?? false} key={`${currentStep.id}-${shouldShowSpotlight}`} + lockScroll={lockScroll} step={currentStep} {...nonSequentialStepProps} /> ) }) + + return ( + + {tourSteps} + + ) }} - , - 'body' + ) } diff --git a/packages/react/src/components/Tour/TourStep.tsx b/packages/react/src/components/Tour/TourStep.tsx index d337694b..cb050950 100644 --- a/packages/react/src/components/Tour/TourStep.tsx +++ b/packages/react/src/components/Tour/TourStep.tsx @@ -7,6 +7,8 @@ import * as Progress from '@/components/Progress' import { useStepHandlers } from '@/hooks/useStepHandlers' +import { getVideoProps } from '@/components/Media/videoProps' + const fadeIn = keyframes` from { opacity: 0; @@ -27,10 +29,12 @@ export interface TourStepProps export function TourStep({ align, alignOffset, + autoScroll, defaultOpen, dismissible, flow, handleDismiss, + lockScroll, modal, onPrimary, onSecondary, @@ -46,6 +50,8 @@ export function TourStep({ onSecondary, }) + const { videoProps, otherProps } = getVideoProps(props) + const primaryButtonTitle = step.primaryButton?.title ?? step.primaryButtonTitle const secondaryButtonTitle = step.secondaryButton?.title ?? step.secondaryButtonTitle const disabled = !!step.$state.blocked @@ -55,14 +61,21 @@ export function TourStep({ align={align} alignOffset={alignOffset} anchor={step.selector as string} + autoScroll={autoScroll} data-step-id={step.id} defaultOpen={defaultOpen} + lockScroll={lockScroll} modal={modal} part={part} side={side} sideOffset={sideOffset} spotlight={spotlight} - {...props} + onMount={() => { + if (defaultOpen && !disabled) { + step?.start() + } + }} + {...otherProps} > { + if (!scrollComplete && enabled && element instanceof Element) { + const scrollOptions: ScrollIntoViewOptions = + typeof enabled !== 'boolean' ? enabled : { behavior: 'smooth', block: 'center' } + + /* + * NOTE: "scrollend" event isn't supported widely enough yet :( + * + * We'll listen to a capture-phase "scroll" instead, and when it stops + * bouncing, we can infer that the scroll we initiated is over. + */ + let scrollTimeout: ReturnType + window.addEventListener( + 'scroll', + function scrollHandler() { + clearTimeout(scrollTimeout) + scrollTimeout = setTimeout(() => { + window.removeEventListener('scroll', scrollHandler) + setScrollComplete(true) + }, 100) + }, + true + ) + + element.scrollIntoView(scrollOptions) + } else if (!enabled) { + setScrollComplete(true) + } + }, [enabled, element, scrollComplete]) +} diff --git a/packages/react/src/hooks/useBoundingClientRect.ts b/packages/react/src/hooks/useBoundingClientRect.ts index 1d186d9f..0ac8569b 100644 --- a/packages/react/src/hooks/useBoundingClientRect.ts +++ b/packages/react/src/hooks/useBoundingClientRect.ts @@ -1,20 +1,19 @@ import { useCallback, useLayoutEffect, useState } from 'react' +export const EmptyDOMRect = { + height: 0, + width: 0, + x: 0, + y: 0, + bottom: 0, + top: 0, + right: 0, + left: 0, + toJSON: () => {}, +} + export function useBoundingClientRect() { - const initialRect = - 'DOMRect' in globalThis - ? new DOMRect() - : { - height: 0, - width: 0, - x: 0, - y: 0, - bottom: 0, - top: 0, - right: 0, - left: 0, - toJSON: () => {}, - } + const initialRect = 'DOMRect' in globalThis ? new DOMRect() : EmptyDOMRect const [rect, setRect] = useState(initialRect) const [node, setNode] = useState(null) diff --git a/packages/react/src/hooks/useClientPortal.ts b/packages/react/src/hooks/useClientPortal.ts index b8d13c71..404f53a4 100644 --- a/packages/react/src/hooks/useClientPortal.ts +++ b/packages/react/src/hooks/useClientPortal.ts @@ -13,7 +13,7 @@ export function useClientPortal( containerRef.current = typeof container === 'string' ? document.querySelector(container) : container setMounted(true) - }, []) + }, [container]) return mounted ? createPortal(children, containerRef.current, key) : null } diff --git a/packages/react/src/hooks/useCollection.ts b/packages/react/src/hooks/useCollection.ts index 9e2e7ec5..022b644b 100644 --- a/packages/react/src/hooks/useCollection.ts +++ b/packages/react/src/hooks/useCollection.ts @@ -24,13 +24,13 @@ export function useCollection(collectionId?: string): { flow: frigade?.getFlowSync(item.flowId), })) ?? [] - const flowId = enrichedFlows.find(({ flow }) => flow.isVisible)?.flowId + const flowId = enrichedFlows.find(({ flow }) => flow?.isVisible)?.flowId const { flow } = useFlow(flowId) useEffect(() => { frigade?.registerCollection(collectionId) - }, [collectionId]) + }, [collectionId, frigade]) return { collection, diff --git a/packages/react/src/hooks/useCollections.ts b/packages/react/src/hooks/useCollections.ts index 86b89191..75035b22 100644 --- a/packages/react/src/hooks/useCollections.ts +++ b/packages/react/src/hooks/useCollections.ts @@ -1,41 +1,44 @@ -import { useCallback, useContext, useState, useSyncExternalStore } from 'react' +import { useCallback, useContext, useRef, useState } from 'react' +import { CollectionsList } from '@frigade/js' import { FrigadeContext } from '@/components/Provider' - -import { CollectionsList } from '@frigade/js' +import { useSyncExternalStore } from '@/hooks/useSyncExternalStore' export function useCollections() { const { frigade } = useContext(FrigadeContext) const [, setForceRender] = useState(false) - let debounceTimeout: ReturnType + const debounceTimeout = useRef>() - const subscribe = useCallback((cb: () => void) => { - // TODO: Why is there a noticeable delay when this is commented out? - frigade?.getCollections().then(() => { - cb() - }) + const subscribe = useCallback( + (cb: () => void) => { + // TODO: Why is there a noticeable delay when this is commented out? + frigade?.getCollections().then(() => { + cb() + }) - const handler = () => { - clearTimeout(debounceTimeout) + const handler = () => { + clearTimeout(debounceTimeout.current) - /* - * NOTE: Since React doesn't re-render on deep object diffs, - * we need to gently prod it here by creating a state update. - */ - debounceTimeout = setTimeout(() => { - setForceRender((forceRender) => !forceRender) + /* + * NOTE: Since React doesn't re-render on deep object diffs, + * we need to gently prod it here by creating a state update. + */ + debounceTimeout.current = setTimeout(() => { + setForceRender((forceRender) => !forceRender) - cb() - }, 0) - } + cb() + }, 0) + } - frigade?.onStateChange(handler) + frigade?.on('flow.any', handler) - return () => { - frigade?.removeStateChangeHandler(handler) - } - }, []) + return () => { + frigade?.off('flow.any', handler) + } + }, + [frigade] + ) const getSnapshot = () => { let result = undefined diff --git a/packages/react/src/components/Hint/useFloatingHint.ts b/packages/react/src/hooks/useFloating.ts similarity index 62% rename from packages/react/src/components/Hint/useFloatingHint.ts rename to packages/react/src/hooks/useFloating.ts index daca660c..77a49746 100644 --- a/packages/react/src/components/Hint/useFloatingHint.ts +++ b/packages/react/src/hooks/useFloating.ts @@ -8,27 +8,35 @@ import { shift, useClick, useDismiss, - useFloating, + useFloating as useFloatingUI, type UseFloatingOptions, type UseFloatingReturn, + useFocus, useInteractions, type UseInteractionsReturn, useRole, + useTransitionStatus, } from '@floating-ui/react' -import type { AlignValue, ExtendedPlacement, HintProps } from '@/components/Hint' - import { useMutationAwareAnchor } from '@/components/Hint/useMutationAwareAnchor' -export interface FloatingHintProps extends HintProps { - onOpenChange?: UseFloatingOptions['onOpenChange'] - open: boolean +export type AlignValue = 'after' | 'before' | 'center' | 'end' | 'start' +export type SideValue = 'bottom' | 'left' | 'right' | 'top' +export type ExtendedPlacement = `${SideValue}-${AlignValue}` + +export interface FloatingProps extends UseFloatingOptions { + align?: AlignValue + alignOffset?: number + anchor?: string + side?: SideValue + sideOffset?: number } -export interface FloatingHintReturn extends Partial> { +export interface FloatingReturn extends Omit { placement: ExtendedPlacement getFloatingProps: UseInteractionsReturn['getFloatingProps'] getReferenceProps: UseInteractionsReturn['getReferenceProps'] + status: ReturnType } function getOriginalAlign(align: AlignValue) { @@ -44,31 +52,31 @@ function getOriginalAlign(align: AlignValue) { } } -export function useFloatingHint({ +export function useFloating({ align, alignOffset, anchor, + nodeId, onOpenChange = () => {}, open, side, sideOffset, -}: FloatingHintProps): FloatingHintReturn { +}: FloatingProps): FloatingReturn { const placement = `${side}-${getOriginalAlign(align)}` as Placement + // Handle our added "after" and "before" alignments function offsetMiddleware({ rects }) { const offsets = { alignmentAxis: alignOffset, mainAxis: sideOffset, } - // if align is before or after if (['after', 'before'].includes(align)) { - // if side is bottom or top if (['bottom', 'top'].includes(side)) { - // hOffset + // Offset horizontally offsets.alignmentAxis = alignOffset - rects.floating.width } else { - // vOffset + // Offset vertically offsets.alignmentAxis = alignOffset - rects.floating.height } } @@ -81,32 +89,43 @@ export function useFloatingHint({ floatingStyles, placement: computedPlacement, refs, - } = useFloating({ + ...floatingReturn + } = useFloatingUI({ middleware: [offset(offsetMiddleware, [align, alignOffset, side, sideOffset]), flip(), shift()], + nodeId, onOpenChange, open, placement, whileElementsMounted: autoUpdate, }) - const click = useClick(context) - const dismiss = useDismiss(context, { + const clickHandler = useClick(context) + const dismissHandler = useDismiss(context, { outsidePress: false, }) - const role = useRole(context) - - // Merge all the interactions into prop getters - const { getFloatingProps, getReferenceProps } = useInteractions([click, dismiss, role]) - + const focusProps = useFocus(context) + const roleProps = useRole(context) + const status = useTransitionStatus(context) + + const { getFloatingProps, getReferenceProps } = useInteractions([ + clickHandler, + dismissHandler, + focusProps, + roleProps, + ]) + + /* + * Note: If anchor is passed in as a selector, we'll automatically pass it + * through to refs.setReference If not, we assume that the floating reference + * element is being set manually elsewhere (e.g. Popover.Trigger) + */ const { anchorElement } = useMutationAwareAnchor(anchor) useEffect(() => { if (anchorElement != null) { refs.setReference(anchorElement) - } else { - console.debug(`[frigade] Hint: No anchor found for selector: ${anchor}`) } - }, [anchorElement]) + }, [anchor, anchorElement, refs]) // The flip() middleware might reverse the align prop const finalPlacement = computedPlacement.split('-') @@ -125,5 +144,7 @@ export function useFloatingHint({ floatingStyles, placement: finalPlacement.join('-') as ExtendedPlacement, refs, + status, + ...floatingReturn, } } diff --git a/packages/react/src/hooks/useFlow.ts b/packages/react/src/hooks/useFlow.ts index 2d48a09c..0fb6530c 100644 --- a/packages/react/src/hooks/useFlow.ts +++ b/packages/react/src/hooks/useFlow.ts @@ -1,7 +1,9 @@ -import { type Flow } from '@frigade/js' -import { useCallback, useContext, useState, useSyncExternalStore } from 'react' +import { FlowChangeEvent, type Flow } from '@frigade/js' +import { useCallback, useContext, useEffect, useState } from 'react' import { FrigadeContext } from '@/components/Provider' +import { useSyncExternalStore } from '@/hooks/useSyncExternalStore' +import { logOnce } from '@/shared/log' export interface FlowConfig { variables?: Record @@ -14,7 +16,11 @@ export function useFlow( flow: Flow | undefined isLoading: boolean } { - const { frigade, variables } = useContext(FrigadeContext) + const context = useContext(FrigadeContext) + if (!context || !context.frigade) { + logOnce(`useFlow('${flowId}') must be used in a child of the Frigade Provider`, 'warn') + } + const { frigade, variables } = context ?? {} const [, setForceRender] = useState(false) const subscribe = useCallback( @@ -24,7 +30,7 @@ export function useFlow( cb() }) - const handler = (updatedFlow: Flow) => { + const handler = (_event: FlowChangeEvent, updatedFlow: Flow) => { if (updatedFlow.id !== flowId) { return } @@ -40,10 +46,10 @@ export function useFlow( }, 0) } - frigade?.onStateChange(handler) + frigade?.on('flow.any', handler) return () => { - frigade?.removeStateChangeHandler(handler) + frigade?.off('flow.any', handler) } }, [flowId, frigade] @@ -55,10 +61,12 @@ export function useFlow( () => frigade?.getFlowSync(flowId) ) - flow?.applyVariables({ - ...variables, - ...config?.variables, - }) + useEffect(() => { + flow?.applyVariables({ + ...variables, + ...config?.variables, + }) + }, [config?.variables, flow, flowId, variables]) return { flow, diff --git a/packages/react/src/hooks/useFlowHandlers.ts b/packages/react/src/hooks/useFlowHandlers.ts index 91ccb847..458a0da3 100644 --- a/packages/react/src/hooks/useFlowHandlers.ts +++ b/packages/react/src/hooks/useFlowHandlers.ts @@ -32,17 +32,17 @@ export function useFlowHandlers(flow: Flow, { onComplete, onDismiss }: FlowHandl async function callHandler() { if (flow.isCompleted && lastCompleted.current === false) { + lastCompleted.current = true await onComplete?.(flow) } } callHandler() - lastCompleted.current = flow?.isCompleted return () => { callHandler() } - }, [flow?.isCompleted]) + }, [flow, flow?.isCompleted, onComplete]) return { handleDismiss: useCallback( @@ -56,7 +56,7 @@ export function useFlowHandlers(flow: Flow, { onComplete, onDismiss }: FlowHandl flow.skip() }, - [flow] + [flow, onDismiss] ), } } diff --git a/packages/react/src/hooks/useFrigade.ts b/packages/react/src/hooks/useFrigade.ts index d36a7059..e967f3fa 100644 --- a/packages/react/src/hooks/useFrigade.ts +++ b/packages/react/src/hooks/useFrigade.ts @@ -1,9 +1,15 @@ import { useContext } from 'react' import { FrigadeContext } from '@/components/Provider' +import { logOnce } from '../shared/log' export function useFrigade() { - const { frigade } = useContext(FrigadeContext) + const context = useContext(FrigadeContext) + if (!context || !context.frigade) { + logOnce('useFrigade() must be used in a child of the Frigade Provider', 'warn') + } + + const { frigade } = context return { frigade, isLoading: !frigade?.isReady() } } diff --git a/packages/react/src/hooks/useGroup.ts b/packages/react/src/hooks/useGroup.ts index 9f08556f..9e2b17be 100644 --- a/packages/react/src/hooks/useGroup.ts +++ b/packages/react/src/hooks/useGroup.ts @@ -1,9 +1,15 @@ import { useContext } from 'react' import { FrigadeContext } from '@/components/Provider' +import { logOnce } from '../shared/log' export function useGroup() { - const { frigade } = useContext(FrigadeContext) + const context = useContext(FrigadeContext) + if (!context || !context.frigade) { + logOnce('useGroup() must be used in a child of the Frigade Provider', 'warn') + } + const { frigade } = context ?? {} + const groupId = frigade?.getConfig()?.groupId /** * Sets properties for the current group @@ -35,5 +41,11 @@ export function useGroup() { await frigade.group(groupId, properties) } - return { groupId: frigade?.config?.groupId, setGroupId, addProperties, track } + return { + groupId, + setGroupId, + addProperties, + track, + isLoading: !frigade?.isReady(), + } } diff --git a/packages/react/src/hooks/useModal.ts b/packages/react/src/hooks/useModal.ts index 4342c6af..6d3d6621 100644 --- a/packages/react/src/hooks/useModal.ts +++ b/packages/react/src/hooks/useModal.ts @@ -1,53 +1,70 @@ -import { useCallback, useEffect } from 'react' +import { useCallback, useContext, useEffect } from 'react' import type { Flow } from '@frigade/js' -export const globalModalState: Set = new Set() +import { FrigadeContext } from '@/components/Provider' -export function useModal(flow: Flow, isModal: boolean = true) { - const removeModal = useCallback(() => { - if (globalModalState.has(flow?.id)) { - globalModalState.delete(flow?.id) - } - }, [globalModalState, isModal]) +export function useCheckForModalCollision(flow: Flow, isModal = true) { + const { currentModal, setCurrentModal } = useContext(FrigadeContext) + + const claimLock = useCallback( + (flowId: string) => { + if (isModal && flow?.isVisible) { + setCurrentModal(flowId) + } + }, + [isModal, flow?.isVisible, setCurrentModal] + ) - const registerModal = useCallback(() => { - if (isModal && flow?.isVisible && !globalModalState.has(flow.id)) { - globalModalState.add(flow.id) + const releaseLock = useCallback(() => { + if (flow != null && currentModal === flow?.id) { + setCurrentModal(null) } - }, [globalModalState, isModal]) + }, [currentModal, flow, setCurrentModal]) useEffect(() => { - return () => { - removeModal() + if (flow != null && currentModal === null && flow.isVisible) { + claimLock(flow.id) } - }, []) - useEffect(() => { - registerModal() + return releaseLock + }, [claimLock, currentModal, flow, releaseLock]) - const handleRouteChange = () => { - removeModal() + // Edge case: The current modal may become non-modal while still mounted + useEffect(() => { + if (flow != null && (!isModal || !flow.isVisible)) { + releaseLock() } + }, [flow, isModal, releaseLock]) - window.addEventListener('popstate', handleRouteChange) - window.addEventListener('beforeunload', handleRouteChange) + // No flow? No problem. + if (flow == null) { + return { + hasModalCollision: false, + } + } - return () => { - removeModal() - window.removeEventListener('popstate', handleRouteChange) - window.removeEventListener('beforeunload', handleRouteChange) + // Non-modal and hidden components, by definition, can't collide with modals + if (!isModal || !flow.isVisible) { + return { + hasModalCollision: false, } - }, [registerModal, removeModal]) + } - if (!flow?.isVisible) { - removeModal() - } else { - registerModal() + // We already have the lock, send it + if (currentModal === flow.id) { + return { + hasModalCollision: false, + } } - const currentModal = globalModalState.size > 0 ? globalModalState.values().next().value : null + if (currentModal === null) { + return { + hasModalCollision: false, + } + } + // If we didn't short circuit and didn't have the lock, assume that we're out of lock luck. return { - isCurrentModal: !isModal ? true : currentModal === flow?.id || globalModalState.size == 0, + hasModalCollision: true, } } diff --git a/packages/react/src/hooks/useStepHandlers.ts b/packages/react/src/hooks/useStepHandlers.ts index 1ae1b432..7af5828a 100644 --- a/packages/react/src/hooks/useStepHandlers.ts +++ b/packages/react/src/hooks/useStepHandlers.ts @@ -1,4 +1,4 @@ -import { type SyntheticEvent, useCallback, useContext } from 'react' +import { type SyntheticEvent, useCallback, useContext, useMemo } from 'react' import type { FlowStep, PropertyPayload } from '@frigade/js' @@ -35,18 +35,26 @@ export type StepHandler = ( export function useStepHandlers(step: FlowStep, { onPrimary, onSecondary }: StepHandlerProps = {}) { const { navigate } = useContext(FrigadeContext) - const stepActions = { - 'flow.back': (...args) => step.flow.back(...args), - 'flow.complete': (...args) => step.flow.complete(...args), - 'flow.forward': (...args) => step.flow.forward(...args), - 'flow.restart': () => step.flow.restart(), - 'flow.skip': (...args) => step.flow.skip(...args), - 'flow.start': (...args) => step.flow.start(...args), - 'step.complete': (...args) => step.complete(...args), - 'step.skip': (...args) => step.skip(...args), - 'step.reset': () => step.reset(), - 'step.start': (...args) => step.start(...args), - } + const stepActions = useMemo( + () => + step == null + ? {} + : { + 'flow.back': (properties?: PropertyPayload) => step.flow.back(properties), + 'flow.complete': (properties?: PropertyPayload) => step.flow.complete(properties), + 'flow.forward': (properties?: PropertyPayload) => step.flow.forward(properties), + 'flow.restart': () => step.flow.restart(), + 'flow.skip': (properties?: PropertyPayload) => step.flow.skip(properties), + 'flow.start': (properties?: PropertyPayload) => step.flow.start(properties), + 'step.complete': (properties?: PropertyPayload, optimistic?: boolean) => + step.complete(properties, optimistic), + 'step.skip': (properties?: PropertyPayload, optimistic?: boolean) => + step.skip(properties, optimistic), + 'step.reset': () => step.reset(), + 'step.start': (properties?: PropertyPayload) => step.start(properties), + }, + [step] + ) return { handlePrimary: useCallback( @@ -80,7 +88,7 @@ export function useStepHandlers(step: FlowStep, { onPrimary, onSecondary }: Step return true }, - [step] + [navigate, onPrimary, step, stepActions] ), handleSecondary: useCallback( @@ -116,7 +124,7 @@ export function useStepHandlers(step: FlowStep, { onPrimary, onSecondary }: Step return true }, - [step] + [navigate, onSecondary, step, stepActions] ), } } diff --git a/packages/react/src/hooks/useSyncExternalStore.ts b/packages/react/src/hooks/useSyncExternalStore.ts new file mode 100644 index 00000000..7ee82e5b --- /dev/null +++ b/packages/react/src/hooks/useSyncExternalStore.ts @@ -0,0 +1,10 @@ +import * as React from 'react' +import * as UseSyncExternalStoreShim from 'use-sync-external-store/shim/index.js' + +// useSyncExternalStore doesn't exist in React 17, so shim it if necessary +const useSyncExternalStore = + 'useSyncExternalStore' in React + ? React.useSyncExternalStore + : UseSyncExternalStoreShim.useSyncExternalStore + +export { useSyncExternalStore } diff --git a/packages/react/src/hooks/useUser.ts b/packages/react/src/hooks/useUser.ts index 7c485224..ae19ef77 100644 --- a/packages/react/src/hooks/useUser.ts +++ b/packages/react/src/hooks/useUser.ts @@ -1,9 +1,15 @@ import { useContext } from 'react' import { FrigadeContext } from '@/components/Provider' +import { logOnce } from '../shared/log' export function useUser() { - const { userId, frigade } = useContext(FrigadeContext) + const context = useContext(FrigadeContext) + if (!context || !context.frigade) { + logOnce('useUser() must be used in a child of the Frigade Provider', 'warn') + } + const { frigade } = context ?? {} + const userId = frigade?.config.userId /** * Adds properties for the current user @@ -22,5 +28,5 @@ export function useUser() { await frigade.track(eventName, properties) } - return { userId, addProperties, track } + return { userId, addProperties, track, isLoading: !frigade?.isReady() } } diff --git a/packages/react/src/hooks/useVisibility.ts b/packages/react/src/hooks/useVisibility.ts new file mode 100644 index 00000000..79e095b8 --- /dev/null +++ b/packages/react/src/hooks/useVisibility.ts @@ -0,0 +1,37 @@ +import { ClientRectObject } from '@floating-ui/react' +import { useEffect, useState } from 'react' + +import { EmptyDOMRect } from '@/hooks/useBoundingClientRect' + +export function useVisibility(element: Element | null) { + const [isIntersecting, setIsIntersecting] = useState(false) + const [clientRect, setClientRect] = useState(EmptyDOMRect) + + const hasDimensions = clientRect.height > 0 && clientRect.width > 0 + + useEffect(() => { + if (element == null) { + return + } + + const observer = new IntersectionObserver( + (entries) => { + const el = entries[0] + + setClientRect(el.boundingClientRect) + setIsIntersecting(el.isIntersecting) + }, + { root: null } + ) + + observer.observe(element) + + return () => { + observer.disconnect() + } + }, [element]) + + return { + isVisible: isIntersecting && hasDimensions, + } +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 5b17d838..822c32f7 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -6,6 +6,7 @@ export { Box, type BoxProps } from './components/Box' export { Button, type ButtonProps } from './components/Button' export { Card, type CardProps, type CardHeaderProps } from './components/Card' export * as Checklist from './components/Checklist' +export * from './components/ClientPortal' export type { CollapsibleProps, CollapsibleStepProps } from './components/Checklist' export { Collection } from './components/Collection' export { Dialog, type DialogProps } from './components/Dialog' @@ -25,7 +26,9 @@ export { RadioField } from './components/Form/fields/RadioField' export { Label } from './components/Form/fields/Label' export { BaseField } from './components/Form/fields/BaseField' export { Media, Image, Video } from './components/Media' - +export { type NPSProps } from './components/Survey/NPS' +export { Ping } from './components/Ping' +export * as Popover from './components/Popover' export * as Progress from './components/Progress' export { ProgressBadge, type ProgressBadgeProps } from './components/ProgressBadge' export { Provider, type ProviderProps } from './components/Provider' @@ -44,6 +47,7 @@ export * as FrigadeJS from '@frigade/js' export { themeVariables, type Theme } from './shared/theme' export { tokens, type Tokens } from './shared/tokens' +export { useAutoScroll } from './hooks/useAutoScroll' export { useBoundingClientRect } from './hooks/useBoundingClientRect' export { useFlow, type FlowConfig } from './hooks/useFlow' export { @@ -52,7 +56,6 @@ export { type FlowHandlerProp, type FlowHandlerProps, } from './hooks/useFlowHandlers' -export { useModal } from './hooks/useModal' export { useStepHandlers, type StepHandler, @@ -62,3 +65,6 @@ export { export { useFrigade } from './hooks/useFrigade' export { useUser } from './hooks/useUser' export { useGroup } from './hooks/useGroup' + +// TEMP: Remove this, used for testing Storybook +export * as FloatingUI from '@floating-ui/react' diff --git a/packages/react/src/shared/log.ts b/packages/react/src/shared/log.ts new file mode 100644 index 00000000..d02f6235 --- /dev/null +++ b/packages/react/src/shared/log.ts @@ -0,0 +1,13 @@ +const logOnce = (message: string, type: 'log' | 'warn' | 'error' = 'log') => { + const key = `__frigade_logged_${message}` + + if (globalThis[key as keyof typeof globalThis]) { + return + } + + // @ts-expect-error: globalThis is not typed + globalThis[key as keyof typeof globalThis] = true + console[type](message) +} + +export { logOnce } diff --git a/packages/react/src/shared/tokens/palette.ts b/packages/react/src/shared/tokens/palette.ts index 0e138b32..45fefd4e 100644 --- a/packages/react/src/shared/tokens/palette.ts +++ b/packages/react/src/shared/tokens/palette.ts @@ -82,7 +82,7 @@ export const palette = { blue500: scaledColors.blue['500'], blue800: scaledColors.blue['800'], blue900: scaledColors.blue['900'], - green400: scaledColors.blue['400'], + green400: scaledColors.green['400'], green500: scaledColors.green['500'], green800: scaledColors.green['800'], red500: scaledColors.red['500'], diff --git a/packages/react/src/shared/tokens/semantic.ts b/packages/react/src/shared/tokens/semantic.ts index 947c0a91..0c59838b 100644 --- a/packages/react/src/shared/tokens/semantic.ts +++ b/packages/react/src/shared/tokens/semantic.ts @@ -154,10 +154,10 @@ export const semantic: SemanticColors = { surface: colorVar('primary-500'), active: { - background: colorVar('primary-400'), - border: colorVar('primary-400'), + background: colorVar('primary-300'), + border: colorVar('primary-300'), foreground: colorVar('white'), - surface: colorVar('primary-400'), + surface: colorVar('primary-300'), }, focus: { background: colorVar('primary-500'), diff --git a/packages/react/src/version.ts b/packages/react/src/version.ts index c184cb18..030afd49 100644 --- a/packages/react/src/version.ts +++ b/packages/react/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '2.5.17'; \ No newline at end of file +export const SDK_VERSION = '2.10.1'; \ No newline at end of file diff --git a/packages/reactv1/package.json b/packages/reactv1/package.json index a559f6c1..56d80216 100644 --- a/packages/reactv1/package.json +++ b/packages/reactv1/package.json @@ -1,6 +1,6 @@ { "name": "@frigade/reactv1", - "version": "1.38.51", + "version": "1.38.52", "description": "Build better product onboarding, faster.", "main": "./dist/index.js", "private": true, @@ -10,7 +10,6 @@ ], "scripts": { "clean": "rimraf ./dist", - "test": "jest", "lint": "eslint --fix --ext .ts,.tsx .", "build": "yarn clean && tsup", "local-release": "tsup", @@ -90,7 +89,7 @@ "homepage": "https://github.com/FrigadeHQ/javascript/tree/main/packages/react#readme", "dependencies": { "core-js-pure": "^3.32.0", - "dompurify": "^3.1.3", + "dompurify": "^3.2.4", "react-portal": "^4.2.2", "styled-components": "5.3.6", "styled-system": "^5.1.5", diff --git a/packages/reactv1/src/api/flows.ts b/packages/reactv1/src/api/flows.ts index 068ec165..720ff892 100644 --- a/packages/reactv1/src/api/flows.ts +++ b/packages/reactv1/src/api/flows.ts @@ -529,36 +529,43 @@ export function useFlows() { return true } + const getStepStatus = useCallback( + (flowId: string, stepId: string): StepActionType | null => { + const maybeFlowResponse = getStepStateForFlow(flowId, stepId) - function getStepStatus(flowId: string, stepId: string): StepActionType | null { - const maybeFlowResponse = getStepStateForFlow(flowId, stepId) - - if (isLoadingUserFlowStateData) { - return null - } - - return (maybeFlowResponse ? maybeFlowResponse.actionType : NOT_STARTED_STEP) as StepActionType - } + if (isLoadingUserFlowStateData) { + return null + } - function isStepBlocked(flowId: string, stepId: string): boolean { - const maybeFlowResponse = getStepStateForFlow(flowId, stepId) + return (maybeFlowResponse ? maybeFlowResponse.actionType : NOT_STARTED_STEP) as StepActionType + }, + [userFlowStatesData, isLoadingUserFlowStateData] + ) + const isStepBlocked = useCallback( + (flowId: string, stepId: string): boolean => { + const maybeFlowResponse = getStepStateForFlow(flowId, stepId) - if (!maybeFlowResponse) { - return false - } + if (!maybeFlowResponse) { + return false + } - return maybeFlowResponse.blocked - } + return maybeFlowResponse.blocked + }, + [userFlowStatesData, isLoadingUserFlowStateData] + ) - function isStepHidden(flowId: string, stepId: string): boolean { - const maybeFlowResponse = getStepStateForFlow(flowId, stepId) + const isStepHidden = useCallback( + (flowId: string, stepId: string): boolean => { + const maybeFlowResponse = getStepStateForFlow(flowId, stepId) - if (!maybeFlowResponse) { - return false - } + if (!maybeFlowResponse) { + return false + } - return maybeFlowResponse.hidden - } + return maybeFlowResponse.hidden + }, + [userFlowStatesData, isLoadingUserFlowStateData] + ) function getStepStateForFlow(flowId: string, stepId: string): FlowResponse | null { if (isLoadingUserFlowStateData) { @@ -615,79 +622,96 @@ export function useFlows() { return total === 0 ? undefined : completed / total } - function getFlowStatus(flowId: string) { - const userFlowState = userFlowStatesData?.find((f) => f.flowId === flowId) - if (!userFlowState) { - return null - } - return userFlowState.flowState - } - - function getNumberOfStepsCompleted(flowId: string): number { - const steps = getFlowSteps(flowId) - if (steps.length === 0) { - return 0 - } + const getFlowStatus = useCallback( + (flowId: string) => { + const userFlowState = userFlowStatesData?.find((f) => f.flowId === flowId) + if (!userFlowState) { + return null + } + return userFlowState.flowState + }, + [userFlowStatesData] + ) + const getNumberOfStepsCompleted = useCallback( + (flowId: string): number => { + const steps = getFlowSteps(flowId) + if (steps.length === 0) { + return 0 + } - const completedSteps = steps.filter((s) => getStepStatus(flowId, s.id) === COMPLETED_STEP) + const completedSteps = steps.filter((s) => getStepStatus(flowId, s.id) === COMPLETED_STEP) - return completedSteps.length - } + return completedSteps.length + }, + [userFlowStatesData, isLoadingUserFlowStateData] + ) - function getNumberOfSteps(flowId: string) { - return getFlowSteps(flowId).length - } + const getNumberOfSteps = useCallback( + (flowId: string) => { + return getFlowSteps(flowId).length + }, + [userFlowStatesData, isLoadingUserFlowStateData] + ) /** * Generic method for getting the raw Flow data as a Javascript object. * For typescript, pass in T to get the correct type. * @param flowId */ - function getFlowData(flowId: string): T | null { - const maybeFlow = flows.find((f) => f.slug === flowId) - if (!maybeFlow) { - return null - } - if (flowDataOverrides && flowDataOverrides[flowId]) { - maybeFlow.data = flowDataOverrides[flowId] - } - return safeParse(maybeFlow.data) - } - - function targetingLogicShouldHideFlow(flow: Flow) { - if (readonly) { - return false - } - if (isLoadingUserFlowStateData) { - return true - } - if (shouldGracefullyDegrade) { - return true - } - if (flow?.targetingLogic && userFlowStatesData) { - // Iterate through matching userFlowState for the flow and if shouldTrigger is true, return false - const matchingUserFlowState = userFlowStatesData.find((ufs) => ufs.flowId === flow.slug) - if (matchingUserFlowState) { - return matchingUserFlowState.shouldTrigger === false + const getFlowData = useCallback( + (flowId: string): T | null => { + const maybeFlow = flows.find((f) => f.slug === flowId) + if (!maybeFlow) { + return null } - } - if (flow?.targetingLogic && userId && userId.startsWith('guest_')) { - return true - } + if (flowDataOverrides && flowDataOverrides[flowId]) { + maybeFlow.data = flowDataOverrides[flowId] + } + return safeParse(maybeFlow.data) + }, + [flows, flowDataOverrides] + ) - return false - } + const targetingLogicShouldHideFlow = useCallback( + (flow: Flow) => { + if (readonly) { + return false + } + if (isLoadingUserFlowStateData) { + return true + } + if (shouldGracefullyDegrade) { + return true + } + if (flow?.targetingLogic && userFlowStatesData) { + // Iterate through matching userFlowState for the flow and if shouldTrigger is true, return false + const matchingUserFlowState = userFlowStatesData.find((ufs) => ufs.flowId === flow.slug) + if (matchingUserFlowState) { + return matchingUserFlowState.shouldTrigger === false + } + } + if (flow?.targetingLogic && userId && userId.startsWith('guest_')) { + return true + } - function isFlowAvailableToUser(flowId: string) { - const flow = getFlow(flowId) - if (!flow) { return false - } - if (flow.active === false) { - return false - } - return !targetingLogicShouldHideFlow(getFlow(flowId)) - } + }, + [readonly, isLoadingUserFlowStateData, shouldGracefullyDegrade, userFlowStatesData, userId] + ) + + const isFlowAvailableToUser = useCallback( + (flowId: string) => { + const flow = getFlow(flowId) + if (!flow) { + return false + } + if (flow.active === false) { + return false + } + return !targetingLogicShouldHideFlow(getFlow(flowId)) + }, + [userId, organizationId, userFlowStatesData] + ) function refresh() { if (userId) { diff --git a/yarn.lock b/yarn.lock index f4daf985..354d4400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,10 +12,10 @@ __metadata: languageName: node linkType: hard -"@adobe/css-tools@npm:^4.3.2": - version: 4.4.0 - resolution: "@adobe/css-tools@npm:4.4.0" - checksum: 10/9c6315fe9efa5075d6ddb6ded7a1424bc9c41a01f2314b6bdcc368723985fe161008d03ddcc2b27b2da50cb9c14190fbce965d15cefe5f9a31bdd43f35b52115 +"@adobe/css-tools@npm:^4.4.0": + version: 4.4.1 + resolution: "@adobe/css-tools@npm:4.4.1" + checksum: 10/a0ea05517308593a52728936a833b1075c4cf1a6b68baaea817063f34e75faa1dba1209dd285003c4f8072804227dfa563e7e903f72ae2d39cb520aaee3f4bcc languageName: node linkType: hard @@ -80,6 +80,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.26.2": + version: 7.26.2 + resolution: "@babel/code-frame@npm:7.26.2" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.25.9" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10/db2c2122af79d31ca916755331bb4bac96feb2b334cdaca5097a6b467fdd41963b89b14b6836a14f083de7ff887fc78fa1b3c10b14e743d33e12dbfe5ee3d223 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": version: 7.23.5 resolution: "@babel/compat-data@npm:7.23.5" @@ -571,6 +582,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-string-parser@npm:7.25.9" + checksum: 10/c28656c52bd48e8c1d9f3e8e68ecafd09d949c57755b0d353739eb4eae7ba4f7e67e92e4036f1cd43378cc1397a2c943ed7bcaf5949b04ab48607def0258b775 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -585,6 +603,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 10/3f9b649be0c2fd457fa1957b694b4e69532a668866b8a0d81eabfa34ba16dbf3107b39e0e7144c55c3c652bf773ec816af8df4a61273a2bb4eb3145ca9cf478e + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": version: 7.23.5 resolution: "@babel/helper-validator-option@npm:7.23.5" @@ -621,24 +646,13 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/helpers@npm:7.23.9" - dependencies: - "@babel/template": "npm:^7.23.9" - "@babel/traverse": "npm:^7.23.9" - "@babel/types": "npm:^7.23.9" - checksum: 10/dd56daac8bbd7ed174bb00fd185926fd449e591d9a00edaceb7ac6edbdd7a8db57e2cb365b4fafda382201752789ced2f7ae010f667eab0f198a4571cda4d2c5 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.25.0": - version: 7.25.6 - resolution: "@babel/helpers@npm:7.25.6" +"@babel/helpers@npm:^7.23.9, @babel/helpers@npm:^7.25.0": + version: 7.26.10 + resolution: "@babel/helpers@npm:7.26.10" dependencies: - "@babel/template": "npm:^7.25.0" - "@babel/types": "npm:^7.25.6" - checksum: 10/43abc8d017b754619aa189d05e2bdb54aaf44f03ec0439e89b3e7c180d538adb01ce9014a1689f632a7e8b17655c72bfac0a92268476eec708b41d3ba0a65296 + "@babel/template": "npm:^7.26.9" + "@babel/types": "npm:^7.26.10" + checksum: 10/664146257974ccf064b42bd99b1b85717cce2bcebc5068273e13b230ee8bd98d87187c3783706758d76b678ebe0d2f48150eaa6cffc4f77af1342a78ec1cf57a languageName: node linkType: hard @@ -685,6 +699,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.26.9": + version: 7.26.10 + resolution: "@babel/parser@npm:7.26.10" + dependencies: + "@babel/types": "npm:^7.26.10" + bin: + parser: ./bin/babel-parser.js + checksum: 10/3f87781f46795ba72448168061d9e99c394fdf9cd4aa3ddf053a06334247da4d25d0923ccc89195937d3360d384cee181e99711763c1e8fe81d4f17ee22541fc + languageName: node + linkType: hard + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.3": version: 7.25.3 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.25.3" @@ -2607,15 +2632,6 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.9.2": - version: 7.25.6 - resolution: "@babel/runtime@npm:7.25.6" - dependencies: - regenerator-runtime: "npm:^0.14.0" - checksum: 10/0c4134734deb20e1005ffb9165bf342e1074576621b246d8e5e41cc7cb315a885b7d98950fbf5c63619a2990a56ae82f444d35fe8c4691a0b70c2fe5673667dc - languageName: node - linkType: hard - "@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3": version: 7.23.9 resolution: "@babel/template@npm:7.23.9" @@ -2638,6 +2654,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.26.9": + version: 7.26.9 + resolution: "@babel/template@npm:7.26.9" + dependencies: + "@babel/code-frame": "npm:^7.26.2" + "@babel/parser": "npm:^7.26.9" + "@babel/types": "npm:^7.26.9" + checksum: 10/240288cebac95b1cc1cb045ad143365643da0470e905e11731e63280e43480785bd259924f4aea83898ef68e9fa7c176f5f2d1e8b0a059b27966e8ca0b41a1b6 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.9, @babel/traverse@npm:^7.4.5": version: 7.23.9 resolution: "@babel/traverse@npm:7.23.9" @@ -2693,6 +2720,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.10, @babel/types@npm:^7.26.9": + version: 7.26.10 + resolution: "@babel/types@npm:7.26.10" + dependencies: + "@babel/helper-string-parser": "npm:^7.25.9" + "@babel/helper-validator-identifier": "npm:^7.25.9" + checksum: 10/6b4f24ee77af853c2126eaabb65328cd44a7d6f439685131cf929c30567e01b6ea2e5d5653b2c304a09c63a5a6199968f0e27228a007acf35032036d79a9dee6 + languageName: node + linkType: hard + "@base2/pretty-print-object@npm:1.0.1": version: 1.0.1 resolution: "@base2/pretty-print-object@npm:1.0.1" @@ -2956,42 +2993,42 @@ __metadata: languageName: node linkType: hard -"@emotion/babel-plugin@npm:^11.11.0": - version: 11.11.0 - resolution: "@emotion/babel-plugin@npm:11.11.0" +"@emotion/babel-plugin@npm:^11.13.5": + version: 11.13.5 + resolution: "@emotion/babel-plugin@npm:11.13.5" dependencies: "@babel/helper-module-imports": "npm:^7.16.7" "@babel/runtime": "npm:^7.18.3" - "@emotion/hash": "npm:^0.9.1" - "@emotion/memoize": "npm:^0.8.1" - "@emotion/serialize": "npm:^1.1.2" + "@emotion/hash": "npm:^0.9.2" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/serialize": "npm:^1.3.3" babel-plugin-macros: "npm:^3.1.0" convert-source-map: "npm:^1.5.0" escape-string-regexp: "npm:^4.0.0" find-root: "npm:^1.1.0" source-map: "npm:^0.5.7" stylis: "npm:4.2.0" - checksum: 10/8de017666838fc06b1a961d7a49b4e6dc0c83dbb064ea33512bae056594f0811a87e3242ef90fa2aa49fc080fab1cc7af536e7aee9398eaca7a1fc020d2dd527 + checksum: 10/cd310568314d886ca328e504f84c4f7f9c7f092ea34a2b43fdb61f84665bf301ba2ef49e0fd1e7ded3d81363d9bbefbb32674ce88b317cfb64db2b65e5ff423f languageName: node linkType: hard -"@emotion/cache@npm:^11.11.0": - version: 11.11.0 - resolution: "@emotion/cache@npm:11.11.0" +"@emotion/cache@npm:^11.14.0": + version: 11.14.0 + resolution: "@emotion/cache@npm:11.14.0" dependencies: - "@emotion/memoize": "npm:^0.8.1" - "@emotion/sheet": "npm:^1.2.2" - "@emotion/utils": "npm:^1.2.1" - "@emotion/weak-memoize": "npm:^0.3.1" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/sheet": "npm:^1.4.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" stylis: "npm:4.2.0" - checksum: 10/ef29756247dafb87168b4ffb76ee60feb06b8a1016323ecb1d3ba8aed3f4300ca10049bedbfe83aa11e0d81e616c328002a9d50020ebb3af6e4f5337a785c1fe + checksum: 10/52336b28a27b07dde8fcdfd80851cbd1487672bbd4db1e24cca1440c95d8a6a968c57b0453c2b7c88d9b432b717f99554dbecc05b5cdef27933299827e69fd8e languageName: node linkType: hard -"@emotion/hash@npm:^0.9.1": - version: 0.9.1 - resolution: "@emotion/hash@npm:0.9.1" - checksum: 10/716e17e48bf9047bf9383982c071de49f2615310fb4e986738931776f5a823bc1f29c84501abe0d3df91a3803c80122d24e28b57351bca9e01356ebb33d89876 +"@emotion/hash@npm:^0.9.2": + version: 0.9.2 + resolution: "@emotion/hash@npm:0.9.2" + checksum: 10/379bde2830ccb0328c2617ec009642321c0e009a46aa383dfbe75b679c6aea977ca698c832d225a893901f29d7b3eef0e38cf341f560f6b2b56f1ff23c172387 languageName: node linkType: hard @@ -3011,44 +3048,51 @@ __metadata: languageName: node linkType: hard -"@emotion/react@npm:^11.11.4": - version: 11.11.4 - resolution: "@emotion/react@npm:11.11.4" +"@emotion/memoize@npm:^0.9.0": + version: 0.9.0 + resolution: "@emotion/memoize@npm:0.9.0" + checksum: 10/038132359397348e378c593a773b1148cd0cf0a2285ffd067a0f63447b945f5278860d9de718f906a74c7c940ba1783ac2ca18f1c06a307b01cc0e3944e783b1 + languageName: node + linkType: hard + +"@emotion/react@npm:^11.14.0": + version: 11.14.0 + resolution: "@emotion/react@npm:11.14.0" dependencies: "@babel/runtime": "npm:^7.18.3" - "@emotion/babel-plugin": "npm:^11.11.0" - "@emotion/cache": "npm:^11.11.0" - "@emotion/serialize": "npm:^1.1.3" - "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.0.1" - "@emotion/utils": "npm:^1.2.1" - "@emotion/weak-memoize": "npm:^0.3.1" + "@emotion/babel-plugin": "npm:^11.13.5" + "@emotion/cache": "npm:^11.14.0" + "@emotion/serialize": "npm:^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks": "npm:^1.2.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" hoist-non-react-statics: "npm:^3.3.1" peerDependencies: react: ">=16.8.0" peerDependenciesMeta: "@types/react": optional: true - checksum: 10/e7da3a1ddc1d72a4179010bdfd17423c13b1a77bf83a8b18271e919fd382d08c62dc2313ed5347acfd1ef85bb1bae8932597647a986e8a1ea1462552716cd495 + checksum: 10/3356c1d66f37f4e7abf88a2be843f6023b794b286c9c99a0aaf1cd1b2b7c50f8d80a2ef77183da737de70150f638e698ff4a2a38ab2d922f868615f1d5761c37 languageName: node linkType: hard -"@emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.1.3": - version: 1.1.3 - resolution: "@emotion/serialize@npm:1.1.3" +"@emotion/serialize@npm:^1.3.3": + version: 1.3.3 + resolution: "@emotion/serialize@npm:1.3.3" dependencies: - "@emotion/hash": "npm:^0.9.1" - "@emotion/memoize": "npm:^0.8.1" - "@emotion/unitless": "npm:^0.8.1" - "@emotion/utils": "npm:^1.2.1" + "@emotion/hash": "npm:^0.9.2" + "@emotion/memoize": "npm:^0.9.0" + "@emotion/unitless": "npm:^0.10.0" + "@emotion/utils": "npm:^1.4.2" csstype: "npm:^3.0.2" - checksum: 10/48d88923663273ae70359bc1a1f30454136716cbe0ddd9664be08e257ce56acedab911f125b627627358e37c9f450bbac3ea09b534ef42f9f67325d47b1e2a7b + checksum: 10/44a2e06fc52dba177d9cf720f7b2c5d45ee4c0d9c09b78302d9a625e758d728ef3ae26f849237fec6f70e9eeb7d87e45a65028e944dc1f877df97c599f1cdaee languageName: node linkType: hard -"@emotion/sheet@npm:^1.2.2": - version: 1.2.2 - resolution: "@emotion/sheet@npm:1.2.2" - checksum: 10/cc46b20ef7273dc28de889927ae1498f854be2890905745fcc3154fbbacaa54df1e28c3d89ff3339c2022782c78933f51955bb950d105d5a219576db1eadfb7a +"@emotion/sheet@npm:^1.4.0": + version: 1.4.0 + resolution: "@emotion/sheet@npm:1.4.0" + checksum: 10/8ac6e9bf6b373a648f26ae7f1c24041038524f4c72f436f4f8c4761c665e58880c3229d8d89b1f7a4815dd8e5b49634d03e60187cb6f93097d7f7c1859e869d5 languageName: node linkType: hard @@ -3059,6 +3103,13 @@ __metadata: languageName: node linkType: hard +"@emotion/unitless@npm:^0.10.0": + version: 0.10.0 + resolution: "@emotion/unitless@npm:0.10.0" + checksum: 10/6851c16edce01c494305f43b2cad7a26b939a821131b7c354e49b8e3b012c8810024755b0f4a03ef51117750309e55339825a97bd10411fb3687e68904769106 + languageName: node + linkType: hard + "@emotion/unitless@npm:^0.7.4": version: 0.7.5 resolution: "@emotion/unitless@npm:0.7.5" @@ -3066,14 +3117,7 @@ __metadata: languageName: node linkType: hard -"@emotion/unitless@npm:^0.8.1": - version: 0.8.1 - resolution: "@emotion/unitless@npm:0.8.1" - checksum: 10/918f73c46ac0b7161e3c341cc07d651ce87e31ab1695e74b12adb7da6bb98dfbff8c69cf68a4e40d9eb3d820ca055dc1267aeb3007927ce88f98b885bf729b63 - languageName: node - linkType: hard - -"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.0, @emotion/use-insertion-effect-with-fallbacks@npm:^1.0.1": +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.0": version: 1.0.1 resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.0.1" peerDependencies: @@ -3082,23 +3126,32 @@ __metadata: languageName: node linkType: hard -"@emotion/utils@npm:^1.2.1": - version: 1.2.1 - resolution: "@emotion/utils@npm:1.2.1" - checksum: 10/472fa529c64a13edff80aa11698092e8841c1ffb5001c739d84eb9d0fdd6d8e1cd1848669310578ccfa6383b8601132eca54f8749fca40af85d21fdfc9b776c4 +"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0": + version: 1.2.0 + resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10/2374999db8d53ef661d61ed1026c42a849632e4f03826f7eba0314c1d92ae342161d737f5045453aa46dd4008e13ccefeba68d3165b667dfad8e5784fcb0c643 languageName: node linkType: hard -"@emotion/weak-memoize@npm:^0.3.1": - version: 0.3.1 - resolution: "@emotion/weak-memoize@npm:0.3.1" - checksum: 10/b2be47caa24a8122622ea18cd2d650dbb4f8ad37b636dc41ed420c2e082f7f1e564ecdea68122b546df7f305b159bf5ab9ffee872abd0f052e687428459af594 +"@emotion/utils@npm:^1.4.2": + version: 1.4.2 + resolution: "@emotion/utils@npm:1.4.2" + checksum: 10/e5f3b8bca066b3361a7ad9064baeb9d01ed1bf51d98416a67359b62cb3affec6bb0249802c4ed11f4f8030f93cc4b67506909420bdb110adec6983d712897208 languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/aix-ppc64@npm:0.21.5" +"@emotion/weak-memoize@npm:^0.4.0": + version: 0.4.0 + resolution: "@emotion/weak-memoize@npm:0.4.0" + checksum: 10/db5da0e89bd752c78b6bd65a1e56231f0abebe2f71c0bd8fc47dff96408f7065b02e214080f99924f6a3bfe7ee15afc48dad999d76df86b39b16e513f7a94f52 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/aix-ppc64@npm:0.24.2" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -3117,9 +3170,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-arm64@npm:0.21.5" +"@esbuild/android-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-arm64@npm:0.24.2" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -3138,9 +3191,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-arm@npm:0.21.5" +"@esbuild/android-arm@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-arm@npm:0.24.2" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -3159,9 +3212,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/android-x64@npm:0.21.5" +"@esbuild/android-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/android-x64@npm:0.24.2" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -3180,9 +3233,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/darwin-arm64@npm:0.21.5" +"@esbuild/darwin-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/darwin-arm64@npm:0.24.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -3201,9 +3254,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/darwin-x64@npm:0.21.5" +"@esbuild/darwin-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/darwin-x64@npm:0.24.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -3222,9 +3275,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/freebsd-arm64@npm:0.21.5" +"@esbuild/freebsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/freebsd-arm64@npm:0.24.2" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -3243,9 +3296,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/freebsd-x64@npm:0.21.5" +"@esbuild/freebsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/freebsd-x64@npm:0.24.2" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -3264,9 +3317,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-arm64@npm:0.21.5" +"@esbuild/linux-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-arm64@npm:0.24.2" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -3285,9 +3338,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-arm@npm:0.21.5" +"@esbuild/linux-arm@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-arm@npm:0.24.2" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -3306,9 +3359,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-ia32@npm:0.21.5" +"@esbuild/linux-ia32@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-ia32@npm:0.24.2" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -3327,9 +3380,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-loong64@npm:0.21.5" +"@esbuild/linux-loong64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-loong64@npm:0.24.2" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -3348,9 +3401,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-mips64el@npm:0.21.5" +"@esbuild/linux-mips64el@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-mips64el@npm:0.24.2" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -3369,9 +3422,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-ppc64@npm:0.21.5" +"@esbuild/linux-ppc64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-ppc64@npm:0.24.2" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -3390,9 +3443,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-riscv64@npm:0.21.5" +"@esbuild/linux-riscv64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-riscv64@npm:0.24.2" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -3411,9 +3464,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-s390x@npm:0.21.5" +"@esbuild/linux-s390x@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-s390x@npm:0.24.2" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -3432,13 +3485,20 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/linux-x64@npm:0.21.5" +"@esbuild/linux-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/linux-x64@npm:0.24.2" conditions: os=linux & cpu=x64 languageName: node linkType: hard +"@esbuild/netbsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/netbsd-arm64@npm:0.24.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -3453,13 +3513,20 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/netbsd-x64@npm:0.21.5" +"@esbuild/netbsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/netbsd-x64@npm:0.24.2" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard +"@esbuild/openbsd-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/openbsd-arm64@npm:0.24.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -3474,9 +3541,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/openbsd-x64@npm:0.21.5" +"@esbuild/openbsd-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/openbsd-x64@npm:0.24.2" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -3495,9 +3562,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/sunos-x64@npm:0.21.5" +"@esbuild/sunos-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/sunos-x64@npm:0.24.2" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -3516,9 +3583,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-arm64@npm:0.21.5" +"@esbuild/win32-arm64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-arm64@npm:0.24.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -3537,9 +3604,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-ia32@npm:0.21.5" +"@esbuild/win32-ia32@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-ia32@npm:0.24.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -3558,9 +3625,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.21.5": - version: 0.21.5 - resolution: "@esbuild/win32-x64@npm:0.21.5" +"@esbuild/win32-x64@npm:0.24.2": + version: 0.24.2 + resolution: "@esbuild/win32-x64@npm:0.24.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -3721,7 +3788,7 @@ __metadata: languageName: node linkType: hard -"@frigade/js@npm:^0.7.15, @frigade/js@workspace:packages/js-api": +"@frigade/js@npm:^0.9.5, @frigade/js@workspace:packages/js-api": version: 0.0.0-use.local resolution: "@frigade/js@workspace:packages/js-api" dependencies: @@ -3753,9 +3820,9 @@ __metadata: version: 0.0.0-use.local resolution: "@frigade/react@workspace:packages/react" dependencies: - "@emotion/react": "npm:^11.11.4" + "@emotion/react": "npm:^11.14.0" "@floating-ui/react": "npm:^0.26.22" - "@frigade/js": "npm:^0.7.15" + "@frigade/js": "npm:^0.9.5" "@radix-ui/react-checkbox": "npm:^1.1.1" "@radix-ui/react-collapsible": "npm:^1.0.3" "@radix-ui/react-dialog": "npm:^1.0.5" @@ -3767,10 +3834,11 @@ __metadata: "@types/node": "npm:^20.10.5" "@types/react": "npm:^18.2.34" "@types/react-dom": "npm:^18.2.14" + "@types/use-sync-external-store": "npm:^0.0.6" "@typescript-eslint/eslint-plugin": "npm:^6.20.0" "@typescript-eslint/parser": "npm:^6.20.0" clsx: "npm:^2.0.0" - dompurify: "npm:^3.1.3" + dompurify: "npm:^3.2.4" embla-carousel-react: "npm:^8.1.3" eslint: "npm:^8.56.0" eslint-config-prettier: "npm:^9.1.0" @@ -3789,9 +3857,11 @@ __metadata: typedoc: "npm:^0.25.12" typedoc-plugin-markdown: "npm:^3.17.1" typescript: "npm:^4.9.4" + use-sync-external-store: "npm:^1.4.0" peerDependencies: - react: 17 - 18 - react-dom: 17 - 18 + "@emotion/react": ^11.14.0 + react: 17 - 19 + react-dom: 17 - 19 languageName: unknown linkType: soft @@ -3818,7 +3888,7 @@ __metadata: babel-jest: "npm:^29.4.1" copyfiles: "npm:^2.4.1" core-js-pure: "npm:^3.32.0" - dompurify: "npm:^3.1.3" + dompurify: "npm:^3.2.4" eslint-config-backpacker-react-ts: "npm:^0.3.0" husky: "npm:^8.0.3" jest: "npm:^29.6.2" @@ -3853,6 +3923,8 @@ __metadata: dependencies: "@changesets/cli": "npm:^2.22.0" eslint: "npm:^7.32.0" + eslint-plugin-react-hooks: "npm:^5.1.0" + husky: "npm:^9.1.7" prettier: "npm:^2.5.1" rimraf: "npm:^5.0.0" turbo: "npm:^1.10.14" @@ -4204,21 +4276,19 @@ __metadata: languageName: node linkType: hard -"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1": - version: 0.3.1 - resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.3.1" +"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.4.2": + version: 0.4.2 + resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.4.2" dependencies: - glob: "npm:^7.2.0" - glob-promise: "npm:^4.2.0" magic-string: "npm:^0.27.0" react-docgen-typescript: "npm:^2.2.2" peerDependencies: typescript: ">= 4.3.x" - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/ec5052f9cb50e7388ebd2f49f974fd0d67dcd429571047bfeb75304bc6b128b5779d05be5f2143d3d22cd1ee3d4f0e9a4c66168999d861e2e60a2ba6787d11a4 + checksum: 10/0878171c598ee85997a2b9ea452715ea3df4c0faa3c646ffc0be62a772c3f4919986a9045864fe7cf2208b3f577bbe1e029f8ea3f3bf83f509be8d7a064f0396 languageName: node linkType: hard @@ -5588,9 +5658,9 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-actions@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-actions@npm:8.2.9" +"@storybook/addon-actions@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-actions@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" "@types/uuid": "npm:^9.0.1" @@ -5598,8 +5668,8 @@ __metadata: polished: "npm:^4.2.2" uuid: "npm:^9.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/afde25d35194911daaa6aee025cd9da51397100ab78602b5969767bd6d26e12d86f1148b1daf5bb97b2e1836565e492cc4458a7494bedeea2e4601a8b03a2175 + storybook: ^8.5.3 + checksum: 10/4e0f73e1469f17452533ba5af56729bb1bd5426b9325e091e814d85764465d7a8a17889a0bba8280c3ef8b80461f6183c3bd7b23b8e394865ea182d928d7e31a languageName: node linkType: hard @@ -5614,16 +5684,16 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-backgrounds@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-backgrounds@npm:8.2.9" +"@storybook/addon-backgrounds@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-backgrounds@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" memoizerific: "npm:^1.11.3" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/a93cd5a68c012de5ed4dafa4e1d1c3c06406ca8d5d19809e3dcf7c2f369d5c28b4ccf0d846a45f91665a33bef8831093376a070d61155d167494dd88f4b9b901 + storybook: ^8.5.3 + checksum: 10/b8884b3d455fe6167755609e2fc89174389aaee4e800f9385cfea8b6ab7fdc32e105669d506acb213750b14e8bab99fc19bd47f379e2f7e4a35cf724acf7a092 languageName: node linkType: hard @@ -5638,16 +5708,16 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-controls@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-controls@npm:8.2.9" +"@storybook/addon-controls@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-controls@npm:8.5.3" dependencies: + "@storybook/global": "npm:^5.0.0" dequal: "npm:^2.0.2" - lodash: "npm:^4.17.21" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/0027db4387f909268d60dba205f07e57a22873634463a8c28aa24d2945610ac6a4b9d199697ffb1cf2eeba907dd76e4e84c8e6583e55443d109f4210db6210c5 + storybook: ^8.5.3 + checksum: 10/df9359d49a23d8c1d9679a9d8b9dd5b2ebf8e1a8f9246c1282ccfc05c613d849880ce259925449ee3a2b321d1d2e408bc4c9128756bcd60a4869d79d18133626 languageName: node linkType: hard @@ -5681,26 +5751,20 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-docs@npm:8.2.9, @storybook/addon-docs@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-docs@npm:8.2.9" +"@storybook/addon-docs@npm:8.5.3, @storybook/addon-docs@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-docs@npm:8.5.3" dependencies: - "@babel/core": "npm:^7.24.4" "@mdx-js/react": "npm:^3.0.0" - "@storybook/blocks": "npm:8.2.9" - "@storybook/csf-plugin": "npm:8.2.9" - "@storybook/global": "npm:^5.0.0" - "@storybook/react-dom-shim": "npm:8.2.9" - "@types/react": "npm:^16.8.0 || ^17.0.0 || ^18.0.0" - fs-extra: "npm:^11.1.0" + "@storybook/blocks": "npm:8.5.3" + "@storybook/csf-plugin": "npm:8.5.3" + "@storybook/react-dom-shim": "npm:8.5.3" react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0" react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0" - rehype-external-links: "npm:^3.0.0" - rehype-slug: "npm:^6.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/7e940327b84cc257da72498e6295d8124044b7e3ae00453f9302a86c19642915743b567237f47989bdc5d3fbeb7d7cf8e9035a91d6b34c08c795838e8d00a82a + storybook: ^8.5.3 + checksum: 10/916e01189387103081695b5c36a5405f8859186a6f69f291b1a0788c0de45824d92f65143ad9d7aafcb879059e29b2f90650239a59ecae427edae9e308e330a3 languageName: node linkType: hard @@ -5729,23 +5793,23 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-essentials@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-essentials@npm:8.2.9" +"@storybook/addon-essentials@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-essentials@npm:8.5.3" dependencies: - "@storybook/addon-actions": "npm:8.2.9" - "@storybook/addon-backgrounds": "npm:8.2.9" - "@storybook/addon-controls": "npm:8.2.9" - "@storybook/addon-docs": "npm:8.2.9" - "@storybook/addon-highlight": "npm:8.2.9" - "@storybook/addon-measure": "npm:8.2.9" - "@storybook/addon-outline": "npm:8.2.9" - "@storybook/addon-toolbars": "npm:8.2.9" - "@storybook/addon-viewport": "npm:8.2.9" + "@storybook/addon-actions": "npm:8.5.3" + "@storybook/addon-backgrounds": "npm:8.5.3" + "@storybook/addon-controls": "npm:8.5.3" + "@storybook/addon-docs": "npm:8.5.3" + "@storybook/addon-highlight": "npm:8.5.3" + "@storybook/addon-measure": "npm:8.5.3" + "@storybook/addon-outline": "npm:8.5.3" + "@storybook/addon-toolbars": "npm:8.5.3" + "@storybook/addon-viewport": "npm:8.5.3" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/70cc46b9188cf61a30af578fa79d15135e6c51e9406f9d044668fd395c4c93b9a408481039da6dc824100016dd76da711daef79897252e982382d2262292103d + storybook: ^8.5.3 + checksum: 10/200e04a7e6795102c75c7c4eac7cbc14e8767732b69e98a6a3c52c9813887ef9076357b7cfbaa1b8b43bc8745b25aeb8a482c174e2feab0e64a499d37cb8d2a9 languageName: node linkType: hard @@ -5758,14 +5822,14 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-highlight@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-highlight@npm:8.2.9" +"@storybook/addon-highlight@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-highlight@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/273a10768ec0abcc4f816972ec581c0be3963a6c85cb99dda7be0c605bf47fb92538c9a1b3339f2c38c38f9ad9ca3f784ec0b2c8b3bb55e153407351faff8f1b + storybook: ^8.5.3 + checksum: 10/276e8e74a5eded2b43a4cdc11ec79a479931ea66b85a75bba7409ebfa898a99a2101e2bf300312eb8b206e76dfa6de734befcdfd39ef63b349a94d3bbecd850a languageName: node linkType: hard @@ -5782,18 +5846,18 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-interactions@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-interactions@npm:8.2.9" +"@storybook/addon-interactions@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-interactions@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" - "@storybook/instrumenter": "npm:8.2.9" - "@storybook/test": "npm:8.2.9" + "@storybook/instrumenter": "npm:8.5.3" + "@storybook/test": "npm:8.5.3" polished: "npm:^4.2.2" ts-dedent: "npm:^2.2.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/6ed572a281b7b295e858324b961b02c32a18d0f7a1870cde5f00d111a8fade57dd7f975f32ffcfdf3a6565b03943e98c8fcc1875be866fa6bbc17d26b67f412d + storybook: ^8.5.3 + checksum: 10/8c691dfcededf33abd184fce9e0e83a87694d7860ee4d07c91d5b6df38a76b9c7c2b225a49bd71a35acd0bd75ba2ffd2c7340512a6f7ac5c8ef69975c19c1f92 languageName: node linkType: hard @@ -5813,20 +5877,20 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-links@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-links@npm:8.2.9" +"@storybook/addon-links@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-links@npm:8.5.3" dependencies: - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:0.1.12" "@storybook/global": "npm:^5.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.9 + storybook: ^8.5.3 peerDependenciesMeta: react: optional: true - checksum: 10/e6b14e2cb6763f25027965c90404afb2572b27298e3d1fafa136176113ad4296b1ce48eaa8caf4f521fb6d3404921f17eb3cbe62061ebdc5f2324e0c85333742 + checksum: 10/5fd459e79add6391f4feac1cb822776b8ef8764368bf0ee6714423df6fbb82749588221a4d93a1d8021659c5d85b10aba09fcd92b5f9d916af759df22b2845ea languageName: node linkType: hard @@ -5840,15 +5904,15 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-measure@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-measure@npm:8.2.9" +"@storybook/addon-measure@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-measure@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" tiny-invariant: "npm:^1.3.1" peerDependencies: - storybook: ^8.2.9 - checksum: 10/5a0c31b617bfdcd024c5325ab48771b8cf7b726336e24b9b0c7d4a4e8bda2093a8c2c264272b7fb36bf010f1bd54896df45b6f9092d020e696226b34e23ce208 + storybook: ^8.5.3 + checksum: 10/4aae4500456f770427047c76a9b423cfbf5e9c748738099d1eabae4a036ec89e4ce7e5be7b1578a99cfb224719ef7d99ca4b3c80d56a53aa570097c28c3fbd9b languageName: node linkType: hard @@ -5862,15 +5926,15 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-outline@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-outline@npm:8.2.9" +"@storybook/addon-outline@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-outline@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.2.9 - checksum: 10/ad88e5d501270e7e47e955ff9e9c2aa3c5a3d9b38fe592cc7e4b5890d5c905a5ab9b644bdf7d566cdd9f66ae9ca9b9ac481f95f759a41cdfff5a3dd43103602d + storybook: ^8.5.3 + checksum: 10/8daa69b3c2cea9a32b650e4602979050b2eafa53bc979f5008c00ddb015dd4b1777aec46d6bd3ccd83da0e3a2ce4f772b86e56bb716161943822d462ca34e32b languageName: node linkType: hard @@ -5881,12 +5945,12 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-toolbars@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-toolbars@npm:8.2.9" +"@storybook/addon-toolbars@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-toolbars@npm:8.5.3" peerDependencies: - storybook: ^8.2.9 - checksum: 10/77811c752d74f4fb0f5ab6d4a836a5c940a00a7ed9c4779327e1531ea704b4950ea542d5b3bd88380414d13218a3acd93fa7b67f923830cc2aef70e70881d43f + storybook: ^8.5.3 + checksum: 10/e2085b64aa1587c1f2c7bf9c4078be3b0297b0e8bb879795b9eef403c5f2775dd6acce888878c416b6b0f5e6e694d3ff48f189141eb2b0f4d1072de85d16874f languageName: node linkType: hard @@ -5899,14 +5963,14 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-viewport@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/addon-viewport@npm:8.2.9" +"@storybook/addon-viewport@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/addon-viewport@npm:8.5.3" dependencies: memoizerific: "npm:^1.11.3" peerDependencies: - storybook: ^8.2.9 - checksum: 10/1e634e8bdae61d5d89b4a04ccfc0ddbde3c480e9bf8655772be27ff88edc0d6556305eb48dd3e47b105446e02856511f9f4988399de633663f5f08dcf2610dca + storybook: ^8.5.3 + checksum: 10/3aa52bc69da64619d5c7b95b0046ca629cffd5200e48ae11a48c6643f49989c1e5dd81223c4106438e6f6681654cabca213ad5b36e953a77095319f1e3026766 languageName: node linkType: hard @@ -5944,34 +6008,23 @@ __metadata: languageName: node linkType: hard -"@storybook/blocks@npm:8.2.9, @storybook/blocks@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/blocks@npm:8.2.9" +"@storybook/blocks@npm:8.5.3, @storybook/blocks@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/blocks@npm:8.5.3" dependencies: - "@storybook/csf": "npm:0.1.11" - "@storybook/global": "npm:^5.0.0" - "@storybook/icons": "npm:^1.2.5" - "@types/lodash": "npm:^4.14.167" - color-convert: "npm:^2.0.1" - dequal: "npm:^2.0.2" - lodash: "npm:^4.17.21" - markdown-to-jsx: "npm:^7.4.5" - memoizerific: "npm:^1.11.3" - polished: "npm:^4.2.2" - react-colorful: "npm:^5.1.2" - telejson: "npm:^7.2.0" + "@storybook/csf": "npm:0.1.12" + "@storybook/icons": "npm:^1.2.12" ts-dedent: "npm:^2.0.0" - util-deprecate: "npm:^1.0.2" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.9 + storybook: ^8.5.3 peerDependenciesMeta: react: optional: true react-dom: optional: true - checksum: 10/a9d4bf1f4a19806c3ebcd6677fb7c8bdffa67562f7e0c10b9f15b1450dab641e2282a9197a6b5d3361c788688d958795620a9bb5f4737a20cd802f91041ad011 + checksum: 10/c43afed29ffef6aa53c71953285b32436841e8a5e26d503d51857c7ff29541c2034406a7aadb187fd30793277397e74d35b3448f36f88d0c0cdfcc661c93c86f languageName: node linkType: hard @@ -5999,33 +6052,17 @@ __metadata: languageName: node linkType: hard -"@storybook/builder-vite@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/builder-vite@npm:8.2.9" +"@storybook/builder-vite@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/builder-vite@npm:8.5.3" dependencies: - "@storybook/csf-plugin": "npm:8.2.9" - "@types/find-cache-dir": "npm:^3.2.1" + "@storybook/csf-plugin": "npm:8.5.3" browser-assert: "npm:^1.2.1" - es-module-lexer: "npm:^1.5.0" - express: "npm:^4.19.2" - find-cache-dir: "npm:^3.0.0" - fs-extra: "npm:^11.1.0" - magic-string: "npm:^0.30.0" ts-dedent: "npm:^2.0.0" peerDependencies: - "@preact/preset-vite": "*" - storybook: ^8.2.9 - typescript: ">= 4.3.x" - vite: ^4.0.0 || ^5.0.0 - vite-plugin-glimmerx: "*" - peerDependenciesMeta: - "@preact/preset-vite": - optional: true - typescript: - optional: true - vite-plugin-glimmerx: - optional: true - checksum: 10/ed8e28b6949089939611ed25530be019c8bc80d79a232a022862c9052e3a683ca2122e9ec1fd3a349a476a14fabede98fb24d4cc88c5fc518de66afef026f771 + storybook: ^8.5.3 + vite: ^4.0.0 || ^5.0.0 || ^6.0.0 + checksum: 10/b3816ee8baeb947b28522a17819498224fe5ee4caf21988a8c879b2a71b565725ba0f9f7cb25bf9ede3cf90a72067aade71fbf21ff807fb068eb8f9f4e34fd75 languageName: node linkType: hard @@ -6143,15 +6180,33 @@ __metadata: languageName: node linkType: hard -"@storybook/cli@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/cli@npm:8.2.9" +"@storybook/cli@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/cli@npm:8.5.3" dependencies: - storybook: "npm:8.2.9" + "@babel/core": "npm:^7.24.4" + "@babel/types": "npm:^7.24.0" + "@storybook/codemod": "npm:8.5.3" + "@types/semver": "npm:^7.3.4" + commander: "npm:^12.1.0" + create-storybook: "npm:8.5.3" + cross-spawn: "npm:^7.0.3" + envinfo: "npm:^7.7.3" + fd-package-json: "npm:^1.2.0" + find-up: "npm:^5.0.0" + giget: "npm:^1.0.0" + glob: "npm:^10.0.0" + globby: "npm:^14.0.1" + jscodeshift: "npm:^0.15.1" + leven: "npm:^3.1.0" + prompts: "npm:^2.4.0" + semver: "npm:^7.3.7" + storybook: "npm:8.5.3" + tiny-invariant: "npm:^1.3.1" + ts-dedent: "npm:^2.0.0" bin: - sb: ./index.js - storybook: ./index.js - checksum: 10/32f995b5767628633ec8808df5ba043be189e7880b1516ecab56d2fef5408012fcaefc266a3ed68eb76009428fd005ef3cdae7424e92f3c9d0b6fbecf1b3cc54 + cli: ./bin/index.cjs + checksum: 10/61b196f1bd9ecaa005fa0c64e6efffd75db41a2282d9437cc27b32e3f56d4269c1a15bae5d8a73d08a6c64ff5f4cdbaccebc47ed7d752845dfa38cc04a1ecaf3 languageName: node linkType: hard @@ -6186,24 +6241,24 @@ __metadata: languageName: node linkType: hard -"@storybook/codemod@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/codemod@npm:8.2.9" +"@storybook/codemod@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/codemod@npm:8.5.3" dependencies: "@babel/core": "npm:^7.24.4" "@babel/preset-env": "npm:^7.24.4" "@babel/types": "npm:^7.24.0" - "@storybook/core": "npm:8.2.9" - "@storybook/csf": "npm:0.1.11" + "@storybook/core": "npm:8.5.3" + "@storybook/csf": "npm:0.1.12" "@types/cross-spawn": "npm:^6.0.2" cross-spawn: "npm:^7.0.3" + es-toolkit: "npm:^1.22.0" globby: "npm:^14.0.1" jscodeshift: "npm:^0.15.1" - lodash: "npm:^4.17.21" prettier: "npm:^3.1.1" recast: "npm:^0.23.5" tiny-invariant: "npm:^1.3.1" - checksum: 10/a0760f6612038f1a771c89c0d9054439af5b29865b44bdebb26ea6bbbf4ed768db92d349f44deb72297085e5c6c43a029b9b7d29ac531f8d998752d9c2273bf4 + checksum: 10/5d78b8ffacee8c60ef619da8c1dd6230aa8a9d533f3861459dacccc2c4031b57e6023ad5bd2fcc9c4acbb65d2aa9d02d70ec2538f783989e59cfacd5a05a123b languageName: node linkType: hard @@ -6228,12 +6283,12 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/components@npm:8.2.9" +"@storybook/components@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/components@npm:8.5.3" peerDependencies: - storybook: ^8.2.9 - checksum: 10/32153df19777ea751d1adb33da6e92b5647d19fd924753b2ca1c80bcc102ee0489eac7b02914a2e56406233940f6eca7f1aa3ba0c441a164cabc954679a67869 + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + checksum: 10/8c39ef2733a6d12a92f807608ee18f9f1f11236be8e954e743addc92c9f0ba6344d139975789f74ee89cd6bb80d476701d4f2a33aefd5a8e0eeeba3ceb84658b languageName: node linkType: hard @@ -6278,15 +6333,6 @@ __metadata: languageName: node linkType: hard -"@storybook/core-common@npm:^8.0.0": - version: 8.3.1 - resolution: "@storybook/core-common@npm:8.3.1" - peerDependencies: - storybook: ^8.3.1 - checksum: 10/e1078e0dc8cb5027b948ff9762a509d7aca35f95c1d76d53ba9a7f70b991d8d76a735f377672738b8da40e7dfde1c71e0d42cedbefd0e842fc5a21f545d7c77f - languageName: node - linkType: hard - "@storybook/core-events@npm:7.6.16": version: 7.6.16 resolution: "@storybook/core-events@npm:7.6.16" @@ -6358,22 +6404,27 @@ __metadata: languageName: node linkType: hard -"@storybook/core@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/core@npm:8.2.9" +"@storybook/core@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/core@npm:8.5.3" dependencies: - "@storybook/csf": "npm:0.1.11" - "@types/express": "npm:^4.17.21" - "@types/node": "npm:^18.0.0" + "@storybook/csf": "npm:0.1.12" + better-opn: "npm:^3.0.2" browser-assert: "npm:^1.2.1" - esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0" + esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0" esbuild-register: "npm:^3.5.0" - express: "npm:^4.19.2" + jsdoc-type-pratt-parser: "npm:^4.0.0" process: "npm:^0.11.10" recast: "npm:^0.23.5" - util: "npm:^0.12.4" + semver: "npm:^7.6.2" + util: "npm:^0.12.5" ws: "npm:^8.2.3" - checksum: 10/38602bae881a9824520b9369fdb37c4178bbdcc158934905af6d11963df289e9b958bdc05ef61773c70274a41188c473040e7d9113cc3043475f48005ec8f479 + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true + checksum: 10/af0d4a74c1e8cca1acf0b601ffaad47c4ff8cfa43075d3c1a6de28053c61494b2c1a7d3ee1c500efe3af3ee22de64a9ca2f647b8a6e75037d99fc113dff41556 languageName: node linkType: hard @@ -6387,14 +6438,14 @@ __metadata: languageName: node linkType: hard -"@storybook/csf-plugin@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/csf-plugin@npm:8.2.9" +"@storybook/csf-plugin@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/csf-plugin@npm:8.5.3" dependencies: unplugin: "npm:^1.3.1" peerDependencies: - storybook: ^8.2.9 - checksum: 10/514171f66a4e71849ee7a4efacc3051de0714fda56dfdb7581f5d08a268d5a9d0bee6264404bd766f631f2ab8a0358b4c226ecfcee8965d8560d1afc5d17c1b9 + storybook: ^8.5.3 + checksum: 10/2981e153040a06da0889e55050fd23b0980745b2d6ca78f04f011646f681b5db36fda76fabc4d6338ad65cc6fb5570d693725a053ada2b7ba6386be50e7b54f1 languageName: node linkType: hard @@ -6415,16 +6466,16 @@ __metadata: languageName: node linkType: hard -"@storybook/csf-tools@npm:^8.0.0": - version: 8.3.1 - resolution: "@storybook/csf-tools@npm:8.3.1" - peerDependencies: - storybook: ^8.3.1 - checksum: 10/8df82a30de61bea9abc57a501addf077a507b3f763e379cba620e227e4f60a41820264c725ba078005181f0ed215ec062348df2e975726873fb12636638cf863 +"@storybook/csf@npm:0.1.12": + version: 0.1.12 + resolution: "@storybook/csf@npm:0.1.12" + dependencies: + type-fest: "npm:^2.19.0" + checksum: 10/f661709de5bd68bfd4ced67df31ef26341168d6679bc13564cb024cfdbc8fdfa94d384267c20b3c858a3058b1ee8dbd71cea169245fcf7b28298890d6c3e1da4 languageName: node linkType: hard -"@storybook/csf@npm:0.1.11, @storybook/csf@npm:^0.1.11": +"@storybook/csf@npm:^0.1.11": version: 0.1.11 resolution: "@storybook/csf@npm:0.1.11" dependencies: @@ -6433,15 +6484,6 @@ __metadata: languageName: node linkType: hard -"@storybook/csf@npm:^0.0.1": - version: 0.0.1 - resolution: "@storybook/csf@npm:0.0.1" - dependencies: - lodash: "npm:^4.17.15" - checksum: 10/f6bb019bccd8abc14e45a85258158b7bd8cc525887ac8dc9151ed8c4908be3b5f5523da8a7a9b96ff11b13b6c1744e1a0e070560d63d836b950f595f9a5719d4 - languageName: node - linkType: hard - "@storybook/csf@npm:^0.1.2": version: 0.1.2 resolution: "@storybook/csf@npm:0.1.2" @@ -6480,26 +6522,25 @@ __metadata: languageName: node linkType: hard -"@storybook/icons@npm:^1.2.5": - version: 1.2.10 - resolution: "@storybook/icons@npm:1.2.10" +"@storybook/icons@npm:^1.2.12": + version: 1.3.2 + resolution: "@storybook/icons@npm:1.3.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10/fad929a7e3c7a1a0fbf6b924b0be73f557b1bba9519faa15422482f89513ceb4b649444c224ee3d1dfbdce3616e684063cff23da08f6b1dd96f1aff4381388a6 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta + checksum: 10/40dc378e3e42a0f37985df538920b5b032d19c3166155f0000d2f7cc317ff20dc83a28b4e2f2d291d5e2b3e4875401b1896e8a95da3c18f3e6ea417b574092b4 languageName: node linkType: hard -"@storybook/instrumenter@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/instrumenter@npm:8.2.9" +"@storybook/instrumenter@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/instrumenter@npm:8.5.3" dependencies: "@storybook/global": "npm:^5.0.0" - "@vitest/utils": "npm:^1.3.1" - util: "npm:^0.12.4" + "@vitest/utils": "npm:^2.1.1" peerDependencies: - storybook: ^8.2.9 - checksum: 10/c31a3ec70e252975f56d141942db1e8187b976fbb69e718f6ec83fe1b693457a1ca9173341ae17a8bff294ff02dfe734449c7120098442f01d3e14dd3b20f667 + storybook: ^8.5.3 + checksum: 10/c3b7ddfa2efe7e5a49500b906f7026193e555645c83b865137b24a838260c172f73300f7311df296f3b8b0eb2b0d1d12fe687656d2e9464d1161857554f60a6b languageName: node linkType: hard @@ -6525,12 +6566,12 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/manager-api@npm:8.2.9" +"@storybook/manager-api@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/manager-api@npm:8.5.3" peerDependencies: - storybook: ^8.2.9 - checksum: 10/5ee66ebdc88f886c05425acff2c362681a6f778e2f27023a43c6c36767ef265175c58b8f23f74684dba64477b26b310fad7cf1761e6643325325a8724f53d93c + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + checksum: 10/39987da92eca3adc04d27533371280c0df5581a9524a44a858ae6cf859bac9a33407e7ebcd953086718d6335abd70f71a3b49b7c019dc108ad90b374a944818f languageName: node linkType: hard @@ -6618,21 +6659,12 @@ __metadata: languageName: node linkType: hard -"@storybook/preview-api@npm:^8.0.0": - version: 8.3.1 - resolution: "@storybook/preview-api@npm:8.3.1" +"@storybook/preview-api@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/preview-api@npm:8.5.3" peerDependencies: - storybook: ^8.3.1 - checksum: 10/b6268888373f0f306e9dd9c875183919ddd76fcbeb393509e562c671a8f091463db3701747c5606baa9d5cd643f97014399b03b5a5bcde349a48618236e5cdb1 - languageName: node - linkType: hard - -"@storybook/preview-api@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/preview-api@npm:8.2.9" - peerDependencies: - storybook: ^8.2.9 - checksum: 10/4973ca3ede45e363a54071a200ee1aeeea9c735ee1e6ba93d46c756bae905057a0e6c30882964aee14fb1631029c5f86b2a4cb8deaee4a80aa5770bf63a00eb3 + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + checksum: 10/aa046078377904253afed9f6450f4ef8fbae9ab7429720954672ad5f217a4cc94d1ccbaedd1277b03a98a03ed6544493f046d06192e83d74dec078c03b52b146 languageName: node linkType: hard @@ -6671,36 +6703,40 @@ __metadata: languageName: node linkType: hard -"@storybook/react-dom-shim@npm:8.2.9": - version: 8.2.9 - resolution: "@storybook/react-dom-shim@npm:8.2.9" +"@storybook/react-dom-shim@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/react-dom-shim@npm:8.5.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.9 - checksum: 10/a8ede3f14a3e877c07425baa59bbe505a5011bbec7f70a5440d1af49c1d3cafd03adba471905ed4dd3815b8272fd4f9aff65b65940bff85e81b4b30b8b4e37b5 + storybook: ^8.5.3 + checksum: 10/78e411706b9d8b0ff9267c42c970cb2f00e1c4d84127daa20fc9167d89a063a339a23a69487375b1bafbf36cc999f067e0b717933f9265a7c7abb787782221c3 languageName: node linkType: hard -"@storybook/react-vite@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/react-vite@npm:8.2.9" +"@storybook/react-vite@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/react-vite@npm:8.5.3" dependencies: - "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.1" + "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.4.2" "@rollup/pluginutils": "npm:^5.0.2" - "@storybook/builder-vite": "npm:8.2.9" - "@storybook/react": "npm:8.2.9" + "@storybook/builder-vite": "npm:8.5.3" + "@storybook/react": "npm:8.5.3" find-up: "npm:^5.0.0" magic-string: "npm:^0.30.0" react-docgen: "npm:^7.0.0" resolve: "npm:^1.22.8" tsconfig-paths: "npm:^4.2.0" peerDependencies: + "@storybook/test": 8.5.3 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.9 - vite: ^4.0.0 || ^5.0.0 - checksum: 10/1acae6bae3c5f4cb0067a23235f2e5030f28b35b74e279b7a74795b49a230e331cf3dec498645ccfe24726bb04dbb1d56b696b03a51c281ccb26e6000416e2c2 + storybook: ^8.5.3 + vite: ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + "@storybook/test": + optional: true + checksum: 10/9e62c846dcaa1d06af4fb36bd82fe00e8eca0d8ffc2ffe60725f57ff7468113915be2d486b8b7daf887bd43cbc6987fba74d4513c67e23e814c2ffc672f7e567 languageName: node linkType: hard @@ -6762,40 +6798,28 @@ __metadata: languageName: node linkType: hard -"@storybook/react@npm:8.2.9, @storybook/react@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/react@npm:8.2.9" +"@storybook/react@npm:8.5.3, @storybook/react@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/react@npm:8.5.3" dependencies: - "@storybook/components": "npm:^8.2.9" + "@storybook/components": "npm:8.5.3" "@storybook/global": "npm:^5.0.0" - "@storybook/manager-api": "npm:^8.2.9" - "@storybook/preview-api": "npm:^8.2.9" - "@storybook/react-dom-shim": "npm:8.2.9" - "@storybook/theming": "npm:^8.2.9" - "@types/escodegen": "npm:^0.0.6" - "@types/estree": "npm:^0.0.51" - "@types/node": "npm:^18.0.0" - acorn: "npm:^7.4.1" - acorn-jsx: "npm:^5.3.1" - acorn-walk: "npm:^7.2.0" - escodegen: "npm:^2.1.0" - html-tags: "npm:^3.1.0" - lodash: "npm:^4.17.21" - prop-types: "npm:^15.7.2" - react-element-to-jsx-string: "npm:^15.0.0" - semver: "npm:^7.3.7" - ts-dedent: "npm:^2.0.0" - type-fest: "npm:~2.19" - util-deprecate: "npm:^1.0.2" + "@storybook/manager-api": "npm:8.5.3" + "@storybook/preview-api": "npm:8.5.3" + "@storybook/react-dom-shim": "npm:8.5.3" + "@storybook/theming": "npm:8.5.3" peerDependencies: + "@storybook/test": 8.5.3 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.9 + storybook: ^8.5.3 typescript: ">= 4.2.x" peerDependenciesMeta: + "@storybook/test": + optional: true typescript: optional: true - checksum: 10/8687e323f8345d0c1350b05879df09ce8ea303f247307e192f0d8789908e87383631f669754fb1482ae71346456e91c2f9bd3310e09cf7b7b89ec97332c5a27e + checksum: 10/e72533456c222360def9d79fcfd2c3321dd55e8b7f1dca3af7988e6c48477333d90f309df3128d86fedfb2a615b03ad06bdc871a65b3bb26bebdbbf1a7e80a63 languageName: node linkType: hard @@ -6826,19 +6850,16 @@ __metadata: languageName: node linkType: hard -"@storybook/test-runner@npm:^0.19.1": - version: 0.19.1 - resolution: "@storybook/test-runner@npm:0.19.1" +"@storybook/test-runner@npm:^0.21.0": + version: 0.21.0 + resolution: "@storybook/test-runner@npm:0.21.0" dependencies: "@babel/core": "npm:^7.22.5" "@babel/generator": "npm:^7.22.5" "@babel/template": "npm:^7.22.5" "@babel/types": "npm:^7.22.5" "@jest/types": "npm:^29.6.3" - "@storybook/core-common": "npm:^8.0.0" "@storybook/csf": "npm:^0.1.11" - "@storybook/csf-tools": "npm:^8.0.0" - "@storybook/preview-api": "npm:^8.0.0" "@swc/core": "npm:^1.5.22" "@swc/jest": "npm:^0.2.23" expect-playwright: "npm:^0.8.0" @@ -6852,27 +6873,29 @@ __metadata: jest-watch-typeahead: "npm:^2.0.0" nyc: "npm:^15.1.0" playwright: "npm:^1.14.0" + peerDependencies: + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 bin: test-storybook: dist/test-storybook.js - checksum: 10/cb0a68e6fc9472b4cf82c969fb58a74404f7865ab2ecbffe42f78f3ec3da0c21797c62a19e1a77a02d3c0559a6a03bae2e28104f10dd239b5ded7bcbaa232b98 + checksum: 10/45c008b7c3bbd687c674e5c1fd3f90dd4efec5c6627beeb836030c9454f22de995da358200bc0271c5ddc5d561478f3f0fe8cf0564b7febbdfda1eda76b3a3de languageName: node linkType: hard -"@storybook/test@npm:8.2.9, @storybook/test@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/test@npm:8.2.9" +"@storybook/test@npm:8.5.3, @storybook/test@npm:^8.5.3": + version: 8.5.3 + resolution: "@storybook/test@npm:8.5.3" dependencies: - "@storybook/csf": "npm:0.1.11" - "@storybook/instrumenter": "npm:8.2.9" - "@testing-library/dom": "npm:10.1.0" - "@testing-library/jest-dom": "npm:6.4.5" + "@storybook/csf": "npm:0.1.12" + "@storybook/global": "npm:^5.0.0" + "@storybook/instrumenter": "npm:8.5.3" + "@testing-library/dom": "npm:10.4.0" + "@testing-library/jest-dom": "npm:6.5.0" "@testing-library/user-event": "npm:14.5.2" - "@vitest/expect": "npm:1.6.0" - "@vitest/spy": "npm:1.6.0" - util: "npm:^0.12.4" + "@vitest/expect": "npm:2.0.5" + "@vitest/spy": "npm:2.0.5" peerDependencies: - storybook: ^8.2.9 - checksum: 10/2440fac3b9f2205f5ef9762dccbfcb72bbe4f5db881c57c5ceb06fecfd072e039643ed2456d2b3260af6a4419f6fecafa77d247f9570afd553e4b0e8a19175a3 + storybook: ^8.5.3 + checksum: 10/e6a079a161e1da7fde27c526bf8f09d7c3d8214a33db1dcd6e0ed44bc22ece246c52d2d344f91ca507b81960bdf16855131a1979a6e7960fd827c878cf9bba5e languageName: node linkType: hard @@ -6902,12 +6925,12 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:^8.2.9": - version: 8.2.9 - resolution: "@storybook/theming@npm:8.2.9" +"@storybook/theming@npm:8.5.3": + version: 8.5.3 + resolution: "@storybook/theming@npm:8.5.3" peerDependencies: - storybook: ^8.2.9 - checksum: 10/13d71e1b69fb254d5de6349c60c7ac0518cbaec0c26a591c820752dd5b25fe66d8a32e4b081bd9470c0b07648e478eb88ab628d10196918ee2806c9640c2ee2f + storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + checksum: 10/9ed121f6a5c0a013decb3d973664d07cf412a607c787537ab56fc339334c42cad6494cf67865e03be9406c9dd6cc0c1ae7fa78102a11fe5e198d398c073f461e languageName: node linkType: hard @@ -7307,9 +7330,9 @@ __metadata: languageName: node linkType: hard -"@testing-library/dom@npm:10.1.0": - version: 10.1.0 - resolution: "@testing-library/dom@npm:10.1.0" +"@testing-library/dom@npm:10.4.0": + version: 10.4.0 + resolution: "@testing-library/dom@npm:10.4.0" dependencies: "@babel/code-frame": "npm:^7.10.4" "@babel/runtime": "npm:^7.12.5" @@ -7319,7 +7342,7 @@ __metadata: dom-accessibility-api: "npm:^0.5.9" lz-string: "npm:^1.5.0" pretty-format: "npm:^27.0.2" - checksum: 10/6d6ef942deedf547180c76d4cc2c43fe8e52a98ef68be6ba7382a43d3b1e1e5696d9c32ae0b2df12c92ea50023187d132ad2542fc118ba4b900f149e97d019e0 + checksum: 10/05825ee9a15b88cbdae12c137db7111c34069ed3c7a1bd03b6696cb1b37b29f6f2d2de581ebf03033e7df1ab7ebf08399310293f440a4845d95c02c0a9ecc899 languageName: node linkType: hard @@ -7339,36 +7362,18 @@ __metadata: languageName: node linkType: hard -"@testing-library/jest-dom@npm:6.4.5": - version: 6.4.5 - resolution: "@testing-library/jest-dom@npm:6.4.5" +"@testing-library/jest-dom@npm:6.5.0": + version: 6.5.0 + resolution: "@testing-library/jest-dom@npm:6.5.0" dependencies: - "@adobe/css-tools": "npm:^4.3.2" - "@babel/runtime": "npm:^7.9.2" + "@adobe/css-tools": "npm:^4.4.0" aria-query: "npm:^5.0.0" chalk: "npm:^3.0.0" css.escape: "npm:^1.5.1" dom-accessibility-api: "npm:^0.6.3" lodash: "npm:^4.17.21" redent: "npm:^3.0.0" - peerDependencies: - "@jest/globals": ">= 28" - "@types/bun": "*" - "@types/jest": ">= 28" - jest: ">= 28" - vitest: ">= 0.32" - peerDependenciesMeta: - "@jest/globals": - optional: true - "@types/bun": - optional: true - "@types/jest": - optional: true - jest: - optional: true - vitest: - optional: true - checksum: 10/6d9e6cc01ec9111ea631657d93596fa9505d294fdfc4172fbd750b8df6268f02d55900626423b195dac5b067a302557453894a5814bdf4e770dee37cdb1c0f2d + checksum: 10/3d2080888af5fd7306f57448beb5a23f55d965e265b5e53394fffc112dfb0678d616a5274ff0200c46c7618f293520f86fc8562eecd8bdbc0dbb3294d63ec431 languageName: node linkType: hard @@ -7555,7 +7560,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:^4.17.21, @types/express@npm:^4.7.0": +"@types/express@npm:^4.7.0": version: 4.17.21 resolution: "@types/express@npm:4.17.21" dependencies: @@ -7574,16 +7579,6 @@ __metadata: languageName: node linkType: hard -"@types/glob@npm:^7.1.3": - version: 7.2.0 - resolution: "@types/glob@npm:7.2.0" - dependencies: - "@types/minimatch": "npm:*" - "@types/node": "npm:*" - checksum: 10/6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 - languageName: node - linkType: hard - "@types/graceful-fs@npm:^4.1.3": version: 4.1.9 resolution: "@types/graceful-fs@npm:4.1.9" @@ -7593,15 +7588,6 @@ __metadata: languageName: node linkType: hard -"@types/hast@npm:^3.0.0": - version: 3.0.4 - resolution: "@types/hast@npm:3.0.4" - dependencies: - "@types/unist": "npm:*" - checksum: 10/732920d81bb7605895776841b7658b4d8cc74a43a8fa176017cc0fb0ecc1a4c82a2b75a4fe6b71aa262b649d3fb62858c6789efa3793ea1d40269953af96ecb5 - languageName: node - linkType: hard - "@types/html-minifier-terser@npm:^6.0.0": version: 6.1.0 resolution: "@types/html-minifier-terser@npm:6.1.0" @@ -7718,13 +7704,6 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:*": - version: 5.1.2 - resolution: "@types/minimatch@npm:5.1.2" - checksum: 10/94db5060d20df2b80d77b74dd384df3115f01889b5b6c40fa2dfa27cfc03a68fb0ff7c1f2a0366070263eb2e9d6bfd8c87111d4bc3ae93c3f291297c1bf56c85 - languageName: node - linkType: hard - "@types/minimist@npm:^1.2.0": version: 1.2.5 resolution: "@types/minimist@npm:1.2.5" @@ -7818,6 +7797,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^19.0.0": + version: 19.0.3 + resolution: "@types/react-dom@npm:19.0.3" + peerDependencies: + "@types/react": ^19.0.0 + checksum: 10/815907f7adaa078acbf1d1ae7b6bf69cebe86bd301b8b9744e392bc0f16feb31bfb9fe0bfa2681d7d86678c83d52dedba5ed9bc7776736d4050cdd426b8b2d2b + languageName: node + linkType: hard + "@types/react@npm:*, @types/react@npm:>=16, @types/react@npm:^18.2.34": version: 18.2.57 resolution: "@types/react@npm:18.2.57" @@ -7829,13 +7817,12 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^16.8.0 || ^17.0.0 || ^18.0.0": - version: 18.3.5 - resolution: "@types/react@npm:18.3.5" +"@types/react@npm:^19.0.0": + version: 19.0.8 + resolution: "@types/react@npm:19.0.8" dependencies: - "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/ba0477c5ad4a762157c6262a199af6ccf9e24576877a26a7f516d5a9ba35374a6ac7f8686a10e5e8030513214f02bcb66e8363e43905afb7cd313deaf673de05 + checksum: 10/1080d5b96ee0b4395f8f167ae6952f570088ee03bdce69f8237aab82c32d9bd2b71106f787bac17ba351acc4aba5e3454bafca51f2eb11d1562073b821e63d15 languageName: node linkType: hard @@ -7853,7 +7840,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0": +"@types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0": version: 7.5.7 resolution: "@types/semver@npm:7.5.7" checksum: 10/535d88ec577fe59e38211881f79a1e2ba391e9e1516f8fff74e7196a5ba54315bace9c67a4616c334c830c89027d70a9f473a4ceb634526086a9da39180f2f9a @@ -7895,20 +7882,13 @@ __metadata: languageName: node linkType: hard -"@types/trusted-types@npm:*": +"@types/trusted-types@npm:*, @types/trusted-types@npm:^2.0.7": version: 2.0.7 resolution: "@types/trusted-types@npm:2.0.7" checksum: 10/8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3 languageName: node linkType: hard -"@types/unist@npm:*, @types/unist@npm:^3.0.0": - version: 3.0.3 - resolution: "@types/unist@npm:3.0.3" - checksum: 10/96e6453da9e075aaef1dc22482463898198acdc1eeb99b465e65e34303e2ec1e3b1ed4469a9118275ec284dc98019f63c3f5d49422f0e4ac707e5ab90fb3b71a - languageName: node - linkType: hard - "@types/unist@npm:^2.0.0": version: 2.0.10 resolution: "@types/unist@npm:2.0.10" @@ -7916,6 +7896,13 @@ __metadata: languageName: node linkType: hard +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10/a95ce330668501ad9b1c5b7f2b14872ad201e552a0e567787b8f1588b22c7040c7c3d80f142cbb9f92d13c4ea41c46af57a20f2af4edf27f224d352abcfe4049 + languageName: node + linkType: hard + "@types/uuid@npm:^9.0.1": version: 9.0.8 resolution: "@types/uuid@npm:9.0.8" @@ -7979,7 +7966,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.0.0, @typescript-eslint/eslint-plugin@npm:^6.20.0": +"@typescript-eslint/eslint-plugin@npm:^6.20.0": version: 6.21.0 resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" dependencies: @@ -8037,7 +8024,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.0.0, @typescript-eslint/parser@npm:^6.20.0": +"@typescript-eslint/parser@npm:^6.20.0": version: 6.21.0 resolution: "@typescript-eslint/parser@npm:6.21.0" dependencies: @@ -8065,16 +8052,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/scope-manager@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - checksum: 10/e827770baa202223bc0387e2fd24f630690809e460435b7dc9af336c77322290a770d62bd5284260fa881c86074d6a9fd6c97b07382520b115f6786b8ed499da - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/scope-manager@npm:6.21.0" @@ -8085,6 +8062,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/scope-manager@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/visitor-keys": "npm:8.23.0" + checksum: 10/eb4624ccd907c21ca49c4600dec0c447349d7e987cda21181c008dc5ce855590e311efabe73b79b15da0948ce5f63ce0c33613ab4a39ea95578b099b724392e3 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/type-utils@npm:6.21.0" @@ -8109,13 +8096,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 10/24e8443177be84823242d6729d56af2c4b47bfc664dd411a1d730506abf2150d6c31bdefbbc6d97c8f91043e3a50e0c698239dcb145b79bb6b0c34469aaf6c45 - languageName: node - linkType: hard - "@typescript-eslint/types@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/types@npm:6.21.0" @@ -8123,6 +8103,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/types@npm:8.23.0" + checksum: 10/e2a68bc6e89226e47e495a91e0614aa5c3c4580b11f7fd99ac6728c1fce92f10755b0d7ade3cf6d3eb1209cd9cd0f29bd742f8dddc394b28bcead7025394eaa2 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:4.33.0": version: 4.33.0 resolution: "@typescript-eslint/typescript-estree@npm:4.33.0" @@ -8141,24 +8128,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10/06c975eb5f44b43bd19fadc2e1023c50cf87038fe4c0dd989d4331c67b3ff509b17fa60a3251896668ab4d7322bdc56162a9926971218d2e1a1874d2bef9a52e - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" @@ -8178,6 +8147,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/visitor-keys": "npm:8.23.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.0.1" + peerDependencies: + typescript: ">=4.8.4 <5.8.0" + checksum: 10/ddc9790d460bea065eeed3760759c034aef307e72c51b5ec7d869fdc77f18c28354c9e35841b44eebbdc54241bab4154809ae8213d33593a9bff20dc3b247fc3 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/utils@npm:6.21.0" @@ -8195,21 +8182,18 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:^5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" +"@typescript-eslint/utils@npm:^8.8.1": + version: 8.23.0 + resolution: "@typescript-eslint/utils@npm:8.23.0" dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.23.0" + "@typescript-eslint/types": "npm:8.23.0" + "@typescript-eslint/typescript-estree": "npm:8.23.0" peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10/15ef13e43998a082b15f85db979f8d3ceb1f9ce4467b8016c267b1738d5e7cdb12aa90faf4b4e6dd6486c236cf9d33c463200465cf25ff997dbc0f12358550a1 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10/72588d617ee5b1fa1020d008a7ff714a4a1e0fc1167aa9ff4b8ae71a37b25f43b2d40bca3380c56bb84d4092b6cac8d5d14d74e290e80217175ccf8237faf22a languageName: node linkType: hard @@ -8223,16 +8207,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - eslint-visitor-keys: "npm:^3.3.0" - checksum: 10/dc613ab7569df9bbe0b2ca677635eb91839dfb2ca2c6fa47870a5da4f160db0b436f7ec0764362e756d4164e9445d49d5eb1ff0b87f4c058946ae9d8c92eb388 - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:6.21.0": version: 6.21.0 resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" @@ -8243,7 +8217,17 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0": +"@typescript-eslint/visitor-keys@npm:8.23.0": + version: 8.23.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.23.0" + dependencies: + "@typescript-eslint/types": "npm:8.23.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10/fd473849b85e564e31aec64feb3417a4e16e48bf21f1959fbab56258e19c21ef47bbdb523c64a8921cdc82a5083735418890b6f74b564fd1ece305c977a0f7a6 + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" checksum: 10/c6fe89a505e513a7592e1438280db1c075764793a2397877ff1351721fe8792a966a5359769e30242b3cd023f2efb9e63ca2ca88019d73b564488cc20e3eab12 @@ -8265,35 +8249,65 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:1.6.0": - version: 1.6.0 - resolution: "@vitest/expect@npm:1.6.0" +"@vitest/expect@npm:2.0.5": + version: 2.0.5 + resolution: "@vitest/expect@npm:2.0.5" dependencies: - "@vitest/spy": "npm:1.6.0" - "@vitest/utils": "npm:1.6.0" - chai: "npm:^4.3.10" - checksum: 10/e82304a12e22b98c1ccea81e8f33c838561deb878588eac463164cc4f8fc0c401ace3a9e6758d9e3a6bcc01313e845e8478aaefb7548eaded04b8de12c1928f6 + "@vitest/spy": "npm:2.0.5" + "@vitest/utils": "npm:2.0.5" + chai: "npm:^5.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10/ca9a218f50254b2259fd16166b2d8c9ccc8ee2cc068905e6b3d6281da10967b1590cc7d34b5fa9d429297f97e740450233745583b4cc12272ff11705faf70a37 languageName: node linkType: hard -"@vitest/spy@npm:1.6.0": - version: 1.6.0 - resolution: "@vitest/spy@npm:1.6.0" +"@vitest/pretty-format@npm:2.0.5": + version: 2.0.5 + resolution: "@vitest/pretty-format@npm:2.0.5" dependencies: - tinyspy: "npm:^2.2.0" - checksum: 10/1c9698272a58aa47708bb8a1672d655fcec3285b02067cc3f70bfe76f4eda7a756eb379f8c945ccbe61677f5189aeb5ba93c2737a9d7db2de8c4e7bbdffcd372 + tinyrainbow: "npm:^1.2.0" + checksum: 10/70bf452dd0b8525e658795125b3f11110bd6baadfaa38c5bb91ca763bded35ec6dc80e27964ad4e91b91be6544d35e18ea7748c1997693988f975a7283c3e9a0 languageName: node linkType: hard -"@vitest/utils@npm:1.6.0, @vitest/utils@npm:^1.3.1": - version: 1.6.0 - resolution: "@vitest/utils@npm:1.6.0" +"@vitest/pretty-format@npm:2.1.9": + version: 2.1.9 + resolution: "@vitest/pretty-format@npm:2.1.9" dependencies: - diff-sequences: "npm:^29.6.3" + tinyrainbow: "npm:^1.2.0" + checksum: 10/557dc637c5825abd62ccb15080e59e04d22121e746d8020a0815d7c0c45132fed81b1ff36b26f5991e57a9f1d36e52aa19712abbfe1d0cbcd14252b449a919dc + languageName: node + linkType: hard + +"@vitest/spy@npm:2.0.5": + version: 2.0.5 + resolution: "@vitest/spy@npm:2.0.5" + dependencies: + tinyspy: "npm:^3.0.0" + checksum: 10/ed19f4c3bb4d3853241e8070979615138e24403ce4c137fa48c903b3af2c8b3ada2cc26aca9c1aa323bb314a457a8130a29acbb18dafd4e42737deefb2abf1ca + languageName: node + linkType: hard + +"@vitest/utils@npm:2.0.5": + version: 2.0.5 + resolution: "@vitest/utils@npm:2.0.5" + dependencies: + "@vitest/pretty-format": "npm:2.0.5" estree-walker: "npm:^3.0.3" - loupe: "npm:^2.3.7" - pretty-format: "npm:^29.7.0" - checksum: 10/5c5d7295ac13fcea1da039232bcc7c3fc6f070070fe12ba2ad152456af6e216e48a3ae169016cfcd5055706a00dc567b8f62e4a9b1914f069f52b8f0a3c25e60 + loupe: "npm:^3.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10/d631d56d29c33bc8de631166b2b6691c470187a345469dfef7048befe6027e1c6ff9552f2ee11c8a247522c325c4a64bfcc73f8f0f0c525da39cb9f190f119f8 + languageName: node + linkType: hard + +"@vitest/utils@npm:^2.1.1": + version: 2.1.9 + resolution: "@vitest/utils@npm:2.1.9" + dependencies: + "@vitest/pretty-format": "npm:2.1.9" + loupe: "npm:^3.1.2" + tinyrainbow: "npm:^1.2.0" + checksum: 10/83d62d5703a3210a2f137c25dc4e797a7a1d74d5d2e14ecc33b274c7710304fa8b5099101c98bc8d66cc2bf18a14f88ebf21f0996a99d0ee1439ae23b49f3961 languageName: node linkType: hard @@ -8990,10 +9004,10 @@ __metadata: languageName: node linkType: hard -"assertion-error@npm:^1.1.0": - version: 1.1.0 - resolution: "assertion-error@npm:1.1.0" - checksum: 10/fd9429d3a3d4fd61782eb3962ae76b6d08aa7383123fca0596020013b3ebd6647891a85b05ce821c47d1471ed1271f00b0545cf6a4326cf2fc91efcc3b0fbecf +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10/a0789dd882211b87116e81e2648ccb7f60340b34f19877dd020b39ebb4714e475eb943e14ba3e22201c221ef6645b7bfe10297e76b6ac95b48a9898c1211ce66 languageName: node linkType: hard @@ -9067,13 +9081,13 @@ __metadata: linkType: hard "axios@npm:^1.6.1": - version: 1.7.7 - resolution: "axios@npm:1.7.7" + version: 1.8.3 + resolution: "axios@npm:1.8.3" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10/7f875ea13b9298cd7b40fd09985209f7a38d38321f1118c701520939de2f113c4ba137832fe8e3f811f99a38e12c8225481011023209a77b0c0641270e20cde1 + checksum: 10/050f911cadd6d47a38ddbf91d2f8da2c34661dda8077e7ad6546e8178701125366fddbba07211a648b6815cf6c2c3c91c0a65d8b968e3d1a6054a21141ff9c01 languageName: node linkType: hard @@ -9585,6 +9599,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5 + languageName: node + linkType: hard + "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" @@ -9668,18 +9692,16 @@ __metadata: languageName: node linkType: hard -"chai@npm:^4.3.10": - version: 4.5.0 - resolution: "chai@npm:4.5.0" +"chai@npm:^5.1.1": + version: 5.1.2 + resolution: "chai@npm:5.1.2" dependencies: - assertion-error: "npm:^1.1.0" - check-error: "npm:^1.0.3" - deep-eql: "npm:^4.1.3" - get-func-name: "npm:^2.0.2" - loupe: "npm:^2.3.6" - pathval: "npm:^1.1.1" - type-detect: "npm:^4.1.0" - checksum: 10/cde341aee15b0a51559c7cfc20788dcfb4d586a498cfb93b937bb568fd45c777b73b1461274be6092b6bf868adb4e3a63f3fec13c89f7d8fb194f84c6fa42d5f + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10/e8c2bbc83cb5a2f87130d93056d4cfbbe04106e12aa798b504816dbe3fa538a9f68541b472e56cbf0f54558b501d7e31867d74b8218abcd5a8cc8ba536fba46c languageName: node linkType: hard @@ -9742,12 +9764,10 @@ __metadata: languageName: node linkType: hard -"check-error@npm:^1.0.3": - version: 1.0.3 - resolution: "check-error@npm:1.0.3" - dependencies: - get-func-name: "npm:^2.0.2" - checksum: 10/e2131025cf059b21080f4813e55b3c480419256914601750b0fee3bd9b2b8315b531e551ef12560419b8b6d92a3636511322752b1ce905703239e7cc451b6399 +"check-error@npm:^2.1.1": + version: 2.1.1 + resolution: "check-error@npm:2.1.1" + checksum: 10/d785ed17b1d4a4796b6e75c765a9a290098cf52ff9728ce0756e8ffd4293d2e419dd30c67200aee34202463b474306913f2fcfaf1890641026d9fc6966fea27a languageName: node linkType: hard @@ -10012,6 +10032,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10/cdaeb672d979816853a4eed7f1310a9319e8b976172485c2a6b437ed0db0a389a44cfb222bfbde772781efa9f215bdd1b936f80d6b249485b465c6cb906e1f93 + languageName: node + linkType: hard + "commander@npm:^2.20.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -10251,8 +10278,30 @@ __metadata: jest-util: "npm:^29.7.0" prompts: "npm:^2.0.1" bin: - create-jest: bin/create-jest.js - checksum: 10/847b4764451672b4174be4d5c6d7d63442ec3aa5f3de52af924e4d996d87d7801c18e125504f25232fc75840f6625b3ac85860fac6ce799b5efae7bdcaf4a2b7 + create-jest: bin/create-jest.js + checksum: 10/847b4764451672b4174be4d5c6d7d63442ec3aa5f3de52af924e4d996d87d7801c18e125504f25232fc75840f6625b3ac85860fac6ce799b5efae7bdcaf4a2b7 + languageName: node + linkType: hard + +"create-storybook@npm:8.5.3": + version: 8.5.3 + resolution: "create-storybook@npm:8.5.3" + dependencies: + "@types/semver": "npm:^7.3.4" + commander: "npm:^12.1.0" + execa: "npm:^5.0.0" + fd-package-json: "npm:^1.2.0" + find-up: "npm:^5.0.0" + ora: "npm:^5.4.1" + prettier: "npm:^3.1.1" + prompts: "npm:^2.4.0" + semver: "npm:^7.3.7" + storybook: "npm:8.5.3" + tiny-invariant: "npm:^1.3.1" + ts-dedent: "npm:^2.0.0" + bin: + create-storybook: ./bin/index.cjs + checksum: 10/9688206b8d77d1f9ca4ca012b2dd801569e03176123318b4e239dfa2fa1367157b0a3d4e4065af56a7416a4e1b68dd35ab617b79298d9373e6e2ef9bf197c4b4 languageName: node linkType: hard @@ -10294,15 +10343,6 @@ __metadata: languageName: node linkType: hard -"crypto-random-string@npm:^4.0.0": - version: 4.0.0 - resolution: "crypto-random-string@npm:4.0.0" - dependencies: - type-fest: "npm:^1.0.1" - checksum: 10/cd5d7ae13803de53680aaed4c732f67209af5988cbeec5f6b29082020347c2d8849ca921b2008be7d6bd1d9d198c3c3697e7441d6d0d3da1bf51e9e4d2032149 - languageName: node - linkType: hard - "css-color-keywords@npm:^1.0.0": version: 1.0.0 resolution: "css-color-keywords@npm:1.0.0" @@ -10574,12 +10614,10 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^4.1.3": - version: 4.1.4 - resolution: "deep-eql@npm:4.1.4" - dependencies: - type-detect: "npm:^4.0.0" - checksum: 10/f04f4d581f044a824a6322fe4f68fbee4d6780e93fc710cd9852cbc82bfc7010df00f0e05894b848abbe14dc3a25acac44f424e181ae64d12f2ab9d0a875a5ef +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10/a529b81e2ef8821621d20a36959a0328873a3e49d393ad11f8efe8559f31239494c2eb889b80342808674c475802ba95b9d6c4c27641b9a029405104c1b59fcf languageName: node linkType: hard @@ -10902,10 +10940,15 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:^3.1.3": - version: 3.1.6 - resolution: "dompurify@npm:3.1.6" - checksum: 10/036844bc9b717b172ba27f5863b56f950289a05d8eebfb702d6953bbf80bd021e480ce4217bd084567186f2d0ada13358ce5556963492cfe402d774e8667f120 +"dompurify@npm:^3.2.4": + version: 3.2.4 + resolution: "dompurify@npm:3.2.4" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10/98570c53385518a2f9b617f796926338856acfdd3369c88b5905bddf96bd7d391bf8a5433127155e0046e6faa2bfb767185fcd571b865dfabe624c099e2537f5 languageName: node linkType: hard @@ -10954,6 +10997,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934 + languageName: node + linkType: hard + "duplexify@npm:^3.5.0, duplexify@npm:^3.6.0": version: 3.7.1 resolution: "duplexify@npm:3.7.1" @@ -11251,6 +11305,13 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78 + languageName: node + linkType: hard + "es-errors@npm:^1.0.0, es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -11305,10 +11366,12 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.5.0": - version: 1.5.4 - resolution: "es-module-lexer@npm:1.5.4" - checksum: 10/f29c7c97a58eb17640dcbd71bd6ef754ad4f58f95c3073894573d29dae2cad43ecd2060d97ed5b866dfb7804d5590fb7de1d2c5339a5fceae8bd60b580387fc5 +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867 languageName: node linkType: hard @@ -11323,6 +11386,18 @@ __metadata: languageName: node linkType: hard +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10/86814bf8afbcd8966653f731415888019d4bc4aca6b6c354132a7a75bb87566751e320369654a101d23a91c87a85c79b178bcf40332839bd347aff437c4fb65f + languageName: node + linkType: hard + "es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2": version: 1.0.2 resolution: "es-shim-unscopables@npm:1.0.2" @@ -11343,6 +11418,18 @@ __metadata: languageName: node linkType: hard +"es-toolkit@npm:^1.22.0": + version: 1.32.0 + resolution: "es-toolkit@npm:1.32.0" + dependenciesMeta: + "@trivago/prettier-plugin-sort-imports@4.3.0": + unplugged: true + prettier-plugin-sort-re-exports@0.0.1: + unplugged: true + checksum: 10/98eaf802d5d2fbb2bd7ab69073034b7571b56abfa8e7cb046f22a501718b288c901b4f61a53f36b000549ab57c4265b2f8b86ba255e1a7d6e841ff86822e01be + languageName: node + linkType: hard + "es6-error@npm:^4.0.1": version: 4.1.1 resolution: "es6-error@npm:4.1.1" @@ -11445,33 +11532,35 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0": - version: 0.21.5 - resolution: "esbuild@npm:0.21.5" - dependencies: - "@esbuild/aix-ppc64": "npm:0.21.5" - "@esbuild/android-arm": "npm:0.21.5" - "@esbuild/android-arm64": "npm:0.21.5" - "@esbuild/android-x64": "npm:0.21.5" - "@esbuild/darwin-arm64": "npm:0.21.5" - "@esbuild/darwin-x64": "npm:0.21.5" - "@esbuild/freebsd-arm64": "npm:0.21.5" - "@esbuild/freebsd-x64": "npm:0.21.5" - "@esbuild/linux-arm": "npm:0.21.5" - "@esbuild/linux-arm64": "npm:0.21.5" - "@esbuild/linux-ia32": "npm:0.21.5" - "@esbuild/linux-loong64": "npm:0.21.5" - "@esbuild/linux-mips64el": "npm:0.21.5" - "@esbuild/linux-ppc64": "npm:0.21.5" - "@esbuild/linux-riscv64": "npm:0.21.5" - "@esbuild/linux-s390x": "npm:0.21.5" - "@esbuild/linux-x64": "npm:0.21.5" - "@esbuild/netbsd-x64": "npm:0.21.5" - "@esbuild/openbsd-x64": "npm:0.21.5" - "@esbuild/sunos-x64": "npm:0.21.5" - "@esbuild/win32-arm64": "npm:0.21.5" - "@esbuild/win32-ia32": "npm:0.21.5" - "@esbuild/win32-x64": "npm:0.21.5" +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0": + version: 0.24.2 + resolution: "esbuild@npm:0.24.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.24.2" + "@esbuild/android-arm": "npm:0.24.2" + "@esbuild/android-arm64": "npm:0.24.2" + "@esbuild/android-x64": "npm:0.24.2" + "@esbuild/darwin-arm64": "npm:0.24.2" + "@esbuild/darwin-x64": "npm:0.24.2" + "@esbuild/freebsd-arm64": "npm:0.24.2" + "@esbuild/freebsd-x64": "npm:0.24.2" + "@esbuild/linux-arm": "npm:0.24.2" + "@esbuild/linux-arm64": "npm:0.24.2" + "@esbuild/linux-ia32": "npm:0.24.2" + "@esbuild/linux-loong64": "npm:0.24.2" + "@esbuild/linux-mips64el": "npm:0.24.2" + "@esbuild/linux-ppc64": "npm:0.24.2" + "@esbuild/linux-riscv64": "npm:0.24.2" + "@esbuild/linux-s390x": "npm:0.24.2" + "@esbuild/linux-x64": "npm:0.24.2" + "@esbuild/netbsd-arm64": "npm:0.24.2" + "@esbuild/netbsd-x64": "npm:0.24.2" + "@esbuild/openbsd-arm64": "npm:0.24.2" + "@esbuild/openbsd-x64": "npm:0.24.2" + "@esbuild/sunos-x64": "npm:0.24.2" + "@esbuild/win32-arm64": "npm:0.24.2" + "@esbuild/win32-ia32": "npm:0.24.2" + "@esbuild/win32-x64": "npm:0.24.2" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -11507,8 +11596,12 @@ __metadata: optional: true "@esbuild/linux-x64": optional: true + "@esbuild/netbsd-arm64": + optional: true "@esbuild/netbsd-x64": optional: true + "@esbuild/openbsd-arm64": + optional: true "@esbuild/openbsd-x64": optional: true "@esbuild/sunos-x64": @@ -11521,7 +11614,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/d2ff2ca84d30cce8e871517374d6c2290835380dc7cd413b2d49189ed170d45e407be14de2cb4794cf76f75cf89955c4714726ebd3de7444b3046f5cab23ab6b + checksum: 10/95425071c9f24ff88bf61e0710b636ec0eb24ddf8bd1f7e1edef3044e1221104bbfa7bbb31c18018c8c36fa7902c5c0b843f829b981ebc89160cf5eebdaa58f4 languageName: node linkType: hard @@ -11862,7 +11955,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react-refresh@npm:^0.4.3, eslint-plugin-react-refresh@npm:^0.4.5": +"eslint-plugin-react-hooks@npm:^5.1.0": + version: 5.1.0 + resolution: "eslint-plugin-react-hooks@npm:5.1.0" + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + checksum: 10/b6778fd9e1940b06868921309e8b269426e17eda555816d4b71def4dcf0572de1199fdb627ac09ce42160b9569a93cd9b0fd81b740ab4df98205461c53997a43 + languageName: node + linkType: hard + +"eslint-plugin-react-refresh@npm:^0.4.5": version: 0.4.5 resolution: "eslint-plugin-react-refresh@npm:0.4.5" peerDependencies: @@ -11897,17 +11999,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-storybook@npm:^0.8.0": - version: 0.8.0 - resolution: "eslint-plugin-storybook@npm:0.8.0" +"eslint-plugin-storybook@npm:^0.11.2": + version: 0.11.2 + resolution: "eslint-plugin-storybook@npm:0.11.2" dependencies: - "@storybook/csf": "npm:^0.0.1" - "@typescript-eslint/utils": "npm:^5.62.0" - requireindex: "npm:^1.2.0" + "@storybook/csf": "npm:^0.1.11" + "@typescript-eslint/utils": "npm:^8.8.1" ts-dedent: "npm:^2.2.0" peerDependencies: - eslint: ">=6" - checksum: 10/a66e6737298af9bb830e3b14cdbd204e589a38adb810f02d843849936ef9175a80a49c8b8fa9263f8c2b9a8f36fdd3a2d429382d8051568c58d6272c65c2f5d3 + eslint: ">=8" + checksum: 10/104c2d329f2c2bf1819015adc8d0e67c0216ed5b284b4524f968412424106c97c4056720e3c26a6fc50016f872acb80c156a68009409898fe5ae40909eeafe47 languageName: node linkType: hard @@ -11972,6 +12073,13 @@ __metadata: languageName: node linkType: hard +"eslint-visitor-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-visitor-keys@npm:4.2.0" + checksum: 10/9651b3356b01760e586b4c631c5268c0e1a85236e3292bf754f0472f465bf9a856c0ddc261fceace155334118c0151778effafbab981413dbf9288349343fa25 + languageName: node + linkType: hard + "eslint@npm:^7.25.0, eslint@npm:^7.32.0": version: 7.32.0 resolution: "eslint@npm:7.32.0" @@ -12022,7 +12130,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.45.0, eslint@npm:^8.56.0": +"eslint@npm:^8.56.0": version: 8.56.0 resolution: "eslint@npm:8.56.0" dependencies: @@ -12272,7 +12380,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.3, express@npm:^4.19.2": +"express@npm:^4.17.3": version: 4.21.0 resolution: "express@npm:4.21.0" dependencies: @@ -12701,13 +12809,15 @@ __metadata: linkType: hard "form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" + version: 4.0.4 + resolution: "form-data@npm:4.0.4" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10/7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + checksum: 10/a4b62e21932f48702bc468cc26fb276d186e6b07b557e3dd7cc455872bdbb82db7db066844a64ad3cf40eaf3a753c830538183570462d3649fdfd705601cbcfb languageName: node linkType: hard @@ -12918,13 +13028,6 @@ __metadata: languageName: node linkType: hard -"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": - version: 2.0.2 - resolution: "get-func-name@npm:2.0.2" - checksum: 10/3f62f4c23647de9d46e6f76d2b3eafe58933a9b3830c60669e4180d6c601ce1b4aa310ba8366143f55e52b139f992087a9f0647274e8745621fa2af7e0acf13b - languageName: node - linkType: hard - "get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" @@ -12938,6 +13041,24 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.6": + version: 1.3.0 + resolution: "get-intrinsic@npm:1.3.0" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10/6e9dd920ff054147b6f44cb98104330e87caafae051b6d37b13384a45ba15e71af33c3baeac7cb630a0aaa23142718dcf25b45cfdd86c184c5dcb4e56d953a10 + languageName: node + linkType: hard + "get-nonce@npm:^1.0.0": version: 1.0.1 resolution: "get-nonce@npm:1.0.1" @@ -12966,6 +13087,16 @@ __metadata: languageName: node linkType: hard +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b + languageName: node + linkType: hard + "get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -13016,13 +13147,6 @@ __metadata: languageName: node linkType: hard -"github-slugger@npm:^2.0.0": - version: 2.0.0 - resolution: "github-slugger@npm:2.0.0" - checksum: 10/2fb15d78262eeba1e68671f048c62d05ed21e51281cccc7b1c9e8e089e8510b3037fb648b8ba27290e60534df2cb251312a1e7e813204495df621220192fd600 - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -13041,17 +13165,6 @@ __metadata: languageName: node linkType: hard -"glob-promise@npm:^4.2.0": - version: 4.2.2 - resolution: "glob-promise@npm:4.2.2" - dependencies: - "@types/glob": "npm:^7.1.3" - peerDependencies: - glob: ^7.1.6 - checksum: 10/c1a3d95f7c8393e4151d4899ec4e42bb2e8237160f840ad1eccbe9247407da8b6c13e28f463022e011708bc40862db87b9b77236d35afa3feb8aa86d518f2dfe - languageName: node - linkType: hard - "glob-to-regexp@npm:^0.4.1": version: 0.4.1 resolution: "glob-to-regexp@npm:0.4.1" @@ -13074,7 +13187,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.2.0": +"glob@npm:^7.0.5, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -13184,6 +13297,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1 + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -13290,7 +13410,14 @@ __metadata: languageName: node linkType: hard -"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1": +"has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1, has-tostringtag@npm:^1.0.2": version: 1.0.2 resolution: "has-tostringtag@npm:1.0.2" dependencies: @@ -13318,30 +13445,12 @@ __metadata: languageName: node linkType: hard -"hast-util-heading-rank@npm:^3.0.0": - version: 3.0.0 - resolution: "hast-util-heading-rank@npm:3.0.0" - dependencies: - "@types/hast": "npm:^3.0.0" - checksum: 10/e5ce4ec9e8017b24ab72702fa0dd401ec6eaf32574120d71c2aa4e8e0f43829dba2e291f49d305a47e8d65b82a9c5adad7985385dc5bc8370f8cec7c8f9313d3 - languageName: node - linkType: hard - -"hast-util-is-element@npm:^3.0.0": - version: 3.0.0 - resolution: "hast-util-is-element@npm:3.0.0" - dependencies: - "@types/hast": "npm:^3.0.0" - checksum: 10/b4e6d84c763ffdc44198ba0c4a5a7430794a7b2c1eec699d37776ea9832eef79f129726c175982103eb3b21f531a6bfd2fa43ce26e1ed6d8f6a87c102ba212c8 - languageName: node - linkType: hard - -"hast-util-to-string@npm:^3.0.0": - version: 3.0.0 - resolution: "hast-util-to-string@npm:3.0.0" +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" dependencies: - "@types/hast": "npm:^3.0.0" - checksum: 10/b0d51e2cf228edcbed0494755a7f095c5c2b7a0e7564f3ad7b83b89abbabf098b62b3c884e1bb4d3394c0c84486ba39800d78f2ccdbdaa38122be62330dd2357 + function-bind: "npm:^1.1.2" + checksum: 10/7898a9c1788b2862cf0f9c345a6bec77ba4a0c0983c7f19d610c382343d4f98fa260686b225dfb1f88393a66679d2ec58ee310c1d6868c081eda7918f32cc70a languageName: node linkType: hard @@ -13590,6 +13699,15 @@ __metadata: languageName: node linkType: hard +"husky@npm:^9.1.7": + version: 9.1.7 + resolution: "husky@npm:9.1.7" + bin: + husky: bin.js + checksum: 10/c2412753f15695db369634ba70f50f5c0b7e5cb13b673d0826c411ec1bd9ddef08c1dad89ea154f57da2521d2605bd64308af748749b27d08c5f563bcd89975f + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -13749,13 +13867,6 @@ __metadata: languageName: node linkType: hard -"is-absolute-url@npm:^4.0.0": - version: 4.0.1 - resolution: "is-absolute-url@npm:4.0.1" - checksum: 10/de172a718439982a54477fdae55f21be69ec0e6a4b205db5484975d2f4ee749851fd46c28f3790dfc51a274c2ed1d0f8457b6d1fff02ab829069fd9cc761e48c - languageName: node - linkType: hard - "is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" @@ -14926,14 +15037,14 @@ __metadata: linkType: hard "js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" + version: 3.14.2 + resolution: "js-yaml@npm:3.14.2" dependencies: argparse: "npm:^1.0.7" esprima: "npm:^4.0.0" bin: js-yaml: bin/js-yaml.js - checksum: 10/9e22d80b4d0105b9899135365f746d47466ed53ef4223c529b3c0f7a39907743fdbd3c4379f94f1106f02755b5e90b2faaf84801a891135544e1ea475d1a1379 + checksum: 10/172e0b6007b0bf0fc8d2469c94424f7dd765c64a047d2b790831fecef2204a4054eabf4d911eb73ab8c9a3256ab8ba1ee8d655b789bf24bf059c772acc2075a1 languageName: node linkType: hard @@ -14990,6 +15101,13 @@ __metadata: languageName: node linkType: hard +"jsdoc-type-pratt-parser@npm:^4.0.0": + version: 4.1.0 + resolution: "jsdoc-type-pratt-parser@npm:4.1.0" + checksum: 10/30d88f95f6cbb4a1aa6d4b0d0ae46eb1096e606235ecaf9bab7a3ed5da860516b5d1cd967182765002f292c627526db918f3e56d34637bcf810e6ef84d403f3f + languageName: node + linkType: hard + "jsdom@npm:^20.0.0": version: 20.0.3 resolution: "jsdom@npm:20.0.3" @@ -15437,7 +15555,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21": +"lodash@npm:^4.17.20, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 @@ -15478,12 +15596,10 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^2.3.6, loupe@npm:^2.3.7": - version: 2.3.7 - resolution: "loupe@npm:2.3.7" - dependencies: - get-func-name: "npm:^2.0.1" - checksum: 10/635c8f0914c2ce7ecfe4e239fbaf0ce1d2c00e4246fafcc4ed000bfdb1b8f89d05db1a220054175cca631ebf3894872a26fffba0124477fcb562f78762848fb1 +"loupe@npm:^3.1.0, loupe@npm:^3.1.1, loupe@npm:^3.1.2": + version: 3.1.3 + resolution: "loupe@npm:3.1.3" + checksum: 10/9e98c34daf0eba48ccc603595e51f2ae002110982d84879cf78c51de2c632f0c571dfe82ce4210af60c32203d06b443465c269bda925076fe6d9b612cc65c321 languageName: node linkType: hard @@ -15658,15 +15774,6 @@ __metadata: languageName: node linkType: hard -"markdown-to-jsx@npm:^7.4.5": - version: 7.5.0 - resolution: "markdown-to-jsx@npm:7.5.0" - peerDependencies: - react: ">= 0.14.0" - checksum: 10/b1fbe4429b968aefe02d4549eebb8d7456ccd7a8417805bb7f4bde1b466bdd0c81df3b14c5a1d9dcc49c6451ae50cf23cd04228fb6a0e1f8579ad0b76adae044 - languageName: node - linkType: hard - "marked@npm:^4.3.0": version: 4.3.0 resolution: "marked@npm:4.3.0" @@ -15676,6 +15783,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd + languageName: node + linkType: hard + "mdast-util-definitions@npm:^4.0.0": version: 4.0.0 resolution: "mdast-util-definitions@npm:4.0.0" @@ -15872,6 +15986,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10/dd6a8927b063aca6d910b119e1f2df6d2ce7d36eab91de83167dd136bb85e1ebff97b0d3de1cb08bd1f7e018ca170b4962479fefab5b2a69e2ae12cb2edc8348 + languageName: node + linkType: hard + "minimist-options@npm:^4.0.2": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -16048,11 +16171,11 @@ __metadata: linkType: hard "nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" bin: nanoid: bin/nanoid.cjs - checksum: 10/ac1eb60f615b272bccb0e2b9cd933720dad30bf9708424f691b8113826bb91aca7e9d14ef5d9415a6ba15c266b37817256f58d8ce980c82b0ba3185352565679 + checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40 languageName: node linkType: hard @@ -16799,10 +16922,10 @@ __metadata: languageName: node linkType: hard -"pathval@npm:^1.1.1": - version: 1.1.1 - resolution: "pathval@npm:1.1.1" - checksum: 10/b50a4751068aa3a5428f5a0b480deecedc6f537666a3630a0c2ae2d5e7c0f4bf0ee77b48404441ec1220bef0c91625e6030b3d3cf5a32ab0d9764018d1d9dbb6 +"pathval@npm:^2.0.0": + version: 2.0.0 + resolution: "pathval@npm:2.0.0" + checksum: 10/b91575bf9cdf01757afd7b5e521eb8a0b874a49bc972d08e0047cfea0cd3c019f5614521d4bc83d2855e3fcc331db6817dfd533dd8f3d90b16bc76fad2450fc1 languageName: node linkType: hard @@ -16904,7 +17027,16 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.47.1, playwright-core@npm:>=1.2.0": +"playwright-core@npm:1.56.1": + version: 1.56.1 + resolution: "playwright-core@npm:1.56.1" + bin: + playwright-core: cli.js + checksum: 10/df785eb3b3a8392b10dcde5f768e09b7fe459a7b06ed81180da69e048f2154b761f86d79572c2b62037a1f18a44e4ace72f5b6547f4f473b4ab13ab1d94007d2 + languageName: node + linkType: hard + +"playwright-core@npm:>=1.2.0": version: 1.47.1 resolution: "playwright-core@npm:1.47.1" bin: @@ -16913,18 +17045,18 @@ __metadata: languageName: node linkType: hard -"playwright@npm:^1.14.0, playwright@npm:^1.47.1": - version: 1.47.1 - resolution: "playwright@npm:1.47.1" +"playwright@npm:^1.14.0, playwright@npm:^1.55.1": + version: 1.56.1 + resolution: "playwright@npm:1.56.1" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.47.1" + playwright-core: "npm:1.56.1" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10/f4340f28485bd83a856a365a03af2013b3203c8134a075d05f1a834665e1204b98141c335d5c5c1600720c9e07db702db3dbff5ef138fc1c31d5feec3ac0057f + checksum: 10/f1743f93b26f1d497257771428d93f3c9ed2d75b00d935f0cd1556ff2dc61d47f2df8b381d752fbd2c47082b685f0ffe4cc4b7ba440d7b4ba3a08572aec58fba languageName: node linkType: hard @@ -17411,15 +17543,14 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0": - version: 18.2.0 - resolution: "react-dom@npm:18.2.0" +"react-dom@npm:^19.0.0": + version: 19.0.0 + resolution: "react-dom@npm:19.0.0" dependencies: - loose-envify: "npm:^1.1.0" - scheduler: "npm:^0.23.0" + scheduler: "npm:^0.25.0" peerDependencies: - react: ^18.2.0 - checksum: 10/ca5e7762ec8c17a472a3605b6f111895c9f87ac7d43a610ab7024f68cd833d08eda0625ce02ec7178cc1f3c957cf0b9273cdc17aa2cd02da87544331c43b1d21 + react: ^19.0.0 + checksum: 10/aa64a2f1991042f516260e8b0eca0ae777b6c8f1aa2b5ae096e80bbb6ac9b005aef2bca697969841d34f7e1819556263476bdfea36c35092e8d9aefde3de2d9a languageName: node linkType: hard @@ -17615,12 +17746,10 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": - version: 18.2.0 - resolution: "react@npm:18.2.0" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10/b9214a9bd79e99d08de55f8bef2b7fc8c39630be97c4e29d7be173d14a9a10670b5325e94485f74cd8bff4966ef3c78ee53c79a7b0b9b70cba20aa8973acc694 +"react@npm:^19.0.0": + version: 19.0.0 + resolution: "react@npm:19.0.0" + checksum: 10/2490969c503f644703c88990d20e4011fa6119ddeca451e9de48f6d7ab058d670d2852a5fcd3aa3cd90a923ab2815d532637bd4a814add402ae5c0d4f129ee71 languageName: node linkType: hard @@ -17833,33 +17962,6 @@ __metadata: languageName: node linkType: hard -"rehype-external-links@npm:^3.0.0": - version: 3.0.0 - resolution: "rehype-external-links@npm:3.0.0" - dependencies: - "@types/hast": "npm:^3.0.0" - "@ungap/structured-clone": "npm:^1.0.0" - hast-util-is-element: "npm:^3.0.0" - is-absolute-url: "npm:^4.0.0" - space-separated-tokens: "npm:^2.0.0" - unist-util-visit: "npm:^5.0.0" - checksum: 10/b9b2e4e5974a7d1e4030dc42bfad980d4700af35b6b20b36fc7ff0521897a8f20d3fe5e170255c428148fdd5a0653a73683da783124038d17b24f26dd59d20e8 - languageName: node - linkType: hard - -"rehype-slug@npm:^6.0.0": - version: 6.0.0 - resolution: "rehype-slug@npm:6.0.0" - dependencies: - "@types/hast": "npm:^3.0.0" - github-slugger: "npm:^2.0.0" - hast-util-heading-rank: "npm:^3.0.0" - hast-util-to-string: "npm:^3.0.0" - unist-util-visit: "npm:^5.0.0" - checksum: 10/292074643f8462c70f498bc8bf18a8c959073b96efc249f61e69fa9e36eb81d9b91d62199a90217c604e1c3904e8ff0a75df70d67e41bead56de93afb725c2d0 - languageName: node - linkType: hard - "relateurl@npm:^0.2.7": version: 0.2.7 resolution: "relateurl@npm:0.2.7" @@ -17934,13 +18036,6 @@ __metadata: languageName: node linkType: hard -"requireindex@npm:^1.2.0": - version: 1.2.0 - resolution: "requireindex@npm:1.2.0" - checksum: 10/266d1cb31f6cbc4b6cf2e898f5bbc45581f7919bcf61bba5c45d0adb69b722b9ff5a13727be3350cde4520d7cd37f39df45d58a29854baaa4552cd6b05ae4a1a - languageName: node - linkType: hard - "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -18238,15 +18333,6 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.0": - version: 0.23.0 - resolution: "scheduler@npm:0.23.0" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10/0c4557aa37bafca44ff21dc0ea7c92e2dbcb298bc62eae92b29a39b029134f02fb23917d6ebc8b1fa536b4184934314c20d8864d156a9f6357f3398aaf7bfda8 - languageName: node - linkType: hard - "scheduler@npm:^0.23.2": version: 0.23.2 resolution: "scheduler@npm:0.23.2" @@ -18256,6 +18342,13 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.25.0": + version: 0.25.0 + resolution: "scheduler@npm:0.25.0" + checksum: 10/e661e38503ab29a153429a99203fefa764f28b35c079719eb5efdd2c1c1086522f6653d8ffce388209682c23891a6d1d32fa6badf53c35fb5b9cd0c55ace42de + languageName: node + linkType: hard + "schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" @@ -18308,6 +18401,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.6.0, semver@npm:^7.6.2": + version: 7.7.0 + resolution: "semver@npm:7.7.0" + bin: + semver: bin/semver.js + checksum: 10/5d615860a54ff563955c451e467bff3aaf74c8d060489f936f25551d5ca05f5ac683eb46c9ed7ade082e1e53b313f205ed9c5df0b25ebb3517ec25c79e1f0d9c + languageName: node + linkType: hard + "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" @@ -18557,32 +18659,32 @@ __metadata: resolution: "smithy@workspace:apps/smithy" dependencies: "@frigade/react": "workspace:*" - "@storybook/addon-docs": "npm:^8.2.9" - "@storybook/addon-essentials": "npm:^8.2.9" - "@storybook/addon-interactions": "npm:^8.2.9" - "@storybook/addon-links": "npm:^8.2.9" - "@storybook/blocks": "npm:^8.2.9" - "@storybook/cli": "npm:^8.2.9" - "@storybook/react": "npm:^8.2.9" - "@storybook/react-vite": "npm:^8.2.9" - "@storybook/test": "npm:^8.2.9" - "@storybook/test-runner": "npm:^0.19.1" - "@types/react": "npm:^18.2.34" - "@types/react-dom": "npm:^18.2.14" - "@typescript-eslint/eslint-plugin": "npm:^6.0.0" - "@typescript-eslint/parser": "npm:^6.0.0" + "@storybook/addon-docs": "npm:^8.5.3" + "@storybook/addon-essentials": "npm:^8.5.3" + "@storybook/addon-interactions": "npm:^8.5.3" + "@storybook/addon-links": "npm:^8.5.3" + "@storybook/blocks": "npm:^8.5.3" + "@storybook/cli": "npm:^8.5.3" + "@storybook/react": "npm:^8.5.3" + "@storybook/react-vite": "npm:^8.5.3" + "@storybook/test": "npm:^8.5.3" + "@storybook/test-runner": "npm:^0.21.0" + "@types/react": "npm:^19.0.0" + "@types/react-dom": "npm:^19.0.0" + "@typescript-eslint/eslint-plugin": "npm:^6.20.0" + "@typescript-eslint/parser": "npm:^6.20.0" "@vitejs/plugin-react": "npm:^4.0.3" - eslint: "npm:^8.45.0" + eslint: "npm:^8.56.0" eslint-plugin-react-hooks: "npm:^4.6.0" - eslint-plugin-react-refresh: "npm:^0.4.3" - eslint-plugin-storybook: "npm:^0.8.0" - playwright: "npm:^1.47.1" + eslint-plugin-react-refresh: "npm:^0.4.5" + eslint-plugin-storybook: "npm:^0.11.2" + playwright: "npm:^1.55.1" prop-types: "npm:^15.8.1" - react: "npm:^18.2.0" - react-dom: "npm:^18.2.0" - storybook: "npm:^8.2.9" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" + storybook: "npm:^8.5.3" typescript: "npm:^4.9.4" - vite: "npm:^4.5.5" + vite: "npm:^4.5.14" languageName: unknown linkType: soft @@ -18671,13 +18773,6 @@ __metadata: languageName: node linkType: hard -"space-separated-tokens@npm:^2.0.0": - version: 2.0.2 - resolution: "space-separated-tokens@npm:2.0.2" - checksum: 10/202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990 - languageName: node - linkType: hard - "spawn-wrap@npm:^2.0.0": version: 2.0.0 resolution: "spawn-wrap@npm:2.0.0" @@ -18804,49 +18899,27 @@ __metadata: linkType: hard "store2@npm:^2.14.2": - version: 2.14.3 - resolution: "store2@npm:2.14.3" - checksum: 10/f95f6fbacff14cc3bb9e5e16ced2f29e2d706e30b248c16cf19abed8b2bb31d8f3907c8ccf1a5284d806fdcaf06e96710e4f4f52195e51522a452536beaf7af9 + version: 2.14.4 + resolution: "store2@npm:2.14.4" + checksum: 10/c92713e75544693b47053531b6e98d7b538512061017e06b8c7e99937da67866856227862f7bf1a818ecc36673b8d343c7b67c9cce03bd4ba0f68eff7cccaa8c languageName: node linkType: hard -"storybook@npm:8.2.9, storybook@npm:^8.2.9": - version: 8.2.9 - resolution: "storybook@npm:8.2.9" +"storybook@npm:8.5.3, storybook@npm:^8.5.3": + version: 8.5.3 + resolution: "storybook@npm:8.5.3" dependencies: - "@babel/core": "npm:^7.24.4" - "@babel/types": "npm:^7.24.0" - "@storybook/codemod": "npm:8.2.9" - "@storybook/core": "npm:8.2.9" - "@types/semver": "npm:^7.3.4" - "@yarnpkg/fslib": "npm:2.10.3" - "@yarnpkg/libzip": "npm:2.3.0" - chalk: "npm:^4.1.0" - commander: "npm:^6.2.1" - cross-spawn: "npm:^7.0.3" - detect-indent: "npm:^6.1.0" - envinfo: "npm:^7.7.3" - execa: "npm:^5.0.0" - fd-package-json: "npm:^1.2.0" - find-up: "npm:^5.0.0" - fs-extra: "npm:^11.1.0" - giget: "npm:^1.0.0" - globby: "npm:^14.0.1" - jscodeshift: "npm:^0.15.1" - leven: "npm:^3.1.0" - ora: "npm:^5.4.1" - prettier: "npm:^3.1.1" - prompts: "npm:^2.4.0" - semver: "npm:^7.3.7" - strip-json-comments: "npm:^3.0.1" - tempy: "npm:^3.1.0" - tiny-invariant: "npm:^1.3.1" - ts-dedent: "npm:^2.0.0" + "@storybook/core": "npm:8.5.3" + peerDependencies: + prettier: ^2 || ^3 + peerDependenciesMeta: + prettier: + optional: true bin: getstorybook: ./bin/index.cjs sb: ./bin/index.cjs storybook: ./bin/index.cjs - checksum: 10/2d5473ba1ab31067d07c63d79799db05cf81927f517945999d124a337f209d685b2e1e4ff37d13924410ec5582d28f474fee6dee98be08f079869ec831c10df8 + checksum: 10/776bef6f61faa4ccfdf5a8044c853099c3af300b268dc127cbf17ba7a6485d7a275f027170f8ef41e32e59b1d6da15b72a6f033266873f25325d7028291fee36 languageName: node linkType: hard @@ -19250,14 +19323,14 @@ __metadata: linkType: hard "tar-fs@npm:^2.1.1": - version: 2.1.1 - resolution: "tar-fs@npm:2.1.1" + version: 2.1.4 + resolution: "tar-fs@npm:2.1.4" dependencies: chownr: "npm:^1.1.1" mkdirp-classic: "npm:^0.5.2" pump: "npm:^3.0.0" tar-stream: "npm:^2.1.4" - checksum: 10/526deae025453e825f87650808969662fbb12eb0461d033e9b447de60ec951c6c4607d0afe7ce057defe9d4e45cf80399dd74bc15f9d9e0773d5e990a78ce4ac + checksum: 10/bdf7e3cb039522e39c6dae3084b1bca8d7bcc1de1906eae4a1caea6a2250d22d26dcc234118bf879b345d91ebf250a744b196e379334a4abcbb109a78db7d3be languageName: node linkType: hard @@ -19304,13 +19377,6 @@ __metadata: languageName: node linkType: hard -"temp-dir@npm:^3.0.0": - version: 3.0.0 - resolution: "temp-dir@npm:3.0.0" - checksum: 10/577211e995d1d584dd60f1469351d45e8a5b4524e4a9e42d3bdd12cfde1d0bb8f5898311bef24e02aaafb69514c1feb58c7b4c33dcec7129da3b0861a4ca935b - languageName: node - linkType: hard - "temp@npm:^0.8.4": version: 0.8.4 resolution: "temp@npm:0.8.4" @@ -19333,18 +19399,6 @@ __metadata: languageName: node linkType: hard -"tempy@npm:^3.1.0": - version: 3.1.0 - resolution: "tempy@npm:3.1.0" - dependencies: - is-stream: "npm:^3.0.0" - temp-dir: "npm:^3.0.0" - type-fest: "npm:^2.12.2" - unique-string: "npm:^3.0.0" - checksum: 10/f5540bc24dcd9d41ab0b31e9eed73c3ef825080f1c8b1e854e4b73059155c889f72f5f7c15e8cd462d59aa10c9726e423c81d6a365d614b538c6cc78a1209cc6 - languageName: node - linkType: hard - "term-size@npm:^2.1.0": version: 2.2.1 resolution: "term-size@npm:2.2.1" @@ -19448,10 +19502,17 @@ __metadata: languageName: node linkType: hard -"tinyspy@npm:^2.2.0": - version: 2.2.1 - resolution: "tinyspy@npm:2.2.1" - checksum: 10/170d6232e87f9044f537b50b406a38fbfd6f79a261cd12b92879947bd340939a833a678632ce4f5c4a6feab4477e9c21cd43faac3b90b68b77dd0536c4149736 +"tinyrainbow@npm:^1.2.0": + version: 1.2.0 + resolution: "tinyrainbow@npm:1.2.0" + checksum: 10/2924444db6804355e5ba2b6e586c7f77329d93abdd7257a069a0f4530dff9f16de484e80479094e3f39273462541b003a65ee3a6afc2d12555aa745132deba5d + languageName: node + linkType: hard + +"tinyspy@npm:^3.0.0": + version: 3.0.2 + resolution: "tinyspy@npm:3.0.2" + checksum: 10/5db671b2ff5cd309de650c8c4761ca945459d7204afb1776db9a04fb4efa28a75f08517a8620c01ee32a577748802231ad92f7d5b194dc003ee7f987a2a06337 languageName: node linkType: hard @@ -19572,6 +19633,15 @@ __metadata: languageName: node linkType: hard +"ts-api-utils@npm:^2.0.1": + version: 2.0.1 + resolution: "ts-api-utils@npm:2.0.1" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10/2e68938cd5acad6b5157744215ce10cd097f9f667fd36b5fdd5efdd4b0c51063e855459d835f94f6777bb8a0f334916b6eb5c1eedab8c325feb34baa39238898 + languageName: node + linkType: hard + "ts-dedent@npm:^2.0.0, ts-dedent@npm:^2.2.0": version: 2.2.0 resolution: "ts-dedent@npm:2.2.0" @@ -19807,13 +19877,6 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:^4.0.0, type-detect@npm:^4.1.0": - version: 4.1.0 - resolution: "type-detect@npm:4.1.0" - checksum: 10/e363bf0352427a79301f26a7795a27718624c49c576965076624eb5495d87515030b207217845f7018093adcbe169b2d119bb9b7f1a31a92bfbb1ab9639ca8dd - languageName: node - linkType: hard - "type-fest@npm:^0.13.1": version: 0.13.1 resolution: "type-fest@npm:0.13.1" @@ -19856,14 +19919,14 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.1, type-fest@npm:^1.0.2": +"type-fest@npm:^1.0.2": version: 1.4.0 resolution: "type-fest@npm:1.4.0" checksum: 10/89875c247564601c2650bacad5ff80b859007fbdb6c9e43713ae3ffa3f584552eea60f33711dd762e16496a1ab4debd409822627be14097d9a17e39c49db591a languageName: node linkType: hard -"type-fest@npm:^2.12.2, type-fest@npm:^2.19.0, type-fest@npm:~2.19": +"type-fest@npm:^2.19.0, type-fest@npm:~2.19": version: 2.19.0 resolution: "type-fest@npm:2.19.0" checksum: 10/7bf9e8fdf34f92c8bb364c0af14ca875fac7e0183f2985498b77be129dc1b3b1ad0a6b3281580f19e48c6105c037fb966ad9934520c69c6434d17fd0af4eed78 @@ -20123,15 +20186,6 @@ __metadata: languageName: node linkType: hard -"unique-string@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-string@npm:3.0.0" - dependencies: - crypto-random-string: "npm:^4.0.0" - checksum: 10/1a1e2e7d02eab1bb10f720475da735e1990c8a5ff34edd1a3b6bc31590cb4210b7a1233d779360cc622ce11c211e43afa1628dd658f35d3e6a89964b622940df - languageName: node - linkType: hard - "unist-util-is@npm:^4.0.0": version: 4.1.0 resolution: "unist-util-is@npm:4.1.0" @@ -20139,15 +20193,6 @@ __metadata: languageName: node linkType: hard -"unist-util-is@npm:^6.0.0": - version: 6.0.0 - resolution: "unist-util-is@npm:6.0.0" - dependencies: - "@types/unist": "npm:^3.0.0" - checksum: 10/edd6a93fb2255addf4b9eeb304c1da63c62179aef793169dd64ab955cf2f6814885fe25f95f8105893e3562dead348af535718d7a84333826e0491c04bf42511 - languageName: node - linkType: hard - "unist-util-visit-parents@npm:^3.0.0": version: 3.1.1 resolution: "unist-util-visit-parents@npm:3.1.1" @@ -20158,16 +20203,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit-parents@npm:^6.0.0": - version: 6.0.1 - resolution: "unist-util-visit-parents@npm:6.0.1" - dependencies: - "@types/unist": "npm:^3.0.0" - unist-util-is: "npm:^6.0.0" - checksum: 10/645b3cbc5e923bc692b1eb1a9ca17bffc5aabc25e6090ff3f1489bff8effd1890b28f7a09dc853cb6a7fa0da8581bfebc9b670a68b53c4c086cb9610dfd37701 - languageName: node - linkType: hard - "unist-util-visit@npm:^2.0.0": version: 2.0.3 resolution: "unist-util-visit@npm:2.0.3" @@ -20179,17 +20214,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:^5.0.0": - version: 5.0.0 - resolution: "unist-util-visit@npm:5.0.0" - dependencies: - "@types/unist": "npm:^3.0.0" - unist-util-is: "npm:^6.0.0" - unist-util-visit-parents: "npm:^6.0.0" - checksum: 10/f2bbde23641e9ade7640358c06ddeec0f38342322eb8e7819d9ee380b0f859d25d084dde22bf63db0280b3b2f36575f15aa1d6c23acf276c91c2493cf799e3b0 - languageName: node - linkType: hard - "universalify@npm:^0.1.0": version: 0.1.2 resolution: "universalify@npm:0.1.2" @@ -20346,6 +20370,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.4.0": + version: 1.4.0 + resolution: "use-sync-external-store@npm:1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10/08bf581a8a2effaefc355e9d18ed025d436230f4cc973db2f593166df357cf63e47b9097b6e5089b594758bde322e1737754ad64905e030d70f8ff7ee671fd01 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -20433,9 +20466,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^4.5.5": - version: 4.5.5 - resolution: "vite@npm:4.5.5" +"vite@npm:^4.5.14": + version: 4.5.14 + resolution: "vite@npm:4.5.14" dependencies: esbuild: "npm:^0.18.10" fsevents: "npm:~2.3.2" @@ -20469,7 +20502,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/2e8b39e004f2b2e72506b816700b3aafaf3f85bdcb9dd9392075bb234ce7333f859a0d2078a85ce5d0039f0f659ca564a7366af587848e5166f14b136cb0ad37 + checksum: 10/316a02f0d310511241fae15e6085c9f7285e87b7a75b7b04216925e72ec17107ae6f666181c79c711f6008fdaa8a0b4510280c68ce0bfab436ebea2470783d0a languageName: node linkType: hard