mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 04:59:23 +08:00
refactor(web): migrate notion page selectors to tanstack virtual (#34508)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,7 +5,6 @@
|
|||||||
"prepare": "vp config"
|
"prepare": "vp config"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"taze": "catalog:",
|
|
||||||
"vite-plus": "catalog:"
|
"vite-plus": "catalog:"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
176
pnpm-lock.yaml
generated
176
pnpm-lock.yaml
generated
@@ -171,6 +171,9 @@ catalogs:
|
|||||||
'@tanstack/react-query-devtools':
|
'@tanstack/react-query-devtools':
|
||||||
specifier: 5.96.1
|
specifier: 5.96.1
|
||||||
version: 5.96.1
|
version: 5.96.1
|
||||||
|
'@tanstack/react-virtual':
|
||||||
|
specifier: 3.13.23
|
||||||
|
version: 3.13.23
|
||||||
'@testing-library/dom':
|
'@testing-library/dom':
|
||||||
specifier: 10.4.1
|
specifier: 10.4.1
|
||||||
version: 10.4.1
|
version: 10.4.1
|
||||||
@@ -213,9 +216,6 @@ catalogs:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 19.2.3
|
specifier: 19.2.3
|
||||||
version: 19.2.3
|
version: 19.2.3
|
||||||
'@types/react-window':
|
|
||||||
specifier: 1.8.8
|
|
||||||
version: 1.8.8
|
|
||||||
'@types/sortablejs':
|
'@types/sortablejs':
|
||||||
specifier: 1.15.9
|
specifier: 1.15.9
|
||||||
version: 1.15.9
|
version: 1.15.9
|
||||||
@@ -453,9 +453,6 @@ catalogs:
|
|||||||
react-textarea-autosize:
|
react-textarea-autosize:
|
||||||
specifier: 8.5.9
|
specifier: 8.5.9
|
||||||
version: 8.5.9
|
version: 8.5.9
|
||||||
react-window:
|
|
||||||
specifier: 1.8.11
|
|
||||||
version: 1.8.11
|
|
||||||
reactflow:
|
reactflow:
|
||||||
specifier: 11.11.4
|
specifier: 11.11.4
|
||||||
version: 11.11.4
|
version: 11.11.4
|
||||||
@@ -495,9 +492,6 @@ catalogs:
|
|||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: 4.2.2
|
specifier: 4.2.2
|
||||||
version: 4.2.2
|
version: 4.2.2
|
||||||
taze:
|
|
||||||
specifier: 19.11.0
|
|
||||||
version: 19.11.0
|
|
||||||
tldts:
|
tldts:
|
||||||
specifier: 7.0.27
|
specifier: 7.0.27
|
||||||
version: 7.0.27
|
version: 7.0.27
|
||||||
@@ -605,9 +599,6 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
taze:
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 19.11.0
|
|
||||||
vite-plus:
|
vite-plus:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 0.1.15(@types/node@25.5.0)(esbuild@0.27.2)(happy-dom@20.8.9)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(yaml@2.8.3)
|
version: 0.1.15(@types/node@25.5.0)(esbuild@0.27.2)(happy-dom@20.8.9)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(yaml@2.8.3)
|
||||||
@@ -755,6 +746,9 @@ importers:
|
|||||||
'@tanstack/react-query':
|
'@tanstack/react-query':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 5.96.1(react@19.2.4)
|
version: 5.96.1(react@19.2.4)
|
||||||
|
'@tanstack/react-virtual':
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 3.13.23(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
abcjs:
|
abcjs:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 6.6.2
|
version: 6.6.2
|
||||||
@@ -920,9 +914,6 @@ importers:
|
|||||||
react-textarea-autosize:
|
react-textarea-autosize:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 8.5.9(@types/react@19.2.14)(react@19.2.4)
|
version: 8.5.9(@types/react@19.2.14)(react@19.2.4)
|
||||||
react-window:
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 1.8.11(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
reactflow:
|
reactflow:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 11.11.4(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 11.11.4(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -1098,9 +1089,6 @@ importers:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 19.2.3(@types/react@19.2.14)
|
version: 19.2.3(@types/react@19.2.14)
|
||||||
'@types/react-window':
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 1.8.8
|
|
||||||
'@types/sortablejs':
|
'@types/sortablejs':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 1.15.9
|
version: 1.15.9
|
||||||
@@ -1351,11 +1339,6 @@ packages:
|
|||||||
'@antfu/install-pkg@1.1.0':
|
'@antfu/install-pkg@1.1.0':
|
||||||
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
|
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
|
||||||
|
|
||||||
'@antfu/ni@30.0.0':
|
|
||||||
resolution: {integrity: sha512-DqBVB3XqXH4VsDpER7iLlEtayMC98iXrY7kwBzp1v6LGc/6U6+qnN3+X0bcPK63LMXJCRG2D/XDq7dvtKDGogg==}
|
|
||||||
engines: {node: '>=20.19.0'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
'@antfu/utils@8.1.1':
|
'@antfu/utils@8.1.1':
|
||||||
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
|
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
|
||||||
|
|
||||||
@@ -1944,9 +1927,6 @@ packages:
|
|||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||||
|
|
||||||
'@henrygd/queue@1.2.0':
|
|
||||||
resolution: {integrity: sha512-jW/BLSTpcvExDhqJGxtIPgGr2O0IFF8XUNDwEbfCfhrXT8a4xztQ9Lv6U/vbYzYC0xVWn+3zv6YnLUh3bEFUKA==}
|
|
||||||
|
|
||||||
'@heroicons/react@2.2.0':
|
'@heroicons/react@2.2.0':
|
||||||
resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==}
|
resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3049,9 +3029,6 @@ packages:
|
|||||||
'@preact/signals-core@1.14.0':
|
'@preact/signals-core@1.14.0':
|
||||||
resolution: {integrity: sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ==}
|
resolution: {integrity: sha512-AowtCcCU/33lFlh1zRFf/u+12rfrhtNakj7UpaGEsmMwUKpKWMVvcktOGcwBBNiB4lWrZWc01LhiyyzVklJyaQ==}
|
||||||
|
|
||||||
'@quansync/fs@1.0.0':
|
|
||||||
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
|
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.3':
|
'@radix-ui/primitive@1.1.3':
|
||||||
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||||
|
|
||||||
@@ -4327,9 +4304,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^19.2.0
|
'@types/react': ^19.2.0
|
||||||
|
|
||||||
'@types/react-window@1.8.8':
|
|
||||||
resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==}
|
|
||||||
|
|
||||||
'@types/react@19.2.14':
|
'@types/react@19.2.14':
|
||||||
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
|
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
|
||||||
|
|
||||||
@@ -5508,9 +5482,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
|
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
defu@6.1.4:
|
|
||||||
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
|
||||||
|
|
||||||
delaunator@5.1.0:
|
delaunator@5.1.0:
|
||||||
resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==}
|
resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==}
|
||||||
|
|
||||||
@@ -5518,9 +5489,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
destr@2.0.5:
|
|
||||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2:
|
||||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -6161,9 +6129,6 @@ packages:
|
|||||||
functional-red-black-tree@1.0.1:
|
functional-red-black-tree@1.0.1:
|
||||||
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
|
resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
|
||||||
|
|
||||||
fzf@0.5.2:
|
|
||||||
resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==}
|
|
||||||
|
|
||||||
gensync@1.0.0-beta.2:
|
gensync@1.0.0-beta.2:
|
||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -6886,9 +6851,6 @@ packages:
|
|||||||
mdn-data@2.23.0:
|
mdn-data@2.23.0:
|
||||||
resolution: {integrity: sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==}
|
resolution: {integrity: sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==}
|
||||||
|
|
||||||
memoize-one@5.2.1:
|
|
||||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
|
||||||
|
|
||||||
merge-stream@2.0.0:
|
merge-stream@2.0.0:
|
||||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||||
|
|
||||||
@@ -7035,10 +6997,6 @@ packages:
|
|||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
mimic-function@5.0.1:
|
|
||||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
mimic-response@3.1.0:
|
mimic-response@3.1.0:
|
||||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -7160,9 +7118,6 @@ packages:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
node-fetch-native@1.6.7:
|
|
||||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
|
||||||
|
|
||||||
node-releases@2.0.36:
|
node-releases@2.0.36:
|
||||||
resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==}
|
resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==}
|
||||||
|
|
||||||
@@ -7207,19 +7162,12 @@ packages:
|
|||||||
obug@2.1.1:
|
obug@2.1.1:
|
||||||
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
|
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
|
||||||
|
|
||||||
ofetch@1.5.1:
|
|
||||||
resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
|
|
||||||
|
|
||||||
ohash@2.0.11:
|
ohash@2.0.11:
|
||||||
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
|
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
|
|
||||||
onetime@7.0.0:
|
|
||||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
oniguruma-parser@0.12.1:
|
oniguruma-parser@0.12.1:
|
||||||
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
|
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
|
||||||
|
|
||||||
@@ -7497,9 +7445,6 @@ packages:
|
|||||||
quansync@0.2.11:
|
quansync@0.2.11:
|
||||||
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
|
resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
|
||||||
|
|
||||||
quansync@1.0.0:
|
|
||||||
resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==}
|
|
||||||
|
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
@@ -7658,13 +7603,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
react-window@1.8.11:
|
|
||||||
resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==}
|
|
||||||
engines: {node: '>8.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
|
|
||||||
react@19.2.4:
|
react@19.2.4:
|
||||||
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
|
resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -7816,10 +7754,6 @@ packages:
|
|||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
restore-cursor@5.1.0:
|
|
||||||
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
|
|
||||||
reusify@1.1.0:
|
reusify@1.1.0:
|
||||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||||
@@ -7928,10 +7862,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==}
|
resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
signal-exit@4.1.0:
|
|
||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
|
||||||
engines: {node: '>=14'}
|
|
||||||
|
|
||||||
simple-concat@1.0.1:
|
simple-concat@1.0.1:
|
||||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||||
|
|
||||||
@@ -8158,10 +8088,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==}
|
resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
taze@19.11.0:
|
|
||||||
resolution: {integrity: sha512-BlfH8Z6JdoIsrUptnz4P4YuEqdYsa/bSNNDOMhTlsHZ7Bbg1/0NyYh6uPkoRREjrt/kVovV+HYdi1ilHxvChfw==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
terser-webpack-plugin@5.4.0:
|
terser-webpack-plugin@5.4.0:
|
||||||
resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==}
|
resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==}
|
||||||
engines: {node: '>= 10.13.0'}
|
engines: {node: '>= 10.13.0'}
|
||||||
@@ -8356,12 +8282,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==}
|
resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
unconfig-core@7.5.0:
|
|
||||||
resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==}
|
|
||||||
|
|
||||||
unconfig@7.5.0:
|
|
||||||
resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==}
|
|
||||||
|
|
||||||
undici-types@7.18.2:
|
undici-types@7.18.2:
|
||||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||||
|
|
||||||
@@ -9060,13 +8980,6 @@ snapshots:
|
|||||||
package-manager-detector: 1.6.0
|
package-manager-detector: 1.6.0
|
||||||
tinyexec: 1.0.4
|
tinyexec: 1.0.4
|
||||||
|
|
||||||
'@antfu/ni@30.0.0':
|
|
||||||
dependencies:
|
|
||||||
fzf: 0.5.2
|
|
||||||
package-manager-detector: 1.6.0
|
|
||||||
tinyexec: 1.0.4
|
|
||||||
tinyglobby: 0.2.15
|
|
||||||
|
|
||||||
'@antfu/utils@8.1.1': {}
|
'@antfu/utils@8.1.1': {}
|
||||||
|
|
||||||
'@babel/code-frame@7.29.0':
|
'@babel/code-frame@7.29.0':
|
||||||
@@ -9772,8 +9685,6 @@ snapshots:
|
|||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
use-sync-external-store: 1.6.0(react@19.2.4)
|
use-sync-external-store: 1.6.0(react@19.2.4)
|
||||||
|
|
||||||
'@henrygd/queue@1.2.0': {}
|
|
||||||
|
|
||||||
'@heroicons/react@2.2.0(react@19.2.4)':
|
'@heroicons/react@2.2.0(react@19.2.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
@@ -10662,10 +10573,6 @@ snapshots:
|
|||||||
|
|
||||||
'@preact/signals-core@1.14.0': {}
|
'@preact/signals-core@1.14.0': {}
|
||||||
|
|
||||||
'@quansync/fs@1.0.0':
|
|
||||||
dependencies:
|
|
||||||
quansync: 1.0.0
|
|
||||||
|
|
||||||
'@radix-ui/primitive@1.1.3': {}
|
'@radix-ui/primitive@1.1.3': {}
|
||||||
|
|
||||||
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)':
|
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.4)':
|
||||||
@@ -11911,10 +11818,6 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 19.2.14
|
'@types/react': 19.2.14
|
||||||
|
|
||||||
'@types/react-window@1.8.8':
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 19.2.14
|
|
||||||
|
|
||||||
'@types/react@19.2.14':
|
'@types/react@19.2.14':
|
||||||
dependencies:
|
dependencies:
|
||||||
csstype: 3.2.3
|
csstype: 3.2.3
|
||||||
@@ -13209,16 +13112,12 @@ snapshots:
|
|||||||
|
|
||||||
define-lazy-prop@3.0.0: {}
|
define-lazy-prop@3.0.0: {}
|
||||||
|
|
||||||
defu@6.1.4: {}
|
|
||||||
|
|
||||||
delaunator@5.1.0:
|
delaunator@5.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
robust-predicates: 3.0.3
|
robust-predicates: 3.0.3
|
||||||
|
|
||||||
dequal@2.0.3: {}
|
dequal@2.0.3: {}
|
||||||
|
|
||||||
destr@2.0.5: {}
|
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
detect-node-es@1.1.0: {}
|
detect-node-es@1.1.0: {}
|
||||||
@@ -14065,8 +13964,6 @@ snapshots:
|
|||||||
|
|
||||||
functional-red-black-tree@1.0.1: {}
|
functional-red-black-tree@1.0.1: {}
|
||||||
|
|
||||||
fzf@0.5.2: {}
|
|
||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
get-east-asian-width@1.5.0: {}
|
get-east-asian-width@1.5.0: {}
|
||||||
@@ -14926,8 +14823,6 @@ snapshots:
|
|||||||
|
|
||||||
mdn-data@2.23.0: {}
|
mdn-data@2.23.0: {}
|
||||||
|
|
||||||
memoize-one@5.2.1: {}
|
|
||||||
|
|
||||||
merge-stream@2.0.0: {}
|
merge-stream@2.0.0: {}
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
@@ -15262,8 +15157,6 @@ snapshots:
|
|||||||
|
|
||||||
mime@4.1.0: {}
|
mime@4.1.0: {}
|
||||||
|
|
||||||
mimic-function@5.0.1: {}
|
|
||||||
|
|
||||||
mimic-response@3.1.0:
|
mimic-response@3.1.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -15379,8 +15272,6 @@ snapshots:
|
|||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
node-fetch-native@1.6.7: {}
|
|
||||||
|
|
||||||
node-releases@2.0.36: {}
|
node-releases@2.0.36: {}
|
||||||
|
|
||||||
normalize-package-data@8.0.0:
|
normalize-package-data@8.0.0:
|
||||||
@@ -15408,22 +15299,12 @@ snapshots:
|
|||||||
|
|
||||||
obug@2.1.1: {}
|
obug@2.1.1: {}
|
||||||
|
|
||||||
ofetch@1.5.1:
|
|
||||||
dependencies:
|
|
||||||
destr: 2.0.5
|
|
||||||
node-fetch-native: 1.6.7
|
|
||||||
ufo: 1.6.3
|
|
||||||
|
|
||||||
ohash@2.0.11: {}
|
ohash@2.0.11: {}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy: 1.0.2
|
wrappy: 1.0.2
|
||||||
|
|
||||||
onetime@7.0.0:
|
|
||||||
dependencies:
|
|
||||||
mimic-function: 5.0.1
|
|
||||||
|
|
||||||
oniguruma-parser@0.12.1: {}
|
oniguruma-parser@0.12.1: {}
|
||||||
|
|
||||||
oniguruma-to-es@4.3.5:
|
oniguruma-to-es@4.3.5:
|
||||||
@@ -15811,8 +15692,6 @@ snapshots:
|
|||||||
|
|
||||||
quansync@0.2.11: {}
|
quansync@0.2.11: {}
|
||||||
|
|
||||||
quansync@1.0.0: {}
|
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
radash@12.1.1: {}
|
radash@12.1.1: {}
|
||||||
@@ -15979,13 +15858,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
|
|
||||||
react-window@1.8.11(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.29.2
|
|
||||||
memoize-one: 5.2.1
|
|
||||||
react: 19.2.4
|
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
|
||||||
|
|
||||||
react@19.2.4: {}
|
react@19.2.4: {}
|
||||||
|
|
||||||
reactflow@11.11.4(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
reactflow@11.11.4(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
@@ -16219,11 +16091,6 @@ snapshots:
|
|||||||
path-parse: 1.0.7
|
path-parse: 1.0.7
|
||||||
supports-preserve-symlinks-flag: 1.0.0
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
restore-cursor@5.1.0:
|
|
||||||
dependencies:
|
|
||||||
onetime: 7.0.0
|
|
||||||
signal-exit: 4.1.0
|
|
||||||
|
|
||||||
reusify@1.1.0: {}
|
reusify@1.1.0: {}
|
||||||
|
|
||||||
robust-predicates@3.0.3: {}
|
robust-predicates@3.0.3: {}
|
||||||
@@ -16409,8 +16276,6 @@ snapshots:
|
|||||||
'@shikijs/vscode-textmate': 10.0.2
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
'@types/hast': 3.0.4
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
signal-exit@4.1.0: {}
|
|
||||||
|
|
||||||
simple-concat@1.0.1:
|
simple-concat@1.0.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -16652,22 +16517,6 @@ snapshots:
|
|||||||
minizlib: 3.1.0
|
minizlib: 3.1.0
|
||||||
yallist: 5.0.0
|
yallist: 5.0.0
|
||||||
|
|
||||||
taze@19.11.0:
|
|
||||||
dependencies:
|
|
||||||
'@antfu/ni': 30.0.0
|
|
||||||
'@henrygd/queue': 1.2.0
|
|
||||||
cac: 7.0.0
|
|
||||||
find-up-simple: 1.0.1
|
|
||||||
ofetch: 1.5.1
|
|
||||||
package-manager-detector: 1.6.0
|
|
||||||
pathe: 2.0.3
|
|
||||||
pnpm-workspace-yaml: 1.6.0
|
|
||||||
restore-cursor: 5.1.0
|
|
||||||
tinyexec: 1.0.4
|
|
||||||
tinyglobby: 0.2.15
|
|
||||||
unconfig: 7.5.0
|
|
||||||
yaml: 2.8.3
|
|
||||||
|
|
||||||
terser-webpack-plugin@5.4.0(esbuild@0.27.2)(uglify-js@3.19.3)(webpack@5.105.4(esbuild@0.27.2)(uglify-js@3.19.3)):
|
terser-webpack-plugin@5.4.0(esbuild@0.27.2)(uglify-js@3.19.3)(webpack@5.105.4(esbuild@0.27.2)(uglify-js@3.19.3)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.31
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
@@ -16820,19 +16669,6 @@ snapshots:
|
|||||||
|
|
||||||
unbash@2.2.0: {}
|
unbash@2.2.0: {}
|
||||||
|
|
||||||
unconfig-core@7.5.0:
|
|
||||||
dependencies:
|
|
||||||
'@quansync/fs': 1.0.0
|
|
||||||
quansync: 1.0.0
|
|
||||||
|
|
||||||
unconfig@7.5.0:
|
|
||||||
dependencies:
|
|
||||||
'@quansync/fs': 1.0.0
|
|
||||||
defu: 6.1.4
|
|
||||||
jiti: 2.6.1
|
|
||||||
quansync: 1.0.0
|
|
||||||
unconfig-core: 7.5.0
|
|
||||||
|
|
||||||
undici-types@7.18.2: {}
|
undici-types@7.18.2: {}
|
||||||
|
|
||||||
undici@7.24.0: {}
|
undici@7.24.0: {}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ catalog:
|
|||||||
"@tanstack/react-form-devtools": 0.2.20
|
"@tanstack/react-form-devtools": 0.2.20
|
||||||
"@tanstack/react-query": 5.96.1
|
"@tanstack/react-query": 5.96.1
|
||||||
"@tanstack/react-query-devtools": 5.96.1
|
"@tanstack/react-query-devtools": 5.96.1
|
||||||
|
"@tanstack/react-virtual": 3.13.23
|
||||||
"@testing-library/dom": 10.4.1
|
"@testing-library/dom": 10.4.1
|
||||||
"@testing-library/jest-dom": 6.9.1
|
"@testing-library/jest-dom": 6.9.1
|
||||||
"@testing-library/react": 16.3.2
|
"@testing-library/react": 16.3.2
|
||||||
@@ -147,7 +148,6 @@ catalog:
|
|||||||
"@types/qs": 6.15.0
|
"@types/qs": 6.15.0
|
||||||
"@types/react": 19.2.14
|
"@types/react": 19.2.14
|
||||||
"@types/react-dom": 19.2.3
|
"@types/react-dom": 19.2.3
|
||||||
"@types/react-window": 1.8.8
|
|
||||||
"@types/sortablejs": 1.15.9
|
"@types/sortablejs": 1.15.9
|
||||||
"@typescript-eslint/eslint-plugin": 8.58.0
|
"@typescript-eslint/eslint-plugin": 8.58.0
|
||||||
"@typescript-eslint/parser": 8.58.0
|
"@typescript-eslint/parser": 8.58.0
|
||||||
@@ -229,7 +229,6 @@ catalog:
|
|||||||
react-server-dom-webpack: 19.2.4
|
react-server-dom-webpack: 19.2.4
|
||||||
react-sortablejs: 6.1.4
|
react-sortablejs: 6.1.4
|
||||||
react-textarea-autosize: 8.5.9
|
react-textarea-autosize: 8.5.9
|
||||||
react-window: 1.8.11
|
|
||||||
reactflow: 11.11.4
|
reactflow: 11.11.4
|
||||||
remark-breaks: 4.0.0
|
remark-breaks: 4.0.0
|
||||||
remark-directive: 4.0.0
|
remark-directive: 4.0.0
|
||||||
@@ -243,7 +242,6 @@ catalog:
|
|||||||
string-ts: 2.3.1
|
string-ts: 2.3.1
|
||||||
tailwind-merge: 3.5.0
|
tailwind-merge: 3.5.0
|
||||||
tailwindcss: 4.2.2
|
tailwindcss: 4.2.2
|
||||||
taze: 19.11.0
|
|
||||||
tldts: 7.0.27
|
tldts: 7.0.27
|
||||||
tsdown: 0.21.7
|
tsdown: 0.21.7
|
||||||
tsx: 4.21.0
|
tsx: 4.21.0
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import { defineConfig } from 'taze'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
exclude: [
|
|
||||||
// We are going to replace these
|
|
||||||
'react-window',
|
|
||||||
'@types/react-window',
|
|
||||||
],
|
|
||||||
})
|
|
||||||
36
web/__mocks__/@tanstack/react-virtual.ts
Normal file
36
web/__mocks__/@tanstack/react-virtual.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { vi } from 'vitest'
|
||||||
|
|
||||||
|
const mockVirtualizer = ({
|
||||||
|
count,
|
||||||
|
estimateSize,
|
||||||
|
}: {
|
||||||
|
count: number
|
||||||
|
estimateSize?: (index: number) => number
|
||||||
|
}) => {
|
||||||
|
const getSize = (index: number) => estimateSize?.(index) ?? 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
getTotalSize: () => Array.from({ length: count }).reduce<number>((total, _, index) => total + getSize(index), 0),
|
||||||
|
getVirtualItems: () => {
|
||||||
|
let start = 0
|
||||||
|
|
||||||
|
return Array.from({ length: count }).map((_, index) => {
|
||||||
|
const size = getSize(index)
|
||||||
|
const virtualItem = {
|
||||||
|
end: start + size,
|
||||||
|
index,
|
||||||
|
key: index,
|
||||||
|
size,
|
||||||
|
start,
|
||||||
|
}
|
||||||
|
|
||||||
|
start += size
|
||||||
|
return virtualItem
|
||||||
|
})
|
||||||
|
},
|
||||||
|
measureElement: vi.fn(),
|
||||||
|
scrollToIndex: vi.fn(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { mockVirtualizer as useVirtualizer }
|
||||||
@@ -15,7 +15,6 @@ import { highlightCode } from './shiki-highlight'
|
|||||||
|
|
||||||
const Flowchart = dynamic(() => import('@/app/components/base/mermaid'), { ssr: false })
|
const Flowchart = dynamic(() => import('@/app/components/base/mermaid'), { ssr: false })
|
||||||
|
|
||||||
// Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
|
|
||||||
const capitalizationLanguageNameMap: Record<string, string> = {
|
const capitalizationLanguageNameMap: Record<string, string> = {
|
||||||
sql: 'SQL',
|
sql: 'SQL',
|
||||||
javascript: 'JavaScript',
|
javascript: 'JavaScript',
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { useModalContextSelector } from '@/context/modal-context'
|
|||||||
import { useInvalidPreImportNotionPages, usePreImportNotionPages } from '@/service/knowledge/use-import'
|
import { useInvalidPreImportNotionPages, usePreImportNotionPages } from '@/service/knowledge/use-import'
|
||||||
import NotionPageSelector from '../base'
|
import NotionPageSelector from '../base'
|
||||||
|
|
||||||
|
vi.mock('@tanstack/react-virtual')
|
||||||
|
|
||||||
vi.mock('@/service/knowledge/use-import', () => ({
|
vi.mock('@/service/knowledge/use-import', () => ({
|
||||||
usePreImportNotionPages: vi.fn(),
|
usePreImportNotionPages: vi.fn(),
|
||||||
useInvalidPreImportNotionPages: vi.fn(),
|
useInvalidPreImportNotionPages: vi.fn(),
|
||||||
@@ -183,7 +185,7 @@ describe('NotionPageSelector Base', () => {
|
|||||||
const user = userEvent.setup()
|
const user = userEvent.setup()
|
||||||
render(<NotionPageSelector credentialList={mockCredentialList} onSelect={vi.fn()} />)
|
render(<NotionPageSelector credentialList={mockCredentialList} onSelect={vi.fn()} />)
|
||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: 'Configure Notion' }))
|
await user.click(screen.getByRole('button', { name: 'common.dataSource.notion.selector.configure' }))
|
||||||
expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({ payload: ACCOUNT_SETTING_TAB.DATA_SOURCE })
|
expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({ payload: ACCOUNT_SETTING_TAB.DATA_SOURCE })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { DataSourceCredential } from '../../header/account-setting/data-sou
|
|||||||
import type { NotionCredential } from './credential-selector'
|
import type { NotionCredential } from './credential-selector'
|
||||||
import type { DataSourceNotionPageMap, DataSourceNotionWorkspace, NotionPage } from '@/models/common'
|
import type { DataSourceNotionPageMap, DataSourceNotionWorkspace, NotionPage } from '@/models/common'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||||
import { useModalContextSelector } from '@/context/modal-context'
|
import { useModalContextSelector } from '@/context/modal-context'
|
||||||
import { useInvalidPreImportNotionPages, usePreImportNotionPages } from '@/service/knowledge/use-import'
|
import { useInvalidPreImportNotionPages, usePreImportNotionPages } from '@/service/knowledge/use-import'
|
||||||
@@ -33,6 +34,7 @@ const NotionPageSelector = ({
|
|||||||
credentialList,
|
credentialList,
|
||||||
onSelectCredential,
|
onSelectCredential,
|
||||||
}: NotionPageSelectorProps) => {
|
}: NotionPageSelectorProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
|
const setShowAccountSettingModal = useModalContextSelector(s => s.setShowAccountSettingModal)
|
||||||
|
|
||||||
@@ -48,27 +50,34 @@ const NotionPageSelector = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [credentialList])
|
}, [credentialList])
|
||||||
const [currentCredential, setCurrentCredential] = useState(notionCredentials[0])
|
const [selectedCredentialId, setSelectedCredentialId] = useState(() => notionCredentials[0]?.credentialId ?? '')
|
||||||
|
const currentCredential = useMemo(() => {
|
||||||
|
return notionCredentials.find(item => item.credentialId === selectedCredentialId) ?? notionCredentials[0] ?? null
|
||||||
|
}, [notionCredentials, selectedCredentialId])
|
||||||
|
const currentCredentialId = currentCredential?.credentialId ?? ''
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const credential = notionCredentials.find(item => item.credentialId === currentCredential?.credentialId)
|
onSelectCredential?.(currentCredentialId)
|
||||||
if (!credential) {
|
}, [currentCredentialId, onSelectCredential])
|
||||||
const firstCredential = notionCredentials[0]
|
|
||||||
invalidPreImportNotionPages({ datasetId, credentialId: firstCredential.credentialId })
|
useEffect(() => {
|
||||||
setCurrentCredential(notionCredentials[0])
|
if (!notionCredentials.length) {
|
||||||
onSelect([]) // Clear selected pages when changing credential
|
onSelect([])
|
||||||
onSelectCredential?.(firstCredential.credentialId)
|
return
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
onSelectCredential?.(credential?.credentialId || '')
|
if (!selectedCredentialId || selectedCredentialId === currentCredentialId)
|
||||||
}
|
return
|
||||||
}, [notionCredentials])
|
|
||||||
|
invalidPreImportNotionPages({ datasetId, credentialId: currentCredentialId })
|
||||||
|
onSelect([])
|
||||||
|
}, [currentCredentialId, datasetId, invalidPreImportNotionPages, notionCredentials.length, onSelect, selectedCredentialId])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: notionsPages,
|
data: notionsPages,
|
||||||
isFetching: isFetchingNotionPages,
|
isFetching: isFetchingNotionPages,
|
||||||
isError: isFetchingNotionPagesError,
|
isError: isFetchingNotionPagesError,
|
||||||
} = usePreImportNotionPages({ datasetId, credentialId: currentCredential.credentialId || '' })
|
} = usePreImportNotionPages({ datasetId, credentialId: currentCredentialId })
|
||||||
|
|
||||||
const pagesMapAndSelectedPagesId: [DataSourceNotionPageMap, Set<string>, Set<string>] = useMemo(() => {
|
const pagesMapAndSelectedPagesId: [DataSourceNotionPageMap, Set<string>, Set<string>] = useMemo(() => {
|
||||||
const selectedPagesId = new Set<string>()
|
const selectedPagesId = new Set<string>()
|
||||||
@@ -94,28 +103,24 @@ const NotionPageSelector = ({
|
|||||||
const defaultSelectedPagesId = useMemo(() => {
|
const defaultSelectedPagesId = useMemo(() => {
|
||||||
return [...Array.from(pagesMapAndSelectedPagesId[1]), ...(value || [])]
|
return [...Array.from(pagesMapAndSelectedPagesId[1]), ...(value || [])]
|
||||||
}, [pagesMapAndSelectedPagesId, value])
|
}, [pagesMapAndSelectedPagesId, value])
|
||||||
const [selectedPagesId, setSelectedPagesId] = useState<Set<string>>(() => new Set(defaultSelectedPagesId))
|
const selectedPagesId = useMemo(() => new Set(defaultSelectedPagesId), [defaultSelectedPagesId])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setSelectedPagesId(new Set(defaultSelectedPagesId))
|
|
||||||
}, [defaultSelectedPagesId])
|
|
||||||
|
|
||||||
const handleSearchValueChange = useCallback((value: string) => {
|
const handleSearchValueChange = useCallback((value: string) => {
|
||||||
setSearchValue(value)
|
setSearchValue(value)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleSelectCredential = useCallback((credentialId: string) => {
|
const handleSelectCredential = useCallback((credentialId: string) => {
|
||||||
const credential = notionCredentials.find(item => item.credentialId === credentialId)!
|
if (credentialId === currentCredentialId)
|
||||||
invalidPreImportNotionPages({ datasetId, credentialId: credential.credentialId })
|
return
|
||||||
setCurrentCredential(credential)
|
|
||||||
|
invalidPreImportNotionPages({ datasetId, credentialId })
|
||||||
|
setSelectedCredentialId(credentialId)
|
||||||
onSelect([]) // Clear selected pages when changing credential
|
onSelect([]) // Clear selected pages when changing credential
|
||||||
onSelectCredential?.(credential.credentialId)
|
}, [currentCredentialId, datasetId, invalidPreImportNotionPages, onSelect])
|
||||||
}, [datasetId, invalidPreImportNotionPages, notionCredentials, onSelect, onSelectCredential])
|
|
||||||
|
|
||||||
const handleSelectPages = useCallback((newSelectedPagesId: Set<string>) => {
|
const handleSelectPages = useCallback((newSelectedPagesId: Set<string>) => {
|
||||||
const selectedPages = Array.from(newSelectedPagesId).map(pageId => pagesMapAndSelectedPagesId[0][pageId])
|
const selectedPages = Array.from(newSelectedPagesId).map(pageId => pagesMapAndSelectedPagesId[0][pageId])
|
||||||
|
|
||||||
setSelectedPagesId(new Set(Array.from(newSelectedPagesId)))
|
|
||||||
onSelect(selectedPages)
|
onSelect(selectedPages)
|
||||||
}, [pagesMapAndSelectedPagesId, onSelect])
|
}, [pagesMapAndSelectedPagesId, onSelect])
|
||||||
|
|
||||||
@@ -140,16 +145,16 @@ const NotionPageSelector = ({
|
|||||||
<div className="flex flex-col gap-y-2" data-testid="notion-page-selector-base">
|
<div className="flex flex-col gap-y-2" data-testid="notion-page-selector-base">
|
||||||
<Header
|
<Header
|
||||||
onClickConfiguration={handleConfigureNotion}
|
onClickConfiguration={handleConfigureNotion}
|
||||||
title="Choose notion pages"
|
title={t('dataSource.notion.selector.headerTitle', { ns: 'common' })}
|
||||||
buttonText="Configure Notion"
|
buttonText={t('dataSource.notion.selector.configure', { ns: 'common' })}
|
||||||
docTitle="Notion docs"
|
docTitle={t('dataSource.notion.selector.docs', { ns: 'common' })}
|
||||||
docLink="https://www.notion.so/docs"
|
docLink="https://www.notion.so/docs"
|
||||||
/>
|
/>
|
||||||
<div className="rounded-xl border border-components-panel-border bg-background-default-subtle">
|
<div className="rounded-xl border border-components-panel-border bg-background-default-subtle">
|
||||||
<div className="flex h-12 items-center gap-x-2 rounded-t-xl border-b border-b-divider-regular bg-components-panel-bg p-2">
|
<div className="flex h-12 items-center gap-x-2 rounded-t-xl border-b border-b-divider-regular bg-components-panel-bg p-2">
|
||||||
<div className="flex grow items-center gap-x-1">
|
<div className="flex grow items-center gap-x-1">
|
||||||
<WorkspaceSelector
|
<WorkspaceSelector
|
||||||
value={currentCredential.credentialId}
|
value={currentCredentialId}
|
||||||
items={notionCredentials}
|
items={notionCredentials}
|
||||||
onSelect={handleSelectCredential}
|
onSelect={handleSelectCredential}
|
||||||
/>
|
/>
|
||||||
@@ -168,6 +173,7 @@ const NotionPageSelector = ({
|
|||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<PageSelector
|
<PageSelector
|
||||||
|
key={currentCredentialId || 'default'}
|
||||||
value={selectedPagesId}
|
value={selectedPagesId}
|
||||||
disabledValue={pagesMapAndSelectedPagesId[2]}
|
disabledValue={pagesMapAndSelectedPagesId[2]}
|
||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { render, screen, waitFor } from '@testing-library/react'
|
|||||||
import userEvent from '@testing-library/user-event'
|
import userEvent from '@testing-library/user-event'
|
||||||
import PageSelector from '../index'
|
import PageSelector from '../index'
|
||||||
|
|
||||||
|
vi.mock('@tanstack/react-virtual')
|
||||||
|
|
||||||
const buildPage = (overrides: Partial<DataSourceNotionPage>): DataSourceNotionPage => ({
|
const buildPage = (overrides: Partial<DataSourceNotionPage>): DataSourceNotionPage => ({
|
||||||
page_id: 'page-id',
|
page_id: 'page-id',
|
||||||
page_name: 'Page name',
|
page_name: 'Page name',
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import type { ListChildComponentProps } from 'react-window'
|
|
||||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||||
import { memo, useEffect, useMemo, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { areEqual, FixedSizeList as List } from 'react-window'
|
import { usePageSelectorModel } from './use-page-selector-model'
|
||||||
import { cn } from '@/utils/classnames'
|
import VirtualPageList from './virtual-page-list'
|
||||||
import Checkbox from '../../checkbox'
|
|
||||||
import NotionIcon from '../../notion-icon'
|
|
||||||
|
|
||||||
type PageSelectorProps = {
|
type PageSelectorProps = {
|
||||||
value: Set<string>
|
value: Set<string>
|
||||||
@@ -17,173 +13,7 @@ type PageSelectorProps = {
|
|||||||
canPreview?: boolean
|
canPreview?: boolean
|
||||||
previewPageId?: string
|
previewPageId?: string
|
||||||
onPreview?: (selectedPageId: string) => void
|
onPreview?: (selectedPageId: string) => void
|
||||||
isMultipleChoice?: boolean
|
|
||||||
}
|
}
|
||||||
type NotionPageTreeItem = {
|
|
||||||
children: Set<string>
|
|
||||||
descendants: Set<string>
|
|
||||||
depth: number
|
|
||||||
ancestors: string[]
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
type NotionPageTreeMap = Record<string, NotionPageTreeItem>
|
|
||||||
type NotionPageItem = {
|
|
||||||
expand: boolean
|
|
||||||
depth: number
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
|
|
||||||
const recursivePushInParentDescendants = (
|
|
||||||
pagesMap: DataSourceNotionPageMap,
|
|
||||||
listTreeMap: NotionPageTreeMap,
|
|
||||||
current: NotionPageTreeItem,
|
|
||||||
leafItem: NotionPageTreeItem,
|
|
||||||
) => {
|
|
||||||
const parentId = current.parent_id
|
|
||||||
const pageId = current.page_id
|
|
||||||
|
|
||||||
if (!parentId || !pageId)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (parentId !== 'root' && pagesMap[parentId]) {
|
|
||||||
if (!listTreeMap[parentId]) {
|
|
||||||
const children = new Set([pageId])
|
|
||||||
const descendants = new Set([pageId, leafItem.page_id])
|
|
||||||
listTreeMap[parentId] = {
|
|
||||||
...pagesMap[parentId],
|
|
||||||
children,
|
|
||||||
descendants,
|
|
||||||
depth: 0,
|
|
||||||
ancestors: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listTreeMap[parentId].children.add(pageId)
|
|
||||||
listTreeMap[parentId].descendants.add(pageId)
|
|
||||||
listTreeMap[parentId].descendants.add(leafItem.page_id)
|
|
||||||
}
|
|
||||||
leafItem.depth++
|
|
||||||
leafItem.ancestors.unshift(listTreeMap[parentId].page_name)
|
|
||||||
|
|
||||||
if (listTreeMap[parentId].parent_id !== 'root')
|
|
||||||
recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap[parentId], leafItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
|
||||||
dataList: NotionPageItem[]
|
|
||||||
handleToggle: (index: number) => void
|
|
||||||
checkedIds: Set<string>
|
|
||||||
disabledCheckedIds: Set<string>
|
|
||||||
handleCheck: (index: number) => void
|
|
||||||
canPreview?: boolean
|
|
||||||
handlePreview: (index: number) => void
|
|
||||||
listMapWithChildrenAndDescendants: NotionPageTreeMap
|
|
||||||
searchValue: string
|
|
||||||
previewPageId: string
|
|
||||||
pagesMap: DataSourceNotionPageMap
|
|
||||||
}>) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const {
|
|
||||||
dataList,
|
|
||||||
handleToggle,
|
|
||||||
checkedIds,
|
|
||||||
disabledCheckedIds,
|
|
||||||
handleCheck,
|
|
||||||
canPreview,
|
|
||||||
handlePreview,
|
|
||||||
listMapWithChildrenAndDescendants,
|
|
||||||
searchValue,
|
|
||||||
previewPageId,
|
|
||||||
pagesMap,
|
|
||||||
} = data
|
|
||||||
const current = dataList[index]
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id]
|
|
||||||
const hasChild = currentWithChildrenAndDescendants.descendants.size > 0
|
|
||||||
const ancestors = currentWithChildrenAndDescendants.ancestors
|
|
||||||
const breadCrumbs = ancestors.length ? [...ancestors, current.page_name] : [current.page_name]
|
|
||||||
const disabled = disabledCheckedIds.has(current.page_id)
|
|
||||||
|
|
||||||
const renderArrow = () => {
|
|
||||||
if (hasChild) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="mr-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-md hover:bg-components-button-ghost-bg-hover"
|
|
||||||
style={{ marginLeft: current.depth * 8 }}
|
|
||||||
onClick={() => handleToggle(index)}
|
|
||||||
data-testid={`notion-page-toggle-${current.page_id}`}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
current.expand
|
|
||||||
? <div className="i-ri-arrow-down-s-line h-4 w-4 text-text-tertiary" />
|
|
||||||
: <div className="i-ri-arrow-right-s-line h-4 w-4 text-text-tertiary" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (current.parent_id === 'root' || !pagesMap[current.parent_id]) {
|
|
||||||
return (
|
|
||||||
<div></div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="mr-1 h-5 w-5 shrink-0" style={{ marginLeft: current.depth * 8 }} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn('group flex cursor-pointer items-center rounded-md pl-2 pr-[2px] hover:bg-state-base-hover', previewPageId === current.page_id && 'bg-state-base-hover')}
|
|
||||||
style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
|
|
||||||
data-testid={`notion-page-row-${current.page_id}`}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
className="mr-2 shrink-0"
|
|
||||||
checked={checkedIds.has(current.page_id)}
|
|
||||||
disabled={disabled}
|
|
||||||
onCheck={() => {
|
|
||||||
handleCheck(index)
|
|
||||||
}}
|
|
||||||
id={`notion-page-checkbox-${current.page_id}`}
|
|
||||||
/>
|
|
||||||
{!searchValue && renderArrow()}
|
|
||||||
<NotionIcon
|
|
||||||
className="mr-1 shrink-0"
|
|
||||||
type="page"
|
|
||||||
src={current.page_icon}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="grow truncate text-[13px] font-medium leading-4 text-text-secondary"
|
|
||||||
title={current.page_name}
|
|
||||||
data-testid={`notion-page-name-${current.page_id}`}
|
|
||||||
>
|
|
||||||
{current.page_name}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
canPreview && (
|
|
||||||
<div
|
|
||||||
className="ml-1 hidden h-6 shrink-0 cursor-pointer items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-xs
|
|
||||||
font-medium leading-4 text-components-button-secondary-text shadow-xs shadow-shadow-shadow-3 backdrop-blur-[10px]
|
|
||||||
hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover group-hover:flex"
|
|
||||||
onClick={() => handlePreview(index)}
|
|
||||||
data-testid={`notion-page-preview-${current.page_id}`}
|
|
||||||
>
|
|
||||||
{t('dataSource.notion.selector.preview', { ns: 'common' })}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
searchValue && (
|
|
||||||
<div
|
|
||||||
className="ml-1 max-w-[120px] shrink-0 truncate text-xs text-text-quaternary"
|
|
||||||
title={breadCrumbs.join(' / ')}
|
|
||||||
>
|
|
||||||
{breadCrumbs.join(' / ')}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const Item = memo(ItemComponent, areEqual)
|
|
||||||
|
|
||||||
const PageSelector = ({
|
const PageSelector = ({
|
||||||
value,
|
value,
|
||||||
@@ -197,108 +27,25 @@ const PageSelector = ({
|
|||||||
onPreview,
|
onPreview,
|
||||||
}: PageSelectorProps) => {
|
}: PageSelectorProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [dataList, setDataList] = useState<NotionPageItem[]>([])
|
const {
|
||||||
const [localPreviewPageId, setLocalPreviewPageId] = useState('')
|
currentPreviewPageId,
|
||||||
|
effectiveSearchValue,
|
||||||
useEffect(() => {
|
rows,
|
||||||
setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => {
|
handlePreview,
|
||||||
return {
|
handleSelect,
|
||||||
...item,
|
handleToggle,
|
||||||
expand: false,
|
} = usePageSelectorModel({
|
||||||
depth: 0,
|
checkedIds: value,
|
||||||
}
|
list,
|
||||||
}))
|
onPreview,
|
||||||
}, [list])
|
onSelect,
|
||||||
|
pagesMap,
|
||||||
const searchDataList = list.filter((item) => {
|
previewPageId,
|
||||||
return item.page_name.includes(searchValue)
|
searchValue,
|
||||||
}).map((item) => {
|
selectionMode: 'multiple',
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
expand: false,
|
|
||||||
depth: 0,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const currentDataList = searchValue ? searchDataList : dataList
|
|
||||||
const currentPreviewPageId = previewPageId === undefined ? localPreviewPageId : previewPageId
|
|
||||||
|
|
||||||
const listMapWithChildrenAndDescendants = useMemo(() => {
|
if (!rows.length) {
|
||||||
return list.reduce((prev: NotionPageTreeMap, next: DataSourceNotionPage) => {
|
|
||||||
const pageId = next.page_id
|
|
||||||
if (!prev[pageId])
|
|
||||||
prev[pageId] = { ...next, children: new Set(), descendants: new Set(), depth: 0, ancestors: [] }
|
|
||||||
|
|
||||||
recursivePushInParentDescendants(pagesMap, prev, prev[pageId], prev[pageId])
|
|
||||||
return prev
|
|
||||||
}, {})
|
|
||||||
}, [list, pagesMap])
|
|
||||||
|
|
||||||
const handleToggle = (index: number) => {
|
|
||||||
const current = dataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
|
|
||||||
const descendantsIds = Array.from(currentWithChildrenAndDescendants.descendants)
|
|
||||||
const childrenIds = Array.from(currentWithChildrenAndDescendants.children)
|
|
||||||
let newDataList = []
|
|
||||||
|
|
||||||
if (current.expand) {
|
|
||||||
current.expand = false
|
|
||||||
|
|
||||||
newDataList = dataList.filter(item => !descendantsIds.includes(item.page_id))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
current.expand = true
|
|
||||||
|
|
||||||
newDataList = [
|
|
||||||
...dataList.slice(0, index + 1),
|
|
||||||
...childrenIds.map(item => ({
|
|
||||||
...pagesMap[item],
|
|
||||||
expand: false,
|
|
||||||
depth: listMapWithChildrenAndDescendants[item].depth,
|
|
||||||
})),
|
|
||||||
...dataList.slice(index + 1),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
setDataList(newDataList)
|
|
||||||
}
|
|
||||||
|
|
||||||
const copyValue = new Set(value)
|
|
||||||
const handleCheck = (index: number) => {
|
|
||||||
const current = currentDataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
|
|
||||||
|
|
||||||
if (copyValue.has(pageId)) {
|
|
||||||
if (!searchValue) {
|
|
||||||
for (const item of currentWithChildrenAndDescendants.descendants)
|
|
||||||
copyValue.delete(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
copyValue.delete(pageId)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!searchValue) {
|
|
||||||
for (const item of currentWithChildrenAndDescendants.descendants)
|
|
||||||
copyValue.add(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
copyValue.add(pageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelect(new Set(copyValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePreview = (index: number) => {
|
|
||||||
const current = currentDataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
|
|
||||||
setLocalPreviewPageId(pageId)
|
|
||||||
|
|
||||||
if (onPreview)
|
|
||||||
onPreview(pageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentDataList.length) {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[296px] items-center justify-center text-[13px] text-text-tertiary">
|
<div className="flex h-[296px] items-center justify-center text-[13px] text-text-tertiary">
|
||||||
{t('dataSource.notion.selector.noSearchResult', { ns: 'common' })}
|
{t('dataSource.notion.selector.noSearchResult', { ns: 'common' })}
|
||||||
@@ -307,29 +54,18 @@ const PageSelector = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<VirtualPageList
|
||||||
className="py-2"
|
checkedIds={value}
|
||||||
height={296}
|
disabledValue={disabledValue}
|
||||||
itemCount={currentDataList.length}
|
onPreview={handlePreview}
|
||||||
itemSize={28}
|
onSelect={handleSelect}
|
||||||
width="100%"
|
onToggle={handleToggle}
|
||||||
itemKey={(index, data) => data.dataList[index].page_id}
|
previewPageId={currentPreviewPageId}
|
||||||
itemData={{
|
rows={rows}
|
||||||
dataList: currentDataList,
|
searchValue={effectiveSearchValue}
|
||||||
handleToggle,
|
selectionMode="multiple"
|
||||||
checkedIds: value,
|
showPreview={canPreview}
|
||||||
disabledCheckedIds: disabledValue,
|
/>
|
||||||
handleCheck,
|
|
||||||
canPreview,
|
|
||||||
handlePreview,
|
|
||||||
listMapWithChildrenAndDescendants,
|
|
||||||
searchValue,
|
|
||||||
previewPageId: currentPreviewPageId,
|
|
||||||
pagesMap,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Item}
|
|
||||||
</List>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import type { CSSProperties } from 'react'
|
||||||
|
import type { NotionPageRow as NotionPageRowData, NotionPageSelectionMode } from './types'
|
||||||
|
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||||
|
import { memo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import Checkbox from '@/app/components/base/checkbox'
|
||||||
|
import NotionIcon from '@/app/components/base/notion-icon'
|
||||||
|
import Radio from '@/app/components/base/radio/ui'
|
||||||
|
import { cn } from '@/utils/classnames'
|
||||||
|
|
||||||
|
type NotionPageRowProps = {
|
||||||
|
checked: boolean
|
||||||
|
disabled: boolean
|
||||||
|
isPreviewed: boolean
|
||||||
|
onPreview: (pageId: string) => void
|
||||||
|
onSelect: (pageId: string) => void
|
||||||
|
onToggle: (pageId: string) => void
|
||||||
|
row: NotionPageRowData
|
||||||
|
searchValue: string
|
||||||
|
selectionMode: NotionPageSelectionMode
|
||||||
|
showPreview: boolean
|
||||||
|
style: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotionPageRow = ({
|
||||||
|
checked,
|
||||||
|
disabled,
|
||||||
|
isPreviewed,
|
||||||
|
onPreview,
|
||||||
|
onSelect,
|
||||||
|
onToggle,
|
||||||
|
row,
|
||||||
|
searchValue,
|
||||||
|
selectionMode,
|
||||||
|
showPreview,
|
||||||
|
style,
|
||||||
|
}: NotionPageRowProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const pageId = row.page.page_id
|
||||||
|
const breadcrumbs = row.ancestors.length ? [...row.ancestors, row.page.page_name] : [row.page.page_name]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn('group flex cursor-pointer items-center rounded-md pr-[2px] pl-2 hover:bg-state-base-hover', isPreviewed && 'bg-state-base-hover')}
|
||||||
|
style={style}
|
||||||
|
data-testid={`notion-page-row-${pageId}`}
|
||||||
|
>
|
||||||
|
{selectionMode === 'multiple'
|
||||||
|
? (
|
||||||
|
<Checkbox
|
||||||
|
className="mr-2 shrink-0"
|
||||||
|
checked={checked}
|
||||||
|
disabled={disabled}
|
||||||
|
onCheck={() => onSelect(pageId)}
|
||||||
|
id={`notion-page-checkbox-${pageId}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Radio
|
||||||
|
className="mr-2 shrink-0"
|
||||||
|
isChecked={checked}
|
||||||
|
disabled={disabled}
|
||||||
|
onCheck={() => onSelect(pageId)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!searchValue && row.hasChild && (
|
||||||
|
<div
|
||||||
|
className="mr-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-md hover:bg-components-button-ghost-bg-hover"
|
||||||
|
style={{ marginLeft: row.depth * 8 }}
|
||||||
|
onClick={() => onToggle(pageId)}
|
||||||
|
data-testid={`notion-page-toggle-${pageId}`}
|
||||||
|
>
|
||||||
|
{row.expand
|
||||||
|
? <RiArrowDownSLine className="h-4 w-4 text-text-tertiary" />
|
||||||
|
: <RiArrowRightSLine className="h-4 w-4 text-text-tertiary" />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!searchValue && !row.hasChild && row.parentExists && (
|
||||||
|
<div className="mr-1 h-5 w-5 shrink-0" style={{ marginLeft: row.depth * 8 }} />
|
||||||
|
)}
|
||||||
|
<NotionIcon
|
||||||
|
className="mr-1 shrink-0"
|
||||||
|
type="page"
|
||||||
|
src={row.page.page_icon}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="grow truncate text-[13px] leading-4 font-medium text-text-secondary"
|
||||||
|
title={row.page.page_name}
|
||||||
|
data-testid={`notion-page-name-${pageId}`}
|
||||||
|
>
|
||||||
|
{row.page.page_name}
|
||||||
|
</div>
|
||||||
|
{showPreview && (
|
||||||
|
<div
|
||||||
|
className="ml-1 hidden h-6 shrink-0 cursor-pointer items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-xs
|
||||||
|
leading-4 font-medium text-components-button-secondary-text shadow-xs shadow-shadow-shadow-3 backdrop-blur-[10px]
|
||||||
|
group-hover:flex hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover"
|
||||||
|
onClick={() => onPreview(pageId)}
|
||||||
|
data-testid={`notion-page-preview-${pageId}`}
|
||||||
|
>
|
||||||
|
{t('dataSource.notion.selector.preview', { ns: 'common' })}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{searchValue && (
|
||||||
|
<div
|
||||||
|
className="ml-1 max-w-[120px] shrink-0 truncate text-xs text-text-quaternary"
|
||||||
|
title={breadcrumbs.join(' / ')}
|
||||||
|
>
|
||||||
|
{breadcrumbs.join(' / ')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(NotionPageRow)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import type { DataSourceNotionPage } from '@/models/common'
|
||||||
|
|
||||||
|
export type NotionPageSelectionMode = 'multiple' | 'single'
|
||||||
|
|
||||||
|
export type NotionPageTreeItem = {
|
||||||
|
children: Set<string>
|
||||||
|
descendants: Set<string>
|
||||||
|
depth: number
|
||||||
|
ancestors: string[]
|
||||||
|
} & DataSourceNotionPage
|
||||||
|
|
||||||
|
export type NotionPageTreeMap = Record<string, NotionPageTreeItem>
|
||||||
|
|
||||||
|
export type NotionPageRow = {
|
||||||
|
page: DataSourceNotionPage
|
||||||
|
parentExists: boolean
|
||||||
|
depth: number
|
||||||
|
expand: boolean
|
||||||
|
hasChild: boolean
|
||||||
|
ancestors: string[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import type { NotionPageSelectionMode } from './types'
|
||||||
|
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||||
|
import { startTransition, useCallback, useDeferredValue, useMemo, useState } from 'react'
|
||||||
|
import { buildNotionPageTree, getNextSelectedPageIds, getRootPageIds, getVisiblePageRows } from './utils'
|
||||||
|
|
||||||
|
type UsePageSelectorModelProps = {
|
||||||
|
checkedIds: Set<string>
|
||||||
|
searchValue: string
|
||||||
|
pagesMap: DataSourceNotionPageMap
|
||||||
|
list: DataSourceNotionPage[]
|
||||||
|
onSelect: (selectedPagesId: Set<string>) => void
|
||||||
|
previewPageId?: string
|
||||||
|
onPreview?: (selectedPageId: string) => void
|
||||||
|
selectionMode: NotionPageSelectionMode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const usePageSelectorModel = ({
|
||||||
|
checkedIds,
|
||||||
|
searchValue,
|
||||||
|
pagesMap,
|
||||||
|
list,
|
||||||
|
onSelect,
|
||||||
|
previewPageId,
|
||||||
|
onPreview,
|
||||||
|
selectionMode,
|
||||||
|
}: UsePageSelectorModelProps) => {
|
||||||
|
const deferredSearchValue = useDeferredValue(searchValue)
|
||||||
|
const [expandedIds, setExpandedIds] = useState<Set<string>>(() => new Set())
|
||||||
|
const [localPreviewPageId, setLocalPreviewPageId] = useState('')
|
||||||
|
|
||||||
|
const treeMap = useMemo(() => buildNotionPageTree(list, pagesMap), [list, pagesMap])
|
||||||
|
const rootPageIds = useMemo(() => getRootPageIds(list, pagesMap), [list, pagesMap])
|
||||||
|
|
||||||
|
const rows = useMemo(() => {
|
||||||
|
return getVisiblePageRows({
|
||||||
|
list,
|
||||||
|
pagesMap,
|
||||||
|
searchValue: deferredSearchValue,
|
||||||
|
treeMap,
|
||||||
|
rootPageIds,
|
||||||
|
expandedIds,
|
||||||
|
})
|
||||||
|
}, [deferredSearchValue, expandedIds, list, pagesMap, rootPageIds, treeMap])
|
||||||
|
|
||||||
|
const currentPreviewPageId = previewPageId ?? localPreviewPageId
|
||||||
|
|
||||||
|
const handleToggle = useCallback((pageId: string) => {
|
||||||
|
startTransition(() => {
|
||||||
|
setExpandedIds((currentExpandedIds) => {
|
||||||
|
const nextExpandedIds = new Set(currentExpandedIds)
|
||||||
|
|
||||||
|
if (nextExpandedIds.has(pageId)) {
|
||||||
|
nextExpandedIds.delete(pageId)
|
||||||
|
treeMap[pageId]?.descendants.forEach(descendantId => nextExpandedIds.delete(descendantId))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nextExpandedIds.add(pageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextExpandedIds
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [treeMap])
|
||||||
|
|
||||||
|
const handleSelect = useCallback((pageId: string) => {
|
||||||
|
onSelect(getNextSelectedPageIds({
|
||||||
|
checkedIds,
|
||||||
|
pageId,
|
||||||
|
searchValue: deferredSearchValue,
|
||||||
|
selectionMode,
|
||||||
|
treeMap,
|
||||||
|
}))
|
||||||
|
}, [checkedIds, deferredSearchValue, onSelect, selectionMode, treeMap])
|
||||||
|
|
||||||
|
const handlePreview = useCallback((pageId: string) => {
|
||||||
|
setLocalPreviewPageId(pageId)
|
||||||
|
onPreview?.(pageId)
|
||||||
|
}, [onPreview])
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPreviewPageId,
|
||||||
|
effectiveSearchValue: deferredSearchValue,
|
||||||
|
rows,
|
||||||
|
handlePreview,
|
||||||
|
handleSelect,
|
||||||
|
handleToggle,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import type { NotionPageRow, NotionPageSelectionMode, NotionPageTreeItem, NotionPageTreeMap } from './types'
|
||||||
|
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||||
|
|
||||||
|
export const recursivePushInParentDescendants = (
|
||||||
|
pagesMap: DataSourceNotionPageMap,
|
||||||
|
listTreeMap: NotionPageTreeMap,
|
||||||
|
current: NotionPageTreeItem,
|
||||||
|
leafItem: NotionPageTreeItem,
|
||||||
|
) => {
|
||||||
|
const parentId = current.parent_id
|
||||||
|
const pageId = current.page_id
|
||||||
|
|
||||||
|
if (!parentId || !pageId)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (parentId !== 'root' && pagesMap[parentId]) {
|
||||||
|
if (!listTreeMap[parentId]) {
|
||||||
|
const children = new Set([pageId])
|
||||||
|
const descendants = new Set([pageId, leafItem.page_id])
|
||||||
|
listTreeMap[parentId] = {
|
||||||
|
...pagesMap[parentId],
|
||||||
|
children,
|
||||||
|
descendants,
|
||||||
|
depth: 0,
|
||||||
|
ancestors: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listTreeMap[parentId].children.add(pageId)
|
||||||
|
listTreeMap[parentId].descendants.add(pageId)
|
||||||
|
listTreeMap[parentId].descendants.add(leafItem.page_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
leafItem.depth++
|
||||||
|
leafItem.ancestors.unshift(listTreeMap[parentId].page_name)
|
||||||
|
|
||||||
|
if (listTreeMap[parentId].parent_id !== 'root')
|
||||||
|
recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap[parentId], leafItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const buildNotionPageTree = (
|
||||||
|
list: DataSourceNotionPage[],
|
||||||
|
pagesMap: DataSourceNotionPageMap,
|
||||||
|
): NotionPageTreeMap => {
|
||||||
|
return list.reduce((prev: NotionPageTreeMap, next) => {
|
||||||
|
const pageId = next.page_id
|
||||||
|
if (!prev[pageId])
|
||||||
|
prev[pageId] = { ...next, children: new Set(), descendants: new Set(), depth: 0, ancestors: [] }
|
||||||
|
|
||||||
|
recursivePushInParentDescendants(pagesMap, prev, prev[pageId], prev[pageId])
|
||||||
|
return prev
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRootPageIds = (
|
||||||
|
list: DataSourceNotionPage[],
|
||||||
|
pagesMap: DataSourceNotionPageMap,
|
||||||
|
) => {
|
||||||
|
return list
|
||||||
|
.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id])
|
||||||
|
.map(item => item.page_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getVisiblePageRows = ({
|
||||||
|
list,
|
||||||
|
pagesMap,
|
||||||
|
searchValue,
|
||||||
|
treeMap,
|
||||||
|
rootPageIds,
|
||||||
|
expandedIds,
|
||||||
|
}: {
|
||||||
|
list: DataSourceNotionPage[]
|
||||||
|
pagesMap: DataSourceNotionPageMap
|
||||||
|
searchValue: string
|
||||||
|
treeMap: NotionPageTreeMap
|
||||||
|
rootPageIds: string[]
|
||||||
|
expandedIds: Set<string>
|
||||||
|
}): NotionPageRow[] => {
|
||||||
|
if (searchValue) {
|
||||||
|
return list
|
||||||
|
.filter(item => item.page_name.includes(searchValue))
|
||||||
|
.map(item => ({
|
||||||
|
page: item,
|
||||||
|
parentExists: item.parent_id !== 'root' && Boolean(pagesMap[item.parent_id]),
|
||||||
|
depth: treeMap[item.page_id]?.depth ?? 0,
|
||||||
|
expand: false,
|
||||||
|
hasChild: (treeMap[item.page_id]?.children.size ?? 0) > 0,
|
||||||
|
ancestors: treeMap[item.page_id]?.ancestors ?? [],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows: NotionPageRow[] = []
|
||||||
|
|
||||||
|
const visit = (pageId: string) => {
|
||||||
|
const current = treeMap[pageId]
|
||||||
|
if (!current)
|
||||||
|
return
|
||||||
|
|
||||||
|
const expand = expandedIds.has(pageId)
|
||||||
|
rows.push({
|
||||||
|
page: current,
|
||||||
|
parentExists: current.parent_id !== 'root' && Boolean(pagesMap[current.parent_id]),
|
||||||
|
depth: current.depth,
|
||||||
|
expand,
|
||||||
|
hasChild: current.children.size > 0,
|
||||||
|
ancestors: current.ancestors,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!expand)
|
||||||
|
return
|
||||||
|
|
||||||
|
current.children.forEach(visit)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootPageIds.forEach(visit)
|
||||||
|
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNextSelectedPageIds = ({
|
||||||
|
checkedIds,
|
||||||
|
pageId,
|
||||||
|
searchValue,
|
||||||
|
selectionMode,
|
||||||
|
treeMap,
|
||||||
|
}: {
|
||||||
|
checkedIds: Set<string>
|
||||||
|
pageId: string
|
||||||
|
searchValue: string
|
||||||
|
selectionMode: NotionPageSelectionMode
|
||||||
|
treeMap: NotionPageTreeMap
|
||||||
|
}) => {
|
||||||
|
const nextCheckedIds = new Set(checkedIds)
|
||||||
|
const descendants = treeMap[pageId]?.descendants ?? new Set<string>()
|
||||||
|
|
||||||
|
if (selectionMode === 'single') {
|
||||||
|
if (nextCheckedIds.has(pageId)) {
|
||||||
|
nextCheckedIds.delete(pageId)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nextCheckedIds.clear()
|
||||||
|
nextCheckedIds.add(pageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextCheckedIds
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextCheckedIds.has(pageId)) {
|
||||||
|
if (!searchValue)
|
||||||
|
descendants.forEach(item => nextCheckedIds.delete(item))
|
||||||
|
|
||||||
|
nextCheckedIds.delete(pageId)
|
||||||
|
return nextCheckedIds
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchValue)
|
||||||
|
descendants.forEach(item => nextCheckedIds.add(item))
|
||||||
|
|
||||||
|
nextCheckedIds.add(pageId)
|
||||||
|
|
||||||
|
return nextCheckedIds
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import type { NotionPageRow, NotionPageSelectionMode } from './types'
|
||||||
|
import { useVirtualizer } from '@tanstack/react-virtual'
|
||||||
|
import { useRef } from 'react'
|
||||||
|
import PageRow from './page-row'
|
||||||
|
|
||||||
|
type VirtualPageListProps = {
|
||||||
|
checkedIds: Set<string>
|
||||||
|
disabledValue: Set<string>
|
||||||
|
onPreview: (pageId: string) => void
|
||||||
|
onSelect: (pageId: string) => void
|
||||||
|
onToggle: (pageId: string) => void
|
||||||
|
previewPageId: string
|
||||||
|
rows: NotionPageRow[]
|
||||||
|
searchValue: string
|
||||||
|
selectionMode: NotionPageSelectionMode
|
||||||
|
showPreview: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowHeight = 28
|
||||||
|
|
||||||
|
const VirtualPageList = ({
|
||||||
|
checkedIds,
|
||||||
|
disabledValue,
|
||||||
|
onPreview,
|
||||||
|
onSelect,
|
||||||
|
onToggle,
|
||||||
|
previewPageId,
|
||||||
|
rows,
|
||||||
|
searchValue,
|
||||||
|
selectionMode,
|
||||||
|
showPreview,
|
||||||
|
}: VirtualPageListProps) => {
|
||||||
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const rowVirtualizer = useVirtualizer({
|
||||||
|
count: rows.length,
|
||||||
|
estimateSize: () => rowHeight,
|
||||||
|
getScrollElement: () => scrollRef.current,
|
||||||
|
overscan: 6,
|
||||||
|
paddingEnd: 8,
|
||||||
|
paddingStart: 8,
|
||||||
|
})
|
||||||
|
|
||||||
|
const virtualRows = rowVirtualizer.getVirtualItems()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={scrollRef}
|
||||||
|
className="h-[296px] overflow-auto"
|
||||||
|
data-testid="virtual-list"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: `${rowVirtualizer.getTotalSize()}px`,
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{virtualRows.map((virtualRow) => {
|
||||||
|
const row = rows[virtualRow.index]
|
||||||
|
const pageId = row.page.page_id
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageRow
|
||||||
|
key={pageId}
|
||||||
|
checked={checkedIds.has(pageId)}
|
||||||
|
disabled={disabledValue.has(pageId)}
|
||||||
|
isPreviewed={previewPageId === pageId}
|
||||||
|
onPreview={onPreview}
|
||||||
|
onSelect={onSelect}
|
||||||
|
onToggle={onToggle}
|
||||||
|
row={row}
|
||||||
|
searchValue={searchValue}
|
||||||
|
selectionMode={selectionMode}
|
||||||
|
showPreview={showPreview}
|
||||||
|
style={{
|
||||||
|
height: `${virtualRow.size}px`,
|
||||||
|
left: 8,
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
transform: `translateY(${virtualRow.start}px)`,
|
||||||
|
width: 'calc(100% - 16px)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VirtualPageList
|
||||||
@@ -106,7 +106,7 @@ const OnlineDocuments = ({
|
|||||||
if (!currentCredentialId)
|
if (!currentCredentialId)
|
||||||
return
|
return
|
||||||
getOnlineDocuments()
|
getOnlineDocuments()
|
||||||
}, [currentCredentialId])
|
}, [currentCredentialId, getOnlineDocuments])
|
||||||
|
|
||||||
const handleSearchValueChange = useCallback((value: string) => {
|
const handleSearchValueChange = useCallback((value: string) => {
|
||||||
const { setSearchValue } = dataSourceStore.getState()
|
const { setSearchValue } = dataSourceStore.getState()
|
||||||
@@ -156,6 +156,7 @@ const OnlineDocuments = ({
|
|||||||
{documentsData?.length
|
{documentsData?.length
|
||||||
? (
|
? (
|
||||||
<PageSelector
|
<PageSelector
|
||||||
|
key={`${currentCredentialId}:${supportBatchUpload ? 'multiple' : 'single'}`}
|
||||||
checkedIds={selectedPagesId}
|
checkedIds={selectedPagesId}
|
||||||
disabledValue={new Set()}
|
disabledValue={new Set()}
|
||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
@@ -165,7 +166,6 @@ const OnlineDocuments = ({
|
|||||||
canPreview={!isInPipeline}
|
canPreview={!isInPipeline}
|
||||||
onPreview={handlePreviewPage}
|
onPreview={handlePreviewPage}
|
||||||
isMultipleChoice={supportBatchUpload}
|
isMultipleChoice={supportBatchUpload}
|
||||||
currentCredentialId={currentCredentialId}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
|
|||||||
@@ -1,26 +1,11 @@
|
|||||||
import type { NotionPageTreeItem, NotionPageTreeMap } from '../index'
|
import type { NotionPageTreeItem, NotionPageTreeMap } from '@/app/components/base/notion-page-selector/page-selector/types'
|
||||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||||
import { fireEvent, render, screen } from '@testing-library/react'
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import { recursivePushInParentDescendants } from '@/app/components/base/notion-page-selector/page-selector/utils'
|
||||||
import PageSelector from '../index'
|
import PageSelector from '../index'
|
||||||
import { recursivePushInParentDescendants } from '../utils'
|
|
||||||
|
|
||||||
// Mock react-window FixedSizeList - renders items directly for testing
|
vi.mock('@tanstack/react-virtual')
|
||||||
vi.mock('react-window', () => ({
|
|
||||||
FixedSizeList: ({ children: ItemComponent, itemCount, itemData, itemKey }: { children: React.ComponentType<{ index: number, style: React.CSSProperties, data: unknown }>, itemCount: number, itemData: unknown, itemKey?: (index: number, data: unknown) => string | number }) => (
|
|
||||||
<div data-testid="virtual-list">
|
|
||||||
{Array.from({ length: itemCount }).map((_, index) => (
|
|
||||||
<ItemComponent
|
|
||||||
key={itemKey?.(index, itemData) || index}
|
|
||||||
index={index}
|
|
||||||
style={{ top: index * 28, left: 0, right: 0, width: '100%', position: 'absolute' as const }}
|
|
||||||
data={itemData}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
areEqual: (prevProps: Record<string, unknown>, nextProps: Record<string, unknown>) => prevProps === nextProps,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Note: NotionIcon from @/app/components/base/ is NOT mocked - using real component per testing guidelines
|
// Note: NotionIcon from @/app/components/base/ is NOT mocked - using real component per testing guidelines
|
||||||
|
|
||||||
@@ -70,7 +55,6 @@ const createDefaultProps = (overrides?: Partial<PageSelectorProps>): PageSelecto
|
|||||||
canPreview: true,
|
canPreview: true,
|
||||||
onPreview: vi.fn(),
|
onPreview: vi.fn(),
|
||||||
isMultipleChoice: true,
|
isMultipleChoice: true,
|
||||||
currentCredentialId: 'cred-1',
|
|
||||||
...overrides,
|
...overrides,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +98,7 @@ describe('PageSelector', () => {
|
|||||||
expect(screen.queryByTestId('virtual-list')).not.toBeInTheDocument()
|
expect(screen.queryByTestId('virtual-list')).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render items using FixedSizeList', () => {
|
it('should render items using VirtualList', () => {
|
||||||
const pages = [
|
const pages = [
|
||||||
createMockPage({ page_id: 'page-1', page_name: 'Page 1' }),
|
createMockPage({ page_id: 'page-1', page_name: 'Page 1' }),
|
||||||
createMockPage({ page_id: 'page-2', page_name: 'Page 2' }),
|
createMockPage({ page_id: 'page-2', page_name: 'Page 2' }),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NotionPageTreeItem, NotionPageTreeMap } from '../index'
|
import type { NotionPageTreeItem, NotionPageTreeMap } from '@/app/components/base/notion-page-selector/page-selector/types'
|
||||||
import type { DataSourceNotionPageMap } from '@/models/common'
|
import type { DataSourceNotionPageMap } from '@/models/common'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { recursivePushInParentDescendants } from '../utils'
|
import { recursivePushInParentDescendants } from '@/app/components/base/notion-page-selector/page-selector/utils'
|
||||||
|
|
||||||
const makePageEntry = (overrides: Partial<NotionPageTreeItem>): NotionPageTreeItem => ({
|
const makePageEntry = (overrides: Partial<NotionPageTreeItem>): NotionPageTreeItem => ({
|
||||||
page_icon: null,
|
page_icon: null,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FixedSizeList as List } from 'react-window'
|
import { usePageSelectorModel } from '@/app/components/base/notion-page-selector/page-selector/use-page-selector-model'
|
||||||
import Item from './item'
|
import VirtualPageList from '@/app/components/base/notion-page-selector/page-selector/virtual-page-list'
|
||||||
import { recursivePushInParentDescendants } from './utils'
|
|
||||||
|
|
||||||
type PageSelectorProps = {
|
type PageSelectorProps = {
|
||||||
checkedIds: Set<string>
|
checkedIds: Set<string>
|
||||||
@@ -15,23 +13,9 @@ type PageSelectorProps = {
|
|||||||
canPreview?: boolean
|
canPreview?: boolean
|
||||||
onPreview?: (selectedPageId: string) => void
|
onPreview?: (selectedPageId: string) => void
|
||||||
isMultipleChoice?: boolean
|
isMultipleChoice?: boolean
|
||||||
currentCredentialId: string
|
currentCredentialId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NotionPageTreeItem = {
|
|
||||||
children: Set<string>
|
|
||||||
descendants: Set<string>
|
|
||||||
depth: number
|
|
||||||
ancestors: string[]
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
|
|
||||||
export type NotionPageTreeMap = Record<string, NotionPageTreeItem>
|
|
||||||
|
|
||||||
type NotionPageItem = {
|
|
||||||
expand: boolean
|
|
||||||
depth: number
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
|
|
||||||
const PageSelector = ({
|
const PageSelector = ({
|
||||||
checkedIds,
|
checkedIds,
|
||||||
disabledValue,
|
disabledValue,
|
||||||
@@ -42,116 +26,28 @@ const PageSelector = ({
|
|||||||
canPreview = true,
|
canPreview = true,
|
||||||
onPreview,
|
onPreview,
|
||||||
isMultipleChoice = true,
|
isMultipleChoice = true,
|
||||||
currentCredentialId,
|
currentCredentialId: _currentCredentialId,
|
||||||
}: PageSelectorProps) => {
|
}: PageSelectorProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [dataList, setDataList] = useState<NotionPageItem[]>([])
|
const selectionMode = isMultipleChoice ? 'multiple' : 'single'
|
||||||
const [currentPreviewPageId, setCurrentPreviewPageId] = useState('')
|
const {
|
||||||
|
currentPreviewPageId,
|
||||||
useEffect(() => {
|
effectiveSearchValue,
|
||||||
setDataList(list.filter(item => item.parent_id === 'root' || !pagesMap[item.parent_id]).map((item) => {
|
rows,
|
||||||
return {
|
handlePreview,
|
||||||
...item,
|
handleSelect,
|
||||||
expand: false,
|
handleToggle,
|
||||||
depth: 0,
|
} = usePageSelectorModel({
|
||||||
}
|
checkedIds,
|
||||||
}))
|
list,
|
||||||
}, [currentCredentialId])
|
onPreview,
|
||||||
|
onSelect,
|
||||||
const searchDataList = list.filter((item) => {
|
pagesMap,
|
||||||
return item.page_name.includes(searchValue)
|
searchValue,
|
||||||
}).map((item) => {
|
selectionMode,
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
expand: false,
|
|
||||||
depth: 0,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const currentDataList = searchValue ? searchDataList : dataList
|
|
||||||
|
|
||||||
const listMapWithChildrenAndDescendants = useMemo(() => {
|
if (!rows.length) {
|
||||||
return list.reduce((prev: NotionPageTreeMap, next: DataSourceNotionPage) => {
|
|
||||||
const pageId = next.page_id
|
|
||||||
if (!prev[pageId])
|
|
||||||
prev[pageId] = { ...next, children: new Set(), descendants: new Set(), depth: 0, ancestors: [] }
|
|
||||||
|
|
||||||
recursivePushInParentDescendants(pagesMap, prev, prev[pageId], prev[pageId])
|
|
||||||
return prev
|
|
||||||
}, {})
|
|
||||||
}, [list, pagesMap])
|
|
||||||
|
|
||||||
const handleToggle = useCallback((index: number) => {
|
|
||||||
const current = dataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
|
|
||||||
const descendantsIds = Array.from(currentWithChildrenAndDescendants.descendants)
|
|
||||||
const childrenIds = Array.from(currentWithChildrenAndDescendants.children)
|
|
||||||
let newDataList = []
|
|
||||||
|
|
||||||
if (current.expand) {
|
|
||||||
current.expand = false
|
|
||||||
|
|
||||||
newDataList = dataList.filter(item => !descendantsIds.includes(item.page_id))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
current.expand = true
|
|
||||||
|
|
||||||
newDataList = [
|
|
||||||
...dataList.slice(0, index + 1),
|
|
||||||
...childrenIds.map(item => ({
|
|
||||||
...pagesMap[item],
|
|
||||||
expand: false,
|
|
||||||
depth: listMapWithChildrenAndDescendants[item].depth,
|
|
||||||
})),
|
|
||||||
...dataList.slice(index + 1),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
setDataList(newDataList)
|
|
||||||
}, [dataList, listMapWithChildrenAndDescendants, pagesMap])
|
|
||||||
|
|
||||||
const handleCheck = useCallback((index: number) => {
|
|
||||||
const copyValue = new Set(checkedIds)
|
|
||||||
const current = currentDataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[pageId]
|
|
||||||
|
|
||||||
if (copyValue.has(pageId)) {
|
|
||||||
if (!searchValue && isMultipleChoice) {
|
|
||||||
for (const item of currentWithChildrenAndDescendants.descendants)
|
|
||||||
copyValue.delete(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
copyValue.delete(pageId)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!searchValue && isMultipleChoice) {
|
|
||||||
for (const item of currentWithChildrenAndDescendants.descendants)
|
|
||||||
copyValue.add(item)
|
|
||||||
}
|
|
||||||
// Single choice mode, clear previous selection
|
|
||||||
if (!isMultipleChoice && copyValue.size > 0) {
|
|
||||||
copyValue.clear()
|
|
||||||
copyValue.add(pageId)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
copyValue.add(pageId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelect(new Set(copyValue))
|
|
||||||
}, [currentDataList, isMultipleChoice, listMapWithChildrenAndDescendants, onSelect, searchValue, checkedIds])
|
|
||||||
|
|
||||||
const handlePreview = useCallback((index: number) => {
|
|
||||||
const current = currentDataList[index]
|
|
||||||
const pageId = current.page_id
|
|
||||||
|
|
||||||
setCurrentPreviewPageId(pageId)
|
|
||||||
|
|
||||||
if (onPreview)
|
|
||||||
onPreview(pageId)
|
|
||||||
}, [currentDataList, onPreview])
|
|
||||||
|
|
||||||
if (!currentDataList.length) {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[296px] items-center justify-center text-[13px] text-text-tertiary">
|
<div className="flex h-[296px] items-center justify-center text-[13px] text-text-tertiary">
|
||||||
{t('dataSource.notion.selector.noSearchResult', { ns: 'common' })}
|
{t('dataSource.notion.selector.noSearchResult', { ns: 'common' })}
|
||||||
@@ -160,30 +56,18 @@ const PageSelector = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List
|
<VirtualPageList
|
||||||
className="py-2"
|
checkedIds={checkedIds}
|
||||||
height={296}
|
disabledValue={disabledValue}
|
||||||
itemCount={currentDataList.length}
|
onPreview={handlePreview}
|
||||||
itemSize={28}
|
onSelect={handleSelect}
|
||||||
width="100%"
|
onToggle={handleToggle}
|
||||||
itemKey={(index, data) => data.dataList[index].page_id}
|
previewPageId={currentPreviewPageId}
|
||||||
itemData={{
|
rows={rows}
|
||||||
dataList: currentDataList,
|
searchValue={effectiveSearchValue}
|
||||||
handleToggle,
|
selectionMode={selectionMode}
|
||||||
checkedIds,
|
showPreview={canPreview}
|
||||||
disabledCheckedIds: disabledValue,
|
/>
|
||||||
handleCheck,
|
|
||||||
canPreview,
|
|
||||||
handlePreview,
|
|
||||||
listMapWithChildrenAndDescendants,
|
|
||||||
searchValue,
|
|
||||||
previewPageId: currentPreviewPageId,
|
|
||||||
pagesMap,
|
|
||||||
isMultipleChoice,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Item}
|
|
||||||
</List>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,152 +0,0 @@
|
|||||||
import type { ListChildComponentProps } from 'react-window'
|
|
||||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
|
||||||
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
|
||||||
import * as React from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { areEqual } from 'react-window'
|
|
||||||
import Checkbox from '@/app/components/base/checkbox'
|
|
||||||
import NotionIcon from '@/app/components/base/notion-icon'
|
|
||||||
import Radio from '@/app/components/base/radio/ui'
|
|
||||||
import { cn } from '@/utils/classnames'
|
|
||||||
|
|
||||||
type NotionPageTreeItem = {
|
|
||||||
children: Set<string>
|
|
||||||
descendants: Set<string>
|
|
||||||
depth: number
|
|
||||||
ancestors: string[]
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
|
|
||||||
type NotionPageTreeMap = Record<string, NotionPageTreeItem>
|
|
||||||
|
|
||||||
type NotionPageItem = {
|
|
||||||
expand: boolean
|
|
||||||
depth: number
|
|
||||||
} & DataSourceNotionPage
|
|
||||||
|
|
||||||
const Item = ({ index, style, data }: ListChildComponentProps<{
|
|
||||||
dataList: NotionPageItem[]
|
|
||||||
handleToggle: (index: number) => void
|
|
||||||
checkedIds: Set<string>
|
|
||||||
disabledCheckedIds: Set<string>
|
|
||||||
handleCheck: (index: number) => void
|
|
||||||
canPreview?: boolean
|
|
||||||
handlePreview: (index: number) => void
|
|
||||||
listMapWithChildrenAndDescendants: NotionPageTreeMap
|
|
||||||
searchValue: string
|
|
||||||
previewPageId: string
|
|
||||||
pagesMap: DataSourceNotionPageMap
|
|
||||||
isMultipleChoice?: boolean
|
|
||||||
}>) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const {
|
|
||||||
dataList,
|
|
||||||
handleToggle,
|
|
||||||
checkedIds,
|
|
||||||
disabledCheckedIds,
|
|
||||||
handleCheck,
|
|
||||||
canPreview,
|
|
||||||
handlePreview,
|
|
||||||
listMapWithChildrenAndDescendants,
|
|
||||||
searchValue,
|
|
||||||
previewPageId,
|
|
||||||
pagesMap,
|
|
||||||
isMultipleChoice,
|
|
||||||
} = data
|
|
||||||
const current = dataList[index]
|
|
||||||
const currentWithChildrenAndDescendants = listMapWithChildrenAndDescendants[current.page_id]
|
|
||||||
const hasChild = currentWithChildrenAndDescendants.descendants.size > 0
|
|
||||||
const ancestors = currentWithChildrenAndDescendants.ancestors
|
|
||||||
const breadCrumbs = ancestors.length ? [...ancestors, current.page_name] : [current.page_name]
|
|
||||||
const disabled = disabledCheckedIds.has(current.page_id)
|
|
||||||
|
|
||||||
const renderArrow = () => {
|
|
||||||
if (hasChild) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="mr-1 flex h-5 w-5 shrink-0 items-center justify-center rounded-md hover:bg-components-button-ghost-bg-hover"
|
|
||||||
style={{ marginLeft: current.depth * 8 }}
|
|
||||||
onClick={() => handleToggle(index)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
current.expand
|
|
||||||
? <RiArrowDownSLine className="h-4 w-4 text-text-tertiary" />
|
|
||||||
: <RiArrowRightSLine className="h-4 w-4 text-text-tertiary" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (current.parent_id === 'root' || !pagesMap[current.parent_id]) {
|
|
||||||
return (
|
|
||||||
<div></div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="mr-1 h-5 w-5 shrink-0" style={{ marginLeft: current.depth * 8 }} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn('group flex cursor-pointer items-center rounded-md pl-2 pr-[2px] hover:bg-state-base-hover', previewPageId === current.page_id && 'bg-state-base-hover')}
|
|
||||||
style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
|
|
||||||
>
|
|
||||||
{isMultipleChoice
|
|
||||||
? (
|
|
||||||
<Checkbox
|
|
||||||
className="mr-2 shrink-0"
|
|
||||||
checked={checkedIds.has(current.page_id)}
|
|
||||||
disabled={disabled}
|
|
||||||
onCheck={() => {
|
|
||||||
handleCheck(index)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Radio
|
|
||||||
className="mr-2 shrink-0"
|
|
||||||
isChecked={checkedIds.has(current.page_id)}
|
|
||||||
disabled={disabled}
|
|
||||||
onCheck={() => {
|
|
||||||
handleCheck(index)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!searchValue && renderArrow()}
|
|
||||||
<NotionIcon
|
|
||||||
className="mr-1 shrink-0"
|
|
||||||
type="page"
|
|
||||||
src={current.page_icon}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="grow truncate text-[13px] font-medium leading-4 text-text-secondary"
|
|
||||||
title={current.page_name}
|
|
||||||
>
|
|
||||||
{current.page_name}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
canPreview && (
|
|
||||||
<div
|
|
||||||
className="ml-1 hidden h-6 shrink-0 cursor-pointer items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-xs
|
|
||||||
font-medium leading-4 text-components-button-secondary-text shadow-xs shadow-shadow-shadow-3 backdrop-blur-[10px]
|
|
||||||
hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover group-hover:flex"
|
|
||||||
onClick={() => handlePreview(index)}
|
|
||||||
>
|
|
||||||
{t('dataSource.notion.selector.preview', { ns: 'common' })}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
searchValue && (
|
|
||||||
<div
|
|
||||||
className="ml-1 max-w-[120px] shrink-0 truncate text-xs text-text-quaternary"
|
|
||||||
title={breadCrumbs.join(' / ')}
|
|
||||||
>
|
|
||||||
{breadCrumbs.join(' / ')}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default React.memo(Item, areEqual)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { NotionPageTreeItem, NotionPageTreeMap } from './index'
|
|
||||||
import type { DataSourceNotionPageMap } from '@/models/common'
|
|
||||||
|
|
||||||
export const recursivePushInParentDescendants = (
|
|
||||||
pagesMap: DataSourceNotionPageMap,
|
|
||||||
listTreeMap: NotionPageTreeMap,
|
|
||||||
current: NotionPageTreeItem,
|
|
||||||
leafItem: NotionPageTreeItem,
|
|
||||||
) => {
|
|
||||||
const parentId = current.parent_id
|
|
||||||
const pageId = current.page_id
|
|
||||||
|
|
||||||
if (!parentId || !pageId)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (parentId !== 'root' && pagesMap[parentId]) {
|
|
||||||
if (!listTreeMap[parentId]) {
|
|
||||||
const children = new Set([pageId])
|
|
||||||
const descendants = new Set([pageId, leafItem.page_id])
|
|
||||||
listTreeMap[parentId] = {
|
|
||||||
...pagesMap[parentId],
|
|
||||||
children,
|
|
||||||
descendants,
|
|
||||||
depth: 0,
|
|
||||||
ancestors: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
listTreeMap[parentId].children.add(pageId)
|
|
||||||
listTreeMap[parentId].descendants.add(pageId)
|
|
||||||
listTreeMap[parentId].descendants.add(leafItem.page_id)
|
|
||||||
}
|
|
||||||
leafItem.depth++
|
|
||||||
leafItem.ancestors.unshift(listTreeMap[parentId].page_name)
|
|
||||||
|
|
||||||
if (listTreeMap[parentId].parent_id !== 'root')
|
|
||||||
recursivePushInParentDescendants(pagesMap, listTreeMap, listTreeMap[parentId], leafItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1070,9 +1070,6 @@
|
|||||||
filter: invert(50%);
|
filter: invert(50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-body .react-syntax-highlighter-line-number {
|
|
||||||
color: var(--color-text-quaternary);
|
|
||||||
}
|
|
||||||
.markdown-body .abcjs-inline-audio .abcjs-btn {
|
.markdown-body .abcjs-inline-audio .abcjs-btn {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3328,11 +3328,6 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/components/base/notion-page-selector/base.tsx": {
|
|
||||||
"react/set-state-in-effect": {
|
|
||||||
"count": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/components/base/notion-page-selector/credential-selector/index.tsx": {
|
"app/components/base/notion-page-selector/credential-selector/index.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 2
|
"count": 2
|
||||||
@@ -3348,14 +3343,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/components/base/notion-page-selector/page-selector/index.tsx": {
|
|
||||||
"react/set-state-in-effect": {
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/components/base/notion-page-selector/search-input/index.tsx": {
|
"app/components/base/notion-page-selector/search-input/index.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -4831,16 +4818,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/index.tsx": {
|
|
||||||
"react/set-state-in-effect": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/item.tsx": {
|
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx": {
|
"app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 1
|
"count": 1
|
||||||
|
|||||||
@@ -135,6 +135,9 @@
|
|||||||
"dataSource.notion.pagesAuthorized": "Pages authorized",
|
"dataSource.notion.pagesAuthorized": "Pages authorized",
|
||||||
"dataSource.notion.remove": "Remove",
|
"dataSource.notion.remove": "Remove",
|
||||||
"dataSource.notion.selector.addPages": "Add pages",
|
"dataSource.notion.selector.addPages": "Add pages",
|
||||||
|
"dataSource.notion.selector.configure": "Configure Notion",
|
||||||
|
"dataSource.notion.selector.docs": "Notion docs",
|
||||||
|
"dataSource.notion.selector.headerTitle": "Choose Notion pages",
|
||||||
"dataSource.notion.selector.noSearchResult": "No search results",
|
"dataSource.notion.selector.noSearchResult": "No search results",
|
||||||
"dataSource.notion.selector.pageSelected": "Pages Selected",
|
"dataSource.notion.selector.pageSelected": "Pages Selected",
|
||||||
"dataSource.notion.selector.preview": "PREVIEW",
|
"dataSource.notion.selector.preview": "PREVIEW",
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
"@tailwindcss/typography": "catalog:",
|
"@tailwindcss/typography": "catalog:",
|
||||||
"@tanstack/react-form": "catalog:",
|
"@tanstack/react-form": "catalog:",
|
||||||
"@tanstack/react-query": "catalog:",
|
"@tanstack/react-query": "catalog:",
|
||||||
|
"@tanstack/react-virtual": "catalog:",
|
||||||
"abcjs": "catalog:",
|
"abcjs": "catalog:",
|
||||||
"ahooks": "catalog:",
|
"ahooks": "catalog:",
|
||||||
"class-variance-authority": "catalog:",
|
"class-variance-authority": "catalog:",
|
||||||
@@ -136,7 +137,6 @@
|
|||||||
"react-pdf-highlighter": "catalog:",
|
"react-pdf-highlighter": "catalog:",
|
||||||
"react-sortablejs": "catalog:",
|
"react-sortablejs": "catalog:",
|
||||||
"react-textarea-autosize": "catalog:",
|
"react-textarea-autosize": "catalog:",
|
||||||
"react-window": "catalog:",
|
|
||||||
"reactflow": "catalog:",
|
"reactflow": "catalog:",
|
||||||
"remark-breaks": "catalog:",
|
"remark-breaks": "catalog:",
|
||||||
"remark-directive": "catalog:",
|
"remark-directive": "catalog:",
|
||||||
@@ -197,7 +197,6 @@
|
|||||||
"@types/qs": "catalog:",
|
"@types/qs": "catalog:",
|
||||||
"@types/react": "catalog:",
|
"@types/react": "catalog:",
|
||||||
"@types/react-dom": "catalog:",
|
"@types/react-dom": "catalog:",
|
||||||
"@types/react-window": "catalog:",
|
|
||||||
"@types/sortablejs": "catalog:",
|
"@types/sortablejs": "catalog:",
|
||||||
"@typescript-eslint/parser": "catalog:",
|
"@typescript-eslint/parser": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
|||||||
Reference in New Issue
Block a user