mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
feat: swith to pragmatic-drag-and-drop for better performance
This commit is contained in:
parent
aee0e936b6
commit
a1d25f7d6f
@ -25,13 +25,14 @@
|
|||||||
"test": "vitest -c vitest.config.ts"
|
"test": "vitest -c vitest.config.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.1.0",
|
"@atlaskit/pragmatic-drag-and-drop": "^1.4.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.4.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3",
|
||||||
"@iconify-json/si": "^1.2.1",
|
"@iconify-json/si": "^1.2.1",
|
||||||
"@tanstack/react-query-devtools": "^5.59.9",
|
"@tanstack/react-query-devtools": "^5.59.9",
|
||||||
"@tanstack/react-router": "^1.64.0",
|
"@tanstack/react-router": "^1.64.0",
|
||||||
"@unocss/reset": "^0.63.4",
|
"@unocss/reset": "^0.63.4",
|
||||||
|
"ahooks": "^3.8.1",
|
||||||
"better-sqlite3": "^11.3.0",
|
"better-sqlite3": "^11.3.0",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
138
pnpm-lock.yaml
generated
138
pnpm-lock.yaml
generated
@ -17,15 +17,15 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dnd-kit/core':
|
'@atlaskit/pragmatic-drag-and-drop':
|
||||||
specifier: ^6.1.0
|
specifier: ^1.4.0
|
||||||
version: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.4.0
|
||||||
'@dnd-kit/sortable':
|
'@atlaskit/pragmatic-drag-and-drop-auto-scroll':
|
||||||
specifier: ^8.0.0
|
specifier: ^1.4.0
|
||||||
version: 8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
version: 1.4.0
|
||||||
'@dnd-kit/utilities':
|
'@atlaskit/pragmatic-drag-and-drop-hitbox':
|
||||||
specifier: ^3.2.2
|
specifier: ^1.0.3
|
||||||
version: 3.2.2(react@18.3.1)
|
version: 1.0.3
|
||||||
'@iconify-json/si':
|
'@iconify-json/si':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
@ -38,6 +38,9 @@ importers:
|
|||||||
'@unocss/reset':
|
'@unocss/reset':
|
||||||
specifier: ^0.63.4
|
specifier: ^0.63.4
|
||||||
version: 0.63.6
|
version: 0.63.6
|
||||||
|
ahooks:
|
||||||
|
specifier: ^3.8.1
|
||||||
|
version: 3.8.1(react@18.3.1)
|
||||||
better-sqlite3:
|
better-sqlite3:
|
||||||
specifier: ^11.3.0
|
specifier: ^11.3.0
|
||||||
version: 11.5.0
|
version: 11.5.0
|
||||||
@ -244,6 +247,15 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
ajv: '>=8'
|
ajv: '>=8'
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0':
|
||||||
|
resolution: {integrity: sha512-5GoikoTSW13UX76F9TDeWB8x3jbbGlp/Y+3aRkHe1MOBMkrWkwNpJ42MIVhhX/6NSeaZiPumP0KbGJVs2tOWSQ==}
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop-hitbox@1.0.3':
|
||||||
|
resolution: {integrity: sha512-/Sbu/HqN2VGLYBhnsG7SbRNg98XKkbF6L7XDdBi+izRybfaK1FeMfodPpm/xnBHPJzwYMdkE0qtLyv6afhgMUA==}
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop@1.4.0':
|
||||||
|
resolution: {integrity: sha512-qRY3PTJIcxfl/QB8Gwswz+BRvlmgAC5pB+J2hL6dkIxgqAgVwOhAamMUKsrOcFU/axG2Q7RbNs1xfoLKDuhoPg==}
|
||||||
|
|
||||||
'@babel/code-frame@7.25.9':
|
'@babel/code-frame@7.25.9':
|
||||||
resolution: {integrity: sha512-z88xeGxnzehn2sqZ8UdGQEvYErF1odv2CftxInpSYJt6uHuPe9YjahKZITGs3l5LeI9d2ROG+obuDAoSlqbNfQ==}
|
resolution: {integrity: sha512-z88xeGxnzehn2sqZ8UdGQEvYErF1odv2CftxInpSYJt6uHuPe9YjahKZITGs3l5LeI9d2ROG+obuDAoSlqbNfQ==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@ -801,28 +813,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.0':
|
|
||||||
resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/core@6.1.0':
|
|
||||||
resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
react-dom: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/sortable@8.0.0':
|
|
||||||
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
|
|
||||||
peerDependencies:
|
|
||||||
'@dnd-kit/core': ^6.1.0
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@dnd-kit/utilities@3.2.2':
|
|
||||||
resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=16.8.0'
|
|
||||||
|
|
||||||
'@es-joy/jsdoccomment@0.48.0':
|
'@es-joy/jsdoccomment@0.48.0':
|
||||||
resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==}
|
resolution: {integrity: sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
@ -2634,6 +2624,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
|
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
ahooks@3.8.1:
|
||||||
|
resolution: {integrity: sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA==}
|
||||||
|
engines: {node: '>=8.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
|
||||||
@ -2765,6 +2761,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
bind-event-listener@3.0.0:
|
||||||
|
resolution: {integrity: sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||||
|
|
||||||
@ -4064,6 +4063,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
|
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
intersection-observer@0.12.2:
|
||||||
|
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
|
||||||
|
|
||||||
invariant@2.2.4:
|
invariant@2.2.4:
|
||||||
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
|
||||||
|
|
||||||
@ -4299,6 +4301,10 @@ packages:
|
|||||||
js-cookie@2.2.1:
|
js-cookie@2.2.1:
|
||||||
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
|
resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
|
||||||
|
|
||||||
|
js-cookie@3.0.5:
|
||||||
|
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
js-levenshtein@1.1.6:
|
js-levenshtein@1.1.6:
|
||||||
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -4983,6 +4989,9 @@ packages:
|
|||||||
radix3@1.1.2:
|
radix3@1.1.2:
|
||||||
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
|
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
|
||||||
|
|
||||||
|
raf-schd@4.0.3:
|
||||||
|
resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
|
||||||
|
|
||||||
randombytes@2.1.0:
|
randombytes@2.1.0:
|
||||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||||
|
|
||||||
@ -5002,6 +5011,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.3.1
|
react: ^18.3.1
|
||||||
|
|
||||||
|
react-fast-compare@3.2.2:
|
||||||
|
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
||||||
|
|
||||||
react-remove-scroll-bar@2.3.6:
|
react-remove-scroll-bar@2.3.6:
|
||||||
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -6247,6 +6259,22 @@ snapshots:
|
|||||||
jsonpointer: 5.0.1
|
jsonpointer: 5.0.1
|
||||||
leven: 3.1.0
|
leven: 3.1.0
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop-auto-scroll@1.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop': 1.4.0
|
||||||
|
'@babel/runtime': 7.25.9
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop-hitbox@1.0.3':
|
||||||
|
dependencies:
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop': 1.4.0
|
||||||
|
'@babel/runtime': 7.25.9
|
||||||
|
|
||||||
|
'@atlaskit/pragmatic-drag-and-drop@1.4.0':
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.25.9
|
||||||
|
bind-event-listener: 3.0.0
|
||||||
|
raf-schd: 4.0.3
|
||||||
|
|
||||||
'@babel/code-frame@7.25.9':
|
'@babel/code-frame@7.25.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/highlight': 7.25.9
|
'@babel/highlight': 7.25.9
|
||||||
@ -6954,31 +6982,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.0(react@18.3.1)':
|
|
||||||
dependencies:
|
|
||||||
react: 18.3.1
|
|
||||||
tslib: 2.8.0
|
|
||||||
|
|
||||||
'@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
|
||||||
dependencies:
|
|
||||||
'@dnd-kit/accessibility': 3.1.0(react@18.3.1)
|
|
||||||
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
|
|
||||||
react: 18.3.1
|
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
|
||||||
tslib: 2.8.0
|
|
||||||
|
|
||||||
'@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
|
|
||||||
dependencies:
|
|
||||||
'@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
|
||||||
'@dnd-kit/utilities': 3.2.2(react@18.3.1)
|
|
||||||
react: 18.3.1
|
|
||||||
tslib: 2.8.0
|
|
||||||
|
|
||||||
'@dnd-kit/utilities@3.2.2(react@18.3.1)':
|
|
||||||
dependencies:
|
|
||||||
react: 18.3.1
|
|
||||||
tslib: 2.8.0
|
|
||||||
|
|
||||||
'@es-joy/jsdoccomment@0.48.0':
|
'@es-joy/jsdoccomment@0.48.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
comment-parser: 1.4.1
|
comment-parser: 1.4.1
|
||||||
@ -8694,6 +8697,19 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
ahooks@3.8.1(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.25.9
|
||||||
|
dayjs: 1.11.13(patch_hash=4wu3h3hwxidwuv4ovjl53zavle)
|
||||||
|
intersection-observer: 0.12.2
|
||||||
|
js-cookie: 3.0.5
|
||||||
|
lodash: 4.17.21
|
||||||
|
react: 18.3.1
|
||||||
|
react-fast-compare: 3.2.2
|
||||||
|
resize-observer-polyfill: 1.5.1
|
||||||
|
screenfull: 5.2.0
|
||||||
|
tslib: 2.8.0
|
||||||
|
|
||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
@ -8849,6 +8865,8 @@ snapshots:
|
|||||||
|
|
||||||
binary-extensions@2.3.0: {}
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
|
bind-event-listener@3.0.0: {}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
file-uri-to-path: 1.0.0
|
file-uri-to-path: 1.0.0
|
||||||
@ -10437,6 +10455,8 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
side-channel: 1.0.6
|
side-channel: 1.0.6
|
||||||
|
|
||||||
|
intersection-observer@0.12.2: {}
|
||||||
|
|
||||||
invariant@2.2.4:
|
invariant@2.2.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify: 1.4.0
|
loose-envify: 1.4.0
|
||||||
@ -10641,6 +10661,8 @@ snapshots:
|
|||||||
|
|
||||||
js-cookie@2.2.1: {}
|
js-cookie@2.2.1: {}
|
||||||
|
|
||||||
|
js-cookie@3.0.5: {}
|
||||||
|
|
||||||
js-levenshtein@1.1.6: {}
|
js-levenshtein@1.1.6: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
@ -11407,6 +11429,8 @@ snapshots:
|
|||||||
|
|
||||||
radix3@1.1.2: {}
|
radix3@1.1.2: {}
|
||||||
|
|
||||||
|
raf-schd@4.0.3: {}
|
||||||
|
|
||||||
randombytes@2.1.0:
|
randombytes@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
@ -11431,6 +11455,8 @@ snapshots:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
scheduler: 0.23.2
|
scheduler: 0.23.2
|
||||||
|
|
||||||
|
react-fast-compare@3.2.2: {}
|
||||||
|
|
||||||
react-remove-scroll-bar@2.3.6(@types/react@18.3.12)(react@18.3.1):
|
react-remove-scroll-bar@2.3.6(@types/react@18.3.12)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { NewsItem, SourceID, SourceResponse } from "@shared/types"
|
import type { NewsItem, SourceID, SourceResponse } from "@shared/types"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import { AnimatePresence, motion, useInView } from "framer-motion"
|
import { AnimatePresence, motion, useInView } from "framer-motion"
|
||||||
import type { SyntheticListenerMap } from "@dnd-kit/core/dist/hooks/utilities"
|
|
||||||
import { useWindowSize } from "react-use"
|
import { useWindowSize } from "react-use"
|
||||||
import { forwardRef, useImperativeHandle } from "react"
|
import { forwardRef, useImperativeHandle } from "react"
|
||||||
import { OverlayScrollbar } from "../common/overlay-scrollbar"
|
import { OverlayScrollbar } from "../common/overlay-scrollbar"
|
||||||
@ -12,23 +11,23 @@ export interface ItemsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|||||||
/**
|
/**
|
||||||
* 是否显示透明度,拖动时原卡片的样式
|
* 是否显示透明度,拖动时原卡片的样式
|
||||||
*/
|
*/
|
||||||
isDragged?: boolean
|
isDragging?: boolean
|
||||||
handleListeners?: SyntheticListenerMap
|
setHandleRef?: (ref: HTMLElement | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NewsCardProps {
|
interface NewsCardProps {
|
||||||
id: SourceID
|
id: SourceID
|
||||||
handleListeners?: SyntheticListenerMap
|
setHandleRef?: (ref: HTMLElement | null) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragged, handleListeners, style, ...props }, dndRef) => {
|
export const CardWrapper = forwardRef<HTMLElement, ItemsProps>(({ id, isDragging, setHandleRef, style, ...props }, dndRef) => {
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const inView = useInView(ref, {
|
const inView = useInView(ref, {
|
||||||
once: true,
|
once: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
useImperativeHandle(dndRef, () => ref.current!)
|
useImperativeHandle(dndRef, () => ref.current! as HTMLDivElement)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -36,7 +35,7 @@ export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragg
|
|||||||
className={$(
|
className={$(
|
||||||
"flex flex-col h-500px rounded-2xl p-4 cursor-default",
|
"flex flex-col h-500px rounded-2xl p-4 cursor-default",
|
||||||
"backdrop-blur-5 transition-opacity-300",
|
"backdrop-blur-5 transition-opacity-300",
|
||||||
isDragged && "op-50",
|
isDragging && "op-50",
|
||||||
`bg-${sources[id].color}-500 dark:bg-${sources[id].color} bg-op-40!`,
|
`bg-${sources[id].color}-500 dark:bg-${sources[id].color} bg-op-40!`,
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
@ -45,12 +44,12 @@ export const CardWrapper = forwardRef<HTMLDivElement, ItemsProps>(({ id, isDragg
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{inView && <NewsCard id={id} handleListeners={handleListeners} />}
|
{inView && <NewsCard id={id} setHandleRef={setHandleRef} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
function NewsCard({ id, handleListeners }: NewsCardProps) {
|
function NewsCard({ id, setHandleRef }: NewsCardProps) {
|
||||||
const { refresh } = useRefetch()
|
const { refresh } = useRefetch()
|
||||||
const { data, isFetching, isError } = useQuery({
|
const { data, isFetching, isError } = useQuery({
|
||||||
queryKey: ["source", id],
|
queryKey: ["source", id],
|
||||||
@ -141,9 +140,9 @@ function NewsCard({ id, handleListeners }: NewsCardProps) {
|
|||||||
className={$("btn", isFocused ? "i-ph:star-fill" : "i-ph:star-duotone")}
|
className={$("btn", isFocused ? "i-ph:star-fill" : "i-ph:star-duotone")}
|
||||||
onClick={toggleFocus}
|
onClick={toggleFocus}
|
||||||
/>
|
/>
|
||||||
{handleListeners && (
|
{setHandleRef && (
|
||||||
<button
|
<button
|
||||||
{...handleListeners}
|
ref={setHandleRef}
|
||||||
type="button"
|
type="button"
|
||||||
className={$("btn", "i-ph:dots-six-vertical-duotone", "cursor-grab")}
|
className={$("btn", "i-ph:dots-six-vertical-duotone", "cursor-grab")}
|
||||||
/>
|
/>
|
||||||
|
@ -1,26 +1,18 @@
|
|||||||
import type { PropsWithChildren } from "react"
|
import type { PropsWithChildren } from "react"
|
||||||
import { useCallback, useState } from "react"
|
|
||||||
import type { DragEndEvent, DragStartEvent } from "@dnd-kit/core"
|
|
||||||
import {
|
|
||||||
DndContext,
|
|
||||||
DragOverlay,
|
|
||||||
MeasuringStrategy,
|
|
||||||
MouseSensor,
|
|
||||||
TouchSensor,
|
|
||||||
closestCenter,
|
|
||||||
defaultDropAnimationSideEffects,
|
|
||||||
useSensor,
|
|
||||||
useSensors,
|
|
||||||
} from "@dnd-kit/core"
|
|
||||||
import type { AnimateLayoutChanges } from "@dnd-kit/sortable"
|
|
||||||
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, rectSortingStrategy, useSortable } from "@dnd-kit/sortable"
|
|
||||||
import type { SourceID } from "@shared/types"
|
import type { SourceID } from "@shared/types"
|
||||||
import { CSS } from "@dnd-kit/utilities"
|
|
||||||
import { motion } from "framer-motion"
|
import { motion } from "framer-motion"
|
||||||
|
import type { BaseEventPayload, ElementDragType } from "@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types"
|
||||||
|
import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"
|
||||||
|
import { reorderWithEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/util/reorder-with-edge"
|
||||||
|
import { createPortal } from "react-dom"
|
||||||
|
import { useThrottleFn } from "ahooks"
|
||||||
|
import { DndContext } from "../common/dnd"
|
||||||
|
import { useSortable } from "../common/dnd/useSortable"
|
||||||
import type { ItemsProps } from "./card"
|
import type { ItemsProps } from "./card"
|
||||||
import { CardWrapper } from "./card"
|
import { CardWrapper } from "./card"
|
||||||
import { currentSourcesAtom } from "~/atoms"
|
import { currentSourcesAtom } from "~/atoms"
|
||||||
|
|
||||||
|
const AnimationDuration = 200
|
||||||
export function Dnd() {
|
export function Dnd() {
|
||||||
const [items, setItems] = useAtom(currentSourcesAtom)
|
const [items, setItems] = useAtom(currentSourcesAtom)
|
||||||
useEntireQuery(items)
|
useEntireQuery(items)
|
||||||
@ -50,17 +42,17 @@ export function Dnd() {
|
|||||||
{items.map(id => (
|
{items.map(id => (
|
||||||
<motion.li
|
<motion.li
|
||||||
key={id}
|
key={id}
|
||||||
|
layout
|
||||||
transition={{
|
transition={{
|
||||||
type: "tween",
|
type: "tween",
|
||||||
|
duration: AnimationDuration / 1000,
|
||||||
}}
|
}}
|
||||||
variants={{
|
variants={{
|
||||||
hidden: {
|
hidden: {
|
||||||
y: 20,
|
y: 20,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
display: "none",
|
|
||||||
},
|
},
|
||||||
visible: {
|
visible: {
|
||||||
display: "block",
|
|
||||||
y: 0,
|
y: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
},
|
},
|
||||||
@ -74,65 +66,35 @@ export function Dnd() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DndProps {
|
function DndWrapper({ items, setItems, children }: PropsWithChildren<{
|
||||||
items: SourceID[]
|
items: SourceID[]
|
||||||
setItems: (update: SourceID[]) => void
|
setItems: (items: SourceID[]) => void
|
||||||
}
|
}>) {
|
||||||
|
const onDropTargetChange = useCallback(({ location, source }: BaseEventPayload<ElementDragType>) => {
|
||||||
function DndWrapper({ items, setItems, children }: PropsWithChildren<DndProps>) {
|
const traget = location.current.dropTargets[0]
|
||||||
const [activeId, setActiveId] = useState<string | null>(null)
|
if (!traget?.data || !source?.data) return
|
||||||
const sensors = useSensors(useSensor(MouseSensor), useSensor(TouchSensor))
|
const closestEdgeOfTarget = extractClosestEdge(traget.data)
|
||||||
|
const fromIndex = items.indexOf(source.data.id as SourceID)
|
||||||
const handleDragStart = useCallback((event: DragStartEvent) => {
|
const toIndex = items.indexOf(traget.data.id as SourceID)
|
||||||
setActiveId(event.active.id as string)
|
if (fromIndex === toIndex || fromIndex === -1 || toIndex === -1) return
|
||||||
}, [])
|
const update = reorderWithEdge({
|
||||||
const handleDragEnd = useCallback((event: DragEndEvent) => {
|
list: items,
|
||||||
const { active, over } = event
|
startIndex: fromIndex,
|
||||||
|
indexOfTarget: toIndex,
|
||||||
if (active.id !== over?.id) {
|
closestEdgeOfTarget,
|
||||||
const oldIndex = items.indexOf(active.id as any)
|
axis: "vertical",
|
||||||
const newIndex = items.indexOf(over!.id as any)
|
})
|
||||||
setItems(arrayMove(items, oldIndex, newIndex))
|
setItems(update)
|
||||||
}
|
}, [items, setItems])
|
||||||
|
// 避免动画干扰
|
||||||
setActiveId(null)
|
const { run } = useThrottleFn(onDropTargetChange, {
|
||||||
}, [setItems, items])
|
leading: true,
|
||||||
|
trailing: true,
|
||||||
const handleDragCancel = useCallback(() => {
|
wait: AnimationDuration,
|
||||||
setActiveId(null)
|
})
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DndContext
|
<DndContext onDropTargetChange={run}>
|
||||||
sensors={sensors}
|
{children}
|
||||||
measuring={{
|
|
||||||
droppable: {
|
|
||||||
strategy: MeasuringStrategy.Always,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
collisionDetection={closestCenter}
|
|
||||||
onDragStart={handleDragStart}
|
|
||||||
onDragEnd={handleDragEnd}
|
|
||||||
onDragCancel={handleDragCancel}
|
|
||||||
>
|
|
||||||
<SortableContext items={items} strategy={rectSortingStrategy}>
|
|
||||||
{children}
|
|
||||||
</SortableContext>
|
|
||||||
<DragOverlay
|
|
||||||
className="transition-opacity-300"
|
|
||||||
dropAnimation={{
|
|
||||||
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
|
|
||||||
duration: 300,
|
|
||||||
sideEffects: defaultDropAnimationSideEffects({
|
|
||||||
className: {
|
|
||||||
active: "op-100",
|
|
||||||
dragOverlay: "op-0",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!!activeId && <CardOverlay id={activeId as SourceID} />}
|
|
||||||
</DragOverlay>
|
|
||||||
</DndContext>
|
</DndContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -140,8 +102,9 @@ function DndWrapper({ items, setItems, children }: PropsWithChildren<DndProps>)
|
|||||||
function CardOverlay({ id }: { id: SourceID }) {
|
function CardOverlay({ id }: { id: SourceID }) {
|
||||||
return (
|
return (
|
||||||
<div className={$(
|
<div className={$(
|
||||||
"flex flex-col rounded-2xl p-4 backdrop-blur-5",
|
"flex flex-col p-4 backdrop-blur-5",
|
||||||
`bg-${sources[id].color}-500 dark:bg-${sources[id].color} bg-op-40!`,
|
`bg-${sources[id].color}-500 dark:bg-${sources[id].color} bg-op-40!`,
|
||||||
|
!isiOS() && "rounded-2xl",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={$("flex justify-between mx-2 items-center")}>
|
<div className={$("flex justify-between mx-2 items-center")}>
|
||||||
@ -173,45 +136,29 @@ function CardOverlay({ id }: { id: SourceID }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const animateLayoutChanges: AnimateLayoutChanges = (args) => {
|
function SortableCardWrapper({ id }: ItemsProps) {
|
||||||
const { isSorting, wasDragging } = args
|
|
||||||
if (isSorting || wasDragging) {
|
|
||||||
return defaultAnimateLayoutChanges(args)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
function SortableCardWrapper({ id, ...props }: ItemsProps) {
|
|
||||||
const {
|
const {
|
||||||
isDragging,
|
isDragging,
|
||||||
attributes,
|
|
||||||
listeners,
|
|
||||||
setNodeRef,
|
setNodeRef,
|
||||||
transform,
|
setHandleRef,
|
||||||
transition,
|
OverlayContainer,
|
||||||
} = useSortable({
|
} = useSortable({ id })
|
||||||
id,
|
|
||||||
animateLayoutChanges,
|
|
||||||
transition: {
|
|
||||||
duration: 300,
|
|
||||||
easing: "cubic-bezier(0.25, 1, 0.5, 1)",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const style = {
|
useEffect(() => {
|
||||||
transform: CSS.Transform.toString(transform),
|
if (OverlayContainer) {
|
||||||
transition,
|
OverlayContainer!.className += $(`bg-base`, !isiOS() && "rounded-2xl")
|
||||||
}
|
}
|
||||||
|
}, [OverlayContainer])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardWrapper
|
<>
|
||||||
ref={setNodeRef}
|
<CardWrapper
|
||||||
id={id}
|
ref={setNodeRef}
|
||||||
style={style}
|
id={id}
|
||||||
isDragged={isDragging}
|
isDragging={isDragging}
|
||||||
handleListeners={listeners}
|
setHandleRef={setHandleRef}
|
||||||
{...attributes}
|
/>
|
||||||
{...props}
|
{OverlayContainer && createPortal(<CardOverlay id={id} />, OverlayContainer)}
|
||||||
/>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
28
src/components/common/dnd/index.tsx
Normal file
28
src/components/common/dnd/index.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"
|
||||||
|
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"
|
||||||
|
import type { PropsWithChildren } from "react"
|
||||||
|
import type { AllEvents, ElementDragType } from "@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types"
|
||||||
|
import { InstanceIdContext } from "./useSortable"
|
||||||
|
|
||||||
|
interface ContextProps extends Partial<AllEvents<ElementDragType>> {
|
||||||
|
}
|
||||||
|
export function DndContext({ children, ...callback }: PropsWithChildren<ContextProps>) {
|
||||||
|
const [instanceId] = useState<string>(randomUUID())
|
||||||
|
useEffect(() => {
|
||||||
|
return (
|
||||||
|
combine(
|
||||||
|
monitorForElements({
|
||||||
|
canMonitor({ source }) {
|
||||||
|
return source.data.instanceId === instanceId
|
||||||
|
},
|
||||||
|
...callback,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}, [callback, instanceId])
|
||||||
|
return (
|
||||||
|
<InstanceIdContext.Provider value={instanceId}>
|
||||||
|
{children}
|
||||||
|
</InstanceIdContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
78
src/components/common/dnd/useSortable.ts
Normal file
78
src/components/common/dnd/useSortable.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"
|
||||||
|
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"
|
||||||
|
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview"
|
||||||
|
import { preserveOffsetOnSource } from "@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source"
|
||||||
|
import { createContext } from "react"
|
||||||
|
|
||||||
|
export const InstanceIdContext = createContext<string | null>(null)
|
||||||
|
|
||||||
|
interface SortableProps {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DraggableState {
|
||||||
|
type: "idle" | "dragging"
|
||||||
|
container?: HTMLElement
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSortable(props: SortableProps) {
|
||||||
|
const instanceId = useContext(InstanceIdContext)
|
||||||
|
const [draggableState, setDraggableState] = useState<DraggableState>({
|
||||||
|
type: "idle",
|
||||||
|
})
|
||||||
|
useEffect(() => {
|
||||||
|
if (draggableState.type === "idle") {
|
||||||
|
document.querySelector("html")?.classList.remove("grabbing")
|
||||||
|
} else if (draggableState.type === "dragging") {
|
||||||
|
// https://github.com/SortableJS/Vue.Draggable/issues/815#issuecomment-1552904628
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelector("html")?.classList.add("grabbing")
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
}, [draggableState])
|
||||||
|
const [handleRef, setHandleRef] = useState<HTMLElement | null>(null)
|
||||||
|
const [nodeRef, setNodeRef] = useState<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (handleRef && nodeRef) {
|
||||||
|
const cleanup = combine(
|
||||||
|
draggable({
|
||||||
|
// use custom drag preview
|
||||||
|
element: handleRef,
|
||||||
|
// element: ref,
|
||||||
|
// dragHandle: handleRef,
|
||||||
|
getInitialData: () => ({ id: props.id, instanceId }),
|
||||||
|
onGenerateDragPreview({ nativeSetDragImage, location }) {
|
||||||
|
setCustomNativeDragPreview({
|
||||||
|
getOffset: preserveOffsetOnSource({
|
||||||
|
element: nodeRef,
|
||||||
|
input: location.current.input,
|
||||||
|
}),
|
||||||
|
render({ container }) {
|
||||||
|
container.style.width = `${nodeRef.clientWidth}px`
|
||||||
|
setDraggableState({ type: "dragging", container })
|
||||||
|
},
|
||||||
|
nativeSetDragImage,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onDrop: () => {
|
||||||
|
setDraggableState({ type: "idle" })
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
dropTargetForElements({
|
||||||
|
element: nodeRef,
|
||||||
|
getData: () => ({ id: props.id }),
|
||||||
|
getIsSticky: () => true,
|
||||||
|
canDrop: ({ source }) => source.data.instanceId === instanceId,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
return cleanup
|
||||||
|
}
|
||||||
|
}, [props.id, instanceId, handleRef, nodeRef])
|
||||||
|
return {
|
||||||
|
setHandleRef,
|
||||||
|
setNodeRef,
|
||||||
|
isDragging: draggableState.type === "dragging",
|
||||||
|
OverlayContainer: draggableState.container,
|
||||||
|
}
|
||||||
|
}
|
@ -51,3 +51,8 @@ button:disabled {
|
|||||||
#dropdown-menu li {
|
#dropdown-menu li {
|
||||||
--at-apply: hover:bg-neutral-400/10 rounded-md flex items-center p-1 gap-1;
|
--at-apply: hover:bg-neutral-400/10 rounded-md flex items-center p-1 gap-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.grabbing * {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
@ -42,3 +42,15 @@ export const myFetch = $fetch.create({
|
|||||||
retry: 0,
|
retry: 0,
|
||||||
baseURL: "/api",
|
baseURL: "/api",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function isiOS() {
|
||||||
|
return [
|
||||||
|
"iPad Simulator",
|
||||||
|
"iPhone Simulator",
|
||||||
|
"iPod Simulator",
|
||||||
|
"iPad",
|
||||||
|
"iPhone",
|
||||||
|
"iPod",
|
||||||
|
].includes(navigator.platform)
|
||||||
|
|| (navigator.userAgent.includes("Mac") && "ontouchend" in document)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user