diff --git a/frontend/appflowy_web_app/index.html b/frontend/appflowy_web_app/index.html index 5480f37859..3d8bc89cd6 100644 --- a/frontend/appflowy_web_app/index.html +++ b/frontend/appflowy_web_app/index.html @@ -3,15 +3,44 @@ - + + AppFlowy
+ diff --git a/frontend/appflowy_web_app/package.json b/frontend/appflowy_web_app/package.json index 6eeb31ee09..8c8fd67627 100644 --- a/frontend/appflowy_web_app/package.json +++ b/frontend/appflowy_web_app/package.json @@ -12,7 +12,7 @@ "lint": "pnpm run sync:i18n && tsc --noEmit --project tsconfig.web.json && eslint --ext .js,.ts,.tsx . --ignore-path .eslintignore.web", "start": "vite preview --port 3000", "tauri:dev": "tauri dev", - "css:variables": "node style-dictionary/config.cjs", + "css:variables": "node scripts/generateTailwindColors.cjs", "sync:i18n": "node scripts/i18n.cjs", "link:client-api": "rm -rf node_modules/.vite && node scripts/create-symlink.cjs", "analyze": "cross-env ANALYZE_MODE=true vite build", @@ -38,6 +38,7 @@ "@types/react-swipeable-views": "^0.13.4", "async-retry": "^1.3.3", "axios": "^1.6.8", + "colorthief": "^2.4.0", "dayjs": "^1.11.9", "decimal.js": "^10.4.3", "emoji-mart": "^5.5.2", @@ -63,6 +64,7 @@ "react-big-calendar": "^1.8.5", "react-color": "^2.19.3", "react-custom-scrollbars": "^4.2.1", + "react-custom-scrollbars-2": "^4.5.0", "react-datepicker": "^4.23.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.13", @@ -88,7 +90,6 @@ "unsplash-js": "^7.0.19", "utf8": "^3.0.0", "validator": "^13.11.0", - "valtio": "^1.12.1", "vite-plugin-wasm": "^3.3.0", "y-indexeddb": "9.0.12", "yjs": "^13.6.14" diff --git a/frontend/appflowy_web_app/pnpm-lock.yaml b/frontend/appflowy_web_app/pnpm-lock.yaml index 7796ae7db8..03e1e7e411 100644 --- a/frontend/appflowy_web_app/pnpm-lock.yaml +++ b/frontend/appflowy_web_app/pnpm-lock.yaml @@ -10,49 +10,52 @@ dependencies: version: 0.0.3 '@atlaskit/primitives': specifier: ^5.5.3 - version: 5.5.3(@types/react@18.2.66)(react@18.2.0) + version: 5.7.0(@types/react@18.2.66)(react@18.2.0) '@emoji-mart/data': specifier: ^1.1.2 - version: 1.1.2 + version: 1.2.1 '@emoji-mart/react': specifier: ^1.1.1 - version: 1.1.1(emoji-mart@5.5.2)(react@18.2.0) + version: 1.1.1(emoji-mart@5.6.0)(react@18.2.0) '@emotion/react': specifier: ^11.10.6 - version: 11.10.6(@types/react@18.2.66)(react@18.2.0) + version: 11.11.4(@types/react@18.2.66)(react@18.2.0) '@emotion/styled': specifier: ^11.10.6 - version: 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) '@jest/globals': specifier: ^29.7.0 version: 29.7.0 '@mui/icons-material': specifier: ^5.11.11 - version: 5.11.11(@mui/material@6.0.0-alpha.2)(@types/react@18.2.66)(react@18.2.0) + version: 5.15.18(@mui/material@6.0.0-alpha.2)(@types/react@18.2.66)(react@18.2.0) '@mui/material': specifier: 6.0.0-alpha.2 - version: 6.0.0-alpha.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) + version: 6.0.0-alpha.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) '@mui/x-date-pickers-pro': specifier: ^6.18.2 - version: 6.18.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) + version: 6.20.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) '@reduxjs/toolkit': specifier: 2.0.0 - version: 2.0.0(react-redux@8.0.5)(react@18.2.0) + version: 2.0.0(react-redux@8.1.3)(react@18.2.0) '@slate-yjs/core': specifier: ^1.0.2 - version: 1.0.2(slate@0.101.4)(yjs@13.6.14) + version: 1.0.2(slate@0.101.5)(yjs@13.6.15) '@tauri-apps/api': specifier: ^1.5.3 - version: 1.5.3 + version: 1.5.6 '@types/react-swipeable-views': specifier: ^0.13.4 - version: 0.13.4 + version: 0.13.5 async-retry: specifier: ^1.3.3 version: 1.3.3 axios: specifier: ^1.6.8 - version: 1.6.8 + version: 1.7.2 + colorthief: + specifier: ^2.4.0 + version: 2.4.0 dayjs: specifier: ^1.11.9 version: 1.11.9 @@ -61,25 +64,25 @@ dependencies: version: 10.4.3 emoji-mart: specifier: ^5.5.2 - version: 5.5.2 + version: 5.6.0 emoji-regex: specifier: ^10.2.1 - version: 10.2.1 + version: 10.3.0 events: specifier: ^3.3.0 version: 3.3.0 google-protobuf: specifier: ^3.15.12 - version: 3.16.0 + version: 3.21.2 i18next: specifier: ^22.4.10 - version: 22.4.10 + version: 22.5.1 i18next-browser-languagedetector: specifier: ^7.0.1 - version: 7.0.1 + version: 7.2.1 i18next-resources-to-backend: specifier: ^1.1.4 - version: 1.1.4 + version: 1.2.1 is-hotkey: specifier: ^0.2.0 version: 0.2.0 @@ -88,16 +91,16 @@ dependencies: version: 29.5.0(@types/node@20.11.30) js-base64: specifier: ^3.7.5 - version: 3.7.5 + version: 3.7.7 katex: specifier: ^0.16.7 - version: 0.16.7 + version: 0.16.10 lodash-es: specifier: ^4.17.21 version: 4.17.21 nanoid: specifier: ^4.0.0 - version: 4.0.0 + version: 4.0.2 numeral: specifier: ^2.0.6 version: 2.0.6 @@ -121,16 +124,19 @@ dependencies: version: 13.1.1(react-dom@18.2.0)(react@18.2.0) react-big-calendar: specifier: ^1.8.5 - version: 1.8.5(react-dom@18.2.0)(react@18.2.0) + version: 1.12.2(react-dom@18.2.0)(react@18.2.0) react-color: specifier: ^2.19.3 version: 2.19.3(react@18.2.0) react-custom-scrollbars: specifier: ^4.2.1 version: 4.2.1(react-dom@18.2.0)(react@18.2.0) + react-custom-scrollbars-2: + specifier: ^4.5.0 + version: 4.5.0(react-dom@18.2.0)(react@18.2.0) react-datepicker: specifier: ^4.23.0 - version: 4.23.0(react-dom@18.2.0)(react@18.2.0) + version: 4.25.0(react-dom@18.2.0)(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -142,7 +148,7 @@ dependencies: version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0) react-i18next: specifier: ^14.1.0 - version: 14.1.0(i18next@22.4.10)(react-dom@18.2.0)(react@18.2.0) + version: 14.1.2(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0) react-katex: specifier: ^3.0.1 version: 3.0.1(prop-types@15.8.1)(react@18.2.0) @@ -151,10 +157,10 @@ dependencies: version: 2.5.2(react-dom@18.2.0)(react@18.2.0) react-redux: specifier: ^8.0.5 - version: 8.0.5(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) + version: 8.1.3(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) react-router-dom: specifier: ^6.22.3 - version: 6.22.3(react-dom@18.2.0)(react@18.2.0) + version: 6.23.1(react-dom@18.2.0)(react@18.2.0) react-swipeable-views: specifier: ^0.14.0 version: 0.14.0(react@18.2.0) @@ -163,7 +169,7 @@ dependencies: version: 4.4.5(react-dom@18.2.0)(react@18.2.0) react-virtualized-auto-sizer: specifier: ^1.0.20 - version: 1.0.20(react-dom@18.2.0)(react@18.2.0) + version: 1.0.24(react-dom@18.2.0)(react@18.2.0) react-vtree: specifier: ^2.0.4 version: 2.0.4(@types/react-window@1.8.8)(react-dom@18.2.0)(react-window@1.8.10)(react@18.2.0) @@ -172,7 +178,7 @@ dependencies: version: 1.8.10(react-dom@18.2.0)(react@18.2.0) react18-input-otp: specifier: ^1.1.2 - version: 1.1.2(react-dom@18.2.0)(react@18.2.0) + version: 1.1.4(react-dom@18.2.0)(react@18.2.0) redux: specifier: ^4.2.1 version: 4.2.1 @@ -181,16 +187,16 @@ dependencies: version: 7.8.0 sass: specifier: ^1.70.0 - version: 1.70.0 + version: 1.77.2 slate: specifier: ^0.101.4 - version: 0.101.4 + version: 0.101.5 slate-history: specifier: ^0.100.0 - version: 0.100.0(slate@0.101.4) + version: 0.100.0(slate@0.101.5) slate-react: specifier: ^0.101.3 - version: 0.101.3(react-dom@18.2.0)(react@18.2.0)(slate@0.101.4) + version: 0.101.6(react-dom@18.2.0)(react@18.2.0)(slate@0.101.5) ts-results: specifier: ^3.3.0 version: 3.3.0 @@ -202,19 +208,16 @@ dependencies: version: 3.0.0 validator: specifier: ^13.11.0 - version: 13.11.0 - valtio: - specifier: ^1.12.1 - version: 1.12.1(@types/react@18.2.66)(react@18.2.0) + version: 13.12.0 vite-plugin-wasm: specifier: ^3.3.0 version: 3.3.0(vite@5.2.0) y-indexeddb: specifier: 9.0.12 - version: 9.0.12(yjs@13.6.14) + version: 9.0.12(yjs@13.6.15) yjs: specifier: ^13.6.14 - version: 13.6.14 + version: 13.6.15 devDependencies: '@svgr/plugin-svgo': @@ -366,7 +369,7 @@ devDependencies: version: 9.0.0 vite: specifier: ^5.2.0 - version: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + version: 5.2.0(@types/node@20.11.30)(sass@1.77.2) vite-plugin-compression2: specifier: ^1.0.0 version: 1.0.0 @@ -410,27 +413,27 @@ packages: tslib: 2.6.2 dev: false - /@atlaskit/analytics-next@9.2.3(react@18.2.0): - resolution: {integrity: sha512-zTtM6xHTNNMO0dxiPrsxMko6BawEh4wN0wWSvMdQXdnusvvWNEE8rnpjn23kS4M08bYWIEstRbgoPLx6VQisug==} + /@atlaskit/analytics-next@9.3.0(react@18.2.0): + resolution: {integrity: sha512-mR5CndP92k2gFl8sWu4DJZZEpEQ4bnp5Z3fWCZE1oySiOKK8iM+KzKH4FMCaSUGOhWW6/5VeuXCcXvaogaAmsA==} peerDependencies: react: ^16.8.0 dependencies: '@atlaskit/analytics-next-stable-react-context': 1.0.1(react@18.2.0) '@atlaskit/platform-feature-flags': 0.2.5 - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.1 prop-types: 15.8.1 react: 18.2.0 use-memo-one: 1.1.3(react@18.2.0) dev: false - /@atlaskit/app-provider@1.3.0(react@18.2.0): - resolution: {integrity: sha512-0bHfIbXyCbYjuv0zMyHPyuCcyW5IIVCBsNbYEbEl9w5P9hChPyewNrz1VkNPRQBg18DF1zxIIsNyZOEuQjvs7Q==} + /@atlaskit/app-provider@1.3.2(react@18.2.0): + resolution: {integrity: sha512-tvyMNrydTyu5yJK78zUjqbJwgNRdW5nQ31imWFav5PvWXwc36lfGiTXRX/JIxJNBC3rBJ0gLAyrrb9YMzyWcTw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ~18.2.0 dependencies: - '@atlaskit/tokens': 1.43.0(react@18.2.0) - '@babel/runtime': 7.24.4 - bind-event-listener: 2.1.1 + '@atlaskit/tokens': 1.49.1(react@18.2.0) + '@babel/runtime': 7.24.1 + bind-event-listener: 3.0.0 react: 18.2.0 transitivePeerDependencies: - supports-color @@ -441,21 +444,21 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ~18.2.0 dependencies: - '@atlaskit/tokens': 1.43.0(react@18.2.0) - '@babel/runtime': 7.24.4 + '@atlaskit/tokens': 1.49.1(react@18.2.0) + '@babel/runtime': 7.24.1 '@compiled/react': 0.17.1(react@18.2.0) react: 18.2.0 transitivePeerDependencies: - supports-color dev: false - /@atlaskit/ds-lib@2.3.0(react@18.2.0): - resolution: {integrity: sha512-ULU9ZTVBvlQ9QUwKXhCju3/fUWT79zdX5omNYOIL3nUrAtBCPX7inNpIJ/D4Lx35O6XSWJRKuNFL5tR75FEYKQ==} + /@atlaskit/ds-lib@2.3.1(react@18.2.0): + resolution: {integrity: sha512-DVUE3hYLhdEZy4NnsxqiCqKC5Ym3CM/DGRQlnSPcABFNL0N0FfTXso3pLpkJnMZBtEnd2pn13mPJ2VQlSISRuw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ~18.2.0 dependencies: - '@babel/runtime': 7.24.4 - bind-event-listener: 2.1.1 + '@babel/runtime': 7.24.1 + bind-event-listener: 3.0.0 react: 18.2.0 dev: false @@ -464,32 +467,32 @@ packages: peerDependencies: react: ^16.8.0 dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.1 react: 18.2.0 dev: false /@atlaskit/platform-feature-flags@0.2.5: resolution: {integrity: sha512-0fD2aDxn2mE59D4acUhVib+YF2HDYuuPH50aYwpQdcV/CsVkAaJsMKy8WhWSulcRFeMYp72kfIfdy0qGdRB7Uw==} dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.1 dev: false - /@atlaskit/primitives@5.5.3(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-UnKx0N/Jeo5Z61wFm5U2OkJM3bcb1qpKUHYrxzmSVe+QfQvBbJ/U5882WMnwQYq9rqYR2NcFvRNyT8Lf/L4FRQ==} + /@atlaskit/primitives@5.7.0(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-eCLyHN1BllNpwqA2YqCmYpqwoiNVcW3R6bHrpKmsW8uvPE/+Bd45hOiPwvCPJUPyK1ZNMfnkegWKkoxcmjMYIQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@atlaskit/analytics-next': 9.2.3(react@18.2.0) - '@atlaskit/app-provider': 1.3.0(react@18.2.0) + '@atlaskit/analytics-next': 9.3.0(react@18.2.0) + '@atlaskit/app-provider': 1.3.2(react@18.2.0) '@atlaskit/css': 0.1.0(react@18.2.0) - '@atlaskit/ds-lib': 2.3.0(react@18.2.0) + '@atlaskit/ds-lib': 2.3.1(react@18.2.0) '@atlaskit/interaction-context': 2.1.4(react@18.2.0) - '@atlaskit/tokens': 1.43.0(react@18.2.0) - '@atlaskit/visually-hidden': 1.2.5(@types/react@18.2.66)(react@18.2.0) - '@babel/runtime': 7.24.4 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) + '@atlaskit/tokens': 1.49.1(react@18.2.0) + '@atlaskit/visually-hidden': 1.3.0(@types/react@18.2.66)(react@18.2.0) + '@babel/runtime': 7.24.1 + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) '@emotion/serialize': 1.1.4 - bind-event-listener: 2.1.1 + bind-event-listener: 3.0.0 react: 18.2.0 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -497,29 +500,29 @@ packages: - supports-color dev: false - /@atlaskit/tokens@1.43.0(react@18.2.0): - resolution: {integrity: sha512-3rRxGRnJGQBVKGqNqy+Zuad3xuDZ7uD+aFGRcU2OpLuIpiFLX95agDZ9w0HGzNiDw9eWi2f1j8Uzq06AyaRqTw==} + /@atlaskit/tokens@1.49.1(react@18.2.0): + resolution: {integrity: sha512-3SuhRMPUTU6b+nv0zVoGsNoqrUMtwQ/4iBbKhwaylRITanFxlxBwzW8XCCnn4sp1S2JupiT5BksI0h6jRoKN9Q==} peerDependencies: - react: ^16.8.0 + react: ^16.8.0 || ^17.0.0 || ~18.2.0 dependencies: - '@atlaskit/ds-lib': 2.3.0(react@18.2.0) + '@atlaskit/ds-lib': 2.3.1(react@18.2.0) '@atlaskit/platform-feature-flags': 0.2.5 - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.1 '@babel/traverse': 7.24.1 '@babel/types': 7.24.0 - bind-event-listener: 2.1.1 + bind-event-listener: 3.0.0 react: 18.2.0 transitivePeerDependencies: - supports-color dev: false - /@atlaskit/visually-hidden@1.2.5(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-khxJB6456uMS1IkwnsH0ZTYGVAnE83aoGarz388I0zPydOZdNeeEdwsRPFOn9mua88Ez+rL0OEWV22k6XRUf6A==} + /@atlaskit/visually-hidden@1.3.0(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-iOHCxRnhNV3gnqOHuyLOnsFibfHpr1T28XUPYZjtN9bDQbn1GdSDYLoIHnLK+2enqdILirsuUWi93mWM3dCCwg==} peerDependencies: - react: ^16.8.0 + react: ^16.8.0 || ^17.0.0 || ~18.2.0 dependencies: - '@babel/runtime': 7.24.4 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) + '@babel/runtime': 7.24.1 + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) react: 18.2.0 transitivePeerDependencies: - '@types/react' @@ -814,8 +817,8 @@ packages: dependencies: regenerator-runtime: 0.14.1 - /@babel/runtime@7.24.4: - resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==} + /@babel/runtime@7.24.6: + resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 @@ -913,17 +916,17 @@ packages: - supports-color dev: true - /@emoji-mart/data@1.1.2: - resolution: {integrity: sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==} + /@emoji-mart/data@1.2.1: + resolution: {integrity: sha512-no2pQMWiBy6gpBEiqGeU77/bFejDqUTRY7KX+0+iur13op3bqUsXdnwoZs6Xb1zbv0gAj5VvS1PWoUUckSr5Dw==} dev: false - /@emoji-mart/react@1.1.1(emoji-mart@5.5.2)(react@18.2.0): + /@emoji-mart/react@1.1.1(emoji-mart@5.6.0)(react@18.2.0): resolution: {integrity: sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==} peerDependencies: emoji-mart: ^5.2 react: ^16.8 || ^17 || ^18 dependencies: - emoji-mart: 5.5.2 + emoji-mart: 5.6.0 react: 18.2.0 dev: false @@ -967,8 +970,8 @@ packages: resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} dev: false - /@emotion/react@11.10.6(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw==} + /@emotion/react@11.11.4(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==} peerDependencies: '@types/react': '*' react: '>=16.8.0' @@ -1002,8 +1005,8 @@ packages: resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} dev: false - /@emotion/styled@11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og==} + /@emotion/styled@11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 '@types/react': '*' @@ -1015,7 +1018,7 @@ packages: '@babel/runtime': 7.24.1 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.2 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) '@emotion/serialize': 1.1.4 '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) '@emotion/utils': 1.2.1 @@ -1264,32 +1267,32 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@floating-ui/core@1.6.0: - resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} + /@floating-ui/core@1.6.2: + resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==} dependencies: - '@floating-ui/utils': 0.2.1 + '@floating-ui/utils': 0.2.2 dev: false - /@floating-ui/dom@1.6.3: - resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} + /@floating-ui/dom@1.6.5: + resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==} dependencies: - '@floating-ui/core': 1.6.0 - '@floating-ui/utils': 0.2.1 + '@floating-ui/core': 1.6.2 + '@floating-ui/utils': 0.2.2 dev: false - /@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==} + /@floating-ui/react-dom@2.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - '@floating-ui/dom': 1.6.3 + '@floating-ui/dom': 1.6.5 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@floating-ui/utils@0.2.1: - resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + /@floating-ui/utils@0.2.2: + resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} dev: false /@humanwhocodes/config-array@0.11.14: @@ -1582,6 +1585,10 @@ packages: resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} dev: false + /@lokesh.dhakar/quantize@1.3.0: + resolution: {integrity: sha512-4KBSyaMj65d8A+2vnzLxtHFu4OmBU4IKO0yLxZ171Itdf9jGV4w+WbG7VsKts2jUdRkFSzsZqpZOz6hTB3qGAw==} + dev: false + /@mui/base@5.0.0-beta.40(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==} engines: {node: '>=12.0.0'} @@ -1594,12 +1601,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@floating-ui/react-dom': 2.1.0(react-dom@18.2.0)(react@18.2.0) '@mui/types': 7.2.14(@types/react@18.2.66) '@mui/utils': 5.15.14(@types/react@18.2.66)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.66 - clsx: 2.1.0 + clsx: 2.1.1 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -1616,24 +1623,24 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 - '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@babel/runtime': 7.24.6 + '@floating-ui/react-dom': 2.1.0(react-dom@18.2.0)(react@18.2.0) '@mui/types': 7.2.14(@types/react@18.2.66) - '@mui/utils': 6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0) + '@mui/utils': 6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.66 - clsx: 2.1.0 + clsx: 2.1.1 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false - /@mui/core-downloads-tracker@6.0.0-alpha.2: - resolution: {integrity: sha512-6JSNsfMePoLZ0tKZzQ2i+W8mcZCbP9snFNf/pQGOsMK2igkPGqOfqgdsda2OJrD5tP9VGjEDrrmYGHuX3ir29g==} + /@mui/core-downloads-tracker@6.0.0-dev.240424162023-9968b4889d: + resolution: {integrity: sha512-doh3M3U7HUGSBIWGe1yvesSbfDguMRjP0N09ogWSBM2hovXAlgULhMgcRTepAZLLwfRxFII0bCohq6B9NqoKuw==} dev: false - /@mui/icons-material@5.11.11(@mui/material@6.0.0-alpha.2)(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw==} + /@mui/icons-material@5.15.18(@mui/material@6.0.0-alpha.2)(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-jGhyw02TSLM0NgW+MDQRLLRUD/K4eN9rlK2pTBTL1OtzyZmQ8nB060zK1wA0b7cVrIiG+zyrRmNAvGWXwm2N9Q==} engines: {node: '>=12.0.0'} peerDependencies: '@mui/material': ^5.0.0 @@ -1644,12 +1651,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@mui/material': 6.0.0-alpha.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) + '@mui/material': 6.0.0-alpha.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.66 react: 18.2.0 dev: false - /@mui/material@6.0.0-alpha.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0): + /@mui/material@6.0.0-alpha.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-p1GpE1a7dQTns0yp0anSNX/Bh1xafTdUCt0roTyqEuL/3hCBKTURE/9/CDttwwQ+Q8oDm5KcsdtXJXJh1ts6Kw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1666,17 +1673,17 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@babel/runtime': 7.24.6 + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) '@mui/base': 5.0.0-beta.42(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) - '@mui/core-downloads-tracker': 6.0.0-alpha.2 - '@mui/system': 6.0.0-alpha.1(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@mui/core-downloads-tracker': 6.0.0-dev.240424162023-9968b4889d + '@mui/system': 6.0.0-dev.240424162023-9968b4889d(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react@18.2.0) '@mui/types': 7.2.14(@types/react@18.2.66) - '@mui/utils': 6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0) + '@mui/utils': 6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0) '@types/react': 18.2.66 '@types/react-transition-group': 4.4.10 - clsx: 2.1.0 + clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 @@ -1695,15 +1702,15 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.6 '@mui/utils': 5.15.14(@types/react@18.2.66)(react@18.2.0) '@types/react': 18.2.66 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/private-theming@6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-S+vlRxja0ncLT1KK+Qkqy6ZNpIsw5ZxoTd70KR8foLtPQXs0XGWQkq9IotbcZnYyKUUhM/5Cqo3w8Q/zNNVzBQ==} + /@mui/private-theming@6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-0iN+hK/OZTaiVfjFYDgWEc/frRB7Z1hfBsSJBniM4KPZnrdeHIArP+3TdYzRT0avh30O2KNkBNk0GG95BnUVEg==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -1712,14 +1719,14 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 - '@mui/utils': 6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0) + '@babel/runtime': 7.24.6 + '@mui/utils': 6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0) '@types/react': 18.2.66 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/styled-engine@5.15.14(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0): + /@mui/styled-engine@5.15.14(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.2.0): resolution: {integrity: sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1732,17 +1739,17 @@ packages: '@emotion/styled': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.6 '@emotion/cache': 11.11.0 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/styled-engine@6.0.0-alpha.1(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0): - resolution: {integrity: sha512-lJmMF1D3SD9unSh5gjXYU3ChiCrNGrpT36cPOmGv7wiH2qLMov5Wo23nK7UfuaQVA4c4/JAS43SLT2O2z6hx4Q==} + /@mui/styled-engine@6.0.0-alpha.8(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.2.0): + resolution: {integrity: sha512-7zJYgbjZRQpGN1SGmLDOgRpJZB26JjPSeqml5m+jA4wAsIONm2im+GHfki4nE3ay0uj1S555OMeNpaQ+sG9LkA==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.4.1 @@ -1754,16 +1761,16 @@ packages: '@emotion/styled': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.6 '@emotion/cache': 11.11.0 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/system@5.15.15(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react@18.2.0): + /@mui/system@5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react@18.2.0): resolution: {integrity: sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1779,22 +1786,22 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@babel/runtime': 7.24.6 + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) '@mui/private-theming': 5.15.14(@types/react@18.2.66)(react@18.2.0) - '@mui/styled-engine': 5.15.14(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0) + '@mui/styled-engine': 5.15.14(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.2.0) '@mui/types': 7.2.14(@types/react@18.2.66) '@mui/utils': 5.15.14(@types/react@18.2.66)(react@18.2.0) '@types/react': 18.2.66 - clsx: 2.1.0 + clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/system@6.0.0-alpha.1(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-NqOFHCT/lvi4v696b0pEEz2Jc3MgjjGRsbFNN+IW5EuIInHr/JEznnPw0GpEU8tgU544MdfMTlFpDgR1LmvmLA==} + /@mui/system@6.0.0-dev.240424162023-9968b4889d(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-Y3yCFUHN1xMK62hJJBqzZb1YQvHNaHc7JUX01eU6QTPojtIbGMF2jCOP/EQw77/byahNbxeLoAIQx10F0IR3Rw==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 @@ -1809,15 +1816,15 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) - '@mui/private-theming': 6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0) - '@mui/styled-engine': 6.0.0-alpha.1(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(react@18.2.0) + '@babel/runtime': 7.24.6 + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) + '@mui/private-theming': 6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0) + '@mui/styled-engine': 6.0.0-alpha.8(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.2.0) '@mui/types': 7.2.14(@types/react@18.2.66) - '@mui/utils': 6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0) + '@mui/utils': 6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0) '@types/react': 18.2.66 - clsx: 2.1.0 + clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 18.2.0 @@ -1852,8 +1859,8 @@ packages: react-is: 18.2.0 dev: false - /@mui/utils@6.0.0-alpha.1(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-BvTVWvW6JDxMjDfEfTTEb6/CLgj87HyS3hYA4hbk0LdFsXeRnFSK6yDj8Dw3gxvXBymQ26kHDyFK3pEoAFlurw==} + /@mui/utils@6.0.0-alpha.8(@types/react@18.2.66)(react@18.2.0): + resolution: {integrity: sha512-X5lg0bh8B6uYt/0HXV+t82HXLTOVFEKcIBmIbJ5El1h9ykXaRTenr8mORxt5UC5w9DHFhkRoI8XiM5qyDuSJVw==} engines: {node: '>=12.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 @@ -1862,7 +1869,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.6 '@types/prop-types': 15.7.12 '@types/react': 18.2.66 prop-types: 15.8.1 @@ -1870,15 +1877,15 @@ packages: react-is: 18.2.0 dev: false - /@mui/x-date-pickers-pro@6.18.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-8lEVEOtCQssKWel4Ey1pRulGPXUQ73TnkHKzHWsjdv03FjiUs3eYB+Ej0Uk5yWPmsqlShWhOzOlOGDpzsYJsUg==} + /@mui/x-date-pickers-pro@6.20.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lXbO8xCKRvIKgu2R8EAGhYwN1BkMS9GxTeinKZg5lCGvxTrd5pErQ4xpOxYVQq7wNLphDiY7I/Xf88VekKTNLQ==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 '@emotion/styled': ^11.8.1 '@mui/material': ^5.8.6 '@mui/system': ^5.8.0 - date-fns: ^2.25.0 + date-fns: ^2.25.0 || ^3.2.0 date-fns-jalali: ^2.13.0-0 dayjs: ^1.10.7 luxon: ^3.0.2 @@ -1908,15 +1915,15 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) '@mui/base': 5.0.0-beta.40(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) - '@mui/material': 6.0.0-alpha.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.15.15(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@mui/material': 6.0.0-alpha.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) + '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react@18.2.0) '@mui/utils': 5.15.14(@types/react@18.2.66)(react@18.2.0) - '@mui/x-date-pickers': 6.18.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) + '@mui/x-date-pickers': 6.20.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) '@mui/x-license-pro': 6.10.2(@types/react@18.2.66)(react@18.2.0) - clsx: 2.1.0 + clsx: 2.1.1 dayjs: 1.11.9 prop-types: 15.8.1 react: 18.2.0 @@ -1926,15 +1933,15 @@ packages: - '@types/react' dev: false - /@mui/x-date-pickers@6.18.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-HJq4uoFQSu5isa/mesWw2BKh8KBRYUQb+KaSlVlWfJNgP3YhPvWZ6yqCNYyxOAiPMxb0n3nBjS9ErO27OHjFMA==} + /@mui/x-date-pickers@6.20.0(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@mui/material@6.0.0-alpha.2)(@mui/system@5.15.15)(@types/react@18.2.66)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-q/x3rNmPYMXnx75+3s9pQb1YDtws9y5bwxpxeB3EW88oCp33eS7bvJpeuoCA1LzW/PpVfIRhi5RCyAvrEeTL7Q==} engines: {node: '>=14.0.0'} peerDependencies: '@emotion/react': ^11.9.0 '@emotion/styled': ^11.8.1 '@mui/material': ^5.8.6 '@mui/system': ^5.8.0 - date-fns: ^2.25.0 + date-fns: ^2.25.0 || ^3.2.0 date-fns-jalali: ^2.13.0-0 dayjs: ^1.10.7 luxon: ^3.0.2 @@ -1964,14 +1971,14 @@ packages: optional: true dependencies: '@babel/runtime': 7.24.1 - '@emotion/react': 11.10.6(@types/react@18.2.66)(react@18.2.0) - '@emotion/styled': 11.10.6(@emotion/react@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.2.66)(react@18.2.0) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.2.66)(react@18.2.0) '@mui/base': 5.0.0-beta.40(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) - '@mui/material': 6.0.0-alpha.2(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.15.15(@emotion/react@11.10.6)(@emotion/styled@11.10.6)(@types/react@18.2.66)(react@18.2.0) + '@mui/material': 6.0.0-alpha.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0) + '@mui/system': 5.15.15(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.2.66)(react@18.2.0) '@mui/utils': 5.15.14(@types/react@18.2.66)(react@18.2.0) '@types/react-transition-group': 4.4.10 - clsx: 2.1.0 + clsx: 2.1.1 dayjs: 1.11.9 prop-types: 15.8.1 react: 18.2.0 @@ -2029,7 +2036,7 @@ packages: /@popperjs/core@2.11.8: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - /@reduxjs/toolkit@2.0.0(react-redux@8.0.5)(react@18.2.0): + /@reduxjs/toolkit@2.0.0(react-redux@8.1.3)(react@18.2.0): resolution: {integrity: sha512-Kq/a+aO28adYdPoNEu9p800MYPKoUc0tlkYfv035Ief9J7MPq8JvmT7UdpYhvXsoMtOdt567KwZjc9H3Rf8yjg==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 @@ -2040,16 +2047,16 @@ packages: react-redux: optional: true dependencies: - immer: 10.0.4 + immer: 10.1.1 react: 18.2.0 - react-redux: 8.0.5(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) + react-redux: 8.1.3(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.0.1 + reselect: 5.1.0 dev: false - /@remix-run/router@1.15.3: - resolution: {integrity: sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==} + /@remix-run/router@1.16.1: + resolution: {integrity: sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==} engines: {node: '>=14.0.0'} dev: false @@ -2208,15 +2215,15 @@ packages: dependencies: '@sinonjs/commons': 3.0.1 - /@slate-yjs/core@1.0.2(slate@0.101.4)(yjs@13.6.14): + /@slate-yjs/core@1.0.2(slate@0.101.5)(yjs@13.6.15): resolution: {integrity: sha512-X0hLFJbQu9c1ItWBaNuEn0pqcXYK76KCp8C4Gvy/VaTQVMo1VgAb2WiiJ0Je/AyuIYEPPSTNVOcyrGHwgA7e6Q==} peerDependencies: slate: '>=0.70.0' yjs: ^13.5.29 dependencies: - slate: 0.101.4 - y-protocols: 1.0.6(yjs@13.6.14) - yjs: 13.6.14 + slate: 0.101.5 + y-protocols: 1.0.6(yjs@13.6.15) + yjs: 13.6.15 dev: false /@svgr/babel-plugin-add-jsx-attribute@7.0.0(@babel/core@7.24.3): @@ -2458,8 +2465,8 @@ packages: - typescript dev: true - /@tauri-apps/api@1.5.3: - resolution: {integrity: sha512-zxnDjHHKjOsrIzZm6nO5Xapb/BxqUq1tc7cGkFXsFkGTsSWgCPH1D8mm0XS9weJY2OaR73I3k3S+b7eSzJDfqA==} + /@tauri-apps/api@1.5.6: + resolution: {integrity: sha512-LH5ToovAHnDVe5Qa9f/+jW28I6DeMhos8bNDtBOmmnaDpPmJmYLyHdeDblAWWWYc7KKRDg9/66vMuKyq0WIeFA==} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} dev: false @@ -2789,8 +2796,8 @@ packages: redux: 4.2.1 dev: false - /@types/react-swipeable-views@0.13.4: - resolution: {integrity: sha512-hQV9Oq6oa+9HKdnGd43xkckElwf5dThOiegtQxqE7qX761oHhxnZO07fz6IsKSnUy9J3tzlRQBu3sNyvC8+kYw==} + /@types/react-swipeable-views@0.13.5: + resolution: {integrity: sha512-ni6WjO7gBq2xB2Y/ZiRdQOgjGOxIik5ow2s7xKieDq8DxsXTdV46jJslSBVK2yoIJHf6mG3uqNTwxwgzbXRRzg==} dependencies: '@types/react': 18.2.66 dev: false @@ -3038,7 +3045,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.3) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + vite: 5.2.0(@types/node@20.11.30)(sass@1.77.2) transitivePeerDependencies: - supports-color dev: true @@ -3121,7 +3128,6 @@ packages: fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -3265,12 +3271,10 @@ packages: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} dependencies: safer-buffer: 2.1.2 - dev: true /assert-plus@1.0.0: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - dev: true /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} @@ -3320,14 +3324,12 @@ packages: /aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - dev: true /aws4@1.12.0: resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} - dev: true - /axios@1.6.8: - resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} + /axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -3457,14 +3459,13 @@ packages: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} dependencies: tweetnacl: 0.14.5 - dev: true /binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - /bind-event-listener@2.1.1: - resolution: {integrity: sha512-O+a5c0D2se/u2VlBJmPRn45IB6R4mYMh1ok3dWxrIZ2pmLqzggBhb875mbq73508ylzofc0+hT9W41x4Y2s8lg==} + /bind-event-listener@3.0.0: + resolution: {integrity: sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==} dev: false /blob-util@2.0.2: @@ -3591,7 +3592,6 @@ packages: /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - dev: true /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -3711,8 +3711,8 @@ packages: engines: {node: '>=6'} dev: false - /clsx@2.1.0: - resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} + /clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} dev: false @@ -3744,6 +3744,13 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true + /colorthief@2.4.0: + resolution: {integrity: sha512-0U48RGNRo5fVO+yusBwgp+d3augWSorXabnqXUu9SabEhCpCgZJEUjUTTI41OOBBYuMMxawa3177POT6qLfLeQ==} + dependencies: + '@lokesh.dhakar/quantize': 1.3.0 + get-pixels: 3.3.3 + dev: false + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3793,7 +3800,6 @@ packages: /core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - dev: true /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3932,6 +3938,12 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /cwise-compiler@1.1.3: + resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==} + dependencies: + uniq: 1.0.1 + dev: false + /cypress@13.7.2: resolution: {integrity: sha512-FF5hFI5wlRIHY8urLZjJjj/YvfCBrRpglbZCLr/cYcL9MdDe0+5usa8kTIrDHthlEc9lwihbkb5dmwqBDNS2yw==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -3987,7 +3999,10 @@ packages: engines: {node: '>=0.10'} dependencies: assert-plus: 1.0.0 - dev: true + + /data-uri-to-buffer@0.0.3: + resolution: {integrity: sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==} + dev: false /data-urls@3.0.2: resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} @@ -4127,14 +4142,6 @@ packages: engines: {node: '>=6'} dev: false - /derive-valtio@0.1.0(valtio@1.12.1): - resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} - peerDependencies: - valtio: '*' - dependencies: - valtio: 1.12.1(@types/react@18.2.66)(react@18.2.0) - dev: false - /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -4273,7 +4280,6 @@ packages: dependencies: jsbn: 0.1.1 safer-buffer: 2.1.2 - dev: true /electron-to-chromium@1.4.722: resolution: {integrity: sha512-5nLE0TWFFpZ80Crhtp4pIp8LXCztjYX41yUcV6b+bKR2PqzjskTMOOlBi1VjBHlvHwS+4gar7kNKOrsbsewEZQ==} @@ -4282,12 +4288,12 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - /emoji-mart@5.5.2: - resolution: {integrity: sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==} + /emoji-mart@5.6.0: + resolution: {integrity: sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==} dev: false - /emoji-regex@10.2.1: - resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} + /emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} dev: false /emoji-regex@8.0.0: @@ -4698,11 +4704,9 @@ packages: /extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} - dev: true /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-diff@1.1.2: resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==} @@ -4833,7 +4837,6 @@ packages: /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - dev: true /form-data@2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} @@ -4842,7 +4845,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} @@ -4927,6 +4929,22 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + /get-pixels@3.3.3: + resolution: {integrity: sha512-5kyGBn90i9tSMUVHTqkgCHsoWoR+/lGbl4yC83Gefyr0HLIhgSWEx/2F/3YgsZ7UpYNuM6pDhDK7zebrUJ5nXg==} + dependencies: + data-uri-to-buffer: 0.0.3 + jpeg-js: 0.4.4 + mime-types: 2.1.35 + ndarray: 1.0.19 + ndarray-pack: 1.2.1 + node-bitmap: 0.0.1 + omggif: 1.0.10 + parse-data-uri: 0.2.0 + pngjs: 3.4.0 + request: 2.88.2 + through: 2.3.8 + dev: false + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -4957,7 +4975,6 @@ packages: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} dependencies: assert-plus: 1.0.0 - dev: true /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -5043,8 +5060,8 @@ packages: csstype: 3.1.3 dev: false - /google-protobuf@3.16.0: - resolution: {integrity: sha512-gBY66yYL1wbQMU2r1POkXSXkm035Ni0wFv3vx0K9IEUsJLP9G5rAcFVn0xUXfZneRu6MmDjaw93pt/DE56VOyw==} + /google-protobuf@3.21.2: + resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} dev: false /gopd@1.0.1: @@ -5071,6 +5088,20 @@ packages: through2: 2.0.5 dev: true + /har-schema@2.0.0: + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} + engines: {node: '>=4'} + dev: false + + /har-validator@5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + dev: false + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -5148,6 +5179,15 @@ packages: - supports-color dev: true + /http-signature@1.2.0: + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} + engines: {node: '>=0.8', npm: '>=1.3.7'} + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.2 + sshpk: 1.18.0 + dev: false + /http-signature@1.3.6: resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} engines: {node: '>=0.10'} @@ -5176,20 +5216,20 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - /i18next-browser-languagedetector@7.0.1: - resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} + /i18next-browser-languagedetector@7.2.1: + resolution: {integrity: sha512-h/pM34bcH6tbz8WgGXcmWauNpQupCGr25XPp9cZwZInR9XHSjIFDYp1SIok7zSPsTOMxdvuLyu86V+g2Kycnfw==} dependencies: '@babel/runtime': 7.24.1 dev: false - /i18next-resources-to-backend@1.1.4: - resolution: {integrity: sha512-hMyr9AOmIea17AOaVe1srNxK/l3mbk81P7Uf3fdcjlw3ehZy3UNTd0OP3EEi6yu4J02kf9jzhCcjokz6AFlEOg==} + /i18next-resources-to-backend@1.2.1: + resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} dependencies: '@babel/runtime': 7.24.1 dev: false - /i18next@22.4.10: - resolution: {integrity: sha512-3EqgGK6fAJRjnGgfkNSStl4mYLCjUoJID338yVyLMj5APT67HUtWoqSayZewiiC5elzMUB1VEUwcmSCoeQcNEA==} + /i18next@22.5.1: + resolution: {integrity: sha512-8TGPgM3pAD+VRsMtUMNknRz3kzqwp/gPALrWMsDnmC1mKqJwpWyooQRLMcbTwq8z8YwSmuj+ZYvc+xCuEpkssA==} dependencies: '@babel/runtime': 7.24.1 dev: false @@ -5210,12 +5250,12 @@ packages: engines: {node: '>= 4'} dev: true - /immer@10.0.4: - resolution: {integrity: sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw==} + /immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} dev: false - /immutable@4.3.5: - resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==} + /immutable@4.3.6: + resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -5270,6 +5310,10 @@ packages: loose-envify: 1.4.0 dev: false + /iota-array@1.0.0: + resolution: {integrity: sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==} + dev: false + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -5309,6 +5353,10 @@ packages: has-tostringtag: 1.0.2 dev: true + /is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + dev: false + /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -5455,7 +5503,6 @@ packages: /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} @@ -5492,7 +5539,6 @@ packages: /isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - dev: true /istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} @@ -5963,8 +6009,12 @@ packages: - supports-color - ts-node - /js-base64@3.7.5: - resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} + /jpeg-js@0.4.4: + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} + dev: false + + /js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} dev: false /js-tokens@4.0.0: @@ -5986,7 +6036,6 @@ packages: /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - dev: true /jsdom@20.0.3: resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} @@ -6043,11 +6092,9 @@ packages: /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true /json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - dev: true /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -6055,7 +6102,6 @@ packages: /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} @@ -6074,6 +6120,16 @@ packages: graceful-fs: 4.2.11 dev: true + /jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + dev: false + /jsprim@2.0.2: resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} engines: {'0': node >=0.6.0} @@ -6094,8 +6150,8 @@ packages: object.values: 1.2.0 dev: true - /katex@0.16.7: - resolution: {integrity: sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==} + /katex@0.16.10: + resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} hasBin: true dependencies: commander: 8.3.0 @@ -6136,8 +6192,8 @@ packages: type-check: 0.4.0 dev: true - /lib0@0.2.93: - resolution: {integrity: sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q==} + /lib0@0.2.94: + resolution: {integrity: sha512-hZ3p54jL4Wpu7IOg26uC7dnEWiMyNlUrb9KoG7+xYs45WkQwpVvKFndVq2+pqLYKe1u8Fp3+zAfZHVvTK34PvQ==} engines: {node: '>=16'} hasBin: true dependencies: @@ -6389,8 +6445,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /nanoid@4.0.0: - resolution: {integrity: sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==} + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} engines: {node: ^14 || ^16 || >=18} hasBin: true dev: false @@ -6398,6 +6454,20 @@ packages: /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + /ndarray-pack@1.2.1: + resolution: {integrity: sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==} + dependencies: + cwise-compiler: 1.1.3 + ndarray: 1.0.19 + dev: false + + /ndarray@1.0.19: + resolution: {integrity: sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==} + dependencies: + iota-array: 1.0.0 + is-buffer: 1.1.6 + dev: false + /no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: @@ -6405,6 +6475,11 @@ packages: tslib: 2.6.2 dev: true + /node-bitmap@0.0.1: + resolution: {integrity: sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==} + engines: {node: '>=v0.6.5'} + dev: false + /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -6440,6 +6515,10 @@ packages: resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} dev: true + /oauth-sign@0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -6512,6 +6591,10 @@ packages: es-object-atoms: 1.0.0 dev: true + /omggif@1.0.10: + resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -6604,6 +6687,12 @@ packages: dependencies: callsites: 3.1.0 + /parse-data-uri@0.2.0: + resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==} + dependencies: + data-uri-to-buffer: 0.0.3 + dev: false + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -6697,6 +6786,11 @@ packages: dependencies: find-up: 4.1.0 + /pngjs@3.4.0: + resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} + engines: {node: '>=4.0.0'} + dev: false + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -6893,10 +6987,6 @@ packages: hasBin: true dev: false - /proxy-compare@2.5.1: - resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==} - dev: false - /proxy-from-env@1.0.0: resolution: {integrity: sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==} dev: true @@ -6907,7 +6997,6 @@ packages: /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: true /pump@2.0.1: resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} @@ -6934,7 +7023,6 @@ packages: /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - dev: true /pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} @@ -6946,6 +7034,11 @@ packages: side-channel: 1.0.6 dev: true + /qs@6.5.3: + resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + engines: {node: '>=0.6'} + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: true @@ -7029,8 +7122,8 @@ packages: - react-native dev: false - /react-big-calendar@1.8.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-cra8WPfoTSQthFfqxi0k9xm/Shv5jWSw19LkNzpSJcnQhP6XGes/eJjd8P8g/iwaJjXIWPpg3+HB5wO5wabRyA==} + /react-big-calendar@1.12.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cPVcwH5V1YiC6QKaV4afvpuZ2DtP8+TocnZY98nGodqq8bfjVDiP3Ch+TewBZzj9mg7JbewHdufDZXZBqQl1lw==} peerDependencies: react: ^16.14.0 || ^17 || ^18 react-dom: ^16.14.0 || ^17 || ^18 @@ -7070,6 +7163,19 @@ packages: tinycolor2: 1.6.0 dev: false + /react-custom-scrollbars-2@4.5.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-/z0nWAeXfMDr4+OXReTpYd1Atq9kkn4oI3qxq3iMXGQx1EEfwETSqB8HTAvg1X7dEqcCachbny1DRNGlqX5bDQ==} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + dom-css: 2.1.0 + prop-types: 15.8.1 + raf: 3.4.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-custom-scrollbars@4.2.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-VtJTUvZ7kPh/auZWIbBRceGPkE30XBYe+HktFxuMWBR2eVQQ+Ur6yFJMoaYcNpyGq22uYJ9Wx4UAEcC0K+LNPQ==} peerDependencies: @@ -7083,8 +7189,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-datepicker@4.23.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-w+msqlOZ14v6H1UknTKtZw/dw9naFMgAOspf59eY130gWpvy5dvKj/bgsFICDdvxB7PtKWxDcbGlAqCloY1d2A==} + /react-datepicker@4.25.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==} peerDependencies: react: ^16.9.0 || ^17 || ^18 react-dom: ^16.9.0 || ^17 || ^18 @@ -7095,7 +7201,7 @@ packages: prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-onclickoutside: 6.13.0(react-dom@18.2.0)(react@18.2.0) + react-onclickoutside: 6.13.1(react-dom@18.2.0)(react@18.2.0) react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0) dev: false @@ -7145,8 +7251,8 @@ packages: - csstype dev: false - /react-i18next@14.1.0(i18next@22.4.10)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ==} + /react-i18next@14.1.2(i18next@22.5.1)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==} peerDependencies: i18next: '>= 23.2.3' react: '>= 16.8.0' @@ -7160,7 +7266,7 @@ packages: dependencies: '@babel/runtime': 7.24.1 html-parse-stringify: 3.0.1 - i18next: 22.4.10 + i18next: 22.5.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) dev: false @@ -7181,7 +7287,7 @@ packages: prop-types: ^15.8.1 react: '>=15.3.2 <=18' dependencies: - katex: 0.16.7 + katex: 0.16.10 prop-types: 15.8.1 react: 18.2.0 dev: false @@ -7196,7 +7302,7 @@ packages: react: '>0.13.0' react-dom: '>0.13.0' dependencies: - '@babel/runtime': 7.24.4 + '@babel/runtime': 7.24.1 get-node-dimensions: 1.2.1 prop-types: 15.8.1 react: 18.2.0 @@ -7204,8 +7310,8 @@ packages: resize-observer-polyfill: 1.5.1 dev: false - /react-onclickoutside@6.13.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==} + /react-onclickoutside@6.13.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==} peerDependencies: react: ^15.5.x || ^16.x || ^17.x || ^18.x react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x @@ -7267,15 +7373,15 @@ packages: react-is: 17.0.2 dev: false - /react-redux@8.0.5(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1): - resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} + /react-redux@8.1.3(@types/react-dom@18.2.22)(@types/react@18.2.66)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1): + resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==} peerDependencies: '@types/react': ^16.8 || ^17.0 || ^18.0 '@types/react-dom': ^16.8 || ^17.0 || ^18.0 react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 react-native: '>=0.59' - redux: ^4 + redux: ^4 || ^5.0.0-beta.0 peerDependenciesMeta: '@types/react': optional: true @@ -7298,7 +7404,7 @@ packages: react-dom: 18.2.0(react@18.2.0) react-is: 18.2.0 redux: 4.2.1 - use-sync-external-store: 1.2.0(react@18.2.0) + use-sync-external-store: 1.2.2(react@18.2.0) dev: false /react-refresh@0.14.0: @@ -7306,26 +7412,26 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-router-dom@6.22.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==} + /react-router-dom@6.23.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' dependencies: - '@remix-run/router': 1.15.3 + '@remix-run/router': 1.16.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-router: 6.22.3(react@18.2.0) + react-router: 6.23.1(react@18.2.0) dev: false - /react-router@6.22.3(react@18.2.0): - resolution: {integrity: sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==} + /react-router@6.23.1(react@18.2.0): + resolution: {integrity: sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' dependencies: - '@remix-run/router': 1.15.3 + '@remix-run/router': 1.16.1 react: 18.2.0 dev: false @@ -7379,11 +7485,11 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react-virtualized-auto-sizer@1.0.20(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA==} + /react-virtualized-auto-sizer@1.0.24(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==} peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) @@ -7417,8 +7523,8 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /react18-input-otp@1.1.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-E21NiPh/KH67Bq/uEAm78E8H+croiGAyX5WcXfX49qh0im1iKrk/3RCKCTESG6WUoJYyh/fj5JY0UrHm+Mm0eQ==} + /react18-input-otp@1.1.4(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-35xvmTeuPWIxd0Z0Opx4z3OoMaTmKN4ubirQCx1YMZiNoe+2h1hsOSUco4aKPlGXWZCtXrfOFieAh46vqiK9mA==} peerDependencies: react: 16.2.0 - 18 react-dom: 16.2.0 - 18 @@ -7506,6 +7612,33 @@ packages: throttleit: 1.0.1 dev: true + /request@2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + dependencies: + aws-sign2: 0.7.0 + aws4: 1.12.0 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.3 + safe-buffer: 5.1.2 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + dev: false + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -7514,8 +7647,8 @@ packages: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true - /reselect@5.0.1: - resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==} + /reselect@5.1.0: + resolution: {integrity: sha512-aw7jcGLDpSgNDyWBQLv2cedml85qd95/iszJjN988zX1t7AVRJi19d9kto5+W7oCfQ94gyo40dVbT6g2k4/kXg==} dev: false /resize-observer-polyfill@1.5.1: @@ -7656,7 +7789,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} @@ -7669,15 +7801,14 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true - /sass@1.70.0: - resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} + /sass@1.77.2: + resolution: {integrity: sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: chokidar: 3.6.0 - immutable: 4.3.5 + immutable: 4.3.6 source-map-js: 1.2.0 /saxes@6.0.0: @@ -7785,17 +7916,17 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - /slate-history@0.100.0(slate@0.101.4): + /slate-history@0.100.0(slate@0.101.5): resolution: {integrity: sha512-x5rUuWLNtH97hs9PrFovGgt3Qc5zkTm/5mcUB+0NR/TK923eLax4HsL6xACLHMs245nI6aJElyM1y6hN0y5W/Q==} peerDependencies: slate: '>=0.65.3' dependencies: is-plain-object: 5.0.0 - slate: 0.101.4 + slate: 0.101.5 dev: false - /slate-react@0.101.3(react-dom@18.2.0)(react@18.2.0)(slate@0.101.4): - resolution: {integrity: sha512-KMXK9FLeS7HYhhoVcI8SUi4Qp1I9C1lTQ2EgbPH95sVXfH/vq+hbhurEGIGCe0VQ9Opj4rSKJIv/g7De1+nJMA==} + /slate-react@0.101.6(react-dom@18.2.0)(react@18.2.0)(slate@0.101.5): + resolution: {integrity: sha512-aMtp9FY127hKWTkCcTBonfKIwKJC2ESPqFdw2o/RuOk3RMQRwsWay8XTOHx8OBGOHanI2fsKaTAPF5zxOLA1Qg==} peerDependencies: react: '>=18.2.0' react-dom: '>=18.2.0' @@ -7811,14 +7942,14 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) scroll-into-view-if-needed: 3.1.0 - slate: 0.101.4 + slate: 0.101.5 tiny-invariant: 1.3.1 dev: false - /slate@0.101.4: - resolution: {integrity: sha512-8LazZrNDsYFKDg1wpb0HouAfX5Pw/UmOZ/vIrtqD2GSCDZvraOkV2nVJ9Ery8kIlsU1jeybwgcaCy4KkVwfvEg==} + /slate@0.101.5: + resolution: {integrity: sha512-ZZt1ia8ayRqxtpILRMi2a4MfdvwdTu64CorxTVq9vNSd0GQ/t3YDkze6wKjdeUtENmBlq5wNIDInZbx38Hfu5Q==} dependencies: - immer: 10.0.4 + immer: 10.1.1 is-plain-object: 5.0.0 tiny-warning: 1.0.3 dev: false @@ -7896,7 +8027,6 @@ packages: jsbn: 0.1.1 safer-buffer: 2.1.2 tweetnacl: 0.14.5 - dev: true /stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} @@ -8158,7 +8288,6 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true /tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} @@ -8214,6 +8343,14 @@ packages: engines: {node: '>=6'} dev: true + /tough-cookie@2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + dev: false + /tough-cookie@4.1.3: resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} engines: {node: '>=6'} @@ -8363,11 +8500,9 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.1.2 - dev: true /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - dev: true /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} @@ -8467,6 +8602,10 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /uniq@1.0.1: + resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} + dev: false + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -8513,7 +8652,6 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 - dev: true /url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} @@ -8530,8 +8668,8 @@ packages: react: 18.2.0 dev: false - /use-sync-external-store@1.2.0(react@18.2.0): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + /use-sync-external-store@1.2.2(react@18.2.0): + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: @@ -8546,6 +8684,12 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + dev: false + /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -8568,30 +8712,11 @@ packages: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - /validator@13.11.0: - resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + /validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} dev: false - /valtio@1.12.1(@types/react@18.2.66)(react@18.2.0): - resolution: {integrity: sha512-R0V4H86Xi2Pp7pmxN/EtV4Q6jr6PMN3t1IwxEvKUp6160r8FimvPh941oWyeK1iec/DTsh9Jb3Q+GputMS8SYg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=16.8' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - dependencies: - '@types/react': 18.2.66 - derive-valtio: 0.1.0(valtio@1.12.1) - proxy-compare: 2.5.1 - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - /verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -8599,7 +8724,6 @@ packages: assert-plus: 1.0.0 core-util-is: 1.0.2 extsprintf: 1.3.0 - dev: true /vite-plugin-compression2@1.0.0: resolution: {integrity: sha512-42XNp6FjxE0JIecxj1fdi770pLhYm3MJhBUAod9EszTgDg9C4LDOgBzWcj/0K52KfJrpRXwUsWV6kqTDuoCfLA==} @@ -8629,7 +8753,7 @@ packages: '@rollup/pluginutils': 5.1.0 '@svgr/core': 7.0.0(typescript@4.9.5) '@svgr/plugin-jsx': 7.0.0 - vite: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + vite: 5.2.0(@types/node@20.11.30)(sass@1.77.2) transitivePeerDependencies: - rollup - supports-color @@ -8647,7 +8771,7 @@ packages: kolorist: 1.8.0 sirv: 2.0.4 ufo: 1.5.3 - vite: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + vite: 5.2.0(@types/node@20.11.30)(sass@1.77.2) transitivePeerDependencies: - rollup - supports-color @@ -8659,7 +8783,7 @@ packages: vite: '>=5.0.0' dependencies: chalk: 5.3.0 - vite: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + vite: 5.2.0(@types/node@20.11.30)(sass@1.77.2) dev: true /vite-plugin-wasm@3.3.0(vite@5.2.0): @@ -8667,10 +8791,10 @@ packages: peerDependencies: vite: ^2 || ^3 || ^4 || ^5 dependencies: - vite: 5.2.0(@types/node@20.11.30)(sass@1.70.0) + vite: 5.2.0(@types/node@20.11.30)(sass@1.77.2) dev: false - /vite@5.2.0(@types/node@20.11.30)(sass@1.70.0): + /vite@5.2.0(@types/node@20.11.30)(sass@1.77.2): resolution: {integrity: sha512-xMSLJNEjNk/3DJRgWlPADDwaU9AgYRodDH2t6oENhJnIlmU9Hx1Q6VpjyXua/JdMw1WJRbnAgHJ9xgET9gnIAg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -8702,7 +8826,7 @@ packages: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.13.2 - sass: 1.70.0 + sass: 1.77.2 optionalDependencies: fsevents: 2.3.3 @@ -8844,24 +8968,24 @@ packages: engines: {node: '>=0.4'} dev: true - /y-indexeddb@9.0.12(yjs@13.6.14): + /y-indexeddb@9.0.12(yjs@13.6.15): resolution: {integrity: sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: yjs: ^13.0.0 dependencies: - lib0: 0.2.93 - yjs: 13.6.14 + lib0: 0.2.94 + yjs: 13.6.15 dev: false - /y-protocols@1.0.6(yjs@13.6.14): + /y-protocols@1.0.6(yjs@13.6.15): resolution: {integrity: sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: yjs: ^13.0.0 dependencies: - lib0: 0.2.93 - yjs: 13.6.14 + lib0: 0.2.94 + yjs: 13.6.15 dev: false /y18n@5.0.8: @@ -8901,11 +9025,11 @@ packages: fd-slicer: 1.1.0 dev: true - /yjs@13.6.14: - resolution: {integrity: sha512-D+7KcUr0j+vBCUSKXXEWfA+bG4UQBviAwP3gYBhkstkgwy5+8diOPMx0iqLIOxNo/HxaREUimZRxqHGAHCL2BQ==} + /yjs@13.6.15: + resolution: {integrity: sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} dependencies: - lib0: 0.2.93 + lib0: 0.2.94 dev: false /yn@3.1.1: diff --git a/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs b/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs new file mode 100644 index 0000000000..83f5bb25d5 --- /dev/null +++ b/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs @@ -0,0 +1,61 @@ +const fs = require('fs'); +const path = require('path'); + +// Read CSS file +const cssFilePath = path.join(__dirname, '../src/styles/variables/light.variables.css'); +const cssContent = fs.readFileSync(cssFilePath, 'utf-8'); + +// Extract color variables +const shadowVariables = cssContent.match(/--shadow:\s.*;/g); +const colorVariables = cssContent.match(/--[\w-]+:\s*#[0-9a-fA-F]{6}/g); + +if (!colorVariables) { + console.error('No color variables found in CSS file.'); + process.exit(1); +} + +const shadows = shadowVariables.reduce((shadows, variable) => { + const [name, value] = variable.split(':').map(str => str.trim()); + const formattedName = name.replace('--', '').replace(/-/g, '_'); + const key = 'md'; + + shadows[key] = `var(${name})`; + return shadows; +}, {}); +// Generate Tailwind CSS colors configuration +// Replace -- with _ and - with _ in color variable names +const tailwindColors = colorVariables.reduce((colors, variable) => { + const [name, value] = variable.split(':').map(str => str.trim()); + const formattedName = name.replace('--', '').replace(/-/g, '_'); + const category = formattedName.split('_')[0]; + const key = formattedName.replace(`${category}_`, ''); + + if (!colors[category]) { + colors[category] = {}; + } + colors[category][key] = `var(${name})`; + return colors; +}, {}); + +const tailwindColorsFormatted = JSON.stringify(tailwindColors, null, 2) + .replace(/_/g, '-'); +const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; + +// Write Tailwind CSS colors configuration to file +const tailwindColorTemplate = ` +${header} +module.exports = ${tailwindColorsFormatted}; +`; + +const tailwindShadowTemplate = ` +${header} +module.exports = ${JSON.stringify(shadows, null, 2).replace(/_/g, '-')}; +`; + +const tailwindConfigFilePath = path.join(__dirname, '../tailwind/colors.cjs'); +fs.writeFileSync(tailwindConfigFilePath, tailwindColorTemplate, 'utf-8'); + +const tailwindShadowFilePath = path.join(__dirname, '../tailwind/box-shadow.cjs'); +fs.writeFileSync(tailwindShadowFilePath, tailwindShadowTemplate, 'utf-8'); + +console.log('Tailwind CSS colors configuration generated successfully.'); diff --git a/frontend/appflowy_web_app/src/application/collab.type.ts b/frontend/appflowy_web_app/src/application/collab.type.ts index affc19e921..4f61246aab 100644 --- a/frontend/appflowy_web_app/src/application/collab.type.ts +++ b/frontend/appflowy_web_app/src/application/collab.type.ts @@ -138,15 +138,15 @@ export interface FolderMeta { current_workspace: string; } -export enum CoverType { +export enum DocCoverType { Color = 'CoverType.color', Image = 'CoverType.file', Asset = 'CoverType.asset', } -export type PageCover = { +export type DocCover = { image_type?: ImageType; - cover_selection_type?: CoverType; + cover_selection_type?: DocCoverType; cover_selection?: string; } | null; @@ -166,6 +166,7 @@ export enum YjsEditorKey { // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values database_row = 'data', user_awareness = 'user_awareness', + empty = 'empty', // document blocks = 'blocks', @@ -199,6 +200,10 @@ export enum YjsFolderKey { id = 'id', name = 'name', icon = 'icon', + extra = 'extra', + cover = 'cover', + line_height_layout = 'line_height_layout', + font_layout = 'font_layout', type = 'ty', value = 'value', layout = 'layout', @@ -337,7 +342,7 @@ export interface YView extends Y.Map { get(key: YjsFolderKey.name): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.icon): string; + get(key: YjsFolderKey.icon | YjsFolderKey.extra): string; // eslint-disable-next-line @typescript-eslint/unified-signatures get(key: YjsFolderKey.layout): string; @@ -607,3 +612,15 @@ export const databaseLayoutMap = { [DatabaseViewLayout.Board]: 'board', [DatabaseViewLayout.Calendar]: 'calendar', }; + +export enum FontLayout { + small = 'small', + normal = 'normal', + large = 'large', +} + +export enum LineHeightLayout { + small = 'small', + normal = 'normal', + large = 'large', +} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/context.ts b/frontend/appflowy_web_app/src/application/database-yjs/context.ts index 96a0f067c4..8e7aa4b867 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/context.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/context.ts @@ -5,9 +5,10 @@ import * as Y from 'yjs'; export interface DatabaseContextState { readOnly: boolean; - doc: YDoc; + databaseDoc: YDoc; viewId: string; rowDocMap: Y.Map; + isDatabaseRowPage?: boolean; navigateToRow?: (rowId: string) => void; } @@ -15,18 +16,30 @@ export const DatabaseContext = createContext(null); export const useDatabase = () => { const database = useContext(DatabaseContext) - ?.doc?.getMap(YjsEditorKey.data_section) + ?.databaseDoc?.getMap(YjsEditorKey.data_section) .get(YjsEditorKey.database) as YDatabase; return database; }; +export function useDatabaseViewId() { + return useContext(DatabaseContext)?.viewId; +} + export const useNavigateToRow = () => { return useContext(DatabaseContext)?.navigateToRow; }; +export const useRowDocMap = () => { + return useContext(DatabaseContext)?.rowDocMap; +}; + +export const useIsDatabaseRowPage = () => { + return useContext(DatabaseContext)?.isDatabaseRowPage; +}; + export const useRow = (rowId: string) => { - const rows = useContext(DatabaseContext)?.rowDocMap; + const rows = useRowDocMap(); return rows?.get(rowId)?.getMap(YjsEditorKey.data_section); }; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts b/frontend/appflowy_web_app/src/application/database-yjs/selector.ts index d42399d882..697cd8560e 100644 --- a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts +++ b/frontend/appflowy_web_app/src/application/database-yjs/selector.ts @@ -1,12 +1,19 @@ -import { FieldId, SortId, YDatabaseField, YjsDatabaseKey, YjsEditorKey, YjsFolderKey } from '@/application/collab.type'; +import { + FieldId, + SortId, + YDatabaseField, + YDoc, + YjsDatabaseKey, + YjsEditorKey, + YjsFolderKey, +} from '@/application/collab.type'; import { getCell, metaIdFromRowId, MIN_COLUMN_WIDTH } from '@/application/database-yjs/const'; import { - DatabaseContext, useDatabase, useDatabaseFields, useDatabaseView, - useRow, - useRowData, + useIsDatabaseRowPage, + useRowDocMap, useRows, useViewId, } from '@/application/database-yjs/context'; @@ -18,8 +25,9 @@ import { useId } from '@/components/_shared/context-provider/IdProvider'; import { parseYDatabaseCellToCell } from '@/components/database/components/cell/cell.parse'; import { DateTimeCell } from '@/components/database/components/cell/cell.type'; import dayjs from 'dayjs'; -import debounce from 'lodash-es/debounce'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { throttle } from 'lodash-es'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import Y from 'yjs'; import { CalendarLayoutSetting, FieldType, FieldVisibility, Filter, RowMetaKey, SortCondition } from './database.type'; export interface Column { @@ -368,8 +376,9 @@ export function useGroup(groupId: string) { export function useRowsByGroup(groupId: string) { const { columns, fieldId } = useGroup(groupId); - const rows = useContext(DatabaseContext)?.rowDocMap; + const rows = useRowDocMap(); const rowOrders = useRowOrdersSelector(); + const fields = useDatabaseFields(); const [notFound, setNotFound] = useState(false); const [groupResult, setGroupResult] = useState>(new Map()); @@ -378,6 +387,8 @@ export function useRowsByGroup(groupId: string) { if (!fieldId || !rowOrders || !rows) return; const onConditionsChange = () => { + if (rows.size !== rowOrders?.length) return; + const newResult = new Map(); const field = fields.get(fieldId); @@ -400,11 +411,9 @@ export function useRowsByGroup(groupId: string) { onConditionsChange(); - const debounceConditionsChange = debounce(onConditionsChange, 200); - - fields.observeDeep(debounceConditionsChange); + fields.observeDeep(onConditionsChange); return () => { - fields.unobserveDeep(debounceConditionsChange); + fields.unobserveDeep(onConditionsChange); }; }, [fieldId, fields, rowOrders, rows]); @@ -419,62 +428,139 @@ export function useRowsByGroup(groupId: string) { } export function useRowOrdersSelector() { - const rows = useContext(DatabaseContext)?.rowDocMap; + const isDatabaseRowPage = useIsDatabaseRowPage(); + const { rows, clock } = useRowDocMapSelector(); const [rowOrders, setRowOrders] = useState(); const view = useDatabaseView(); const sorts = view?.get(YjsDatabaseKey.sorts); const fields = useDatabaseFields(); const filters = view?.get(YjsDatabaseKey.filters); + const onConditionsChange = useCallback(() => { + const originalRowOrders = view?.get(YjsDatabaseKey.row_orders).toJSON(); + + if (!originalRowOrders || !rows) return; + + if (originalRowOrders.length !== rows.size && !isDatabaseRowPage) return; + if (sorts?.length === 0 && filters?.length === 0) { + setRowOrders(originalRowOrders); + return; + } + + let rowOrders: Row[] | undefined; + + if (sorts?.length) { + rowOrders = sortBy(originalRowOrders, sorts, fields, rows); + } + + if (filters?.length) { + rowOrders = filterBy(rowOrders ?? originalRowOrders, filters, fields, rows); + } + + if (rowOrders) { + setRowOrders(rowOrders); + } else { + setRowOrders(originalRowOrders); + } + }, [fields, filters, rows, sorts, view, isDatabaseRowPage]); useEffect(() => { - const onConditionsChange = () => { - const originalRowOrders = view?.get(YjsDatabaseKey.row_orders).toJSON(); - - if (!originalRowOrders || !rows) return; - - if (sorts?.length === 0 && filters?.length === 0) { - setRowOrders(originalRowOrders); - return; - } - - let rowOrders: Row[] | undefined; - - if (sorts?.length) { - rowOrders = sortBy(originalRowOrders, sorts, fields, rows); - } - - if (filters?.length) { - rowOrders = filterBy(rowOrders ?? originalRowOrders, filters, fields, rows); - } - - if (rowOrders) { - setRowOrders(rowOrders); - } else { - setRowOrders(originalRowOrders); - } - }; - - const debounceConditionsChange = debounce(onConditionsChange, 200); - onConditionsChange(); - sorts?.observeDeep(debounceConditionsChange); - filters?.observeDeep(debounceConditionsChange); - fields?.observeDeep(debounceConditionsChange); - rows?.observeDeep(debounceConditionsChange); + }, [onConditionsChange, clock]); + + useEffect(() => { + const throttleChange = throttle(onConditionsChange, 200); + + sorts?.observeDeep(throttleChange); + filters?.observeDeep(throttleChange); + fields?.observeDeep(throttleChange); return () => { - sorts?.unobserveDeep(debounceConditionsChange); - filters?.unobserveDeep(debounceConditionsChange); - fields?.unobserveDeep(debounceConditionsChange); - rows?.observeDeep(debounceConditionsChange); + sorts?.unobserveDeep(throttleChange); + filters?.unobserveDeep(throttleChange); + fields?.unobserveDeep(throttleChange); }; - }, [fields, rows, sorts, filters, view]); + }, [onConditionsChange, fields, filters, sorts]); return rowOrders; } +export function useRowDocMapSelector() { + const rowMap = useRowDocMap(); + const [clock, setClock] = useState(0); + + useEffect(() => { + if (!rowMap) return; + const observerEvent = () => setClock((prev) => prev + 1); + + const rowIds = Array.from(rowMap?.keys() || []); + + rowMap.observe(observerEvent); + + const observers = rowIds.map((rowId) => { + return observeDeepRow(rowId, rowMap, observerEvent); + }); + + return () => { + rowMap.unobserve(observerEvent); + observers.forEach((observer) => observer()); + }; + }, [rowMap]); + + return { + rows: rowMap, + clock, + }; +} + +export function observeDeepRow( + rowId: string, + rowMap: Y.Map, + observerEvent: () => void, + key: YjsEditorKey.meta | YjsEditorKey.database_row = YjsEditorKey.database_row +) { + const rowSharedRoot = rowMap?.get(rowId)?.getMap(YjsEditorKey.data_section); + const row = rowSharedRoot?.get(key); + + rowSharedRoot?.observe(observerEvent); + row?.observeDeep(observerEvent); + return () => { + rowSharedRoot?.unobserve(observerEvent); + row?.unobserveDeep(observerEvent); + }; +} + +export function useRowDataSelector(rowId: string) { + const rowMap = useRowDocMap(); + const rowSharedRoot = rowMap?.get(rowId)?.getMap(YjsEditorKey.data_section); + const row = rowSharedRoot?.get(YjsEditorKey.database_row); + + const [clock, setClock] = useState(0); + + useEffect(() => { + if (!rowMap) return; + const onChange = () => { + setClock((prev) => prev + 1); + }; + + const observer = observeDeepRow(rowId, rowMap, onChange); + + rowMap.observe(onChange); + + return () => { + rowMap.unobserve(onChange); + observer(); + }; + }, [rowId, rowMap]); + + return { + row, + clock, + }; +} + export function useCellSelector({ rowId, fieldId }: { rowId: string; fieldId: string }) { - const row = useRowData(rowId); + const { row } = useRowDataSelector(rowId); + const cell = row?.get(YjsDatabaseKey.cells)?.get(fieldId); const [cellValue, setCellValue] = useState(() => (cell ? parseYDatabaseCellToCell(cell) : undefined)); @@ -504,7 +590,7 @@ export function useCalendarEventsSelector() { const filedId = setting.fieldId; const { field } = useFieldSelector(filedId); const rowOrders = useRowOrdersSelector(); - const rows = useContext(DatabaseContext)?.rowDocMap; + const rows = useRowDocMap(); const [events, setEvents] = useState([]); const [emptyEvents, setEmptyEvents] = useState([]); @@ -610,35 +696,67 @@ export interface RowMeta { isEmptyDocument: boolean; } +const metaIdMapFromRowIdMap = new Map>(); + +function getMetaIdMap(rowId: string) { + const hasMetaIdMap = metaIdMapFromRowIdMap.has(rowId); + + if (!hasMetaIdMap) { + const parser = metaIdFromRowId(rowId); + const map = new Map(); + + map.set(RowMetaKey.IconId, parser(RowMetaKey.IconId)); + map.set(RowMetaKey.CoverId, parser(RowMetaKey.CoverId)); + map.set(RowMetaKey.DocumentId, parser(RowMetaKey.DocumentId)); + map.set(RowMetaKey.IsDocumentEmpty, parser(RowMetaKey.IsDocumentEmpty)); + metaIdMapFromRowIdMap.set(rowId, map); + return map; + } + + return metaIdMapFromRowIdMap.get(rowId) as Map; +} + export const useRowMetaSelector = (rowId: string) => { const [meta, setMeta] = useState(); - const yMeta = useRow(rowId)?.get(YjsEditorKey.meta); + const rowMap = useRowDocMap(); + + const updateMeta = useCallback(() => { + const metaKeyMap = getMetaIdMap(rowId); + + const iconKey = metaKeyMap.get(RowMetaKey.IconId) ?? ''; + const coverKey = metaKeyMap.get(RowMetaKey.CoverId) ?? ''; + const documentId = metaKeyMap.get(RowMetaKey.DocumentId) ?? ''; + const isEmptyDocumentKey = metaKeyMap.get(RowMetaKey.IsDocumentEmpty) ?? ''; + const rowSharedRoot = rowMap?.get(rowId)?.getMap(YjsEditorKey.data_section); + const yMeta = rowSharedRoot?.get(YjsEditorKey.meta); + + if (!yMeta) return; + const metaJson = yMeta.toJSON(); + + const icon = metaJson[iconKey]; + const cover = metaJson[coverKey]; + const isEmptyDocument = metaJson[isEmptyDocumentKey]; + + setMeta({ + icon, + cover, + documentId, + isEmptyDocument, + }); + }, [rowId, rowMap]); useEffect(() => { - if (!yMeta) return; - const onChange = () => { - const metaJson = yMeta.toJSON(); - const getData = metaIdFromRowId(rowId); - const icon = metaJson[getData(RowMetaKey.IconId)]; - const cover = metaJson[getData(RowMetaKey.CoverId)]; - const documentId = getData(RowMetaKey.DocumentId); - const isEmptyDocument = metaJson[getData(RowMetaKey.IsDocumentEmpty)]; + if (!rowMap) return; + updateMeta(); + const observer = observeDeepRow(rowId, rowMap, updateMeta, YjsEditorKey.meta); - return setMeta({ - icon, - cover, - documentId, - isEmptyDocument, - }); - }; + rowMap.observe(updateMeta); - onChange(); - - yMeta.observe(onChange); return () => { - yMeta.unobserve(onChange); + rowMap.unobserve(updateMeta); + observer(); }; - }, [rowId, yMeta]); + }, [rowId, rowMap, updateMeta]); return meta; }; diff --git a/frontend/appflowy_web_app/src/application/folder-yjs/context.ts b/frontend/appflowy_web_app/src/application/folder-yjs/context.ts index 57c4d171df..02a2cfcba4 100644 --- a/frontend/appflowy_web_app/src/application/folder-yjs/context.ts +++ b/frontend/appflowy_web_app/src/application/folder-yjs/context.ts @@ -1,8 +1,49 @@ -import { YFolder } from '@/application/collab.type'; -import { createContext, useContext } from 'react'; +import { ViewLayout, YFolder, YjsFolderKey } from '@/application/collab.type'; +import { createContext, useCallback, useContext } from 'react'; +import { useParams } from 'react-router-dom'; -export const FolderContext = createContext(null); +export interface Crumb { + viewId: string; + rowId?: string; + name: string; + icon: string; +} + +export const FolderContext = createContext<{ + folder: YFolder | null; + onNavigateToView?: (viewId: string) => void; + crumbs?: Crumb[]; + setCrumbs?: React.Dispatch>; +} | null>(null); export const useFolderContext = () => { - return useContext(FolderContext); + return useContext(FolderContext)?.folder; +}; + +export const useViewLayout = () => { + const folder = useFolderContext(); + const { objectId } = useParams(); + const views = folder?.get(YjsFolderKey.views); + const view = objectId ? views?.get(objectId) : null; + + return Number(view?.get(YjsFolderKey.layout)) as ViewLayout; +}; + +export const useNavigateToView = () => { + return useContext(FolderContext)?.onNavigateToView; +}; + +export const useCrumbs = () => { + return useContext(FolderContext)?.crumbs; +}; + +export const usePushCrumb = () => { + const { setCrumbs } = useContext(FolderContext) || {}; + + return useCallback( + (crumb: Crumb) => { + setCrumbs?.((prevCrumbs) => [...prevCrumbs, crumb]); + }, + [setCrumbs] + ); }; diff --git a/frontend/appflowy_web_app/src/application/folder-yjs/folder.type.ts b/frontend/appflowy_web_app/src/application/folder-yjs/folder.type.ts new file mode 100644 index 0000000000..2a3bd0a2e7 --- /dev/null +++ b/frontend/appflowy_web_app/src/application/folder-yjs/folder.type.ts @@ -0,0 +1,8 @@ +export enum CoverType { + NormalColor = 'color', + GradientColor = 'gradient', + BuildInImage = 'none', + CustomImage = 'custom', + LocalImage = 'local', + UpsplashImage = 'unsplash', +} diff --git a/frontend/appflowy_web_app/src/application/folder-yjs/selector.ts b/frontend/appflowy_web_app/src/application/folder-yjs/selector.ts index e5eb68203e..7ef758ba5e 100644 --- a/frontend/appflowy_web_app/src/application/folder-yjs/selector.ts +++ b/frontend/appflowy_web_app/src/application/folder-yjs/selector.ts @@ -54,10 +54,10 @@ export function useViewSelector(viewId: string) { setView(view || null); const observerEvent = () => setClock((prev) => prev + 1); - view.observe(observerEvent); + view?.observe(observerEvent); return () => { - view.unobserve(observerEvent); + view?.unobserve(observerEvent); }; }, [folder, viewId]); diff --git a/frontend/appflowy_web_app/src/application/services/js-services/database.service.ts b/frontend/appflowy_web_app/src/application/services/js-services/database.service.ts index e80915c393..a105df0015 100644 --- a/frontend/appflowy_web_app/src/application/services/js-services/database.service.ts +++ b/frontend/appflowy_web_app/src/application/services/js-services/database.service.ts @@ -11,6 +11,8 @@ import * as Y from 'yjs'; export class JSDatabaseService implements DatabaseService { private loadedDatabaseId: Set = new Set(); + private cacheDatabaseRowDocMap: Map = new Map(); + constructor() { // } @@ -23,9 +25,20 @@ export class JSDatabaseService implements DatabaseService { databaseDoc: YDoc; rows: Y.Map; }> { - const rootRowsDoc = new Y.Doc(); - const rowsFolder = rootRowsDoc.getMap(); const isLoaded = this.loadedDatabaseId.has(databaseId); + + const rootRowsDoc = + this.cacheDatabaseRowDocMap.get(databaseId) ?? + new Y.Doc({ + guid: databaseId, + }); + + if (!this.cacheDatabaseRowDocMap.has(databaseId)) { + this.cacheDatabaseRowDocMap.set(databaseId, rootRowsDoc); + } + + const rowsFolder: Y.Map = rootRowsDoc.getMap(); + let databaseDoc: YDoc | undefined = undefined; if (isLoaded) { @@ -51,13 +64,15 @@ export class JSDatabaseService implements DatabaseService { for (const id of ids) { const { doc } = await getCollabStorage(id, CollabType.DatabaseRow); - rowsFolder.set(id, doc); + if (!rowsFolder.has(id)) { + rowsFolder.set(id, doc); + } } } else { - const rows = await this.loadDatabaseRows(workspaceId, ids); - - rows.forEach((row, id) => { - rowsFolder.set(id, row); + void this.loadDatabaseRows(workspaceId, ids, (id, row) => { + if (!rowsFolder.has(id)) { + rowsFolder.set(id, row); + } }); } @@ -74,19 +89,20 @@ export class JSDatabaseService implements DatabaseService { console.log('Update rows', rowIds); void this.loadDatabaseRows( workspaceId, - rowIds.map((item) => item.id) - ).then((newRows) => { - newRows.forEach((row, id) => { - rowsFolder.set(id, row); - }); - }); + rowIds.map((item) => item.id), + (rowId: string, rowDoc) => { + if (!rowsFolder.has(rowId)) { + rowsFolder.set(rowId, rowDoc); + } + } + ); } }); } return { databaseDoc, - rows: rowsFolder as Y.Map, + rows: rowsFolder, }; } @@ -144,6 +160,7 @@ export class JSDatabaseService implements DatabaseService { }; databaseDoc.on('update', handleUpdate); + console.log('Database loaded', rows.toJSON()); return { databaseDoc, @@ -151,9 +168,7 @@ export class JSDatabaseService implements DatabaseService { }; } - async loadDatabaseRows(workspaceId: string, rowIds: string[]) { - const rows = new Map(); - + async loadDatabaseRows(workspaceId: string, rowIds: string[], rowCallback: (rowId: string, rowDoc: YDoc) => void) { try { await batchCollabs( workspaceId, @@ -161,12 +176,14 @@ export class JSDatabaseService implements DatabaseService { object_id: id, collab_type: CollabType.DatabaseRow, })), - (id, rowDoc) => rows.set(id, rowDoc) + rowCallback ); } catch (e) { console.error(e); } + } - return rows; + async closeDatabase(databaseId: string) { + this.cacheDatabaseRowDocMap.delete(databaseId); } } diff --git a/frontend/appflowy_web_app/src/application/services/js-services/db/index.ts b/frontend/appflowy_web_app/src/application/services/js-services/db/index.ts index bf5f0c7aa1..f17ac50531 100644 --- a/frontend/appflowy_web_app/src/application/services/js-services/db/index.ts +++ b/frontend/appflowy_web_app/src/application/services/js-services/db/index.ts @@ -10,6 +10,7 @@ import * as Y from 'yjs'; export async function openCollabDB(docName: string): Promise { const name = `${databasePrefix}_${docName}`; const doc = new Y.Doc(); + const provider = new IndexeddbPersistence(name, doc); let resolve: (value: unknown) => void; @@ -26,14 +27,6 @@ export async function openCollabDB(docName: string): Promise { return doc as YDoc; } -export async function deleteCollabDB(docName: string) { - const name = `${databasePrefix}_${docName}`; - const doc = new Y.Doc(); - const provider = new IndexeddbPersistence(name, doc); - - await provider.destroy(); -} - export function getDBName(id: string, type: string) { const { uuid } = getAuthInfo() || {}; diff --git a/frontend/appflowy_web_app/src/application/services/js-services/storage/collab.ts b/frontend/appflowy_web_app/src/application/services/js-services/storage/collab.ts index bdc1392024..bdf57518c0 100644 --- a/frontend/appflowy_web_app/src/application/services/js-services/storage/collab.ts +++ b/frontend/appflowy_web_app/src/application/services/js-services/storage/collab.ts @@ -1,4 +1,4 @@ -import { CollabType, YDoc, YjsEditorKey } from '@/application/collab.type'; +import { CollabType, YDoc, YjsEditorKey, YSharedRoot } from '@/application/collab.type'; import { getDBName, openCollabDB } from '@/application/services/js-services/db'; import { APIService } from '@/application/services/js-services/wasm'; import { applyYDoc } from '@/application/ydoc/apply'; @@ -30,11 +30,28 @@ function collabTypeToDBType(type: CollabType) { } } +const collabSharedRootKeyMap = { + [CollabType.Folder]: YjsEditorKey.folder, + [CollabType.Document]: YjsEditorKey.document, + [CollabType.Database]: YjsEditorKey.database, + [CollabType.WorkspaceDatabase]: YjsEditorKey.workspace_database, + [CollabType.DatabaseRow]: YjsEditorKey.database_row, + [CollabType.UserAwareness]: YjsEditorKey.user_awareness, + [CollabType.Empty]: YjsEditorKey.empty, +}; + export async function getCollabStorage(id: string, type: CollabType) { const name = getDBName(id, collabTypeToDBType(type)); const doc = await openCollabDB(name); - const localExist = doc.share.has(YjsEditorKey.data_section); + let localExist = false; + const existData = doc.share.has(YjsEditorKey.data_section); + + if (existData) { + const data = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; + + localExist = data.has(collabSharedRootKeyMap[type] as string); + } return { doc, @@ -74,28 +91,27 @@ export async function batchCollabs( for (const item of params) { const { object_id, collab_type } = item; - const { doc } = await getCollabStorage(object_id, collab_type); + const { doc, localExist } = await getCollabStorage(object_id, collab_type); - if (rowCallback) { + if (rowCallback && localExist) { rowCallback(object_id, doc); } } - // Async fetch collab data and apply to Y.Doc - void (async () => { - const res = await batchFetchCollab(workspaceId, params); + const res = await batchFetchCollab(workspaceId, params); - for (const id of Object.keys(res)) { - const type = params.find((param) => param.object_id === id)?.collab_type; - const data = res[id]; + for (const id of Object.keys(res)) { + const type = params.find((param) => param.object_id === id)?.collab_type; + const data = res[id]; - if (type === undefined || !data) { - continue; - } - - const { doc } = await getCollabStorage(id, type); - - applyYDoc(doc, data); + if (type === undefined || !data) { + continue; } - })(); + + const { doc } = await getCollabStorage(id, type); + + applyYDoc(doc, data); + + rowCallback?.(id, doc); + } } diff --git a/frontend/appflowy_web_app/src/application/services/services.type.ts b/frontend/appflowy_web_app/src/application/services/services.type.ts index 1d6a7d45b0..9dd910d6d5 100644 --- a/frontend/appflowy_web_app/src/application/services/services.type.ts +++ b/frontend/appflowy_web_app/src/application/services/services.type.ts @@ -51,6 +51,7 @@ export interface DatabaseService { databaseDoc: YDoc; rows: Y.Map; }>; + closeDatabase: (databaseId: string) => Promise; } export interface UserService { diff --git a/frontend/appflowy_web_app/src/application/services/tauri-services/database.service.ts b/frontend/appflowy_web_app/src/application/services/tauri-services/database.service.ts index 8644914ca7..7707c3b779 100644 --- a/frontend/appflowy_web_app/src/application/services/tauri-services/database.service.ts +++ b/frontend/appflowy_web_app/src/application/services/tauri-services/database.service.ts @@ -7,6 +7,10 @@ export class TauriDatabaseService implements DatabaseService { // } + async closeDatabase(_databaseId: string) { + return Promise.reject('Not implemented'); + } + async openDatabase( _workspaceId: string, _viewId: string diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/plugins/withYjs.ts b/frontend/appflowy_web_app/src/application/slate-yjs/plugins/withYjs.ts index dd3ec137d7..9784292884 100644 --- a/frontend/appflowy_web_app/src/application/slate-yjs/plugins/withYjs.ts +++ b/frontend/appflowy_web_app/src/application/slate-yjs/plugins/withYjs.ts @@ -59,10 +59,8 @@ export function withYjs( doc: Y.Doc, { localOrigin, - includeRoot = true, }: { localOrigin: CollabOrigin; - includeRoot?: boolean; } ): T & YjsEditor { const e = editor as T & YjsEditor; @@ -71,7 +69,7 @@ export function withYjs( e.sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; const initializeDocumentContent = () => { - const content = yDocToSlateContent(doc, includeRoot); + const content = yDocToSlateContent(doc); if (!content) { return; diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/applySlateOpts.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/applySlateOpts.ts index 5a2fd6670c..f6e9abaf40 100644 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/applySlateOpts.ts +++ b/frontend/appflowy_web_app/src/application/slate-yjs/utils/applySlateOpts.ts @@ -1,6 +1,7 @@ import { Operation, Node } from 'slate'; import * as Y from 'yjs'; -export function applySlateOp(ydoc: Y.Doc, slateRoot: Node, op: Operation) { - console.log('applySlateOp', op); +// transform slate op to yjs op and apply it to ydoc +export function applySlateOp(_ydoc: Y.Doc, _slateRoot: Node, _op: Operation) { + // console.log('applySlateOp', op); } diff --git a/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts b/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts index ae8b6698e6..65689002ad 100644 --- a/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts +++ b/frontend/appflowy_web_app/src/application/slate-yjs/utils/convert.ts @@ -22,8 +22,7 @@ interface BlockJson { external_id?: string; } -export function yDocToSlateContent(doc: YDoc, includeRoot?: boolean): Element | undefined { - console.log(doc); +export function yDocToSlateContent(doc: YDoc): Element | undefined { const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; console.log(sharedRoot.toJSON()); @@ -107,13 +106,6 @@ export function yDocToSlateContent(doc: YDoc, includeRoot?: boolean): Element | if (!result) return; - if (!includeRoot) { - return result; - } - - const { children, ...rootNode } = result; - - // load font family if (fontFamilys.length > 0) { window.WebFont?.load({ google: { @@ -122,21 +114,7 @@ export function yDocToSlateContent(doc: YDoc, includeRoot?: boolean): Element | }); } - return { - children: [ - { - ...rootNode, - children: [ - { - textId: pageId, - type: YjsEditorKey.text, - children: [{ text: '' }], - }, - ], - }, - ...children, - ], - }; + return result; } export function blockToSlateNode(block: BlockJson): Element { diff --git a/frontend/appflowy_web_app/src/components/_shared/context-provider/FolderProvider.tsx b/frontend/appflowy_web_app/src/components/_shared/context-provider/FolderProvider.tsx index be466fdc49..37bb03533b 100644 --- a/frontend/appflowy_web_app/src/components/_shared/context-provider/FolderProvider.tsx +++ b/frontend/appflowy_web_app/src/components/_shared/context-provider/FolderProvider.tsx @@ -1,9 +1,23 @@ import { YFolder } from '@/application/collab.type'; -import { FolderContext } from '@/application/folder-yjs'; +import { Crumb, FolderContext } from '@/application/folder-yjs'; -export const FolderProvider: React.FC<{ folder: YFolder | null; children?: React.ReactNode }> = ({ - folder, - children, -}) => { - return {children}; +export const FolderProvider: React.FC<{ + folder: YFolder | null; + children?: React.ReactNode; + onNavigateToView?: (viewId: string) => void; + crumbs?: Crumb[]; + setCrumbs?: React.Dispatch>; +}> = ({ folder, children, onNavigateToView, crumbs, setCrumbs }) => { + return ( + + {children} + + ); }; diff --git a/frontend/appflowy_web_app/src/components/_shared/not-found/RecordNotFound.tsx b/frontend/appflowy_web_app/src/components/_shared/not-found/RecordNotFound.tsx index 9216a92c69..f8a6412d06 100644 --- a/frontend/appflowy_web_app/src/components/_shared/not-found/RecordNotFound.tsx +++ b/frontend/appflowy_web_app/src/components/_shared/not-found/RecordNotFound.tsx @@ -2,7 +2,7 @@ import { Button, Dialog, DialogActions, DialogContent, DialogContentText, Dialog import React from 'react'; import { useNavigate } from 'react-router-dom'; -export function RecordNotFound({ open, workspaceId }: { workspaceId: string; open: boolean }) { +export function RecordNotFound({ open, workspaceId, title }: { workspaceId: string; open: boolean; title?: string }) { const navigate = useNavigate(); return ( @@ -10,13 +10,13 @@ export function RecordNotFound({ open, workspaceId }: { workspaceId: string; ope Oops.. something went wrong - Sorry, the page you are looking for does not exist. + {title ? title : 'The record you are looking for does not exist.'} - +
+ {primaryFieldId && } {fields.map((field) => { return ; })} diff --git a/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx b/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx new file mode 100644 index 0000000000..6ede73fd99 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/database/components/calendar/event/EventPaperTitle.tsx @@ -0,0 +1,15 @@ +import { useCellSelector } from '@/application/database-yjs'; +import { TextCell } from '@/components/database/components/cell/cell.type'; +import { TextProperty } from '@/components/database/components/property/text'; +import React from 'react'; + +function EventPaperTitle({ fieldId, rowId }: { fieldId: string; rowId: string }) { + const cell = useCellSelector({ + fieldId, + rowId, + }); + + return ; +} + +export default EventPaperTitle; diff --git a/frontend/appflowy_web_app/src/components/database/components/calendar/toolbar/NoDate.tsx b/frontend/appflowy_web_app/src/components/database/components/calendar/toolbar/NoDate.tsx index 7965bc33b7..b6238185eb 100644 --- a/frontend/appflowy_web_app/src/components/database/components/calendar/toolbar/NoDate.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/calendar/toolbar/NoDate.tsx @@ -33,7 +33,7 @@ function NoDate({ emptyEvents }: { emptyEvents: CalendarEvent[] }) { - +
+ +
)}
); diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx index 5bd8eb7b87..47ac405966 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationCell.tsx @@ -1,13 +1,17 @@ +import { FieldType } from '@/application/database-yjs'; import { CellProps, RelationCell as RelationCellType } from '@/components/database/components/cell/cell.type'; import RelationItems from '@/components/database/components/cell/relation/RelationItems'; import React from 'react'; export function RelationCell({ cell, fieldId, style, placeholder }: CellProps) { + if (cell?.fieldType !== FieldType.Relation) return null; + if (!cell?.data) return placeholder ? (
{placeholder}
) : null; + return ; } diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx index 6d68eee4af..1a998c5122 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationItems.tsx @@ -1,23 +1,33 @@ -import { YDatabaseField, YDatabaseFields, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/collab.type'; -import { parseRelationTypeOption, useFieldSelector } from '@/application/database-yjs'; +import { YDatabaseField, YDatabaseFields, YjsDatabaseKey, YjsEditorKey } from '@/application/collab.type'; +import { + DatabaseContextState, + parseRelationTypeOption, + useDatabase, + useFieldSelector, + useNavigateToRow, +} from '@/application/database-yjs'; import { useId } from '@/components/_shared/context-provider/IdProvider'; import { AFConfigContext } from '@/components/app/AppConfig'; import { RelationCell, RelationCellData } from '@/components/database/components/cell/cell.type'; import { RelationPrimaryValue } from '@/components/database/components/cell/relation/RelationPrimaryValue'; import React, { useContext, useEffect, useMemo, useState } from 'react'; -import * as Y from 'yjs'; function RelationItems({ style, cell, fieldId }: { cell: RelationCell; fieldId: string; style?: React.CSSProperties }) { const { field } = useFieldSelector(fieldId); + const currentDatabaseId = useDatabase()?.get(YjsDatabaseKey.id); const workspaceId = useId()?.workspaceId; - const rowIds = useMemo(() => (cell.data.toJSON() as RelationCellData) ?? [], [cell.data]); + const rowIds = useMemo(() => { + return (cell.data?.toJSON() as RelationCellData) ?? []; + }, [cell.data]); const databaseId = rowIds.length > 0 && field ? parseRelationTypeOption(field).database_id : undefined; const databaseService = useContext(AFConfigContext)?.service?.databaseService; const [databasePrimaryFieldId, setDatabasePrimaryFieldId] = useState(undefined); - const [rows, setRows] = useState | null>(); + const [rows, setRows] = useState(); + + const navigateToRow = useNavigateToRow(); useEffect(() => { - if (!workspaceId || !databaseId) return; + if (!workspaceId || !databaseId || !rowIds.length) return; void databaseService?.getDatabase(workspaceId, databaseId, rowIds).then(({ databaseDoc: doc, rows }) => { const fields = doc .getMap(YjsEditorKey.data_section) @@ -34,13 +44,28 @@ function RelationItems({ style, cell, fieldId }: { cell: RelationCell; fieldId: }); }, [workspaceId, databaseId, databaseService, rowIds]); + useEffect(() => { + return () => { + if (currentDatabaseId !== databaseId && databaseId) { + void databaseService?.closeDatabase(databaseId); + } + }; + }, [currentDatabaseId, databaseId, databaseService]); + return (
{rowIds.map((rowId) => { const rowDoc = rows?.get(rowId); return ( -
+
{ + e.stopPropagation(); + navigateToRow?.(rowId); + }} + className={'w-full cursor-pointer underline'} + > {rowDoc && databasePrimaryFieldId && ( )} diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx index 174a3693f0..0c33397eb8 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/relation/RelationPrimaryValue.tsx @@ -4,9 +4,24 @@ import React, { useEffect, useState } from 'react'; export function RelationPrimaryValue({ rowDoc, fieldId }: { rowDoc: YDoc; fieldId: FieldId }) { const [text, setText] = useState(null); + const [row, setRow] = useState(null); useEffect(() => { - const row = rowDoc.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; + const data = rowDoc.getMap(YjsEditorKey.data_section); + + const onRowChange = () => { + setRow(data?.get(YjsEditorKey.database_row) as YDatabaseRow); + }; + + onRowChange(); + data?.observe(onRowChange); + return () => { + data?.unobserve(onRowChange); + }; + }, [rowDoc]); + + useEffect(() => { + if (!row) return; const cells = row.get(YjsDatabaseKey.cells); const primaryCell = cells.get(fieldId); @@ -21,7 +36,7 @@ export function RelationPrimaryValue({ rowDoc, fieldId }: { rowDoc: YDoc; fieldI return () => { primaryCell.unobserve(observeHandler); }; - }, [rowDoc, fieldId]); + }, [row, fieldId]); return
{text}
; } diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx index 4d882b8c28..b4d048b14f 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/text/TextCell.tsx @@ -2,10 +2,15 @@ import { useReadOnly } from '@/application/database-yjs'; import { CellProps, TextCell as TextCellType } from '@/components/database/components/cell/cell.type'; import React from 'react'; -export function TextCell({ cell, style }: CellProps) { +export function TextCell({ cell, style, placeholder }: CellProps) { const readOnly = useReadOnly(); - if (!cell?.data) return null; + if (!cell?.data) + return placeholder ? ( +
+ {placeholder} +
+ ) : null; return (
{cell?.data} diff --git a/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx b/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx index 63e3c4cb10..0ee2c1d5bf 100644 --- a/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/cell/url/UrlCell.tsx @@ -30,9 +30,10 @@ export function UrlCell({ cell, style, placeholder }: CellProps) { return (
{ + onClick={(e) => { if (!isUrl || !cell) return; if (readOnly) { + e.stopPropagation(); void openUrl(cell.data, '_blank'); } }} diff --git a/frontend/appflowy_web_app/src/components/database/components/conditions/DatabaseActions.tsx b/frontend/appflowy_web_app/src/components/database/components/conditions/DatabaseActions.tsx index e095a0eacc..a7559374c2 100644 --- a/frontend/appflowy_web_app/src/components/database/components/conditions/DatabaseActions.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/conditions/DatabaseActions.tsx @@ -1,5 +1,4 @@ -import { DatabaseViewLayout, YjsDatabaseKey } from '@/application/collab.type'; -import { useDatabaseView, useFiltersSelector, useSortsSelector } from '@/application/database-yjs'; +import { useFiltersSelector, useSortsSelector } from '@/application/database-yjs'; import { useConditionsContext } from '@/components/database/components/conditions/context'; import { TextButton } from '@/components/database/components/tabs/TextButton'; import React from 'react'; @@ -7,16 +6,11 @@ import { useTranslation } from 'react-i18next'; export function DatabaseActions() { const { t } = useTranslation(); - const view = useDatabaseView(); - const layout = Number(view?.get(YjsDatabaseKey.layout)) as DatabaseViewLayout; + const sorts = useSortsSelector(); const filter = useFiltersSelector(); const conditionsContext = useConditionsContext(); - if (layout === DatabaseViewLayout.Calendar) { - return null; - } - return (
(null); - const [notFound, setNotFound] = useState(false); const documentService = useContext(AFConfigContext)?.service?.documentService; @@ -23,31 +23,30 @@ export function DatabaseRowSubDocument({ rowId }: { rowId: string }) { setDoc(null); const doc = await documentService.openDocument(workspaceId, documentId); + console.log('doc', doc); setDoc(doc); } catch (e) { - Log.error(e); - setNotFound(true); + console.error(e); + // haven't created by client, ignore error and show empty } }, [documentService, workspaceId, documentId]); useEffect(() => { - setNotFound(false); - void handleOpenDocument(); + setLoading(true); + void handleOpenDocument().then(() => setLoading(false)); }, [handleOpenDocument]); - if (notFound || !documentId) { - return ; - } - - if (!doc) { + if (loading) { return ( -
+
); } - return ; + if (!doc) return null; + + return ; } export default DatabaseRowSubDocument; diff --git a/frontend/appflowy_web_app/src/components/database/components/database-row/OpenAction.tsx b/frontend/appflowy_web_app/src/components/database/components/database-row/OpenAction.tsx new file mode 100644 index 0000000000..94b6977bca --- /dev/null +++ b/frontend/appflowy_web_app/src/components/database/components/database-row/OpenAction.tsx @@ -0,0 +1,27 @@ +import { ReactComponent as ExpandMoreIcon } from '$icons/16x/full_view.svg'; +import { useTranslation } from 'react-i18next'; +import { useNavigateToRow } from '@/application/database-yjs'; +import { Tooltip } from '@mui/material'; +import React from 'react'; + +function OpenAction({ rowId }: { rowId: string }) { + const navigateToRow = useNavigateToRow(); + + const { t } = useTranslation(); + + return ( + + + + ); +} + +export default OpenAction; diff --git a/frontend/appflowy_web_app/src/components/database/components/field/CardField.tsx b/frontend/appflowy_web_app/src/components/database/components/field/CardField.tsx index 585a3d2ce0..2575aa47ca 100644 --- a/frontend/appflowy_web_app/src/components/database/components/field/CardField.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/field/CardField.tsx @@ -14,13 +14,11 @@ function CardField({ rowId, fieldId, index }: { rowId: string; fieldId: string; const isPrimary = field?.get(YjsDatabaseKey.is_primary); const style = useMemo(() => { - const styleProperties = { - fontSize: '12px', - }; + const styleProperties = {}; if (isPrimary) { Object.assign(styleProperties, { - fontSize: '14px', + fontSize: '1.25em', fontWeight: 500, }); } diff --git a/frontend/appflowy_web_app/src/components/database/components/grid/grid-column/GridColumn.tsx b/frontend/appflowy_web_app/src/components/database/components/grid/grid-column/GridColumn.tsx index 88c6fae84e..56845e8425 100644 --- a/frontend/appflowy_web_app/src/components/database/components/grid/grid-column/GridColumn.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/grid/grid-column/GridColumn.tsx @@ -2,6 +2,7 @@ import { YjsDatabaseKey } from '@/application/collab.type'; import { FieldType } from '@/application/database-yjs/database.type'; import { Column, useFieldSelector } from '@/application/database-yjs/selector'; import { FieldTypeIcon } from '@/components/database/components/field'; +import { Tooltip } from '@mui/material'; import React, { useMemo } from 'react'; export function GridColumn({ column, index }: { column: Column; index: number }) { @@ -16,19 +17,21 @@ export function GridColumn({ column, index }: { column: Column; index: number }) }, [field]); return ( -
-
- + +
+
+ +
+
{name}
-
{name}
-
+ ); } diff --git a/frontend/appflowy_web_app/src/components/database/components/grid/grid-table/GridTable.tsx b/frontend/appflowy_web_app/src/components/database/components/grid/grid-table/GridTable.tsx index ee39bfb957..34f5a97b75 100644 --- a/frontend/appflowy_web_app/src/components/database/components/grid/grid-table/GridTable.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/grid/grid-table/GridTable.tsx @@ -22,7 +22,6 @@ export const GridTable = ({ scrollLeft, columnWidth, columns, onScrollLeft }: Gr useEffect(() => { if (ref.current) { - console.log(ref.current, scrollLeft); ref.current.scrollTo({ scrollLeft }); } }, [scrollLeft]); diff --git a/frontend/appflowy_web_app/src/components/database/components/header/DatabaseRowHeader.tsx b/frontend/appflowy_web_app/src/components/database/components/header/DatabaseRowHeader.tsx index cbbfcaee49..330e5ba1cf 100644 --- a/frontend/appflowy_web_app/src/components/database/components/header/DatabaseRowHeader.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/header/DatabaseRowHeader.tsx @@ -1,15 +1,35 @@ -import { useCellSelector, usePrimaryFieldId, useRowMetaSelector } from '@/application/database-yjs'; +import { useCellSelector, useDatabaseViewId, usePrimaryFieldId, useRowMetaSelector } from '@/application/database-yjs'; +import { FolderContext } from '@/application/folder-yjs'; import Title from '@/components/database/components/header/Title'; -import React from 'react'; +import React, { useContext, useEffect } from 'react'; function DatabaseRowHeader({ rowId }: { rowId: string }) { const fieldId = usePrimaryFieldId() || ''; + const setCrumbs = useContext(FolderContext)?.setCrumbs; + const viewId = useDatabaseViewId(); + const meta = useRowMetaSelector(rowId); const cell = useCellSelector({ rowId, fieldId, }); + useEffect(() => { + if (!viewId) return; + setCrumbs?.((prev) => { + const lastCrumb = prev[prev.length - 1]; + const crumb = { + viewId, + rowId, + name: cell?.data as string, + icon: meta?.icon || '', + }; + + if (lastCrumb?.rowId === rowId) return [...prev.slice(0, -1), crumb]; + return [...prev, crumb]; + }); + }, [cell, meta, rowId, setCrumbs, viewId]); + return ; } diff --git a/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx b/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx index f194a65a72..b1e4662b2d 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/Property.tsx @@ -1,12 +1,13 @@ import { YjsDatabaseKey } from '@/application/collab.type'; import { FieldType, useCellSelector, useFieldSelector } from '@/application/database-yjs'; -import { Cell as CellType, CellProps, TextCell } from '@/components/database/components/cell/cell.type'; +import { Cell as CellType, CellProps } from '@/components/database/components/cell/cell.type'; import { CheckboxCell } from '@/components/database/components/cell/checkbox'; import { RowCreateModifiedTime } from '@/components/database/components/cell/created-modified'; import { DateTimeCell } from '@/components/database/components/cell/date'; import { NumberCell } from '@/components/database/components/cell/number'; import { RelationCell } from '@/components/database/components/cell/relation'; import { SelectOptionCell } from '@/components/database/components/cell/select-option'; +import { TextCell } from '@/components/database/components/cell/text'; import { UrlCell } from '@/components/database/components/cell/url'; import PropertyWrapper from '@/components/database/components/property/PropertyWrapper'; import { TextProperty } from '@/components/database/components/property/text'; @@ -42,6 +43,8 @@ export function Property({ fieldId, rowId }: { fieldId: string; rowId: string }) return ChecklistProperty; case FieldType.Relation: return RelationCell; + case FieldType.RichText: + return TextCell; default: return TextProperty; } @@ -54,10 +57,6 @@ export function Property({ fieldId, rowId }: { fieldId: string; rowId: string }) [] ); - if (fieldType === FieldType.RichText) { - return <TextProperty cell={cell as TextCell} fieldId={fieldId} rowId={rowId} />; - } - if (fieldType === FieldType.CreatedTime || fieldType === FieldType.LastEditedTime) { const attrName = fieldType === FieldType.CreatedTime ? YjsDatabaseKey.created_at : YjsDatabaseKey.last_modified; diff --git a/frontend/appflowy_web_app/src/components/database/components/property/PropertyWrapper.tsx b/frontend/appflowy_web_app/src/components/database/components/property/PropertyWrapper.tsx index 9e970abaee..c930365b37 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/PropertyWrapper.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/PropertyWrapper.tsx @@ -7,7 +7,7 @@ function PropertyWrapper({ fieldId, children }: { fieldId: string; children: Rea <div className={'property-label flex h-[28px] w-[30%] items-center'}> <FieldDisplay fieldId={fieldId} /> </div> - <div className={'flex flex-1 flex-wrap pr-1'}>{children}</div> + <div className={'flex flex-1 flex-wrap items-center overflow-x-hidden pr-1'}>{children}</div> </div> ); } diff --git a/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx b/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx index 6e47ada5fe..fabe862659 100644 --- a/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/property/cheklist/ChecklistProperty.tsx @@ -15,7 +15,7 @@ export function ChecklistProperty(props: CellProps<CellType>) { const selectedOptions = data?.selectedOptionIds; return ( - <div className={'flex w-full flex-col gap-2'}> + <div className={'flex w-full flex-col gap-2 py-2'}> <ChecklistCell {...props} /> {options?.map((option) => { const isSelected = selectedOptions?.includes(option.id); diff --git a/frontend/appflowy_web_app/src/components/database/components/tabs/DatabaseTabs.tsx b/frontend/appflowy_web_app/src/components/database/components/tabs/DatabaseTabs.tsx index 8e33c7eaba..84e2a355ef 100644 --- a/frontend/appflowy_web_app/src/components/database/components/tabs/DatabaseTabs.tsx +++ b/frontend/appflowy_web_app/src/components/database/components/tabs/DatabaseTabs.tsx @@ -1,7 +1,9 @@ -import { ViewLayout, YjsFolderKey, YView } from '@/application/collab.type'; +import { DatabaseViewLayout, ViewLayout, YjsDatabaseKey, YjsFolderKey, YView } from '@/application/collab.type'; +import { useDatabaseView } from '@/application/database-yjs'; import { useFolderContext } from '@/application/folder-yjs'; import { useId } from '@/components/_shared/context-provider/IdProvider'; import { DatabaseActions } from '@/components/database/components/conditions'; +import { Tooltip } from '@mui/material'; import { forwardRef, FunctionComponent, SVGProps, useCallback, useEffect, useMemo } from 'react'; import { ViewTabs, ViewTab } from './ViewTabs'; import { useTranslation } from 'react-i18next'; @@ -31,6 +33,9 @@ export const DatabaseTabs = forwardRef<HTMLDivElement, DatabaseTabBarProps>( const objectId = useId().objectId; const { t } = useTranslation(); const folder = useFolderContext(); + const view = useDatabaseView(); + const layout = Number(view?.get(YjsDatabaseKey.layout)) as DatabaseViewLayout; + const handleChange = (_: React.SyntheticEvent, newValue: string) => { setSelectedViewId?.(newValue); }; @@ -50,12 +55,21 @@ export const DatabaseTabs = forwardRef<HTMLDivElement, DatabaseTabBarProps>( [folder] ); + const className = useMemo(() => { + const classList = [ + 'mx-16 -mb-[0.5px] flex items-center overflow-hidden border-line-divider text-text-title max-md:mx-4', + ]; + + if (layout === DatabaseViewLayout.Calendar) { + classList.push('border-b'); + } + + return classList.join(' '); + }, [layout]); + if (viewIds.length === 0) return null; return ( - <div - ref={ref} - className='mx-16 -mb-[0.5px] flex items-center overflow-hidden border-b border-line-divider text-text-title max-md:mx-4' - > + <div ref={ref} className={className}> <div style={{ width: 'calc(100% - 120px)', @@ -83,14 +97,18 @@ export const DatabaseTabs = forwardRef<HTMLDivElement, DatabaseTabBarProps>( icon={<Icon className={'h-4 w-4'} />} iconPosition='start' color='inherit' - label={<span className={'max-w-[120px] truncate'}>{name || t('grid.title.placeholder')}</span>} + label={ + <Tooltip title={name} placement={'right'}> + <span className={'max-w-[120px] truncate'}>{name || t('grid.title.placeholder')}</span> + </Tooltip> + } value={viewId} /> ); })} </ViewTabs> </div> - <DatabaseActions /> + {layout !== DatabaseViewLayout.Calendar ? <DatabaseActions /> : null} </div> ); } diff --git a/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx b/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx index 954f9cd7c3..ebc7d866ea 100644 --- a/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx +++ b/frontend/appflowy_web_app/src/components/database/grid/Grid.tsx @@ -1,7 +1,7 @@ import { RowsContext, useDatabase, useRowOrdersSelector, useViewId } from '@/application/database-yjs'; import { useRenderFields, GridHeader, GridTable } from '@/components/database/components/grid'; import { CircularProgress } from '@mui/material'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; export function Grid() { const database = useDatabase(); @@ -11,6 +11,10 @@ export function Grid() { const { fields, columnWidth } = useRenderFields(); const rowOrders = useRowOrdersSelector(); + useEffect(() => { + setScrollLeft(0); + }, [viewId]); + if (!database || !rowOrders) { return ( <div className={'flex w-full flex-1 flex-col items-center justify-center'}> diff --git a/frontend/appflowy_web_app/src/components/document/Document.tsx b/frontend/appflowy_web_app/src/components/document/Document.tsx index 219b76e7e5..313ccb1480 100644 --- a/frontend/appflowy_web_app/src/components/document/Document.tsx +++ b/frontend/appflowy_web_app/src/components/document/Document.tsx @@ -1,17 +1,29 @@ import { YDoc } from '@/application/collab.type'; import { useId } from '@/components/_shared/context-provider/IdProvider'; +import { usePageInfo } from '@/components/_shared/page/usePageInfo'; +import ComponentLoading from '@/components/_shared/progress/ComponentLoading'; import { AFConfigContext } from '@/components/app/AppConfig'; import { DocumentHeader } from '@/components/document/document_header'; import { Editor } from '@/components/editor'; +import { EditorLayoutStyle } from '@/components/editor/EditorContext'; import { Log } from '@/utils/log'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import CircularProgress from '@mui/material/CircularProgress'; +import React, { Suspense, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import RecordNotFound from 'src/components/_shared/not-found/RecordNotFound'; export const Document = () => { const { objectId: documentId, workspaceId } = useId() || {}; const [doc, setDoc] = useState<YDoc | null>(null); const [notFound, setNotFound] = useState<boolean>(false); + const extra = usePageInfo(documentId).extra; + const layoutStyle: EditorLayoutStyle = useMemo(() => { + return { + font: extra?.font || '', + fontLayout: extra?.fontLayout, + lineHeightLayout: extra?.lineHeightLayout, + }; + }, [extra]); const documentService = useContext(AFConfigContext)?.service?.documentService; const handleOpenDocument = useCallback(async () => { @@ -32,19 +44,65 @@ export const Document = () => { void handleOpenDocument(); }, [handleOpenDocument]); + const style = useMemo(() => { + const fontSizeMap = { + small: '14px', + normal: '16px', + large: '20px', + }; + + return { + fontFamily: layoutStyle.font, + fontSize: fontSizeMap[layoutStyle.fontLayout], + }; + }, [layoutStyle]); + + const layoutClassName = useMemo(() => { + const classList = []; + + if (layoutStyle.fontLayout === 'large') { + classList.push('font-large'); + } else if (layoutStyle.fontLayout === 'small') { + classList.push('font-small'); + } + + if (layoutStyle.lineHeightLayout === 'large') { + classList.push('line-height-large'); + } else if (layoutStyle.lineHeightLayout === 'small') { + classList.push('line-height-small'); + } + + return classList.join(' '); + }, [layoutStyle]); + + useEffect(() => { + if (!layoutStyle.font) return; + void window.WebFont?.load({ + google: { + families: [layoutStyle.font], + }, + }); + }, [layoutStyle.font]); + if (!documentId) return null; return ( <> - {doc && ( - <div className={'relative w-full'}> + {doc ? ( + <div style={style} className={`relative w-full ${layoutClassName}`}> <DocumentHeader doc={doc} viewId={documentId} /> <div className={'flex w-full justify-center'}> - <div className={'max-w-screen w-[964px] min-w-0'}> - <Editor doc={doc} readOnly={true} includeRoot={true} /> - </div> + <Suspense fallback={<ComponentLoading />}> + <div className={'max-w-screen w-[964px] min-w-0'}> + <Editor doc={doc} readOnly={true} layoutStyle={layoutStyle} /> + </div> + </Suspense> </div> </div> + ) : ( + <div className={'flex h-full w-full items-center justify-center'}> + <CircularProgress /> + </div> )} <RecordNotFound open={notFound} workspaceId={workspaceId} /> diff --git a/frontend/appflowy_web_app/src/components/document/document_header/DocumentCover.tsx b/frontend/appflowy_web_app/src/components/document/document_header/DocumentCover.tsx index 08ee25ef87..53d0315271 100644 --- a/frontend/appflowy_web_app/src/components/document/document_header/DocumentCover.tsx +++ b/frontend/appflowy_web_app/src/components/document/document_header/DocumentCover.tsx @@ -1,11 +1,20 @@ -import { CoverType, YDoc } from '@/application/collab.type'; +import { DocCoverType, YDoc } from '@/application/collab.type'; +import { CoverType } from '@/application/folder-yjs/folder.type'; +import { useId } from '@/components/_shared/context-provider/IdProvider'; +import { usePageInfo } from '@/components/_shared/page/usePageInfo'; import { useBlockCover } from '@/components/document/document_header/useBlockCover'; +import { showColorsForImage } from '@/components/document/document_header/utils'; import { renderColor } from '@/utils/color'; import React, { useCallback } from 'react'; import DefaultImage from './default_cover.jpg'; -function DocumentCover({ doc }: { doc: YDoc }) { +function DocumentCover({ doc, onTextColor }: { doc: YDoc; onTextColor: (color: string) => void }) { + const viewId = useId().objectId; + const { extra } = usePageInfo(viewId); + + const pageCover = extra.cover; const { cover } = useBlockCover(doc); + const renderCoverColor = useCallback((color: string) => { return ( <div @@ -17,21 +26,47 @@ function DocumentCover({ doc }: { doc: YDoc }) { ); }, []); - const renderCoverImage = useCallback((url: string) => { - return <img draggable={false} src={url} alt={''} className={'h-full w-full object-cover'} />; - }, []); + const renderCoverImage = useCallback( + (url: string) => { + return ( + <img + onLoad={(e) => { + void showColorsForImage(e.currentTarget).then((res) => { + onTextColor(res); + }); + }} + draggable={false} + src={url} + alt={''} + className={'h-full w-full object-cover'} + /> + ); + }, + [onTextColor] + ); - const { cover_selection_type: type, cover_selection: value = '' } = cover || {}; - - return value ? ( - <div className={`relative mb-[-80px] flex h-[255px] w-full`}> - <> - {type === CoverType.Asset ? renderCoverImage(DefaultImage) : null} - {type === CoverType.Color ? renderCoverColor(value) : null} - {type === CoverType.Image ? renderCoverImage(value) : null} - </> + if (!pageCover && !cover?.cover_selection) return null; + return ( + <div className={`relative flex h-[255px] w-full max-sm:h-[180px]`}> + {pageCover ? ( + <> + {[CoverType.NormalColor, CoverType.GradientColor].includes(pageCover.type) + ? renderCoverColor(pageCover.value) + : null} + {CoverType.BuildInImage === pageCover.type ? renderCoverImage(DefaultImage) : null} + {[CoverType.CustomImage, CoverType.UpsplashImage].includes(pageCover.type) + ? renderCoverImage(pageCover.value) + : null} + </> + ) : cover?.cover_selection ? ( + <> + {cover.cover_selection_type === DocCoverType.Asset ? renderCoverImage(DefaultImage) : null} + {cover.cover_selection_type === DocCoverType.Color ? renderCoverColor(cover.cover_selection) : null} + {cover.cover_selection_type === DocCoverType.Image ? renderCoverImage(cover.cover_selection) : null} + </> + ) : null} </div> - ) : null; + ); } export default DocumentCover; diff --git a/frontend/appflowy_web_app/src/components/document/document_header/DocumentHeader.tsx b/frontend/appflowy_web_app/src/components/document/document_header/DocumentHeader.tsx index 0610b8a834..3e7fd5ee28 100644 --- a/frontend/appflowy_web_app/src/components/document/document_header/DocumentHeader.tsx +++ b/frontend/appflowy_web_app/src/components/document/document_header/DocumentHeader.tsx @@ -1,12 +1,12 @@ import { YDoc, YjsFolderKey } from '@/application/collab.type'; import { useViewSelector } from '@/application/folder-yjs'; import DocumentCover from '@/components/document/document_header/DocumentCover'; -import React, { memo, useMemo, useRef } from 'react'; +import React, { memo, useMemo, useRef, useState } from 'react'; export function DocumentHeader({ viewId, doc }: { viewId: string; doc: YDoc }) { const ref = useRef<HTMLDivElement>(null); const { view } = useViewSelector(viewId); - + const [textColor, setTextColor] = useState<string>('var(--text-title)'); const icon = view?.get(YjsFolderKey.icon); const iconObject = useMemo(() => { try { @@ -17,21 +17,30 @@ export function DocumentHeader({ viewId, doc }: { viewId: string; doc: YDoc }) { }, [icon]); return ( - <div ref={ref} className={'document-header select-none'}> - <div className={'flex flex-col justify-end'}> - <div className={'view-banner flex w-full flex-col overflow-hidden'}> - <DocumentCover doc={doc} /> + <div ref={ref} className={'document-header mb-[10px] select-none'}> + <div className={'view-banner relative flex w-full flex-col overflow-hidden'}> + <DocumentCover onTextColor={setTextColor} doc={doc} /> - <div className={`relative min-h-[65px] w-[964px] min-w-0 max-w-full px-16 pt-10 max-md:px-4`}> - <div - style={{ - position: 'relative', - bottom: '50%', - }} - > - <div className={`view-icon`}>{iconObject?.value}</div> + <div className={`relative mx-16 w-[964px] min-w-0 max-w-full overflow-visible max-md:mx-4`}> + <div + style={{ + position: 'absolute', + bottom: '100%', + width: '100%', + }} + className={'flex items-center gap-2 px-14 pb-10 text-4xl max-md:px-2 max-md:pb-6 max-sm:text-[7vw]'} + > + <div className={`view-icon`}>{iconObject?.value}</div> + <div className={'flex flex-1 items-center gap-2 overflow-hidden'}> + <div + style={{ + color: textColor, + }} + className={'font-bold leading-[1.5em]'} + > + {view?.get(YjsFolderKey.name)} + </div> </div> - <div className={'py-2'}></div> </div> </div> </div> diff --git a/frontend/appflowy_web_app/src/components/document/document_header/useBlockCover.ts b/frontend/appflowy_web_app/src/components/document/document_header/useBlockCover.ts index 589a1e4169..ba6226a6e8 100644 --- a/frontend/appflowy_web_app/src/components/document/document_header/useBlockCover.ts +++ b/frontend/appflowy_web_app/src/components/document/document_header/useBlockCover.ts @@ -1,4 +1,4 @@ -import { PageCover, YBlocks, YDoc, YDocument, YjsEditorKey } from '@/application/collab.type'; +import { DocCover, YBlocks, YDoc, YDocument, YjsEditorKey } from '@/application/collab.type'; import { useEffect, useMemo, useState } from 'react'; export function useBlockCover(doc: YDoc) { @@ -22,7 +22,7 @@ export function useBlockCover(doc: YDoc) { }; }, [doc]); - const coverObj: PageCover = useMemo(() => { + const coverObj: DocCover = useMemo(() => { try { return JSON.parse(cover || ''); } catch (e) { diff --git a/frontend/appflowy_web_app/src/components/document/document_header/utils.ts b/frontend/appflowy_web_app/src/components/document/document_header/utils.ts new file mode 100644 index 0000000000..fe2c0acbe0 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/document/document_header/utils.ts @@ -0,0 +1,28 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +import ColorThief from 'colorthief'; + +const colorThief = new ColorThief(); + +export function calculateTextColor(rgb: [number, number, number]): string { + const [r, g, b] = rgb; + const brightness = (r * 299 + g * 587 + b * 114) / 1000; + + return brightness > 125 ? 'black' : 'white'; +} + +export async function showColorsForImage(image: HTMLImageElement) { + const img = new Image(); + + img.crossOrigin = 'Anonymous'; // Handle CORS + img.src = image.src; + + await new Promise((resolve, reject) => { + img.onload = resolve; + img.onerror = reject; + }); + + const dominantColor = colorThief.getColor(img); + + return calculateTextColor(dominantColor); +} diff --git a/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx b/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx index 7e5ebbc28c..8820296780 100644 --- a/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx +++ b/frontend/appflowy_web_app/src/components/editor/CollaborativeEditor.tsx @@ -1,8 +1,5 @@ -import { CollabOrigin, YjsFolderKey } from '@/application/collab.type'; -import { useViewSelector } from '@/application/folder-yjs'; +import { CollabOrigin } from '@/application/collab.type'; import { withYjs, YjsEditor } from '@/application/slate-yjs/plugins/withYjs'; -import { useId } from '@/components/_shared/context-provider/IdProvider'; -import { CustomEditor } from '@/components/editor/command'; import EditorEditable from '@/components/editor/Editable'; import { useEditorContext } from '@/components/editor/EditorContext'; import { withPlugins } from '@/components/editor/plugins'; @@ -13,10 +10,7 @@ import * as Y from 'yjs'; const defaultInitialValue: Descendant[] = []; -function CollaborativeEditor({ doc, includeRoot = true }: { doc: Y.Doc; includeRoot?: boolean }) { - const viewId = useId()?.objectId || ''; - const { view } = useViewSelector(viewId); - const title = includeRoot ? view?.get(YjsFolderKey.name) : undefined; +function CollaborativeEditor({ doc }: { doc: Y.Doc }) { const context = useEditorContext(); // if readOnly, collabOrigin is Local, otherwise RemoteSync const localOrigin = context.readOnly ? CollabOrigin.Local : CollabOrigin.LocalSync; @@ -27,13 +21,12 @@ function CollaborativeEditor({ doc, includeRoot = true }: { doc: Y.Doc; includeR withReact( withYjs(createEditor(), doc, { localOrigin, - includeRoot, }) ) ) as YjsEditor), - [doc, localOrigin, includeRoot] + [doc, localOrigin] ); - const [connected, setIsConnected] = useState(false); + const [, setIsConnected] = useState(false); useEffect(() => { if (!editor) return; @@ -45,11 +38,6 @@ function CollaborativeEditor({ doc, includeRoot = true }: { doc: Y.Doc; includeR }; }, [editor]); - useEffect(() => { - if (!editor || !connected || title === undefined) return; - CustomEditor.setDocumentTitle(editor, title); - }, [editor, title, connected]); - return ( <Slate editor={editor} initialValue={defaultInitialValue}> <EditorEditable editor={editor} /> diff --git a/frontend/appflowy_web_app/src/components/editor/Editor.tsx b/frontend/appflowy_web_app/src/components/editor/Editor.tsx index 601b5d44b2..bbb30d2dc0 100644 --- a/frontend/appflowy_web_app/src/components/editor/Editor.tsx +++ b/frontend/appflowy_web_app/src/components/editor/Editor.tsx @@ -1,21 +1,19 @@ import { YDoc } from '@/application/collab.type'; import CollaborativeEditor from '@/components/editor/CollaborativeEditor'; -import { EditorContextProvider } from '@/components/editor/EditorContext'; +import { defaultLayoutStyle, EditorContextProvider, EditorLayoutStyle } from '@/components/editor/EditorContext'; import React from 'react'; import './editor.scss'; -export const Editor = ({ - readOnly, - doc, - includeRoot = true, -}: { +export interface EditorProps { readOnly: boolean; doc: YDoc; - includeRoot?: boolean; -}) => { + layoutStyle?: EditorLayoutStyle; +} + +export const Editor = ({ readOnly, doc, layoutStyle = defaultLayoutStyle }: EditorProps) => { return ( - <EditorContextProvider readOnly={readOnly}> - <CollaborativeEditor doc={doc} includeRoot={includeRoot} /> + <EditorContextProvider layoutStyle={layoutStyle} readOnly={readOnly}> + <CollaborativeEditor doc={doc} /> </EditorContextProvider> ); }; diff --git a/frontend/appflowy_web_app/src/components/editor/EditorContext.tsx b/frontend/appflowy_web_app/src/components/editor/EditorContext.tsx index 7b4162891e..c360c969dd 100644 --- a/frontend/appflowy_web_app/src/components/editor/EditorContext.tsx +++ b/frontend/appflowy_web_app/src/components/editor/EditorContext.tsx @@ -1,11 +1,26 @@ +import { FontLayout, LineHeightLayout } from '@/application/collab.type'; import { createContext, useContext } from 'react'; +export interface EditorLayoutStyle { + fontLayout: FontLayout; + font: string; + lineHeightLayout: LineHeightLayout; +} + +export const defaultLayoutStyle: EditorLayoutStyle = { + fontLayout: FontLayout.normal, + font: '', + lineHeightLayout: LineHeightLayout.normal, +}; + interface EditorContextState { readOnly: boolean; + layoutStyle: EditorLayoutStyle; } export const EditorContext = createContext<EditorContextState>({ readOnly: true, + layoutStyle: defaultLayoutStyle, }); export const EditorContextProvider = ({ children, ...props }: EditorContextState & { children: React.ReactNode }) => { diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/BoardBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/BoardBlock.tsx deleted file mode 100644 index 88e3790551..0000000000 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/BoardBlock.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function BoardBlock() { - return <div></div>; -} - -export default BoardBlock; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/CalendarBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/CalendarBlock.tsx deleted file mode 100644 index 19c2b32bf0..0000000000 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/CalendarBlock.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function CalendarBlock() { - return <div></div>; -} - -export default CalendarBlock; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx index b9bc174969..5e9fc6a37d 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx @@ -1,7 +1,10 @@ +import { ReactComponent as ExpandMoreIcon } from '$icons/16x/full_view.svg'; +import { useNavigateToView } from '@/application/folder-yjs'; import { IdProvider, useId } from '@/components/_shared/context-provider/IdProvider'; import { Database } from '@/components/database'; import { DatabaseNode, EditorElementProps } from '@/components/editor/editor.type'; -import React, { forwardRef, memo, useMemo } from 'react'; +import { Tooltip } from '@mui/material'; +import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { BlockType } from '@/application/collab.type'; @@ -11,6 +14,8 @@ export const DatabaseBlock = memo( const viewId = node.data.view_id; const workspaceId = useId()?.workspaceId; const type = node.type; + const navigateToView = useNavigateToView(); + const [isHovering, setIsHovering] = useState(false); const style = useMemo(() => { const style = {}; @@ -31,16 +36,45 @@ export const DatabaseBlock = memo( return style; }, [type]); + const handleNavigateToRow = useCallback( + (viewId: string, rowId: string) => { + const url = `/view/${workspaceId}/${viewId}?r=${rowId}`; + + window.open(url, '_blank'); + }, + [workspaceId] + ); + return ( <> - <div {...attributes} className={`relative w-full cursor-pointer py-2`}> + <div + {...attributes} + className={`relative w-full cursor-pointer py-2`} + onMouseEnter={() => setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + > <div ref={ref} className={'absolute left-0 top-0 h-full w-full caret-transparent'}> {children} </div> - <div contentEditable={false} style={style} className={`container-bg flex w-full flex-col px-3`}> + <div contentEditable={false} style={style} className={`container-bg relative flex w-full flex-col px-3`}> {viewId ? ( <IdProvider workspaceId={workspaceId} objectId={viewId}> - <Database /> + <Database onNavigateToRow={handleNavigateToRow} /> + {isHovering && ( + <div className={'absolute right-4 top-1'}> + <Tooltip placement={'bottom'} title={t('tooltip.openAsPage')}> + <button + color={'primary'} + className={'rounded border border-line-divider bg-bg-body p-1 hover:bg-fill-list-hover'} + onClick={() => { + navigateToView?.(viewId); + }} + > + <ExpandMoreIcon /> + </button> + </Tooltip> + </div> + )} </IdProvider> ) : ( <div diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/GridBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/GridBlock.tsx deleted file mode 100644 index eaf2742ceb..0000000000 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/GridBlock.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -function GridBlock() { - return <div></div>; -} - -export default GridBlock; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/heading/utils.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/heading/utils.ts index bab542fc84..22fe53980a 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/heading/utils.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/heading/utils.ts @@ -1,17 +1,17 @@ export function getHeadingCssProperty(level: number) { switch (level) { case 1: - return 'text-3xl pt-[10px] pb-[8px] font-bold'; + return 'text-3xl pt-[10px] max-md:pt-[1.5vw] pb-[4px] max-md:pb-[1vw] font-bold max-sm:text-[6vw]'; case 2: - return 'text-2xl pt-[8px] pb-[6px] font-bold'; + return 'text-2xl pt-[8px] max-md:pt-[1vw] pb-[2px] max-md:pb-[0.5vw] font-bold max-sm:text-[5vw]'; case 3: - return 'text-xl pt-[4px] font-bold'; + return 'text-xl pt-[4px] font-bold max-sm:text-[4vw]'; case 4: return 'text-lg pt-[4px] font-bold'; case 5: - return 'text-base pt-[4px] font-bold'; + return 'pt-[4px] font-bold'; case 6: - return 'text-sm pt-[4px] font-bold'; + return 'pt-[4px] font-bold'; default: return ''; } diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/index.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/index.ts index 27b52f50f2..d10b172020 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/index.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/index.ts @@ -1,5 +1,3 @@ import { lazy } from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error -export const MathEquation = lazy(() => import('./MathEquation?chunkName=formula')); +export const MathEquation = lazy(() => import('./MathEquation')); diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Placeholder.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Placeholder.tsx index 44c1391e13..08a66de9d2 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Placeholder.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Placeholder.tsx @@ -10,6 +10,7 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin const { t } = useTranslation(); const { readOnly } = useEditorContext(); const editor = useSlate(); + const selected = useSelected() && !readOnly && !!editor.selection && Range.isCollapsed(editor.selection); const [isComposing, setIsComposing] = useState(false); const block = useMemo(() => { @@ -33,7 +34,7 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin const unSelectedPlaceholder = useMemo(() => { switch (block?.type) { case BlockType.Paragraph: { - if (editor.children.length === 1) { + if (editor.children.length === 1 && !readOnly) { return t('editor.slashPlaceHolder'); } @@ -73,7 +74,7 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin default: return ''; } - }, [block, t, editor.children.length]); + }, [readOnly, block, t, editor.children.length]); const selectedPlaceholder = useMemo(() => { switch (block?.type) { @@ -122,7 +123,7 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin return ( <span - data-placeholder={selected ? selectedPlaceholder : unSelectedPlaceholder} + data-placeholder={selected && !readOnly ? selectedPlaceholder : unSelectedPlaceholder} contentEditable={false} {...attributes} className={className} diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx index 854789969c..cfe167b6e2 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx @@ -11,13 +11,13 @@ export const Text = memo( const { hasStartIcon, renderIcon } = useStartIcon(node); const editor = useSlateStatic(); const isEmpty = editor.isEmpty(node); - const className = useMemo( - () => - `text-element relative my-1 flex w-full whitespace-pre-wrap break-all px-1 ${classNameProp ?? ''} ${ - hasStartIcon ? 'has-start-icon' : '' - }`, - [classNameProp, hasStartIcon] - ); + const className = useMemo(() => { + const classList = ['text-element', 'relative', 'flex', 'w-full', 'whitespace-pre-wrap', 'break-all', 'px-1']; + + if (classNameProp) classList.push(classNameProp); + if (hasStartIcon) classList.push('has-start-icon'); + return classList.join(' '); + }, [classNameProp, hasStartIcon]); return ( <span {...attributes} ref={ref} className={className}> diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/TodoList.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/TodoList.tsx index ab3a437b78..f0356d4cbf 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/TodoList.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/TodoList.tsx @@ -7,7 +7,7 @@ export const TodoList = memo( const className = useMemo(() => { return `flex w-full flex-col ${checked ? 'checked' : ''} ${attributes.className ?? ''}`; }, [attributes.className, checked]); - + return ( <div {...attributes} ref={ref} className={className}> {children} diff --git a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx index 46a784dd37..93712b01ef 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx @@ -13,6 +13,9 @@ import { Paragraph } from '@/components/editor/components/blocks/paragraph'; import { Quote } from '@/components/editor/components/blocks/quote'; import { TableBlock, TableCellBlock } from '@/components/editor/components/blocks/table'; import { Text } from '@/components/editor/components/blocks/text'; +import { ElementFallbackRender } from '@/components/error/ElementFallbackRender'; +import { Skeleton } from '@mui/material'; +import { ErrorBoundary } from 'react-error-boundary'; import { TodoList } from 'src/components/editor/components/blocks/todo-list'; import { ToggleList } from 'src/components/editor/components/blocks/toggle-list'; import { UnSupportedBlock } from '@/components/editor/components/element/UnSupportedBlock'; @@ -20,7 +23,7 @@ import { Formula } from '@/components/editor/components/leaf/formula'; import { Mention } from '@/components/editor/components/leaf/mention'; import { EditorElementProps, TextNode } from '@/components/editor/editor.type'; import { renderColor } from '@/utils/color'; -import React, { FC, useMemo } from 'react'; +import React, { FC, Suspense, useMemo } from 'react'; import { RenderElementProps } from 'slate-react'; import { DatabaseBlock } from 'src/components/editor/components/blocks/database'; @@ -118,10 +121,14 @@ export const Element = ({ } return ( - <div {...attributes} data-block-type={node.type} className={className}> - <Component style={style} className={`flex w-full flex-col`} node={node}> - {children} - </Component> - </div> + <Suspense fallback={<Skeleton width={'100%'} height={24} />}> + <ErrorBoundary fallbackRender={ElementFallbackRender}> + <div {...attributes} data-block-type={node.type} className={className}> + <Component style={style} className={`flex w-full flex-col`} node={node}> + {children} + </Component> + </div> + </ErrorBoundary> + </Suspense> ); }; diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx index e4df335a3b..40e6d31e23 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx @@ -1,20 +1,15 @@ -import { layoutMap, ViewLayout, YjsFolderKey } from '@/application/collab.type'; -import { useId } from '@/components/_shared/context-provider/IdProvider'; +import { useNavigateToView } from '@/application/folder-yjs'; import { usePageInfo } from '@/components/_shared/page/usePageInfo'; import React from 'react'; -import { useNavigate } from 'react-router-dom'; function MentionPage({ pageId }: { pageId: string }) { - const navigate = useNavigate(); - const { workspaceId } = useId(); - const { view, icon, name } = usePageInfo(pageId); + const onNavigateToView = useNavigateToView(); + const { icon, name } = usePageInfo(pageId); return ( <span onClick={() => { - const layout = parseInt(view?.get(YjsFolderKey.layout) ?? '0') as ViewLayout; - - navigate(`/workspace/${workspaceId}/${layoutMap[layout]}/${pageId}`); + onNavigateToView?.(pageId); }} className={`mention-inline px-1 underline`} contentEditable={false} diff --git a/frontend/appflowy_web_app/src/components/editor/editor.scss b/frontend/appflowy_web_app/src/components/editor/editor.scss index abf09c726e..71832ec330 100644 --- a/frontend/appflowy_web_app/src/components/editor/editor.scss +++ b/frontend/appflowy_web_app/src/components/editor/editor.scss @@ -52,6 +52,7 @@ [role=textbox] { .text-element { + @apply my-1; &::selection { @apply bg-transparent; } @@ -209,11 +210,6 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } -.grid-block .grid-scroll-container::-webkit-scrollbar { - width: 0; - height: 0; -} - .image-render { .image-resizer { @apply absolute w-[10px] top-0 z-10 flex h-full cursor-col-resize items-center justify-end; @@ -269,11 +265,39 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } .mention-content { - @apply ml-5; + @apply ml-6; } } .text-block-icon { @apply flex items-center justify-center; -} \ No newline at end of file +} + + +.font-small { + .text-element { + line-height: 1.7; + } +} + + +.font-large { + .text-element { + line-height: 1.2; + } +} + +.line-height-large { + .text-element { + margin-top: 6px; + margin-bottom: 6px; + } +} + +.line-height-small { + .text-element { + margin-top: 0px; + margin-bottom: 0px; + } +} diff --git a/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx b/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx new file mode 100644 index 0000000000..ddfdac39f1 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx @@ -0,0 +1,11 @@ +import { Alert } from '@mui/material'; +import { FallbackProps } from 'react-error-boundary'; + +export function ElementFallbackRender({ error }: FallbackProps) { + return ( + <Alert severity={'error'} variant={'standard'} className={'my-2'}> + <p>Something went wrong:</p> + <pre>{error.message}</pre> + </Alert> + ); +} diff --git a/frontend/appflowy_web_app/src/components/folder/ViewItem.tsx b/frontend/appflowy_web_app/src/components/folder/ViewItem.tsx index c95cd305f9..49feb382e2 100644 --- a/frontend/appflowy_web_app/src/components/folder/ViewItem.tsx +++ b/frontend/appflowy_web_app/src/components/folder/ViewItem.tsx @@ -1,19 +1,15 @@ -import { layoutMap, ViewLayout, YjsFolderKey } from '@/application/collab.type'; +import { useNavigateToView } from '@/application/folder-yjs'; import React from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; import Page from '@/components/_shared/page/Page'; function ViewItem({ id }: { id: string }) { - const navigate = useNavigate(); - const { pathname } = useLocation(); + const onNavigateToView = useNavigateToView(); return ( <div className={'cursor-pointer border-b border-line-border py-4 px-2'}> <Page - onClick={(view) => { - const layout = parseInt(view?.get(YjsFolderKey.layout) ?? '0') as ViewLayout; - - navigate(`${pathname}/${layoutMap[layout]}/${id}`); + onClick={() => { + onNavigateToView?.(id); }} id={id} /> diff --git a/frontend/appflowy_web_app/src/components/layout/Header.tsx b/frontend/appflowy_web_app/src/components/layout/Header.tsx index 5c874db35c..df87892b42 100644 --- a/frontend/appflowy_web_app/src/components/layout/Header.tsx +++ b/frontend/appflowy_web_app/src/components/layout/Header.tsx @@ -2,10 +2,9 @@ import { downloadPage, openAppFlowySchema, openUrl } from '@/utils/url'; import { Button } from '@mui/material'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useParams } from 'react-router-dom'; -import Page from 'src/components/_shared/page/Page'; import { ReactComponent as Logo } from '@/assets/logo.svg'; import Popover, { PopoverOrigin } from '@mui/material/Popover'; +import Breadcrumb from 'src/components/layout/breadcrumb/Breadcrumb'; const popoverOrigin: { anchorOrigin: PopoverOrigin; @@ -22,14 +21,13 @@ const popoverOrigin: { }; function Header() { - const { objectId } = useParams(); const { t } = useTranslation(); const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null); return ( <div className={'appflowy-top-bar flex h-[64px] p-4'}> - <div className={'flex flex-1 items-center justify-between'}> - <div className={'flex-1'}>{objectId && <Page id={objectId} />}</div> + <div className={'flex w-full items-center justify-between overflow-hidden'}> + <Breadcrumb /> <Button className={'border-line-border'} diff --git a/frontend/appflowy_web_app/src/components/layout/Layout.hooks.ts b/frontend/appflowy_web_app/src/components/layout/Layout.hooks.ts new file mode 100644 index 0000000000..0313015d3f --- /dev/null +++ b/frontend/appflowy_web_app/src/components/layout/Layout.hooks.ts @@ -0,0 +1,96 @@ +import { YFolder, YjsEditorKey, YjsFolderKey } from '@/application/collab.type'; +import { Crumb } from '@/application/folder-yjs'; +import { AFConfigContext } from '@/components/app/AppConfig'; +import { useCallback, useContext, useEffect, useState } from 'react'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; + +export function useLayout() { + const { workspaceId, objectId } = useParams(); + const [search] = useSearchParams(); + const folderService = useContext(AFConfigContext)?.service?.folderService; + const [folder, setFolder] = useState<YFolder | null>(null); + const views = folder?.get(YjsFolderKey.views); + const view = objectId ? views?.get(objectId) : null; + const [crumbs, setCrumbs] = useState<Crumb[]>([]); + + const getFolder = useCallback( + async (workspaceId: string) => { + const folder = (await folderService?.openWorkspace(workspaceId)) + ?.getMap(YjsEditorKey.data_section) + .get(YjsEditorKey.folder); + + if (!folder) return; + + console.log(folder.toJSON()); + setFolder(folder); + }, + [folderService] + ); + + useEffect(() => { + if (!workspaceId) return; + + void getFolder(workspaceId); + }, [getFolder, workspaceId]); + + const navigate = useNavigate(); + + const handleNavigateToView = useCallback( + (viewId: string) => { + const view = folder?.get(YjsFolderKey.views)?.get(viewId); + + if (!view) return; + navigate(`/view/${workspaceId}/${viewId}`); + }, + [folder, navigate, workspaceId] + ); + + const onChangeBreadcrumb = useCallback(() => { + if (!view) return; + const queue = [view]; + let parentId = view.get(YjsFolderKey.bid); + + while (parentId) { + const parent = views?.get(parentId); + + if (!parent) break; + + queue.unshift(parent); + parentId = parent?.get(YjsFolderKey.bid); + } + + setCrumbs( + queue + .map((view) => { + let icon = view.get(YjsFolderKey.icon); + + try { + icon = JSON.parse(icon || '')?.value; + } catch (e) { + // do nothing + } + + return { + viewId: view.get(YjsFolderKey.id), + name: view.get(YjsFolderKey.name), + icon: icon || '', + }; + }) + .slice(1) + ); + }, [view, views]); + + useEffect(() => { + onChangeBreadcrumb(); + + view?.observe(onChangeBreadcrumb); + views?.observe(onChangeBreadcrumb); + + return () => { + view?.unobserve(onChangeBreadcrumb); + views?.unobserve(onChangeBreadcrumb); + }; + }, [search, onChangeBreadcrumb, view, views]); + + return { folder, handleNavigateToView, crumbs, setCrumbs }; +} diff --git a/frontend/appflowy_web_app/src/components/layout/Layout.tsx b/frontend/appflowy_web_app/src/components/layout/Layout.tsx index 46e4619cf8..dc3f075f69 100644 --- a/frontend/appflowy_web_app/src/components/layout/Layout.tsx +++ b/frontend/appflowy_web_app/src/components/layout/Layout.tsx @@ -1,37 +1,23 @@ -import { YFolder, YjsEditorKey } from '@/application/collab.type'; import { FolderProvider } from '@/components/_shared/context-provider/FolderProvider'; -import { AFConfigContext } from '@/components/app/AppConfig'; import Header from '@/components/layout/Header'; import { AFScroller } from '@/components/_shared/scroller'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { useParams } from 'react-router-dom'; +import { useLayout } from '@/components/layout/Layout.hooks'; +import React from 'react'; import './layout.scss'; +import { ReactComponent as Logo } from '@/assets/logo.svg'; function Layout({ children }: { children: React.ReactNode }) { - const { workspaceId } = useParams(); - const folderService = useContext(AFConfigContext)?.service?.folderService; - const [folder, setFolder] = useState<YFolder | null>(null); - const getFolder = useCallback( - async (workspaceId: string) => { - const folder = (await folderService?.openWorkspace(workspaceId)) - ?.getMap(YjsEditorKey.data_section) - .get(YjsEditorKey.folder); + const { folder, handleNavigateToView, crumbs, setCrumbs } = useLayout(); - if (!folder) return; + if (!folder) + return ( + <div className={'flex h-screen w-screen items-center justify-center'}> + <Logo className={'h-20 w-20'} /> + </div> + ); - console.log(folder.toJSON()); - setFolder(folder); - }, - [folderService] - ); - - useEffect(() => { - if (!workspaceId) return; - - void getFolder(workspaceId); - }, [getFolder, workspaceId]); return ( - <FolderProvider folder={folder}> + <FolderProvider setCrumbs={setCrumbs} crumbs={crumbs} onNavigateToView={handleNavigateToView} folder={folder}> <Header /> <AFScroller overflowXHidden diff --git a/frontend/appflowy_web_app/src/components/layout/breadcrumb/Breadcrumb.tsx b/frontend/appflowy_web_app/src/components/layout/breadcrumb/Breadcrumb.tsx new file mode 100644 index 0000000000..b5a56b4527 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/layout/breadcrumb/Breadcrumb.tsx @@ -0,0 +1,24 @@ +import { useCrumbs } from '@/application/folder-yjs'; +import Item from '@/components/layout/breadcrumb/Item'; +import React, { useMemo } from 'react'; + +export function Breadcrumb() { + const crumbs = useCrumbs(); + + const renderCrumb = useMemo(() => { + return crumbs?.map((crumb, index) => { + const isLast = index === crumbs.length - 1; + + return ( + <React.Fragment key={crumb.viewId}> + <Item crumb={crumb} disableClick={isLast} /> + {!isLast && <span>/</span>} + </React.Fragment> + ); + }); + }, [crumbs]); + + return <div className={'flex flex-1 items-center gap-2 overflow-hidden'}>{renderCrumb}</div>; +} + +export default Breadcrumb; diff --git a/frontend/appflowy_web_app/src/components/layout/breadcrumb/Item.tsx b/frontend/appflowy_web_app/src/components/layout/breadcrumb/Item.tsx new file mode 100644 index 0000000000..cf9f697ff6 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/layout/breadcrumb/Item.tsx @@ -0,0 +1,27 @@ +import { Crumb, useNavigateToView } from '@/application/folder-yjs'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +function Item({ crumb, disableClick = false }: { crumb: Crumb; disableClick?: boolean }) { + const { viewId, icon, name } = crumb; + + const { t } = useTranslation(); + const onNavigateToView = useNavigateToView(); + + return ( + <div + className={`flex items-center gap-1 ${!disableClick ? 'cursor-pointer' : 'flex-1 overflow-hidden'}`} + onClick={() => { + if (disableClick) return; + onNavigateToView?.(viewId); + }} + > + {icon} + <span className={!disableClick ? 'underline' : 'flex-1 truncate'}> + {name || t('menuAppHeader.defaultNewPageName')} + </span> + </div> + ); +} + +export default Item; diff --git a/frontend/appflowy_web_app/src/components/layout/breadcrumb/index.ts b/frontend/appflowy_web_app/src/components/layout/breadcrumb/index.ts new file mode 100644 index 0000000000..116446358b --- /dev/null +++ b/frontend/appflowy_web_app/src/components/layout/breadcrumb/index.ts @@ -0,0 +1 @@ +export * from './Breadcrumb'; diff --git a/frontend/appflowy_web_app/src/components/layout/layout.scss b/frontend/appflowy_web_app/src/components/layout/layout.scss index 2aa965dd15..16a9a92a08 100644 --- a/frontend/appflowy_web_app/src/components/layout/layout.scss +++ b/frontend/appflowy_web_app/src/components/layout/layout.scss @@ -16,29 +16,33 @@ box-shadow: var(--line-border) 0px 0px 0px 1px inset !important; } -.appflowy-date-picker-calendar { - width: 100%; - -} - -.grid-sticky-header::-webkit-scrollbar { - width: 0; - height: 0; -} - -.grid-scroll-container::-webkit-scrollbar { - width: 0; - height: 0; -} - - -.appflowy-scroll-container { +@mixin hidden-scrollbar { &::-webkit-scrollbar { - width: 0; - height: 0; + display: none; + } + + -ms-overflow-style: none; + scrollbar-width: none; // For Firefox +} + +body { + &[data-os="windows"]:not([data-browser="firefox"]) { + .appflowy-custom-scroller { + @include hidden-scrollbar; + } + } + + .grid-sticky-header { + @include hidden-scrollbar; } } + +.appflowy-date-picker-calendar { + width: 100%; +} + + .appflowy-scrollbar-thumb-horizontal, .appflowy-scrollbar-thumb-vertical { background-color: var(--scrollbar-thumb); border-radius: 4px; @@ -46,16 +50,8 @@ } -.workspaces, .database-conditions, .grid-scroll-table, .grid-board, .MuiPaper-root, .appflowy-database { - ::-webkit-scrollbar { - width: 0; - height: 0; - } -} - - .view-icon { - @apply flex w-fit cursor-pointer rounded-lg py-2 text-6xl; + @apply flex w-fit leading-[1.5em] cursor-pointer rounded-lg py-2 text-[1.5em]; font-family: "Apple Color Emoji", "Segoe UI Emoji", NotoColorEmoji, "Noto Color Emoji", "Segoe UI Symbol", "Android Emoji", EmojiSymbols; line-height: 1em; white-space: nowrap; diff --git a/frontend/appflowy_web_app/src/pages/LoginPage.tsx b/frontend/appflowy_web_app/src/pages/LoginPage.tsx index 5d2dcceee4..a4ded1d5e3 100644 --- a/frontend/appflowy_web_app/src/pages/LoginPage.tsx +++ b/frontend/appflowy_web_app/src/pages/LoginPage.tsx @@ -13,7 +13,7 @@ function LoginPage() { const workspaceId = currentUser.user?.workspaceId; if (!redirect || redirect === '/') { - return navigate(`/workspace/${workspaceId}`); + return navigate(`/view/${workspaceId}`); } navigate(`${redirect}`); diff --git a/frontend/appflowy_web_app/src/pages/ProductPage.tsx b/frontend/appflowy_web_app/src/pages/ProductPage.tsx index 0cd4d16cac..385e7ea3f5 100644 --- a/frontend/appflowy_web_app/src/pages/ProductPage.tsx +++ b/frontend/appflowy_web_app/src/pages/ProductPage.tsx @@ -1,3 +1,5 @@ +import { ViewLayout } from '@/application/collab.type'; +import { useViewLayout } from '@/application/folder-yjs'; import { IdProvider } from '@/components/_shared/context-provider/IdProvider'; import React, { lazy, useMemo } from 'react'; import { useParams } from 'react-router-dom'; @@ -5,30 +7,24 @@ import DocumentPage from '@/pages/DocumentPage'; const DatabasePage = lazy(() => import('./DatabasePage')); -enum URL_COLLAB_TYPE { - DOCUMENT = 'document', - GRID = 'grid', - BOARD = 'board', - CALENDAR = 'calendar', -} - function ProductPage() { - const { workspaceId, type, objectId } = useParams(); + const { workspaceId, objectId } = useParams(); + const type = useViewLayout(); + const PageComponent = useMemo(() => { switch (type) { - case URL_COLLAB_TYPE.DOCUMENT: + case ViewLayout.Document: return DocumentPage; - case URL_COLLAB_TYPE.GRID: - case URL_COLLAB_TYPE.BOARD: - case URL_COLLAB_TYPE.CALENDAR: + case ViewLayout.Grid: + case ViewLayout.Board: + case ViewLayout.Calendar: return DatabasePage; default: return null; } }, [type]); - console.log(workspaceId, type, objectId); - if (!workspaceId || !type || !objectId) return null; + if (!workspaceId || !objectId) return null; return ( <IdProvider workspaceId={workspaceId} objectId={objectId}> diff --git a/frontend/appflowy_web_app/src/styles/template.css b/frontend/appflowy_web_app/src/styles/template.css index b6f7dd3361..82a597519f 100644 --- a/frontend/appflowy_web_app/src/styles/template.css +++ b/frontend/appflowy_web_app/src/styles/template.css @@ -48,3 +48,12 @@ th { @apply text-left font-normal; } +html { + font-size: 16px; +} + +@media (max-width: 600px) { + html { + font-size: 14px; + } +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/styles/variables/dark.variables.css b/frontend/appflowy_web_app/src/styles/variables/dark.variables.css index 6753969ca0..d52ac6e96d 100644 --- a/frontend/appflowy_web_app/src/styles/variables/dark.variables.css +++ b/frontend/appflowy_web_app/src/styles/variables/dark.variables.css @@ -1,121 +1,51 @@ -/** -* Do not edit directly -* Generated on Thu, 09 May 2024 03:26:45 GMT -* Generated from $pnpm css:variables -*/ :root[data-dark-mode=true] { - --base-light-neutral-50: #f9fafd; - --base-light-neutral-100: #e5e5e5; - --base-light-neutral-200: #e2e4eb; - --base-light-neutral-300: #f2f2f2; - --base-light-neutral-400: #e0e0e0; - --base-light-neutral-500: #bdbdbd; - --base-light-neutral-600: #828282; - --base-light-neutral-700: #4f4f4f; - --base-light-neutral-800: #333333; - --base-light-neutral-900: #1f2329; - --base-light-neutral-1000: #000000; - --base-light-neutral-00: #ffffff; - --base-light-blue-50: #f2fcff; - --base-light-blue-100: #e0f8ff; - --base-light-blue-200: #a6ecff; - --base-light-blue-300: #52d1f4; - --base-light-blue-400: #00bcf0; - --base-light-blue-500: #05ade2; - --base-light-blue-600: #009fd1; - --base-light-color-deep-red: #fb006d; - --base-light-color-deep-yellow: #ffd667; - --base-light-color-deep-green: #66cf80; - --base-light-color-deep-blue: #00bcf0; - --base-light-color-light-purple: #e8e0ff; - --base-light-color-light-pink: #ffe7ee; - --base-light-color-light-orange: #ffefe3; - --base-light-color-light-yellow: #fff2cd; - --base-light-color-light-lime: #f5ffdc; - --base-light-color-light-green: #ddffd6; - --base-light-color-light-aqua: #defff1; - --base-light-color-light-blue: #e1fbff; - --base-light-color-light-red: #ffdddd; - --base-black-neutral-100: #252F41; - --base-black-neutral-200: #313c51; - --base-black-neutral-300: #3c4557; - --base-black-neutral-400: #525A69; - --base-black-neutral-500: #59647a; - --base-black-neutral-600: #87A0BF; - --base-black-neutral-700: #99a6b8; - --base-black-neutral-800: #e2e9f2; - --base-black-neutral-900: #eff4fb; - --base-black-neutral-1000: #ffffff; - --base-black-neutral-n50: #232b38; - --base-black-neutral-n00: #1a202c; - --base-black-blue-50: #232b38; - --base-black-blue-100: #005174; - --base-black-blue-200: #a6ecff; - --base-black-blue-300: #52d1f4; - --base-black-blue-400: #00bcf0; - --base-black-blue-500: #05ade2; - --base-black-blue-600: #009fd1; - --base-black-color-deep-red: #d32772; - --base-black-color-deep-yellow: #e9b320; - --base-black-color-deep-green: #3ba856; - --base-black-color-deep-blue: #2e9dbb; - --base-black-color-light-purple: #4D4078; - --base-black-color-light-blue: #2C3B58; - --base-black-color-light-green: #3C5133; - --base-black-color-light-yellow: #695E3E; - --base-black-color-light-pink: #5E3C5E; - --base-black-color-light-red: #56363F; - --base-black-color-light-aqua: #1B3849; - --base-black-color-light-lime: #394027; - --base-black-color-light-orange: #5E3C3C; - --base-else-brand: #2c144b; - --text-title: #e2e9f2; - --text-caption: #87A0BF; - --text-placeholder: #3c4557; - --text-link-default: #00bcf0; - --text-link-hover: #52d1f4; - --text-link-pressed: #009fd1; - --text-link-disabled: #005174; - --icon-primary: #e2e9f2; - --icon-secondary: #59647a; - --icon-disabled: #525A69; - --icon-on-toolbar: white; - --line-border: #59647a; - --line-divider: #252F41; - --line-on-toolbar: #99a6b8; - --fill-default: #00bcf0; - --fill-hover: #005174; - --fill-toolbar: #0F111C; - --fill-selector: #232b38; - --fill-list-active: #3c4557; - --fill-list-hover: #005174; - --content-blue-400: #00bcf0; - --content-blue-300: #52d1f4; - --content-blue-600: #009fd1; - --content-blue-100: #005174; - --content-on-fill: #1a202c; - --content-on-tag: #99a6b8; - --content-blue-50: #232b38; - --bg-body: #1a202c; - --bg-base: #232b38; - --bg-mask: rgba(0,0,0,0.7); - --bg-tips: #005174; - --bg-brand: #2c144b; - --function-error: #d32772; - --function-warning: #e9b320; - --function-success: #3ba856; - --function-info: #2e9dbb; - --tint-red: #56363F; - --tint-green: #3C5133; - --tint-purple: #4D4078; - --tint-blue: #2C3B58; - --tint-yellow: #695E3E; - --tint-pink: #5E3C5E; - --tint-lime: #394027; - --tint-aqua: #1B3849; - --tint-orange: #5E3C3C; - --shadow: 0px 0px 25px 0px rgba(0,0,0,0.3); - --scrollbar-track: #252F41; - --scrollbar-thumb: #3c4557; + --text-title: #e2e9f2; + --text-caption: #87A0BF; + --text-placeholder: #3c4557; + --text-link-default: #00bcf0; + --text-link-hover: #52d1f4; + --text-link-pressed: #009fd1; + --text-link-disabled: #005174; + --icon-primary: #e2e9f2; + --icon-secondary: #59647a; + --icon-disabled: #525A69; + --icon-on-toolbar: white; + --line-border: #59647a; + --line-divider: #252F41; + --line-on-toolbar: #99a6b8; + --fill-default: #00bcf0; + --fill-hover: #005174; + --fill-toolbar: #0F111C; + --fill-selector: #232b38; + --fill-list-active: #3c4557; + --fill-list-hover: #005174; + --content-blue-400: #00bcf0; + --content-blue-300: #52d1f4; + --content-blue-600: #009fd1; + --content-blue-100: #005174; + --content-on-fill: #1a202c; + --content-on-tag: #99a6b8; + --content-blue-50: #232b38; + --bg-body: #1a202c; + --bg-base: #232b38; + --bg-mask: rgba(0, 0, 0, 0.7); + --bg-tips: #005174; + --bg-brand: #2c144b; + --function-error: #d32772; + --function-warning: #e9b320; + --function-success: #3ba856; + --function-info: #2e9dbb; + --tint-red: #56363F; + --tint-green: #3C5133; + --tint-purple: #4D4078; + --tint-blue: #2C3B58; + --tint-yellow: #695E3E; + --tint-pink: #5E3C5E; + --tint-lime: #394027; + --tint-aqua: #1B3849; + --tint-orange: #5E3C3C; + --shadow: 0px 0px 25px 0px rgba(0, 0, 0, 0.3); + --scrollbar-track: #252F41; + --scrollbar-thumb: #3c4557; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/styles/variables/light.variables.css b/frontend/appflowy_web_app/src/styles/variables/light.variables.css index b1494114bd..bae74c9b3e 100644 --- a/frontend/appflowy_web_app/src/styles/variables/light.variables.css +++ b/frontend/appflowy_web_app/src/styles/variables/light.variables.css @@ -1,124 +1,54 @@ -/** -* Do not edit directly -* Generated on Thu, 09 May 2024 03:26:45 GMT -* Generated from $pnpm css:variables -*/ :root { - --base-light-neutral-50: #f9fafd; - --base-light-neutral-100: #e5e5e5; - --base-light-neutral-200: #e2e4eb; - --base-light-neutral-300: #f2f2f2; - --base-light-neutral-400: #e0e0e0; - --base-light-neutral-500: #bdbdbd; - --base-light-neutral-600: #828282; - --base-light-neutral-700: #4f4f4f; - --base-light-neutral-800: #333333; - --base-light-neutral-900: #1f2329; - --base-light-neutral-1000: #000000; - --base-light-neutral-00: #ffffff; - --base-light-blue-50: #f2fcff; - --base-light-blue-100: #e0f8ff; - --base-light-blue-200: #a6ecff; - --base-light-blue-300: #52d1f4; - --base-light-blue-400: #00bcf0; - --base-light-blue-500: #05ade2; - --base-light-blue-600: #009fd1; - --base-light-color-deep-red: #fb006d; - --base-light-color-deep-yellow: #ffd667; - --base-light-color-deep-green: #66cf80; - --base-light-color-deep-blue: #00bcf0; - --base-light-color-light-purple: #e8e0ff; - --base-light-color-light-pink: #ffe7ee; - --base-light-color-light-orange: #ffefe3; - --base-light-color-light-yellow: #fff2cd; - --base-light-color-light-lime: #f5ffdc; - --base-light-color-light-green: #ddffd6; - --base-light-color-light-aqua: #defff1; - --base-light-color-light-blue: #e1fbff; - --base-light-color-light-red: #ffdddd; - --base-black-neutral-100: #252F41; - --base-black-neutral-200: #313c51; - --base-black-neutral-300: #3c4557; - --base-black-neutral-400: #525A69; - --base-black-neutral-500: #59647a; - --base-black-neutral-600: #87A0BF; - --base-black-neutral-700: #99a6b8; - --base-black-neutral-800: #e2e9f2; - --base-black-neutral-900: #eff4fb; - --base-black-neutral-1000: #ffffff; - --base-black-neutral-n50: #232b38; - --base-black-neutral-n00: #1a202c; - --base-black-blue-50: #232b38; - --base-black-blue-100: #005174; - --base-black-blue-200: #a6ecff; - --base-black-blue-300: #52d1f4; - --base-black-blue-400: #00bcf0; - --base-black-blue-500: #05ade2; - --base-black-blue-600: #009fd1; - --base-black-color-deep-red: #d32772; - --base-black-color-deep-yellow: #e9b320; - --base-black-color-deep-green: #3ba856; - --base-black-color-deep-blue: #2e9dbb; - --base-black-color-light-purple: #4D4078; - --base-black-color-light-blue: #2C3B58; - --base-black-color-light-green: #3C5133; - --base-black-color-light-yellow: #695E3E; - --base-black-color-light-pink: #5E3C5E; - --base-black-color-light-red: #56363F; - --base-black-color-light-aqua: #1B3849; - --base-black-color-light-lime: #394027; - --base-black-color-light-orange: #5E3C3C; - --base-else-brand: #2c144b; - --text-title: #333333; - --text-caption: #828282; - --text-placeholder: #bdbdbd; - --text-disabled: #e0e0e0; - --text-link-default: #00bcf0; - --text-link-hover: #52d1f4; - --text-link-pressed: #009fd1; - --text-link-disabled: #e0f8ff; - --icon-primary: #333333; - --icon-secondary: #59647a; - --icon-disabled: #e0e0e0; - --icon-on-toolbar: #ffffff; - --line-border: #bdbdbd; - --line-divider: #e5e5e5; - --line-on-toolbar: #4f4f4f; - --fill-toolbar: #333333; - --fill-default: #00bcf0; - --fill-hover: #52d1f4; - --fill-pressed: #009fd1; - --fill-active: #e0f8ff; - --fill-list-hover: #e0f8ff; - --fill-list-active: #f9fafd; - --content-blue-400: #00bcf0; - --content-blue-300: #52d1f4; - --content-blue-600: #009fd1; - --content-blue-100: #e0f8ff; - --content-blue-50: #f2fcff; - --content-on-fill-hover: #00bcf0; - --content-on-fill: #ffffff; - --content-on-tag: #4f4f4f; - --bg-body: #ffffff; - --bg-base: #f9fafd; - --bg-mask: rgba(0,0,0,0.55); - --bg-tips: #e0f8ff; - --bg-brand: #2c144b; - --function-error: #fb006d; - --function-waring: #ffd667; - --function-success: #66cf80; - --function-info: #00bcf0; - --tint-purple: #e8e0ff; - --tint-pink: #ffe7ee; - --tint-red: #ffdddd; - --tint-lime: #f5ffdc; - --tint-green: #ddffd6; - --tint-aqua: #defff1; - --tint-blue: #e1fbff; - --tint-orange: #ffefe3; - --tint-yellow: #fff2cd; - --shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); - --scrollbar-thumb: #bdbdbd; - --scrollbar-track: #e5e5e5; + --text-title: #333333; + --text-caption: #828282; + --text-placeholder: #bdbdbd; + --text-disabled: #e0e0e0; + --text-link-default: #00bcf0; + --text-link-hover: #52d1f4; + --text-link-pressed: #009fd1; + --text-link-disabled: #e0f8ff; + --icon-primary: #333333; + --icon-secondary: #59647a; + --icon-disabled: #e0e0e0; + --icon-on-toolbar: #ffffff; + --line-border: #bdbdbd; + --line-divider: #e5e5e5; + --line-on-toolbar: #4f4f4f; + --fill-toolbar: #333333; + --fill-default: #00bcf0; + --fill-hover: #52d1f4; + --fill-pressed: #009fd1; + --fill-active: #e0f8ff; + --fill-list-hover: #e0f8ff; + --fill-list-active: #f9fafd; + --content-blue-400: #00bcf0; + --content-blue-300: #52d1f4; + --content-blue-600: #009fd1; + --content-blue-100: #e0f8ff; + --content-blue-50: #f2fcff; + --content-on-fill-hover: #00bcf0; + --content-on-fill: #ffffff; + --content-on-tag: #4f4f4f; + --bg-body: #ffffff; + --bg-base: #f9fafd; + --bg-mask: rgba(0, 0, 0, 0.55); + --bg-tips: #e0f8ff; + --bg-brand: #2c144b; + --function-error: #fb006d; + --function-waring: #ffd667; + --function-success: #66cf80; + --function-info: #00bcf0; + --tint-purple: #e8e0ff; + --tint-pink: #ffe7ee; + --tint-red: #ffdddd; + --tint-lime: #f5ffdc; + --tint-green: #ddffd6; + --tint-aqua: #defff1; + --tint-blue: #e1fbff; + --tint-orange: #ffefe3; + --tint-yellow: #fff2cd; + --shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1); + --scrollbar-thumb: #bdbdbd; + --scrollbar-track: #e5e5e5; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/utils/platform.ts b/frontend/appflowy_web_app/src/utils/platform.ts index 285e38bc73..fc3a538750 100644 --- a/frontend/appflowy_web_app/src/utils/platform.ts +++ b/frontend/appflowy_web_app/src/utils/platform.ts @@ -1,5 +1,6 @@ export function getPlatform() { return { isTauri: !!import.meta.env.TAURI_PLATFORM, + isMobile: window.navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i), }; } diff --git a/frontend/appflowy_web_app/style-dictionary/config.cjs b/frontend/appflowy_web_app/style-dictionary/config.cjs deleted file mode 100644 index 10d7084060..0000000000 --- a/frontend/appflowy_web_app/style-dictionary/config.cjs +++ /dev/null @@ -1,114 +0,0 @@ -const StyleDictionary = require('style-dictionary'); -const fs = require('fs'); -const path = require('path'); - -// Add comment header to generated files -StyleDictionary.registerFormat({ - name: 'css/variables', - formatter: function(dictionary, config) { - const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; - const allProperties = dictionary.allProperties; - const properties = allProperties.map(prop => { - const { name, value } = prop; - return ` --${name}: ${value};` - }).join('\n'); - // generate tailwind config - generateTailwindConfig(allProperties); - return header + `:root${this.selector} {\n${properties}\n}` - } -}); - -// expand shadow tokens into a single string -StyleDictionary.registerTransform({ - name: 'shadow/spreadShadow', - type: 'value', - matcher: function (prop) { - return prop.type === 'boxShadow'; - }, - transformer: function (prop) { - // destructure shadow values from original token value - const { x, y, blur, spread, color } = prop.original.value; - - return `${x}px ${y}px ${blur}px ${spread}px ${color}`; - }, -}); - -const transforms = ['attribute/cti', 'name/cti/kebab', 'shadow/spreadShadow']; - -// Generate Light CSS variables -StyleDictionary.extend({ - source: ['./style-dictionary/tokens/base.json', './style-dictionary/tokens/light.json'], - platforms: { - css: { - transformGroup: 'css', - buildPath: './src/styles/variables/', - files: [ - { - format: 'css/variables', - destination: 'light.variables.css', - selector: '', - options: { - outputReferences: true - } - }, - ], - transforms, - }, - }, -}).buildAllPlatforms(); - -// Generate Dark CSS variables -StyleDictionary.extend({ - source: ['./style-dictionary/tokens/base.json', './style-dictionary/tokens/dark.json'], - platforms: { - css: { - transformGroup: 'css', - buildPath: './src/styles/variables/', - files: [ - { - format: 'css/variables', - destination: 'dark.variables.css', - selector: '[data-dark-mode=true]', - }, - ], - transforms, - }, - }, -}).buildAllPlatforms(); - - -function set(obj, path, value) { - const lastKey = path.pop(); - const lastObj = path.reduce((obj, key) => - obj[key] = obj[key] || {}, - obj); - lastObj[lastKey] = value; -} - -function writeFile (file, data) { - const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; - const exportString = `module.exports = ${JSON.stringify(data, null, 2)}`; - fs.writeFileSync(path.join(__dirname, file), header + exportString); -} - -function generateTailwindConfig(allProperties) { - const tailwindColors = {}; - const tailwindBoxShadow = {}; - allProperties.forEach(prop => { - const { path, type, name, value } = prop; - if (path[0] === 'Base') { - return; - } - if (type === 'color') { - if (name.includes('fill')) { - console.log(prop); - } - set(tailwindColors, path, `var(--${name})`); - } - if (type === 'boxShadow') { - set(tailwindBoxShadow, ['md'], `var(--${name})`); - } - }); - writeFile('./tailwind/colors.cjs', tailwindColors); - writeFile('./tailwind/box-shadow.cjs', tailwindBoxShadow); -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/style-dictionary/tokens/base.json b/frontend/appflowy_web_app/style-dictionary/tokens/base.json deleted file mode 100644 index f92d39267f..0000000000 --- a/frontend/appflowy_web_app/style-dictionary/tokens/base.json +++ /dev/null @@ -1,290 +0,0 @@ -{ - "Base": { - "Light": { - "neutral": { - "50": { - "value": "#f9fafd", - "type": "color" - }, - "100": { - "value": "#e5e5e5", - "type": "color" - }, - "200": { - "value": "#e2e4eb", - "type": "color" - }, - "300": { - "value": "#f2f2f2", - "type": "color" - }, - "400": { - "value": "#e0e0e0", - "type": "color" - }, - "500": { - "value": "#bdbdbd", - "type": "color" - }, - "600": { - "value": "#828282", - "type": "color" - }, - "700": { - "value": "#4f4f4f", - "type": "color" - }, - "800": { - "value": "#333333", - "type": "color" - }, - "900": { - "value": "#1f2329", - "type": "color" - }, - "1000": { - "value": "#000000", - "type": "color" - }, - "00": { - "value": "#ffffff", - "type": "color" - } - }, - "blue": { - "50": { - "value": "#f2fcff", - "type": "color" - }, - "100": { - "value": "#e0f8ff", - "type": "color" - }, - "200": { - "value": "#a6ecff", - "type": "color" - }, - "300": { - "value": "#52d1f4", - "type": "color" - }, - "400": { - "value": "#00bcf0", - "type": "color" - }, - "500": { - "value": "#05ade2", - "type": "color" - }, - "600": { - "value": "#009fd1", - "type": "color" - } - }, - "color": { - "deep": { - "red": { - "value": "#fb006d", - "type": "color" - }, - "yellow": { - "value": "#ffd667", - "type": "color" - }, - "green": { - "value": "#66cf80", - "type": "color" - }, - "blue": { - "value": "#00bcf0", - "type": "color" - } - }, - "light": { - "purple": { - "value": "#e8e0ff", - "type": "color" - }, - "pink": { - "value": "#ffe7ee", - "type": "color" - }, - "orange": { - "value": "#ffefe3", - "type": "color" - }, - "yellow": { - "value": "#fff2cd", - "type": "color" - }, - "lime": { - "value": "#f5ffdc", - "type": "color" - }, - "green": { - "value": "#ddffd6", - "type": "color" - }, - "aqua": { - "value": "#defff1", - "type": "color" - }, - "blue": { - "value": "#e1fbff", - "type": "color" - }, - "red": { - "value": "#ffdddd", - "type": "color" - } - } - } - }, - "black": { - "neutral": { - "100": { - "value": "#252F41", - "type": "color" - }, - "200": { - "value": "#313c51", - "type": "color" - }, - "300": { - "value": "#3c4557", - "type": "color" - }, - "400": { - "value": "#525A69", - "type": "color" - }, - "500": { - "value": "#59647a", - "type": "color" - }, - "600": { - "value": "#87A0BF", - "type": "color" - }, - "700": { - "value": "#99a6b8", - "type": "color" - }, - "800": { - "value": "#e2e9f2", - "type": "color" - }, - "900": { - "value": "#eff4fb", - "type": "color" - }, - "1000": { - "value": "#ffffff", - "type": "color" - }, - "N50": { - "value": "#232b38", - "type": "color" - }, - "N00": { - "value": "#1a202c", - "type": "color" - } - }, - "blue": { - "50": { - "value": "#232b38", - "type": "color" - }, - "100": { - "value": "#005174", - "type": "color" - }, - "200": { - "value": "#a6ecff", - "type": "color" - }, - "300": { - "value": "#52d1f4", - "type": "color" - }, - "400": { - "value": "#00bcf0", - "type": "color" - }, - "500": { - "value": "#05ade2", - "type": "color" - }, - "600": { - "value": "#009fd1", - "type": "color" - } - }, - "color": { - "deep": { - "red": { - "value": "#d32772", - "type": "color" - }, - "yellow": { - "value": "#e9b320", - "type": "color" - }, - "green": { - "value": "#3ba856", - "type": "color" - }, - "blue": { - "value": "#2e9dbb", - "type": "color" - } - }, - "light": { - "purple": { - "value": "#4D4078", - "type": "color" - }, - "blue": { - "value": "#2C3B58", - "type": "color" - }, - "green": { - "value": "#3C5133", - "type": "color" - }, - "yellow": { - "value": "#695E3E", - "type": "color" - }, - "pink": { - "value": "#5E3C5E", - "type": "color" - }, - "red": { - "value": "#56363F", - "type": "color" - }, - "aqua": { - "value": "#1B3849", - "type": "color" - }, - "lime": { - "value": "#394027", - "type": "color" - }, - "orange": { - "value": "#5E3C3C", - "type": "color" - } - } - } - }, - "else": { - "brand": { - "value": "#2c144b", - "type": "color" - } - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/style-dictionary/tokens/dark.json b/frontend/appflowy_web_app/style-dictionary/tokens/dark.json deleted file mode 100644 index c67af7c9ec..0000000000 --- a/frontend/appflowy_web_app/style-dictionary/tokens/dark.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "text": { - "title": { - "value": "{Base.black.neutral.800}", - "type": "color" - }, - "caption": { - "value": "{Base.black.neutral.600}", - "type": "color" - }, - "placeholder": { - "value": "{Base.black.neutral.300}", - "type": "color" - }, - "link-default": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "link-hover": { - "value": "{Base.black.blue.300}", - "type": "color" - }, - "link-pressed": { - "value": "{Base.black.blue.600}", - "type": "color" - }, - "link-disabled": { - "value": "{Base.black.blue.100}", - "type": "color" - } - }, - "icon": { - "primary": { - "value": "{Base.black.neutral.800}", - "type": "color" - }, - "secondary": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.black.neutral.400}", - "type": "color" - }, - "on-toolbar": { - "value": "white", - "type": "color" - } - }, - "line": { - "border": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "divider": { - "value": "{Base.black.neutral.100}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.black.neutral.700}", - "type": "color" - } - }, - "fill": { - "default": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "hover": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "toolbar": { - "value": "#0F111C", - "type": "color" - }, - "selector": { - "value": "{Base.black.blue.50}", - "type": "color" - }, - "list": { - "active": { - "value": "{Base.black.neutral.300}", - "type": "color" - }, - "hover": { - "value": "{Base.black.blue.100}", - "type": "color" - } - } - }, - "content": { - "blue-400": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "blue-300": { - "value": "{Base.black.blue.300}", - "type": "color" - }, - "blue-600": { - "value": "{Base.black.blue.600}", - "type": "color" - }, - "blue-100": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "on-fill": { - "value": "{Base.black.neutral.N00}", - "type": "color" - }, - "on-tag": { - "value": "{Base.black.neutral.700}", - "type": "color" - }, - "blue-50": { - "value": "{Base.black.blue.50}", - "type": "color" - } - }, - "bg": { - "body": { - "value": "{Base.black.neutral.N00}", - "type": "color" - }, - "base": { - "value": "{Base.black.blue.50}", - "type": "color" - }, - "mask": { - "value": "rgba(0,0,0,0.7)", - "type": "color" - }, - "tips": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "brand": { - "value": "{Base.else.brand}", - "type": "color" - } - }, - "function": { - "error": { - "value": "{Base.black.color.deep.red}", - "type": "color" - }, - "warning": { - "value": "{Base.black.color.deep.yellow}", - "type": "color" - }, - "success": { - "value": "#3ba856", - "type": "color" - }, - "info": { - "value": "#2e9dbb", - "type": "color" - } - }, - "tint": { - "red": { - "value": "{Base.black.color.light.red}", - "type": "color" - }, - "green": { - "value": "{Base.black.color.light.green}", - "type": "color" - }, - "purple": { - "value": "{Base.black.color.light.purple}", - "type": "color" - }, - "blue": { - "value": "{Base.black.color.light.blue}", - "type": "color" - }, - "yellow": { - "value": "{Base.black.color.light.yellow}", - "type": "color" - }, - "pink": { - "value": "{Base.black.color.light.pink}", - "type": "color" - }, - "lime": { - "value": "{Base.black.color.light.lime}", - "type": "color" - }, - "aqua": { - "value": "{Base.black.color.light.aqua}", - "type": "color" - }, - "orange": { - "value": "{Base.black.color.light.orange}", - "type": "color" - } - }, - "shadow": { - "value": { - "x": "0", - "y": "0", - "blur": "25", - "spread": "0", - "color": "rgba(0,0,0,0.3)", - "type": "innerShadow" - }, - "type": "boxShadow" - }, - "scrollbar": { - "track": { - "value": "{Base.black.neutral.100}", - "type": "color" - }, - "thumb": { - "value": "{Base.black.neutral.300}", - "type": "color" - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/style-dictionary/tokens/light.json b/frontend/appflowy_web_app/style-dictionary/tokens/light.json deleted file mode 100644 index 173f3d35aa..0000000000 --- a/frontend/appflowy_web_app/style-dictionary/tokens/light.json +++ /dev/null @@ -1,233 +0,0 @@ -{ - "text": { - "title": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "caption": { - "value": "{Base.Light.neutral.600}", - "type": "color" - }, - "placeholder": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.Light.neutral.400}", - "type": "color" - }, - "link-default": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "link-hover": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "link-pressed": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "link-disabled": { - "value": "{Base.Light.blue.100}", - "type": "color" - } - }, - "icon": { - "primary": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "secondary": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.Light.neutral.400}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.Light.neutral.00}", - "type": "color" - } - }, - "line": { - "border": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "divider": { - "value": "{Base.Light.neutral.100}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.Light.neutral.700}", - "type": "color" - } - }, - "fill": { - "toolbar": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "default": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "hover": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "pressed": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "active": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "list": { - "hover": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "active": { - "value": "{Base.Light.neutral.100}", - "type": "color" - } - } - }, - "content": { - "blue-400": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "blue-300": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "blue-600": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "blue-100": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "blue-50": { - "value": "{Base.Light.blue.50}", - "type": "color" - }, - "on-fill-hover": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "on-fill": { - "value": "{Base.Light.neutral.00}", - "type": "color" - }, - "on-tag": { - "value": "{Base.Light.neutral.700}", - "type": "color" - } - }, - "bg": { - "body": { - "value": "{Base.Light.neutral.00}", - "type": "color" - }, - "base": { - "value": "{Base.Light.neutral.50}", - "type": "color" - }, - "mask": { - "value": "rgba(0,0,0,0.55)", - "type": "color" - }, - "tips": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "brand": { - "value": "{Base.else.brand}", - "type": "color" - } - }, - "function": { - "error": { - "value": "{Base.Light.color.deep.red}", - "type": "color" - }, - "waring": { - "value": "{Base.Light.color.deep.yellow}", - "type": "color" - }, - "success": { - "value": "{Base.Light.color.deep.green}", - "type": "color" - }, - "info": { - "value": "{Base.Light.color.deep.blue}", - "type": "color" - } - }, - "tint": { - "purple": { - "value": "{Base.Light.color.light.purple}", - "type": "color" - }, - "pink": { - "value": "{Base.Light.color.light.pink}", - "type": "color" - }, - "red": { - "value": "{Base.Light.color.light.red}", - "type": "color" - }, - "lime": { - "value": "{Base.Light.color.light.lime}", - "type": "color" - }, - "green": { - "value": "{Base.Light.color.light.green}", - "type": "color" - }, - "aqua": { - "value": "{Base.Light.color.light.aqua}", - "type": "color" - }, - "blue": { - "value": "{Base.Light.color.light.blue}", - "type": "color" - }, - "orange": { - "value": "{Base.Light.color.light.orange}", - "type": "color" - }, - "yellow": { - "value": "{Base.Light.color.light.yellow}", - "type": "color" - } - }, - "shadow": { - "value": { - "x": "0", - "y": "0", - "blur": "10", - "spread": "0", - "color": "rgba(0,0,0,0.1)", - "type": "dropShadow" - }, - "type": "boxShadow" - }, - "scrollbar": { - "thumb": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "track": { - "value": "{Base.Light.neutral.100}", - "type": "color" - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/tailwind.config.cjs b/frontend/appflowy_web_app/tailwind.config.cjs index 06390d938f..8589a0f4b6 100644 --- a/frontend/appflowy_web_app/tailwind.config.cjs +++ b/frontend/appflowy_web_app/tailwind.config.cjs @@ -1,5 +1,5 @@ -const colors = require('./style-dictionary/tailwind/colors.cjs'); -const boxShadow = require('./style-dictionary/tailwind/box-shadow.cjs'); +const colors = require('./tailwind/colors.cjs'); +const boxShadow = require('./tailwind/box-shadow.cjs'); /** @type {import('tailwindcss').Config} */ module.exports = { diff --git a/frontend/appflowy_web_app/style-dictionary/tailwind/box-shadow.cjs b/frontend/appflowy_web_app/tailwind/box-shadow.cjs similarity index 69% rename from frontend/appflowy_web_app/style-dictionary/tailwind/box-shadow.cjs rename to frontend/appflowy_web_app/tailwind/box-shadow.cjs index 9de67fc1be..d72c255227 100644 --- a/frontend/appflowy_web_app/style-dictionary/tailwind/box-shadow.cjs +++ b/frontend/appflowy_web_app/tailwind/box-shadow.cjs @@ -1,9 +1,11 @@ + /** * Do not edit directly -* Generated on Thu, 09 May 2024 03:26:45 GMT +* Generated on Mon, 27 May 2024 06:26:20 GMT * Generated from $pnpm css:variables */ + module.exports = { "md": "var(--shadow)" -} \ No newline at end of file +}; diff --git a/frontend/appflowy_web_app/style-dictionary/tailwind/colors.cjs b/frontend/appflowy_web_app/tailwind/colors.cjs similarity index 73% rename from frontend/appflowy_web_app/style-dictionary/tailwind/colors.cjs rename to frontend/appflowy_web_app/tailwind/colors.cjs index 63e679a90a..27a3b07c30 100644 --- a/frontend/appflowy_web_app/style-dictionary/tailwind/colors.cjs +++ b/frontend/appflowy_web_app/tailwind/colors.cjs @@ -1,14 +1,17 @@ + /** * Do not edit directly -* Generated on Thu, 09 May 2024 03:26:45 GMT +* Generated on Mon, 27 May 2024 06:26:20 GMT * Generated from $pnpm css:variables */ + module.exports = { "text": { "title": "var(--text-title)", "caption": "var(--text-caption)", "placeholder": "var(--text-placeholder)", + "disabled": "var(--text-disabled)", "link-default": "var(--text-link-default)", "link-hover": "var(--text-link-hover)", "link-pressed": "var(--text-link-pressed)", @@ -26,50 +29,49 @@ module.exports = { "on-toolbar": "var(--line-on-toolbar)" }, "fill": { + "toolbar": "var(--fill-toolbar)", "default": "var(--fill-default)", "hover": "var(--fill-hover)", - "toolbar": "var(--fill-toolbar)", - "selector": "var(--fill-selector)", - "list": { - "active": "var(--fill-list-active)", - "hover": "var(--fill-list-hover)" - } + "pressed": "var(--fill-pressed)", + "active": "var(--fill-active)", + "list-hover": "var(--fill-list-hover)", + "list-active": "var(--fill-list-active)" }, "content": { "blue-400": "var(--content-blue-400)", "blue-300": "var(--content-blue-300)", "blue-600": "var(--content-blue-600)", "blue-100": "var(--content-blue-100)", + "blue-50": "var(--content-blue-50)", + "on-fill-hover": "var(--content-on-fill-hover)", "on-fill": "var(--content-on-fill)", - "on-tag": "var(--content-on-tag)", - "blue-50": "var(--content-blue-50)" + "on-tag": "var(--content-on-tag)" }, "bg": { "body": "var(--bg-body)", "base": "var(--bg-base)", - "mask": "var(--bg-mask)", "tips": "var(--bg-tips)", "brand": "var(--bg-brand)" }, "function": { "error": "var(--function-error)", - "warning": "var(--function-warning)", + "waring": "var(--function-waring)", "success": "var(--function-success)", "info": "var(--function-info)" }, "tint": { - "red": "var(--tint-red)", - "green": "var(--tint-green)", "purple": "var(--tint-purple)", - "blue": "var(--tint-blue)", - "yellow": "var(--tint-yellow)", "pink": "var(--tint-pink)", + "red": "var(--tint-red)", "lime": "var(--tint-lime)", + "green": "var(--tint-green)", "aqua": "var(--tint-aqua)", - "orange": "var(--tint-orange)" + "blue": "var(--tint-blue)", + "orange": "var(--tint-orange)", + "yellow": "var(--tint-yellow)" }, "scrollbar": { - "track": "var(--scrollbar-track)", - "thumb": "var(--scrollbar-thumb)" + "thumb": "var(--scrollbar-thumb)", + "track": "var(--scrollbar-track)" } -} \ No newline at end of file +}; diff --git a/frontend/appflowy_web_app/vite.config.ts b/frontend/appflowy_web_app/vite.config.ts index 87a5e284bf..8431b8bd2f 100644 --- a/frontend/appflowy_web_app/vite.config.ts +++ b/frontend/appflowy_web_app/vite.config.ts @@ -72,7 +72,7 @@ export default defineConfig({ }, envPrefix: ['AF', 'TAURI_'], esbuild: { - drop: isDev ? [] : ['console', 'debugger'], + pure: !isDev ? ['console.log', 'console.debug', 'console.info'] : [], }, build: !!process.env.TAURI_PLATFORM ? {