Compare commits

...

2 Commits

Author SHA1 Message Date
Guwan 54217e7adf fix: first commit 2025-08-05 00:18:38 +08:00
Guwan 27ede6c1db fix: first commit 2025-08-05 00:17:52 +08:00
5 changed files with 1068 additions and 7 deletions

418
package-lock.json generated
View File

@ -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",

View File

@ -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"
},

View File

@ -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;

View File

@ -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' }}>
JSXTSX
JSXTSX
</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>

View File

@ -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;