init
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Скопируйте в .env — подойдут и имена из Next (NEXT_PUBLIC_*), и VITE_*.
|
||||||
|
VITE_API_URL=https://example.com/api/
|
||||||
|
NEXT_PUBLIC_API=https://example.com/api/
|
||||||
|
|
||||||
|
# База для полей image вида projects/uuid.jpg (со слэшем на конце).
|
||||||
|
# Пример: https://storage.yandexcloud.net/dult-faib-knac-fint/
|
||||||
|
VITE_S3_BUCKET=https://storage.yandexcloud.net/your-bucket/
|
||||||
|
NEXT_PUBLIC_S3_BUCKET=https://storage.yandexcloud.net/your-bucket/
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.local
|
||||||
@@ -0,0 +1,597 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 0,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "stream-demo-standalone",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.62.7",
|
||||||
|
"framer-motion": "^11.17.0",
|
||||||
|
"ky": "^1.4.0",
|
||||||
|
"libphonenumber-js": "^1.11.7",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.53.0",
|
||||||
|
"react-swipeable": "^7.0.2",
|
||||||
|
"usehooks-ts": "^3.1.0",
|
||||||
|
"zustand": "^4.5.4",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"eslint": "^10.2.0",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"globals": "^17.4.0",
|
||||||
|
"postcss": "^8.5.4",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
|
"typescript": "~5.7.2",
|
||||||
|
"typescript-eslint": "^8.58.1",
|
||||||
|
"vite": "^6.0.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||||
|
|
||||||
|
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
||||||
|
|
||||||
|
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
|
||||||
|
|
||||||
|
"@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
||||||
|
|
||||||
|
"@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
||||||
|
|
||||||
|
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
||||||
|
|
||||||
|
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
||||||
|
|
||||||
|
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
||||||
|
|
||||||
|
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
|
||||||
|
|
||||||
|
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="],
|
||||||
|
|
||||||
|
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||||
|
|
||||||
|
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||||
|
|
||||||
|
"@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
|
||||||
|
|
||||||
|
"@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
||||||
|
|
||||||
|
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||||
|
|
||||||
|
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
||||||
|
|
||||||
|
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
||||||
|
|
||||||
|
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
|
||||||
|
|
||||||
|
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
||||||
|
|
||||||
|
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
|
||||||
|
|
||||||
|
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
||||||
|
|
||||||
|
"@eslint/config-array": ["@eslint/config-array@0.23.5", "", { "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" } }, "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA=="],
|
||||||
|
|
||||||
|
"@eslint/config-helpers": ["@eslint/config-helpers@0.5.5", "", { "dependencies": { "@eslint/core": "^1.2.1" } }, "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w=="],
|
||||||
|
|
||||||
|
"@eslint/core": ["@eslint/core@1.2.1", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ=="],
|
||||||
|
|
||||||
|
"@eslint/js": ["@eslint/js@10.0.1", "", { "peerDependencies": { "eslint": "^10.0.0" }, "optionalPeers": ["eslint"] }, "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA=="],
|
||||||
|
|
||||||
|
"@eslint/object-schema": ["@eslint/object-schema@3.0.5", "", {}, "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw=="],
|
||||||
|
|
||||||
|
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.7.1", "", { "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" } }, "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ=="],
|
||||||
|
|
||||||
|
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||||
|
|
||||||
|
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
||||||
|
|
||||||
|
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||||
|
|
||||||
|
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
|
|
||||||
|
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||||
|
|
||||||
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.1", "", { "os": "android", "cpu": "arm64" }, "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="],
|
||||||
|
|
||||||
|
"@tanstack/query-core": ["@tanstack/query-core@5.97.0", "", {}, "sha512-QdpLP5VzVMgo4VtaPppRA2W04UFjIqX+bxke/ZJhE5cfd5UPkRzqIAJQt9uXkQJjqE8LBOMbKv7f8HCsZltXlg=="],
|
||||||
|
|
||||||
|
"@tanstack/react-query": ["@tanstack/react-query@5.97.0", "", { "dependencies": { "@tanstack/query-core": "5.97.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-y4So4eGcQoK2WVMAcDNZE9ofB/p5v1OlKvtc1F3uqHwrtifobT7q+ZnXk2mRkc8E84HKYSlAE9z6HXl2V0+ySQ=="],
|
||||||
|
|
||||||
|
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||||
|
|
||||||
|
"@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
||||||
|
|
||||||
|
"@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
||||||
|
|
||||||
|
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
|
||||||
|
|
||||||
|
"@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="],
|
||||||
|
|
||||||
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
|
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/type-utils": "8.58.1", "@typescript-eslint/utils": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.1", "@typescript-eslint/types": "^8.58.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1" } }, "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/utils": "8.58.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/types": ["@typescript-eslint/types@8.58.1", "", {}, "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.1", "@typescript-eslint/tsconfig-utils": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/visitor-keys": "8.58.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.1", "@typescript-eslint/types": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.1", "", { "dependencies": { "@typescript-eslint/types": "8.58.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ=="],
|
||||||
|
|
||||||
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
|
||||||
|
|
||||||
|
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||||
|
|
||||||
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
|
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
||||||
|
|
||||||
|
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
|
||||||
|
|
||||||
|
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||||
|
|
||||||
|
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
|
||||||
|
|
||||||
|
"autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="],
|
||||||
|
|
||||||
|
"balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
||||||
|
|
||||||
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.17", "", { "bin": "dist/cli.cjs" }, "sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA=="],
|
||||||
|
|
||||||
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
|
"brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
|
||||||
|
|
||||||
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
|
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": "cli.js" }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||||
|
|
||||||
|
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
|
||||||
|
|
||||||
|
"caniuse-lite": ["caniuse-lite@1.0.30001787", "", {}, "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg=="],
|
||||||
|
|
||||||
|
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
|
"commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
|
|
||||||
|
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||||
|
|
||||||
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
||||||
|
"cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
|
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||||
|
|
||||||
|
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
|
||||||
|
|
||||||
|
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||||
|
|
||||||
|
"electron-to-chromium": ["electron-to-chromium@1.5.334", "", {}, "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": "bin/esbuild" }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||||
|
|
||||||
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
||||||
|
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||||
|
|
||||||
|
"eslint": ["eslint@10.2.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.4", "@eslint/config-helpers": "^0.5.4", "@eslint/core": "^1.2.0", "@eslint/plugin-kit": "^0.7.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA=="],
|
||||||
|
|
||||||
|
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.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" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="],
|
||||||
|
|
||||||
|
"eslint-scope": ["eslint-scope@9.1.2", "", { "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ=="],
|
||||||
|
|
||||||
|
"eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
||||||
|
|
||||||
|
"espree": ["espree@11.2.0", "", { "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" } }, "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw=="],
|
||||||
|
|
||||||
|
"esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
|
||||||
|
|
||||||
|
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||||
|
|
||||||
|
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||||
|
|
||||||
|
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||||
|
|
||||||
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
|
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||||
|
|
||||||
|
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||||
|
|
||||||
|
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||||
|
|
||||||
|
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||||
|
|
||||||
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
|
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||||
|
|
||||||
|
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||||
|
|
||||||
|
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||||
|
|
||||||
|
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
||||||
|
|
||||||
|
"flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
|
||||||
|
|
||||||
|
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||||
|
|
||||||
|
"framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||||
|
|
||||||
|
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||||
|
|
||||||
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
||||||
|
"globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
|
||||||
|
|
||||||
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
|
"hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="],
|
||||||
|
|
||||||
|
"hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="],
|
||||||
|
|
||||||
|
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||||
|
|
||||||
|
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||||
|
|
||||||
|
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||||
|
|
||||||
|
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
||||||
|
|
||||||
|
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||||
|
|
||||||
|
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||||
|
|
||||||
|
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||||
|
|
||||||
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
|
"jiti": ["jiti@1.21.7", "", { "bin": "bin/jiti.js" }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
|
||||||
|
|
||||||
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
|
"jsesc": ["jsesc@3.1.0", "", { "bin": "bin/jsesc" }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||||
|
|
||||||
|
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||||
|
|
||||||
|
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||||
|
|
||||||
|
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||||
|
|
||||||
|
"json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||||
|
|
||||||
|
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||||
|
|
||||||
|
"ky": ["ky@1.14.3", "", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="],
|
||||||
|
|
||||||
|
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||||
|
|
||||||
|
"libphonenumber-js": ["libphonenumber-js@1.12.41", "", {}, "sha512-lsmMmGXBxXIK/VMLEj0kL6MtUs1kBGj1nTCzi6zgQoG1DEwqwt2DQyHxcLykceIxAnfE3hya7NuIh6PpC6S3fA=="],
|
||||||
|
|
||||||
|
"lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
|
||||||
|
|
||||||
|
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||||
|
|
||||||
|
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||||
|
|
||||||
|
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
|
||||||
|
|
||||||
|
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
|
|
||||||
|
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||||
|
|
||||||
|
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||||
|
|
||||||
|
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||||
|
|
||||||
|
"motion-dom": ["motion-dom@11.18.1", "", { "dependencies": { "motion-utils": "^11.18.1" } }, "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw=="],
|
||||||
|
|
||||||
|
"motion-utils": ["motion-utils@11.18.1", "", {}, "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||||
|
|
||||||
|
"node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="],
|
||||||
|
|
||||||
|
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||||
|
|
||||||
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
|
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
|
||||||
|
|
||||||
|
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||||
|
|
||||||
|
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||||
|
|
||||||
|
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||||
|
|
||||||
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
|
|
||||||
|
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||||
|
|
||||||
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||||
|
|
||||||
|
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||||
|
|
||||||
|
"pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
|
||||||
|
|
||||||
|
"postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="],
|
||||||
|
|
||||||
|
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
|
||||||
|
|
||||||
|
"postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="],
|
||||||
|
|
||||||
|
"postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
|
||||||
|
|
||||||
|
"postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
|
||||||
|
|
||||||
|
"postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||||
|
|
||||||
|
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||||
|
|
||||||
|
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||||
|
|
||||||
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
|
|
||||||
|
"react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
|
||||||
|
|
||||||
|
"react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="],
|
||||||
|
|
||||||
|
"react-hook-form": ["react-hook-form@7.72.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig=="],
|
||||||
|
|
||||||
|
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||||
|
|
||||||
|
"react-swipeable": ["react-swipeable@7.0.2", "", { "peerDependencies": { "react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w=="],
|
||||||
|
|
||||||
|
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
|
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||||
|
|
||||||
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="],
|
||||||
|
|
||||||
|
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||||
|
|
||||||
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||||
|
|
||||||
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
|
||||||
|
|
||||||
|
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||||
|
|
||||||
|
"tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="],
|
||||||
|
|
||||||
|
"thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
|
||||||
|
|
||||||
|
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
|
||||||
|
|
||||||
|
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
|
||||||
|
|
||||||
|
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
|
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
|
||||||
|
|
||||||
|
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||||
|
|
||||||
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
||||||
|
|
||||||
|
"typescript-eslint": ["typescript-eslint@8.58.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.1", "@typescript-eslint/parser": "8.58.1", "@typescript-eslint/typescript-estree": "8.58.1", "@typescript-eslint/utils": "8.58.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg=="],
|
||||||
|
|
||||||
|
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||||
|
|
||||||
|
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||||
|
|
||||||
|
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
||||||
|
|
||||||
|
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
|
||||||
|
|
||||||
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
|
"vite": ["vite@6.4.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": "bin/vite.js" }, "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ=="],
|
||||||
|
|
||||||
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
|
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||||
|
|
||||||
|
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
|
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|
||||||
|
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||||
|
|
||||||
|
"zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="],
|
||||||
|
|
||||||
|
"zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["immer"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
|
||||||
|
|
||||||
|
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
|
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
|
||||||
|
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
|
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
|
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
|
||||||
|
"readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import eslint from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
|
import globals from "globals";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.browser },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
{
|
||||||
|
ignores: ["dist/**"],
|
||||||
|
}
|
||||||
|
);
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Stream Demo — GRAFF.estate</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Generated
+2776
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "stream-demo-standalone",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint src"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.62.7",
|
||||||
|
"framer-motion": "^11.17.0",
|
||||||
|
"ky": "^1.4.0",
|
||||||
|
"libphonenumber-js": "^1.11.7",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.53.0",
|
||||||
|
"react-swipeable": "^7.0.2",
|
||||||
|
"usehooks-ts": "^3.1.0",
|
||||||
|
"zustand": "^4.5.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"eslint": "^10.2.0",
|
||||||
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
|
"globals": "^17.4.0",
|
||||||
|
"postcss": "^8.5.4",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
|
"typescript": "~5.7.2",
|
||||||
|
"typescript-eslint": "^8.58.1",
|
||||||
|
"vite": "^6.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 317 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,250 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="robots" content="noindex, noarchive">
|
||||||
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
<title>Transfonter demo</title>
|
||||||
|
<link href="stylesheet.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
/*
|
||||||
|
http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
/* demo styles */
|
||||||
|
body {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.page {
|
||||||
|
background: #fff;
|
||||||
|
width: 920px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px 20px 0 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.font-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
line-height: 1.3;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
position: relative;
|
||||||
|
background: #444;
|
||||||
|
font-size: 32px;
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 0 -20px 12px -20px;
|
||||||
|
}
|
||||||
|
.letters {
|
||||||
|
font-size: 25px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.s10:before {
|
||||||
|
content: '10px';
|
||||||
|
}
|
||||||
|
.s11:before {
|
||||||
|
content: '11px';
|
||||||
|
}
|
||||||
|
.s12:before {
|
||||||
|
content: '12px';
|
||||||
|
}
|
||||||
|
.s14:before {
|
||||||
|
content: '14px';
|
||||||
|
}
|
||||||
|
.s18:before {
|
||||||
|
content: '18px';
|
||||||
|
}
|
||||||
|
.s24:before {
|
||||||
|
content: '24px';
|
||||||
|
}
|
||||||
|
.s30:before {
|
||||||
|
content: '30px';
|
||||||
|
}
|
||||||
|
.s36:before {
|
||||||
|
content: '36px';
|
||||||
|
}
|
||||||
|
.s48:before {
|
||||||
|
content: '48px';
|
||||||
|
}
|
||||||
|
.s60:before {
|
||||||
|
content: '60px';
|
||||||
|
}
|
||||||
|
.s72:before {
|
||||||
|
content: '72px';
|
||||||
|
}
|
||||||
|
.s10:before, .s11:before, .s12:before, .s14:before,
|
||||||
|
.s18:before, .s24:before, .s30:before, .s36:before,
|
||||||
|
.s48:before, .s60:before, .s72:before {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
color: #999;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
padding: 9px;
|
||||||
|
margin: 0 0 12px;
|
||||||
|
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.428571429;
|
||||||
|
color: #333;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
overflow-x: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
/* responsive */
|
||||||
|
@media (max-width: 959px) {
|
||||||
|
.page {
|
||||||
|
width: auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
<div class="demo">
|
||||||
|
<h1 style="font-family: 'TTHovesPro-DmBd'; font-weight: 600; font-style: normal;">☝︎TT Hoves Pro DemiBold</h1>
|
||||||
|
<pre title="Usage">.your-style {
|
||||||
|
font-family: 'TTHovesPro-DmBd';
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
}</pre>
|
||||||
|
<pre title="Preload (optional)">
|
||||||
|
<link rel="preload" href="TTHovesPro-DmBd.woff2" as="font" type="font/woff2" crossorigin></pre>
|
||||||
|
<div class="font-container" style="font-family: 'TTHovesPro-DmBd'; font-weight: 600; font-style: normal;">
|
||||||
|
<p class="letters">
|
||||||
|
abcdefghijklmnopqrstuvwxyz<br>
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
|
||||||
|
0123456789.:,;()*!?'@#<>$%&^+-=~
|
||||||
|
</p>
|
||||||
|
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="demo">
|
||||||
|
<h1 style="font-family: 'TTHovesPro-Md'; font-weight: 500; font-style: normal;">☝︎TT Hoves Pro Medium</h1>
|
||||||
|
<pre title="Usage">.your-style {
|
||||||
|
font-family: 'TTHovesPro-Md';
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}</pre>
|
||||||
|
<pre title="Preload (optional)">
|
||||||
|
<link rel="preload" href="TTHovesPro-Md.woff2" as="font" type="font/woff2" crossorigin></pre>
|
||||||
|
<div class="font-container" style="font-family: 'TTHovesPro-Md'; font-weight: 500; font-style: normal;">
|
||||||
|
<p class="letters">
|
||||||
|
abcdefghijklmnopqrstuvwxyz<br>
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
|
||||||
|
0123456789.:,;()*!?'@#<>$%&^+-=~
|
||||||
|
</p>
|
||||||
|
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="demo">
|
||||||
|
<h1 style="font-family: 'TTHovesPro-Rg'; font-weight: normal; font-style: normal;">☝︎TT Hoves Pro Regular</h1>
|
||||||
|
<pre title="Usage">.your-style {
|
||||||
|
font-family: 'TTHovesPro-Rg';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}</pre>
|
||||||
|
<pre title="Preload (optional)">
|
||||||
|
<link rel="preload" href="TTHovesPro-Rg.woff2" as="font" type="font/woff2" crossorigin></pre>
|
||||||
|
<div class="font-container" style="font-family: 'TTHovesPro-Rg'; font-weight: normal; font-style: normal;">
|
||||||
|
<p class="letters">
|
||||||
|
abcdefghijklmnopqrstuvwxyz<br>
|
||||||
|
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
|
||||||
|
0123456789.:,;()*!?'@#<>$%&^+-=~
|
||||||
|
</p>
|
||||||
|
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'TTHovesPro';
|
||||||
|
src: url('TTHovesPro-DmBd.eot');
|
||||||
|
src:
|
||||||
|
url('TTHovesPro-DmBd.eot?#iefix') format('embedded-opentype'),
|
||||||
|
url('TTHovesPro-DmBd.woff2') format('woff2'),
|
||||||
|
url('TTHovesPro-DmBd.woff') format('woff'),
|
||||||
|
url('TTHovesPro-DmBd.ttf') format('truetype');
|
||||||
|
font-weight: 600;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'TTHovesPro';
|
||||||
|
src: url('TTHovesPro-Md.eot');
|
||||||
|
src:
|
||||||
|
url('TTHovesPro-Md.eot?#iefix') format('embedded-opentype'),
|
||||||
|
url('TTHovesPro-Md.woff2') format('woff2'),
|
||||||
|
url('TTHovesPro-Md.woff') format('woff'),
|
||||||
|
url('TTHovesPro-Md.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'TTHovesPro';
|
||||||
|
src: url('TTHovesPro-Rg.eot');
|
||||||
|
src:
|
||||||
|
url('TTHovesPro-Rg.eot?#iefix') format('embedded-opentype'),
|
||||||
|
url('TTHovesPro-Rg.woff2') format('woff2'),
|
||||||
|
url('TTHovesPro-Rg.woff') format('woff'),
|
||||||
|
url('TTHovesPro-Rg.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 12C0 5.37258 5.37258 0 12 0H48V36C48 42.6274 42.6274 48 36 48H12C5.37258 48 0 42.6274 0 36V12Z" fill="#B5F54E"/>
|
||||||
|
<path d="M14.556 20.0507C14.2126 18.6521 15.7064 17.4123 17.6391 17.4123C19.3806 17.4123 21.1257 17.776 21.7876 20.044H24.0212V23.5056C21.239 21.3698 15.2073 22.7088 14.556 20.0473M37.7127 19.0885H32.6863L27.9042 23.9817V14.2086H24.0247V16.5791C23.8973 16.4271 23.7663 16.275 23.6141 16.1262C22.2017 14.731 20.17 14.0234 17.5718 14.0234C14.5666 14.0234 12.8569 15.2798 11.9508 16.3345C10.8287 17.6437 10.3261 19.4291 10.6729 20.8871C11.572 24.6562 15.3135 25.2579 18.0638 25.5919C20.2337 25.8564 22.3115 26.2233 22.2549 28.0318C22.1982 29.9065 19.9328 30.3693 18.4497 30.3693C14.5808 30.3693 14.4498 27.6979 14.4498 27.6979H10.2871C10.3473 28.6997 10.6729 30.3131 11.9932 31.6951C13.441 33.2094 15.6108 33.9764 18.4461 33.9764C20.6195 33.9764 22.6017 33.3152 24.0212 32.1381V33.7549H27.9007V29.1394L29.2741 27.7343L33.1925 33.7549H37.7091L32.0067 24.9372L37.7127 19.0952V19.0885Z" fill="#4C5658"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
Binary file not shown.
+14
@@ -0,0 +1,14 @@
|
|||||||
|
import { Footer } from "@/components/Layout/Footer";
|
||||||
|
import StreamDemo from "@/features/stream-demo/StreamDemo";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-dvh flex-col">
|
||||||
|
{/* Без overflow-clip: иначе flex-1 + clip часто даёт пустой/обрезанный экран */}
|
||||||
|
<div className="min-h-0 flex-1 md:px-4 lg:px-[1.389vw] px-[10px] py-8 md:max-lg:pt-6 pt-4">
|
||||||
|
<StreamDemo />
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { api } from "@/lib/api";
|
||||||
|
import { projectsTags } from "@/consts/projectsTags";
|
||||||
|
import type { Product } from "@/types/Product";
|
||||||
|
import { Button } from "@/ui/Button";
|
||||||
|
import { CheckboxesGroup } from "@/ui/CheckboxesGroup";
|
||||||
|
import { PhoneInputRu } from "@/ui/PhoneInputRu";
|
||||||
|
import useAddReferer from "@/hooks/useAddReferer";
|
||||||
|
import { useRefererStore } from "@/stores/useRefererStore";
|
||||||
|
import { useModalStore } from "@/stores/useModalStore";
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
FormProvider,
|
||||||
|
useForm,
|
||||||
|
} from "react-hook-form";
|
||||||
|
import CheckIcon from "@/components/icons/CheckIcon";
|
||||||
|
import FeedbackModal from "@/components/modals/FeedbackFormModal";
|
||||||
|
|
||||||
|
const DEFAULT_STREAM_DEMO_PRODUCTS = [
|
||||||
|
"Удаленная демонстрация",
|
||||||
|
] as Product[];
|
||||||
|
|
||||||
|
export function Feedback() {
|
||||||
|
useAddReferer();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="contacts"
|
||||||
|
className="lg:mb-20 md:mb-12 lg:flex lg:gap-[0.833vw] max-lg:space-y-12 justify-between lg:mt-[14.07vh] mt-[100px] mb-10"
|
||||||
|
>
|
||||||
|
<h2 className="line2 font-medium max-lg:mb-6 lg:max-w-[45%]">
|
||||||
|
<span className="text-[#7A7A7A]">Хотите увеличить конверсию?</span>
|
||||||
|
<br />
|
||||||
|
Давайте обсудим детали.
|
||||||
|
</h2>
|
||||||
|
<FeedbackForm />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IInput {
|
||||||
|
fullname: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
products: Product[];
|
||||||
|
referer?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeedbackForm() {
|
||||||
|
const { referer } = useRefererStore();
|
||||||
|
const { setModal } = useModalStore();
|
||||||
|
|
||||||
|
const form = useForm<IInput>({
|
||||||
|
defaultValues: {
|
||||||
|
fullname: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
products: DEFAULT_STREAM_DEMO_PRODUCTS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { register, handleSubmit, formState, control } = form;
|
||||||
|
|
||||||
|
async function onSubmit(data: IInput) {
|
||||||
|
const { id } = await api
|
||||||
|
.post("mail", { json: { ...data, referer } })
|
||||||
|
.json<{ id: string }>();
|
||||||
|
setModal(<FeedbackModal id={id} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-1 space-y-4 lg:max-w-[47.431vw]">
|
||||||
|
<div className="space-y-10">
|
||||||
|
{!formState.isSubmitted ? (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<form
|
||||||
|
className="lg:space-y-[1.944vw] md:max-lg:space-y-7 space-y-3"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
|
<div className="lg:space-y-[1.111vw] space-y-4">
|
||||||
|
<p className="heading2 font-medium">Нам нужно</p>
|
||||||
|
<CheckboxesGroup name="products" options={projectsTags} />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
autoComplete="none"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
placeholder="Имя*"
|
||||||
|
{...register("fullname")}
|
||||||
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl btnl placeholder:font-medium placeholder:select-none"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
autoComplete="none"
|
||||||
|
required
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Email*"
|
||||||
|
{...register("email")}
|
||||||
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none btnl outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
|
||||||
|
/>
|
||||||
|
<div className="flex gap-x-3 py-2 border-[#3D425C] relative">
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: true }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<PhoneInputRu
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
onBlur={field.onBlur}
|
||||||
|
inputRef={field.ref}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" />
|
||||||
|
</div>
|
||||||
|
<div className="md:flex items-stretch lg:gap-[0.833vw] gap-3">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="btnl max-md:mb-3 max-md:w-full lg:px-[2.222vw] lg:py-[1.389vw] px-8 py-5 cursor-pointer lg:rounded-[1.111vw] rounded-2xl"
|
||||||
|
>
|
||||||
|
Оставить заявку
|
||||||
|
</Button>
|
||||||
|
<div className="text2 xl:max-w-[60%] md:max-lg:max-w-[40%] md:max-lg:py-1">
|
||||||
|
<span className="text-[#7A7A7A]">
|
||||||
|
*Нажимая кнопку отправить, вы даете
|
||||||
|
</span>{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={"/privacy-policy"}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
согласие на обработку персональных данных
|
||||||
|
</a>{" "}
|
||||||
|
<span className="text-[#7A7A7A]">и принимаете </span>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={"/policy"}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
условия политики
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
) : (
|
||||||
|
<div className="bg-[#37393B99] aspect-[643/480] w-full rounded-2xl flex justify-center items-center">
|
||||||
|
<div className="flex gap-3 justify-center items-center">
|
||||||
|
<div className="bg-gradient p-3 rounded-full">
|
||||||
|
<div className="text-white lg:size-[1.667vw] size-6">
|
||||||
|
<CheckIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="heading2 font-medium">
|
||||||
|
Мы получили заявку
|
||||||
|
<br />и скоро свяжемся с вами!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { PropsWithChildren } from "react";
|
||||||
|
import ArrowMoreIcon from "@/components/icons/ArrowMoreIcon";
|
||||||
|
import RutubeIcon from "@/components/icons/RutubeIcon";
|
||||||
|
import TelegramIcon from "@/components/icons/TgIcon";
|
||||||
|
import VkIcon from "@/components/icons/VKIcon";
|
||||||
|
import YoutubeIcon from "@/components/icons/YoutubeIcon";
|
||||||
|
|
||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="lg:px-5 lg:pb-5 md:max-lg:px-4 md:max-lg:pb-4 px-[10px] pb-[10px] space-y-6 mb-0">
|
||||||
|
<div className="max-md:flex-col lg:gap-[0.833vw] md:max-lg:gap-[1.042vw] flex gap-[1.111vw]">
|
||||||
|
<a
|
||||||
|
href={"tel:" + "8 800 770 00 67".replaceAll(" ", "")}
|
||||||
|
className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[696/248] lg:w-[48.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors"
|
||||||
|
>
|
||||||
|
<div className="text-[#7A7A7A] text1 font-medium">Позвонить</div>
|
||||||
|
<div className="lg:line2 md:max-lg:heading1 line2 flex items-center font-medium">
|
||||||
|
8 800 770 00 67
|
||||||
|
<div className="text-white lg:size-[5.556vw] size-[10vw]">
|
||||||
|
<ArrowMoreIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={"mailto:" + "info@graff.tech"}
|
||||||
|
className="lg:p-[1.667vw] p-6 flex flex-col justify-between max-md:gap-y-10 bg-[linear-gradient(to_top_left,#7A7A7A50,transparent)] lg:rounded-[1.111vw] rounded-2xl lg:aspect-[624/248] lg:w-[43.333vw] md:max-lg:w-[47.656vw] hover:bg-[#37393B99] bg-transparent transition-colors"
|
||||||
|
>
|
||||||
|
<div className="text-[#7A7A7A] text1 font-medium">Написать</div>
|
||||||
|
<div className="lg:line2 md:max-lg:heading1 line2 flex items-center font-medium">
|
||||||
|
info@graff.tech
|
||||||
|
<div className="text-white lg:size-[5.556vw] size-[10vw]">
|
||||||
|
<ArrowMoreIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div className="gap-y-2 justify-between md:gap-x-[1.042vw] gap-x-[1.111vw] md:flex-col flex">
|
||||||
|
<ContactLink href="https://t.me/graffestate">
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-[5.556vw] group-hover:text-black">
|
||||||
|
<TelegramIcon />
|
||||||
|
</div>
|
||||||
|
</ContactLink>
|
||||||
|
<ContactLink href="https://rutube.ru/channel/25505040">
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-[5.556vw] group-hover:text-black">
|
||||||
|
<RutubeIcon />
|
||||||
|
</div>
|
||||||
|
</ContactLink>
|
||||||
|
<ContactLink href="https://vk.com/graff.estate">
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-[5.556vw] group-hover:text-black">
|
||||||
|
<VkIcon />
|
||||||
|
</div>
|
||||||
|
</ContactLink>
|
||||||
|
<ContactLink href="https://www.youtube.com/@GRAFFtech">
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-[5.556vw] group-hover:text-black">
|
||||||
|
<YoutubeIcon />
|
||||||
|
</div>
|
||||||
|
</ContactLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:w-full flex flex-col md:flex-row md:max-lg:gap-[1.042vw] lg:gap-x-[0.833vw] gap-6 lg:pb-6 max-lg:py-6 pb-10 md:max-lg:pt-4 !max-md:mt-[11.111vw] border-b border-[#232425] relative">
|
||||||
|
<div className="flex flex-col gap-y-[1.111vw] lg:min-w-[48.193vw] flex-1">
|
||||||
|
<span className=" text1 text-[#7A7A7A]">Юридический адрес:</span>
|
||||||
|
<span className="headline2 max-md:leading-4 font-medium text-[#7A7A7A]">
|
||||||
|
620063, г. Екатеринбург, <br /> ул. Большакова, д. 66, кв. 6
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-y-[1.111vw] lg:mt-[0px] md:mt-[2.083vw] mt-6">
|
||||||
|
<span className=" text1 text-[#7A7A7A]">Наш основной стек:</span>
|
||||||
|
<div className="headline2 max-md:leading-4 font-medium text-[#7A7A7A]">
|
||||||
|
<p>Unreal Engine 5, C++</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-y-[1.111vw] flex-1">
|
||||||
|
<span className="text1 text-[#7A7A7A]">Реквизиты:</span>
|
||||||
|
<div className="headline2 max-md:leading-4 font-medium text-[#7A7A7A]">
|
||||||
|
<p>ИНН: 6679174128</p>
|
||||||
|
<p>КПП: 667101001</p>
|
||||||
|
<p>ООО "ГРАФФ.ЭСТЕЙТ"</p>
|
||||||
|
<p>ОГРН 1246600010140</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src="/img/components/header/Sk.svg"
|
||||||
|
alt="Сколково"
|
||||||
|
className=" lg:hidden md:size-[6.25vw] size-[13.333vw] max-md:absolute max-md:right-0 max-md:bottom-6"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:gap-x-[0.833vw] gap-y-2 flex max-lg:flex-col">
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={"/policy"}
|
||||||
|
className="text-[#37393B] text1 font-medium leading-[18.9px] lg:w-[48.193vw] w-fit"
|
||||||
|
>
|
||||||
|
Политика конфиденциальности и обработки персональных данных
|
||||||
|
</a>
|
||||||
|
<p className="text-[#37393B] text1 font-medium leading-[18.9px] col-start-1">
|
||||||
|
© 2026 GRAFF interactive. Все права защищены
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={"https://graff.tech"}
|
||||||
|
className="text-[#37393B] text1 font-medium leading-[18.9px] lg:ml-auto w-fit md:col-start-2 md:text-right"
|
||||||
|
>
|
||||||
|
graff.tech
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContactLink({
|
||||||
|
children,
|
||||||
|
href,
|
||||||
|
className = "",
|
||||||
|
}: PropsWithChildren<{ href: string; className?: string }>) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`lg:rounded-[1.111vw] rounded-2xl bg-[#37393B99] lg:p-[1.25vw] p-[18px] hover:bg-white transition-all hover:text-black flex justify-center w-full group ${className}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface BRProps {
|
||||||
|
lg?: boolean;
|
||||||
|
md?: boolean;
|
||||||
|
sm?: boolean;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BR({ lg, md, sm, className }: BRProps) {
|
||||||
|
const modifier =
|
||||||
|
!lg && !md && !sm
|
||||||
|
? ""
|
||||||
|
: `lg:${lg ? `block` : "hidden"} md:${md ? "block" : "hidden"} ${
|
||||||
|
sm ? "block" : "hidden"
|
||||||
|
} `;
|
||||||
|
const combinedClassName = `${modifier} ${className ?? ""}`;
|
||||||
|
|
||||||
|
return <br className={combinedClassName} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { useModalStore } from "@/stores/useModalStore";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
|
export function ModalContainer() {
|
||||||
|
const { modal, setModal } = useModalStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Escape") setModal(null);
|
||||||
|
};
|
||||||
|
document.addEventListener("keydown", listener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("keydown", listener);
|
||||||
|
};
|
||||||
|
}, [setModal]);
|
||||||
|
|
||||||
|
const jsx = modal ? (
|
||||||
|
<div className="fixed left-0 z-[20] w-full h-full flex justify-center items-start transition-opacity">
|
||||||
|
<div className="absolute [backdrop-filter:blur(16px)] bg-[#0F101199] w-full h-full z-[1]" />
|
||||||
|
{modal}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return createPortal(jsx, document.body);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function ArrowMoreIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="m15.588 9.59-8.52 8.52v-2.94l7.049-7.05H6.332V6.04h11.336v11.336h-2.08z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArrowMoreIcon;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function CheckIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="m9.707 18 9.707-9.707L18 6.879l-8.293 8.293-4.293-4.293L4 12.293z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckIcon;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function CloseIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M6.343 4.929 12 10.586l5.657-5.657 1.414 1.414L13.414 12l5.657 5.657-1.414 1.414L12 13.414l-5.657 5.657-1.414-1.414L10.586 12 4.929 6.343z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CloseIcon;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function MuteIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M5.74 3.993a1 1 0 1 0-1.48 1.345l2.116 2.328h-2.71A1.667 1.667 0 0 0 2 9.333v5.333a1.667 1.667 0 0 0 1.667 1.667h3.656l5.73 4.456A1 1 0 0 0 14.667 20v-3.214l2.926 3.22a1 1 0 0 0 1.48-1.345zM4 9.666h2.667v4.667H4zm8.667 8.289-4-3.111v-4.658l4 4.397zM10.083 6.788a1 1 0 0 1 .176-1.404l2.793-2.173A1 1 0 0 1 14.667 4v5.245a1 1 0 0 1-2 0v-3.2l-1.18.917a1 1 0 0 1-1.404-.175zM16.25 10.9a1 1 0 0 1 1.5-1.32 3.67 3.67 0 0 1 .462 4.184 1 1 0 0 1-1.75-.963 1.67 1.67 0 0 0-.212-1.903zM22 12a7 7 0 0 1-1.593 4.446 1.002 1.002 0 0 1-1.663-.16 1 1 0 0 1 .12-1.111 5 5 0 0 0-.137-6.509 1.001 1.001 0 1 1 1.49-1.333A7 7 0 0 1 22 11.999"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MuteIcon;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function PauseIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M10 3H7L5 5v16h3l2-2zm9 0h-3l-2 2v16h3l2-2z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PauseIcon;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
function PlayIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M21 12 6 21V3z" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlayIcon;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function RutubeIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M16.19 10.815H6.928V7.308h9.262c.54 0 .917.09 1.106.248.189.157.306.45.306.877v1.259c0 .45-.117.742-.306.9-.189.157-.565.224-1.106.224m.635-6.814H3V19h3.928v-4.88h7.239L17.602 19H22l-3.787-4.903c1.396-.198 2.023-.607 2.54-1.282s.776-1.753.776-3.193V8.497c0-.854-.094-1.529-.259-2.046a3.4 3.4 0 0 0-.846-1.37 3.9 3.9 0 0 0-1.459-.833C18.4 4.09 17.695 4 16.825 4z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RutubeIcon;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function TgIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M3.27 11.886a640 640 0 0 1 9.65-4.47C17.511 5.36 18.467 5 19.086 5c.13 0 .439.028.645.194.155.14.207.334.232.472.026.14.052.445.026.695-.258 2.804-1.316 9.662-1.883 12.8-.233 1.333-.697 1.777-1.136 1.833-.954.083-1.702-.694-2.631-1.333-1.445-1.027-2.271-1.666-3.69-2.666-1.626-1.166-.568-1.805.361-2.832.232-.277 4.49-4.415 4.567-4.804 0-.055.026-.222-.077-.305-.104-.083-.232-.056-.336-.028-.155.028-2.477 1.694-6.992 4.97-.671.5-1.265.723-1.806.723-.594 0-1.73-.361-2.58-.667-1.033-.36-1.858-.555-1.781-1.166.077-.333.49-.667 1.264-1"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TgIcon;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function UnmutedIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M14.106 3.102a1 1 0 0 0-1.053.109l-5.73 4.456H3.667A1.667 1.667 0 0 0 2 9.333v5.334a1.667 1.667 0 0 0 1.667 1.666h3.656l5.73 4.456A1 1 0 0 0 14.667 20V4a1 1 0 0 0-.561-.898M4 9.667h2.667v4.666H4zm8.667 8.288-4-3.11v-5.69l4-3.11zm6-5.955c0 .893-.326 1.756-.917 2.426a1 1 0 0 1-1.5-1.324 1.67 1.67 0 0 0 0-2.202 1 1 0 0 1 1.5-1.322c.59.669.916 1.53.917 2.422M22 12a7 7 0 0 1-1.782 4.667 1 1 0 0 1-1.491-1.334 5 5 0 0 0 0-6.666 1 1 0 1 1 1.49-1.334A7 7 0 0 1 22 12"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UnmutedIcon;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function VKIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M4.085 6H1.788C1.13 6 1 6.297 1 6.625c0 .586.779 3.49 3.627 7.33C6.525 16.578 9.2 18 11.634 18c1.46 0 1.64-.316 1.64-.86v-1.982c0-.632.139-.758.602-.758.34 0 .925.164 2.288 1.429C17.72 17.327 17.978 18 18.854 18h2.298c.656 0 .984-.316.795-.94-.207-.62-.95-1.521-1.938-2.59-.536-.608-1.34-1.264-1.582-1.592-.341-.421-.243-.609 0-.983 0 0 2.799-3.794 3.092-5.082.145-.469 0-.813-.696-.813h-2.297c-.584 0-.853.297-.999.625 0 0-1.168 2.74-2.823 4.52-.536.515-.78.68-1.071.68-.146 0-.357-.165-.357-.633v-4.38c0-.561-.17-.812-.657-.812H9.01c-.365 0-.584.26-.584.508 0 .533.827.656.912 2.154v3.256c0 .714-.134.843-.427.843-.778 0-2.673-2.752-3.796-5.901-.22-.614-.442-.86-1.029-.86"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VKIcon;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
function YoutubeIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M19.814 5.423a2.52 2.52 0 0 1 1.768 1.79C22 8.79 22 12.082 22 12.082s0 3.293-.418 4.871a2.52 2.52 0 0 1-1.768 1.79c-1.56.423-7.814.423-7.814.423s-6.254 0-7.814-.423a2.52 2.52 0 0 1-1.768-1.79C2 15.377 2 12.084 2 12.084s0-3.294.418-4.872a2.52 2.52 0 0 1 1.768-1.79C5.746 5 12 5 12 5s6.254 0 7.814.423m-9.48 3.744V15l5-2.917z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default YoutubeIcon;
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/ui/Button";
|
||||||
|
import { api } from "@/lib/api";
|
||||||
|
import { useModalStore } from "@/stores/useModalStore";
|
||||||
|
import { modalOptions } from "@/consts/feedback";
|
||||||
|
import CustomCheckbox from "@/ui/CustomCheckbox";
|
||||||
|
import CheckIcon from "@/components/icons/CheckIcon";
|
||||||
|
|
||||||
|
function FeedbackModal({ id }: { id: string }) {
|
||||||
|
const { setModal } = useModalStore();
|
||||||
|
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
|
||||||
|
|
||||||
|
async function sendSources() {
|
||||||
|
await api.put(`mail/${id}`, { json: { source: selectedOptions } });
|
||||||
|
setModal(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCheckboxChange(value: string, checked: boolean) {
|
||||||
|
if (checked) setSelectedOptions([...selectedOptions, value]);
|
||||||
|
else {
|
||||||
|
const updated = [...selectedOptions].filter((item) => item !== value);
|
||||||
|
setSelectedOptions(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed top-[50%] translate-y-[-50%] flex md:flex-row flex-col md:max-w-[695px] max-w-[422px] max-[360px]:max-w-[340px] z-[15] md:p-[48px] p-[32px] max-[360px]:p-[24px] bg-[#37393B99] backdrop-blur-xl backdrop-opacity-60 rounded-2xl">
|
||||||
|
<div className="flex md:flex-col flex-row md:justify-center items-center md:max-w-[200px] md:gap-y-[16px] gap-x-[24px]">
|
||||||
|
<div className="p-3 rounded-full bg-gradient translate-x-[4px]">
|
||||||
|
<div className="z-10 absolute top-[-4px] left-[-4.3px] w-[56px] h-[56px] rounded-full bg-gradient-to-r from-[#6078F299] to-[#C868F599]" />
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-4 relative z-20">
|
||||||
|
<CheckIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="text-xl leading-6 md:text-center text-start max-[360px]:text-base">
|
||||||
|
Мы получили заявку <br className="md:hidden block" /> и скоро свяжемся{" "}
|
||||||
|
<br className="md:block hidden" /> с вами!
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:w-[5px] md:mx-[48px] md:my-[0px] md:h-auto h-[2px] my-[24px] bg-[#37393B] rounded-sm "></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div className="text-xl leading-6 mb-[20px] max-w-[250px] max-[360px]:text-base">
|
||||||
|
Расскажите, пожалуйста, <br /> откуда вы узнали о нас?
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="md:mb-[49px] mb-[58px] flex flex-col gap-y-[12px]">
|
||||||
|
{modalOptions.map((item, index) => (
|
||||||
|
<li key={index} className="flexfont-normal">
|
||||||
|
<CustomCheckbox value={item} onChange={onCheckboxChange} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="flex flex-row gap-x-[12px] ">
|
||||||
|
<Button
|
||||||
|
onClick={sendSources}
|
||||||
|
className="md:px-[31px] max-[360px]:px-[32px] px-[43px] py-[15px] rounded-2xl max-[360px]:text-sm"
|
||||||
|
>
|
||||||
|
Отправить
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setModal(null)}
|
||||||
|
className="md:px-[31px] px-[43px] max-[360px]:px-[32px] py-[15px] rounded-2xl bg-[#37393B] max-[360px]:text-sm"
|
||||||
|
color="secondary"
|
||||||
|
>
|
||||||
|
Пропустить
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FeedbackModal;
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import React, { useRef } from "react";
|
||||||
|
import { Button } from "@/ui/Button";
|
||||||
|
import { PhoneInputRu } from "@/ui/PhoneInputRu";
|
||||||
|
import { useOnClickOutside } from "usehooks-ts";
|
||||||
|
import { useModalStore } from "@/stores/useModalStore";
|
||||||
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
|
import CheckIcon from "@/components/icons/CheckIcon";
|
||||||
|
import { projectsTags } from "@/consts/projectsTags";
|
||||||
|
import { CheckboxesGroup } from "@/ui/CheckboxesGroup";
|
||||||
|
import { Product } from "@/types/Product";
|
||||||
|
import FeedbackModal from "./FeedbackFormModal";
|
||||||
|
import { api } from "@/lib/api";
|
||||||
|
import { useRefererStore } from "@/stores/useRefererStore";
|
||||||
|
import CloseIcon from "@/components/icons/CloseIcon";
|
||||||
|
|
||||||
|
interface IInput {
|
||||||
|
fullname: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
products1: Product[];
|
||||||
|
referer?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IQuestionFormModalProps {
|
||||||
|
products?: Product[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function QuestionFormModal({
|
||||||
|
products,
|
||||||
|
}: IQuestionFormModalProps) {
|
||||||
|
const { referer } = useRefererStore();
|
||||||
|
const { setModal } = useModalStore();
|
||||||
|
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
|
useOnClickOutside(formRef, () => {
|
||||||
|
setModal(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm<IInput>({
|
||||||
|
defaultValues: {
|
||||||
|
phone: "",
|
||||||
|
products1:
|
||||||
|
products || (["Создание сайтов", "Веб-тур по 360 сферам"] as Product[]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { register, handleSubmit, formState, control } = form;
|
||||||
|
|
||||||
|
async function onSubmit(data: IInput) {
|
||||||
|
const { id } = await api
|
||||||
|
.post("mail", { json: { ...data, products: data.products1, referer } })
|
||||||
|
.json<{ id: string }>();
|
||||||
|
setModal(<FeedbackModal id={id} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={formRef}
|
||||||
|
className="p-[3.333vw] w-[64.514vw] backdrop-blur-[20px] rounded-[1.111vw] z-10 bg-[radial-gradient(circle_at_bottom_right,rgba(24,25,26,0.84)_0%,rgba(45,46,47,0.86)_100%)] absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-md:w-[94.444vw] max-md:px-[6.667vw] max-md:py-[5.556vw] max-md:rounded-[4.444vw]"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setModal(null)}
|
||||||
|
className="absolute lg:p-4 p-0 lg:top-[1.667vw] md:top-[3.125vw] top-[3.333vw] right-[3.333vw] md:right-[3.125vw] cursor-pointer"
|
||||||
|
>
|
||||||
|
<div className="text-white lg:size-[1.667vw] md:size-[3.125vw] size-[6.667vw]">
|
||||||
|
<CloseIcon />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{!formState.isSubmitted ? (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<form
|
||||||
|
className="lg:space-y-[1.944vw] md:max-lg:space-y-7 space-y-3"
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
>
|
||||||
|
<div className="lg:space-y-[1.111vw] space-y-4">
|
||||||
|
<p className="heading2 font-medium">Нам нужно</p>
|
||||||
|
<CheckboxesGroup name="products1" options={projectsTags} />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
autoComplete="none"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
placeholder="Имя*"
|
||||||
|
{...register("fullname")}
|
||||||
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:btnl btnl placeholder:font-medium placeholder:select-none"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
autoComplete="none"
|
||||||
|
required
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Email*"
|
||||||
|
{...register("email")}
|
||||||
|
className="bg-transparent border-b border-[#37393B] focus:border-white py-4 rounded-none btnl outline-none transition-all w-full placeholder:btnl placeholder:font-medium placeholder:select-none"
|
||||||
|
/>
|
||||||
|
<div className="flex gap-x-3 py-2 border-[#3D425C] relative">
|
||||||
|
<Controller
|
||||||
|
name="phone"
|
||||||
|
control={control}
|
||||||
|
rules={{ required: true }}
|
||||||
|
render={({ field }) => (
|
||||||
|
<PhoneInputRu
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
onBlur={field.onBlur}
|
||||||
|
inputRef={field.ref}
|
||||||
|
placeholder="+X (XXX) XXX - XX - XX"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="bottom-0 absolute w-full border-b border-[#37393B] peer-focus:border-white -mb-2" />
|
||||||
|
</div>
|
||||||
|
<div className="md:flex items-stretch lg:gap-[0.833vw] gap-3">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="btnl max-md:mb-3 max-md:w-full lg:px-[2.222vw] lg:py-[1.389vw] px-8 py-5 cursor-pointer lg:rounded-[1.111vw] rounded-2xl"
|
||||||
|
>
|
||||||
|
Оставить заявку
|
||||||
|
</Button>
|
||||||
|
<div className="text2 xl:max-w-[60%] md:max-lg:max-w-[40%] md:max-lg:py-1">
|
||||||
|
<span className="text-[#7A7A7A]">
|
||||||
|
*Нажимая кнопку отправить, вы даете
|
||||||
|
</span>{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
href={"/privacy-policy"}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
согласие на обработку персональных данных
|
||||||
|
</a>{" "}
|
||||||
|
<span className="text-[#7A7A7A]">и принимаете </span>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
href={"/policy"}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
условия политики
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
) : (
|
||||||
|
<div className="bg-[#37393B99] aspect-[643/480] w-full rounded-2xl flex justify-center items-center">
|
||||||
|
<div className="flex gap-3 justify-center items-center">
|
||||||
|
<div className="bg-gradient p-3 rounded-full">
|
||||||
|
<div className="text-white lg:size-[1.667vw] size-6">
|
||||||
|
<CheckIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="heading2 font-medium">
|
||||||
|
Мы получили заявку
|
||||||
|
<br />и скоро свяжемся с вами!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export const modalOptions: string[] = [
|
||||||
|
"Увидели на выставке или форуме",
|
||||||
|
"Видели у других застройщиков",
|
||||||
|
"Из рейтингов и статей",
|
||||||
|
"Нашли в интернете",
|
||||||
|
"Перешли по рекламе",
|
||||||
|
"Из рассылки",
|
||||||
|
"Другое",
|
||||||
|
];
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { Product } from "@/types/Product";
|
||||||
|
|
||||||
|
export const projectsTags: Product[] = [
|
||||||
|
"Интерактивная презентация",
|
||||||
|
"Удаленная демонстрация",
|
||||||
|
"Архитектурная визуализация",
|
||||||
|
"Создание сайтов",
|
||||||
|
"Веб-тур по 360 сферам",
|
||||||
|
];
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
export const streaming = [
|
||||||
|
{
|
||||||
|
title: "ЖК «Риваят»",
|
||||||
|
url: "https://stream.graff.tech/?build=PortovayaDev&location=a1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "БК «Прокшино»",
|
||||||
|
url: "https://stream.graff.tech/?build=Prokshino&location=a1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ЖК «DNS Сити»",
|
||||||
|
url: "https://stream.graff.tech/?build=DNScity&location=a1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "ЖК «Upside Towers»",
|
||||||
|
url: "https://stream.graff.tech/?build=upsideTowersDev&location=a1",
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import BR from "@/components/Layout/LineBreak";
|
||||||
|
import { useGetProjectsQuery } from "@/queries/getProjects";
|
||||||
|
import { StreamingProject } from "./StreamingProject";
|
||||||
|
import { useSwipeable } from "react-swipeable";
|
||||||
|
import { useMediaQueries } from "@/hooks/useMediaQueries";
|
||||||
|
|
||||||
|
export default function AvailableDemos() {
|
||||||
|
const { isMd, isLg } = useMediaQueries();
|
||||||
|
const { data: streamingProjects } = useGetProjectsQuery(
|
||||||
|
"Удаленная демонстрация"
|
||||||
|
);
|
||||||
|
const [current, setCurrent] = useState(0);
|
||||||
|
|
||||||
|
const projects = streamingProjects ?? [];
|
||||||
|
|
||||||
|
const slideCount = Math.min(projects.length + 1, 4);
|
||||||
|
|
||||||
|
const handlers = useSwipeable({
|
||||||
|
onSwipedLeft: () =>
|
||||||
|
setCurrent((prev) => (prev + 1) % Math.max(slideCount, 1)),
|
||||||
|
onSwipedRight: () =>
|
||||||
|
setCurrent(
|
||||||
|
(prev) =>
|
||||||
|
(prev + Math.min(projects.length, 4)) % Math.max(slideCount, 1)
|
||||||
|
),
|
||||||
|
trackMouse: true,
|
||||||
|
preventScrollOnSwipe: true,
|
||||||
|
touchEventOptions: { passive: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex lg:flex-row flex-col lg:mb-[4.444vw] md:mb-[8.333vw] md:gap-[3.125vw]">
|
||||||
|
<h2 className="line2 max-md:line1 w-full max-md:mb-[5.556vw]">
|
||||||
|
Доступные <BR lg sm /> демонстрации
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{!isLg && !isMd && (
|
||||||
|
<div
|
||||||
|
className="lg:grid md:flex grid-cols-4 gap-3 px-5 md:-mx-5 md:overflow-auto [scrollbar-width:none] relative max-md:aspect-[340/344] [transform-style:preserve-3d] items-stretch mb-[5.556vw]"
|
||||||
|
{...handlers}
|
||||||
|
>
|
||||||
|
{projects.slice(0, 3).map((project, index, { length }) => (
|
||||||
|
<StreamingProject
|
||||||
|
key={project.id}
|
||||||
|
{...project}
|
||||||
|
index={index}
|
||||||
|
current={current}
|
||||||
|
count={length + 1}
|
||||||
|
href="/"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div
|
||||||
|
className={`bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF00] [background:linear-gradient(to_right,#FFFFFF14,#FFFFFF00)] p-0.5 lg:rounded-[1.111vw] rounded-2xl flex flex-1 justify-center !duration-500 items-center md:min-w-[300px] group max-md:absolute self-stretch max-md:h-full transition-[scale,transform] will-change-[transform,scale] select-none max-md:w-[calc(100%-40px)] max-md:bg-[#0F101199] max-md:[backdrop-filter:blur(40px)] ${
|
||||||
|
slideCount - 1 === current
|
||||||
|
? "max-md:[transform:translateZ(40px)]"
|
||||||
|
: "max-md:[scale:85%]"
|
||||||
|
} ${
|
||||||
|
slideCount - 1 === (current + 1) % slideCount
|
||||||
|
? "max-md:translate-x-[calc(7.5%+20px)]"
|
||||||
|
: slideCount - 1 ===
|
||||||
|
(current - 1 + slideCount) % slideCount
|
||||||
|
? "max-md:translate-x-[calc(-7.5%-20px)]"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="md:bg-[#0F1011] h-full w-full lg:rounded-[1.111vw] rounded-2xl flex items-center p-6">
|
||||||
|
<div className="flex flex-col items-center space-y-6">
|
||||||
|
<p className="heading2 font-medium text-center">
|
||||||
|
Расскажем и покажем как это работает на созвоне
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="/form"
|
||||||
|
className="btnm font-medium group-hover:scale-105 duration-500 lg:px-[1.667vw] lg:py-[1.181vw] px-6 py-[17px] transition-transform lg:rounded-[0.833vw] rounded-xl bg-gradient"
|
||||||
|
>
|
||||||
|
Оставить заявку
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="lg:headline1 headline2 text-[#7A7A7A] w-full pr-[8.333vw] md:max-w-[75vw]">
|
||||||
|
Клиент из любой точки мира может посмотреть жилой комплекс, даже на
|
||||||
|
нулевом этапе строительства. Он выберет лучшую планировку и оценит вид
|
||||||
|
из окон своей будущей квартиры.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(isLg || isMd) && (
|
||||||
|
<div className="flex md:-mx-[2.604vw] md:w-[calc(100%+5.208vw)] md:px-[2.604vw] lg:gap-[0.833vw] md:gap-[1.563vw] lg:h-[27.5vw] md:h-[51.563vw] md:overflow-x-scroll hide-scrollbars">
|
||||||
|
{projects.slice(0, 3).map((project, index, { length }) => (
|
||||||
|
<div
|
||||||
|
key={project.id}
|
||||||
|
className={`w-full ${index === 0 ? "flex-auto" : "flex-1"}`}
|
||||||
|
>
|
||||||
|
<StreamingProject
|
||||||
|
key={project.id}
|
||||||
|
{...project}
|
||||||
|
index={index}
|
||||||
|
current={current}
|
||||||
|
count={length + 1}
|
||||||
|
href="/"
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React from "react";
|
||||||
|
import BR from "@/components/Layout/LineBreak";
|
||||||
|
import { Button } from "@/ui/Button";
|
||||||
|
import QuestionFormModal from "@/components/modals/QuestionFormModal";
|
||||||
|
import { useModalStore } from "@/stores/useModalStore";
|
||||||
|
|
||||||
|
export default function RequestForDemo() {
|
||||||
|
const { setModal } = useModalStore();
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="
|
||||||
|
flex max-md:flex-col flex-row lg:gap-[0.833vw] md:gap-[2.865vw] gap-[11.111vw]
|
||||||
|
lg:-mx-[1.389vw] lg:w-[calc(100%+2.778vw)] lg:pl-[1.389vw]
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col lg:max-w-[31.944vw] min-h-full">
|
||||||
|
<h2 className="line2 max-md:mb-[6.667vw]">
|
||||||
|
Запись <BR lg md /> на удаленную
|
||||||
|
<BR /> демонстрацию
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-col lg:gap-[2.222vw] md:gap-[4.167vw] mt-auto">
|
||||||
|
<p className="lg:headline1 headline2 text-[#7A7A7A]">
|
||||||
|
Запись на демонстрацию может быть оформлена в виде блока на сайте
|
||||||
|
застройщика или жилого комплекса.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setModal(
|
||||||
|
<QuestionFormModal products={["Удаленная демонстрация"]} />
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className="max-md:hidden btnl bg-gradient-saturated lg:py-[1.389vw] lg:px-[2.222vw] md:py-[2.604vw] md:px-[4.167vw] md:rounded-[2.083vw] lg:rounded-[1.111vw]"
|
||||||
|
>
|
||||||
|
Оставить заявку
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img
|
||||||
|
src="/img/pages/stream-demo/showreel.png"
|
||||||
|
alt=""
|
||||||
|
className="lg:h-[44.444vw] md:h-[57.292vw] h-[122.222vw] max-md:rounded-[4.444vw] object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Feedback } from "@/components/Layout/Feedback";
|
||||||
|
import AvailableDemos from "./AvailableDemos";
|
||||||
|
import RequestForDemo from "./RequestForDemo";
|
||||||
|
import StreamPlayer from "./StreamPlayer";
|
||||||
|
|
||||||
|
export default function StreamDemo() {
|
||||||
|
return (
|
||||||
|
<div className="lg:space-y-[9.722vw] md:space-y-[13.021vw] space-y-[27.778vw]">
|
||||||
|
<AvailableDemos />
|
||||||
|
<RequestForDemo />
|
||||||
|
<div className="w-full shrink-0 aspect-[340/600] max-h-[85dvh] md:max-lg:aspect-[736/480] md:max-lg:max-h-[80dvh] lg:aspect-auto lg:h-[44.444vw] lg:max-h-none">
|
||||||
|
<StreamPlayer className="h-full min-h-[12rem]" />
|
||||||
|
</div>
|
||||||
|
<Feedback />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { resolvePublicPath } from "@/lib/env";
|
||||||
|
import { VideoPlayer } from "@/ui/VideoPlayer";
|
||||||
|
|
||||||
|
const STREAMING_VIDEO_SRC = resolvePublicPath(
|
||||||
|
"/videos/pages/home/streaming.mp4"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function StreamPlayer({ className }: { className?: string }) {
|
||||||
|
return (
|
||||||
|
<div className={`w-full ${className ?? ""}`}>
|
||||||
|
<VideoPlayer
|
||||||
|
src={STREAMING_VIDEO_SRC}
|
||||||
|
showMutingBtn
|
||||||
|
loop
|
||||||
|
autoPlay
|
||||||
|
muted
|
||||||
|
className="lg:aspect-[1400/640] max-h-dvh md:max-lg:aspect-[736/480] aspect-[340/600]"
|
||||||
|
>
|
||||||
|
<p className="absolute font-medium md:bottom-6 md:left-6 bottom-4 left-4 lg:max-w-[40%] md:max-lg:max-w-[80%] accent">
|
||||||
|
GRAFF.estate — модуль удаленной демонстрации — доступен на любых
|
||||||
|
устройствах, для демонстрации нужен только интернет
|
||||||
|
</p>
|
||||||
|
</VideoPlayer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { streaming } from "@/consts/streaming";
|
||||||
|
import { resolveProjectImageSrc } from "@/lib/resolveProjectImageSrc";
|
||||||
|
import { IProject } from "@/types/IProject";
|
||||||
|
import ArrowMoreIcon from "@/components/icons/ArrowMoreIcon";
|
||||||
|
|
||||||
|
export function StreamingProject({
|
||||||
|
city,
|
||||||
|
title,
|
||||||
|
image,
|
||||||
|
href,
|
||||||
|
company,
|
||||||
|
index,
|
||||||
|
current,
|
||||||
|
count,
|
||||||
|
className,
|
||||||
|
}: Pick<IProject, "city" | "title" | "image" | "company"> & {
|
||||||
|
href: string;
|
||||||
|
index: number;
|
||||||
|
current: number;
|
||||||
|
count: number;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
const imgSrc = resolveProjectImageSrc(image);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`lg:aspect-[344/396] aspect-[300/344] flex-1 md:max-lg:min-w-[300px] transition-[scale,transform] will-change-[transform,scale] lg:rounded-[1.111vw] rounded-2xl lg:p-[1.111vw] p-4 flex duration-500 relative overflow-hidden group max-md:absolute max-md:w-[calc(100%-40px)] select-none h-full ${
|
||||||
|
index === current
|
||||||
|
? "max-md:[transform:translateZ(40px)]"
|
||||||
|
: "max-md:[scale:85%]"
|
||||||
|
} ${
|
||||||
|
index === (current + 1) % count
|
||||||
|
? "max-md:[transform:translateX(calc(7.5%+20px))]"
|
||||||
|
: index === (current - 1 + count) % count
|
||||||
|
? "max-md:[transform:translateX(calc(-7.5%-20px))]"
|
||||||
|
: ""
|
||||||
|
} ${className ?? ""}`}
|
||||||
|
>
|
||||||
|
<div className="z-0 rounded-2xl absolute inset-0 overflow-hidden transition-transform duration-500 group-hover:scale-110">
|
||||||
|
<img
|
||||||
|
src={imgSrc}
|
||||||
|
alt=""
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
className="absolute inset-0 z-0 size-full object-cover object-bottom"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className="pointer-events-none absolute inset-0 z-[1] rounded-2xl bg-[linear-gradient(to_bottom,rgba(0,0,0,0.45),transparent)] lg:bg-[linear-gradient(to_top,rgba(0,0,0,0.45),transparent)]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="relative z-[2] lg:self-end space-y-3 font-medium">
|
||||||
|
<p className="heading1 font-medium">{title}</p>
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{company && (
|
||||||
|
<div className="px-2 py-1.5 flex lg:gap-[0.278vw] gap-1 items-center lg:rounded-[1.181vw] rounded-2xl [backdrop-filter:blur(12px)] bg-[#37393B99] btns">
|
||||||
|
<div
|
||||||
|
className="lg:w-[0.556vw] lg:h-[0.556vw] w-2 h-2 rounded-full m-1"
|
||||||
|
style={{ backgroundColor: company.color }}
|
||||||
|
/>
|
||||||
|
{company.title}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="lg:px-[0.833vw] lg:py-[0.486vw] px-3 py-[7px] bg-[#37393B99] [backdrop-filter:blur(12px)] lg:rounded-[1.181vw] rounded-2xl btns">
|
||||||
|
{city}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="z-[2] lg:hidden absolute right-4 bottom-4">
|
||||||
|
<a
|
||||||
|
className="bg-gradient btns flex gap-2 items-center px-3 py-2 font-medium rounded-xl"
|
||||||
|
href={streaming.find((s) => s.title === title)?.url ?? href}
|
||||||
|
>
|
||||||
|
Смотреть
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-4">
|
||||||
|
<ArrowMoreIcon />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={streaming.find((s) => s.title === title)?.url ?? href}
|
||||||
|
className="max-lg:hidden lg:group-hover:opacity-100 opacity-0 transition-opacity duration-500 absolute w-full h-full left-0 bottom-0 md:max-lg:rounded-2xl rounded-xl font-medium [backdrop-filter:blur(3px)] content-center text-center z-[3]"
|
||||||
|
>
|
||||||
|
<div className="btnl flex gap-2 justify-center">
|
||||||
|
Начать демонстрацию{" "}
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-4">
|
||||||
|
<ArrowMoreIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { useRefererStore } from "@/stores/useRefererStore";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
export default function useAddReferer() {
|
||||||
|
const { setReferer } = useRefererStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const referer = new URLSearchParams(window.location.search).get("ref");
|
||||||
|
if (!referer) return;
|
||||||
|
setReferer(referer);
|
||||||
|
}, [setReferer]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
function readBreakpoints(lg: number, md: number, sm: number) {
|
||||||
|
if (typeof window === "undefined" || typeof window.matchMedia === "undefined") {
|
||||||
|
return {
|
||||||
|
isLg: false,
|
||||||
|
isMd: false,
|
||||||
|
isSm: false,
|
||||||
|
isXs: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const lgMedia = matchMedia(`(min-width: ${lg}px)`);
|
||||||
|
const mdMedia = matchMedia(
|
||||||
|
`(max-width: ${lg - 1}px) and (min-width: ${md}px)`
|
||||||
|
);
|
||||||
|
const smMedia = matchMedia(
|
||||||
|
`(max-width: ${md - 1}px) and (min-width: ${sm}px)`
|
||||||
|
);
|
||||||
|
const xsMedia = matchMedia(`(max-width: ${sm - 1}px)`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLg: lgMedia.matches,
|
||||||
|
isMd: mdMedia.matches,
|
||||||
|
isSm: smMedia.matches,
|
||||||
|
isXs: xsMedia.matches,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMediaQueries(lg = 1440, md = 768, sm = 640) {
|
||||||
|
const [state, setState] = useState(() => readBreakpoints(lg, md, sm));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const lgMedia = matchMedia(`(min-width: ${lg}px)`);
|
||||||
|
const mdMedia = matchMedia(
|
||||||
|
`(max-width: ${lg - 1}px) and (min-width: ${md}px)`
|
||||||
|
);
|
||||||
|
const smMedia = matchMedia(
|
||||||
|
`(max-width: ${md - 1}px) and (min-width: ${sm}px)`
|
||||||
|
);
|
||||||
|
const xsMedia = matchMedia(`(max-width: ${sm - 1}px)`);
|
||||||
|
|
||||||
|
const sync = () => setState(readBreakpoints(lg, md, sm));
|
||||||
|
|
||||||
|
lgMedia.addEventListener("change", sync);
|
||||||
|
mdMedia.addEventListener("change", sync);
|
||||||
|
smMedia.addEventListener("change", sync);
|
||||||
|
xsMedia.addEventListener("change", sync);
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
lgMedia.removeEventListener("change", sync);
|
||||||
|
mdMedia.removeEventListener("change", sync);
|
||||||
|
smMedia.removeEventListener("change", sync);
|
||||||
|
xsMedia.removeEventListener("change", sync);
|
||||||
|
};
|
||||||
|
}, [lg, md, sm]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLg: state.isLg,
|
||||||
|
isMd: state.isMd,
|
||||||
|
isSm: state.isSm,
|
||||||
|
isXs: state.isXs,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
@import url("/fonts/TTHovesProAll/stylesheet.css");
|
||||||
|
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
min-height: 100dvh;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-x: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "TTHovesPro", system-ui, -apple-system, "Segoe UI", Roboto,
|
||||||
|
sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #0f1011;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
overscroll-behavior-y: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbars {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbars::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.line1 {
|
||||||
|
@apply 2xl:text-[128px] lg:text-[clamp(96px,96px+(100vw-1440px)/96*32,128px)] md:text-[clamp(56px,56px+(100vw-768px)/672*40,96px)] xs:text-[clamp(40px,40px+(100vw-360px)/408*16,56px)] text-[40px] leading-[85%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.line2 {
|
||||||
|
@apply lg:text-[clamp(64px,4.444vw,88px)] md:text-[clamp(40px,40px+(100vw-768px)/672*24,64px)] xs:text-[clamp(32px,32px+(100vw-360px)/408*8,40px)] max-xs:text-[32px] leading-[95%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading1 {
|
||||||
|
@apply lg:text-[clamp(28px,1.944vw,42px)] md:text-[clamp(24px,24px+(100vw-768px)/672*4,28px)] text-2xl leading-[1.167];
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading2 {
|
||||||
|
@apply lg:text-[clamp(24px,1.667vw,36px)] md:text-[clamp(20px,20px+(100vw-768px)/672*4,24px)] xs:text-[clamp(16px,16px+(100vw-360px)/408*4,20px)] text-base lg:leading-[1.2] leading-[1.125];
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent {
|
||||||
|
@apply lg:text-[clamp(32px,2.222vw,56px)] md:text-[clamp(24px,24px+(100vw-768px)/672*8,32px)] text-2xl lg:leading-[1.1] leading-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text1 {
|
||||||
|
@apply lg:text-[clamp(18px,1.25vw,24px)] md:text-[clamp(14px,14px+(100vw-768px)/672*4,18px)] text-sm leading-[1.35];
|
||||||
|
}
|
||||||
|
|
||||||
|
.text2 {
|
||||||
|
@apply lg:text-[clamp(12px,0.972vw,20px)] md:text-[clamp(12px,12px+(100vw-768px)/672*2,14px)] text-xs leading-[1.35];
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnl {
|
||||||
|
@apply lg:text-[clamp(18px,1.25vw,28px)] md:text-[clamp(16px,16+(100vw-768px)/256*2,18px)] text-base leading-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnm {
|
||||||
|
@apply lg:text-[clamp(16px,1.111vw,24px)] md:text-[clamp(14px,14px+(100vw-768px)/256*2,16px)] text-sm leading-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btns {
|
||||||
|
@apply lg:text-[clamp(14px,0.972vw,20px)] md:text-[clamp(12px,12px+(100vw-768px)/256*2,14px)] text-xs leading-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline1 {
|
||||||
|
@apply font-medium text-[1.667vw] leading-[1.944vw] tracking-[-0.02em] md:max-lg:text-[3.125vw] md:max-lg:leading-[3.646vw] max-md:text-[6.667vw] max-md:leading-[7.778vw];
|
||||||
|
}
|
||||||
|
|
||||||
|
.headline2 {
|
||||||
|
@apply font-medium text-[1.389vw] leading-[1.667vw] tracking-[-0.02em] md:max-lg:text-[2.083vw] md:max-lg:leading-[2.344vw] max-md:text-base max-md:leading-[4.444vw] max-md:font-normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient {
|
||||||
|
background: linear-gradient(87deg, #798fff 15%, #d375ff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient-saturated {
|
||||||
|
background: linear-gradient(45deg, #7a55ff 0%, #c932e8 75%, #ff79d2 95%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-device-width: 480px) {
|
||||||
|
body {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import ky from "ky";
|
||||||
|
import { getApiBase } from "@/lib/env";
|
||||||
|
|
||||||
|
const base = getApiBase();
|
||||||
|
|
||||||
|
export const api = ky.create({
|
||||||
|
prefixUrl: base || undefined,
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Доступ к публичным переменным окружения.
|
||||||
|
* Поддерживаются и VITE_*, и NEXT_PUBLIC_* (см. envPrefix в vite.config.ts).
|
||||||
|
*/
|
||||||
|
function str(v: unknown): string {
|
||||||
|
return typeof v === "string" ? v.trim() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getApiBase(): string {
|
||||||
|
const v =
|
||||||
|
str(import.meta.env.VITE_API_URL) || str(import.meta.env.NEXT_PUBLIC_API);
|
||||||
|
if (!v) return "";
|
||||||
|
return v.replace(/\/?$/, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasApiConfigured(): boolean {
|
||||||
|
return getApiBase().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getS3BucketBase(): string {
|
||||||
|
return (
|
||||||
|
str(import.meta.env.VITE_S3_BUCKET) ||
|
||||||
|
str(import.meta.env.NEXT_PUBLIC_S3_BUCKET)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Путь из public/ (`/videos/...`) с учётом `base` в Vite */
|
||||||
|
export function resolvePublicPath(path: string): string {
|
||||||
|
if (
|
||||||
|
path.startsWith("http://") ||
|
||||||
|
path.startsWith("https://") ||
|
||||||
|
path.startsWith("data:")
|
||||||
|
) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
if (!path.startsWith("/")) return path;
|
||||||
|
const base = import.meta.env.BASE_URL;
|
||||||
|
if (!base || base === "/") return path;
|
||||||
|
return `${base.replace(/\/$/, "")}${path}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { getExampleNumber } from "libphonenumber-js";
|
||||||
|
import examples from "libphonenumber-js/mobile/examples";
|
||||||
|
|
||||||
|
export const PHONE_CODE_RU = "+7";
|
||||||
|
export const PHONE_COUNTRY_RU = "RU" as const;
|
||||||
|
|
||||||
|
/** Локальная часть примера номера (без кода страны) для маски */
|
||||||
|
export function getRuMobileExampleLocal(): string | undefined {
|
||||||
|
return getExampleNumber(PHONE_COUNTRY_RU, examples)
|
||||||
|
?.formatInternational()
|
||||||
|
.split(" ")
|
||||||
|
.slice(1)
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildRuPhoneMask(placeholderLocal: string | undefined): string {
|
||||||
|
return `${PHONE_CODE_RU} ${(placeholderLocal?.replace(/\d/g, "9") ?? "")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Только цифры: 7 + до 10 цифр номера (без +) */
|
||||||
|
export function ruPhoneDigits(stored: string): string {
|
||||||
|
let d = stored.replace(/\D/g, "");
|
||||||
|
if (d.startsWith("8")) d = "7" + d.slice(1);
|
||||||
|
if (d.length === 0) return "";
|
||||||
|
if (!d.startsWith("7")) d = "7" + d;
|
||||||
|
return d.slice(0, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Отображение +7 (XXX) XXX-XX-XX без react-input-mask (совместимо с React 19) */
|
||||||
|
export function formatRuPhoneDisplay(stored: string): string {
|
||||||
|
const d = ruPhoneDigits(stored);
|
||||||
|
if (!d) return "";
|
||||||
|
const n = d.slice(1);
|
||||||
|
if (n.length === 0) return "+7";
|
||||||
|
|
||||||
|
const a = n.slice(0, 3);
|
||||||
|
const b = n.slice(3, 6);
|
||||||
|
const c = n.slice(6, 8);
|
||||||
|
const e = n.slice(8, 10);
|
||||||
|
|
||||||
|
let s = "+7 (" + a;
|
||||||
|
if (n.length <= 3) return s;
|
||||||
|
s += ") " + b;
|
||||||
|
if (n.length <= 6) return s;
|
||||||
|
s += "-" + c;
|
||||||
|
if (n.length <= 8) return s;
|
||||||
|
s += "-" + e;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeRuPhoneFromInput(
|
||||||
|
cleanValue: string,
|
||||||
|
inputType: string | undefined
|
||||||
|
): string {
|
||||||
|
const shouldAddPhoneCode =
|
||||||
|
inputType !== "insertFromPaste" &&
|
||||||
|
inputType !== "insertFromDrop" &&
|
||||||
|
inputType !== "insertCompositionText" &&
|
||||||
|
!cleanValue.startsWith("+") &&
|
||||||
|
!cleanValue.startsWith("7") &&
|
||||||
|
!cleanValue.startsWith(PHONE_CODE_RU.replace("+", ""));
|
||||||
|
|
||||||
|
return (shouldAddPhoneCode ? PHONE_CODE_RU : "") + cleanValue;
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { getS3BucketBase } from "@/lib/env";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Как на основном сайте: в API может прийти уже полный URL
|
||||||
|
* (`https://storage.yandexcloud.net/bucket/projects/uuid.jpg`) или ключ
|
||||||
|
* (`projects/uuid.jpg`) — тогда к нему дописывается VITE_S3_BUCKET / NEXT_PUBLIC_S3_BUCKET.
|
||||||
|
*/
|
||||||
|
export function resolveProjectImageSrc(image: string): string {
|
||||||
|
let src = image
|
||||||
|
.trim()
|
||||||
|
.replaceAll(""", '"')
|
||||||
|
.replace(/^["']+|["']+$/g, "");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (src.includes("%")) src = decodeURIComponent(src);
|
||||||
|
} catch {
|
||||||
|
/* оставляем как есть */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.startsWith("//")) {
|
||||||
|
src = `https:${src}`;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
src.startsWith("http://") ||
|
||||||
|
src.startsWith("https://") ||
|
||||||
|
src.startsWith("data:")
|
||||||
|
) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.startsWith("/")) {
|
||||||
|
const base = import.meta.env.BASE_URL;
|
||||||
|
if (!base || base === "/") return src;
|
||||||
|
return `${base.replace(/\/$/, "")}${src}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s3BaseRaw = getS3BucketBase();
|
||||||
|
if (!s3BaseRaw) return src;
|
||||||
|
const base = s3BaseRaw.replace(/\/?$/, "/");
|
||||||
|
const path = src.replace(/^\//, "");
|
||||||
|
return `${base}${path}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { StrictMode } from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import { ModalContainer } from "@/components/Layout/ModalContainer";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root")!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<App />
|
||||||
|
<ModalContainer />
|
||||||
|
</QueryClientProvider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { api } from "@/lib/api";
|
||||||
|
import { streaming } from "@/consts/streaming";
|
||||||
|
import { hasApiConfigured } from "@/lib/env";
|
||||||
|
import { IProject } from "@/types/IProject";
|
||||||
|
import { queryKeys } from "@/queries/keys";
|
||||||
|
import { queryOptions, useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
const hasApi = hasApiConfigured();
|
||||||
|
|
||||||
|
export const queryProjectsOptions = queryOptions({
|
||||||
|
queryKey: queryKeys.projects,
|
||||||
|
queryFn: () => api.get("projects").json<IProject[]>(),
|
||||||
|
enabled: hasApi,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useGetProjectsQuery(tags?: string | string[]) {
|
||||||
|
return useQuery(
|
||||||
|
tags && tags.length > 0
|
||||||
|
? queryOptions({
|
||||||
|
queryKey: queryKeys.projectsWithTags(
|
||||||
|
Array.isArray(tags) ? tags : [tags]
|
||||||
|
),
|
||||||
|
queryFn: () =>
|
||||||
|
api
|
||||||
|
.get(
|
||||||
|
`projects?${
|
||||||
|
Array.isArray(tags)
|
||||||
|
? tags.map((tag) => `tags=${tag}`).join("&")
|
||||||
|
: "tags=" + tags
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
.json<IProject[]>(),
|
||||||
|
enabled: hasApi,
|
||||||
|
select:
|
||||||
|
tags === "Удаленная демонстрация"
|
||||||
|
? (data) => {
|
||||||
|
const filtered = data.filter((p) =>
|
||||||
|
streaming.some((s) => s.title === p.title)
|
||||||
|
);
|
||||||
|
if (filtered.length > 0) return filtered;
|
||||||
|
/* Нет совпадений со списком streaming — показываем первые 3 проекта из ответа API */
|
||||||
|
return data.slice(0, 3);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
: queryProjectsOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export const queryKeys = {
|
||||||
|
projects: ["projects"] as const,
|
||||||
|
projectsWithTags: (tags: string[]) =>
|
||||||
|
["projects", ...(Array.isArray(tags) ? tags : [tags])] as const,
|
||||||
|
};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface IModalState {
|
||||||
|
modal: ReactNode | null;
|
||||||
|
setModal: (modal: ReactNode | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useModalStore = create<IModalState>((set) => ({
|
||||||
|
modal: null,
|
||||||
|
setModal: (modal) => set({ modal }),
|
||||||
|
}));
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { persist } from "zustand/middleware";
|
||||||
|
|
||||||
|
export const useRefererStore = create<{
|
||||||
|
referer: string | null;
|
||||||
|
setReferer: (referer: string | null) => void;
|
||||||
|
}>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
referer: null,
|
||||||
|
setReferer: (referer) => set({ referer }),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "referer-stream-demo",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { IProject } from "./IProject";
|
||||||
|
|
||||||
|
export interface ICompany {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
color: string;
|
||||||
|
mapIcon?: string;
|
||||||
|
logo?: string;
|
||||||
|
projects: IProject[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ICompany } from "./ICompany";
|
||||||
|
import { Product } from "./Product";
|
||||||
|
|
||||||
|
export type Device = "Stream" | "Touch" | "Mobile" | "VR";
|
||||||
|
|
||||||
|
export interface IProject {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
englishTitle: string;
|
||||||
|
description: string;
|
||||||
|
company?: ICompany;
|
||||||
|
companyId?: string;
|
||||||
|
city: string;
|
||||||
|
englishCity: string;
|
||||||
|
image: string;
|
||||||
|
stage: number;
|
||||||
|
releaseDate: string;
|
||||||
|
tags: Product[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export type Product =
|
||||||
|
| "Интерактивная презентация"
|
||||||
|
| "Удаленная демонстрация"
|
||||||
|
| "Создание сайтов"
|
||||||
|
| "Архитектурная визуализация"
|
||||||
|
| "Веб-тур по 360 сферам";
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
children: ReactNode;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
color?: "primary" | "secondary";
|
||||||
|
width?: "fit" | "full";
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
type?: "submit" | "reset" | "button";
|
||||||
|
rounded?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Button({
|
||||||
|
children,
|
||||||
|
color = "primary",
|
||||||
|
icon,
|
||||||
|
width = "fit",
|
||||||
|
disabled = false,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
type,
|
||||||
|
rounded,
|
||||||
|
}: ButtonProps) {
|
||||||
|
const widthClass = width === "full" ? "w-full" : "w-fit";
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={type}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (type !== "submit") e.preventDefault();
|
||||||
|
onClick?.();
|
||||||
|
}}
|
||||||
|
className={`group cursor-pointer relative px-6 py-2${
|
||||||
|
rounded ? " rounded-" + rounded : ""
|
||||||
|
} min-w-fit ${
|
||||||
|
(color === "primary" ? "bg-gradient" : "") ||
|
||||||
|
(color === "secondary" ? " outline-1 outline-[#3D425C]" : "")
|
||||||
|
} ${icon ? "pr-4" : ""} flex gap-1 items-center overflow-hidden ${widthClass} ${className ?? ""} justify-between`}
|
||||||
|
>
|
||||||
|
<span className="group-hover:opacity-10 absolute top-0 left-0 w-full h-full transition-opacity bg-black opacity-0"></span>
|
||||||
|
<span className={"relative font-medium" + (icon ? "" : " m-auto")}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
<span className="relative">{icon}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
FieldValues,
|
||||||
|
Path,
|
||||||
|
useController,
|
||||||
|
useFormContext,
|
||||||
|
useWatch,
|
||||||
|
} from "react-hook-form";
|
||||||
|
|
||||||
|
export function CheckboxesGroup<IFieldValues extends FieldValues>({
|
||||||
|
options,
|
||||||
|
name,
|
||||||
|
}: {
|
||||||
|
options: string[];
|
||||||
|
name: Path<IFieldValues>;
|
||||||
|
}) {
|
||||||
|
const { control } = useFormContext<IFieldValues>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
field: { ref, onChange, ...inputProps },
|
||||||
|
} = useController({ control, name });
|
||||||
|
|
||||||
|
const values: string[] = useWatch({ control, name });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap lg:gap-[0.556vw] gap-2">
|
||||||
|
{options.map((option) => (
|
||||||
|
<label
|
||||||
|
htmlFor={name + "_" + option}
|
||||||
|
key={option}
|
||||||
|
className={`cursor-pointer transition-colors lg:rounded-[1.111vw] rounded-2xl font-medium text-nowrap select-none lg:px-[1.667vw] px-6 lg:py-[1.181vw] py-[17px] btnm ${
|
||||||
|
values.includes(option)
|
||||||
|
? "bg-white text-black"
|
||||||
|
: "bg-[#37393B99] hover:bg-[#37393B]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{option}
|
||||||
|
<input
|
||||||
|
id={name + "_" + option}
|
||||||
|
className="hidden"
|
||||||
|
type="checkbox"
|
||||||
|
{...inputProps}
|
||||||
|
checked={values.includes(option)}
|
||||||
|
ref={ref}
|
||||||
|
onChange={() => {
|
||||||
|
onChange(
|
||||||
|
values.includes(option)
|
||||||
|
? values.filter((x) => x !== option)
|
||||||
|
: [...values, option]
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import CheckIcon from "@/components/icons/CheckIcon";
|
||||||
|
|
||||||
|
function CustomCheckbox({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string, checked: boolean) => void;
|
||||||
|
}) {
|
||||||
|
const [checked, setChecked] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onChange(value, checked);
|
||||||
|
}, [checked, onChange, value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
onClick={() => setChecked(!checked)}
|
||||||
|
className="flex gap-x-[10px] items-center hover:cursor-pointer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
checked ? "bg-gradient" : "bg-[#37393B]"
|
||||||
|
} w-[20px] h-[20px] radius-[5px] rounded relative`}
|
||||||
|
>
|
||||||
|
{checked && (
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-4 absolute top-0">
|
||||||
|
<CheckIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="md:text-sm">{value}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomCheckbox;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { type Ref } from "react";
|
||||||
|
import {
|
||||||
|
formatRuPhoneDisplay,
|
||||||
|
normalizeRuPhoneFromInput,
|
||||||
|
} from "@/lib/phoneRu";
|
||||||
|
|
||||||
|
const inputClassName =
|
||||||
|
"placeholder:btnl placeholder:font-medium placeholder:select-none peer btnl w-full h-full bg-transparent rounded-none transition-all outline-none";
|
||||||
|
|
||||||
|
interface PhoneInputRuProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
onBlur?: () => void;
|
||||||
|
inputRef?: Ref<HTMLInputElement>;
|
||||||
|
id?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PhoneInputRu({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
inputRef,
|
||||||
|
id = "tel",
|
||||||
|
placeholder = "+7 (XXX) XXX - XX - XX",
|
||||||
|
}: PhoneInputRuProps) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="tel"
|
||||||
|
autoComplete="tel"
|
||||||
|
id={id}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={inputClassName}
|
||||||
|
value={formatRuPhoneDisplay(value)}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (!e.nativeEvent.type.startsWith("input")) return;
|
||||||
|
const cleanValue = e.target.value.replace(/\s/g, "");
|
||||||
|
const inputType = (e.nativeEvent as InputEvent).inputType;
|
||||||
|
onChange(normalizeRuPhoneFromInput(cleanValue, inputType));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import MuteIcon from "@/components/icons/MuteIcon";
|
||||||
|
import UnmutedIcon from "@/components/icons/UnmutedIcon";
|
||||||
|
|
||||||
|
export function VideoMutingBtn({
|
||||||
|
handleClick,
|
||||||
|
muted,
|
||||||
|
}: {
|
||||||
|
muted: boolean;
|
||||||
|
handleClick: () => void;
|
||||||
|
}) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [point, setPoint] = useState([0, 0]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = ref.current;
|
||||||
|
const move = (e: MouseEvent) => setPoint([e.clientX, e.clientY]);
|
||||||
|
el?.addEventListener("mousemove", move);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
el?.removeEventListener("mousemove", move);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute left-0 top-0 h-[calc(5/6*100%)] w-full z-[7]">
|
||||||
|
<div ref={ref} className="group relative w-full h-full">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-[#37393B99] p-[1.736vw] [backdrop-filter:blur(30.72px)] rounded-full group-hover:opacity-100 transition-opacity group-hover:cursor-none opacity-0 sticky outline-none"
|
||||||
|
style={{ left: point[0] - 32, top: point[1] - 32 }}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{muted ? (
|
||||||
|
<div className="text-white lg:size-[1.944vw] size-7">
|
||||||
|
<UnmutedIcon />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-white lg:size-[1.944vw] size-7">
|
||||||
|
<MuteIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import {
|
||||||
|
ComponentProps,
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { VideoMutingBtn } from "./VideoMutingBtn";
|
||||||
|
import { VideoProgressBar } from "./VideoProgressBar";
|
||||||
|
|
||||||
|
export const VideoPlayer = forwardRef<
|
||||||
|
HTMLVideoElement,
|
||||||
|
{
|
||||||
|
src: string;
|
||||||
|
showMutingBtn: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
} & ComponentProps<"video">
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{ src, showMutingBtn, children, loop = true, autoPlay = true, className },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const progressbarRef = useRef<HTMLDivElement>(null);
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => videoRef.current!);
|
||||||
|
|
||||||
|
const [muted, setMuted] = useState(autoPlay);
|
||||||
|
const [playing, setPlaying] = useState(autoPlay);
|
||||||
|
const [progress, setProgress] = useState(0);
|
||||||
|
|
||||||
|
function handleProgressbarClick(e: React.MouseEvent) {
|
||||||
|
const video = videoRef.current;
|
||||||
|
const bar = progressbarRef.current;
|
||||||
|
if (!video || !bar) return;
|
||||||
|
video.currentTime =
|
||||||
|
(video.duration * (e.clientX - bar.getBoundingClientRect().x)) /
|
||||||
|
bar.clientWidth;
|
||||||
|
setProgress(
|
||||||
|
((video.currentTime ?? 0) / (video.duration ?? 1)) * 100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePlaybackClick() {
|
||||||
|
if (!videoRef.current) return;
|
||||||
|
setPlaying(videoRef.current.paused);
|
||||||
|
videoRef.current[videoRef.current.paused ? "play" : "pause"]();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video) return;
|
||||||
|
const timeUpdateHandler = () =>
|
||||||
|
setProgress(((video.currentTime ?? 0) / (video.duration ?? 1)) * 100);
|
||||||
|
|
||||||
|
video.addEventListener("timeupdate", timeUpdateHandler);
|
||||||
|
return () => video.removeEventListener("timeupdate", timeUpdateHandler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-full">
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
src={src}
|
||||||
|
autoPlay={autoPlay}
|
||||||
|
muted={muted}
|
||||||
|
loop={loop}
|
||||||
|
playsInline
|
||||||
|
className={`lg:rounded-[1.111vw] rounded-2xl w-full h-full object-cover${
|
||||||
|
className ? " " + className : ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{showMutingBtn && (
|
||||||
|
<VideoMutingBtn
|
||||||
|
handleClick={() => setMuted(!videoRef.current!.muted)}
|
||||||
|
muted={muted}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="absolute inset-0 rounded-2xl [background:linear-gradient(to_top,rgba(20,22,31,0.6),rgba(20,22,31,0))]" />
|
||||||
|
<AnimatePresence>
|
||||||
|
{muted && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
<VideoProgressBar
|
||||||
|
muted={muted}
|
||||||
|
progress={progress}
|
||||||
|
progressbarRef={progressbarRef}
|
||||||
|
playing={playing}
|
||||||
|
handlePlaybackClick={handlePlaybackClick}
|
||||||
|
handleProgressbarClick={handleProgressbarClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
VideoPlayer.displayName = "VideoPlayer";
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { MouseEventHandler, RefObject, useState } from "react";
|
||||||
|
import PauseIcon from "@/components/icons/PauseIcon";
|
||||||
|
import PlayIcon from "@/components/icons/PlayIcon";
|
||||||
|
|
||||||
|
export function VideoProgressBar({
|
||||||
|
muted,
|
||||||
|
progressbarRef,
|
||||||
|
playing,
|
||||||
|
handlePlaybackClick,
|
||||||
|
handleProgressbarClick,
|
||||||
|
progress,
|
||||||
|
}: {
|
||||||
|
muted: boolean;
|
||||||
|
progress: number;
|
||||||
|
progressbarRef: RefObject<HTMLDivElement | null>;
|
||||||
|
playing: boolean;
|
||||||
|
handlePlaybackClick: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
handleProgressbarClick: MouseEventHandler<HTMLDivElement>;
|
||||||
|
}) {
|
||||||
|
const [isMouseDown, setIsMouseDown] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bottom-2 left-2 right-2 absolute z-10 select-none flex items-stretch gap-1 ${
|
||||||
|
muted ? "hidden" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="p-[18px] bg-[#37393B99] rounded-2xl cursor-pointer"
|
||||||
|
onClick={handlePlaybackClick}
|
||||||
|
>
|
||||||
|
{playing ? (
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-5">
|
||||||
|
<PauseIcon />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-white lg:size-[1.389vw] size-5">
|
||||||
|
<PlayIcon />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className="flex-1 rounded-2xl bg-[#37393B99] px-6 cursor-pointer flex items-center select-none"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
setIsMouseDown(true);
|
||||||
|
handleProgressbarClick(e);
|
||||||
|
}}
|
||||||
|
onMouseMove={(e) => {
|
||||||
|
if (isMouseDown) handleProgressbarClick(e);
|
||||||
|
}}
|
||||||
|
onMouseUp={() => setIsMouseDown(false)}
|
||||||
|
onMouseLeave={() => setIsMouseDown(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={progressbarRef}
|
||||||
|
className="h-1 bg-[#7A7A7A] relative rounded-3xl cursor-pointer w-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="left-0 h-1 bg-white rounded-3xl transition-all"
|
||||||
|
style={{
|
||||||
|
width: progress + "%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_API_URL?: string;
|
||||||
|
readonly VITE_S3_BUCKET?: string;
|
||||||
|
readonly NEXT_PUBLIC_API?: string;
|
||||||
|
readonly NEXT_PUBLIC_S3_BUCKET?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
|
theme: {
|
||||||
|
screens: {
|
||||||
|
xs: "360px",
|
||||||
|
sm: "640px",
|
||||||
|
md: "768px",
|
||||||
|
lg: "1440px",
|
||||||
|
"2xl": "1536px",
|
||||||
|
},
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
/** Подхватываем .env из корня репозитория (рядом с Next), если в подпапке своего нет */
|
||||||
|
envDir: path.resolve(__dirname, ".."),
|
||||||
|
/** Как в Next: можно копировать .env с NEXT_PUBLIC_API / NEXT_PUBLIC_S3_BUCKET */
|
||||||
|
envPrefix: ["VITE_", "NEXT_PUBLIC_"],
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user