diff --git a/.eslintrc.js b/.eslintrc.js index 46722ae2da..92f30898c7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ module.exports = { root: true, // This tells ESLint to load the config from the package `eslint-config-custom` - extends: ["custom"], + extends: ["@webstudio-is/eslint-config-custom"], }; diff --git a/.github/actions/publish/action.yaml b/.github/actions/publish/action.yaml new file mode 100644 index 0000000000..24a129d324 --- /dev/null +++ b/.github/actions/publish/action.yaml @@ -0,0 +1,28 @@ +name: 'Release NPM package' +description: 'Release NPM package' +inputs: + package: + description: 'Package.json location (relative)' + default: 'package.json' + required: true + token: + description: 'NPM Token' + default: 'null' + required: true +runs: + using: "composite" + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install Dependencies + run: yarn install + shell: bash + - uses: JS-DevTools/npm-publish@v1 + with: + token: ${{ inputs.token }} + dry-run: false + check-version: true + access: 'public' + package: ${{ inputs.package }} \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cef4574ccc..9436f72f4e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,7 +14,7 @@ ## Before merging -- [ ] tested locally and on preview environment (preview dev login: 5de6e) +- [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio-designer/blob/main/apps/designer/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env.example` and the `designer/env-check.js` if mandatory diff --git a/.github/workflows/design-system.yml b/.github/workflows/design-system.yml new file mode 100644 index 0000000000..109456bc75 --- /dev/null +++ b/.github/workflows/design-system.yml @@ -0,0 +1,16 @@ +name: Design System Publish +on: + push: + paths: + - "packages/design-system/**/*" + - ".github/workflows/design-system.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/design-system/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/eslint-config-custom.yml b/.github/workflows/eslint-config-custom.yml new file mode 100644 index 0000000000..3effb53420 --- /dev/null +++ b/.github/workflows/eslint-config-custom.yml @@ -0,0 +1,16 @@ +name: Eslint Config Publish +on: + push: + paths: + - "packages/eslint-config-custom/**/*" + - ".github/workflows/eslint-config-custom.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/eslint-config-custom/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/icons.yml b/.github/workflows/icons.yml new file mode 100644 index 0000000000..3deb808f0b --- /dev/null +++ b/.github/workflows/icons.yml @@ -0,0 +1,16 @@ +name: Icons Publish +on: + push: + paths: + - "packages/icons/**/*" + - ".github/workflows/icons-publish.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/icons/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/jest-config.yml b/.github/workflows/jest-config.yml new file mode 100644 index 0000000000..2d5a1079cb --- /dev/null +++ b/.github/workflows/jest-config.yml @@ -0,0 +1,16 @@ +name: Jest Config Publish +on: + push: + paths: + - "packages/jest-config/**/*" + - ".github/workflows/jest-config.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/jest-config/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/react-sdk-publish.yml b/.github/workflows/react-sdk-publish.yml deleted file mode 100644 index ff7d173cff..0000000000 --- a/.github/workflows/react-sdk-publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: SDK publish -on: - push: - paths: - - 'packages/react-sdk/**/*' - - '.github/workflows/react-sdk-publish.yml' -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: Install Dependencies - run: yarn install - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - dry-run: false - check-version: true - package: './packages/react-sdk/package.json' \ No newline at end of file diff --git a/.github/workflows/react-sdk.yml b/.github/workflows/react-sdk.yml new file mode 100644 index 0000000000..9a122a632b --- /dev/null +++ b/.github/workflows/react-sdk.yml @@ -0,0 +1,16 @@ +name: SDK publish +on: + push: + paths: + - "packages/react-sdk/**/*" + - ".github/workflows/react-sdk.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/react-sdk/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/tsconfig.yml b/.github/workflows/tsconfig.yml new file mode 100644 index 0000000000..f9b6ed774e --- /dev/null +++ b/.github/workflows/tsconfig.yml @@ -0,0 +1,16 @@ +name: Tsconfig Publish +on: + push: + paths: + - "packages/tsconfig/**/*" + - ".github/workflows/tsconfig.yml" + - ".github/actions/publish/action.yaml" +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/publish + with: + package: "./packages/tsconfig/package.json" + token: ${{ secrets.NPM_TOKEN }} diff --git a/apps/designer/app/designer/features/inspector/inspector.tsx b/apps/designer/app/designer/features/inspector/inspector.tsx index f9e428fe36..66dd3caf17 100644 --- a/apps/designer/app/designer/features/inspector/inspector.tsx +++ b/apps/designer/app/designer/features/inspector/inspector.tsx @@ -28,9 +28,9 @@ export const Inspector = ({ publish }: InspectorProps) => { if (selectedInstanceData === undefined) { return ( - + {/* @todo: use this space for something more usefull: a-la figma's no instance selected sate, maybe create an issue with a more specific proposal? */} - + Select an instance on the canvas diff --git a/apps/designer/app/designer/features/sidebar-left/panels/asset-manager.tsx b/apps/designer/app/designer/features/sidebar-left/panels/asset-manager.tsx index 5cd97686ea..df64934c85 100644 --- a/apps/designer/app/designer/features/sidebar-left/panels/asset-manager.tsx +++ b/apps/designer/app/designer/features/sidebar-left/panels/asset-manager.tsx @@ -2,48 +2,53 @@ import { ImageIcon } from "@webstudio-is/icons"; import { Flex, Grid, Heading } from "@webstudio-is/design-system"; import { useEffect, useState } from "react"; import { useActionData } from "@remix-run/react"; -import { Asset } from "@webstudio-is/prisma-client"; -import { AssetManagerImage } from "./components/image"; +import { AssetManagerThumbnail } from "./components/thumbnail"; import { AddAnAssetForm } from "./components/add-an-asset-form"; -import { UploadingAsset } from "../types"; +import { Asset } from "../types"; + +export const useAssetsState = (baseAssets: Array) => { + const imageChanges = useActionData(); + + const [assets, setAssets] = useState(baseAssets); + + useEffect(() => { + if (imageChanges?.uploadedAssets?.length) { + setAssets((currentAssets) => [ + ...imageChanges.uploadedAssets, + ...currentAssets.filter((asset) => asset.status !== "uploading"), + ]); + } + if (imageChanges?.deletedAsset?.id) { + setAssets((currentAssets) => [ + ...currentAssets.filter( + (asset) => asset.id !== imageChanges.deletedAsset.id + ), + ]); + } + }, [imageChanges]); + + const onUploadAsset = (uploadedAssets: Array) => + setAssets((assets) => [...uploadedAssets, ...assets]); + + return { assets, onUploadAsset }; +}; export const TabContent = ({ assets: baseAssets, }: { - assets: Array; + assets: Array; }) => { - const newImages = useActionData(); - - const [assets, setAssets] = useState(baseAssets); - - useEffect(() => { - if (newImages?.length) { - setAssets((currentAssets) => [ - ...newImages, - ...currentAssets.filter((asset) => asset.status !== "uploading"), - ]); - } - }, [newImages]); - + const { assets, onUploadAsset } = useAssetsState(baseAssets); return ( - + Assets - ) => - setAssets((assets) => [...uploadedAssets, ...assets]) - } - /> + {assets.map((asset) => ( - + ))} diff --git a/apps/designer/app/designer/features/sidebar-left/panels/components/image.tsx b/apps/designer/app/designer/features/sidebar-left/panels/components/image.tsx deleted file mode 100644 index c52cf3925c..0000000000 --- a/apps/designer/app/designer/features/sidebar-left/panels/components/image.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useEffect, useState } from "react"; -import { Box, ProgressBar } from "@webstudio-is/design-system"; -import { useInterval } from "react-use"; -import placeholderImage from "~/shared/images/image-placeholder.svg"; -import brokenImage from "~/shared/images/broken-image-placeholder.svg"; -import { Asset } from "@webstudio-is/prisma-client"; - -const useImageWithFallback = ({ path }: { path: string }) => { - const [src, setSrc] = useState(placeholderImage); - - useEffect(() => { - const newImage = new Image(); - newImage.onload = () => setSrc(path); - newImage.onerror = () => setSrc(brokenImage); - newImage.src = path; - }, [path]); - - return src; -}; - -export const AssetManagerImage = ({ - path, - alt, - status, -}: { - path: string; - alt?: string; - status?: Asset["status"]; -}) => { - const isUploading = status === "uploading"; - const src = useImageWithFallback({ path }); - const [progressBarPercentage, setProgressBarPercentage] = useState(0); - - // @todo rewrite this fake indication to show real progress - useInterval( - () => { - setProgressBarPercentage((percentage) => - percentage < 60 ? percentage + 1 : percentage - ); - }, - isUploading ? 100 : null - ); - - return ( - - - {isUploading && ( - - )} - - ); -}; diff --git a/apps/designer/app/designer/features/sidebar-left/panels/components/thumbnail.tsx b/apps/designer/app/designer/features/sidebar-left/panels/components/thumbnail.tsx new file mode 100644 index 0000000000..894f95ea71 --- /dev/null +++ b/apps/designer/app/designer/features/sidebar-left/panels/components/thumbnail.tsx @@ -0,0 +1,142 @@ +import { useEffect, useState } from "react"; +import { + Box, + Button, + Flex, + ProgressBar, + Tooltip, +} from "@webstudio-is/design-system"; +import { useInterval } from "react-use"; +import placeholderImage from "~/shared/images/image-placeholder.svg"; +import brokenImage from "~/shared/images/broken-image-placeholder.svg"; +import { useSubmit } from "@remix-run/react"; +import { Asset } from "~/designer/features/sidebar-left/types"; +import { Cross2Icon } from "@radix-ui/react-icons"; + +const useImageWithFallback = ({ path }: { path: string }) => { + const [src, setSrc] = useState(placeholderImage); + + useEffect(() => { + const newImage = new Image(); + newImage.onload = () => setSrc(path); + newImage.onerror = () => setSrc(brokenImage); + newImage.src = path; + }, [path]); + + return src; +}; + +const useFakeProgress = ({ + isUploading, + isDeleting, +}: { + isUploading: boolean; + isDeleting: boolean; +}) => { + const [progressBarPercentage, setProgressBarPercentage] = useState(0); + + // @todo rewrite this fake indication to show real progress + useInterval( + () => { + setProgressBarPercentage((percentage) => + percentage < 60 ? percentage + 1 : percentage + ); + }, + isUploading || isDeleting ? 100 : null + ); + + return progressBarPercentage; +}; + +export const AssetManagerThumbnail = ({ + path, + alt, + status, + name, + id, +}: Asset) => { + const submit = useSubmit(); + const [isDeleting, setIsDeleting] = useState(false); + const isUploading = status === "uploading"; + const src = useImageWithFallback({ path }); + const [isTooltipOpen, setTolltipOpen] = useState(false); + const progressBarPercentage = useFakeProgress({ isUploading, isDeleting }); + + const closeTooltip = () => setTolltipOpen(false); + + const deleteAsset = () => { + const formData = new FormData(); + formData.append("assetId", id); + formData.append("assetName", name); + closeTooltip(); + setIsDeleting(true); + submit(formData, { method: "delete" }); + }; + + return ( + + + {!isUploading && ( + + Are you sure you want to delete this asset? + + + } + > + + + )} + {(isUploading || isDeleting) && ( + + )} + + ); +}; diff --git a/apps/designer/app/designer/features/sidebar-left/types.ts b/apps/designer/app/designer/features/sidebar-left/types.ts index 29fd62a28d..4783a96849 100644 --- a/apps/designer/app/designer/features/sidebar-left/types.ts +++ b/apps/designer/app/designer/features/sidebar-left/types.ts @@ -1,9 +1,10 @@ -import { Asset } from "@webstudio-is/prisma-client"; +import { Asset as BaseAsset } from "@webstudio-is/prisma-client"; import { panels } from "./panels"; export type TabName = keyof typeof panels | "none"; export type UploadingAsset = Pick< - Asset, + BaseAsset, "id" | "status" | "name" | "path" | "alt" >; +export type Asset = BaseAsset | UploadingAsset; diff --git a/apps/designer/app/root.tsx b/apps/designer/app/root.tsx index a73cf3cffd..9013b2cf39 100644 --- a/apps/designer/app/root.tsx +++ b/apps/designer/app/root.tsx @@ -1,4 +1,5 @@ // Our root outlet doesn't contain a layout because we have 2 types of documents: canvas and designer and we need to decide down the line which one to render, thre is no single root document. +import { TooltipProvider } from "@radix-ui/react-tooltip"; import { Outlet } from "@remix-run/react"; import { withSentryRouteTracing } from "@sentry/remix"; import { ErrorBoundary } from "@sentry/remix"; @@ -7,7 +8,9 @@ import { OutletProps } from "react-router-dom"; const RootWithErrorBoundary = (props: OutletProps) => ( // @ts-expect-error 'ErrorBoundary' cannot be used as a JSX component. - + + + ); diff --git a/apps/designer/app/routes/dashboard/index.tsx b/apps/designer/app/routes/dashboard/index.tsx index 6dfbbf0ef8..92d903e1bf 100644 --- a/apps/designer/app/routes/dashboard/index.tsx +++ b/apps/designer/app/routes/dashboard/index.tsx @@ -10,12 +10,16 @@ import * as db from "~/shared/db"; import config from "~/config"; import { ensureUserCookie } from "~/shared/session"; import { authenticator } from "~/services/auth.server"; +import { zfd } from "zod-form-data"; export { links }; +const schema = zfd.formData({ + project: zfd.text(), +}); + export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData(); - const title = formData.get("project"); - if (typeof title !== "string") return { errors: "Title required" }; + const { project: title } = schema.parse(await request.formData()); + const { userId, headers } = await ensureUserCookie(request); const authenticatedUser = await authenticator.isAuthenticated(request); try { diff --git a/apps/designer/app/routes/designer/$id.tsx b/apps/designer/app/routes/designer/$id.tsx index 322389f433..40fb5a4597 100644 --- a/apps/designer/app/routes/designer/$id.tsx +++ b/apps/designer/app/routes/designer/$id.tsx @@ -9,7 +9,11 @@ import { uploadAssets } from "~/shared/db/misc.server"; import { ErrorMessage } from "~/shared/error"; // if this file does not end in .server remix will not build // since it only allows node code in those files -import { loadByProject } from "@webstudio-is/asset-uploader/index.server"; +import { + deleteAsset, + loadByProject, +} from "@webstudio-is/asset-uploader/index.server"; +import { zfd } from "zod-form-data"; export { links }; @@ -33,19 +37,44 @@ type Error = { errors: "string"; }; +const deleteAssetSchema = zfd.formData({ + assetId: zfd.text(), + assetName: zfd.text(), +}); + export const action: ActionFunction = async ({ request, params }) => { if (params.id === undefined) throw new Error("Project id undefined"); + try { + if (request.method === "DELETE") { + const { assetId, assetName } = deleteAssetSchema.parse( + await request.formData() + ); + const deletedAsset = await deleteAsset({ + id: assetId, + name: assetName, + }); + + return { deletedAsset }; + } + } catch (error) { + if (error instanceof Error) { + return { + errors: error.message, + }; + } + } if (request.method === "POST") { try { const assets = await uploadAssets({ request, projectId: params.id, - dirname: __dirname, }); - return assets.map((asset: Asset) => ({ - ...asset, - status: "uploaded", - })); + return { + uploadedAssets: assets.map((asset: Asset) => ({ + ...asset, + status: "uploaded", + })), + }; } catch (error) { if (error instanceof Error) { return { diff --git a/apps/designer/app/routes/rest/publish.ts b/apps/designer/app/routes/rest/publish.ts index 04f5bdeb34..29fa2dd5cf 100644 --- a/apps/designer/app/routes/rest/publish.ts +++ b/apps/designer/app/routes/rest/publish.ts @@ -1,10 +1,14 @@ import { type ActionFunction } from "@remix-run/node"; +import { zfd } from "zod-form-data"; import * as db from "~/shared/db"; +const schema = zfd.formData({ + domain: zfd.text(), + projectId: zfd.text(), +}); + export const action: ActionFunction = async ({ request }) => { - const formData = await request.formData(); - const domain = formData.get("domain") as string | null; - const projectId = formData.get("projectId") as string | null; + const { domain, projectId } = schema.parse(await request.formData()); try { const project = await db.misc.publish({ projectId, domain }); return { domain: project.domain }; diff --git a/apps/designer/docs/test-cases.md b/apps/designer/docs/test-cases.md index b991c78e17..224ee60065 100644 --- a/apps/designer/docs/test-cases.md +++ b/apps/designer/docs/test-cases.md @@ -74,3 +74,10 @@ - Add a Box component to the canvas - Open Props panel - See the default tag is provided and can be modified + +1. Asset manager + + - Open assets panel + - Upload an image + - Check it loads and shows a progress bar + - Delete an asset by clicking on the `x` icon and then the delete button in the tooltip diff --git a/apps/designer/package.json b/apps/designer/package.json index 6af3752695..9aca9df4b8 100644 --- a/apps/designer/package.json +++ b/apps/designer/package.json @@ -26,8 +26,8 @@ "@stitches/react": "^1.2.7", "@vercel/node": "^2.4.1", "@webstudio-is/asset-uploader": "*", - "@webstudio-is/react-sdk": "*", "@webstudio-is/prisma-client": "*", + "@webstudio-is/react-sdk": "*", "@webstudio-is/design-system": "*", "@webstudio-is/icons": "*", "bson-objectid": "^2.0.2", @@ -60,7 +60,8 @@ "remix-auth-github": "^1.1.1", "remix-auth-google": "^1.1.0", "sharp": "^0.30.7", - "slugify": "^1.6.5" + "slugify": "^1.6.5", + "zod-form-data": "^1.2.1" }, "devDependencies": { "@babel/core": "^7.18.5", @@ -98,12 +99,12 @@ "esbuild-jest": "^0.5.0", "esbuild-node-externals": "^1.4.1", "esbuild-node-loader": "^0.8.0", - "eslint-config-custom": "*", "jest": "^27.5.1", "prettier": "^2.6.2", "react-test-renderer": "^17.0.2", "relative-deps": "^1.0.7", - "tsconfig": "*", + "@webstudio-is/eslint-config-custom": "*", + "@webstudio-is/tsconfig": "*", "typescript": "4.6.4" }, "sideEffects": false, diff --git a/apps/designer/tsconfig.json b/apps/designer/tsconfig.json index fbf716785d..4ce92c95f5 100644 --- a/apps/designer/tsconfig.json +++ b/apps/designer/tsconfig.json @@ -1,6 +1,5 @@ { - // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 - "extends": "../../node_modules/tsconfig/remix.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/remix.json", "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], "compilerOptions": { "paths": { diff --git a/package.json b/package.json index 504f3f4a01..cbcd14529f 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "storybook": "turbo run storybook:run" }, "devDependencies": { - "eslint-config-custom": "*", + "@webstudio-is/eslint-config-custom": "*", "husky": ">=6", "lint-staged": "^13.0.2", "prettier": "latest", diff --git a/packages/asset-uploader/jest.config.js b/packages/asset-uploader/jest.config.js index 03c0db11ea..9eae0843c6 100644 --- a/packages/asset-uploader/jest.config.js +++ b/packages/asset-uploader/jest.config.js @@ -1,5 +1,5 @@ // eslint-disable-next-line -const base = require("jest-config/jest.config.js"); +const base = require("@webstudio-is/jest-config"); module.exports = { ...base, diff --git a/packages/asset-uploader/package.json b/packages/asset-uploader/package.json index cbef3f028d..875be06eba 100644 --- a/packages/asset-uploader/package.json +++ b/packages/asset-uploader/package.json @@ -15,13 +15,15 @@ "dependencies": { "@aws-sdk/client-s3": "^3.131.0", "@aws-sdk/lib-storage": "^3.131.0", - "sharp": "^0.30.7", - "@webstudio-is/prisma-client": "*" + "@webstudio-is/prisma-client": "*", + "app-root-path": "^3.0.0", + "sharp": "^0.30.7" }, "devDependencies": { + "@types/app-root-path": "^1.2.4", "@types/node": "^17.0.21", "@types/sharp": "^0.30.4", - "jest-config": "*", + "@webstudio-is/jest-config": "*", "tsup": "^6.1.3", "typescript": "^4.6.2", "zod": "^3.17.3" diff --git a/packages/asset-uploader/src/constants.ts b/packages/asset-uploader/src/constants.ts index 6750073003..6616fc8004 100644 --- a/packages/asset-uploader/src/constants.ts +++ b/packages/asset-uploader/src/constants.ts @@ -1 +1 @@ -export const DEFAULT_UPLPOAD_PATH = "uploads"; +export const DEFAULT_UPLOAD_PATH = "public/uploads"; diff --git a/packages/asset-uploader/src/db/delete.ts b/packages/asset-uploader/src/db/delete.ts new file mode 100644 index 0000000000..9898fbe420 --- /dev/null +++ b/packages/asset-uploader/src/db/delete.ts @@ -0,0 +1,12 @@ +import { prisma, Asset } from "@webstudio-is/prisma-client"; +import { getAssetPath } from "../helpers/get-asset-path"; + +export const deleteAssetInDb = async (assetId: Asset["id"]): Promise => { + if (typeof assetId !== "string") { + throw new Error("Asset ID required"); + } + + const removedAsset = await prisma.asset.delete({ where: { id: assetId } }); + + return { ...removedAsset, path: getAssetPath(removedAsset) }; +}; diff --git a/packages/asset-uploader/src/db/index.ts b/packages/asset-uploader/src/db/index.ts index 52122ec551..7a4923ced3 100644 --- a/packages/asset-uploader/src/db/index.ts +++ b/packages/asset-uploader/src/db/index.ts @@ -1,2 +1,3 @@ export * from "./create"; export * from "./load"; +export * from "./delete"; diff --git a/packages/asset-uploader/src/db/load.ts b/packages/asset-uploader/src/db/load.ts index 25cfa43872..9fde8948d2 100644 --- a/packages/asset-uploader/src/db/load.ts +++ b/packages/asset-uploader/src/db/load.ts @@ -5,7 +5,7 @@ export const loadByProject = async ( projectId?: Project["id"] ): Promise => { if (typeof projectId !== "string") { - throw new Error("Tree ID required"); + throw new Error("Project ID required"); } const assets = await prisma.asset.findMany({ diff --git a/packages/asset-uploader/src/delete.ts b/packages/asset-uploader/src/delete.ts new file mode 100644 index 0000000000..b53930a09d --- /dev/null +++ b/packages/asset-uploader/src/delete.ts @@ -0,0 +1,38 @@ +import { DeleteObjectCommand } from "@aws-sdk/client-s3"; +import { prisma, Asset } from "@webstudio-is/prisma-client"; +import { unlink } from "fs/promises"; +import path from "path"; +import { deleteAssetInDb } from "./db"; +import { imageFsDirectory } from "./helpers/image-fs-path"; +import { s3EnvVariables } from "./schema"; +import { getS3Client } from "./targets/s3/client"; + +export const deleteAsset = async ({ + id, + name, +}: { + id: string; + name: string; +}): Promise => { + const currentAsset = await prisma.asset.findUnique({ + where: { id }, + }); + if (!currentAsset) throw new Error("Asset does not exist"); + + if (currentAsset.location === "REMOTE") { + const s3Envs = s3EnvVariables.parse(process.env); + await getS3Client().send( + new DeleteObjectCommand({ + Bucket: s3Envs.S3_BUCKET, + Key: name, + }) + ); + + return await deleteAssetInDb(id); + } else { + const directory = await imageFsDirectory(); + await unlink(path.join(directory, name)); + + return await deleteAssetInDb(id); + } +}; diff --git a/packages/asset-uploader/src/helpers/get-asset-path.ts b/packages/asset-uploader/src/helpers/get-asset-path.ts index 168fdc6c52..1403f9790b 100644 --- a/packages/asset-uploader/src/helpers/get-asset-path.ts +++ b/packages/asset-uploader/src/helpers/get-asset-path.ts @@ -7,7 +7,8 @@ const fsEnvs = fsEnvVariables.parse(process.env); export const getAssetPath = (asset: DbAsset) => { if (asset.location === Location.FS) { - return path.join("/", fsEnvs.FILE_UPLOAD_PATH, asset.name); + const splitPath = fsEnvs.FILE_UPLOAD_PATH.split("public"); + return path.join("/", splitPath[splitPath.length - 1], asset.name); } if (asset.location === Location.REMOTE && s3Envs.success) { diff --git a/packages/asset-uploader/src/helpers/image-fs-path.ts b/packages/asset-uploader/src/helpers/image-fs-path.ts new file mode 100644 index 0000000000..6889eb1d61 --- /dev/null +++ b/packages/asset-uploader/src/helpers/image-fs-path.ts @@ -0,0 +1,17 @@ +import path from "path"; +import appRoot from "app-root-path"; +import { fsEnvVariables } from "../schema"; +const fsUploadVars = fsEnvVariables.parse(process.env); + +export const imageFsDirectory = () => { + const isProd = process.env.NODE_ENV === "production"; + + if (isProd) return path.join(appRoot.path, fsUploadVars.FILE_UPLOAD_PATH); + + return path.join( + appRoot.path, + "apps", + "designer", + fsUploadVars.FILE_UPLOAD_PATH + ); +}; diff --git a/packages/asset-uploader/src/index.ts b/packages/asset-uploader/src/index.ts index 22321d4d77..25948908ae 100644 --- a/packages/asset-uploader/src/index.ts +++ b/packages/asset-uploader/src/index.ts @@ -1,2 +1,3 @@ export * from "./upload"; +export * from "./delete"; export * from "./db"; diff --git a/packages/asset-uploader/src/schema.ts b/packages/asset-uploader/src/schema.ts index 45399df529..12205c9724 100644 --- a/packages/asset-uploader/src/schema.ts +++ b/packages/asset-uploader/src/schema.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { NodeOnDiskFile } from "@remix-run/node"; -import { DEFAULT_UPLPOAD_PATH } from "./constants"; +import { DEFAULT_UPLOAD_PATH } from "./constants"; const SingleImageInUpload = z.instanceof(NodeOnDiskFile); @@ -25,7 +25,7 @@ export const s3EnvVariables = z.object({ }); export const fsEnvVariables = z.object({ - FILE_UPLOAD_PATH: z.string().optional().default(DEFAULT_UPLPOAD_PATH), + FILE_UPLOAD_PATH: z.string().optional().default(DEFAULT_UPLOAD_PATH), }); export const assetEnvVariables = z.object({ diff --git a/packages/asset-uploader/src/targets/s3/client.ts b/packages/asset-uploader/src/targets/s3/client.ts new file mode 100644 index 0000000000..0532597bcb --- /dev/null +++ b/packages/asset-uploader/src/targets/s3/client.ts @@ -0,0 +1,14 @@ +import { S3Client } from "@aws-sdk/client-s3"; +import { s3EnvVariables } from "../../schema"; + +export const getS3Client = () => { + const s3Envs = s3EnvVariables.parse(process.env); + return new S3Client({ + endpoint: s3Envs.S3_ENDPOINT, + region: s3Envs.S3_REGION, + credentials: { + accessKeyId: s3Envs.S3_ACCESS_KEY_ID, + secretAccessKey: s3Envs.S3_SECRET_ACCESS_KEY, + }, + }); +}; diff --git a/packages/asset-uploader/src/targets/s3/handler.ts b/packages/asset-uploader/src/targets/s3/handler.ts index a2573ff9c1..24440b0050 100644 --- a/packages/asset-uploader/src/targets/s3/handler.ts +++ b/packages/asset-uploader/src/targets/s3/handler.ts @@ -1,5 +1,5 @@ import { UploadHandlerPart } from "@remix-run/node"; -import { PutObjectCommandInput, S3Client } from "@aws-sdk/client-s3"; +import { PutObjectCommandInput } from "@aws-sdk/client-s3"; import { Upload } from "@aws-sdk/lib-storage"; import ObjectID from "bson-objectid"; @@ -14,6 +14,7 @@ import { getFilenameAndExtension, } from "../../helpers/array-buffer-helpers"; import { Location } from "@webstudio-is/prisma-client"; +import { getS3Client } from "./client"; type S3UploadHandler = ({ file, @@ -57,16 +58,7 @@ export const s3UploadHandler: S3UploadHandler = async ({ }, }; - const client = new S3Client({ - endpoint: s3Envs.S3_ENDPOINT, - region: s3Envs.S3_REGION, - credentials: { - accessKeyId: s3Envs.S3_ACCESS_KEY_ID, - secretAccessKey: s3Envs.S3_SECRET_ACCESS_KEY, - }, - }); - - const upload = new Upload({ client, params }); + const upload = new Upload({ client: getS3Client(), params }); ImagesUploadedSuccess.parse(await upload.done()); const image = sharp(uint8Array); diff --git a/packages/asset-uploader/src/upload.ts b/packages/asset-uploader/src/upload.ts index 34fcf6075e..cd1c26af61 100644 --- a/packages/asset-uploader/src/upload.ts +++ b/packages/asset-uploader/src/upload.ts @@ -1,4 +1,3 @@ -import path from "path"; import { unstable_createFileUploadHandler, unstable_parseMultipartFormData, @@ -6,10 +5,12 @@ import { import { s3UploadHandler } from "./targets/s3/handler"; import { uploadToS3 } from "./targets/s3/uploader"; import { uploadToDisk } from "./targets/disk/upload"; -import { assetEnvVariables, fsEnvVariables, s3EnvVariables } from "./schema"; +import { assetEnvVariables, s3EnvVariables } from "./schema"; +import { Asset } from "@webstudio-is/prisma-client"; +import { imageFsDirectory } from "./helpers/image-fs-path"; const isS3Upload = s3EnvVariables.safeParse(process.env).success; -const fsUploadVars = fsEnvVariables.parse(process.env); + const commonUploadVars = assetEnvVariables.parse(process.env); // user inputs the max value in mb and we transform it to bytes @@ -18,14 +19,11 @@ export const MAX_UPLOAD_SIZE = parseInt(commonUploadVars.MAX_UPLOAD_SIZE) * 1e6; export const uploadAssets = async ({ request, projectId, - dirname, }: { request: Request; projectId: string; - dirname: string; -}) => { - const uploads = path.join(dirname, "../public"); - const directory = path.join(uploads, fsUploadVars.FILE_UPLOAD_PATH); +}): Promise => { + const directory = await imageFsDirectory(); const formData = await unstable_parseMultipartFormData( request, isS3Upload diff --git a/packages/asset-uploader/tsconfig.json b/packages/asset-uploader/tsconfig.json index fe21c2ae7b..654f479a30 100644 --- a/packages/asset-uploader/tsconfig.json +++ b/packages/asset-uploader/tsconfig.json @@ -1,6 +1,6 @@ // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 { - "extends": "../../node_modules/tsconfig/base.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts"], "compilerOptions": { "module": "commonjs", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 1b32378017..d181546931 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -55,9 +55,10 @@ "@radix-ui/react-tabs": "^0.1.5", "@radix-ui/react-toggle": "^0.1.4", "@radix-ui/react-toggle-group": "^0.1.5", - "@radix-ui/react-tooltip": "^0.1.7", + "@radix-ui/react-tooltip": "^1.0.0", "@radix-ui/react-use-layout-effect": "^0.1.0", - "@webstudio-is/icons": "*" + "@webstudio-is/icons": "*", + "@webstudio-is/react-sdk": "*" }, "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/design-system/src/components/tooltip.tsx b/packages/design-system/src/components/tooltip.tsx index 5eee96c9f7..0cb9d347ee 100644 --- a/packages/design-system/src/components/tooltip.tsx +++ b/packages/design-system/src/components/tooltip.tsx @@ -1,8 +1,9 @@ -import React from "react"; +import React, { Ref } from "react"; import { styled } from "../stitches.config"; import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { Box } from "./box"; import { Text } from "./text"; +import type { CSS } from "@webstudio-is/react-sdk"; type TooltipProps = React.ComponentProps & React.ComponentProps & { @@ -10,6 +11,7 @@ type TooltipProps = React.ComponentProps & content: React.ReactNode; multiline?: boolean; delayDuration?: number; + css?: CSS; }; const Content = styled(TooltipPrimitive.Content, { @@ -18,27 +20,40 @@ const Content = styled(TooltipPrimitive.Content, { color: "$hiContrast", borderRadius: "$1", padding: "$1 $2", + zIndex: "$max", + position: "relative", variants: { multiline: { true: { - maxWidth: 250, + // @todo makew this part of the design system + maxWidth: 110, pb: 7, }, }, }, }); -export const Tooltip = ({ - children, - content, - open, - defaultOpen, - onOpenChange, - multiline, - delayDuration, - ...props -}: TooltipProps) => { +const Arrow = styled(TooltipPrimitive.Arrow, { + fill: "$loContrast", + stroke: "$slate7", + strokeWidth: "$1", + marginTop: -0.5, +}); + +export const Tooltip = React.forwardRef(function TooltipWrapper( + { + children, + content, + open, + defaultOpen, + onOpenChange, + multiline, + delayDuration, + ...props + }: TooltipProps, + ref: Ref +) { return ( {children} - + ); -}; +}); diff --git a/packages/design-system/tsconfig.json b/packages/design-system/tsconfig.json index 329cf3ea13..e599e806b6 100644 --- a/packages/design-system/tsconfig.json +++ b/packages/design-system/tsconfig.json @@ -1,6 +1,6 @@ // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 { - "extends": "../../node_modules/tsconfig/base.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2019"], diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 158b766152..30efd8e4c4 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -1,6 +1,6 @@ { - "name": "eslint-config-custom", - "version": "0.0.0", + "name": "@webstudio-is/eslint-config-custom", + "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { diff --git a/packages/icons/package.json b/packages/icons/package.json index b764e507ef..69a6fb16c7 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,7 +1,7 @@ { "name": "@webstudio-is/icons", - "version": "0.5.0", - "description": "Webstudio JavaScript / TypeScript API", + "version": "0.1.0", + "description": "Webstudio Icons", "author": "Webstudio ", "homepage": "https://webstudio.is", "scripts": { diff --git a/packages/icons/tsconfig.json b/packages/icons/tsconfig.json index 329cf3ea13..e599e806b6 100644 --- a/packages/icons/tsconfig.json +++ b/packages/icons/tsconfig.json @@ -1,6 +1,6 @@ // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 { - "extends": "../../node_modules/tsconfig/base.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2019"], diff --git a/packages/jest-config/jest.config.js b/packages/jest-config/index.js similarity index 100% rename from packages/jest-config/jest.config.js rename to packages/jest-config/index.js diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index ef615bcf82..001d6d9a39 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -1,6 +1,6 @@ { - "name": "jest-config", - "version": "0.0.0", + "name": "@webstudio-is/jest-config", + "version": "1.0.1", "license": "MIT", "dependencies": { "@types/jest": "^27.4.1", diff --git a/packages/prisma-client/tsconfig.json b/packages/prisma-client/tsconfig.json index e895781976..ff828da22f 100644 --- a/packages/prisma-client/tsconfig.json +++ b/packages/prisma-client/tsconfig.json @@ -1,6 +1,6 @@ // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 { - "extends": "../../node_modules/tsconfig/base.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts"], "compilerOptions": { "module": "commonjs", diff --git a/packages/react-sdk/jest.config.js b/packages/react-sdk/jest.config.js index 03c0db11ea..9eae0843c6 100644 --- a/packages/react-sdk/jest.config.js +++ b/packages/react-sdk/jest.config.js @@ -1,5 +1,5 @@ // eslint-disable-next-line -const base = require("jest-config/jest.config.js"); +const base = require("@webstudio-is/jest-config"); module.exports = { ...base, diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index 3b6256d555..5bf5020e21 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -33,18 +33,18 @@ "@types/css-tree": "^1.0.7", "@types/fs-extra": "^9.0.13", "@types/node": "^17.0.21", - "@webstudio-is/prisma-client": "*", "babel-loader": "^8.2.5", "camelcase": "^6.3.0", "css-tree": "^2.1.0", "esbuild": "^0.14.25", "esbuild-node-externals": "^1.4.1", "esbuild-node-loader": "^0.8.0", - "jest-config": "*", + "@webstudio-is/jest-config": "*", "mdn-data": "2.0.23", "react": "^17.0.2", "react-docgen-typescript": "^2.2.2", "react-dom": "^17.0.2", + "@webstudio-is/prisma-client": "*", "tsup": "^6.1.3", "typescript": "^4.6.2" }, @@ -59,9 +59,9 @@ "@stitches/core": "^1.2.7", "@webstudio-is/icons": "*", "immer": "^9.0.12", - "jest-config": "*", "mitt": "^3.0.0", "react-nano-state": "^0.4.0", + "@webstudio-is/jest-config": "*", "zod": "^3.17.3" }, "main": "lib/index.js", diff --git a/packages/react-sdk/tsconfig.json b/packages/react-sdk/tsconfig.json index 329cf3ea13..e599e806b6 100644 --- a/packages/react-sdk/tsconfig.json +++ b/packages/react-sdk/tsconfig.json @@ -1,6 +1,6 @@ // Extends can't use node's resolution rn https://github.com/dividab/tsconfig-paths/issues/209#issuecomment-1177261091 { - "extends": "../../node_modules/tsconfig/base.json", + "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2019"], diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index a32fc41922..56f2bd444d 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -1,7 +1,7 @@ { - "name": "tsconfig", - "version": "0.0.0", - "private": true, + "name": "@webstudio-is/tsconfig", + "version": "1.0.0", + "private": false, "main": "index.js", "files": [ "base.json", diff --git a/yarn.lock b/yarn.lock index fd8230baa8..38b02e8583 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2752,6 +2752,13 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/primitive@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253" + integrity sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-accessible-icon@^0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-accessible-icon/-/react-accessible-icon-0.1.4.tgz#bdbf1e3226a0e9e7778b68728b175bdc532b720c" @@ -2931,6 +2938,18 @@ "@radix-ui/react-use-callback-ref" "0.1.0" "@radix-ui/react-use-escape-keydown" "0.1.0" +"@radix-ui/react-dismissable-layer@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b" + integrity sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-escape-keydown" "1.0.0" + "@radix-ui/react-dropdown-menu@^0.1.6": version "0.1.6" resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-0.1.6.tgz#3203229788cd57e552c9f19dcc7008e2b545919c" @@ -2975,6 +2994,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "0.1.0" +"@radix-ui/react-id@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.0.tgz#8d43224910741870a45a8c9d092f25887bb6d11e" + integrity sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.0" + "@radix-ui/react-label@0.1.5", "@radix-ui/react-label@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-0.1.5.tgz#12cd965bfc983e0148121d4c99fb8e27a917c45c" @@ -3046,7 +3073,7 @@ "@radix-ui/react-use-size" "0.1.1" "@radix-ui/rect" "0.1.1" -"@radix-ui/react-popper@^1.0.0": +"@radix-ui/react-popper@1.0.0", "@radix-ui/react-popper@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.0.0.tgz#fb4f937864bf39c48f27f55beee61fa9f2bef93c" integrity sha512-k2dDd+1Wl0XWAMs9ZvAxxYsB9sOsEhrFQV4CINd7IUZf0wfdye4OHen9siwxvZImbzhgVeKTJi68OQmPRvVdMg== @@ -3071,6 +3098,14 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-layout-effect" "0.1.0" +"@radix-ui/react-portal@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b" + integrity sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/react-presence@0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.1.2.tgz#9f11cce3df73cf65bc348e8b76d891f0d54c1fe3" @@ -3080,6 +3115,15 @@ "@radix-ui/react-compose-refs" "0.1.0" "@radix-ui/react-use-layout-effect" "0.1.0" +"@radix-ui/react-presence@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a" + integrity sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-use-layout-effect" "1.0.0" + "@radix-ui/react-primitive@0.1.4": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz#6c233cf08b0cb87fecd107e9efecb3f21861edc1" @@ -3255,26 +3299,24 @@ "@radix-ui/react-primitive" "0.1.4" "@radix-ui/react-use-controllable-state" "0.1.0" -"@radix-ui/react-tooltip@^0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.1.7.tgz#6f8c00d6e489565d14abf209ce0fb8853c8c8ee3" - integrity sha512-eiBUsVOHenZ0JR16tl970bB0DafJBz6mFgSGfIGIVpflFj0LIsIDiLMsYyvYdx1KwwsIUDTEZtxcPm/sWjPzqA== +"@radix-ui/react-tooltip@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.0.tgz#f7fcecf2bac5c31cd14666b5acd056015fc21646" + integrity sha512-RB06pov+O4Npy10ei1C6fsyB9QoOjz7Ubo8Sl3qdKtLgkL9iI96925DYtH0bxx6MH6YB2FuzLU6B75qn3AQQQw== dependencies: "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "0.1.0" - "@radix-ui/react-compose-refs" "0.1.0" - "@radix-ui/react-context" "0.1.1" - "@radix-ui/react-id" "0.1.5" - "@radix-ui/react-popper" "0.1.4" - "@radix-ui/react-portal" "0.1.4" - "@radix-ui/react-presence" "0.1.2" - "@radix-ui/react-primitive" "0.1.4" - "@radix-ui/react-slot" "0.1.2" - "@radix-ui/react-use-controllable-state" "0.1.0" - "@radix-ui/react-use-escape-keydown" "0.1.0" - "@radix-ui/react-use-previous" "0.1.1" - "@radix-ui/react-use-rect" "0.1.1" - "@radix-ui/react-visually-hidden" "0.1.4" + "@radix-ui/primitive" "1.0.0" + "@radix-ui/react-compose-refs" "1.0.0" + "@radix-ui/react-context" "1.0.0" + "@radix-ui/react-dismissable-layer" "1.0.0" + "@radix-ui/react-id" "1.0.0" + "@radix-ui/react-popper" "1.0.0" + "@radix-ui/react-portal" "1.0.0" + "@radix-ui/react-presence" "1.0.0" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/react-slot" "1.0.0" + "@radix-ui/react-use-controllable-state" "1.0.0" + "@radix-ui/react-visually-hidden" "1.0.0" "@radix-ui/react-use-body-pointer-events@0.1.1": version "0.1.1" @@ -3291,6 +3333,13 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-callback-ref@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz#9e7b8b6b4946fe3cbe8f748c82a2cce54e7b6a90" + integrity sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-controllable-state@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.1.0.tgz#4fced164acfc69a4e34fb9d193afdab973a55de1" @@ -3299,6 +3348,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "0.1.0" +"@radix-ui/react-use-controllable-state@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f" + integrity sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-direction@0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-direction/-/react-use-direction-0.1.0.tgz#97ac1d52e497c974389e7988f809238ed72e7df7" @@ -3314,6 +3371,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "0.1.0" +"@radix-ui/react-use-escape-keydown@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1" + integrity sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.0" + "@radix-ui/react-use-layout-effect@0.1.0", "@radix-ui/react-use-layout-effect@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.1.0.tgz#ebf71bd6d2825de8f1fbb984abf2293823f0f223" @@ -3374,6 +3439,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "0.1.4" +"@radix-ui/react-visually-hidden@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.0.tgz#4d69d7e3b6d21ee4678ed6de5215dcd068394401" + integrity sha512-MwAhMdX+n6S4InwRKSnpUsp+lLkYG6izQF56ul6guSX2mBBLOMV9Frx7xJlkEe2GjKLzbNuHhaCS6e5gopmZNA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-primitive" "1.0.0" + "@radix-ui/rect@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419" @@ -4762,6 +4835,11 @@ dependencies: "@types/estree" "*" +"@types/app-root-path@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/app-root-path/-/app-root-path-1.2.4.tgz#a78b703282b32ac54de768f5512ecc3569919dc7" + integrity sha512-yhURoXmWN/zfw2MXXcOdUTwe5CWhzRWtb4Rs2+JwGpuhm2hxLCzjASi/aIuyVeJB3Iyks1teaHyOEfNNmltdvQ== + "@types/aria-query@^4.2.0": version "4.2.2" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" @@ -5893,6 +5971,11 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== +app-root-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" + integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -17918,6 +18001,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zod-form-data@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/zod-form-data/-/zod-form-data-1.2.1.tgz#6da07d7964765b464e959a75f0d797690ee477f7" + integrity sha512-L4zocmlzikYB9A2uVVj3OUKX/t3spOZTzQLOhoCXECn9w+eN3bw0D/KYGX4J72iLdCIwDP6YJyYwVXkx3WTu7A== + zod@^3.17.3: version "3.17.3" resolved "https://registry.yarnpkg.com/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba"