added blur on topBar
This commit is contained in:
753
package-lock.json
generated
753
package-lock.json
generated
@@ -41,7 +41,10 @@
|
|||||||
"zustand": "^5.0.12"
|
"zustand": "^5.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.6.0"
|
"@types/node": "^25.6.0",
|
||||||
|
"@types/react": "^19.2.18",
|
||||||
|
"@types/react-dom": "^19.2.7",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
@@ -3040,6 +3043,16 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@oxc-project/types": {
|
||||||
|
"version": "0.149.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.149.0.tgz",
|
||||||
|
"integrity": "sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/oxc-project"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pbe/react-yandex-maps": {
|
"node_modules/@pbe/react-yandex-maps": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@pbe/react-yandex-maps/-/react-yandex-maps-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@pbe/react-yandex-maps/-/react-yandex-maps-1.2.5.tgz",
|
||||||
@@ -3103,6 +3116,286 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rolldown/binding-android-arm-eabi": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/pluginutils": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
@@ -4146,6 +4439,26 @@
|
|||||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react": {
|
||||||
|
"version": "19.2.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz",
|
||||||
|
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": "^3.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/react-dom": {
|
||||||
|
"version": "19.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz",
|
||||||
|
"integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^19.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||||
@@ -6785,6 +7098,13 @@
|
|||||||
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
|
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/damerau-levenshtein": {
|
"node_modules/damerau-levenshtein": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||||
@@ -12265,9 +12585,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -12949,9 +13269,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.12",
|
"version": "8.5.28",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
|
||||||
"integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==",
|
"integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -12968,7 +13288,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.18",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -15250,6 +15570,40 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rolldown": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@oxc-project/types": "=0.149.0",
|
||||||
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rolldown": "bin/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rolldown/binding-android-arm-eabi": "1.2.8",
|
||||||
|
"@rolldown/binding-android-arm64": "1.2.8",
|
||||||
|
"@rolldown/binding-darwin-arm64": "1.2.8",
|
||||||
|
"@rolldown/binding-darwin-x64": "1.2.8",
|
||||||
|
"@rolldown/binding-freebsd-x64": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-arm-gnueabihf": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-arm64-gnu": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-arm64-musl": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-ppc64-gnu": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-s390x-gnu": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-x64-gnu": "1.2.8",
|
||||||
|
"@rolldown/binding-linux-x64-musl": "1.2.8",
|
||||||
|
"@rolldown/binding-openharmony-arm64": "1.2.8",
|
||||||
|
"@rolldown/binding-win32-arm64-msvc": "1.2.8",
|
||||||
|
"@rolldown/binding-win32-x64-msvc": "1.2.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "2.80.0",
|
"version": "2.80.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
|
||||||
@@ -16786,9 +17140,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.16",
|
"version": "0.2.17",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -17094,6 +17448,19 @@
|
|||||||
"is-typedarray": "^1.0.0"
|
"is-typedarray": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.9.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unbox-primitive": {
|
"node_modules/unbox-primitive": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||||
@@ -17333,6 +17700,370 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "8.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz",
|
||||||
|
"integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lightningcss": "^1.33.0",
|
||||||
|
"picomatch": "^4.0.5",
|
||||||
|
"postcss": "^8.5.26",
|
||||||
|
"rolldown": "~1.2.4",
|
||||||
|
"tinyglobby": "^0.2.17"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
|
"@vitejs/devtools": "^0.4.0 || ^0.5.0",
|
||||||
|
"esbuild": "^0.27.0 || ^0.28.0",
|
||||||
|
"jiti": ">=1.21.0",
|
||||||
|
"less": "^4.0.0",
|
||||||
|
"sass": "^1.70.0",
|
||||||
|
"sass-embedded": "^1.70.0",
|
||||||
|
"stylus": ">=0.54.8",
|
||||||
|
"sugarss": "^5.0.0",
|
||||||
|
"terser": "^5.16.0",
|
||||||
|
"tsx": "^4.8.1",
|
||||||
|
"yaml": "^2.4.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitejs/devtools": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"jiti": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"tsx": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"lightningcss-android-arm64": "1.33.0",
|
||||||
|
"lightningcss-darwin-arm64": "1.33.0",
|
||||||
|
"lightningcss-darwin-x64": "1.33.0",
|
||||||
|
"lightningcss-freebsd-x64": "1.33.0",
|
||||||
|
"lightningcss-linux-arm-gnueabihf": "1.33.0",
|
||||||
|
"lightningcss-linux-arm64-gnu": "1.33.0",
|
||||||
|
"lightningcss-linux-arm64-musl": "1.33.0",
|
||||||
|
"lightningcss-linux-x64-gnu": "1.33.0",
|
||||||
|
"lightningcss-linux-x64-musl": "1.33.0",
|
||||||
|
"lightningcss-win32-arm64-msvc": "1.33.0",
|
||||||
|
"lightningcss-win32-x64-msvc": "1.33.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-android-arm64": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-darwin-arm64": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-darwin-x64": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-freebsd-x64": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-linux-arm64-musl": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-linux-x64-gnu": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"glibc"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-linux-x64-musl": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"libc": [
|
||||||
|
"musl"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/lightningcss-win32-x64-msvc": {
|
||||||
|
"version": "1.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
|
||||||
|
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/picomatch": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/void-elements": {
|
"node_modules/void-elements": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||||
|
|||||||
@@ -60,6 +60,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^25.6.0"
|
"@types/node": "^25.6.0",
|
||||||
|
"@types/react": "^19.2.18",
|
||||||
|
"@types/react-dom": "^19.2.7",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
import { useState, useRef, useEffect } from "react";
|
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import MenuIcon from "../icons/MenuIcon";
|
|
||||||
import NahodkaLogo from "../NahodkaLogo";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TopBar ленты (Telegram-style):
|
|
||||||
* слева — круглая стеклянная кнопка-логотип (открывает фильтр типа),
|
|
||||||
* справа — squircle-кнопка-гамбургер (открывает SideBar).
|
|
||||||
*/
|
|
||||||
export default function FeedTopBar({ onMenuClick, typeFilter, onTypeFilterChange }) {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const menuRef = useRef(null);
|
|
||||||
const btnRef = useRef(null);
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ key: "loss", label: t("feed.filterLoss") },
|
|
||||||
{ key: null, label: t("feed.filterAll") },
|
|
||||||
{ key: "found", label: t("feed.filterFound") },
|
|
||||||
];
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) return;
|
|
||||||
const handler = (e) => {
|
|
||||||
if (
|
|
||||||
menuRef.current && !menuRef.current.contains(e.target) &&
|
|
||||||
btnRef.current && !btnRef.current.contains(e.target)
|
|
||||||
) {
|
|
||||||
setOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.addEventListener("mousedown", handler);
|
|
||||||
return () => document.removeEventListener("mousedown", handler);
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const handleSelect = (key) => {
|
|
||||||
onTypeFilterChange(key);
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="f-topbar-dropdown-wrap">
|
|
||||||
<div className="f-topbar">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="f-menu-btn"
|
|
||||||
onClick={onMenuClick}
|
|
||||||
aria-label={t("feed.openMenu")}
|
|
||||||
>
|
|
||||||
<MenuIcon />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
ref={btnRef}
|
|
||||||
type="button"
|
|
||||||
className="f-logo-btn"
|
|
||||||
onClick={() => setOpen((v) => !v)}
|
|
||||||
aria-label={t("feed.openFilter")}
|
|
||||||
aria-haspopup="menu"
|
|
||||||
aria-expanded={open}
|
|
||||||
>
|
|
||||||
<NahodkaLogo className="f-topbar-logo" />
|
|
||||||
<svg
|
|
||||||
className={`f-topbar-chevron${open ? " open" : ""}`}
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<polyline points="6 9 12 15 18 9" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span className="f-topbar-spacer" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AnimatePresence>
|
|
||||||
{open && (
|
|
||||||
<motion.div
|
|
||||||
className="f-topbar-dropdown-overlay"
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
exit={{ opacity: 0 }}
|
|
||||||
transition={{ duration: 0.15 }}
|
|
||||||
onClick={() => setOpen(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
|
|
||||||
<AnimatePresence>
|
|
||||||
{open && (
|
|
||||||
<motion.div
|
|
||||||
ref={menuRef}
|
|
||||||
className="f-topbar-dropdown-menu"
|
|
||||||
initial={{ opacity: 0, scale: 0.95, y: -8 }}
|
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, scale: 0.95, y: -8 }}
|
|
||||||
transition={{ duration: 0.18, ease: "easeOut" }}
|
|
||||||
>
|
|
||||||
{options.map((opt) => (
|
|
||||||
<button
|
|
||||||
key={opt.key ?? "all"}
|
|
||||||
type="button"
|
|
||||||
className={`f-topbar-dropdown-item${typeFilter === opt.key ? " active" : ""}`}
|
|
||||||
onClick={() => handleSelect(opt.key)}
|
|
||||||
>
|
|
||||||
{opt.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,484 +0,0 @@
|
|||||||
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
|
||||||
import "../styles/index.css";
|
|
||||||
import { getImageUrl } from "../config/authConstants";
|
|
||||||
import Sidebar from "../components/Sidebar";
|
|
||||||
import BottomNav from "../components/nav/BottomNav";
|
|
||||||
import RightSidebar from "../components/RightSidebar";
|
|
||||||
import CreateUpdatePost from "./CreateUpdatePost";
|
|
||||||
import ImageViewer from "../components/ImageViewer";
|
|
||||||
import CityFilter from "../components/CityFilter";
|
|
||||||
import { ProfileModal } from "../components/profile";
|
|
||||||
import AuthModal from "../components/AuthModal";
|
|
||||||
import RulesModal from "./RulesModal";
|
|
||||||
import FeedTopBar from "../components/feed/FeedTopBar";
|
|
||||||
import FeedSearchBar from "../components/feed/FeedSearchBar";
|
|
||||||
|
|
||||||
import FeedContent from "../components/feed/FeedContent";
|
|
||||||
import MapView from "../components/MapView";
|
|
||||||
import PostDetailModal from "../components/PostDetailModal";
|
|
||||||
import { useAuthContext } from "../context/AuthContext";
|
|
||||||
import { useCities } from "../hooks/useCities";
|
|
||||||
import { useFeedPosts } from "../hooks/useFeedPosts";
|
|
||||||
import { useMyPosts } from "../hooks/useMyPosts";
|
|
||||||
import { useCityFeed } from "../hooks/useCityFeed";
|
|
||||||
import { useInfiniteScroll } from "../hooks/useInfiniteScroll";
|
|
||||||
import { useModals } from "../hooks/useModals";
|
|
||||||
import { useFeedActions } from "../hooks/useFeedActions";
|
|
||||||
import { useFavorites } from "../hooks/useFavorites";
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useProfile } from "../hooks/useProfile";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MainFeed — корневой компонент приложения.
|
|
||||||
* Композиция хуков состояния + презентационных компонентов ленты.
|
|
||||||
*/
|
|
||||||
export default function MainFeed() {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const location = useLocation();
|
|
||||||
const { user, token, isAuthenticated, updateUser } = useAuthContext();
|
|
||||||
const { fetchProfile } = useProfile();
|
|
||||||
const { cities } = useCities();
|
|
||||||
const { loadFavorites, loadFavoritePosts, error: favoritesError } = useFavorites();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const modals = useModals();
|
|
||||||
|
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
||||||
const [activeNav, setActiveNav] = useState("home");
|
|
||||||
const [typeFilter, setTypeFilter] = useState(null);
|
|
||||||
const [selectedCities, setSelectedCities] = useState([]);
|
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
|
||||||
const [liked, setLiked] = useState(new Set());
|
|
||||||
const [likeCountAdj, setLikeCountAdj] = useState({});
|
|
||||||
const [favoritePosts, setFavoritePosts] = useState([]);
|
|
||||||
const profileFetchedFor = useRef(null);
|
|
||||||
const [favoritesLoading, setFavoritesLoading] = useState(false);
|
|
||||||
|
|
||||||
const feedRef = useRef(null);
|
|
||||||
// Состояние ленты перед переходом на карту — чтобы вернуть юзера на то же место
|
|
||||||
const feedStateRef = useRef({ scrollTop: 0, url: "/feed", activeNav: "home" });
|
|
||||||
const restoreScrollRef = useRef(false);
|
|
||||||
|
|
||||||
const isSearchView = new URLSearchParams(location.search).get("view") === "search";
|
|
||||||
const isMapView = new URLSearchParams(location.search).get("view") === "map";
|
|
||||||
// параметры фокуса карты на конкретном объявлении (?view=map&post=ID&lat=..&lng=..)
|
|
||||||
const mapFocusParams = new URLSearchParams(location.search);
|
|
||||||
const focusPostId = isMapView ? mapFocusParams.get("post") : null;
|
|
||||||
const focusLat = parseFloat(mapFocusParams.get("lat"));
|
|
||||||
const focusLng = parseFloat(mapFocusParams.get("lng"));
|
|
||||||
const hasActiveFilters = searchQuery.trim().length > 0 || selectedCities.length > 0;
|
|
||||||
const hasSearchParams = searchQuery.trim().length > 0 || selectedCities.length > 0;
|
|
||||||
const searchPrompt = isSearchView && !hasSearchParams;
|
|
||||||
|
|
||||||
const feed = useFeedPosts();
|
|
||||||
const myPosts = useMyPosts(user?.id);
|
|
||||||
const cityFeed = useCityFeed(selectedCities);
|
|
||||||
|
|
||||||
const profileData = useMemo(() => {
|
|
||||||
if (!user) return {};
|
|
||||||
const nameParts = (user.name || "").trim().split(/\s+/);
|
|
||||||
const firstName = user.firstName || nameParts[0] || "";
|
|
||||||
const lastName = user.lastName || nameParts.slice(1).join(" ") || "";
|
|
||||||
return {
|
|
||||||
login: user.login || "",
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
email: user.email || "",
|
|
||||||
phone: user.phone || "",
|
|
||||||
avatar: getImageUrl(user.avatar),
|
|
||||||
id: user.id,
|
|
||||||
};
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
const refreshCurrentList = useCallback(() => {
|
|
||||||
if (selectedCities.length > 0) {
|
|
||||||
cityFeed.searchCities(selectedCities);
|
|
||||||
} else {
|
|
||||||
feed.loadFeed();
|
|
||||||
}
|
|
||||||
}, [selectedCities, cityFeed, feed]);
|
|
||||||
|
|
||||||
const actions = useFeedActions({
|
|
||||||
refresh: refreshCurrentList,
|
|
||||||
loadMyPosts: myPosts.loadMyPosts,
|
|
||||||
onAuthRequired: () => modals.open("auth"),
|
|
||||||
isAuthenticated,
|
|
||||||
t,
|
|
||||||
setLiked,
|
|
||||||
setLikeCountAdj,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Загружаем лайки пользователя при авторизации
|
|
||||||
useEffect(() => {
|
|
||||||
if (isAuthenticated && token) {
|
|
||||||
loadFavorites().then(favoriteIds => {
|
|
||||||
const s = new Set(favoriteIds);
|
|
||||||
actions.syncLiked(s);
|
|
||||||
setLiked(s);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
actions.syncLiked(new Set());
|
|
||||||
setLiked(new Set());
|
|
||||||
}
|
|
||||||
setLikeCountAdj({});
|
|
||||||
}, [isAuthenticated, token, loadFavorites]);
|
|
||||||
|
|
||||||
// Открываем модалку аутентификации если пришли с лендинга
|
|
||||||
useEffect(() => {
|
|
||||||
if (location.state?.openAuth) {
|
|
||||||
modals.open("auth");
|
|
||||||
window.history.replaceState({}, document.title);
|
|
||||||
}
|
|
||||||
}, [location.state]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
// При загрузке приложения подтягиваем свежие данные профиля (аватар и т.д.)
|
|
||||||
// с бэка, чтобы нижняя навигация и профиль отражали изменения с других
|
|
||||||
// устройств, а не устаревший кэш контекста. Дергаем один раз на пользователя.
|
|
||||||
useEffect(() => {
|
|
||||||
if (isAuthenticated && user?.id && profileFetchedFor.current !== user.id) {
|
|
||||||
profileFetchedFor.current = user.id;
|
|
||||||
fetchProfile(user.id).then(result => {
|
|
||||||
if (result.success) updateUser(result.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isAuthenticated, user?.id]);
|
|
||||||
|
|
||||||
// При открытии профиля подтягиваем свежие данные (аватар/профиль) с бэка,
|
|
||||||
// чтобы изменения с других устройств отражались, а не брались из кэша контекста
|
|
||||||
useEffect(() => {
|
|
||||||
if (modals.isOpen("profile") && isAuthenticated && user?.id) {
|
|
||||||
fetchProfile(user.id).then(result => {
|
|
||||||
if (result.success) updateUser(result.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [modals.isOpen("profile"), isAuthenticated, user?.id]);
|
|
||||||
|
|
||||||
// Загрузка основной ленты (не в режиме поиска)
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isSearchView && selectedCities.length === 0 && !searchQuery.trim()) {
|
|
||||||
feed.loadFeed(0, "", false);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isSearchView]);
|
|
||||||
|
|
||||||
// Дебаунс поиска: только когда в search view и есть параметры
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isSearchView) return;
|
|
||||||
if (!hasSearchParams) return;
|
|
||||||
const timer = setTimeout(() => {
|
|
||||||
feed.loadFeed(0, searchQuery, false);
|
|
||||||
}, 400);
|
|
||||||
return () => clearTimeout(timer);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [searchQuery, selectedCities, isSearchView]);
|
|
||||||
|
|
||||||
// Когда переключаемся на «Мои объявления» — подгружаем с бэкенда
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeNav === "myads" && isAuthenticated && user?.id) {
|
|
||||||
myPosts.loadMyPosts();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [activeNav, isAuthenticated, user?.id]);
|
|
||||||
|
|
||||||
// Загрузка избранных постов при переходе во вкладку «Избранное»
|
|
||||||
useEffect(() => {
|
|
||||||
if (activeNav !== "favorites") return;
|
|
||||||
if (!isAuthenticated || !token) return;
|
|
||||||
let cancelled = false;
|
|
||||||
setFavoritesLoading(true);
|
|
||||||
loadFavoritePosts().then(posts => {
|
|
||||||
if (cancelled) return;
|
|
||||||
setFavoritePosts(posts);
|
|
||||||
setFavoritesLoading(false);
|
|
||||||
});
|
|
||||||
return () => { cancelled = true; };
|
|
||||||
}, [activeNav, isAuthenticated, token, loadFavoritePosts]);
|
|
||||||
|
|
||||||
const sentinelRef = useInfiniteScroll(
|
|
||||||
() => feed.loadFeed(feed.page + 1, searchQuery, true),
|
|
||||||
activeNav === "home" && selectedCities.length === 0 && feed.hasMore && !feed.loading && !feed.loadingMore
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleImageClick = useCallback((images, index, ad) => {
|
|
||||||
if (images?.length) modals.open("viewer", { images, index, ad });
|
|
||||||
}, [modals]);
|
|
||||||
|
|
||||||
const handleCreatePost = useCallback(() => {
|
|
||||||
if (!isAuthenticated) modals.open("auth");
|
|
||||||
else modals.open("create");
|
|
||||||
}, [isAuthenticated, modals]);
|
|
||||||
|
|
||||||
const handleEdit = useCallback((ad) => {
|
|
||||||
if (!isAuthenticated) modals.open("auth");
|
|
||||||
else modals.open("edit", ad);
|
|
||||||
}, [isAuthenticated, modals]);
|
|
||||||
|
|
||||||
const handleHomeClick = useCallback(() => {
|
|
||||||
setActiveNav("home");
|
|
||||||
setSearchQuery("");
|
|
||||||
setSelectedCities([]);
|
|
||||||
if (isSearchView || isMapView) navigate("/feed");
|
|
||||||
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
|
||||||
}, [isSearchView, isMapView, navigate]);
|
|
||||||
|
|
||||||
const handleSearchClick = useCallback(() => {
|
|
||||||
setActiveNav("home");
|
|
||||||
setSearchQuery("");
|
|
||||||
setSelectedCities([]);
|
|
||||||
if (!isSearchView) navigate("/feed?view=search");
|
|
||||||
else if (isMapView) navigate("/feed?view=search");
|
|
||||||
feedRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
|
||||||
}, [isSearchView, isMapView, navigate]);
|
|
||||||
|
|
||||||
// Карта с объявлениями
|
|
||||||
const saveFeedState = useCallback(() => {
|
|
||||||
feedStateRef.current = {
|
|
||||||
scrollTop: feedRef.current?.scrollTop ?? 0,
|
|
||||||
url: `${location.pathname}${location.search}`,
|
|
||||||
activeNav,
|
|
||||||
};
|
|
||||||
restoreScrollRef.current = true;
|
|
||||||
}, [location.pathname, location.search, activeNav]);
|
|
||||||
|
|
||||||
const handleMapClick = useCallback(() => {
|
|
||||||
saveFeedState();
|
|
||||||
setActiveNav("map");
|
|
||||||
if (!isMapView) navigate("/feed?view=map");
|
|
||||||
}, [isMapView, navigate, saveFeedState]);
|
|
||||||
|
|
||||||
const handleMapPostClick = useCallback((postId) => {
|
|
||||||
modals.open("postDetail", { postId });
|
|
||||||
}, [modals]);
|
|
||||||
|
|
||||||
// Возврат с карты на ленту — на то же место, где юзер был до открытия карты
|
|
||||||
const handleMapBack = useCallback(() => {
|
|
||||||
const { url, activeNav: prevNav } = feedStateRef.current;
|
|
||||||
setActiveNav(prevNav || "home");
|
|
||||||
navigate(url || "/feed", { replace: true });
|
|
||||||
}, [navigate]);
|
|
||||||
|
|
||||||
// Восстанавливаем прокрутку ленты после возврата с карты
|
|
||||||
useEffect(() => {
|
|
||||||
if (isMapView || !restoreScrollRef.current) return;
|
|
||||||
const el = feedRef.current;
|
|
||||||
if (el) el.scrollTop = feedStateRef.current.scrollTop;
|
|
||||||
restoreScrollRef.current = false;
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isMapView, feed.ads, cityFeed.cityAds, feed.loading, cityFeed.cityLoading]);
|
|
||||||
|
|
||||||
// Клик по локации на карточке — карта с фокусом на этом объявлении
|
|
||||||
const handleLocationClick = useCallback((ad) => {
|
|
||||||
saveFeedState();
|
|
||||||
const params = new URLSearchParams({ view: "map", post: ad.id });
|
|
||||||
if (ad.latitude != null && ad.longitude != null) {
|
|
||||||
params.set("lat", ad.latitude);
|
|
||||||
params.set("lng", ad.longitude);
|
|
||||||
}
|
|
||||||
setActiveNav("map");
|
|
||||||
navigate(`/feed?${params.toString()}`);
|
|
||||||
}, [navigate, saveFeedState]);
|
|
||||||
|
|
||||||
const handleProfileClick = useCallback(() => {
|
|
||||||
if (!isAuthenticated) modals.open("auth");
|
|
||||||
else modals.open("profile");
|
|
||||||
}, [isAuthenticated, modals]);
|
|
||||||
|
|
||||||
const filteredAds = useMemo(() => {
|
|
||||||
let result;
|
|
||||||
if (activeNav === "myads") {
|
|
||||||
result = myPosts.myAds;
|
|
||||||
} else if (activeNav === "favorites") {
|
|
||||||
result = favoritePosts;
|
|
||||||
} else if (selectedCities.length > 0) {
|
|
||||||
result = cityFeed.cityAds;
|
|
||||||
} else {
|
|
||||||
result = feed.ads;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeNav === "items") {
|
|
||||||
result = result.filter(ad => ["Вещь", "Другое"].includes(ad.category));
|
|
||||||
} else if (activeNav === "pets") {
|
|
||||||
result = result.filter(ad => ad.category === "Питомец");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeFilter) {
|
|
||||||
result = result.filter(ad => ad.type === typeFilter);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, [activeNav, myPosts.myAds, favoritePosts, cityFeed.cityAds, feed.ads, selectedCities, typeFilter]);
|
|
||||||
|
|
||||||
const listLoading = activeNav === "myads"
|
|
||||||
? myPosts.loading
|
|
||||||
: activeNav === "favorites"
|
|
||||||
? favoritesLoading
|
|
||||||
: feed.loading;
|
|
||||||
const listError = activeNav === "myads"
|
|
||||||
? myPosts.error
|
|
||||||
: activeNav === "favorites"
|
|
||||||
? favoritesError
|
|
||||||
: feed.error;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`f-app${sidebarOpen ? " f-sb-open" : ""}${isSearchView ? " f-search-view" : ""}${isMapView ? " f-map-view" : ""}`}>
|
|
||||||
<Sidebar
|
|
||||||
open={sidebarOpen}
|
|
||||||
activeNav={activeNav}
|
|
||||||
onClose={() => setSidebarOpen(false)}
|
|
||||||
onNavClick={(key) => {
|
|
||||||
setActiveNav(key);
|
|
||||||
setSearchQuery("");
|
|
||||||
setSelectedCities([]);
|
|
||||||
if (isMapView || isSearchView) navigate("/feed");
|
|
||||||
}}
|
|
||||||
isAuthenticated={isAuthenticated}
|
|
||||||
onLoginClick={() => modals.open("auth")}
|
|
||||||
onRulesClick={() => modals.open("rules")}
|
|
||||||
onReviewsClick={() => navigate("/reviews")}
|
|
||||||
onCreatePost={handleCreatePost}
|
|
||||||
onProfileClick={() => modals.open("profile")}
|
|
||||||
profileData={profileData}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="f-main f-main-feed">
|
|
||||||
{isSearchView && (
|
|
||||||
<FeedTopBar
|
|
||||||
onMenuClick={() => setSidebarOpen(v => !v)}
|
|
||||||
typeFilter={typeFilter}
|
|
||||||
onTypeFilterChange={setTypeFilter}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="f-search-panel">
|
|
||||||
<FeedSearchBar value={searchQuery} onChange={setSearchQuery} t={t} />
|
|
||||||
|
|
||||||
{activeNav === "home" && (
|
|
||||||
<div className="f-city-filter-wrap">
|
|
||||||
<CityFilter cities={cities} value={selectedCities} onChange={setSelectedCities} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="f-wrap">
|
|
||||||
{isMapView ? (
|
|
||||||
<MapView
|
|
||||||
focusPostId={focusPostId}
|
|
||||||
focusLat={focusLat}
|
|
||||||
focusLng={focusLng}
|
|
||||||
onPostClick={handleMapPostClick}
|
|
||||||
onBack={handleMapBack}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="f-feed" ref={feedRef}>
|
|
||||||
{!isSearchView && (
|
|
||||||
<FeedTopBar
|
|
||||||
onMenuClick={() => setSidebarOpen(v => !v)}
|
|
||||||
typeFilter={typeFilter}
|
|
||||||
onTypeFilterChange={setTypeFilter}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<FeedContent
|
|
||||||
loading={listLoading}
|
|
||||||
cityLoading={cityFeed.cityLoading}
|
|
||||||
citySearchActive={selectedCities.length > 0}
|
|
||||||
error={listError}
|
|
||||||
filteredAds={filteredAds}
|
|
||||||
activeNav={activeNav}
|
|
||||||
searchPrompt={searchPrompt}
|
|
||||||
liked={liked}
|
|
||||||
likeCountAdj={likeCountAdj}
|
|
||||||
onLike={actions.toggleLike}
|
|
||||||
onEdit={handleEdit}
|
|
||||||
onDelete={actions.handleDelete}
|
|
||||||
onMarkFound={actions.handleMarkFound}
|
|
||||||
onImageClick={handleImageClick}
|
|
||||||
onLocationClick={handleLocationClick}
|
|
||||||
onAuthRequired={() => modals.open("auth")}
|
|
||||||
t={t}
|
|
||||||
/>
|
|
||||||
{activeNav === "home" && selectedCities.length === 0 && !feed.loading && (
|
|
||||||
<div ref={sentinelRef} style={{ height: "1px" }} />
|
|
||||||
)}
|
|
||||||
{feed.loadingMore && (
|
|
||||||
<div className="f-center" style={{ padding: "1rem" }}>
|
|
||||||
<div className="f-spinner" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<RightSidebar
|
|
||||||
ads={filteredAds}
|
|
||||||
profileData={profileData}
|
|
||||||
isAuthenticated={isAuthenticated}
|
|
||||||
onProfileClick={() => modals.open("profile")}
|
|
||||||
onLoginClick={() => modals.open("auth")}
|
|
||||||
onNavClick={(key) => {
|
|
||||||
setActiveNav(key);
|
|
||||||
setSearchQuery("");
|
|
||||||
setSelectedCities([]);
|
|
||||||
if (isMapView || isSearchView) navigate("/feed");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{modals.isOpen("create") && isAuthenticated && (
|
|
||||||
<CreateUpdatePost onClose={modals.close} onSuccess={actions.handleNewAd} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{modals.isOpen("edit") && isAuthenticated && (
|
|
||||||
<CreateUpdatePost
|
|
||||||
initialData={modals.modal.payload}
|
|
||||||
onClose={modals.close}
|
|
||||||
onSuccess={actions.handleEditSave}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{modals.isOpen("profile") && isAuthenticated && (
|
|
||||||
<ProfileModal onClose={modals.close} initialData={profileData} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{modals.isOpen("auth") && <AuthModal onClose={modals.close} />}
|
|
||||||
|
|
||||||
{modals.isOpen("rules") && <RulesModal onClose={modals.close} />}
|
|
||||||
|
|
||||||
{modals.isOpen("viewer") && (
|
|
||||||
<ImageViewer
|
|
||||||
images={modals.modal.payload.images}
|
|
||||||
initialIndex={modals.modal.payload.index}
|
|
||||||
onClose={modals.close}
|
|
||||||
postData={modals.modal.payload.ad}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{modals.isOpen("postDetail") && (
|
|
||||||
<PostDetailModal
|
|
||||||
postId={modals.modal.payload.postId}
|
|
||||||
onClose={modals.close}
|
|
||||||
onImageClick={(images, index, ad) => {
|
|
||||||
modals.open("viewer", { images, index, ad });
|
|
||||||
}}
|
|
||||||
onLocationClick={(ad) => {
|
|
||||||
modals.close();
|
|
||||||
handleLocationClick(ad);
|
|
||||||
}}
|
|
||||||
onAuthRequired={() => modals.open("auth")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<BottomNav
|
|
||||||
activeTab={isMapView ? "map" : isSearchView ? "search" : "home"}
|
|
||||||
hasFilters={hasActiveFilters}
|
|
||||||
profileData={profileData}
|
|
||||||
onHome={handleHomeClick}
|
|
||||||
onMap={handleMapClick}
|
|
||||||
onCreatePost={handleCreatePost}
|
|
||||||
onSearch={handleSearchClick}
|
|
||||||
onProfile={handleProfileClick}
|
|
||||||
t={t}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
--err: #ed4956;
|
--err: #ed4956;
|
||||||
|
|
||||||
--bg-app: #fafafa;
|
--bg-app: #fafafa;
|
||||||
|
--bg-rgb: 250, 250, 250;
|
||||||
--bg-card: #ffffff;
|
--bg-card: #ffffff;
|
||||||
--bg-card-popup: #ffffff;
|
--bg-card-popup: #ffffff;
|
||||||
--bg-surface: #ffffff;
|
--bg-surface: #ffffff;
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
--err: #ed4956;
|
--err: #ed4956;
|
||||||
|
|
||||||
--bg-app: #000000;
|
--bg-app: #000000;
|
||||||
|
--bg-rgb: 0, 0, 0;
|
||||||
--bg-card: #1b1b1b;
|
--bg-card: #1b1b1b;
|
||||||
--bg-card-popup: #262626;
|
--bg-card-popup: #262626;
|
||||||
--bg-surface: #000000;
|
--bg-surface: #000000;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
/* ─────────────────────────────────────────────────────────────────────
|
||||||
|
СТЕКЛЯННАЯ ШАПКА + ЗАТУХАНИЕ ЛЕНТЫ (Telegram Web style)
|
||||||
|
─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Регистрация кастомного свойства: именно благодаря @property смена
|
||||||
|
--feed-fade-height анимируется transition'ом. В браузерах без поддержки
|
||||||
|
@property значение просто меняется мгновенно (graceful degradation).
|
||||||
|
*/
|
||||||
|
@property --feed-fade-height {
|
||||||
|
syntax: '<length>';
|
||||||
|
inherits: false;
|
||||||
|
initial-value: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── App Shell (Instagram-style) ── */
|
/* ── App Shell (Instagram-style) ── */
|
||||||
.f-app {
|
.f-app {
|
||||||
display: flex; height: 100vh; height: 100dvh;
|
display: flex; height: 100vh; height: 100dvh;
|
||||||
@@ -19,17 +33,46 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── Topbar (telegram-style; mobile only; desktop uses the nav sidebar) ──
|
/* ── Topbar (telegram-style; mobile only; desktop uses the nav sidebar) ──
|
||||||
Фон отсутствует полностью: стеклянными выглядят только сами кнопки. */
|
«Стекло»: полупрозрачный фон темы + мягкий blur по содержимому под шапкой.
|
||||||
|
blur/saturate явно умеренные — сильно агрессивное размытие даёт
|
||||||
|
пересвеченное «стекло» (blur 12px + saturate ~135%). */
|
||||||
.f-topbar {
|
.f-topbar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100;
|
z-index: 10;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
padding-top: calc(8px + env(safe-area-inset-top));
|
padding-top: calc(8px + env(safe-area-inset-top));
|
||||||
|
/* Без backdrop-filter: blur имеет жёсткую границу по кромке прямоугольника,
|
||||||
|
которую маской не убрать. Плавность даёт только сквозной альфа-градиент,
|
||||||
|
сходящийся к 0 уже к низу бара. */
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(var(--bg-rgb), 0.14) 0%,
|
||||||
|
rgba(var(--bg-rgb), 0.07) 55%,
|
||||||
|
rgba(var(--bg-rgb), 0.05) 100%
|
||||||
|
);
|
||||||
|
transition: transform 0.25s ease, background-color 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Продолжение того же сквозного градиента ниже бара: цвет уходит в 0,
|
||||||
|
резкой границы нет, «плашки» не видно вообще. */
|
||||||
|
.f-topbar::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 100%;
|
||||||
|
height: 64px;
|
||||||
|
pointer-events: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(var(--bg-rgb), 0.05) 0%,
|
||||||
|
rgba(var(--bg-rgb), 0) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Кнопка-логотип по центру: wordmark «Находка» в округлой стеклянной капсуле */
|
/* Кнопка-логотип по центру: wordmark «Находка» в округлой стеклянной капсуле */
|
||||||
@@ -206,18 +249,22 @@
|
|||||||
.f-topbar-dropdown-wrap {
|
.f-topbar-dropdown-wrap {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
/* В ленте шапка липнет к верху скроллера: контент прокручивается под ней
|
.f-topbar-dropdown-wrap .f-topbar.f-topbar-hidden {
|
||||||
и «замыливается» через backdrop-filter на .f-topbar */
|
/* Плавное скрытие шапки при скролле вниз (translateY вверх, за кромку .f-feed).
|
||||||
.f-feed > .f-topbar-dropdown-wrap {
|
Висим на самом .f-topbar, а НЕ на обёртке: transform на обёртке создал бы
|
||||||
position: sticky;
|
containing block для position:fixed оверлея дропдауна и сломал бы клик вне. */
|
||||||
top: 0;
|
transform: translateY(-100%);
|
||||||
z-index: 100;
|
|
||||||
margin: -20px -16px 0;
|
|
||||||
}
|
}
|
||||||
@media (max-width: 900px) {
|
/* В ленте шапка лежит ПОВЕРХ маскированного скролл-контейнера (.f-feed-scroll):
|
||||||
.f-feed > .f-topbar-dropdown-wrap {
|
по z-index она выше «слоя затухания», но ниже, чем сам контент не бывает
|
||||||
margin: -10px -10px 0;
|
никогда — жёстко прижата к верху .f-feed. */
|
||||||
}
|
.f-feed > .f-topbar-dropdown-wrap {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.f-topbar-dropdown-overlay {
|
.f-topbar-dropdown-overlay {
|
||||||
@@ -577,14 +624,45 @@
|
|||||||
/* ── Feed Wrap + Scroll ── */
|
/* ── Feed Wrap + Scroll ── */
|
||||||
.f-wrap { display: flex; flex: 1; overflow: hidden; min-height: 0; }
|
.f-wrap { display: flex; flex: 1; overflow: hidden; min-height: 0; }
|
||||||
|
|
||||||
|
/* Внешний контейнер ленты: не скроллится, лишь позиционирует слои.
|
||||||
|
Сверху — стеклянная шапка (абсолютный слой), под ней — маскированный
|
||||||
|
скролл-список; такой порядок даёт нужную иерархию z-index. */
|
||||||
.f-feed {
|
.f-feed {
|
||||||
flex: 1; overflow-y: scroll; overflow-x: hidden; overscroll-behavior: contain; min-height: 0;
|
position: relative;
|
||||||
padding: 20px 16px; display: flex; flex-direction: column; gap: 16px;
|
overflow: hidden;
|
||||||
width: 100%; max-width: 660px; margin: 0 auto;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 660px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
.f-feed::-webkit-scrollbar { width: 4px; }
|
|
||||||
.f-feed::-webkit-scrollbar-track { background: transparent; }
|
/* ── Слой затухания ленты ──
|
||||||
.f-feed::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
|
mask-image на самом контейнере списка (НЕ blur, НЕ opacity карточек):
|
||||||
|
верхние элементы, доезжающие до кромки под FeedTopBar, «растворяются».
|
||||||
|
Маска якорена к border-box скролл-контейнера, т.е. НЕ уезжает вместе
|
||||||
|
с карточками — зона затухания всегда под шапкой. Высота зоны задаётся
|
||||||
|
--feed-fade-height (управляется из React/ResizeObserver) и через
|
||||||
|
@property + transition анимируется при изменении.
|
||||||
|
padding-top синхронизирован с этой же переменной, чтобы первая карточка
|
||||||
|
в состоянии покоя не проваливалась под шапку. */
|
||||||
|
.f-feed-scroll {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
padding: calc(var(--feed-fade-height, 48px) + 20px) 16px 20px;
|
||||||
|
-webkit-mask-image: linear-gradient(to bottom, transparent 0, black var(--feed-fade-height, 48px));
|
||||||
|
mask-image: linear-gradient(to bottom, transparent 0, black var(--feed-fade-height, 48px));
|
||||||
|
transition: --feed-fade-height 0.2s ease;
|
||||||
|
}
|
||||||
|
.f-feed-scroll::-webkit-scrollbar { width: 4px; }
|
||||||
|
.f-feed-scroll::-webkit-scrollbar-track { background: transparent; }
|
||||||
|
.f-feed-scroll::-webkit-scrollbar-thumb { background: var(--muted2); border-radius: 2px; }
|
||||||
|
|
||||||
@media (min-width: 900px) {
|
@media (min-width: 900px) {
|
||||||
.f-main-feed .f-topbar {
|
.f-main-feed .f-topbar {
|
||||||
@@ -611,8 +689,8 @@
|
|||||||
bottom: calc(84px + env(safe-area-inset-bottom));
|
bottom: calc(84px + env(safe-area-inset-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
.f-feed {
|
.f-feed-scroll {
|
||||||
padding: 10px;
|
padding: calc(var(--feed-fade-height, 48px) + 10px) 10px calc(150px + env(safe-area-inset-bottom));
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,11 +723,6 @@
|
|||||||
margin-top: 4px; border-radius: 999px;
|
margin-top: 4px; border-radius: 999px;
|
||||||
}
|
}
|
||||||
.f-topbar-dropdown-item { padding: 7px 13px; font-size: 0.8rem; }
|
.f-topbar-dropdown-item { padding: 7px 13px; font-size: 0.8rem; }
|
||||||
|
|
||||||
/* Нижняя панель fixed — освобождаем место в конце ленты */
|
|
||||||
.f-feed {
|
|
||||||
padding-bottom: calc(150px + env(safe-area-inset-bottom));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user