产品和提取页面
This commit is contained in:
24
README.md
24
README.md
@@ -1,23 +1,3 @@
|
|||||||
## 功能布局
|
## TODO
|
||||||
|
|
||||||
### 首页
|
首页页头菜单导航的 bug
|
||||||
|
|
||||||
- 页头
|
|
||||||
- LOGO
|
|
||||||
- 菜单
|
|
||||||
- 登录/注册
|
|
||||||
- 正文
|
|
||||||
- 优势介绍
|
|
||||||
- 数据展示
|
|
||||||
- 产品推荐
|
|
||||||
- 客户案例
|
|
||||||
- 合作伙伴
|
|
||||||
- 新闻动态
|
|
||||||
- 页脚
|
|
||||||
- 关于我们
|
|
||||||
- 网站地图
|
|
||||||
- 备案/版权信息
|
|
||||||
|
|
||||||
### 后台
|
|
||||||
|
|
||||||
### 授权
|
|
||||||
|
|||||||
21
components.json
Normal file
21
components.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/app/globals.css",
|
||||||
|
"baseColor": "gray",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
|
}
|
||||||
14
package.json
14
package.json
@@ -9,10 +9,22 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hookform/resolvers": "^4.1.3",
|
||||||
|
"@radix-ui/react-label": "^2.1.2",
|
||||||
|
"@radix-ui/react-radio-group": "^1.2.3",
|
||||||
|
"@radix-ui/react-select": "^2.1.6",
|
||||||
|
"@radix-ui/react-slot": "^1.1.2",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.479.0",
|
||||||
"motion": "^12.5.0",
|
"motion": "^12.5.0",
|
||||||
"next": "15.2.1",
|
"next": "15.2.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0",
|
||||||
|
"react-hook-form": "^7.54.2",
|
||||||
|
"tailwind-merge": "^3.0.2",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
|||||||
804
pnpm-lock.yaml
generated
804
pnpm-lock.yaml
generated
@@ -8,6 +8,30 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@hookform/resolvers':
|
||||||
|
specifier: ^4.1.3
|
||||||
|
version: 4.1.3(react-hook-form@7.54.2(react@19.0.0))
|
||||||
|
'@radix-ui/react-label':
|
||||||
|
specifier: ^2.1.2
|
||||||
|
version: 2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-radio-group':
|
||||||
|
specifier: ^1.2.3
|
||||||
|
version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-select':
|
||||||
|
specifier: ^2.1.6
|
||||||
|
version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-slot':
|
||||||
|
specifier: ^1.1.2
|
||||||
|
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
class-variance-authority:
|
||||||
|
specifier: ^0.7.1
|
||||||
|
version: 0.7.1
|
||||||
|
clsx:
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1
|
||||||
|
lucide-react:
|
||||||
|
specifier: ^0.479.0
|
||||||
|
version: 0.479.0(react@19.0.0)
|
||||||
motion:
|
motion:
|
||||||
specifier: ^12.5.0
|
specifier: ^12.5.0
|
||||||
version: 12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
@@ -20,6 +44,18 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.0.0(react@19.0.0)
|
version: 19.0.0(react@19.0.0)
|
||||||
|
react-hook-form:
|
||||||
|
specifier: ^7.54.2
|
||||||
|
version: 7.54.2(react@19.0.0)
|
||||||
|
tailwind-merge:
|
||||||
|
specifier: ^3.0.2
|
||||||
|
version: 3.0.2
|
||||||
|
tailwindcss-animate:
|
||||||
|
specifier: ^1.0.7
|
||||||
|
version: 1.0.7(tailwindcss@4.0.9)
|
||||||
|
zod:
|
||||||
|
specifier: ^3.24.2
|
||||||
|
version: 3.24.2
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: ^3
|
specifier: ^3
|
||||||
@@ -98,6 +134,26 @@ packages:
|
|||||||
resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==}
|
resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@floating-ui/core@1.6.9':
|
||||||
|
resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.6.13':
|
||||||
|
resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==}
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.2':
|
||||||
|
resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.9':
|
||||||
|
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
|
||||||
|
|
||||||
|
'@hookform/resolvers@4.1.3':
|
||||||
|
resolution: {integrity: sha512-Jsv6UOWYTrEFJ/01ZrnwVXs7KDvP8XIo115i++5PWvNkNvkrsTfGiLS6w+eJ57CYtUtDQalUWovCZDHFJ8u1VQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react-hook-form: ^7.0.0
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
@@ -309,12 +365,310 @@ packages:
|
|||||||
resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
|
resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
|
||||||
engines: {node: '>=12.4.0'}
|
engines: {node: '>=12.4.0'}
|
||||||
|
|
||||||
|
'@radix-ui/number@1.1.0':
|
||||||
|
resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
|
||||||
|
|
||||||
|
'@radix-ui/primitive@1.1.1':
|
||||||
|
resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==}
|
||||||
|
|
||||||
|
'@radix-ui/react-arrow@1.1.2':
|
||||||
|
resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-collection@1.1.2':
|
||||||
|
resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-compose-refs@1.1.1':
|
||||||
|
resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.1':
|
||||||
|
resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-direction@1.1.0':
|
||||||
|
resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.5':
|
||||||
|
resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.1':
|
||||||
|
resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-scope@1.1.2':
|
||||||
|
resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-id@1.1.0':
|
||||||
|
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-label@2.1.2':
|
||||||
|
resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-popper@1.2.2':
|
||||||
|
resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.4':
|
||||||
|
resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.2':
|
||||||
|
resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-primitive@2.0.2':
|
||||||
|
resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-radio-group@1.2.3':
|
||||||
|
resolution: {integrity: sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-roving-focus@1.1.2':
|
||||||
|
resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.6':
|
||||||
|
resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-slot@1.1.2':
|
||||||
|
resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-callback-ref@1.1.0':
|
||||||
|
resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-controllable-state@1.1.0':
|
||||||
|
resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-escape-keydown@1.1.0':
|
||||||
|
resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-layout-effect@1.1.0':
|
||||||
|
resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-previous@1.1.0':
|
||||||
|
resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-rect@1.1.0':
|
||||||
|
resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-size@1.1.0':
|
||||||
|
resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-visually-hidden@1.1.2':
|
||||||
|
resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/rect@1.1.0':
|
||||||
|
resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
|
||||||
|
|
||||||
'@rtsao/scc@1.1.0':
|
'@rtsao/scc@1.1.0':
|
||||||
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
|
||||||
|
|
||||||
'@rushstack/eslint-patch@1.10.5':
|
'@rushstack/eslint-patch@1.10.5':
|
||||||
resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==}
|
resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==}
|
||||||
|
|
||||||
|
'@standard-schema/utils@0.3.0':
|
||||||
|
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
|
||||||
|
|
||||||
'@swc/counter@0.1.3':
|
'@swc/counter@0.1.3':
|
||||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||||
|
|
||||||
@@ -488,6 +842,10 @@ packages:
|
|||||||
argparse@2.0.1:
|
argparse@2.0.1:
|
||||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
|
||||||
|
aria-hidden@1.2.4:
|
||||||
|
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
aria-query@5.3.2:
|
aria-query@5.3.2:
|
||||||
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
|
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -583,9 +941,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
class-variance-authority@0.7.1:
|
||||||
|
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||||
|
|
||||||
client-only@0.0.1:
|
client-only@0.0.1:
|
||||||
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
||||||
|
|
||||||
|
clsx@2.1.1:
|
||||||
|
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -662,6 +1027,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
detect-node-es@1.1.0:
|
||||||
|
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -908,6 +1276,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
get-nonce@1.0.1:
|
||||||
|
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
get-proto@1.0.1:
|
get-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1221,6 +1593,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
lucide-react@0.479.0:
|
||||||
|
resolution: {integrity: sha512-aBhNnveRhorBOK7uA4gDjgaf+YlHMdMhQ/3cupk6exM10hWlEU+2QtWYOfhXhjAsmdb6LeKR+NZnow4UxRRiTQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
math-intrinsics@1.1.0:
|
math-intrinsics@1.1.0:
|
||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1400,9 +1777,45 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^19.0.0
|
react: ^19.0.0
|
||||||
|
|
||||||
|
react-hook-form@7.54.2:
|
||||||
|
resolution: {integrity: sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||||
|
|
||||||
react-is@16.13.1:
|
react-is@16.13.1:
|
||||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||||
|
|
||||||
|
react-remove-scroll-bar@2.3.8:
|
||||||
|
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
react-remove-scroll@2.6.3:
|
||||||
|
resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
react-style-singleton@2.2.3:
|
||||||
|
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
react@19.0.0:
|
react@19.0.0:
|
||||||
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
|
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1568,6 +1981,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
tailwind-merge@3.0.2:
|
||||||
|
resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==}
|
||||||
|
|
||||||
|
tailwindcss-animate@1.0.7:
|
||||||
|
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || insiders'
|
||||||
|
|
||||||
tailwindcss@4.0.9:
|
tailwindcss@4.0.9:
|
||||||
resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==}
|
resolution: {integrity: sha512-12laZu+fv1ONDRoNR9ipTOpUD7RN9essRVkX36sjxuRUInpN7hIiHN4lBd/SIFjbISvnXzp8h/hXzmU8SQQYhw==}
|
||||||
|
|
||||||
@@ -1630,6 +2051,26 @@ packages:
|
|||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
|
use-callback-ref@1.3.3:
|
||||||
|
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
use-sidecar@1.1.3:
|
||||||
|
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1659,6 +2100,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
zod@3.24.2:
|
||||||
|
resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0': {}
|
'@alloc/quick-lru@5.2.0': {}
|
||||||
@@ -1710,6 +2154,28 @@ snapshots:
|
|||||||
'@eslint/core': 0.12.0
|
'@eslint/core': 0.12.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
|
'@floating-ui/core@1.6.9':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/utils': 0.2.9
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.6.13':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/core': 1.6.9
|
||||||
|
'@floating-ui/utils': 0.2.9
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/dom': 1.6.13
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.9': {}
|
||||||
|
|
||||||
|
'@hookform/resolvers@4.1.3(react-hook-form@7.54.2(react@19.0.0))':
|
||||||
|
dependencies:
|
||||||
|
'@standard-schema/utils': 0.3.0
|
||||||
|
react-hook-form: 7.54.2(react@19.0.0)
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
'@humanfs/node@0.16.6':
|
'@humanfs/node@0.16.6':
|
||||||
@@ -1842,10 +2308,276 @@ snapshots:
|
|||||||
|
|
||||||
'@nolyfill/is-core-module@1.0.39': {}
|
'@nolyfill/is-core-module@1.0.39': {}
|
||||||
|
|
||||||
|
'@radix-ui/number@1.1.0': {}
|
||||||
|
|
||||||
|
'@radix-ui/primitive@1.1.1': {}
|
||||||
|
|
||||||
|
'@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-collection@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-direction@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.1
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-id@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-label@2.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-arrow': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/rect': 1.1.0
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-radio-group@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.1
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-roving-focus@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.1
|
||||||
|
'@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/number': 1.1.0
|
||||||
|
'@radix-ui/primitive': 1.1.1
|
||||||
|
'@radix-ui/react-collection': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
aria-hidden: 1.2.4
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-previous@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-rect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/rect': 1.1.0
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-use-size@1.1.0(@types/react@19.0.10)(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
'@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
'@types/react-dom': 19.0.4(@types/react@19.0.10)
|
||||||
|
|
||||||
|
'@radix-ui/rect@1.1.0': {}
|
||||||
|
|
||||||
'@rtsao/scc@1.1.0': {}
|
'@rtsao/scc@1.1.0': {}
|
||||||
|
|
||||||
'@rushstack/eslint-patch@1.10.5': {}
|
'@rushstack/eslint-patch@1.10.5': {}
|
||||||
|
|
||||||
|
'@standard-schema/utils@0.3.0': {}
|
||||||
|
|
||||||
'@swc/counter@0.1.3': {}
|
'@swc/counter@0.1.3': {}
|
||||||
|
|
||||||
'@swc/helpers@0.5.15':
|
'@swc/helpers@0.5.15':
|
||||||
@@ -2028,6 +2760,10 @@ snapshots:
|
|||||||
|
|
||||||
argparse@2.0.1: {}
|
argparse@2.0.1: {}
|
||||||
|
|
||||||
|
aria-hidden@1.2.4:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
aria-query@5.3.2: {}
|
aria-query@5.3.2: {}
|
||||||
|
|
||||||
array-buffer-byte-length@1.0.2:
|
array-buffer-byte-length@1.0.2:
|
||||||
@@ -2151,8 +2887,14 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
class-variance-authority@0.7.1:
|
||||||
|
dependencies:
|
||||||
|
clsx: 2.1.1
|
||||||
|
|
||||||
client-only@0.0.1: {}
|
client-only@0.0.1: {}
|
||||||
|
|
||||||
|
clsx@2.1.1: {}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -2228,6 +2970,8 @@ snapshots:
|
|||||||
detect-libc@2.0.3:
|
detect-libc@2.0.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
detect-node-es@1.1.0: {}
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
@@ -2630,6 +3374,8 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
math-intrinsics: 1.1.0
|
math-intrinsics: 1.1.0
|
||||||
|
|
||||||
|
get-nonce@1.0.1: {}
|
||||||
|
|
||||||
get-proto@1.0.1:
|
get-proto@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
dunder-proto: 1.0.1
|
dunder-proto: 1.0.1
|
||||||
@@ -2923,6 +3669,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 4.0.0
|
js-tokens: 4.0.0
|
||||||
|
|
||||||
|
lucide-react@0.479.0(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
@@ -3098,8 +3848,39 @@ snapshots:
|
|||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
scheduler: 0.25.0
|
scheduler: 0.25.0
|
||||||
|
|
||||||
|
react-hook-form@7.54.2(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
|
||||||
react-is@16.13.1: {}
|
react-is@16.13.1: {}
|
||||||
|
|
||||||
|
react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
tslib: 2.8.1
|
||||||
|
use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
get-nonce: 1.0.1
|
||||||
|
react: 19.0.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
react@19.0.0: {}
|
react@19.0.0: {}
|
||||||
|
|
||||||
reflect.getprototypeof@1.0.10:
|
reflect.getprototypeof@1.0.10:
|
||||||
@@ -3328,6 +4109,12 @@ snapshots:
|
|||||||
|
|
||||||
supports-preserve-symlinks-flag@1.0.0: {}
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
|
tailwind-merge@3.0.2: {}
|
||||||
|
|
||||||
|
tailwindcss-animate@1.0.7(tailwindcss@4.0.9):
|
||||||
|
dependencies:
|
||||||
|
tailwindcss: 4.0.9
|
||||||
|
|
||||||
tailwindcss@4.0.9: {}
|
tailwindcss@4.0.9: {}
|
||||||
|
|
||||||
tapable@2.2.1: {}
|
tapable@2.2.1: {}
|
||||||
@@ -3406,6 +4193,21 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
|
use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
detect-node-es: 1.1.0
|
||||||
|
react: 19.0.0
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.0.10
|
||||||
|
|
||||||
which-boxed-primitive@1.1.1:
|
which-boxed-primitive@1.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-bigint: 1.1.0
|
is-bigint: 1.1.0
|
||||||
@@ -3453,3 +4255,5 @@ snapshots:
|
|||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
zod@3.24.2: {}
|
||||||
|
|||||||
10
public/collect/warn.svg
Normal file
10
public/collect/warn.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_152_604)">
|
||||||
|
<path d="M10 -8.74228e-07C15.5225 -3.91435e-07 20 4.4775 20 10C20 15.5225 15.5225 20 10 20C4.4775 20 3.91435e-07 15.5225 8.74228e-07 10C1.35702e-06 4.4775 4.4775 -1.35702e-06 10 -8.74228e-07ZM10 1.66667C5.3975 1.66666 1.66667 5.3975 1.66667 10C1.66667 14.6025 5.3975 18.3333 10 18.3333C14.6025 18.3333 18.3333 14.6025 18.3333 10C18.3333 5.3975 14.6025 1.66667 10 1.66667ZM10.8333 10.8333C10.8333 11.0543 10.7455 11.2663 10.5893 11.4226C10.433 11.5789 10.221 11.6667 10 11.6667C9.77899 11.6667 9.56703 11.5789 9.41075 11.4226C9.25446 11.2663 9.16667 11.0543 9.16667 10.8333L9.16667 5C9.16667 4.77899 9.25447 4.56702 9.41075 4.41074C9.56703 4.25446 9.77899 4.16667 10 4.16667C10.221 4.16667 10.433 4.25446 10.5893 4.41074C10.7455 4.56702 10.8333 4.77899 10.8333 5L10.8333 10.8333ZM10.0833 13.5C10.3928 13.5 10.6895 13.6229 10.9083 13.8417C11.1271 14.0605 11.25 14.3572 11.25 14.6667C11.25 14.9761 11.1271 15.2728 10.9083 15.4916C10.6895 15.7104 10.3928 15.8333 10.0833 15.8333C9.77391 15.8333 9.47717 15.7104 9.25838 15.4916C9.03958 15.2728 8.91667 14.9761 8.91667 14.6667C8.91667 14.3572 9.03958 14.0605 9.25838 13.8417C9.47717 13.6229 9.77391 13.5 10.0833 13.5Z" fill="#FF6B00"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_152_604">
|
||||||
|
<rect width="20" height="20" fill="white" transform="translate(20 20) rotate(-180)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
10
public/product/alipay.svg
Normal file
10
public/product/alipay.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_98_223)">
|
||||||
|
<path d="M20.001 13.692V3.845C20.0005 2.82523 19.5951 1.8474 18.8739 1.12641C18.1527 0.405415 17.1748 0.000264944 16.155 0L3.845 0C2.82541 0.000529935 1.84772 0.405797 1.12676 1.12676C0.405797 1.84772 0.000529935 2.82541 0 3.845V16.155C0.000265092 17.1747 0.405447 18.1525 1.12647 18.8735C1.84749 19.5946 2.82532 19.9997 3.845 20H16.155C17.057 19.9994 17.9301 19.682 18.622 19.1034C19.3139 18.5248 19.7808 17.7216 19.941 16.834C18.921 16.392 14.501 14.484 12.198 13.384C10.446 15.507 8.61 16.781 5.844 16.781C3.078 16.781 1.231 15.077 1.453 12.991C1.599 11.623 2.538 9.386 6.615 9.769C8.765 9.971 9.748 10.372 11.501 10.951C11.954 10.119 12.331 9.204 12.617 8.231H4.845V7.461H8.691V6.077H4V5.23H8.69V3.235C8.69 3.235 8.732 2.923 9.077 2.923H11V5.23H16V6.078H11V7.46H15.079C14.7272 8.91647 14.1692 10.3152 13.422 11.614C14.607 12.044 20 13.692 20 13.692H20.001ZM5.538 15.461C2.615 15.461 2.153 13.616 2.308 12.845C2.461 12.077 3.308 11.075 4.933 11.075C6.8 11.075 8.473 11.553 10.481 12.531C9.071 14.367 7.338 15.461 5.538 15.461Z" fill="#00A5F1"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_98_223">
|
||||||
|
<rect width="20" height="20" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/product/banner.webp
Normal file
BIN
public/product/banner.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
10
public/product/check.svg
Normal file
10
public/product/check.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_98_189)">
|
||||||
|
<path d="M3.46154 0H14.5385C15.4565 0 16.337 0.364697 16.9861 1.01386C17.6353 1.66303 18 2.54348 18 3.46154V14.5385C18 15.4565 17.6353 16.337 16.9861 16.9861C16.337 17.6353 15.4565 18 14.5385 18H3.46154C2.54348 18 1.66303 17.6353 1.01386 16.9861C0.364697 16.337 0 15.4565 0 14.5385L0 3.46154C0 2.54348 0.364697 1.66303 1.01386 1.01386C1.66303 0.364697 2.54348 0 3.46154 0ZM3.46154 1.38462C2.9107 1.38462 2.38243 1.60343 1.99293 1.99293C1.60343 2.38243 1.38462 2.9107 1.38462 3.46154V14.5385C1.38462 14.8112 1.43834 15.0813 1.54271 15.3333C1.64709 15.5852 1.80007 15.8142 1.99293 16.0071C2.18579 16.1999 2.41475 16.3529 2.66673 16.4573C2.91872 16.5617 3.18879 16.6154 3.46154 16.6154H14.5385C14.8112 16.6154 15.0813 16.5617 15.3333 16.4573C15.5852 16.3529 15.8142 16.1999 16.0071 16.0071C16.1999 15.8142 16.3529 15.5852 16.4573 15.3333C16.5617 15.0813 16.6154 14.8112 16.6154 14.5385V3.46154C16.6154 3.18879 16.5617 2.91872 16.4573 2.66673C16.3529 2.41475 16.1999 2.18579 16.0071 1.99293C15.8142 1.80007 15.5852 1.64709 15.3333 1.54271C15.0813 1.43834 14.8112 1.38462 14.5385 1.38462H3.46154ZM13.743 5.92338C13.8041 5.98151 13.8531 6.0511 13.8873 6.12818C13.9214 6.20526 13.9401 6.28831 13.9421 6.3726C13.9442 6.45689 13.9296 6.54076 13.8993 6.61941C13.8689 6.69806 13.8233 6.76996 13.7652 6.831L8.50085 12.3348C8.49877 12.3376 8.49392 12.339 8.48977 12.3432L8.48285 12.3528C8.44477 12.3902 8.39977 12.4096 8.35615 12.4359C8.32846 12.4512 8.307 12.474 8.27862 12.4858C8.20213 12.5178 8.12007 12.5345 8.03714 12.5347C7.95421 12.5349 7.87205 12.5188 7.79538 12.4872C7.77323 12.4782 7.75523 12.4588 7.73308 12.447C7.68462 12.4214 7.63477 12.3972 7.59185 12.357C7.58838 12.3542 7.587 12.3494 7.58285 12.3452C7.57938 12.3432 7.57592 12.3418 7.57315 12.339L4.92162 9.58638C4.86293 9.5258 4.81679 9.45421 4.78585 9.37574C4.75491 9.29727 4.73978 9.21346 4.74132 9.12912C4.74287 9.04479 4.76106 8.96159 4.79485 8.8843C4.82864 8.80702 4.87737 8.73717 4.93823 8.67877C4.99877 8.62 5.07035 8.57379 5.14882 8.5428C5.2273 8.5118 5.31113 8.49664 5.3955 8.49819C5.47986 8.49973 5.56308 8.51795 5.64037 8.5518C5.71766 8.58564 5.78749 8.63444 5.84585 8.69538L8.03354 10.9648L12.834 5.94415C12.8923 5.88305 12.9621 5.83404 13.0393 5.79992C13.1166 5.7658 13.1998 5.74724 13.2842 5.74532C13.3686 5.74339 13.4526 5.75812 13.5313 5.78868C13.6101 5.81923 13.682 5.86501 13.743 5.92338Z" fill="#2470F9"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_98_189">
|
||||||
|
<rect width="18" height="18" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
16
public/product/wechat.svg
Normal file
16
public/product/wechat.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_98_228)">
|
||||||
|
<path d="M3.6 0H16.4C18.8 0 20 1.2 20 3.6V16.4C20 18.8 18.8 20 16.4 20H3.6C1.2 20 0 18.8 0 16.4V3.6C0 1.2 1.2 0 3.6 0Z" fill="#28D846"/>
|
||||||
|
<path d="M12.9534 7.94922C10.4386 7.94922 8.39999 9.65422 8.39999 11.7574C8.39999 13.8606 10.4386 15.5656 12.9534 15.5656C13.5434 15.5658 14.1294 15.4683 14.6876 15.2772L15.9912 15.9716L15.8158 14.7192C16.3212 14.4045 16.7403 13.969 17.0355 13.452C17.3307 12.9349 17.4927 12.3526 17.5068 11.7574C17.5066 9.65422 15.468 7.94922 12.9534 7.94922Z" fill="white"/>
|
||||||
|
<path d="M7.89999 3.80078C10.576 3.80078 12.7884 5.44078 13.2938 7.61478C12.3402 7.53638 7.39379 8.06938 8.17939 12.9148C7.46839 12.9168 6.49939 12.8856 5.80539 12.6518L4.23059 13.4912L4.44259 11.9784C3.21699 11.1678 2.39999 9.86418 2.39999 8.40078C2.39999 5.86078 4.86239 3.80078 7.89999 3.80078Z" fill="white"/>
|
||||||
|
<path d="M5.39999 6.88016C5.39999 7.07111 5.47585 7.25425 5.61088 7.38927C5.7459 7.5243 5.92904 7.60016 6.11999 7.60016C6.31095 7.60016 6.49408 7.5243 6.62911 7.38927C6.76414 7.25425 6.83999 7.07111 6.83999 6.88016C6.83999 6.6892 6.76414 6.50607 6.62911 6.37104C6.49408 6.23601 6.31095 6.16016 6.11999 6.16016C5.92904 6.16016 5.7459 6.23601 5.61088 6.37104C5.47585 6.50607 5.39999 6.6892 5.39999 6.88016Z" fill="#28D846"/>
|
||||||
|
<path d="M9 6.88016C9 7.07111 9.07586 7.25425 9.21088 7.38927C9.34591 7.5243 9.52904 7.60016 9.72 7.60016C9.91096 7.60016 10.0941 7.5243 10.2291 7.38927C10.3641 7.25425 10.44 7.07111 10.44 6.88016C10.44 6.6892 10.3641 6.50607 10.2291 6.37104C10.0941 6.23601 9.91096 6.16016 9.72 6.16016C9.52904 6.16016 9.34591 6.23601 9.21088 6.37104C9.07586 6.50607 9 6.6892 9 6.88016Z" fill="#28D846"/>
|
||||||
|
<path d="M10.8 10.64C10.8 10.8097 10.8674 10.9725 10.9874 11.0925C11.1075 11.2126 11.2702 11.28 11.44 11.28C11.6097 11.28 11.7725 11.2126 11.8925 11.0925C12.0126 10.9725 12.08 10.8097 12.08 10.64C12.08 10.4703 12.0126 10.3075 11.8925 10.1875C11.7725 10.0674 11.6097 10 11.44 10C11.2702 10 11.1075 10.0674 10.9874 10.1875C10.8674 10.3075 10.8 10.4703 10.8 10.64Z" fill="#28D846"/>
|
||||||
|
<path d="M13.84 10.64C13.84 10.8097 13.9074 10.9725 14.0274 11.0925C14.1475 11.2126 14.3103 11.28 14.48 11.28C14.6497 11.28 14.8125 11.2126 14.9325 11.0925C15.0526 10.9725 15.12 10.8097 15.12 10.64C15.12 10.4703 15.0526 10.3075 14.9325 10.1875C14.8125 10.0674 14.6497 10 14.48 10C14.3103 10 14.1475 10.0674 14.0274 10.1875C13.9074 10.3075 13.84 10.4703 13.84 10.64Z" fill="#28D846"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_98_228">
|
||||||
|
<rect width="20" height="20" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1,227 +0,0 @@
|
|||||||
import {ReactNode} from 'react'
|
|
||||||
import Link from 'next/link'
|
|
||||||
import Header from '@/app/(home)/header'
|
|
||||||
import Wrap from '@/components/wrap'
|
|
||||||
import Image from 'next/image'
|
|
||||||
import Footer from './footer'
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<div className={`overflow-auto flex flex-col items-stretch relative`}>
|
|
||||||
{/* 页头 */}
|
|
||||||
<Header/>
|
|
||||||
|
|
||||||
{/* 正文 */}
|
|
||||||
<main className={`flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32`}>
|
|
||||||
|
|
||||||
{/* banner */}
|
|
||||||
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}>
|
|
||||||
<Wrap className={`pt-64 pb-48 max-md:pt-32 max-md:pb-24`}>
|
|
||||||
<h1 className={`text-4xl font-medium`}>安全,稳定,快速,合规的代理服务器</h1>
|
|
||||||
<p className={`mt-10 text-gray-500`}>遍布全国的代理服务器节点为用户提供智能可靠的IP代理服务</p>
|
|
||||||
|
|
||||||
<div className={`mt-24 max-md:mt-14 flex gap-8 max-md:flex-col`}>
|
|
||||||
<p className={`flex gap-4 items-center`}>
|
|
||||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
|
||||||
<span className={`lg:text-lg font-light`}>全国200+城市节点</span>
|
|
||||||
</p>
|
|
||||||
<p className={`flex gap-4 items-center`}>
|
|
||||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
|
||||||
<span className={`lg:text-lg font-light`}>300+城市级精准定位</span>
|
|
||||||
</p>
|
|
||||||
<p className={`flex gap-4 items-center`}>
|
|
||||||
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
|
||||||
<span className={`lg:text-lg font-light`}>低延迟&高并发提取</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className={[
|
|
||||||
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
|
|
||||||
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl font-medium`,
|
|
||||||
].join(' ')}>
|
|
||||||
免费试用
|
|
||||||
</button>
|
|
||||||
</Wrap>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* 数据展示 */}
|
|
||||||
<Section title={`覆盖全国的IP资源及超大的带宽线路`}>
|
|
||||||
<ul className={`shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col`}>
|
|
||||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
|
||||||
<p className={`text-xl`}>全国城市线路数量</p>
|
|
||||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>350+</p>
|
|
||||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
|
||||||
</li>
|
|
||||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
|
||||||
<p className={`text-xl`}>每日更新IP数量</p>
|
|
||||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>1,350,129</p>
|
|
||||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
|
||||||
</li>
|
|
||||||
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
|
||||||
<p className={`text-xl`}>用户量</p>
|
|
||||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>26,578</p>
|
|
||||||
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
|
||||||
</li>
|
|
||||||
<li className={`flex-1 flex flex-col items-center justify-center`}>
|
|
||||||
<p className={`text-xl`}>IP可用率</p>
|
|
||||||
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>99%</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<img src={`/map.webp`} alt={`map`} className="w-[1200px]"/>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
|
|
||||||
{/* 优势 1 */}
|
|
||||||
<Section title={`HTTP安全合规的代理IP资源池`}>
|
|
||||||
<ul
|
|
||||||
className={[
|
|
||||||
`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8`,
|
|
||||||
].join(' ')}>
|
|
||||||
<Sec3Item
|
|
||||||
icon={`s1-1`} title={`短期动态IP池`} terms={[
|
|
||||||
{icon: `s1-check`, text: `IP时效3-30分钟(可定制)`},
|
|
||||||
{icon: `s1-check`, text: `支持高并发提取`},
|
|
||||||
]}/>
|
|
||||||
<Sec3Item
|
|
||||||
icon={`s1-2`} title={`长期静态IP池`} terms={[
|
|
||||||
{icon: `s1-check`, text: `IP覆盖全国各地`},
|
|
||||||
{icon: `s1-check`, text: `平均响应时长:0.03s`},
|
|
||||||
]}/>
|
|
||||||
<Sec3Item
|
|
||||||
icon={`s1-3`} title={`固定IP池`} terms={[
|
|
||||||
{icon: `s1-check`, text: `稳定长输不掉线`},
|
|
||||||
{icon: `s1-check`, text: `全国热门静态IP线路`},
|
|
||||||
]}/>
|
|
||||||
<Sec3Item
|
|
||||||
icon={`s1-4`} title={`企业级定制池`} terms={[
|
|
||||||
{icon: `s1-check`, text: `可视化监控设计`},
|
|
||||||
{icon: `s1-check`, text: `技术团队现场支持`},
|
|
||||||
]}/>
|
|
||||||
</ul>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* 优势 2 */}
|
|
||||||
<Section title={`HTTP 产品优势`}>
|
|
||||||
<div className={`flex gap-36`}>
|
|
||||||
<ul className={`flex-1 flex flex-col gap-6`}>
|
|
||||||
<Sec4Item icon={`s4-1-1`} title={`安全合规`} description={`国内三大运营商支持`}/>
|
|
||||||
<Sec4Item icon={`s4-1-2`} title={`稳定链接`} description={`IP纯净度高达99.9%`}/>
|
|
||||||
<Sec4Item icon={`s4-1-3`} title={`超匿名性`} description={`稳定传输,保护隐私安全`}/>
|
|
||||||
</ul>
|
|
||||||
<img src={`/s4-1-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`flex gap-36`}>
|
|
||||||
<img src={`/s4-2-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
|
||||||
<ul className={`flex-1 flex flex-col gap-6`}>
|
|
||||||
<Sec4Item icon={`s4-2-1`} title={`API接口文档`} description={`与第三方软件轻松集成`}/>
|
|
||||||
<Sec4Item icon={`s4-2-2`} title={`多种编程语言代码`} description={`C语言、GO语言、Python...`}/>
|
|
||||||
<Sec4Item icon={`s4-2-3`} title={`双重认证方式`} description={`API提取+账密认证`}/>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
{/* 行业资讯 */}
|
|
||||||
<Section title={`行业资讯`}>
|
|
||||||
<div className={`flex gap-8 max-md:gap-4`}>
|
|
||||||
<button className={`px-4 max-md:-mx-4`}>
|
|
||||||
<img src={`/next.svg`} alt={`prev`} className={`rotate-180`}/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={[
|
|
||||||
`shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`,
|
|
||||||
`flex p-14 md:gap-14 max-md:flex-col max-md:p-4`,
|
|
||||||
].join(' ')}>
|
|
||||||
<img src="/s3-main.webp" alt="tumb" className={`w-2/3 md:flex-1 md:w-0 object-cover max-md:self-center`}/>
|
|
||||||
<div className={`flex-2 flex flex-col justify-between gap-4`}>
|
|
||||||
<h3 className={`flex justify-between`}>
|
|
||||||
<span className={`text-xl font-medium`}>我是标题</span>
|
|
||||||
<sub className={`text-sm text-gray-500`}>2025-03-04</sub>
|
|
||||||
</h3>
|
|
||||||
<p className={`text-gray-400 md:leading-12`}>
|
|
||||||
我是内容我是内容我是内容我是内容我是内容我是容我是内容我是内容内容我是内容我是内容我是内我是内容我是内容我是内容我是内容我是内容...
|
|
||||||
</p>
|
|
||||||
<div className={`flex justify-end`}>
|
|
||||||
<a href="#" className={`text-sm text-gray-500 flex items-center gap-4`}>
|
|
||||||
更多详情
|
|
||||||
<img src={`/next.svg`} alt={`more`} className={`h-4 fill-gray-400`}/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button className={`px-4 max-md:-mx-4`}>
|
|
||||||
<img src={`/next.svg`} alt={`prev`}/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* 页脚 */}
|
|
||||||
<Footer/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Section(props: {
|
|
||||||
title: string
|
|
||||||
children: ReactNode
|
|
||||||
}) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<div className={`max-w-[1232px] mx-auto px-4 flex flex-col items-stretch`}>
|
|
||||||
<h2 className={`text-center text-3xl font-medium mb-8 lg:mb-24`}>{props.title}</h2>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sec3Item(props: {
|
|
||||||
icon: string,
|
|
||||||
title: string,
|
|
||||||
terms: {
|
|
||||||
icon: string,
|
|
||||||
text: string,
|
|
||||||
}[]
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
className={[
|
|
||||||
`p-8 flex flex-col gap-5 shadow-[4px_4px_20px_4px] shadow-blue-50 bg-white rounded-lg`,
|
|
||||||
`max-md:items-center`,
|
|
||||||
].join(' ')}>
|
|
||||||
<img src={`/${props.icon}.webp`} alt={`s1-1`} aria-hidden className="w-44 h-44 object-cover"/>
|
|
||||||
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
|
||||||
<div className={`flex flex-col gap-3`}>
|
|
||||||
{props.terms.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<p key={index} className={`text-sm text-gray-500 flex gap-3 items-center`}>
|
|
||||||
<img src={`/${item.icon}.svg`} alt={`check`} aria-hidden className={`w-5 h-5`}/>
|
|
||||||
<span>{item.text}</span>
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Sec4Item(props: {
|
|
||||||
icon: string
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<li className={`flex gap-8 items-center p-4 lg:p-8 shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`}>
|
|
||||||
<img src={`/${props.icon}.webp`} alt={`s2-1-1`} aria-hidden className="w-24 h-24 object-contain"/>
|
|
||||||
<div className={`flex flex-col gap-3`}>
|
|
||||||
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
|
||||||
<p>{props.description}</p>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
311
src/app/(root)/collect/form-section.tsx
Normal file
311
src/app/(root)/collect/form-section.tsx
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
'use client'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import { useForm } from 'react-hook-form'
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod'
|
||||||
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'
|
||||||
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
type: z.enum([`num`, `time`]),
|
||||||
|
order: z.number(),
|
||||||
|
region: z.string(),
|
||||||
|
provider: z.string(),
|
||||||
|
proto: z.string(),
|
||||||
|
distinct: z.string(),
|
||||||
|
format: z.enum([`txt`, `json`]),
|
||||||
|
separator: z.string(),
|
||||||
|
count: z.number(),
|
||||||
|
})
|
||||||
|
|
||||||
|
type FormSectionProps = {}
|
||||||
|
|
||||||
|
export default function FormSection(props: FormSectionProps) {
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof schema>>({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
type: `num`,
|
||||||
|
order: 0,
|
||||||
|
region: ``,
|
||||||
|
provider: ``,
|
||||||
|
proto: ``,
|
||||||
|
distinct: ``,
|
||||||
|
format: `txt`,
|
||||||
|
separator: `,`,
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const onSubmit = (values: z.infer<typeof schema>) => {
|
||||||
|
console.log(values)
|
||||||
|
// 在这里处理表单提交
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)} className={`p-8 bg-white flex flex-col gap-4 rounded-lg`}>
|
||||||
|
<ul role={`tablist`} className={`p-2 w-fit flex gap-2 bg-gray-100 rounded-lg`}>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button type="button" className={`px-4 h-10 bg-white rounded-md shadow-sm`}>
|
||||||
|
短效动态IP提取
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button type="button" className={`px-4 h-10 rounded-md`}>
|
||||||
|
长效静态IP提取
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p className={`px-4 h-10 bg-orange-50 flex gap-3 items-center rounded-lg`}>
|
||||||
|
<img src={`/collect/warn.svg`} alt={`warn`} aria-hidden className={`w-5 h-5`} />
|
||||||
|
<span className={`text-sm`}>提取IP前需要将本机IP添加到白名单后才可使用</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className={`flex flex-col gap-y-4`}>
|
||||||
|
{/* 套餐类型 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">套餐类型</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="type"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="flex gap-4"
|
||||||
|
>
|
||||||
|
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||||
|
<RadioGroupItem value="num" id="num" className="mr-2" />
|
||||||
|
<label htmlFor="num">包量套餐</label>
|
||||||
|
</div>
|
||||||
|
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||||
|
<RadioGroupItem value="time" id="time" className="mr-2" />
|
||||||
|
<label htmlFor="time">包时套餐</label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 已购套餐 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">已购套餐</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="order"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select
|
||||||
|
onValueChange={value => field.onChange(Number(value))}
|
||||||
|
value={String(field.value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
|
<SelectValue placeholder="选择您的套餐" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="0">基础IP套餐</SelectItem>
|
||||||
|
<SelectItem value="1">高级IP套餐</SelectItem>
|
||||||
|
<SelectItem value="2">企业IP套餐</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 地区筛选 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">地区筛选</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="region"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
|
<SelectValue placeholder="选择地区" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="cn">中国大陆</SelectItem>
|
||||||
|
<SelectItem value="hk">香港</SelectItem>
|
||||||
|
<SelectItem value="us">美国</SelectItem>
|
||||||
|
<SelectItem value="all">不限</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 运营商筛选 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">运营商筛选</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="provider"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
|
<SelectValue placeholder="选择运营商" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="telecom">电信</SelectItem>
|
||||||
|
<SelectItem value="mobile">移动</SelectItem>
|
||||||
|
<SelectItem value="unicom">联通</SelectItem>
|
||||||
|
<SelectItem value="all">不限</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 协议类型 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">协议类型</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="proto"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
|
<SelectValue placeholder="选择协议类型" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="http">HTTP</SelectItem>
|
||||||
|
<SelectItem value="https">HTTPS</SelectItem>
|
||||||
|
<SelectItem value="socks5">SOCKS5</SelectItem>
|
||||||
|
<SelectItem value="all">不限</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 去重选项 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">去重选项</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="distinct"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Select onValueChange={field.onChange} value={field.value}>
|
||||||
|
<SelectTrigger className="h-10">
|
||||||
|
<SelectValue placeholder="选择去重方式" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">不去重</SelectItem>
|
||||||
|
<SelectItem value="ip">按IP去重</SelectItem>
|
||||||
|
<SelectItem value="domain">按域名去重</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 导出格式 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">导出格式</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="format"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<RadioGroup
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
className="flex gap-4"
|
||||||
|
>
|
||||||
|
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||||
|
<RadioGroupItem value="txt" id="txt" className="mr-2" />
|
||||||
|
<label htmlFor="txt">TXT格式</label>
|
||||||
|
</div>
|
||||||
|
<div className={`px-4 h-10 border rounded-lg flex items-center`}>
|
||||||
|
<RadioGroupItem value="json" id="json" className="mr-2" />
|
||||||
|
<label htmlFor="json">JSON格式</label>
|
||||||
|
</div>
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 分隔符 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">分隔符</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="separator"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Input {...field} className="h-10" placeholder="输入分隔符,默认为逗号" />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 提取数量 */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<FormLabel className="w-24 flex-shrink-0">提取数量</FormLabel>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="count"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
{...field}
|
||||||
|
onChange={e => field.onChange(Number(e.target.value))}
|
||||||
|
className="h-10"
|
||||||
|
placeholder="输入提取数量"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end mt-6">
|
||||||
|
<Button type="submit" className="w-32 h-10 bg-blue-500 text-white rounded-lg">提取IP</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
19
src/app/(root)/collect/page.tsx
Normal file
19
src/app/(root)/collect/page.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import BreadCrumb from '@/components/bread-crumb'
|
||||||
|
import Wrap from '@/components/wrap'
|
||||||
|
import FormSection from '@/app/(root)/collect/form-section'
|
||||||
|
|
||||||
|
export type CollectPageProps = {}
|
||||||
|
|
||||||
|
export default function CollectPage(props: CollectPageProps) {
|
||||||
|
return (
|
||||||
|
<main className={`mt-20 flex flex-col gap-4`}>
|
||||||
|
<Wrap className="flex flex-col py-8 gap-8">
|
||||||
|
<BreadCrumb items={[
|
||||||
|
{label: 'IP 提取', href: '/collect'},
|
||||||
|
]}/>
|
||||||
|
<h2 className={`text-3xl text-center`}>提取 IP</h2>
|
||||||
|
<FormSection/>
|
||||||
|
</Wrap>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -134,7 +134,7 @@ export default function Header(props: HeaderProps) {
|
|||||||
<Wrap className="h-20 max-md:h-16 flex justify-between">
|
<Wrap className="h-20 max-md:h-16 flex justify-between">
|
||||||
<div className="flex justify-between gap-8">
|
<div className="flex justify-between gap-8">
|
||||||
{/* logo */}
|
{/* logo */}
|
||||||
<Link href="/" className={`flex items-center`}>
|
<Link href="/public" className={`flex items-center`}>
|
||||||
<img src={`/logo.svg`} alt={`logo`} className={`w-16 max-md:w-12 h-16 max-md:h-12 rounded-full bg-gray-100`}/>
|
<img src={`/logo.svg`} alt={`logo`} className={`w-16 max-md:w-12 h-16 max-md:h-12 rounded-full bg-gray-100`}/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
22
src/app/(root)/layout.tsx
Normal file
22
src/app/(root)/layout.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import Header from '@/app/(root)/header'
|
||||||
|
import Footer from '@/app/(root)/footer'
|
||||||
|
import {ReactNode} from 'react'
|
||||||
|
|
||||||
|
export type RootLayoutProps = {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RootLayout(props: RootLayoutProps) {
|
||||||
|
return (
|
||||||
|
<div className={`overflow-auto flex flex-col items-stretch relative`}>
|
||||||
|
{/* 页头 */}
|
||||||
|
<Header/>
|
||||||
|
|
||||||
|
{/* 正文 */}
|
||||||
|
{props.children}
|
||||||
|
|
||||||
|
{/* 页脚 */}
|
||||||
|
<Footer/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
215
src/app/(root)/page.tsx
Normal file
215
src/app/(root)/page.tsx
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
import {ReactNode} from 'react'
|
||||||
|
import Wrap from '@/components/wrap'
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className={`flex flex-col gap-16 lg:gap-32 mb-16 lg:mb-32`}>
|
||||||
|
|
||||||
|
{/* banner */}
|
||||||
|
<section className={`w-full bg-[url('/banner.webp')] bg-cover bg-[center_right_40%]`}>
|
||||||
|
<Wrap className={`pt-64 pb-48 max-md:pt-32 max-md:pb-24`}>
|
||||||
|
<h1 className={`text-4xl font-medium`}>安全,稳定,快速,合规的代理服务器</h1>
|
||||||
|
<p className={`mt-10 text-gray-500`}>遍布全国的代理服务器节点为用户提供智能可靠的IP代理服务</p>
|
||||||
|
|
||||||
|
<div className={`mt-24 max-md:mt-14 flex gap-8 max-md:flex-col`}>
|
||||||
|
<p className={`flex gap-4 items-center`}>
|
||||||
|
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||||
|
<span className={`lg:text-lg font-light`}>全国200+城市节点</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-4 items-center`}>
|
||||||
|
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||||
|
<span className={`lg:text-lg font-light`}>300+城市级精准定位</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-4 items-center`}>
|
||||||
|
<Image src={`/check.svg`} alt={`checkbox`} width={24} height={24}/>
|
||||||
|
<span className={`lg:text-lg font-light`}>低延迟&高并发提取</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={[
|
||||||
|
`mt-32 max-md:mt-20 w-96 max-md:w-full h-16 md:h-24 rounded-lg shadow-lg`,
|
||||||
|
`bg-gradient-to-r from-blue-500 to-cyan-400 text-white text-xl lg:text-4xl font-medium`,
|
||||||
|
].join(' ')}>
|
||||||
|
免费试用
|
||||||
|
</button>
|
||||||
|
</Wrap>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 数据展示 */}
|
||||||
|
<Section title={`覆盖全国的IP资源及超大的带宽线路`}>
|
||||||
|
<ul className={`shadow-[0_0_20px_4px] shadow-blue-50 p-8 flex max-lg:flex-col`}>
|
||||||
|
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||||
|
<p className={`text-xl`}>全国城市线路数量</p>
|
||||||
|
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>350+</p>
|
||||||
|
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||||
|
</li>
|
||||||
|
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||||
|
<p className={`text-xl`}>每日更新IP数量</p>
|
||||||
|
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>1,350,129</p>
|
||||||
|
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||||
|
</li>
|
||||||
|
<li className={`flex-1 flex flex-col items-center justify-center lg:border-r max-lg:mb-4 border-gray-200`}>
|
||||||
|
<p className={`text-xl`}>用户量</p>
|
||||||
|
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>26,578</p>
|
||||||
|
<div className={`lg:hidden w-24 border-b mt-4 border-gray-200`}></div>
|
||||||
|
</li>
|
||||||
|
<li className={`flex-1 flex flex-col items-center justify-center`}>
|
||||||
|
<p className={`text-xl`}>IP可用率</p>
|
||||||
|
<p className={`mt-9 max-lg:mt-2 text-5xl bg-gradient-to-t from-blue-500 to-cyan-400 bg-clip-text text-transparent font-bold pb-2 -mb-2`}>99%</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<img src={`/map.webp`} alt={`map`} className="w-[1200px]"/>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
|
||||||
|
{/* 优势 1 */}
|
||||||
|
<Section title={`HTTP安全合规的代理IP资源池`}>
|
||||||
|
<ul
|
||||||
|
className={[
|
||||||
|
`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8`,
|
||||||
|
].join(' ')}>
|
||||||
|
<Sec3Item
|
||||||
|
icon={`s1-1`} title={`短期动态IP池`} terms={[
|
||||||
|
{icon: `s1-check`, text: `IP时效3-30分钟(可定制)`},
|
||||||
|
{icon: `s1-check`, text: `支持高并发提取`},
|
||||||
|
]}/>
|
||||||
|
<Sec3Item
|
||||||
|
icon={`s1-2`} title={`长期静态IP池`} terms={[
|
||||||
|
{icon: `s1-check`, text: `IP覆盖全国各地`},
|
||||||
|
{icon: `s1-check`, text: `平均响应时长:0.03s`},
|
||||||
|
]}/>
|
||||||
|
<Sec3Item
|
||||||
|
icon={`s1-3`} title={`固定IP池`} terms={[
|
||||||
|
{icon: `s1-check`, text: `稳定长输不掉线`},
|
||||||
|
{icon: `s1-check`, text: `全国热门静态IP线路`},
|
||||||
|
]}/>
|
||||||
|
<Sec3Item
|
||||||
|
icon={`s1-4`} title={`企业级定制池`} terms={[
|
||||||
|
{icon: `s1-check`, text: `可视化监控设计`},
|
||||||
|
{icon: `s1-check`, text: `技术团队现场支持`},
|
||||||
|
]}/>
|
||||||
|
</ul>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 优势 2 */}
|
||||||
|
<Section title={`HTTP 产品优势`}>
|
||||||
|
<div className={`flex gap-36`}>
|
||||||
|
<ul className={`flex-1 flex flex-col gap-6`}>
|
||||||
|
<Sec4Item icon={`s4-1-1`} title={`安全合规`} description={`国内三大运营商支持`}/>
|
||||||
|
<Sec4Item icon={`s4-1-2`} title={`稳定链接`} description={`IP纯净度高达99.9%`}/>
|
||||||
|
<Sec4Item icon={`s4-1-3`} title={`超匿名性`} description={`稳定传输,保护隐私安全`}/>
|
||||||
|
</ul>
|
||||||
|
<img src={`/s4-1-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex gap-36`}>
|
||||||
|
<img src={`/s4-2-main.webp`} alt={`s2-1-main`} className={`w-0 flex-1 object-contain max-lg:hidden`}/>
|
||||||
|
<ul className={`flex-1 flex flex-col gap-6`}>
|
||||||
|
<Sec4Item icon={`s4-2-1`} title={`API接口文档`} description={`与第三方软件轻松集成`}/>
|
||||||
|
<Sec4Item icon={`s4-2-2`} title={`多种编程语言代码`} description={`C语言、GO语言、Python...`}/>
|
||||||
|
<Sec4Item icon={`s4-2-3`} title={`双重认证方式`} description={`API提取+账密认证`}/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
{/* 行业资讯 */}
|
||||||
|
<Section title={`行业资讯`}>
|
||||||
|
<div className={`flex gap-8 max-md:gap-4`}>
|
||||||
|
<button className={`px-4 max-md:-mx-4`}>
|
||||||
|
<img src={`/next.svg`} alt={`prev`} className={`rotate-180`}/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
`shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`,
|
||||||
|
`flex p-14 md:gap-14 max-md:flex-col max-md:p-4`,
|
||||||
|
].join(' ')}>
|
||||||
|
<img src="/s3-main.webp" alt="tumb" className={`w-2/3 md:flex-1 md:w-0 object-cover max-md:self-center`}/>
|
||||||
|
<div className={`flex-2 flex flex-col justify-between gap-4`}>
|
||||||
|
<h3 className={`flex justify-between`}>
|
||||||
|
<span className={`text-xl font-medium`}>我是标题</span>
|
||||||
|
<sub className={`text-sm text-gray-500`}>2025-03-04</sub>
|
||||||
|
</h3>
|
||||||
|
<p className={`text-gray-400 md:leading-12`}>
|
||||||
|
我是内容我是内容我是内容我是内容我是内容我是容我是内容我是内容内容我是内容我是内容我是内我是内容我是内容我是内容我是内容我是内容...
|
||||||
|
</p>
|
||||||
|
<div className={`flex justify-end`}>
|
||||||
|
<a href="#" className={`text-sm text-gray-500 flex items-center gap-4`}>
|
||||||
|
更多详情
|
||||||
|
<img src={`/next.svg`} alt={`more`} className={`h-4 fill-gray-400`}/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className={`px-4 max-md:-mx-4`}>
|
||||||
|
<img src={`/next.svg`} alt={`prev`}/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Section(props: {
|
||||||
|
title: string
|
||||||
|
children: ReactNode
|
||||||
|
}) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className={`max-w-[1232px] mx-auto px-4 flex flex-col items-stretch`}>
|
||||||
|
<h2 className={`text-center text-3xl font-medium mb-8 lg:mb-24`}>{props.title}</h2>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sec3Item(props: {
|
||||||
|
icon: string,
|
||||||
|
title: string,
|
||||||
|
terms: {
|
||||||
|
icon: string,
|
||||||
|
text: string,
|
||||||
|
}[]
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className={[
|
||||||
|
`p-8 flex flex-col gap-5 shadow-[4px_4px_20px_4px] shadow-blue-50 bg-white rounded-lg`,
|
||||||
|
`max-md:items-center`,
|
||||||
|
].join(' ')}>
|
||||||
|
<img src={`/${props.icon}.webp`} alt={`s1-1`} aria-hidden className="w-44 h-44 object-cover"/>
|
||||||
|
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
||||||
|
<div className={`flex flex-col gap-3`}>
|
||||||
|
{props.terms.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<p key={index} className={`text-sm text-gray-500 flex gap-3 items-center`}>
|
||||||
|
<img src={`/${item.icon}.svg`} alt={`check`} aria-hidden className={`w-5 h-5`}/>
|
||||||
|
<span>{item.text}</span>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sec4Item(props: {
|
||||||
|
icon: string
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<li className={`flex gap-8 items-center p-4 lg:p-8 shadow-[4px_4px_20px_4px] shadow-blue-50 rounded-lg`}>
|
||||||
|
<img src={`/${props.icon}.webp`} alt={`s2-1-1`} aria-hidden className="w-24 h-24 object-contain"/>
|
||||||
|
<div className={`flex flex-col gap-3`}>
|
||||||
|
<h3 className={`text-xl font-medium`}>{props.title}</h3>
|
||||||
|
<p>{props.description}</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
43
src/app/(root)/product/combo.tsx
Normal file
43
src/app/(root)/product/combo.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
'use client'
|
||||||
|
import {useState} from 'react'
|
||||||
|
|
||||||
|
export function Combo(props: {
|
||||||
|
name: string
|
||||||
|
level?: {
|
||||||
|
number: number
|
||||||
|
discount: number
|
||||||
|
}[]
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<p className={`flex justify-between items-center`}>
|
||||||
|
<span>{props.name}</span>
|
||||||
|
<button
|
||||||
|
className={`text-gray-500 text-sm`}
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
{open ? '收起' : '展开'}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
{props.level && (
|
||||||
|
<ul className={[
|
||||||
|
`flex flex-col gap-3 overflow-hidden`,
|
||||||
|
`transition-[opacity,padding,max-height] transition-discrete duration-200 ease-in-out`,
|
||||||
|
open
|
||||||
|
? 'delay-[0s, 0s] opacity-100 py-3 max-h-80'
|
||||||
|
: 'delay-[0s, 0.2s] opacity-0 p-0 max-h-0',
|
||||||
|
].join(' ')}>
|
||||||
|
{props.level.map((item, index) => (
|
||||||
|
<li key={index} className={`flex flex-row justify-between items-center`}>
|
||||||
|
<span className={`text-gray-500 text-sm`}>{item.number}</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>赠送 {item.discount} %</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
259
src/app/(root)/product/page.tsx
Normal file
259
src/app/(root)/product/page.tsx
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
import BreadCrumb from '@/components/bread-crumb'
|
||||||
|
import Wrap from '@/components/wrap'
|
||||||
|
import {Combo} from '@/app/(root)/product/combo'
|
||||||
|
|
||||||
|
|
||||||
|
export type ProductPageProps = {}
|
||||||
|
|
||||||
|
export default function ProductPage(props: ProductPageProps) {
|
||||||
|
return (
|
||||||
|
<main className={`mt-20`}>
|
||||||
|
<Wrap className="flex flex-col py-8 gap-8">
|
||||||
|
<BreadCrumb items={[
|
||||||
|
{label: '产品中心', href: '/product'},
|
||||||
|
]}/>
|
||||||
|
|
||||||
|
<h2 className={`text-3xl text-center`}>多种套餐选择</h2>
|
||||||
|
|
||||||
|
<ul role={`tablist`} className={`flex justify-center items-stretch bg-white rounded-lg`}>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button className={`h-14 px-8 text-lg`}>短效动态套餐</button>
|
||||||
|
</li>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button className={`h-14 px-8 text-lg`}>长效静态套餐</button>
|
||||||
|
</li>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button className={`h-14 px-8 text-lg`}>固定套餐</button>
|
||||||
|
</li>
|
||||||
|
<li role={`tab`}>
|
||||||
|
<button className={`h-14 px-8 text-lg`}>定制套餐</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<section role={`tabpanel`} className={`flex flex-row bg-white`}>
|
||||||
|
<Left/>
|
||||||
|
<Center/>
|
||||||
|
<Right/>
|
||||||
|
</section>
|
||||||
|
</Wrap>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Left() {
|
||||||
|
return (
|
||||||
|
<div className="flex-none basis-56 p-8 flex flex-col gap-4">
|
||||||
|
<img src={`/product/banner.webp`} alt={`banner`} className={`w-full`}/>
|
||||||
|
<h3 className={`text-lg`}>包量套餐</h3>
|
||||||
|
<ul className={`flex flex-col gap-3`}>
|
||||||
|
<Combo name={`3分钟`} level={[
|
||||||
|
{number: 30000, discount: 10},
|
||||||
|
{number: 80000, discount: 20},
|
||||||
|
{number: 200000, discount: 30},
|
||||||
|
{number: 450000, discount: 40},
|
||||||
|
{number: 1000000, discount: 50},
|
||||||
|
{number: 1600000, discount: 65},
|
||||||
|
]}/>
|
||||||
|
<Combo name={`5分钟`} level={[
|
||||||
|
{number: 30000, discount: 10},
|
||||||
|
{number: 80000, discount: 20},
|
||||||
|
{number: 200000, discount: 30},
|
||||||
|
{number: 450000, discount: 40},
|
||||||
|
{number: 1000000, discount: 50},
|
||||||
|
{number: 1600000, discount: 65},
|
||||||
|
]}/>
|
||||||
|
<Combo name={`10分钟`} level={[
|
||||||
|
{number: 30000, discount: 10},
|
||||||
|
{number: 80000, discount: 20},
|
||||||
|
{number: 200000, discount: 30},
|
||||||
|
{number: 450000, discount: 40},
|
||||||
|
{number: 1000000, discount: 50},
|
||||||
|
{number: 1600000, discount: 65},
|
||||||
|
]}/>
|
||||||
|
<Combo name={`15分钟`} level={[
|
||||||
|
{number: 30000, discount: 10},
|
||||||
|
{number: 80000, discount: 20},
|
||||||
|
{number: 200000, discount: 30},
|
||||||
|
{number: 450000, discount: 40},
|
||||||
|
{number: 1000000, discount: 50},
|
||||||
|
{number: 1600000, discount: 65},
|
||||||
|
]}/>
|
||||||
|
<Combo name={`30分钟`} level={[
|
||||||
|
{number: 30000, discount: 10},
|
||||||
|
{number: 80000, discount: 20},
|
||||||
|
{number: 200000, discount: 30},
|
||||||
|
{number: 450000, discount: 40},
|
||||||
|
{number: 1000000, discount: 50},
|
||||||
|
{number: 1600000, discount: 65},
|
||||||
|
]}/>
|
||||||
|
</ul>
|
||||||
|
<div className={`border-b border-gray-200`}></div>
|
||||||
|
<h3 className={`text-lg`}>包时套餐</h3>
|
||||||
|
<ul className={`flex flex-col gap-3`}>
|
||||||
|
<li className={`flex justify-between`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>7天</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>9折</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>30天</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>8折</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>90天</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>7折</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>180天</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>6折</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>360天</span>
|
||||||
|
<span className={`text-orange-500 text-xs text-light px-2 py-1 bg-orange-50 rounded-full`}>5折</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Center() {
|
||||||
|
return (
|
||||||
|
<div className={`flex-auto p-8 flex flex-col relative gap-4 `}>
|
||||||
|
|
||||||
|
<h3>计费方式</h3>
|
||||||
|
<div className={`grid grid-cols-2 auto-cols-fr place-items-stretch gap-4`}>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col items-start gap-2 cursor-pointer`}>
|
||||||
|
<h4>包量套餐</h4>
|
||||||
|
<p className={`text-sm text-gray-500`}>适用于短期或不定期高提取业务场景</p>
|
||||||
|
</button>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col items-start gap-2 cursor-pointer`}>
|
||||||
|
<h4>包时套餐</h4>
|
||||||
|
<p className={`text-sm text-gray-500`}>适用于每日提取量稳定的业务场景</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className={`mt-2`}>IP 时效</h3>
|
||||||
|
<div className={`grid grid-cols-5 auto-cols-fr place-items-stretch gap-4`}>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||||
|
<span>3 分钟</span>
|
||||||
|
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||||
|
</button>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||||
|
<span>3 分钟</span>
|
||||||
|
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||||
|
</button>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||||
|
<span>3 分钟</span>
|
||||||
|
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||||
|
</button>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||||
|
<span>3 分钟</span>
|
||||||
|
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||||
|
</button>
|
||||||
|
<button className={`p-4 bg-blue-50 border border-blue-500 rounded-lg flex flex-col gap-2 cursor-pointer`}>
|
||||||
|
<span>3 分钟</span>
|
||||||
|
<span className={`text-sm text-gray-500`}>¥0.005/IP</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 赠送 IP 数 */}
|
||||||
|
<h3 className={`mt-2`}>赠送IP总数</h3>
|
||||||
|
<div className={`flex gap-4`}>
|
||||||
|
<button className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg`}>-</button>
|
||||||
|
<input type="number" className={`w-40 h-10 border border-gray-200 rounded-sm`}/>
|
||||||
|
<button className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg`}>+</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 产品特性 */}
|
||||||
|
<h3 className={`mt-2`}>产品特性</h3>
|
||||||
|
<div className={`grid grid-cols-3 auto-rows-fr gap-y-4`}>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>支持高并发提取</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>指定省份、城市或混播</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>账密+白名单验证</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>完备的API接口</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>IP时效3-30分钟(可定制)</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>IP资源定期筛选</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>完备的API接口</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>包量/包时计费方式</span>
|
||||||
|
</p>
|
||||||
|
<p className={`flex gap-2 items-center`}>
|
||||||
|
<img src={`/product/check.svg`} alt={`check`} aria-hidden className={`w-4 h-4`}/>
|
||||||
|
<span className={`text-sm text-gray-500`}>每日去重量:500万</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 左右的边框 */}
|
||||||
|
<div className={`absolute inset-0 my-8 border-l border-r border-gray-200 pointer-events-none`}></div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Right() {
|
||||||
|
return (
|
||||||
|
<div className={`flex-none basis-80 p-8 flex flex-col gap-4`}>
|
||||||
|
<h3>订单详情</h3>
|
||||||
|
<ul className={`flex flex-col gap-4`}>
|
||||||
|
<li className={`flex justify-between items-center`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>套餐名称</span>
|
||||||
|
<span className={`text-sm`}>包量套餐</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between items-center`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>套餐时长</span>
|
||||||
|
<span className={`text-sm`}>3分钟</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between items-center`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>购买 IP 量</span>
|
||||||
|
<span className={`text-sm`}>1000个</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between items-center`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>实到 IP 量</span>
|
||||||
|
<span className={`text-sm`}>1000个</span>
|
||||||
|
</li>
|
||||||
|
<li className={`flex justify-between items-center`}>
|
||||||
|
<span className={`text-sm text-gray-500`}>原价</span>
|
||||||
|
<span className={`text-sm`}>¥50</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div className={`border-b border-gray-200`}></div>
|
||||||
|
<p className={`flex justify-between items-center`}>
|
||||||
|
<span>实付价格</span>
|
||||||
|
<span className={`text-xl text-orange-500`}>¥50</span>
|
||||||
|
</p>
|
||||||
|
<div className={`flex gap-4`}>
|
||||||
|
<button className={`flex-1 p-3 bg-blue-50 border border-blue-500 rounded-lg flex justify-center items-center gap-2`}>
|
||||||
|
<img src={`/product/alipay.svg`} alt={`alipay`} className={`w-5 h-5`}/>
|
||||||
|
<span className={`text-sm`}>支付宝</span>
|
||||||
|
</button>
|
||||||
|
<button className={`flex-1 p-3 bg-blue-50 border border-blue-500 rounded-lg flex justify-center items-center gap-2`}>
|
||||||
|
<img src={`/product/wechat.svg`} alt={`wechat`} className={`w-5 h-5`}/>
|
||||||
|
<span className={`text-sm`}>微信</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button className={`mt-4 h-12 bg-blue-500 text-white rounded-lg`}>
|
||||||
|
立即支付
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,126 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@plugin "tailwindcss-animate";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: hsl(0, 0%, 20%);
|
color: hsl(0, 0%, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.13 0.028 261.692);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.13 0.028 261.692);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.13 0.028 261.692);
|
||||||
|
--primary: oklch(0.21 0.034 264.665);
|
||||||
|
--primary-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--secondary: oklch(0.967 0.003 264.542);
|
||||||
|
--secondary-foreground: oklch(0.21 0.034 264.665);
|
||||||
|
--muted: oklch(0.967 0.003 264.542);
|
||||||
|
--muted-foreground: oklch(0.551 0.027 264.364);
|
||||||
|
--accent: oklch(0.967 0.003 264.542);
|
||||||
|
--accent-foreground: oklch(0.21 0.034 264.665);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.928 0.006 264.531);
|
||||||
|
--input: oklch(0.928 0.006 264.531);
|
||||||
|
--ring: oklch(0.707 0.022 261.325);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0.002 247.839);
|
||||||
|
--sidebar-foreground: oklch(0.13 0.028 261.692);
|
||||||
|
--sidebar-primary: oklch(0.21 0.034 264.665);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--sidebar-accent: oklch(0.967 0.003 264.542);
|
||||||
|
--sidebar-accent-foreground: oklch(0.21 0.034 264.665);
|
||||||
|
--sidebar-border: oklch(0.928 0.006 264.531);
|
||||||
|
--sidebar-ring: oklch(0.707 0.022 261.325);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.13 0.028 261.692);
|
||||||
|
--foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--card: oklch(0.21 0.034 264.665);
|
||||||
|
--card-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--popover: oklch(0.21 0.034 264.665);
|
||||||
|
--popover-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--primary: oklch(0.928 0.006 264.531);
|
||||||
|
--primary-foreground: oklch(0.21 0.034 264.665);
|
||||||
|
--secondary: oklch(0.278 0.033 256.848);
|
||||||
|
--secondary-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--muted: oklch(0.278 0.033 256.848);
|
||||||
|
--muted-foreground: oklch(0.707 0.022 261.325);
|
||||||
|
--accent: oklch(0.278 0.033 256.848);
|
||||||
|
--accent-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.551 0.027 264.364);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.21 0.034 264.665);
|
||||||
|
--sidebar-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--sidebar-accent: oklch(0.278 0.033 256.848);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0.002 247.839);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.551 0.027 264.364);
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="zh-Cn">
|
<html lang="zh-Cn">
|
||||||
<body className={font.className}>
|
<body className={`${font.className} bg-blue-50`}>
|
||||||
{children}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
63
src/components/bread-crumb.tsx
Normal file
63
src/components/bread-crumb.tsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { ReactNode } from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
export type BreadCrumbItem = {
|
||||||
|
label: string
|
||||||
|
href?: string
|
||||||
|
onClick?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BreadCrumbProps = {
|
||||||
|
items: BreadCrumbItem[]
|
||||||
|
divider?: ReactNode
|
||||||
|
showHomeIcon?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BreadCrumb({
|
||||||
|
items,
|
||||||
|
divider = '/',
|
||||||
|
showHomeIcon = true,
|
||||||
|
className = '',
|
||||||
|
}: BreadCrumbProps) {
|
||||||
|
return (
|
||||||
|
<nav className={`flex items-center text-sm ${className}`} aria-label="Breadcrumb">
|
||||||
|
<ol className="flex items-center space-x-2">
|
||||||
|
{showHomeIcon && (
|
||||||
|
<li className="flex items-center">
|
||||||
|
<Link href="/" className="text-gray-500 hover:text-gray-700 transition-colors">
|
||||||
|
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z" />
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<li key={index} className="flex items-center">
|
||||||
|
{index > 0 || showHomeIcon ? (
|
||||||
|
<span className="mx-2 text-gray-400">{divider}</span>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{item.href ? (
|
||||||
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className="text-gray-600 hover:text-blue-600 transition-colors"
|
||||||
|
onClick={item.onClick}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className="text-gray-900 font-medium"
|
||||||
|
onClick={item.onClick}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
|
||||||
|
|
||||||
export const Dropdown = () => {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<button onClick={() => setIsOpen(!isOpen)}>
|
|
||||||
打开菜单
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<AnimatePresence>
|
|
||||||
{isOpen && (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: -10 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, y: -10 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
>
|
|
||||||
{/* 菜单内容 */}
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
59
src/components/ui/button.tsx
Normal file
59
src/components/ui/button.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
|
outline:
|
||||||
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
|
icon: "size-9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Button({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> &
|
||||||
|
VariantProps<typeof buttonVariants> & {
|
||||||
|
asChild?: boolean
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="button"
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
||||||
167
src/components/ui/form.tsx
Normal file
167
src/components/ui/form.tsx
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
FormProvider,
|
||||||
|
useFormContext,
|
||||||
|
useFormState,
|
||||||
|
type ControllerProps,
|
||||||
|
type FieldPath,
|
||||||
|
type FieldValues,
|
||||||
|
} from "react-hook-form"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Label } from "@/components/ui/label"
|
||||||
|
|
||||||
|
const Form = FormProvider
|
||||||
|
|
||||||
|
type FormFieldContextValue<
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||||
|
> = {
|
||||||
|
name: TName
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||||
|
{} as FormFieldContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormField = <
|
||||||
|
TFieldValues extends FieldValues = FieldValues,
|
||||||
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||||
|
>({
|
||||||
|
...props
|
||||||
|
}: ControllerProps<TFieldValues, TName>) => {
|
||||||
|
return (
|
||||||
|
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||||
|
<Controller {...props} />
|
||||||
|
</FormFieldContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFormField = () => {
|
||||||
|
const fieldContext = React.useContext(FormFieldContext)
|
||||||
|
const itemContext = React.useContext(FormItemContext)
|
||||||
|
const { getFieldState } = useFormContext()
|
||||||
|
const formState = useFormState({ name: fieldContext.name })
|
||||||
|
const fieldState = getFieldState(fieldContext.name, formState)
|
||||||
|
|
||||||
|
if (!fieldContext) {
|
||||||
|
throw new Error("useFormField should be used within <FormField>")
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = itemContext
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: fieldContext.name,
|
||||||
|
formItemId: `${id}-form-item`,
|
||||||
|
formDescriptionId: `${id}-form-item-description`,
|
||||||
|
formMessageId: `${id}-form-item-message`,
|
||||||
|
...fieldState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormItemContextValue = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||||
|
{} as FormItemContextValue
|
||||||
|
)
|
||||||
|
|
||||||
|
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
const id = React.useId()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormItemContext.Provider value={{ id }}>
|
||||||
|
<div
|
||||||
|
data-slot="form-item"
|
||||||
|
className={cn("grid gap-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</FormItemContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormLabel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||||
|
const { error, formItemId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Label
|
||||||
|
data-slot="form-label"
|
||||||
|
data-error={!!error}
|
||||||
|
className={cn("data-[error=true]:text-destructive", className)}
|
||||||
|
htmlFor={formItemId}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
|
||||||
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Slot
|
||||||
|
data-slot="form-control"
|
||||||
|
id={formItemId}
|
||||||
|
aria-describedby={
|
||||||
|
!error
|
||||||
|
? `${formDescriptionId}`
|
||||||
|
: `${formDescriptionId} ${formMessageId}`
|
||||||
|
}
|
||||||
|
aria-invalid={!!error}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
|
||||||
|
const { formDescriptionId } = useFormField()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
data-slot="form-description"
|
||||||
|
id={formDescriptionId}
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
|
||||||
|
const { error, formMessageId } = useFormField()
|
||||||
|
const body = error ? String(error?.message ?? "") : props.children
|
||||||
|
|
||||||
|
if (!body) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
data-slot="form-message"
|
||||||
|
id={formMessageId}
|
||||||
|
className={cn("text-destructive text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{body}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
useFormField,
|
||||||
|
Form,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormMessage,
|
||||||
|
FormField,
|
||||||
|
}
|
||||||
21
src/components/ui/input.tsx
Normal file
21
src/components/ui/input.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
data-slot="input"
|
||||||
|
className={cn(
|
||||||
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||||
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Input }
|
||||||
24
src/components/ui/label.tsx
Normal file
24
src/components/ui/label.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Label({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
data-slot="label"
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Label }
|
||||||
45
src/components/ui/radio-group.tsx
Normal file
45
src/components/ui/radio-group.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||||
|
import { CircleIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function RadioGroup({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<RadioGroupPrimitive.Root
|
||||||
|
data-slot="radio-group"
|
||||||
|
className={cn("grid gap-3", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RadioGroupItem({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
|
||||||
|
return (
|
||||||
|
<RadioGroupPrimitive.Item
|
||||||
|
data-slot="radio-group-item"
|
||||||
|
className={cn(
|
||||||
|
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<RadioGroupPrimitive.Indicator
|
||||||
|
data-slot="radio-group-indicator"
|
||||||
|
className="relative flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
|
</RadioGroupPrimitive.Indicator>
|
||||||
|
</RadioGroupPrimitive.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RadioGroup, RadioGroupItem }
|
||||||
185
src/components/ui/select.tsx
Normal file
185
src/components/ui/select.tsx
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||||
|
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Select({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||||
|
return <SelectPrimitive.Root data-slot="select" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectGroup({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||||
|
return <SelectPrimitive.Group data-slot="select-group" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectValue({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||||
|
return <SelectPrimitive.Value data-slot="select-value" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectTrigger({
|
||||||
|
className,
|
||||||
|
size = "default",
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||||
|
size?: "sm" | "default"
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
data-slot="select-trigger"
|
||||||
|
data-size={size}
|
||||||
|
className={cn(
|
||||||
|
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<ChevronDownIcon className="size-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
position = "popper",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
data-slot="select-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
||||||
|
position === "popper" &&
|
||||||
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
"p-1",
|
||||||
|
position === "popper" &&
|
||||||
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectLabel({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
data-slot="select-label"
|
||||||
|
className={cn("px-2 py-1.5 text-sm font-medium", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectItem({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
data-slot="select-item"
|
||||||
|
className={cn(
|
||||||
|
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute right-2 flex size-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectSeparator({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
data-slot="select-separator"
|
||||||
|
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectScrollUpButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
data-slot="select-scroll-up-button"
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectScrollDownButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
||||||
|
return (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
data-slot="select-scroll-down-button"
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDownIcon className="size-4" />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
}
|
||||||
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user