Compare commits
2 Commits
e287f6c9d2
...
54217e7adf
Author | SHA1 | Date |
---|---|---|
|
54217e7adf | |
|
27ede6c1db |
|
@ -16,9 +16,12 @@
|
|||
"@types/node": "^24.2.0",
|
||||
"@types/react": "^19.1.9",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"recharts": "^3.1.0",
|
||||
"typescript": "^5.9.2",
|
||||
"web-vitals": "^2.1.4"
|
||||
}
|
||||
|
@ -2926,6 +2929,40 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmmirror.com/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
|
||||
"integrity": "sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@standard-schema/utils": "^0.3.0",
|
||||
"immer": "^10.0.3",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"reselect": "^5.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
||||
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit/node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-babel": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||
|
@ -3031,6 +3068,16 @@
|
|||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.0.0.tgz",
|
||||
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="
|
||||
},
|
||||
"node_modules/@standard-schema/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="
|
||||
},
|
||||
"node_modules/@surma/rollup-plugin-off-main-thread": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz",
|
||||
|
@ -3432,6 +3479,60 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.12",
|
||||
"resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.12.tgz",
|
||||
|
@ -3866,6 +3967,11 @@
|
|||
"resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz",
|
||||
|
@ -5484,6 +5590,14 @@
|
|||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz",
|
||||
|
@ -6157,6 +6271,116 @@
|
|||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||
|
@ -6244,6 +6468,11 @@
|
|||
"resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/dedent/-/dedent-0.7.0.tgz",
|
||||
|
@ -6831,6 +7060,15 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/es-toolkit": {
|
||||
"version": "1.39.8",
|
||||
"resolved": "https://registry.npmmirror.com/es-toolkit/-/es-toolkit-1.39.8.tgz",
|
||||
"integrity": "sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==",
|
||||
"workspaces": [
|
||||
"docs",
|
||||
"benchmarks"
|
||||
]
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
|
||||
|
@ -8008,6 +8246,32 @@
|
|||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.23.12",
|
||||
"resolved": "https://registry.npmmirror.com/framer-motion/-/framer-motion-12.23.12.tgz",
|
||||
"integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.23.12",
|
||||
"motion-utils": "^12.23.6",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
|
||||
|
@ -8820,6 +9084,14 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
|
||||
|
@ -10865,6 +11137,19 @@
|
|||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.23.12",
|
||||
"resolved": "https://registry.npmmirror.com/motion-dom/-/motion-dom-12.23.12.tgz",
|
||||
"integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.23.6",
|
||||
"resolved": "https://registry.npmmirror.com/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
|
||||
|
@ -13003,6 +13288,20 @@
|
|||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/react-confetti": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-confetti/-/react-confetti-6.4.0.tgz",
|
||||
"integrity": "sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==",
|
||||
"dependencies": {
|
||||
"tween-functions": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dev-utils": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
|
||||
|
@ -13123,6 +13422,28 @@
|
|||
"resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.2.0.tgz",
|
||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.2.25 || ^19",
|
||||
"react": "^18.0 || ^19",
|
||||
"redux": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.11.0.tgz",
|
||||
|
@ -13235,6 +13556,46 @@
|
|||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/recharts/-/recharts-3.1.0.tgz",
|
||||
"integrity": "sha512-NqAqQcGBmLrfDs2mHX/bz8jJCQtG2FeXfE0GqpZmIuXIjkpIwj8sd9ad0WyvKiBKPd8ZgNG0hL85c8sFDwascw==",
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "1.x.x || 2.x.x",
|
||||
"clsx": "^2.1.1",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"es-toolkit": "^1.39.3",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"immer": "^10.1.1",
|
||||
"react-redux": "8.x.x || 9.x.x",
|
||||
"reselect": "5.1.1",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"use-sync-external-store": "^1.2.2",
|
||||
"victory-vendor": "^37.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts/node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||
},
|
||||
"node_modules/recharts/node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/recursive-readdir": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
||||
|
@ -13258,6 +13619,19 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/redux-thunk/-/redux-thunk-3.1.0.tgz",
|
||||
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
|
||||
"peerDependencies": {
|
||||
"redux": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||
|
@ -13408,6 +13782,11 @@
|
|||
"resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/reselect/-/reselect-5.1.1.tgz",
|
||||
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
|
||||
|
@ -15123,6 +15502,11 @@
|
|||
"resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz",
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
@ -15244,6 +15628,11 @@
|
|||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/tween-functions": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/tween-functions/-/tween-functions-1.2.0.tgz",
|
||||
"integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA=="
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
|
||||
|
@ -15526,6 +15915,14 @@
|
|||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
@ -15592,6 +15989,27 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "37.3.6",
|
||||
"resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-37.3.6.tgz",
|
||||
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-hr-time": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
"@types/node": "^24.2.0",
|
||||
"@types/react": "^19.1.9",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"framer-motion": "^12.23.12",
|
||||
"react": "^19.1.1",
|
||||
"react-confetti": "^6.4.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"recharts": "^3.1.0",
|
||||
"typescript": "^5.9.2",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
|
|
|
@ -100,7 +100,7 @@ input[type="text"]:focus {
|
|||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.App-header {
|
||||
.App-header {
|
||||
padding: 15px !important;
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ input[type="text"]:focus {
|
|||
|
||||
.App-header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
|
@ -150,7 +150,7 @@ a {
|
|||
color: #667eea;
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #764ba2;
|
||||
|
|
14
src/App.tsx
14
src/App.tsx
|
@ -7,8 +7,9 @@ import VueStyleExamples from './examples/VueStyleExamples';
|
|||
import AdvancedConcepts from './examples/AdvancedConcepts';
|
||||
import PerformanceDemo from './examples/PerformanceDemo';
|
||||
import InteractiveGames from './examples/InteractiveGames';
|
||||
import CoolLibrariesDemo from './examples/CoolLibrariesDemo';
|
||||
|
||||
type ViewType = 'jsx-tsx' | 'react-vue' | 'advanced' | 'performance' | 'games';
|
||||
type ViewType = 'jsx-tsx' | 'react-vue' | 'advanced' | 'performance' | 'games' | 'libraries';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [currentView, setCurrentView] = useState<ViewType>('jsx-tsx');
|
||||
|
@ -18,7 +19,8 @@ const App: React.FC = () => {
|
|||
{ key: 'react-vue', label: 'React vs Vue', emoji: '⚔️' },
|
||||
{ key: 'advanced', label: '高级概念', emoji: '🚀' },
|
||||
{ key: 'performance', label: '性能优化', emoji: '⚡' },
|
||||
{ key: 'games', label: '交互游戏', emoji: '🎮' }
|
||||
{ key: 'games', label: '交互游戏', emoji: '🎮' },
|
||||
{ key: 'libraries', label: '炫酷组件库', emoji: '✨' }
|
||||
];
|
||||
|
||||
const renderContent = () => {
|
||||
|
@ -96,6 +98,9 @@ const App: React.FC = () => {
|
|||
case 'games':
|
||||
return <InteractiveGames />;
|
||||
|
||||
case 'libraries':
|
||||
return <CoolLibrariesDemo />;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -106,7 +111,7 @@ const App: React.FC = () => {
|
|||
<header className="App-header" style={{ padding: '20px 20px 10px 20px' }}>
|
||||
<h1 style={{ margin: '0 0 10px 0' }}>React 全面学习演示</h1>
|
||||
<p style={{ margin: '0 0 20px 0', color: '#666' }}>
|
||||
深入理解JSX、TSX、设计思想、高级概念、性能优化和实战应用
|
||||
深入理解JSX、TSX、设计思想、高级概念、性能优化和炫酷组件库
|
||||
</p>
|
||||
|
||||
{/* 导航按钮 */}
|
||||
|
@ -166,6 +171,7 @@ const App: React.FC = () => {
|
|||
{currentView === 'advanced' && '掌握Context、自定义Hooks、HOC、Render Props等高级模式'}
|
||||
{currentView === 'performance' && '学习React性能优化技巧:memo、虚拟滚动、懒加载等'}
|
||||
{currentView === 'games' && '通过交互游戏实践React状态管理和事件处理'}
|
||||
{currentView === 'libraries' && '体验最受欢迎的React组件库:动画、图表、特效等'}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
@ -184,7 +190,7 @@ const App: React.FC = () => {
|
|||
}}>
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<span style={{ marginRight: '20px' }}>🛠️ 技术栈: React 19 + TypeScript</span>
|
||||
<span style={{ marginRight: '20px' }}>📚 包含: 基础语法 + 设计模式 + 性能优化</span>
|
||||
<span style={{ marginRight: '20px' }}>📚 包含: 基础语法 + 设计模式 + 性能优化 + 组件库</span>
|
||||
<span>🎯 目标: 全面掌握React开发</span>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -0,0 +1,634 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import Confetti from 'react-confetti';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, BarChart, Bar, PieChart, Pie, Cell } from 'recharts';
|
||||
|
||||
// ========== 炫酷组件库演示 ==========
|
||||
|
||||
// 1. Framer Motion 动画演示
|
||||
const FramerMotionDemo: React.FC = () => {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [selectedCard, setSelectedCard] = useState<number | null>(null);
|
||||
|
||||
const cards = [
|
||||
{ id: 1, title: '卡片 1', color: '#FF6B6B', icon: '🚀' },
|
||||
{ id: 2, title: '卡片 2', color: '#4ECDC4', icon: '🎨' },
|
||||
{ id: 3, title: '卡片 3', color: '#45B7D1', icon: '⚡' },
|
||||
{ id: 4, title: '卡片 4', color: '#96CEB4', icon: '🌟' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
border: '2px solid #6c5ce7',
|
||||
borderRadius: '12px',
|
||||
margin: '15px',
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
color: 'white'
|
||||
}}>
|
||||
<h3>🎬 Framer Motion 动画库</h3>
|
||||
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => setIsVisible(!isVisible)}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: '#ff7675',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '25px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '16px',
|
||||
marginRight: '10px'
|
||||
}}
|
||||
>
|
||||
{isVisible ? '隐藏动画元素' : '显示动画元素'}
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
{isVisible && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -50 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
style={{
|
||||
padding: '20px',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
borderRadius: '10px',
|
||||
marginBottom: '20px'
|
||||
}}
|
||||
>
|
||||
<h4>✨ 入场/退场动画</h4>
|
||||
<p>这个元素有平滑的进入和退出动画效果!</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
gap: '15px',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
{cards.map((card) => (
|
||||
<motion.div
|
||||
key={card.id}
|
||||
layoutId={`card-${card.id}`}
|
||||
onClick={() => setSelectedCard(selectedCard === card.id ? null : card.id)}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
rotate: selectedCard === card.id ? 0 : 5,
|
||||
transition: { duration: 0.2 }
|
||||
}}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
animate={{
|
||||
backgroundColor: selectedCard === card.id ? '#2d3436' : card.color,
|
||||
scale: selectedCard === card.id ? 1.1 : 1
|
||||
}}
|
||||
style={{
|
||||
padding: '20px',
|
||||
borderRadius: '15px',
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
backgroundColor: card.color
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: selectedCard === card.id ? 360 : 0,
|
||||
scale: selectedCard === card.id ? 1.2 : 1
|
||||
}}
|
||||
transition={{ duration: 0.5 }}
|
||||
style={{ fontSize: '32px', marginBottom: '10px' }}
|
||||
>
|
||||
{card.icon}
|
||||
</motion.div>
|
||||
<h4 style={{ margin: 0, color: 'white' }}>{card.title}</h4>
|
||||
{selectedCard === card.id && (
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
style={{ marginTop: '10px', fontSize: '14px' }}
|
||||
>
|
||||
选中状态!
|
||||
</motion.p>
|
||||
)}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
|
||||
style={{
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
backgroundColor: '#fdcb6e',
|
||||
borderRadius: '50%',
|
||||
margin: '20px auto',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '24px'
|
||||
}}
|
||||
>
|
||||
🌀
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 2. React Confetti 彩纸效果演示
|
||||
const ConfettiDemo: React.FC = () => {
|
||||
const [showConfetti, setShowConfetti] = useState(false);
|
||||
const [confettiConfig, setConfettiConfig] = useState({
|
||||
numberOfPieces: 200,
|
||||
gravity: 0.1
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (showConfetti) {
|
||||
const timer = setTimeout(() => setShowConfetti(false), 5000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [showConfetti]);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
border: '2px solid #00b894',
|
||||
borderRadius: '12px',
|
||||
margin: '15px',
|
||||
background: 'linear-gradient(135deg, #00b894 0%, #00cec9 100%)',
|
||||
color: 'white',
|
||||
position: 'relative'
|
||||
}}>
|
||||
{showConfetti && (
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
numberOfPieces={confettiConfig.numberOfPieces}
|
||||
gravity={confettiConfig.gravity}
|
||||
style={{ position: 'fixed', top: 0, left: 0, zIndex: 1000 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<h3>🎉 React Confetti 彩纸效果</h3>
|
||||
<p>点击按钮释放彩纸庆祝效果!</p>
|
||||
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
<label style={{ display: 'block', marginBottom: '10px' }}>
|
||||
彩纸数量: {confettiConfig.numberOfPieces}
|
||||
<input
|
||||
type="range"
|
||||
min="50"
|
||||
max="500"
|
||||
value={confettiConfig.numberOfPieces}
|
||||
onChange={(e) => setConfettiConfig(prev => ({
|
||||
...prev,
|
||||
numberOfPieces: Number(e.target.value)
|
||||
}))}
|
||||
style={{ marginLeft: '10px', width: '200px' }}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label style={{ display: 'block', marginBottom: '15px' }}>
|
||||
重力: {confettiConfig.gravity}
|
||||
<input
|
||||
type="range"
|
||||
min="0.05"
|
||||
max="0.3"
|
||||
step="0.01"
|
||||
value={confettiConfig.gravity}
|
||||
onChange={(e) => setConfettiConfig(prev => ({
|
||||
...prev,
|
||||
gravity: Number(e.target.value)
|
||||
}))}
|
||||
style={{ marginLeft: '10px', width: '200px' }}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => setShowConfetti(true)}
|
||||
disabled={showConfetti}
|
||||
style={{
|
||||
padding: '15px 30px',
|
||||
backgroundColor: showConfetti ? '#95a5a6' : '#e74c3c',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '25px',
|
||||
cursor: showConfetti ? 'not-allowed' : 'pointer',
|
||||
fontSize: '18px',
|
||||
fontWeight: 'bold'
|
||||
}}
|
||||
>
|
||||
{showConfetti ? '彩纸飞舞中...' : '🎊 释放彩纸!'}
|
||||
</motion.button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 3. Recharts 图表演示
|
||||
const ChartsDemo: React.FC = () => {
|
||||
const [chartType, setChartType] = useState<'line' | 'bar' | 'pie'>('line');
|
||||
|
||||
const lineData = [
|
||||
{ name: '1月', 用户: 400, 销售: 240, 访问: 300 },
|
||||
{ name: '2月', 用户: 300, 销售: 139, 访问: 200 },
|
||||
{ name: '3月', 用户: 200, 销售: 980, 访问: 400 },
|
||||
{ name: '4月', 用户: 278, 销售: 390, 访问: 350 },
|
||||
{ name: '5月', 用户: 189, 销售: 480, 访问: 200 },
|
||||
{ name: '6月', 用户: 239, 销售: 380, 访问: 500 },
|
||||
];
|
||||
|
||||
const pieData = [
|
||||
{ name: 'React', value: 35, color: '#61dafb' },
|
||||
{ name: 'Vue', value: 25, color: '#4fc08d' },
|
||||
{ name: 'Angular', value: 20, color: '#dd1b16' },
|
||||
{ name: 'Svelte', value: 10, color: '#ff3e00' },
|
||||
{ name: '其他', value: 10, color: '#9ca3af' }
|
||||
];
|
||||
|
||||
const renderChart = () => {
|
||||
switch (chartType) {
|
||||
case 'line':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<LineChart data={lineData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="用户" stroke="#8884d8" strokeWidth={2} />
|
||||
<Line type="monotone" dataKey="销售" stroke="#82ca9d" strokeWidth={2} />
|
||||
<Line type="monotone" dataKey="访问" stroke="#ffc658" strokeWidth={2} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'bar':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={lineData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="用户" fill="#8884d8" />
|
||||
<Bar dataKey="销售" fill="#82ca9d" />
|
||||
<Bar dataKey="访问" fill="#ffc658" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
case 'pie':
|
||||
return (
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={pieData}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
labelLine={false}
|
||||
label={({ name, percent }) => `${name} ${((percent || 0) * 100).toFixed(0)}%`}
|
||||
outerRadius={80}
|
||||
fill="#8884d8"
|
||||
dataKey="value"
|
||||
>
|
||||
{pieData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
border: '2px solid #e17055',
|
||||
borderRadius: '12px',
|
||||
margin: '15px',
|
||||
backgroundColor: '#f8f9fa'
|
||||
}}>
|
||||
<h3 style={{ color: '#2d3436' }}>📊 Recharts 图表库</h3>
|
||||
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
{(['line', 'bar', 'pie'] as const).map((type) => (
|
||||
<motion.button
|
||||
key={type}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => setChartType(type)}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
marginRight: '10px',
|
||||
backgroundColor: chartType === type ? '#e17055' : '#ddd',
|
||||
color: chartType === type ? 'white' : '#333',
|
||||
border: 'none',
|
||||
borderRadius: '20px',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
{type === 'line' ? '📈 折线图' : type === 'bar' ? '📊 柱状图' : '🥧 饼图'}
|
||||
</motion.button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
key={chartType}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
{renderChart()}
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 4. 炫酷加载动画演示
|
||||
const LoadingAnimationsDemo: React.FC = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const startLoading = () => {
|
||||
setIsLoading(true);
|
||||
setTimeout(() => setIsLoading(false), 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
border: '2px solid #a29bfe',
|
||||
borderRadius: '12px',
|
||||
margin: '15px',
|
||||
background: 'linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%)',
|
||||
color: 'white'
|
||||
}}>
|
||||
<h3>⏳ 炫酷加载动画</h3>
|
||||
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={startLoading}
|
||||
disabled={isLoading}
|
||||
style={{
|
||||
padding: '12px 24px',
|
||||
backgroundColor: isLoading ? '#95a5a6' : '#fd79a8',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '25px',
|
||||
cursor: isLoading ? 'not-allowed' : 'pointer',
|
||||
fontSize: '16px',
|
||||
marginBottom: '20px'
|
||||
}}
|
||||
>
|
||||
{isLoading ? '加载中...' : '开始加载'}
|
||||
</motion.button>
|
||||
|
||||
<AnimatePresence>
|
||||
{isLoading && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
style={{ textAlign: 'center' }}
|
||||
>
|
||||
{/* 旋转圆环 */}
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
||||
style={{
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
border: '4px solid rgba(255,255,255,0.3)',
|
||||
borderTop: '4px solid white',
|
||||
borderRadius: '50%',
|
||||
margin: '20px auto'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 脉冲圆点 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'center', gap: '10px', marginBottom: '20px' }}>
|
||||
{[0, 1, 2].map((i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
animate={{
|
||||
scale: [1, 1.5, 1],
|
||||
opacity: [0.5, 1, 0.5]
|
||||
}}
|
||||
transition={{
|
||||
duration: 1,
|
||||
repeat: Infinity,
|
||||
delay: i * 0.2
|
||||
}}
|
||||
style={{
|
||||
width: '15px',
|
||||
height: '15px',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 波浪进度条 */}
|
||||
<div style={{
|
||||
width: '200px',
|
||||
height: '6px',
|
||||
backgroundColor: 'rgba(255,255,255,0.3)',
|
||||
borderRadius: '3px',
|
||||
margin: '0 auto',
|
||||
overflow: 'hidden'
|
||||
}}>
|
||||
<motion.div
|
||||
animate={{ x: [-200, 200] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100%',
|
||||
background: 'linear-gradient(90deg, transparent, white, transparent)',
|
||||
borderRadius: '3px'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 5. 交互式卡片网格
|
||||
const InteractiveCardsDemo: React.FC = () => {
|
||||
const [hoveredCard, setHoveredCard] = useState<number | null>(null);
|
||||
|
||||
const features = [
|
||||
{ id: 1, title: '高性能', icon: '⚡', description: '极致的性能优化' },
|
||||
{ id: 2, title: '易使用', icon: '🎯', description: '简单直观的API' },
|
||||
{ id: 3, title: '可定制', icon: '🎨', description: '灵活的主题配置' },
|
||||
{ id: 4, title: '响应式', icon: '📱', description: '完美适配各种设备' },
|
||||
{ id: 5, title: '现代化', icon: '🚀', description: '最新技术栈支持' },
|
||||
{ id: 6, title: '文档完善', icon: '📚', description: '详细的使用指南' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
border: '2px solid #00cec9',
|
||||
borderRadius: '12px',
|
||||
margin: '15px',
|
||||
backgroundColor: '#f8f9fa'
|
||||
}}>
|
||||
<h3 style={{ color: '#2d3436', textAlign: 'center' }}>🎴 交互式卡片网格</h3>
|
||||
|
||||
<motion.div
|
||||
layout
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
||||
gap: '20px',
|
||||
marginTop: '20px'
|
||||
}}
|
||||
>
|
||||
{features.map((feature) => (
|
||||
<motion.div
|
||||
key={feature.id}
|
||||
layout
|
||||
onHoverStart={() => setHoveredCard(feature.id)}
|
||||
onHoverEnd={() => setHoveredCard(null)}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
rotateY: 5,
|
||||
z: 50
|
||||
}}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
style={{
|
||||
padding: '20px',
|
||||
backgroundColor: hoveredCard === feature.id ? '#00cec9' : 'white',
|
||||
color: hoveredCard === feature.id ? 'white' : '#2d3436',
|
||||
borderRadius: '15px',
|
||||
boxShadow: hoveredCard === feature.id
|
||||
? '0 10px 30px rgba(0, 206, 201, 0.3)'
|
||||
: '0 5px 15px rgba(0,0,0,0.1)',
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
transition: 'all 0.3s ease'
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
animate={{
|
||||
scale: hoveredCard === feature.id ? 1.2 : 1,
|
||||
rotate: hoveredCard === feature.id ? 360 : 0
|
||||
}}
|
||||
transition={{ duration: 0.3 }}
|
||||
style={{ fontSize: '32px', marginBottom: '10px' }}
|
||||
>
|
||||
{feature.icon}
|
||||
</motion.div>
|
||||
<h4 style={{ margin: '10px 0' }}>{feature.title}</h4>
|
||||
<AnimatePresence>
|
||||
{hoveredCard === feature.id && (
|
||||
<motion.p
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
style={{ margin: 0, fontSize: '14px' }}
|
||||
>
|
||||
{feature.description}
|
||||
</motion.p>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 主组件
|
||||
const CoolLibrariesDemo: React.FC = () => {
|
||||
return (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<h2 style={{ textAlign: 'center', color: '#2d3436', marginBottom: '10px' }}>
|
||||
✨ 炫酷React组件库演示
|
||||
</h2>
|
||||
<p style={{ textAlign: 'center', color: '#636e72', marginBottom: '30px' }}>
|
||||
探索最受欢迎的React UI库和动画库,提升用户体验
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<FramerMotionDemo />
|
||||
<ConfettiDemo />
|
||||
<ChartsDemo />
|
||||
<LoadingAnimationsDemo />
|
||||
<InteractiveCardsDemo />
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
style={{
|
||||
marginTop: '30px',
|
||||
padding: '25px',
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
borderRadius: '15px',
|
||||
color: 'white',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<h3>🎯 组件库推荐总结</h3>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
||||
gap: '20px',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<div>
|
||||
<h4>🎬 动画库</h4>
|
||||
<ul style={{ textAlign: 'left', lineHeight: '1.8' }}>
|
||||
<li><strong>Framer Motion</strong>: 最强大的React动画库</li>
|
||||
<li><strong>React Spring</strong>: 基于物理的动画</li>
|
||||
<li><strong>React Transition Group</strong>: 过渡动画</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>🎨 UI组件库</h4>
|
||||
<ul style={{ textAlign: 'left', lineHeight: '1.8' }}>
|
||||
<li><strong>Ant Design</strong>: 企业级UI设计语言</li>
|
||||
<li><strong>Material-UI</strong>: Google Material Design</li>
|
||||
<li><strong>Chakra UI</strong>: 简单、模块化、易用</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>📊 数据可视化</h4>
|
||||
<ul style={{ textAlign: 'left', lineHeight: '1.8' }}>
|
||||
<li><strong>Recharts</strong>: 基于D3的图表库</li>
|
||||
<li><strong>Victory</strong>: 模块化图表库</li>
|
||||
<li><strong>React-vis</strong>: Uber开源图表库</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CoolLibrariesDemo;
|
Loading…
Reference in New Issue