Browse Source

Merge branch 'dev/7.0.x' into support/128303-128304-build-antarctic

soumaeda 2 years ago
parent
commit
1061c40973
100 changed files with 822 additions and 808 deletions
  1. 18 18
      .devcontainer/docker-compose.yml
  2. 1 1
      .github/release-drafter-dev-7.0.x.yml
  3. 5 3
      .github/workflows/ci-app-prod.yml
  4. 21 21
      .github/workflows/ci-app.yml
  5. 3 3
      .github/workflows/ci-slackbot-proxy.yml
  6. 1 1
      .github/workflows/release-slackbot-proxy.yml
  7. 2 2
      .github/workflows/release.yml
  8. 12 12
      .github/workflows/reusable-app-prod.yml
  9. 3 3
      .github/workflows/reusable-app-reg-suit.yml
  10. 0 2
      apps/app/.eslintignore
  11. 0 0
      apps/app/_obsolete/src/client/util/codemirror/autorefresh.ext.js
  12. 0 0
      apps/app/_obsolete/src/client/util/codemirror/drawio-fold.ext.js
  13. 0 0
      apps/app/_obsolete/src/client/util/codemirror/gfm-growi.mode.js
  14. 0 0
      apps/app/_obsolete/src/client/util/codemirror/update-display-util.ext.js
  15. 1 20
      apps/app/_obsolete/src/components/Navbar/GrowiNavbar.module.scss
  16. 3 47
      apps/app/_obsolete/src/components/Navbar/GrowiNavbar.tsx
  17. 0 0
      apps/app/_obsolete/src/components/PageEditor/CodeMirrorEditor.jsx
  18. 0 0
      apps/app/_obsolete/src/components/PageEditor/CodeMirrorEditor.module.scss
  19. 0 0
      apps/app/_obsolete/src/components/PageEditor/CommentMentionHelper.ts
  20. 0 0
      apps/app/_obsolete/src/components/PageEditor/ConflictDiffModal.tsx
  21. 0 0
      apps/app/_obsolete/src/components/PageEditor/EmojiPicker.tsx
  22. 0 0
      apps/app/_obsolete/src/components/PageEditor/EmojiPickerHelper.ts
  23. 0 0
      apps/app/_obsolete/src/components/UncontrolledCodeMirror.tsx
  24. 1 1
      apps/app/package.json
  25. 3 2
      apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx
  26. 3 2
      apps/app/src/components/Common/Dropdown/PageItemControl.tsx
  27. 4 9
      apps/app/src/components/Common/LazyRenderer.tsx
  28. 0 63
      apps/app/src/components/Fab.module.scss
  29. 0 119
      apps/app/src/components/Fab.tsx
  30. 1 1
      apps/app/src/components/InAppNotification/InAppNotificationDropdown.tsx
  31. 0 2
      apps/app/src/components/Layout/AdminLayout.tsx
  32. 2 11
      apps/app/src/components/Layout/BasicLayout.tsx
  33. 5 5
      apps/app/src/components/Layout/SearchResultLayout.module.scss
  34. 0 7
      apps/app/src/components/Layout/ShareLinkLayout.tsx
  35. 5 3
      apps/app/src/components/Navbar/DrawerToggler.tsx
  36. 0 108
      apps/app/src/components/Navbar/PersonalDropdown.jsx
  37. 1 10
      apps/app/src/components/Page/DisplaySwitcher.tsx
  38. 13 12
      apps/app/src/components/PageEditor/Editor.tsx
  39. 34 14
      apps/app/src/components/PageEditor/PageEditor.tsx
  40. 3 0
      apps/app/src/components/PageEditor/index.ts
  41. 5 5
      apps/app/src/components/Sidebar/AppearanceModeDropdown.tsx
  42. 1 2
      apps/app/src/components/Sidebar/PageTree/ItemsTree.module.scss
  43. 105 0
      apps/app/src/components/Sidebar/PersonalDropdown.tsx
  44. 24 61
      apps/app/src/components/Sidebar/Sidebar.module.scss
  45. 12 14
      apps/app/src/components/Sidebar/Sidebar.tsx
  46. 18 0
      apps/app/src/components/Sidebar/SidebarBrandLogo.tsx
  47. 26 0
      apps/app/src/components/Sidebar/SidebarNav.module.scss
  48. 30 1
      apps/app/src/components/Sidebar/SidebarNav.tsx
  49. 1 0
      apps/app/src/components/Sidebar/index.ts
  50. 2 3
      apps/app/src/components/UsersHomepageFooter.module.scss
  51. 0 7
      apps/app/src/pages/[[...path]].page.tsx
  52. 8 18
      apps/app/src/styles/_editor.scss
  53. 1 46
      apps/app/src/styles/_mixins.scss
  54. 0 12
      apps/app/src/styles/_variables.scss
  55. 0 12
      apps/app/src/styles/theme/apply-colors.scss
  56. 2 5
      apps/app/test/cypress/e2e/20-basic-features/20-basic-features--access-to-page.cy.ts
  57. 3 49
      apps/app/test/cypress/e2e/20-basic-features/20-basic-features--sticky-features.cy.ts
  58. 0 3
      apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.cy.ts
  59. 2 30
      apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--sticky-for-guest.cy.ts
  60. 11 11
      apps/app/test/cypress/e2e/50-sidebar/50-sidebar--access-to-side-bar.cy.ts
  61. 0 2
      apps/app/test/cypress/e2e/60-home/60-home--home.cy.ts
  62. 2 2
      apps/app/test/cypress/support/commands.ts
  63. 2 2
      apps/slackbot-proxy/package.json
  64. 1 1
      package.json
  65. 2 0
      packages/editor/.eslintignore
  66. 13 0
      packages/editor/.eslintrc.cjs
  67. 24 0
      packages/editor/.gitignore
  68. 14 0
      packages/editor/README.md
  69. 13 0
      packages/editor/index.html
  70. 37 0
      packages/editor/package.json
  71. 2 0
      packages/editor/src/@types/declaration.d.ts
  72. 13 0
      packages/editor/src/components/CodeMirrorEditorContainer.module.scss
  73. 9 0
      packages/editor/src/components/CodeMirrorEditorContainer.tsx
  74. 1 0
      packages/editor/src/components/index.ts
  75. 34 0
      packages/editor/src/components/playground/Playground.tsx
  76. 39 0
      packages/editor/src/components/playground/PlaygroundController.tsx
  77. 1 0
      packages/editor/src/components/playground/index.ts
  78. 2 0
      packages/editor/src/index.ts
  79. 1 0
      packages/editor/src/main.scss
  80. 17 0
      packages/editor/src/main.tsx
  81. 42 0
      packages/editor/src/services/codemirror-editor.ts
  82. 1 0
      packages/editor/src/services/index.ts
  83. 12 0
      packages/editor/src/stores/codemirror-editor.ts
  84. 1 0
      packages/editor/src/stores/index.ts
  85. 33 0
      packages/editor/src/stores/use-static-swr.tsx
  86. 1 0
      packages/editor/src/vite-env.d.ts
  87. 31 0
      packages/editor/tsconfig.json
  88. 11 0
      packages/editor/tsconfig.node.json
  89. 55 0
      packages/editor/vite.config.ts
  90. 3 0
      packages/pluginkit/package.json
  91. 1 0
      packages/pluginkit/vite.config.ts
  92. 0 12
      packages/preset-themes/src/styles/_variables.scss
  93. 1 1
      packages/remark-attachment-refs/package.json
  94. 1 1
      packages/remark-attachment-refs/vite.client.config.ts
  95. 1 1
      packages/remark-attachment-refs/vite.server.config.ts
  96. 0 1
      packages/remark-growi-directive/package.json
  97. 1 0
      packages/remark-lsx/src/utils/index.ts
  98. 1 1
      packages/remark-lsx/vite.client.config.ts
  99. 1 1
      packages/remark-lsx/vite.server.config.ts
  100. 2 2
      packages/ui/src/utils/browser-utils.ts

+ 18 - 18
.devcontainer/docker-compose.yml

@@ -31,16 +31,16 @@ services:
     image: mongo:6.0
     image: mongo:6.0
     restart: unless-stopped
     restart: unless-stopped
     ports:
     ports:
-      - 27017:27017
+      - 27018:27017
     volumes:
     volumes:
       - /data/db
       - /data/db
 
 
-  ogp:
-    image: ghcr.io/weseek/growi-unique-ogp:latest
-    ports:
-      - 8088:8088
-    restart: unless-stopped
-    tty: true
+  # ogp:
+  #   image: ghcr.io/weseek/growi-unique-ogp:latest
+  #   ports:
+  #     - 8088:8088
+  #   restart: unless-stopped
+  #   tty: true
 
 
   # This container requires '../../growi-docker-compose' repository
   # This container requires '../../growi-docker-compose' repository
   #   cloned from https://github.com/weseek/growi-docker-compose.git
   #   cloned from https://github.com/weseek/growi-docker-compose.git
@@ -52,7 +52,7 @@ services:
         - version=8.7.0
         - version=8.7.0
     restart: unless-stopped
     restart: unless-stopped
     ports:
     ports:
-      - 9200:9200
+      - 9201:9200
     environment:
     environment:
       - bootstrap.memory_lock=true
       - bootstrap.memory_lock=true
       - "ES_JAVA_OPTS=-Xms256m -Xmx256m"
       - "ES_JAVA_OPTS=-Xms256m -Xmx256m"
@@ -66,15 +66,15 @@ services:
       - ../../growi-docker-compose/elasticsearch/v8/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
       - ../../growi-docker-compose/elasticsearch/v8/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
 
 
   #need to adjust kibana version based on elasticsearch version (use same version as elasticsearch version)
   #need to adjust kibana version based on elasticsearch version (use same version as elasticsearch version)
-  kibana:
-    image: docker.elastic.co/kibana/kibana:8.7.0
-    restart: unless-stopped
-    environment:
-      ELASTICSEARCH_HOSTS: 'http://elasticsearch:9200'
-    ports:
-      - 5601:5601
-    depends_on:
-      - elasticsearch
+  # kibana:
+  #   image: docker.elastic.co/kibana/kibana:8.7.0
+  #   restart: unless-stopped
+  #   environment:
+  #     ELASTICSEARCH_HOSTS: 'http://elasticsearch:9200'
+  #   ports:
+  #     - 5601:5601
+  #   depends_on:
+  #     - elasticsearch
 
 
   # This container requires '../../growi-docker-compose' repository
   # This container requires '../../growi-docker-compose' repository
   #   cloned from https://github.com/weseek/growi-docker-compose.git
   #   cloned from https://github.com/weseek/growi-docker-compose.git
@@ -89,7 +89,7 @@ services:
       - CMD_DB_URL=sqlite://dummyhost/hackmd/sqlite/codimd.db
       - CMD_DB_URL=sqlite://dummyhost/hackmd/sqlite/codimd.db
       - CMD_CSP_ENABLE=false
       - CMD_CSP_ENABLE=false
     ports:
     ports:
-      - 3010:3000
+      - 3011:3000
     volumes:
     volumes:
       - /files/sqlite
       - /files/sqlite
 volumes:
 volumes:

+ 1 - 1
.github/release-drafter-dev-6.2.x.yml → .github/release-drafter-dev-7.0.x.yml

@@ -4,4 +4,4 @@ prerelease: true
 
 
 # Filter previous releases to consider only those with the tags starts with 'v6.2'
 # Filter previous releases to consider only those with the tags starts with 'v6.2'
 include-pre-releases: true
 include-pre-releases: true
-tag-prefix: v6.2
+tag-prefix: v7.0

+ 5 - 3
.github/workflows/ci-app-prod.yml

@@ -4,6 +4,7 @@ on:
   push:
   push:
     branches:
     branches:
       - master
       - master
+      - dev/7.*.x
       - dev/6.*.x
       - dev/6.*.x
     paths:
     paths:
       - .github/workflows/ci-app-prod.yml
       - .github/workflows/ci-app-prod.yml
@@ -19,6 +20,7 @@ on:
   pull_request:
   pull_request:
     branches:
     branches:
       - master
       - master
+      - dev/7.*.x
       - dev/6.*.x
       - dev/6.*.x
     types: [opened, reopened, synchronize]
     types: [opened, reopened, synchronize]
     paths:
     paths:
@@ -47,7 +49,7 @@ concurrency:
 jobs:
 jobs:
 
 
   test-prod-node16:
   test-prod-node16:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@dev/7.0.x
     with:
     with:
       node-version: 16.x
       node-version: 16.x
       skip-cypress: true
       skip-cypress: true
@@ -56,7 +58,7 @@ jobs:
 
 
 
 
   test-prod-node18:
   test-prod-node18:
-    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@master
+    uses: weseek/growi/.github/workflows/reusable-app-prod.yml@dev/7.0.x
     with:
     with:
       node-version: 18.x
       node-version: 18.x
       skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) }}
       skip-cypress: ${{ contains( github.event.pull_request.labels.*.name, 'dependencies' ) }}
@@ -69,7 +71,7 @@ jobs:
   run-reg-suit-node18:
   run-reg-suit-node18:
     needs: [test-prod-node18]
     needs: [test-prod-node18]
 
 
-    uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@master
+    uses: weseek/growi/.github/workflows/reusable-app-reg-suit.yml@dev/7.0.x
 
 
     if: always()
     if: always()
 
 

+ 21 - 21
.github/workflows/ci-app.yml

@@ -43,10 +43,10 @@ jobs:
         with:
         with:
           path: |
           path: |
             **/node_modules
             **/node_modules
-          key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
+          key: node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
           restore-keys: |
           restore-keys: |
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Restore dist
       - name: Restore dist
         uses: actions/cache/restore@v3
         uses: actions/cache/restore@v3
@@ -54,13 +54,13 @@ jobs:
           path: |
           path: |
             **/.turbo
             **/.turbo
             **/dist
             **/dist
-          key: dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
           restore-keys: |
           restore-keys: |
-            dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-
+            dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: Lint
       - name: Lint
@@ -83,7 +83,7 @@ jobs:
           path: |
           path: |
             **/.turbo
             **/.turbo
             **/dist
             **/dist
-          key: dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
 
 
 
 
   test:
   test:
@@ -114,10 +114,10 @@ jobs:
         with:
         with:
           path: |
           path: |
             **/node_modules
             **/node_modules
-          key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
+          key: node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
           restore-keys: |
           restore-keys: |
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Restore dist
       - name: Restore dist
         uses: actions/cache/restore@v3
         uses: actions/cache/restore@v3
@@ -125,13 +125,13 @@ jobs:
           path: |
           path: |
             **/.turbo
             **/.turbo
             **/dist
             **/dist
-          key: dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
           restore-keys: |
           restore-keys: |
-            dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-
+            dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: Test
       - name: Test
@@ -164,7 +164,7 @@ jobs:
           path: |
           path: |
             **/.turbo
             **/.turbo
             **/dist
             **/dist
-          key: dist-app-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-ci-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
 
 
 
 
   launch-dev:
   launch-dev:
@@ -195,10 +195,10 @@ jobs:
         with:
         with:
           path: |
           path: |
             **/node_modules
             **/node_modules
-          key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
+          key: node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/app/package.json') }}
           restore-keys: |
           restore-keys: |
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-            node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
+            node_modules-7.x-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Restore dist
       - name: Restore dist
         uses: actions/cache/restore@v3
         uses: actions/cache/restore@v3
@@ -207,13 +207,13 @@ jobs:
             **/.turbo
             **/.turbo
             **/dist
             **/dist
             ${{ github.workspace }}/apps/app/.next
             ${{ github.workspace }}/apps/app/.next
-          key: dist-app-dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
           restore-keys: |
           restore-keys: |
-            dist-app-dev-${{ runner.OS }}-node${{ matrix.node-version }}-
+            dist-app-7.x-dev-${{ runner.OS }}-node${{ matrix.node-version }}-
 
 
       - name: Install dependencies
       - name: Install dependencies
         run: |
         run: |
-          yarn global add turbo@1.10.9
+          yarn global add turbo
           yarn --frozen-lockfile
           yarn --frozen-lockfile
 
 
       - name: turbo run dev:ci
       - name: turbo run dev:ci
@@ -241,4 +241,4 @@ jobs:
             **/.turbo
             **/.turbo
             **/dist
             **/dist
             ${{ github.workspace }}/apps/app/.next
             ${{ github.workspace }}/apps/app/.next
-          key: dist-app-dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}
+          key: dist-app-7.x-dev-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('node_modules/.cache/turbo/*-meta.json') }}

+ 3 - 3
.github/workflows/ci-slackbot-proxy.yml

@@ -62,7 +62,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Lint
     - name: Lint
@@ -136,7 +136,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: yarn dev:ci
     - name: yarn dev:ci
@@ -200,7 +200,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |

+ 1 - 1
.github/workflows/release-slackbot-proxy.yml

@@ -108,7 +108,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions for next RC
     - name: Bump versions for next RC

+ 2 - 2
.github/workflows/release.yml

@@ -30,7 +30,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions
     - name: Bump versions
@@ -93,7 +93,7 @@ jobs:
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
         yarn --frozen-lockfile
         yarn --frozen-lockfile
 
 
     - name: Bump versions for next RC
     - name: Bump versions for next RC

+ 12 - 12
.github/workflows/reusable-app-prod.yml

@@ -36,7 +36,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -50,9 +50,9 @@ jobs:
       with:
       with:
         path: |
         path: |
           **/node_modules
           **/node_modules
-        key: node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
+        key: node_modules-app-7.x-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
         restore-keys: |
-          node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+          node_modules-app-7.x-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
@@ -66,10 +66,10 @@ jobs:
           **/.turbo
           **/.turbo
           **/dist
           **/dist
           ${{ github.workspace }}/apps/app/.next
           ${{ github.workspace }}/apps/app/.next
-        key: dist-app-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ github.ref_name }}-${{ github.sha }}
+        key: dist-app-7.x-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ github.ref_name }}-${{ github.sha }}
         restore-keys: |
         restore-keys: |
-          dist-app-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ github.ref_name }}-
-          dist-app-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+          dist-app-7.x-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ github.ref_name }}-
+          dist-app-7.x-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
 
 
     - name: Build
     - name: Build
       working-directory: ./apps/app
       working-directory: ./apps/app
@@ -147,7 +147,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -161,9 +161,9 @@ jobs:
       with:
       with:
         path: |
         path: |
           **/node_modules
           **/node_modules
-        key: node_modules-app-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
+        key: node_modules-app-7.x-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
         restore-keys: |
-          node_modules-app-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+          node_modules-app-7.x-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |
@@ -238,7 +238,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -252,9 +252,9 @@ jobs:
       with:
       with:
         path: |
         path: |
           **/node_modules
           **/node_modules
-        key: node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
+        key: node_modules-app-7.x-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
         restore-keys: |
-          node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+          node_modules-app-7.x-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
 
 
     - name: Cache/Restore Cypress files
     - name: Cache/Restore Cypress files
       uses: actions/cache@v3
       uses: actions/cache@v3

+ 3 - 3
.github/workflows/reusable-app-reg-suit.yml

@@ -62,7 +62,7 @@ jobs:
 
 
     - name: Install turbo
     - name: Install turbo
       run: |
       run: |
-        yarn global add turbo@1.10.9
+        yarn global add turbo
 
 
     - name: Prune repositories
     - name: Prune repositories
       run: |
       run: |
@@ -76,9 +76,9 @@ jobs:
       with:
       with:
         path: |
         path: |
           **/node_modules
           **/node_modules
-        key: node_modules-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
+        key: node_modules-7.x-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
         restore-keys: |
         restore-keys: |
-          node_modules-${{ runner.OS }}-node${{ inputs.node-version }}-
+          node_modules-7.x-${{ runner.OS }}-node${{ inputs.node-version }}-
 
 
     - name: Install dependencies
     - name: Install dependencies
       run: |
       run: |

+ 0 - 2
apps/app/.eslintignore

@@ -2,8 +2,6 @@
 /dist/**
 /dist/**
 /transpiled/**
 /transpiled/**
 /public/**
 /public/**
-/src/client/legacy/thirdparty-js/**
-/src/client/util/reveal/plugins/markdown.js
 /src/linter-checker/**
 /src/linter-checker/**
 /tmp/**
 /tmp/**
 /next-env.d.ts
 /next-env.d.ts

+ 0 - 0
apps/app/src/client/util/codemirror/autorefresh.ext.js → apps/app/_obsolete/src/client/util/codemirror/autorefresh.ext.js


+ 0 - 0
apps/app/src/client/util/codemirror/drawio-fold.ext.js → apps/app/_obsolete/src/client/util/codemirror/drawio-fold.ext.js


+ 0 - 0
apps/app/src/client/util/codemirror/gfm-growi.mode.js → apps/app/_obsolete/src/client/util/codemirror/gfm-growi.mode.js


+ 0 - 0
apps/app/src/client/util/codemirror/update-display-util.ext.js → apps/app/_obsolete/src/client/util/codemirror/update-display-util.ext.js


+ 1 - 20
apps/app/src/components/Navbar/GrowiNavbar.module.scss → apps/app/_obsolete/src/components/Navbar/GrowiNavbar.module.scss

@@ -1,17 +1,9 @@
 @use '~/styles/variables' as var;
 @use '~/styles/variables' as var;
-@use '@growi/core/scss/bootstrap/init' as bs;
+@use '~/styles/bootstrap/init' as bs;
 @use '~/styles/mixins';
 @use '~/styles/mixins';
 
 
 .grw-navbar :global {
 .grw-navbar :global {
 
 
-  .grw-logo {
-    svg {
-      width: var.$grw-logo-width;
-      height: var.$grw-navbar-height;
-      padding: (var.$grw-logo-width - var.$grw-logomark-width) / 2;
-    }
-  }
-
   .confidential {
   .confidential {
     font-weight: bold;
     font-weight: bold;
   }
   }
@@ -70,17 +62,6 @@
     background: rgba(0, 0, 0, 0.2);
     background: rgba(0, 0, 0, 0.2);
   }
   }
 
 
-  .grw-apperance-mode-dropdown,
-  .grw-personal-dropdown {
-    .dropdown-menu {
-      min-width: 15rem;
-
-      .grw-icon-container svg {
-        width: 18px;
-        height: 18px;
-      }
-    }
-  }
   .grw-email-sm {
   .grw-email-sm {
     font-size: 0.75em;
     font-size: 0.75em;
   }
   }

+ 3 - 47
apps/app/src/components/Navbar/GrowiNavbar.tsx → apps/app/_obsolete/src/components/Navbar/GrowiNavbar.tsx

@@ -4,28 +4,21 @@ import React, {
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
-import Link from 'next/link';
 import { useRipple } from 'react-use-ripple';
 import { useRipple } from 'react-use-ripple';
 import { UncontrolledTooltip } from 'reactstrap';
 import { UncontrolledTooltip } from 'reactstrap';
 
 
 import {
 import {
-  useIsSearchPage, useIsGuestUser, useIsReadOnlyUser, useIsSearchServiceConfigured, useAppTitle, useConfidential, useIsDefaultLogo,
+  useIsSearchPage, useIsGuestUser, useIsReadOnlyUser, useIsSearchServiceConfigured, useAppTitle, useConfidential,
 } from '~/stores/context';
 } from '~/stores/context';
 import { usePageCreateModal } from '~/stores/modal';
 import { usePageCreateModal } from '~/stores/modal';
 import { useCurrentPagePath } from '~/stores/page';
 import { useCurrentPagePath } from '~/stores/page';
 import { useIsDeviceSmallerThanMd } from '~/stores/ui';
 import { useIsDeviceSmallerThanMd } from '~/stores/ui';
 
 
-import GrowiLogo from '../Icons/GrowiLogo';
 
 
 import { GlobalSearchProps } from './GlobalSearch';
 import { GlobalSearchProps } from './GlobalSearch';
 
 
 import styles from './GrowiNavbar.module.scss';
 import styles from './GrowiNavbar.module.scss';
 
 
-const PersonalDropdown = dynamic(() => import('./PersonalDropdown'), { ssr: false });
-const InAppNotificationDropdown = dynamic(() => import('../InAppNotification/InAppNotificationDropdown')
-  .then(mod => mod.InAppNotificationDropdown), { ssr: false });
-const AppearanceModeDropdown = dynamic(() => import('./AppearanceModeDropdown').then(mod => mod.AppearanceModeDropdown), { ssr: false });
-
 const NavbarRight = memo((): JSX.Element => {
 const NavbarRight = memo((): JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
@@ -44,10 +37,6 @@ const NavbarRight = memo((): JSX.Element => {
   const authenticatedNavItem = useMemo(() => {
   const authenticatedNavItem = useMemo(() => {
     return (
     return (
       <>
       <>
-        <li className="nav-item">
-          <InAppNotificationDropdown />
-        </li>
-
         {!isReadOnlyUser
         {!isReadOnlyUser
           && <li className="nav-item d-none d-md-block">
           && <li className="nav-item d-none d-md-block">
             <button
             <button
@@ -62,28 +51,17 @@ const NavbarRight = memo((): JSX.Element => {
             </button>
             </button>
           </li>
           </li>
         }
         }
-
-        <li className="grw-apperance-mode-dropdown nav-item dropdown">
-          <AppearanceModeDropdown isAuthenticated={isAuthenticated} />
-        </li>
-
-        <li className="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret" data-testid="grw-personal-dropdown">
-          <PersonalDropdown />
-        </li>
       </>
       </>
     );
     );
-  }, [isReadOnlyUser, t, isAuthenticated, openCreateModal, currentPagePath]);
+  }, [isReadOnlyUser, t, openCreateModal, currentPagePath]);
 
 
   const notAuthenticatedNavItem = useMemo(() => {
   const notAuthenticatedNavItem = useMemo(() => {
     return (
     return (
       <>
       <>
-        <li className="grw-apperance-mode-dropdown nav-item dropdown">
-          <AppearanceModeDropdown isAuthenticated={isAuthenticated} />
-        </li>
         <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>
         <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>
       </>
       </>
     );
     );
-  }, [isAuthenticated]);
+  }, []);
 
 
   return (
   return (
     <>
     <>
@@ -121,21 +99,6 @@ const Confidential: FC<ConfidentialProps> = memo((props: ConfidentialProps): JSX
 });
 });
 Confidential.displayName = 'Confidential';
 Confidential.displayName = 'Confidential';
 
 
-interface NavbarLogoProps {
-  isDefaultLogo?: boolean
-}
-
-const GrowiNavbarLogo: FC<NavbarLogoProps> = memo((props: NavbarLogoProps) => {
-  const { isDefaultLogo } = props;
-
-  return isDefaultLogo
-    ? <GrowiLogo />
-    // eslint-disable-next-line @next/next/no-img-element
-    : (<img src='/attachment/brand-logo' alt="custom logo" className="picture picture-lg p-2 mx-2" id="settingBrandLogo" width="32" />);
-});
-
-GrowiNavbarLogo.displayName = 'GrowiNavbarLogo';
-
 type Props = {
 type Props = {
   isGlobalSearchHidden?: boolean
   isGlobalSearchHidden?: boolean
 }
 }
@@ -151,16 +114,9 @@ export const GrowiNavbar = (props: Props): JSX.Element => {
   const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
   const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const { data: isSearchPage } = useIsSearchPage();
   const { data: isSearchPage } = useIsSearchPage();
-  const { data: isDefaultLogo } = useIsDefaultLogo();
 
 
   return (
   return (
     <nav id="grw-navbar" className={`navbar grw-navbar ${styles['grw-navbar']} navbar-expand navbar-dark sticky-top mb-0 px-0`}>
     <nav id="grw-navbar" className={`navbar grw-navbar ${styles['grw-navbar']} navbar-expand navbar-dark sticky-top mb-0 px-0`}>
-      {/* Brand Logo  */}
-      <div className="navbar-brand mr-0">
-        <Link href="/" className="grw-logo d-block">
-          <GrowiNavbarLogo isDefaultLogo={isDefaultLogo} />
-        </Link>
-      </div>
 
 
       <div className="grw-app-title d-none d-md-block">
       <div className="grw-app-title d-none d-md-block">
         {appTitle}
         {appTitle}

+ 0 - 0
apps/app/src/components/PageEditor/CodeMirrorEditor.jsx → apps/app/_obsolete/src/components/PageEditor/CodeMirrorEditor.jsx


+ 0 - 0
apps/app/src/components/PageEditor/CodeMirrorEditor.module.scss → apps/app/_obsolete/src/components/PageEditor/CodeMirrorEditor.module.scss


+ 0 - 0
apps/app/src/components/PageEditor/CommentMentionHelper.ts → apps/app/_obsolete/src/components/PageEditor/CommentMentionHelper.ts


+ 0 - 0
apps/app/src/components/PageEditor/ConflictDiffModal.tsx → apps/app/_obsolete/src/components/PageEditor/ConflictDiffModal.tsx


+ 0 - 0
apps/app/src/components/PageEditor/EmojiPicker.tsx → apps/app/_obsolete/src/components/PageEditor/EmojiPicker.tsx


+ 0 - 0
apps/app/src/components/PageEditor/EmojiPickerHelper.ts → apps/app/_obsolete/src/components/PageEditor/EmojiPickerHelper.ts


+ 0 - 0
apps/app/src/components/UncontrolledCodeMirror.tsx → apps/app/_obsolete/src/components/UncontrolledCodeMirror.tsx


+ 1 - 1
apps/app/package.json

@@ -211,6 +211,7 @@
     "handsontable": "v7.0.0 or above is no loger MIT lisence."
     "handsontable": "v7.0.0 or above is no loger MIT lisence."
   },
   },
   "devDependencies": {
   "devDependencies": {
+    "@growi/editor": "link:../../packages/editor",
     "@growi/presentation": "link:../../packages/presentation",
     "@growi/presentation": "link:../../packages/presentation",
     "@growi/ui": "link:../../packages/ui",
     "@growi/ui": "link:../../packages/ui",
     "@handsontable/react": "=2.1.0",
     "@handsontable/react": "=2.1.0",
@@ -224,7 +225,6 @@
     "autoprefixer": "^9.0.0",
     "autoprefixer": "^9.0.0",
     "babel-loader": "^8.2.5",
     "babel-loader": "^8.2.5",
     "bootstrap": "^5.3.1",
     "bootstrap": "^5.3.1",
-    "codemirror": "^5.64.0",
     "connect-browser-sync": "^2.1.0",
     "connect-browser-sync": "^2.1.0",
     "diff2html": "^3.4.35",
     "diff2html": "^3.4.35",
     "eazy-logger": "^3.1.0",
     "eazy-logger": "^3.1.0",

+ 3 - 2
apps/app/src/components/Bookmarks/BookmarkFolderMenu.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback, useMemo, useState } from 'react';
 import React, { useCallback, useMemo, useState } from 'react';
 
 
-import { getCustomModifiers } from '@growi/ui/dist/utils';
+// import { getCustomModifiers } from '@growi/ui/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { DropdownItem, DropdownMenu, UncontrolledDropdown } from 'reactstrap';
 import { DropdownItem, DropdownMenu, UncontrolledDropdown } from 'reactstrap';
 
 
@@ -195,7 +195,8 @@ export const BookmarkFolderMenu = (props: BookmarkFolderMenuProps): JSX.Element
         persist
         persist
         positionFixed
         positionFixed
         className='grw-bookmark-folder-menu'
         className='grw-bookmark-folder-menu'
-        modifiers={getCustomModifiers(true)}
+        // TODO: activate (https://redmine.weseek.co.jp/issues/128468)
+        // modifiers={getCustomModifiers(true)}
       >
       >
         { renderBookmarkMenuItem() }
         { renderBookmarkMenuItem() }
       </DropdownMenu>
       </DropdownMenu>

+ 3 - 2
apps/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -5,7 +5,7 @@ import React, {
 import {
 import {
   type IPageInfoAll, isIPageInfoForOperation,
   type IPageInfoAll, isIPageInfoForOperation,
 } from '@growi/core';
 } from '@growi/core';
-import { getCustomModifiers } from '@growi/ui/dist/utils';
+// import { getCustomModifiers } from '@growi/ui/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
   Dropdown, DropdownMenu, DropdownToggle, DropdownItem,
   Dropdown, DropdownMenu, DropdownToggle, DropdownItem,
@@ -253,7 +253,8 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
     <DropdownMenu
     <DropdownMenu
       data-testid="page-item-control-menu"
       data-testid="page-item-control-menu"
       right={alignRight}
       right={alignRight}
-      modifiers={getCustomModifiers(alignRight)}
+      // TODO: activate (https://redmine.weseek.co.jp/issues/128468)
+      // modifiers={getCustomModifiers(alignRight)}
       container="body"
       container="body"
       persist={!!alignRight}
       persist={!!alignRight}
       style={{ zIndex: 1055 }} /* make it larger than $zindex-modal of bootstrap */
       style={{ zIndex: 1055 }} /* make it larger than $zindex-modal of bootstrap */

+ 4 - 9
apps/app/src/components/Common/LazyRenderer.tsx

@@ -21,17 +21,12 @@ export const LazyRenderer = (props: Props): JSX.Element => {
     setActivated(shouldRender);
     setActivated(shouldRender);
   }, [isActivated, shouldRender]);
   }, [isActivated, shouldRender]);
 
 
-  const additionalClassName = shouldRender ? '' : 'd-none';
-
   if (!isActivated) {
   if (!isActivated) {
     return <></>;
     return <></>;
   }
   }
 
 
-  return (
-    <>
-      { React.cloneElement(children, {
-        className: `${children.props.className ?? ''} ${additionalClassName}`,
-      }) }
-    </>
-  );
+  const child = React.Children.only(children);
+
+  return React.cloneElement(child, { visibility: shouldRender });
+
 };
 };

+ 0 - 63
apps/app/src/components/Fab.module.scss

@@ -1,63 +0,0 @@
-@use '@growi/core/scss/bootstrap/init' as bs;
-
-.grw-fab :global {
-  position: fixed;
-  right: 1.5rem;
-  bottom: 3rem;
-  z-index: bs.$zindex-fixed;
-
-  transition: all 200ms linear;
-
-  .btn-create-page {
-    width: 60px;
-    height: 60px;
-    font-size: 24px;
-
-    box-shadow: 2px 3px 6px #0000005d;
-    svg {
-      width: 28px;
-      height: 28px;
-    }
-  }
-
-  .btn-scroll-to-top {
-    width: 40px;
-    height: 40px;
-
-    opacity: 0.4;
-    svg {
-      width: 18px;
-      height: 18px;
-    }
-  }
-
-  // workaround
-  // https://stackoverflow.com/a/57667536
-  .fadeInUp {
-    & :local {
-      animation: fab-fadeinup 0.5s ease 0s;
-    }
-  }
-  .fadeOut {
-    & :local {
-      animation: fab-fadeout 0.5s ease 0s forwards;
-    }
-  }
-}
-
-@keyframes fab-fadeinup {
-  0% {
-    opacity: 0;
-    transform: translateY(100px);
-  }
-  100% {
-    opacity: 1;
-    transform: translateY(0px);
-  }
-}
-
-@keyframes fab-fadeout {
-  100% {
-    opacity: 0
-  }
-}

+ 0 - 119
apps/app/src/components/Fab.tsx

@@ -1,119 +0,0 @@
-import React, {
-  useState, useCallback, useRef, useEffect,
-} from 'react';
-
-import { animateScroll } from 'react-scroll';
-import { useRipple } from 'react-use-ripple';
-
-import { useSticky } from '~/client/services/side-effects/use-sticky';
-import { usePageCreateModal } from '~/stores/modal';
-import { useCurrentPagePath } from '~/stores/page';
-import { useIsAbleToChangeEditorMode } from '~/stores/ui';
-import loggerFactory from '~/utils/logger';
-
-import { CreatePageIcon } from './Icons/CreatePageIcon';
-import { ReturnTopIcon } from './Icons/ReturnTopIcon';
-
-import styles from './Fab.module.scss';
-
-const logger = loggerFactory('growi:cli:Fab');
-
-export const Fab = (): JSX.Element => {
-
-  const { data: isAbleToChangeEditorMode } = useIsAbleToChangeEditorMode();
-  const { data: currentPath = '' } = useCurrentPagePath();
-  const { open: openCreateModal } = usePageCreateModal();
-
-  const [animateClasses, setAnimateClasses] = useState<string>('invisible');
-  const [buttonClasses, setButtonClasses] = useState<string>('');
-  const [isStickyApplied, setIsStickyApplied] = useState(false);
-
-  // ripple
-  const createBtnRef = useRef(null);
-  useRipple(createBtnRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
-
-  // Get sticky status
-  const isSticky = useSticky('#grw-fav-sticky-trigger');
-
-  // check if isSticky is already initialized then save it in isStickyApplied state
-  useEffect(() => {
-    if (isSticky) {
-      setIsStickyApplied(true);
-    }
-  }, [isSticky]);
-
-  // Apply new classes if only isSticky is initialized, otherwise no classes have changed
-  // Prevents the Fab button from showing on first load due to the isSticky effect
-  useEffect(() => {
-    if (isStickyApplied) {
-      const timer = setTimeout(() => {
-        if (isSticky) {
-          setAnimateClasses('visible');
-          setButtonClasses('');
-        }
-        else {
-          setAnimateClasses('invisible');
-        }
-      }, 500);
-
-      const newAnimateClasses = isSticky ? 'animated fadeInUp faster' : 'animated fadeOut faster';
-      const newButtonClasses = isSticky ? '' : 'disabled grw-pointer-events-none';
-
-      setAnimateClasses(newAnimateClasses);
-      setButtonClasses(newButtonClasses);
-
-      return () => clearTimeout(timer);
-    }
-  }, [isSticky, isStickyApplied]);
-
-  const PageCreateButton = useCallback(() => {
-    return (
-      <div
-        className={`rounded-circle position-absolute ${animateClasses}`}
-        style={{ bottom: '2.3rem', right: '4rem' }}
-        data-testid="grw-fab-page-create-button"
-      >
-        <button
-          type="button"
-          className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 ${buttonClasses}`}
-          ref={createBtnRef}
-          onClick={currentPath != null
-            ? () => openCreateModal(currentPath)
-            : undefined}
-        >
-          <CreatePageIcon />
-        </button>
-      </div>
-    );
-  }, [animateClasses, buttonClasses, currentPath, openCreateModal]);
-
-  const ScrollToTopButton = useCallback(() => {
-    const clickHandler = () => {
-      animateScroll.scrollToTop({ duration: 200 });
-    };
-
-    return (
-      <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: 0, right: 0 }} data-testid="grw-fab-return-to-top">
-        <button
-          type="button"
-          className={`btn btn-light btn-scroll-to-top rounded-circle p-0 ${buttonClasses}`}
-          onClick={clickHandler}
-        >
-          <ReturnTopIcon />
-        </button>
-      </div>
-    );
-  }, [animateClasses, buttonClasses]);
-
-  if (currentPath == null) {
-    return <></>;
-  }
-
-  return (
-    <div className={`${styles['grw-fab']} grw-fab d-none d-md-block d-edit-none`} data-testid="grw-fab-container">
-      {isAbleToChangeEditorMode && <PageCreateButton />}
-      <ScrollToTopButton />
-    </div>
-  );
-
-};

+ 1 - 1
apps/app/src/components/InAppNotification/InAppNotificationDropdown.tsx

@@ -82,7 +82,7 @@ export const InAppNotificationDropdown = (): JSX.Element => {
   }
   }
 
 
   return (
   return (
-    <Dropdown className="notification-wrapper grw-notification-dropdown" isOpen={isOpen} toggle={toggleDropdownHandler}>
+    <Dropdown className="notification-wrapper grw-notification-dropdown" isOpen={isOpen} toggle={toggleDropdownHandler} direction="right">
       <DropdownToggle className="px-3 nav-link border-0 bg-transparent" innerRef={buttonRef}>
       <DropdownToggle className="px-3 nav-link border-0 bg-transparent" innerRef={buttonRef}>
         <i className="icon-bell" /> {badge}
         <i className="icon-bell" /> {badge}
       </DropdownToggle>
       </DropdownToggle>

+ 0 - 2
apps/app/src/components/Layout/AdminLayout.tsx

@@ -3,7 +3,6 @@ import React, { ReactNode } from 'react';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 
 
 import { AdminNavigation } from '../Admin/Common/AdminNavigation';
 import { AdminNavigation } from '../Admin/Common/AdminNavigation';
-import { GrowiNavbar } from '../Navbar/GrowiNavbar';
 
 
 import { RawLayout } from './RawLayout';
 import { RawLayout } from './RawLayout';
 
 
@@ -28,7 +27,6 @@ const AdminLayout = ({
   return (
   return (
     <RawLayout>
     <RawLayout>
       <div className={`admin-page ${styles['admin-page']}`}>
       <div className={`admin-page ${styles['admin-page']}`}>
-        <GrowiNavbar isGlobalSearchHidden={true} />
 
 
         <header className="py-0 container-fluid">
         <header className="py-0 container-fluid">
           <h1 className="title px-3">{componentTitle}</h1>
           <h1 className="title px-3">{componentTitle}</h1>

+ 2 - 11
apps/app/src/components/Layout/BasicLayout.tsx

@@ -4,15 +4,13 @@ import dynamic from 'next/dynamic';
 import { DndProvider } from 'react-dnd';
 import { DndProvider } from 'react-dnd';
 import { HTML5Backend } from 'react-dnd-html5-backend';
 import { HTML5Backend } from 'react-dnd-html5-backend';
 
 
-import { GrowiNavbar } from '../Navbar/GrowiNavbar';
-import Sidebar from '../Sidebar';
+import { Sidebar } from '../Sidebar';
 
 
 import { RawLayout } from './RawLayout';
 import { RawLayout } from './RawLayout';
 
 
 const AlertSiteUrlUndefined = dynamic(() => import('../AlertSiteUrlUndefined').then(mod => mod.AlertSiteUrlUndefined), { ssr: false });
 const AlertSiteUrlUndefined = dynamic(() => import('../AlertSiteUrlUndefined').then(mod => mod.AlertSiteUrlUndefined), { ssr: false });
 const DeleteAttachmentModal = dynamic(() => import('../PageAttachment/DeleteAttachmentModal').then(mod => mod.DeleteAttachmentModal), { ssr: false });
 const DeleteAttachmentModal = dynamic(() => import('../PageAttachment/DeleteAttachmentModal').then(mod => mod.DeleteAttachmentModal), { ssr: false });
 const HotkeysManager = dynamic(() => import('../Hotkeys/HotkeysManager'), { ssr: false });
 const HotkeysManager = dynamic(() => import('../Hotkeys/HotkeysManager'), { ssr: false });
-// const PageCreateModal = dynamic(() => import('../client/js/components/PageCreateModal'), { ssr: false });
 const GrowiNavbarBottom = dynamic(() => import('../Navbar/GrowiNavbarBottom').then(mod => mod.GrowiNavbarBottom), { ssr: false });
 const GrowiNavbarBottom = dynamic(() => import('../Navbar/GrowiNavbarBottom').then(mod => mod.GrowiNavbarBottom), { ssr: false });
 const ShortcutsModal = dynamic(() => import('../ShortcutsModal'), { ssr: false });
 const ShortcutsModal = dynamic(() => import('../ShortcutsModal'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
@@ -25,8 +23,6 @@ const PageRenameModal = dynamic(() => import('../PageRenameModal'), { ssr: false
 const PagePresentationModal = dynamic(() => import('../PagePresentationModal'), { ssr: false });
 const PagePresentationModal = dynamic(() => import('../PagePresentationModal'), { ssr: false });
 const PageAccessoriesModal = dynamic(() => import('../PageAccessoriesModal').then(mod => mod.PageAccessoriesModal), { ssr: false });
 const PageAccessoriesModal = dynamic(() => import('../PageAccessoriesModal').then(mod => mod.PageAccessoriesModal), { ssr: false });
 const DeleteBookmarkFolderModal = dynamic(() => import('../DeleteBookmarkFolderModal').then(mod => mod.DeleteBookmarkFolderModal), { ssr: false });
 const DeleteBookmarkFolderModal = dynamic(() => import('../DeleteBookmarkFolderModal').then(mod => mod.DeleteBookmarkFolderModal), { ssr: false });
-// Fab
-const Fab = dynamic(() => import('../Fab').then(mod => mod.Fab), { ssr: false });
 
 
 
 
 type Props = {
 type Props = {
@@ -38,12 +34,9 @@ export const BasicLayout = ({ children, className }: Props): JSX.Element => {
   return (
   return (
     <RawLayout className={className ?? ''}>
     <RawLayout className={className ?? ''}>
       <DndProvider backend={HTML5Backend}>
       <DndProvider backend={HTML5Backend}>
-        <GrowiNavbar />
 
 
         <div className="page-wrapper d-flex d-print-block">
         <div className="page-wrapper d-flex d-print-block">
-          <div className="grw-sidebar-wrapper">
-            <Sidebar />
-          </div>
+          <Sidebar />
 
 
           <div className="flex-fill mw-0">
           <div className="flex-fill mw-0">
             <AlertSiteUrlUndefined />
             <AlertSiteUrlUndefined />
@@ -66,8 +59,6 @@ export const BasicLayout = ({ children, className }: Props): JSX.Element => {
       <PagePresentationModal />
       <PagePresentationModal />
       <HotkeysManager />
       <HotkeysManager />
 
 
-      <Fab />
-
       <ShortcutsModal />
       <ShortcutsModal />
       <SystemVersion showShortcutsButton />
       <SystemVersion showShortcutsButton />
     </RawLayout>
     </RawLayout>

+ 5 - 5
apps/app/src/components/Layout/SearchResultLayout.module.scss

@@ -13,12 +13,12 @@
   }
   }
   .search-result-list {
   .search-result-list {
     .search-result-list-scroll {
     .search-result-list-scroll {
-      // subtract the height of GrowiNavbar + (SearchControl component + other factors)
-      height: calc(100vh - ((var.$grw-navbar-height + var.$grw-navbar-border-width) + 110px));
+      // subtract the height of (SearchControl component + other factors)
+      height: calc(100vh - 110px);
       overflow-y: scroll;
       overflow-y: scroll;
 
 
       @include bs.media-breakpoint-down(sm) {
       @include bs.media-breakpoint-down(sm) {
-        height: calc(100vh - ((var.$grw-navbar-height + var.$grw-navbar-border-width + var.$grw-navbar-bottom-height) + 123px));
+        height: calc(100vh - (var.$grw-navbar-bottom-height + 123px));
       }
       }
     }
     }
 
 
@@ -54,7 +54,7 @@
     }
     }
 
 
     .search-result-content {
     .search-result-content {
-      height: calc(100vh - (var.$grw-navbar-height + var.$grw-navbar-border-width));
+      height: 100vh;
 
 
       > h2 {
       > h2 {
         margin-right: 10px;
         margin-right: 10px;
@@ -85,7 +85,7 @@
 // style to apply when displaying search page
 // style to apply when displaying search page
 .on-search :global {
 .on-search :global {
   // set sidebar height shown in search page
   // set sidebar height shown in search page
-  $search-page-sidebar-height: calc(100vh - (var.$grw-navbar-height + var.$grw-navbar-border-width));
+  $search-page-sidebar-height: 100vh;
 
 
   .grw-sidebar {
   .grw-sidebar {
     height: $search-page-sidebar-height;
     height: $search-page-sidebar-height;

+ 0 - 7
apps/app/src/components/Layout/ShareLinkLayout.tsx

@@ -3,7 +3,6 @@ import React, { ReactNode } from 'react';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 
 
 import { useEditorModeClassName } from '../../client/services/layout';
 import { useEditorModeClassName } from '../../client/services/layout';
-import { GrowiNavbar } from '../Navbar/GrowiNavbar';
 
 
 import { RawLayout } from './RawLayout';
 import { RawLayout } from './RawLayout';
 
 
@@ -12,9 +11,6 @@ const GrowiNavbarBottom = dynamic(() => import('../Navbar/GrowiNavbarBottom').th
 const ShortcutsModal = dynamic(() => import('../ShortcutsModal'), { ssr: false });
 const ShortcutsModal = dynamic(() => import('../ShortcutsModal'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
 const SystemVersion = dynamic(() => import('../SystemVersion'), { ssr: false });
 
 
-// Fab
-const Fab = dynamic(() => import('../Fab').then(mod => mod.Fab), { ssr: false });
-
 
 
 type Props = {
 type Props = {
   children?: ReactNode
   children?: ReactNode
@@ -25,7 +21,6 @@ export const ShareLinkLayout = ({ children }: Props): JSX.Element => {
 
 
   return (
   return (
     <RawLayout className={className}>
     <RawLayout className={className}>
-      <GrowiNavbar isGlobalSearchHidden={true} />
 
 
       <div className="page-wrapper d-flex d-print-block">
       <div className="page-wrapper d-flex d-print-block">
         <div className="flex-fill mw-0">
         <div className="flex-fill mw-0">
@@ -35,8 +30,6 @@ export const ShareLinkLayout = ({ children }: Props): JSX.Element => {
 
 
       <GrowiNavbarBottom />
       <GrowiNavbarBottom />
 
 
-      <Fab />
-
       <ShortcutsModal />
       <ShortcutsModal />
       <PageCreateModal />
       <PageCreateModal />
       <SystemVersion showShortcutsButton />
       <SystemVersion showShortcutsButton />

+ 5 - 3
apps/app/src/components/Navbar/DrawerToggler.tsx

@@ -1,4 +1,4 @@
-import React, { FC } from 'react';
+import React from 'react';
 
 
 import { useDrawerOpened } from '~/stores/ui';
 import { useDrawerOpened } from '~/stores/ui';
 
 
@@ -6,11 +6,13 @@ type Props = {
   iconClass?: string,
   iconClass?: string,
 }
 }
 
 
-const DrawerToggler: FC<Props> = (props: Props) => {
+const DrawerToggler = (props: Props): JSX.Element => {
 
 
   const { data: isOpened, mutate } = useDrawerOpened();
   const { data: isOpened, mutate } = useDrawerOpened();
 
 
-  const iconClass = props.iconClass || 'icon-menu';
+  const iconClass = props.iconClass ?? isOpened
+    ? 'icon-arrow-left'
+    : 'icon-arrow-right';
 
 
   return (
   return (
     <button
     <button

+ 0 - 108
apps/app/src/components/Navbar/PersonalDropdown.jsx

@@ -1,108 +0,0 @@
-import { useRef, useState } from 'react';
-
-import { pagePathUtils } from '@growi/core/dist/utils';
-import { UserPicture } from '@growi/ui/dist/components';
-import { useTranslation } from 'next-i18next';
-import dynamic from 'next/dynamic';
-import Link from 'next/link';
-import { useRipple } from 'react-use-ripple';
-
-import { apiv3Post } from '~/client/util/apiv3-client';
-import { toastError } from '~/client/util/toastr';
-import { useCurrentUser } from '~/stores/context';
-
-const ProactiveQuestionnaireModal = dynamic(() => import('~/features/questionnaire/client/components/ProactiveQuestionnaireModal'), { ssr: false });
-
-const PersonalDropdown = () => {
-  const { t } = useTranslation('commons');
-  const { data: currentUser } = useCurrentUser();
-
-  const [isQuestionnaireModalOpen, setQuestionnaireModalOpen] = useState(false);
-
-  // ripple
-  const buttonRef = useRef(null);
-  useRipple(buttonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
-
-  if (currentUser == null) {
-    return <div className="text-muted text-center mb-5">
-      <i className="fa fa-2x fa-spinner fa-pulse mr-1" />
-    </div>;
-  }
-
-  const logoutHandler = async() => {
-    try {
-      await apiv3Post('/logout');
-      window.location.reload();
-    }
-    catch (err) {
-      toastError(err);
-    }
-  };
-
-  return (
-    <>
-      {/* Button */}
-      {/* remove .dropdown-toggle for hide caret */}
-      {/* See https://stackoverflow.com/a/44577512/13183572 */}
-      <button className="bg-transparent border-0 nav-link" type="button" ref={buttonRef} data-toggle="dropdown" data-testid="personal-dropdown-button">
-        <UserPicture user={currentUser} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{currentUser.name}</span>
-      </button>
-
-      {/* Menu */}
-      <div className="dropdown-menu dropdown-menu-right" data-testid="personal-dropdown-menu">
-
-        <div className="px-4 pt-3 pb-2 text-center">
-          <UserPicture user={currentUser} size="lg" noLink noTooltip />
-
-          <h5 className="mt-2">
-            {currentUser.name}
-          </h5>
-
-          <div className="my-2">
-            <i className="icon-user icon-fw"></i>{currentUser.username}<br />
-            <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
-          </div>
-
-          <div className="btn-group btn-block mt-2" role="group">
-            <Link
-              href={pagePathUtils.userHomepagePath(currentUser)}
-              className="btn btn-sm btn-outline-secondary col"
-              data-testid="grw-personal-dropdown-menu-user-home"
-            >
-              <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
-            </Link>
-            <Link
-              href="/me"
-              className="btn btn-sm btn-outline-secondary col"
-              data-testid="grw-personal-dropdown-menu-user-settings"
-            >
-              <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
-            </Link>
-          </div>
-        </div>
-
-        <div className="dropdown-divider"></div>
-
-        <button
-          data-testid="grw-proactive-questionnaire-modal-toggle-btn"
-          type="button"
-          className="dropdown-item"
-          onClick={() => setQuestionnaireModalOpen(true)}>
-          <i className="icon-fw icon-pencil"></i>{t('personal_dropdown.feedback')}
-        </button>
-
-        <div className="dropdown-divider"></div>
-
-        <button type="button" className="dropdown-item" onClick={logoutHandler}>
-          <i className="icon-fw icon-power"></i>{t('Sign out')}
-        </button>
-      </div>
-
-      <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />
-
-    </>
-  );
-
-};
-
-export default PersonalDropdown;

+ 1 - 10
apps/app/src/components/Page/DisplaySwitcher.tsx

@@ -13,7 +13,6 @@ import { LazyRenderer } from '../Common/LazyRenderer';
 
 
 
 
 const PageEditor = dynamic(() => import('../PageEditor'), { ssr: false });
 const PageEditor = dynamic(() => import('../PageEditor'), { ssr: false });
-const PageEditorByHackmd = dynamic(() => import('../PageEditorByHackmd').then(mod => mod.PageEditorByHackmd), { ssr: false });
 const EditorNavbarBottom = dynamic(() => import('../PageEditor/EditorNavbarBottom'), { ssr: false });
 const EditorNavbarBottom = dynamic(() => import('../PageEditor/EditorNavbarBottom'), { ssr: false });
 
 
 
 
@@ -38,15 +37,7 @@ export const DisplaySwitcher = (props: Props): JSX.Element => {
       { isViewMode && pageView }
       { isViewMode && pageView }
 
 
       <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.Editor}>
       <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.Editor}>
-        <div data-testid="page-editor" id="page-editor" className="editor-root">
-          <PageEditor />
-        </div>
-      </LazyRenderer>
-
-      <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.HackMD}>
-        <div id="page-editor-with-hackmd" className="editor-root">
-          <PageEditorByHackmd />
-        </div>
+        <PageEditor />
       </LazyRenderer>
       </LazyRenderer>
 
 
       { isEditable && !isViewMode && <EditorNavbarBottom /> }
       { isEditable && !isViewMode && <EditorNavbarBottom /> }

+ 13 - 12
apps/app/src/components/PageEditor/Editor.tsx

@@ -20,7 +20,7 @@ import { IEditorMethods } from '../../interfaces/editor-methods';
 
 
 import AbstractEditor from './AbstractEditor';
 import AbstractEditor from './AbstractEditor';
 import { Cheatsheet } from './Cheatsheet';
 import { Cheatsheet } from './Cheatsheet';
-import CodeMirrorEditor from './CodeMirrorEditor';
+// import CodeMirrorEditor from './CodeMirrorEditor';
 import pasteHelper from './PasteHelper';
 import pasteHelper from './PasteHelper';
 import TextAreaEditor from './TextAreaEditor';
 import TextAreaEditor from './TextAreaEditor';
 
 
@@ -306,17 +306,18 @@ const Editor: ForwardRefRenderFunction<IEditorMethods, EditorPropsType> = (props
 
 
                 {/* for PC */}
                 {/* for PC */}
                 { !isMobile && (
                 { !isMobile && (
-                  <CodeMirrorEditor
-                    ref={cmEditorRef}
-                    indentSize={indentSize ?? defaultIndentSize}
-                    onPasteFiles={pasteFilesHandler}
-                    onDragEnter={dragEnterHandler}
-                    onMarkdownHelpButtonClicked={() => { setIsCheatsheetModalShown(true) }}
-                    onAddAttachmentButtonClicked={addAttachmentHandler}
-                    editorSettings={editorSettings}
-                    isGfmMode={isGfmMode}
-                    {...props}
-                  />
+                  // <CodeMirrorEditor
+                  //   ref={cmEditorRef}
+                  //   indentSize={indentSize ?? defaultIndentSize}
+                  //   onPasteFiles={pasteFilesHandler}
+                  //   onDragEnter={dragEnterHandler}
+                  //   onMarkdownHelpButtonClicked={() => { setIsCheatsheetModalShown(true) }}
+                  //   onAddAttachmentButtonClicked={addAttachmentHandler}
+                  //   editorSettings={editorSettings}
+                  //   isGfmMode={isGfmMode}
+                  //   {...props}
+                  // />
+                  <></>
                 )}
                 )}
 
 
                 {/* for mobile */}
                 {/* for mobile */}

+ 34 - 14
apps/app/src/components/PageEditor.tsx → apps/app/src/components/PageEditor/PageEditor.tsx

@@ -8,6 +8,7 @@ import nodePath from 'path';
 
 
 import type { IPageHasId } from '@growi/core';
 import type { IPageHasId } from '@growi/core';
 import { pathUtils } from '@growi/core/dist/utils';
 import { pathUtils } from '@growi/core/dist/utils';
+import { CodeMirrorEditorContainer, useCodeMirrorEditor } from '@growi/editor';
 import detectIndent from 'detect-indent';
 import detectIndent from 'detect-indent';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
@@ -51,10 +52,13 @@ import loggerFactory from '~/utils/logger';
 
 
 
 
 // import { ConflictDiffModal } from './PageEditor/ConflictDiffModal';
 // import { ConflictDiffModal } from './PageEditor/ConflictDiffModal';
-import { ConflictDiffModal } from './PageEditor/ConflictDiffModal';
-import Editor from './PageEditor/Editor';
-import Preview from './PageEditor/Preview';
-import scrollSyncHelper from './PageEditor/ScrollSyncHelper';
+// import { ConflictDiffModal } from './ConflictDiffModal';
+// import Editor from './Editor';
+import Preview from './Preview';
+import scrollSyncHelper from './ScrollSyncHelper';
+
+
+import '@growi/editor/dist/style.css';
 
 
 
 
 const logger = loggerFactory('growi:PageEditor');
 const logger = loggerFactory('growi:PageEditor');
@@ -71,11 +75,20 @@ let lastScrolledDateWithCursor: Date | null = null;
 let isOriginOfScrollSyncEditor = false;
 let isOriginOfScrollSyncEditor = false;
 let isOriginOfScrollSyncPreview = false;
 let isOriginOfScrollSyncPreview = false;
 
 
-const PageEditor = React.memo((): JSX.Element => {
+
+type Props = {
+  visibility?: boolean,
+}
+
+export const PageEditor = React.memo((props: Props): JSX.Element => {
 
 
   const { t } = useTranslation();
   const { t } = useTranslation();
   const router = useRouter();
   const router = useRouter();
 
 
+  const editorRef = useRef<IEditorMethods>(null);
+  const previewRef = useRef<HTMLDivElement>(null);
+  const codeMirrorEditorContainerRef = useRef<HTMLDivElement>(null);
+
   const { data: isNotFound } = useIsNotFound();
   const { data: isNotFound } = useIsNotFound();
   const { data: pageId, mutate: mutateCurrentPageId } = useCurrentPageId();
   const { data: pageId, mutate: mutateCurrentPageId } = useCurrentPageId();
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: currentPagePath } = useCurrentPagePath();
@@ -103,6 +116,15 @@ const PageEditor = React.memo((): JSX.Element => {
   const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
   const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
   const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
   const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
 
 
+  const { setContainer } = useCodeMirrorEditor({
+    container: codeMirrorEditorContainerRef.current,
+  });
+  useEffect(() => {
+    if (codeMirrorEditorContainerRef.current != null) {
+      setContainer(codeMirrorEditorContainerRef.current);
+    }
+  }, [setContainer]);
+
   const { data: rendererOptions } = usePreviewOptions();
   const { data: rendererOptions } = usePreviewOptions();
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const saveOrUpdate = useSaveOrUpdate();
   const saveOrUpdate = useSaveOrUpdate();
@@ -142,9 +164,6 @@ const PageEditor = React.memo((): JSX.Element => {
   const { mutate: mutateIsConflict } = useIsConflict();
   const { mutate: mutateIsConflict } = useIsConflict();
 
 
 
 
-  const editorRef = useRef<IEditorMethods>(null);
-  const previewRef = useRef<HTMLDivElement>(null);
-
   const checkIsConflict = useCallback((data) => {
   const checkIsConflict = useCallback((data) => {
     const { s2cMessagePageUpdated } = data;
     const { s2cMessagePageUpdated } = data;
 
 
@@ -554,9 +573,9 @@ const PageEditor = React.memo((): JSX.Element => {
   const isUploadable = isUploadableImage || isUploadableFile;
   const isUploadable = isUploadableImage || isUploadableFile;
 
 
   return (
   return (
-    <div className="d-flex flex-wrap">
+    <div data-testid="page-editor" id="page-editor" className={`d-flex flex-grow-1 overflow-auto ${props.visibility ? '' : 'd-none'}`}>
       <div className="page-editor-editor-container flex-grow-1 flex-basis-0 mw-0">
       <div className="page-editor-editor-container flex-grow-1 flex-basis-0 mw-0">
-        <Editor
+        {/* <Editor
           ref={editorRef}
           ref={editorRef}
           value={initialValue}
           value={initialValue}
           isUploadable={isUploadable}
           isUploadable={isUploadable}
@@ -567,9 +586,10 @@ const PageEditor = React.memo((): JSX.Element => {
           onChange={markdownChangedHandler}
           onChange={markdownChangedHandler}
           onUpload={uploadHandler}
           onUpload={uploadHandler}
           onSave={saveWithShortcut}
           onSave={saveWithShortcut}
-        />
+        /> */}
+        <CodeMirrorEditorContainer ref={codeMirrorEditorContainerRef} />
       </div>
       </div>
-      <div className="d-none d-lg-block page-editor-preview-container flex-grow-1 flex-basis-0 mw-0">
+      <div className="d-none d-lg-flex page-editor-preview-container justify-content-center flex-grow-1 flex-basis-0 mw-0">
         <Preview
         <Preview
           ref={previewRef}
           ref={previewRef}
           rendererOptions={rendererOptions}
           rendererOptions={rendererOptions}
@@ -578,6 +598,7 @@ const PageEditor = React.memo((): JSX.Element => {
           onScroll={offset => scrollEditorByPreviewScrollWithThrottle(offset)}
           onScroll={offset => scrollEditorByPreviewScrollWithThrottle(offset)}
         />
         />
       </div>
       </div>
+      {/*
       <ConflictDiffModal
       <ConflictDiffModal
         isOpen={conflictDiffModalStatus?.isOpened}
         isOpen={conflictDiffModalStatus?.isOpened}
         onClose={() => closeConflictDiffModal()}
         onClose={() => closeConflictDiffModal()}
@@ -585,9 +606,8 @@ const PageEditor = React.memo((): JSX.Element => {
         optionsToSave={optionsToSave}
         optionsToSave={optionsToSave}
         afterResolvedHandler={afterResolvedHandler}
         afterResolvedHandler={afterResolvedHandler}
       />
       />
+       */}
     </div>
     </div>
   );
   );
 });
 });
 PageEditor.displayName = 'PageEditor';
 PageEditor.displayName = 'PageEditor';
-
-export default PageEditor;

+ 3 - 0
apps/app/src/components/PageEditor/index.ts

@@ -0,0 +1,3 @@
+import { PageEditor } from './PageEditor';
+
+export default PageEditor;

+ 5 - 5
apps/app/src/components/Navbar/AppearanceModeDropdown.tsx → apps/app/src/components/Sidebar/AppearanceModeDropdown.tsx

@@ -101,16 +101,16 @@ export const AppearanceModeDropdown:FC<AppearanceModeDropdownProps> = (props: Ap
   }, [isPreferDrawerMode, isPreferDrawerModeOnEdit, preferDrawerModeSwitchModifiedHandler, t]);
   }, [isPreferDrawerMode, isPreferDrawerModeOnEdit, preferDrawerModeSwitchModifiedHandler, t]);
 
 
   return (
   return (
-    <>
+    <div className="dropend">
       {/* setting button */}
       {/* setting button */}
       {/* remove .dropdown-toggle for hide caret */}
       {/* remove .dropdown-toggle for hide caret */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
-      <button className="bg-transparent border-0 nav-link" type="button" data-toggle="dropdown" ref={buttonRef} aria-haspopup="true">
-        <i className="icon-settings"></i>
+      <button className="btn btn-primary" type="button" data-bs-toggle="dropdown" ref={buttonRef} aria-haspopup="true">
+        <i className="material-icons">settings</i>
       </button>
       </button>
 
 
       {/* dropdown */}
       {/* dropdown */}
-      <div className="dropdown-menu dropdown-menu-right">
+      <div className="dropdown-menu">
 
 
         {/* sidebar mode */}
         {/* sidebar mode */}
         {renderSidebarModeSwitch(false)}
         {renderSidebarModeSwitch(false)}
@@ -170,7 +170,7 @@ export const AppearanceModeDropdown:FC<AppearanceModeDropdownProps> = (props: Ap
 
 
       </div>
       </div>
 
 
-    </>
+    </div>
   );
   );
 
 
 };
 };

+ 1 - 2
apps/app/src/components/Sidebar/PageTree/ItemsTree.module.scss

@@ -1,4 +1,3 @@
-@use '~/styles/variables' as var;
 @use '~/styles/mixins' as *;
 @use '~/styles/mixins' as *;
 $grw-sidebar-content-header-height: 58px;
 $grw-sidebar-content-header-height: 58px;
 $grw-sidebar-content-footer-height: 50px;
 $grw-sidebar-content-footer-height: 50px;
@@ -18,7 +17,7 @@ $grw-pagetree-item-container-height: 40px;
   }
   }
 
 
   :global {
   :global {
-    min-height: calc(100vh - (var.$grw-navbar-height + var.$grw-navbar-border-width + $grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
+    min-height: calc(100vh - ($grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
 
 
     .btn-page-item-control {
     .btn-page-item-control {
       .icon-plus::before {
       .icon-plus::before {

+ 105 - 0
apps/app/src/components/Sidebar/PersonalDropdown.tsx

@@ -0,0 +1,105 @@
+import { useState } from 'react';
+
+import { pagePathUtils } from '@growi/core/dist/utils';
+import { UserPicture } from '@growi/ui/dist/components';
+import { useTranslation } from 'next-i18next';
+import dynamic from 'next/dynamic';
+import Link from 'next/link';
+
+import { apiv3Post } from '~/client/util/apiv3-client';
+import { toastError } from '~/client/util/toastr';
+import { useCurrentUser } from '~/stores/context';
+
+const ProactiveQuestionnaireModal = dynamic(() => import('~/features/questionnaire/client/components/ProactiveQuestionnaireModal'), { ssr: false });
+
+export const PersonalDropdown = (): JSX.Element => {
+  const { t } = useTranslation('commons');
+  const { data: currentUser } = useCurrentUser();
+
+  const [isQuestionnaireModalOpen, setQuestionnaireModalOpen] = useState(false);
+
+  if (currentUser == null) {
+    return <div className="text-muted text-center mb-5">
+      <i className="fa fa-2x fa-spinner fa-pulse mr-1" />
+    </div>;
+  }
+
+  const logoutHandler = async() => {
+    try {
+      await apiv3Post('/logout');
+      window.location.reload();
+    }
+    catch (err) {
+      toastError(err);
+    }
+  };
+
+  return (
+    <>
+      <div className="dropend">
+        {/* Button */}
+        {/* remove .dropdown-toggle for hide caret */}
+        {/* See https://stackoverflow.com/a/44577512/13183572 */}
+        <button type="button"
+          className="btn btn-primary"
+          data-bs-toggle="dropdown" data-testid="personal-dropdown-button" aria-expanded="false"
+        >
+          <UserPicture user={currentUser} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{currentUser.name}</span>
+        </button>
+
+        {/* Menu */}
+        <div className="dropdown-menu" data-testid="personal-dropdown-menu">
+
+          <div className="px-4 pt-3 pb-2 text-center">
+            <UserPicture user={currentUser} size="lg" noLink noTooltip />
+
+            <h5 className="mt-2">
+              {currentUser.name}
+            </h5>
+
+            <div className="my-2">
+              <i className="icon-user icon-fw"></i>{currentUser.username}<br />
+              <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
+            </div>
+
+            <div className="btn-group btn-block mt-2" role="group">
+              <Link
+                href={pagePathUtils.userHomepagePath(currentUser)}
+                className="btn btn-sm btn-outline-secondary col"
+                data-testid="grw-personal-dropdown-menu-user-home"
+              >
+                <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
+              </Link>
+              <Link
+                href="/me"
+                className="btn btn-sm btn-outline-secondary col"
+                data-testid="grw-personal-dropdown-menu-user-settings"
+              >
+                <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
+              </Link>
+            </div>
+          </div>
+
+          <div className="dropdown-divider"></div>
+
+          <button
+            data-testid="grw-proactive-questionnaire-modal-toggle-btn"
+            type="button"
+            className="dropdown-item"
+            onClick={() => setQuestionnaireModalOpen(true)}>
+            <i className="icon-fw icon-pencil"></i>{t('personal_dropdown.feedback')}
+          </button>
+
+          <div className="dropdown-divider"></div>
+
+          <button type="button" className="dropdown-item" onClick={logoutHandler}>
+            <i className="icon-fw icon-power"></i>{t('Sign out')}
+          </button>
+        </div>
+      </div>
+
+      <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />
+    </>
+  );
+
+};

+ 24 - 61
apps/app/src/components/Sidebar.module.scss → apps/app/src/components/Sidebar/Sidebar.module.scss

@@ -1,21 +1,19 @@
-@use '~/styles/variables' as var;
 @use '~/styles/mixins';
 @use '~/styles/mixins';
 @use '@growi/core/scss/bootstrap/init' as bs;
 @use '@growi/core/scss/bootstrap/init' as bs;
 
 
 .grw-sidebar :global {
 .grw-sidebar :global {
   // sticky
   // sticky
   position: sticky;
   position: sticky;
-  top: var.$grw-navbar-border-width;
+  top: 0;
 
 
   // set the max value that should be taken when sticky
   // set the max value that should be taken when sticky
-  height: calc(100vh - var.$grw-navbar-border-width);
+  height: 100vh;
+
 
 
-  // override @atlaskit/navigation-next styles
-  $navbar-total-height: var.$grw-navbar-height + var.$grw-navbar-border-width;
   .data-layout-container {
   .data-layout-container {
     display: flex;
     display: flex;
     flex-direction: row;
     flex-direction: row;
-    height: calc(100vh - 0px);
+    height: 100vh;
     margin-top: 0px;
     margin-top: 0px;
     // css-teprsg
     // css-teprsg
     > div:nth-of-type(2) {
     > div:nth-of-type(2) {
@@ -38,31 +36,16 @@
           transition: width 200ms cubic-bezier(0.2, 0, 0, 1) 0s;
           transition: width 200ms cubic-bezier(0.2, 0, 0, 1) 0s;
         }
         }
         will-change: width;
         will-change: width;
+
         .grw-contextual-navigation-child {
         .grw-contextual-navigation-child {
-          position: absolute;
-          top: 0px;
-          left: 0px;
-          box-sizing: border-box;
-          width: 100%;
-          min-width: 240px;
           height: 100%;
           height: 100%;
           overflow-x: hidden;
           overflow-x: hidden;
-          transition-timing-function: cubic-bezier(0.2, 0, 0, 1);
-          transition-duration: 0.22s;
-          transition-property: boxShadow, transform;
-          animation-duration: 0.22s;
-          animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
-          animation-fill-mode: forwards;
+        }
 
 
-          :global .grw-contextual-navigation-sub {
-            box-sizing: border-box;
-            display: flex;
-            flex-direction: column;
-            width: 100%;
-            height: 100%;
-            overflow: hidden;
-          }
+        .grw-drawer-toggler {
+          display: none; // invisible in default
         }
         }
+
       }
       }
 
 
       .simplebar-mask {
       .simplebar-mask {
@@ -155,10 +138,6 @@
     }
     }
   }
   }
 
 
-  .grw-drawer-toggler {
-    display: none; // invisible in default
-  }
-
   .grw-sidebar-content-header {
   .grw-sidebar-content-header {
     .grw-btn-reload {
     .grw-btn-reload {
       font-size: 18px;
       font-size: 18px;
@@ -169,18 +148,6 @@
 
 
 // Dock Mode
 // Dock Mode
 @mixin dock() {
 @mixin dock() {
-  z-index: bs.$zindex-sticky;
-
-  // override @atlaskit/navigation-next styles
-  $navbar-total-height: var.$grw-navbar-height + var.$grw-navbar-border-width;
-  .data-layout-container {
-    max-height: calc(100vh - #{var.$grw-navbar-border-width});
-  }
-  .navigation {
-    position: unset;
-
-    top: $navbar-total-height;
-  }
 }
 }
 
 
 // Drawer Mode
 // Drawer Mode
@@ -210,8 +177,10 @@
       transform: translateX(0);
       transform: translateX(0);
     }
     }
 
 
-    .grw-drawer-toggler {
-      display: block;
+    .grw-contextual-navigation-child {
+      .grw-drawer-toggler {
+        display: block;
+      }
     }
     }
   }
   }
 
 
@@ -219,24 +188,18 @@
     display: none !important;
     display: none !important;
   }
   }
 
 
-  .grw-drawer-toggler {
-    position: fixed;
-    right: -15px;
-
-    @include bs.media-breakpoint-down(sm) {
-      bottom: 15px;
-      width: 42px;
-      height: 42px;
-      font-size: 18px;
-    }
-    @include bs.media-breakpoint-up(md) {
-      top: 72px;
-      width: 50px;
-      height: 50px;
-      font-size: 24px;
+  .grw-contextual-navigation-child {
+    .grw-drawer-toggler {
+      @include bs.media-breakpoint-down(sm) {
+        position: fixed;
+        right: -15px;
+        bottom: 15px;
+        width: 42px;
+        height: 42px;
+        font-size: 18px;
+        transform: translateX(100%);
+      }
     }
     }
-
-    transform: translateX(100%);
   }
   }
 }
 }
 
 

+ 12 - 14
apps/app/src/components/Sidebar.tsx → apps/app/src/components/Sidebar/Sidebar.tsx

@@ -14,17 +14,18 @@ import {
   useSidebarScrollerRef,
   useSidebarScrollerRef,
 } from '~/stores/ui';
 } from '~/stores/ui';
 
 
-import DrawerToggler from './Navbar/DrawerToggler';
-import { NavigationResizeHexagon } from './Sidebar/NavigationResizeHexagon';
-import { SidebarNav } from './Sidebar/SidebarNav';
-import { SidebarSkeleton } from './Sidebar/Skeleton/SidebarSkeleton';
-import { StickyStretchableScrollerProps } from './StickyStretchableScroller';
+import DrawerToggler from '../Navbar/DrawerToggler';
+import { StickyStretchableScrollerProps } from '../StickyStretchableScroller';
+
+import { NavigationResizeHexagon } from './NavigationResizeHexagon';
+import { SidebarNav } from './SidebarNav';
+import { SidebarSkeleton } from './Skeleton/SidebarSkeleton';
 
 
 import styles from './Sidebar.module.scss';
 import styles from './Sidebar.module.scss';
 
 
-const StickyStretchableScroller = dynamic<StickyStretchableScrollerProps>(() => import('./StickyStretchableScroller')
+const StickyStretchableScroller = dynamic<StickyStretchableScrollerProps>(() => import('../StickyStretchableScroller')
   .then(mod => mod.StickyStretchableScroller), { ssr: false });
   .then(mod => mod.StickyStretchableScroller), { ssr: false });
-const SidebarContents = dynamic(() => import('./Sidebar/SidebarContents')
+const SidebarContents = dynamic(() => import('./SidebarContents')
   .then(mod => mod.SidebarContents), { ssr: false, loading: () => <SidebarSkeleton /> });
   .then(mod => mod.SidebarContents), { ssr: false, loading: () => <SidebarSkeleton /> });
 
 
 const sidebarMinWidth = 240;
 const sidebarMinWidth = 240;
@@ -90,7 +91,7 @@ const SidebarContentsWrapper = memo(() => {
 SidebarContentsWrapper.displayName = 'SidebarContentsWrapper';
 SidebarContentsWrapper.displayName = 'SidebarContentsWrapper';
 
 
 
 
-const Sidebar = memo((): JSX.Element => {
+export const Sidebar = memo((): JSX.Element => {
 
 
   const { data: isDrawerMode } = useDrawerMode();
   const { data: isDrawerMode } = useDrawerMode();
   const { data: isDrawerOpened, mutate: mutateDrawerOpened } = useDrawerOpened();
   const { data: isDrawerOpened, mutate: mutateDrawerOpened } = useDrawerOpened();
@@ -315,10 +316,9 @@ const Sidebar = memo((): JSX.Element => {
                 onMouseLeave={hoverOutResizableContainerHandler}
                 onMouseLeave={hoverOutResizableContainerHandler}
                 style={{ width: isCollapsed ? sidebarMinimizeWidth : currentProductNavWidth }}
                 style={{ width: isCollapsed ? sidebarMinimizeWidth : currentProductNavWidth }}
               >
               >
-                <div className="grw-contextual-navigation-child">
-                  <div role="group" data-testid="grw-contextual-navigation-sub" className={`grw-contextual-navigation-sub ${showContents ? '' : 'd-none'}`}>
-                    <SidebarContentsWrapper></SidebarContentsWrapper>
-                  </div>
+                <div className={`grw-contextual-navigation-child ${showContents ? '' : 'd-none'}`} data-testid="grw-contextual-navigation-child">
+                  <SidebarContents />
+                  <DrawerToggler iconClass="icon-arrow-left" />
                 </div>
                 </div>
               </div>
               </div>
             </div>
             </div>
@@ -358,5 +358,3 @@ const Sidebar = memo((): JSX.Element => {
 
 
 });
 });
 Sidebar.displayName = 'Sidebar';
 Sidebar.displayName = 'Sidebar';
-
-export default Sidebar;

+ 18 - 0
apps/app/src/components/Sidebar/SidebarBrandLogo.tsx

@@ -0,0 +1,18 @@
+import { memo } from 'react';
+
+import GrowiLogo from '../Icons/GrowiLogo';
+
+type SidebarBrandLogoProps = {
+  isDefaultLogo?: boolean
+}
+
+export const SidebarBrandLogo = memo((props: SidebarBrandLogoProps) => {
+  const { isDefaultLogo } = props;
+
+  return isDefaultLogo
+    ? <GrowiLogo />
+    // eslint-disable-next-line @next/next/no-img-element
+    : (<img src='/attachment/brand-logo' alt="custom logo" className="picture picture-lg p-2 mx-2" id="settingBrandLogo" width="32" />);
+});
+
+SidebarBrandLogo.displayName = 'SidebarBrandLogo';

+ 26 - 0
apps/app/src/components/Sidebar/SidebarNav.module.scss

@@ -1,3 +1,5 @@
+@use '@growi/core/scss/bootstrap/init' as bs;
+
 @use '~/styles/variables' as var;
 @use '~/styles/variables' as var;
 
 
 .grw-sidebar-nav :global {
 .grw-sidebar-nav :global {
@@ -20,8 +22,32 @@
     }
     }
   }
   }
 
 
+  // set position and z-index to prevent dropdowns covered by other element
+  position: relative;
+  z-index: bs.$zindex-fixed;
+
   height: 100vh;
   height: 100vh;
 
 
+  .grw-logo {
+    svg {
+      width: var.$grw-logo-width;
+      height: var.$grw-logo-width;
+      padding: (var.$grw-logo-width - var.$grw-logomark-width) / 2;
+    }
+  }
+
+  .grw-apperance-mode-dropdown,
+  .grw-personal-dropdown {
+    .dropdown-menu {
+      min-width: 15rem;
+
+      .grw-icon-container svg {
+        width: 18px;
+        height: 18px;
+      }
+    }
+  }
+
   .btn {
   .btn {
     width: var.$grw-sidebar-nav-width;
     width: var.$grw-sidebar-nav-width;
     line-height: 1em;
     line-height: 1em;

+ 30 - 1
apps/app/src/components/Sidebar/SidebarNav.tsx

@@ -2,16 +2,29 @@ import React, {
   FC, memo, useCallback,
   FC, memo, useCallback,
 } from 'react';
 } from 'react';
 
 
+import dynamic from 'next/dynamic';
 import Link from 'next/link';
 import Link from 'next/link';
 
 
 import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { SidebarContentsType } from '~/interfaces/ui';
-import { useIsAdmin, useGrowiCloudUri } from '~/stores/context';
+import {
+  useIsAdmin, useGrowiCloudUri, useIsDefaultLogo, useIsGuestUser,
+} from '~/stores/context';
 import { useCurrentSidebarContents } from '~/stores/ui';
 import { useCurrentSidebarContents } from '~/stores/ui';
 
 
+import DrawerToggler from '../Navbar/DrawerToggler';
+
+import { SidebarBrandLogo } from './SidebarBrandLogo';
+
 import styles from './SidebarNav.module.scss';
 import styles from './SidebarNav.module.scss';
 
 
 
 
+const PersonalDropdown = dynamic(() => import('./PersonalDropdown').then(mod => mod.PersonalDropdown), { ssr: false });
+const InAppNotificationDropdown = dynamic(() => import('../InAppNotification/InAppNotificationDropdown')
+  .then(mod => mod.InAppNotificationDropdown), { ssr: false });
+const AppearanceModeDropdown = dynamic(() => import('./AppearanceModeDropdown').then(mod => mod.AppearanceModeDropdown), { ssr: false });
+
+
 type PrimaryItemProps = {
 type PrimaryItemProps = {
   contents: SidebarContentsType,
   contents: SidebarContentsType,
   label: string,
   label: string,
@@ -83,12 +96,24 @@ type Props = {
 
 
 export const SidebarNav: FC<Props> = (props: Props) => {
 export const SidebarNav: FC<Props> = (props: Props) => {
   const { data: isAdmin } = useIsAdmin();
   const { data: isAdmin } = useIsAdmin();
+  const { data: isGuestUser } = useIsGuestUser();
   const { data: growiCloudUri } = useGrowiCloudUri();
   const { data: growiCloudUri } = useGrowiCloudUri();
+  const { data: isDefaultLogo } = useIsDefaultLogo();
 
 
   const { onItemSelected } = props;
   const { onItemSelected } = props;
 
 
+  const isAuthenticated = isGuestUser === false;
+
   return (
   return (
     <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
     <div className={`grw-sidebar-nav ${styles['grw-sidebar-nav']}`}>
+      {/* Brand Logo  */}
+      <div className="navbar-brand">
+        <Link href="/" className="grw-logo d-block">
+          <SidebarBrandLogo isDefaultLogo={isDefaultLogo} />
+        </Link>
+        <DrawerToggler />
+      </div>
+
       <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
       <div className="grw-sidebar-nav-primary-container" data-vrt-blackout-sidebar-nav>
         {/* eslint-disable max-len */}
         {/* eslint-disable max-len */}
         <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />
         <PrimaryItem contents={SidebarContentsType.TREE} label="Page Tree" iconName="format_list_bulleted" onItemSelected={onItemSelected} />
@@ -102,6 +127,10 @@ export const SidebarNav: FC<Props> = (props: Props) => {
         <PrimaryItem contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" onItemSelected={onItemSelected} />
         <PrimaryItem contents={SidebarContentsType.BOOKMARKS} label="Bookmarks" iconName="bookmark" onItemSelected={onItemSelected} />
       </div>
       </div>
       <div className="grw-sidebar-nav-secondary-container">
       <div className="grw-sidebar-nav-secondary-container">
+        <AppearanceModeDropdown isAuthenticated={isAuthenticated} />
+        <PersonalDropdown />
+        <InAppNotificationDropdown />
+
         {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
         {isAdmin && <SecondaryItem label="Admin" iconName="settings" href="/admin" />}
         {/* <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" /> */}
         {/* <SecondaryItem label="Draft" iconName="file_copy" href="/me/drafts" /> */}
         <SecondaryItem label="Help" iconName="help" href={ growiCloudUri != null ? 'https://growi.cloud/help/' : 'https://docs.growi.org' } isBlank />
         <SecondaryItem label="Help" iconName="help" href={ growiCloudUri != null ? 'https://growi.cloud/help/' : 'https://docs.growi.org' } isBlank />

+ 1 - 0
apps/app/src/components/Sidebar/index.ts

@@ -0,0 +1 @@
+export * from './Sidebar';

+ 2 - 3
apps/app/src/components/UsersHomepageFooter.module.scss

@@ -1,5 +1,4 @@
 @use '@growi/ui/src/styles/molecules/page_list';
 @use '@growi/ui/src/styles/molecules/page_list';
-@use '~/styles/variables' as var;
 $grw-sidebar-content-header-height: 58px;
 $grw-sidebar-content-header-height: 58px;
 $grw-sidebar-content-footer-height: 50px;
 $grw-sidebar-content-footer-height: 50px;
 
 
@@ -97,11 +96,11 @@ $grw-sidebar-content-footer-height: 50px;
 }
 }
 
 
 .grw-bookarks-contents-compressed {
 .grw-bookarks-contents-compressed {
-  max-height: calc(70vh - (var.$grw-navbar-height + var.$grw-navbar-border-width + $grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
+  max-height: calc(70vh - ($grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
   overflow-y: scroll;
   overflow-y: scroll;
 }
 }
 
 
 .grw-bookarks-contents-expanded {
 .grw-bookarks-contents-expanded {
-  max-height: calc(100vh - (var.$grw-navbar-height + var.$grw-navbar-border-width + $grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
+  max-height: calc(100vh - ($grw-sidebar-content-header-height + $grw-sidebar-content-footer-height));
   overflow-y: scroll;
   overflow-y: scroll;
 }
 }

+ 0 - 7
apps/app/src/pages/[[...path]].page.tsx

@@ -53,7 +53,6 @@ import loggerFactory from '~/utils/logger';
 
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import GrowiContextualSubNavigationSubstance from '../components/Navbar/GrowiContextualSubNavigation';
 import GrowiContextualSubNavigationSubstance from '../components/Navbar/GrowiContextualSubNavigation';
-import type { GrowiSubNavigationSwitcherProps } from '../components/Navbar/GrowiSubNavigationSwitcher';
 import { DisplaySwitcher } from '../components/Page/DisplaySwitcher';
 import { DisplaySwitcher } from '../components/Page/DisplaySwitcher';
 
 
 import type { NextPageWithLayout } from './_app.page';
 import type { NextPageWithLayout } from './_app.page';
@@ -72,8 +71,6 @@ declare global {
 const GrowiPluginsActivator = dynamic(() => import('~/features/growi-plugin/client/components').then(mod => mod.GrowiPluginsActivator), { ssr: false });
 const GrowiPluginsActivator = dynamic(() => import('~/features/growi-plugin/client/components').then(mod => mod.GrowiPluginsActivator), { ssr: false });
 const DescendantsPageListModal = dynamic(() => import('../components/DescendantsPageListModal').then(mod => mod.DescendantsPageListModal), { ssr: false });
 const DescendantsPageListModal = dynamic(() => import('../components/DescendantsPageListModal').then(mod => mod.DescendantsPageListModal), { ssr: false });
 const UnsavedAlertDialog = dynamic(() => import('../components/UnsavedAlertDialog'), { ssr: false });
 const UnsavedAlertDialog = dynamic(() => import('../components/UnsavedAlertDialog'), { ssr: false });
-const GrowiSubNavigationSwitcher = dynamic<GrowiSubNavigationSwitcherProps>(() => import('../components/Navbar/GrowiSubNavigationSwitcher')
-  .then(mod => mod.GrowiSubNavigationSwitcher), { ssr: false });
 const DrawioModal = dynamic(() => import('../components/PageEditor/DrawioModal').then(mod => mod.DrawioModal), { ssr: false });
 const DrawioModal = dynamic(() => import('../components/PageEditor/DrawioModal').then(mod => mod.DrawioModal), { ssr: false });
 const HandsontableModal = dynamic(() => import('../components/PageEditor/HandsontableModal').then(mod => mod.HandsontableModal), { ssr: false });
 const HandsontableModal = dynamic(() => import('../components/PageEditor/HandsontableModal').then(mod => mod.HandsontableModal), { ssr: false });
 const TemplateModal = dynamic(() => import('../components/TemplateModal').then(mod => mod.TemplateModal), { ssr: false });
 const TemplateModal = dynamic(() => import('../components/TemplateModal').then(mod => mod.TemplateModal), { ssr: false });
@@ -339,10 +336,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
           </div>
           </div>
         </header>
         </header>
 
 
-        <div className="d-edit-none">
-          <GrowiSubNavigationSwitcher isLinkSharingDisabled={props.disableLinkSharing} />
-        </div>
-
         <div id="grw-subnav-sticky-trigger" className="sticky-top"></div>
         <div id="grw-subnav-sticky-trigger" className="sticky-top"></div>
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
 
 

+ 8 - 18
apps/app/src/styles/_editor.scss

@@ -1,7 +1,6 @@
 @use '@growi/core/scss/bootstrap/init' as bs;
 @use '@growi/core/scss/bootstrap/init' as bs;
+@use './variables' as var;
 
 
-@import './variables' ;
-@import './mixins' ;
 @import './organisms/wiki-custom-sidebar';
 @import './organisms/wiki-custom-sidebar';
 
 
 // global imported
 // global imported
@@ -15,31 +14,20 @@
 
 
   // restrict height of subnav
   // restrict height of subnav
   .grw-subnav {
   .grw-subnav {
-    height: $grw-subnav-height-on-edit;
+    height: var.$grw-subnav-height-on-edit;
     min-height: unset;
     min-height: unset;
     padding-top: 0;
     padding-top: 0;
     padding-right: 15px;
     padding-right: 15px;
     padding-left: 15px;
     padding-left: 15px;
 
 
     @include bs.media-breakpoint-up(lg) {
     @include bs.media-breakpoint-up(lg) {
-      height: $grw-subnav-height-lg-on-edit;
+      height: var.$grw-subnav-height-lg-on-edit;
     }
     }
   }
   }
 
 
   .page-wrapper {
   .page-wrapper {
-    position: relative;
-    top: $grw-navbar-border-width;
-    height: calc(100vh - #{$grw-navbar-border-width});
-  }
-
-  // calculate margin
-  $editor-margin-top: $grw-navbar-border-width + $grw-subnav-height-on-edit;
-  @include expand-editor($editor-margin-top);
-
-  @include bs.media-breakpoint-up(lg) {
-    // calculate margin
-    $editor-margin-top: $grw-navbar-border-width + $grw-subnav-height-lg-on-edit;
-    @include expand-editor($editor-margin-top);
+    top: 0;
+    height: 100vh;
   }
   }
 
 
   // show
   // show
@@ -75,7 +63,7 @@
    * Expand Editor
    * Expand Editor
    *****************/
    *****************/
   .grw-editor-navbar-bottom {
   .grw-editor-navbar-bottom {
-    height: $grw-editor-navbar-bottom-height;
+    height: var.$grw-editor-navbar-bottom-height;
 
 
     .grw-grant-selector {
     .grw-grant-selector {
       @include bs.media-breakpoint-down(sm) {
       @include bs.media-breakpoint-down(sm) {
@@ -217,11 +205,13 @@
     }
     }
 
 
     .page-editor-preview-body {
     .page-editor-preview-body {
+      flex-grow: 1;
       padding: 18px 15px 0;
       padding: 18px 15px 0;
       overflow-y: scroll;
       overflow-y: scroll;
     }
     }
     // editing /Sidebar
     // editing /Sidebar
     .page-editor-preview-body.preview-sidebar {
     .page-editor-preview-body.preview-sidebar {
+      flex-grow: 0;
       width: 320px;
       width: 320px;
       margin-right: auto;
       margin-right: auto;
       margin-left: auto;
       margin-left: auto;

+ 1 - 46
apps/app/src/styles/_mixins.scss

@@ -1,4 +1,5 @@
 @use '@growi/core/scss/bootstrap/init' as bs;
 @use '@growi/core/scss/bootstrap/init' as bs;
+@use './variables' as var;
 
 
 @mixin variable-font-size($basesize) {
 @mixin variable-font-size($basesize) {
   font-size: $basesize * 0.6;
   font-size: $basesize * 0.6;
@@ -17,52 +18,6 @@
   }
   }
 }
 }
 
 
-@mixin expand-editor($editor-margin-top) {
-  $header-plus-footer: $editor-margin-top + $grw-editor-navbar-bottom-height;
-
-  $editor-margin: $header-plus-footer //
-    + 25px //   add .btn-open-dropzone height
-    + 30px; //  add .navbar-editor height
-
-  .editor-root {
-    width: 100%;
-    height: calc(100vh - #{$header-plus-footer});
-    min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-    margin-top: 0px !important;
-
-    // left(editor)
-    .page-editor-editor-container {
-      height: calc(100vh - #{$header-plus-footer});
-      min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-
-      .react-codemirror2,
-      .CodeMirror,
-      .CodeMirror-scroll,
-      .textarea-editor {
-        height: calc(100vh - #{$editor-margin});
-      }
-    }
-
-    // right(preview)
-    .page-editor-preview-container,
-    .page-editor-preview-body {
-      height: calc(100vh - #{$header-plus-footer});
-      min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-    }
-  }
-
-  .editor-root#page-editor-with-hackmd {
-    &,
-    .hackmd-preinit,
-    .hackmd-error,
-    #iframe-hackmd-container > iframe {
-      width: 100%;
-      height: calc(100vh - #{$header-plus-footer});
-      min-height: calc(100vh - #{$header-plus-footer}); // for IE11
-    }
-  }
-}
-
 @mixin apply-navigation-transition() {
 @mixin apply-navigation-transition() {
   transition-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
   transition-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
   transition-duration: 300ms;
   transition-duration: 300ms;

+ 0 - 12
apps/app/src/styles/_variables.scss

@@ -10,12 +10,6 @@ $grw-marker-cyan: #6ff;
 $grw-marker-green: #6f6;
 $grw-marker-green: #6f6;
 
 
 //== Layout
 //== Layout
-$grw-navbar-height: 52px;
-$grw-navbar-border-width: 3.3333px;
-// slightly larger than $zindex-sticky
-// https://getbootstrap.jp/docs/4.6/layout/overview/#z-index
-$grw-navbar-z-index: 1025;
-
 $grw-subnav-min-height: 95px;
 $grw-subnav-min-height: 95px;
 $grw-subnav-min-height-md: 115px;
 $grw-subnav-min-height-md: 115px;
 $grw-subnav-height-on-edit: 95px;
 $grw-subnav-height-on-edit: 95px;
@@ -31,10 +25,4 @@ $grw-sidebar-nav-width: 64px; // !!DO NOT CHANGE!! 'margin-left' for '.css-teprs
 $grw-logo-width: $grw-sidebar-nav-width;
 $grw-logo-width: $grw-sidebar-nav-width;
 $grw-logomark-width: 36px;
 $grw-logomark-width: 36px;
 
 
-// fix tab width to 95 pixels
-// see also '_editor.scss'
-$grw-nav-main-left-tab-width: 95px;
-$grw-nav-main-left-tab-width-mobile: 50px;
-$grw-nav-main-tab-height: 42px;
-
 $grw-scroll-margin-top-in-view: 130px;
 $grw-scroll-margin-top-in-view: 130px;

+ 0 - 12
apps/app/src/styles/theme/apply-colors.scss

@@ -665,18 +665,6 @@ mark.rbt-highlight-text {
   background-color: var(--bgcolor-global);
   background-color: var(--bgcolor-global);
 }
 }
 
 
-.grw-fab {
-  .btn-create-page {
-    svg {
-      fill: hsl.contrast(var(--primary));
-    }
-  }
-
-  .btn-scroll-to-top {
-    fill: $gray-900;
-  }
-}
-
 /*
 /*
   Slack Integration
   Slack Integration
 */
 */

+ 2 - 5
apps/app/test/cypress/e2e/20-basic-features/20-basic-features--access-to-page.cy.ts

@@ -42,9 +42,6 @@ context('Access to page', () => {
     // https://redmine.weseek.co.jp/issues/111384
     // https://redmine.weseek.co.jp/issues/111384
     // cy.get('.toc-link').should('be.visible');
     // cy.get('.toc-link').should('be.visible');
 
 
-    // hide fab
-    cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
-
     // assert the element is in viewport
     // assert the element is in viewport
     cy.get('#headers').should('be.inViewport');
     cy.get('#headers').should('be.inViewport');
 
 
@@ -212,7 +209,7 @@ context('Access to Template Editing Mode', () => {
 
 
     // Open sidebar
     // Open sidebar
     cy.collapseSidebar(false);
     cy.collapseSidebar(false);
-    cy.getByTestid('grw-contextual-navigation-sub').should('be.visible');
+    cy.getByTestid('grw-contextual-navigation-child').should('be.visible');
     cy.waitUntilSkeletonDisappear();
     cy.waitUntilSkeletonDisappear();
 
 
     // If PageTree is not active when the sidebar is opened, make it active
     // If PageTree is not active when the sidebar is opened, make it active
@@ -224,7 +221,7 @@ context('Access to Template Editing Mode', () => {
       });
       });
 
 
     // Create page (/{parentPath}}/{newPagePath}) from PageTree
     // Create page (/{parentPath}}/{newPagePath}) from PageTree
-    cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+    cy.getByTestid('grw-contextual-navigation-child').within(() => {
       cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
       cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
         cy.get('#page-create-button-in-page-tree').first().click({force: true})
         cy.get('#page-create-button-in-page-tree').first().click({force: true})
       });
       });

+ 3 - 49
apps/app/test/cypress/e2e/20-basic-features/20-basic-features--sticky-features.cy.ts

@@ -1,5 +1,5 @@
 context('Access to any page', () => {
 context('Access to any page', () => {
-  const ssPrefix = 'subnav-and-fab-';
+  const ssPrefix = 'subnav-';
 
 
   beforeEach(() => {
   beforeEach(() => {
     // login
     // login
@@ -13,7 +13,7 @@ context('Access to any page', () => {
     cy.collapseSidebar(true, true);
     cy.collapseSidebar(true, true);
   });
   });
 
 
-  it('Subnavigation and fab displays changes on scroll down and up', () => {
+  it('Subnavigation displays changes on scroll down and up', () => {
     cy.waitUntil(() => {
     cy.waitUntil(() => {
       // do
       // do
       // Scroll the window 250px down is enough to trigger sticky effect
       // Scroll the window 250px down is enough to trigger sticky effect
@@ -21,8 +21,6 @@ context('Access to any page', () => {
       // wait until
       // wait until
       return cy.getByTestid('grw-subnav-switcher').then($elem => !$elem.hasClass('grw-subnav-switcher-hidden'));
       return cy.getByTestid('grw-subnav-switcher').then($elem => !$elem.hasClass('grw-subnav-switcher-hidden'));
     });
     });
-    // wait until fab visible
-    cy.waitUntil(() => cy.getByTestid('grw-fab-page-create-button').then($elem => $elem.hasClass('visible')));
 
 
     cy.waitUntilSkeletonDisappear();
     cy.waitUntilSkeletonDisappear();
     cy.screenshot(`${ssPrefix}visible-on-scroll-down`);
     cy.screenshot(`${ssPrefix}visible-on-scroll-down`);
@@ -34,13 +32,11 @@ context('Access to any page', () => {
       // wait until
       // wait until
       return cy.waitUntil(() => cy.getByTestid('grw-subnav-switcher').then($elem => $elem.hasClass('grw-subnav-switcher-hidden')));
       return cy.waitUntil(() => cy.getByTestid('grw-subnav-switcher').then($elem => $elem.hasClass('grw-subnav-switcher-hidden')));
     });
     });
-    // wait until fab invisible
-    cy.waitUntil(() => cy.getByTestid('grw-fab-page-create-button').then($elem => $elem.hasClass('invisible')));
 
 
     cy.screenshot(`${ssPrefix}invisible-on-scroll-top`);
     cy.screenshot(`${ssPrefix}invisible-on-scroll-top`);
   });
   });
 
 
-  it('Subnavigation and fab are not displayed when move to other pages', () => {
+  it('Subnavigation is not displayed when move to other pages', () => {
     cy.waitUntil(() => {
     cy.waitUntil(() => {
       // do
       // do
       // Scroll the window 250px down is enough to trigger sticky effect
       // Scroll the window 250px down is enough to trigger sticky effect
@@ -48,7 +44,6 @@ context('Access to any page', () => {
       // wait until
       // wait until
       return () => cy.getByTestid('grw-subnav-switcher').then($elem => !$elem.hasClass('grw-subnav-switcher-hidden'));
       return () => cy.getByTestid('grw-subnav-switcher').then($elem => !$elem.hasClass('grw-subnav-switcher-hidden'));
     });
     });
-    cy.waitUntil(() => cy.getByTestid('grw-fab-page-create-button').then($elem => $elem.hasClass('visible')));
 
 
     // Move to /Sandbox page
     // Move to /Sandbox page
     cy.visit('/Sandbox');
     cy.visit('/Sandbox');
@@ -56,51 +51,10 @@ context('Access to any page', () => {
     cy.waitUntilSkeletonDisappear();
     cy.waitUntilSkeletonDisappear();
     cy.collapseSidebar(true);
     cy.collapseSidebar(true);
 
 
-    cy.waitUntil(() => cy.getByTestid('grw-fab-page-create-button').then($elem => $elem.hasClass('invisible')));
     cy.waitUntil(() => cy.getByTestid('grw-subnav-switcher').then($elem => $elem.hasClass('grw-subnav-switcher-hidden')));
     cy.waitUntil(() => cy.getByTestid('grw-subnav-switcher').then($elem => $elem.hasClass('grw-subnav-switcher-hidden')));
     cy.screenshot(`${ssPrefix}not-visible-on-move-to-other-pages`);
     cy.screenshot(`${ssPrefix}not-visible-on-move-to-other-pages`);
   });
   });
 
 
-  it('Able to open create page modal from fab', () => {
-    cy.waitUntil(() => {
-      // do
-      // Scroll the window back to top
-      cy.scrollTo(0, 250);
-      // wait until
-      return cy.getByTestid('grw-fab-page-create-button')
-      .should('have.class', 'visible')
-      .within(() => {
-        cy.get('.btn-create-page').click();
-        return true;
-      });
-    });
-
-    cy.getByTestid('page-create-modal').should('be.visible').within(() => {
-      cy.screenshot(`${ssPrefix}new-page-modal-opened-from-fab`);
-      cy.get('button.close').click();
-    });
-  });
-
-  it('Able to scroll page to top from fab', () => {
-    // Initial scroll down
-    cy.waitUntil(() => {
-      // do
-      // Scroll the window 250px down is enough to trigger sticky effect
-      cy.scrollTo(0, 250);
-      // wait until
-      return cy.getByTestid('grw-fab-return-to-top')
-        .should('have.class', 'visible')
-        .then(() => {
-          cy.waitUntil(() => {
-            cy.get('.btn-scroll-to-top').click();
-            return cy.getByTestid('grw-fab-return-to-top').should('have.class', 'invisible');
-          });
-        });
-    });
-    cy.waitUntilSkeletonDisappear();
-    cy.screenshot(`${ssPrefix}scroll-page-to-top`);
-  });
-
   it('Able to click buttons on subnavigation switcher when sticky', () => {
   it('Able to click buttons on subnavigation switcher when sticky', () => {
     cy.waitUntil(() => {
     cy.waitUntil(() => {
       // do
       // do

+ 0 - 3
apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.cy.ts

@@ -14,9 +14,6 @@ context('Access to page by guest', () => {
     cy.visit('/Sandbox#headers');
     cy.visit('/Sandbox#headers');
     cy.collapseSidebar(true);
     cy.collapseSidebar(true);
 
 
-    // hide fab
-    cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
-
     // assert the element is in viewport
     // assert the element is in viewport
     cy.get('#headers').should('be.inViewport');
     cy.get('#headers').should('be.inViewport');
 
 

+ 2 - 30
apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--sticky-for-guest.cy.ts

@@ -1,5 +1,6 @@
-context('Access sticky sub navigation switcher and Fab for guest', () => {
+context('Access sticky sub navigation switcher for guest', () => {
   const ssPrefix = 'access-sticky-by-guest-';
   const ssPrefix = 'access-sticky-by-guest-';
+
   it('Sub navigation sticky changes when scrolling down and up', () => {
   it('Sub navigation sticky changes when scrolling down and up', () => {
     cy.visit('/Sandbox');
     cy.visit('/Sandbox');
     cy.waitUntilSkeletonDisappear();
     cy.waitUntilSkeletonDisappear();
@@ -26,33 +27,4 @@ context('Access sticky sub navigation switcher and Fab for guest', () => {
     cy.screenshot(`${ssPrefix}subnav-switcher-is-not-sticky-on-scroll-top`);
     cy.screenshot(`${ssPrefix}subnav-switcher-is-not-sticky-on-scroll-top`);
   });
   });
 
 
-  it('Fab display changes when scrolling down and up', () => {
-    cy.visit('/Sandbox');
-    cy.waitUntilSkeletonDisappear();
-    cy.collapseSidebar(true, true);
-
-    // Visible
-    cy.waitUntil(() => {
-      // do
-      // Scroll the window 250px down is enough to trigger sticky effect
-       cy.scrollTo(0, 250);
-
-      // wait until
-      return cy.getByTestid('grw-fab-return-to-top').then($elem => $elem.hasClass('visible'));
-
-    });
-    cy.screenshot(`${ssPrefix}fab-is-visible-on-scroll-down`);
-
-    // Invisible
-    cy.waitUntil(() => {
-      // do
-      // Scroll page to top
-       cy.scrollTo(0, 0);
-
-       // wait until
-      return cy.getByTestid('grw-fab-return-to-top').then($elem => $elem.hasClass('invisible'));
-    });
-    cy.screenshot(`${ssPrefix}fab-is-invisible-on-scroll-top`);
-
-  });
 });
 });

+ 11 - 11
apps/app/test/cypress/e2e/50-sidebar/50-sidebar--access-to-side-bar.cy.ts

@@ -27,7 +27,7 @@ describe('Access to sidebar', () => {
 
 
       describe('Test show/collapse button', () => {
       describe('Test show/collapse button', () => {
         it('Successfully show sidebar', () => {
         it('Successfully show sidebar', () => {
-          cy.getByTestid('grw-contextual-navigation-sub').should('be.visible');
+          cy.getByTestid('grw-contextual-navigation-child').should('be.visible');
 
 
           cy.waitUntilSkeletonDisappear();
           cy.waitUntilSkeletonDisappear();
           cy.screenshot(`${ssPrefix}1-sidebar-shown`, {
           cy.screenshot(`${ssPrefix}1-sidebar-shown`, {
@@ -39,7 +39,7 @@ describe('Access to sidebar', () => {
         it('Successfully collapse sidebar', () => {
         it('Successfully collapse sidebar', () => {
           cy.getByTestid('grw-navigation-resize-button').click({force: true});
           cy.getByTestid('grw-navigation-resize-button').click({force: true});
 
 
-          cy.getByTestid('grw-contextual-navigation-sub').should('not.be.visible');
+          cy.getByTestid('grw-contextual-navigation-child').should('not.be.visible');
 
 
           cy.waitUntilSkeletonDisappear();
           cy.waitUntilSkeletonDisappear();
           cy.screenshot(`${ssPrefix}2-sidebar-collapsed`, {
           cy.screenshot(`${ssPrefix}2-sidebar-collapsed`, {
@@ -61,7 +61,7 @@ describe('Access to sidebar', () => {
         });
         });
 
 
         it('Successfully access to page tree', () => {
         it('Successfully access to page tree', () => {
-          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+          cy.getByTestid('grw-contextual-navigation-child').within(() => {
             cy.getByTestid('grw-pagetree-item-container').should('be.visible');
             cy.getByTestid('grw-pagetree-item-container').should('be.visible');
 
 
             cy.waitUntilSkeletonDisappear();
             cy.waitUntilSkeletonDisappear();
@@ -70,7 +70,7 @@ describe('Access to sidebar', () => {
         });
         });
 
 
         it('Successfully hide page tree items', () => {
         it('Successfully hide page tree items', () => {
-          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+          cy.getByTestid('grw-contextual-navigation-child').within(() => {
             cy.get('.grw-pagetree-open').should('be.visible');
             cy.get('.grw-pagetree-open').should('be.visible');
 
 
             // hide page tree tiems
             // hide page tree tiems
@@ -83,7 +83,7 @@ describe('Access to sidebar', () => {
         it('Successfully click Add to Bookmarks button', () => {
         it('Successfully click Add to Bookmarks button', () => {
           cy.waitUntil(() => {
           cy.waitUntil(() => {
             // do
             // do
-            cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-contextual-navigation-child').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
               });
               });
@@ -101,7 +101,7 @@ describe('Access to sidebar', () => {
           // show dropdown again
           // show dropdown again
           cy.waitUntil(() => {
           cy.waitUntil(() => {
             // do
             // do
-            cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-contextual-navigation-child').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
               });
               });
@@ -116,7 +116,7 @@ describe('Access to sidebar', () => {
         it('Successfully show duplicate page modal', () => {
         it('Successfully show duplicate page modal', () => {
           cy.waitUntil(() => {
           cy.waitUntil(() => {
             // do
             // do
-            cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-contextual-navigation-child').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
               });
               });
@@ -141,7 +141,7 @@ describe('Access to sidebar', () => {
         it('Successfully rename page', () => {
         it('Successfully rename page', () => {
           cy.waitUntil(() => {
           cy.waitUntil(() => {
             // do
             // do
-            cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-contextual-navigation-child').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
               });
               });
@@ -164,7 +164,7 @@ describe('Access to sidebar', () => {
         it('Successfully show delete page modal', () => {
         it('Successfully show delete page modal', () => {
           cy.waitUntil(() => {
           cy.waitUntil(() => {
             // do
             // do
-            cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+            cy.getByTestid('grw-contextual-navigation-child').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
               cy.get('.grw-pagetree-item-children').first().as('pagetreeItem').within(() => {
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
                 cy.getByTestid('open-page-item-control-btn').find('button').first().invoke('css','display','block').click()
               });
               });
@@ -196,7 +196,7 @@ describe('Access to sidebar', () => {
         });
         });
 
 
         it('Successfully access to custom sidebar', () => {
         it('Successfully access to custom sidebar', () => {
-          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+          cy.getByTestid('grw-contextual-navigation-child').within(() => {
             cy.get('.grw-sidebar-content-header > h3').find('a');
             cy.get('.grw-sidebar-content-header > h3').find('a');
 
 
             cy.waitUntilSkeletonDisappear();
             cy.waitUntilSkeletonDisappear();
@@ -269,7 +269,7 @@ describe('Access to sidebar', () => {
         });
         });
 
 
         it('Successfully access to tags', () => {
         it('Successfully access to tags', () => {
-          cy.getByTestid('grw-contextual-navigation-sub').within(() => {
+          cy.getByTestid('grw-contextual-navigation-child').within(() => {
             cy.getByTestid('grw-tags-list').should('be.visible');
             cy.getByTestid('grw-tags-list').should('be.visible');
 
 
             cy.screenshot(`${ssPrefix}tags-1-access-to-tags`, { blackout: blackoutOverride });
             cy.screenshot(`${ssPrefix}tags-1-access-to-tags`, { blackout: blackoutOverride });

+ 0 - 2
apps/app/test/cypress/e2e/60-home/60-home--home.cy.ts

@@ -46,8 +46,6 @@ context('Access User settings', () => {
     });
     });
     cy.visit('/me');
     cy.visit('/me');
     cy.collapseSidebar(true, true);
     cy.collapseSidebar(true, true);
-    // hide fab
-    cy.getByTestid('grw-fab-container').invoke('attr', 'style', 'display: none');
   });
   });
 
 
   it('Access User information', () => {
   it('Access User information', () => {

+ 2 - 2
apps/app/test/cypress/support/commands.ts

@@ -81,7 +81,7 @@ Cypress.Commands.add('collapseSidebar', (isCollapsed: boolean, waitUntilSaving =
 
 
   cy.getByTestid('grw-sidebar').should('be.visible').within(() => {
   cy.getByTestid('grw-sidebar').should('be.visible').within(() => {
 
 
-    const isSidebarContextualNavigationHidden = isHiddenByTestId('grw-contextual-navigation-sub');
+    const isSidebarContextualNavigationHidden = isHiddenByTestId('grw-contextual-navigation-child');
     if (isSidebarContextualNavigationHidden === isCollapsed) {
     if (isSidebarContextualNavigationHidden === isCollapsed) {
       return;
       return;
     }
     }
@@ -96,7 +96,7 @@ Cypress.Commands.add('collapseSidebar', (isCollapsed: boolean, waitUntilSaving =
       }
       }
 
 
       // wait until
       // wait until
-      return cy.getByTestid('grw-contextual-navigation-sub').then($contents => isHidden($contents) === isCollapsed);
+      return cy.getByTestid('grw-contextual-navigation-child').then($contents => isHidden($contents) === isCollapsed);
     });
     });
   });
   });
 
 

+ 2 - 2
apps/slackbot-proxy/package.json

@@ -7,8 +7,8 @@
     "clean": "shx rm -rf dist",
     "clean": "shx rm -rf dist",
     "cp:public": "cp -RT ./src/public ./dist/public",
     "cp:public": "cp -RT ./src/public ./dist/public",
     "cp:views": "cp -RT ./src/views ./dist/views",
     "cp:views": "cp -RT ./src/views ./dist/views",
-    "cp:bootstrap": "cp -RT ./node_modules/bootstrap/dist ./dist/public/bootstrap",
-    "cp:bootstrap:dev": "cp -RT ./node_modules/bootstrap/dist ./src/public/bootstrap",
+    "cp:bootstrap": "cp -RT ../../node_modules/bootstrap/dist ./dist/public/bootstrap",
+    "cp:bootstrap:dev": "cp -RT ../../node_modules/bootstrap/dist ./src/public/bootstrap",
     "tsc": "tsc -p tsconfig.build.json",
     "tsc": "tsc -p tsconfig.build.json",
     "tsc:w": "yarn tsc -w",
     "tsc:w": "yarn tsc -w",
     "dev:ci": "yarn dev --ci",
     "dev:ci": "yarn dev --ci",

+ 1 - 1
package.json

@@ -91,7 +91,7 @@
     "tsconfig-paths": "^3.9.0",
     "tsconfig-paths": "^3.9.0",
     "typescript": "~5.0.0",
     "typescript": "~5.0.0",
     "vite": "^4.4.0",
     "vite": "^4.4.0",
-    "vite-plugin-dts": "^2.3.0",
+    "vite-plugin-dts": "^3.3.1",
     "vite-tsconfig-paths": "^4.2.0",
     "vite-tsconfig-paths": "^4.2.0",
     "vitest": "^0.31.4",
     "vitest": "^0.31.4",
     "vitest-mock-extended": "^1.1.3"
     "vitest-mock-extended": "^1.1.3"

+ 2 - 0
packages/editor/.eslintignore

@@ -0,0 +1,2 @@
+/dist/**
+vite-env.d.ts

+ 13 - 0
packages/editor/.eslintrc.cjs

@@ -0,0 +1,13 @@
+module.exports = {
+  env: { browser: true, es2020: true },
+  extends: [
+    'weseek/react',
+  ],
+  plugins: ['react-refresh'],
+  rules: {
+    'react-refresh/only-export-components': [
+      'warn',
+      { allowConstantExport: true },
+    ],
+  },
+};

+ 24 - 0
packages/editor/.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 14 - 0
packages/editor/README.md

@@ -0,0 +1,14 @@
+# `@growi/editor`
+
+## Development with vite
+
+```bash
+cd packages/editor
+yarn serve
+```
+
+## Playground
+
+If you need to simulate something `@growi/app` feature, you can implement it into the playground.  
+See the `components/playground` directory.
+

+ 13 - 0
packages/editor/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + React + TS</title>
+  </head>
+  <body>
+    <div id="root" class="d-flex flex-column vw-100 vh-100"></div>
+    <script type="module" src="/src/main.tsx"></script>
+  </body>
+</html>

+ 37 - 0
packages/editor/package.json

@@ -0,0 +1,37 @@
+{
+  "name": "@growi/editor",
+  "version": "6.2.0-RC.0",
+  "license": "MIT",
+  "type": "module",
+  "module": "dist/index.js",
+  "types": "dist/index.d.ts",
+  "scripts": {
+    "build": "tsc && vite build",
+    "clean": "shx rm -rf dist",
+    "dev": "vite build --mode dev",
+    "watch": "yarn dev -w --emptyOutDir=false",
+    "serve": "vite",
+    "lint:js": "yarn eslint **/*.{js,ts}",
+    "lint:typecheck": "tsc",
+    "lint": "npm-run-all -p lint:*",
+    "version": "yarn version --no-git-tag-version --preid=RC"
+  },
+  "dependencies": {
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  },
+  "devDependencies": {
+    "@codemirror/lang-markdown": "^6.2.0",
+    "@codemirror/language-data": "^6.3.1",
+    "@codemirror/state": "^6.2.1",
+    "@codemirror/view": "^6.15.3",
+    "@popperjs/core": "^2.11.8",
+    "@types/react": "^18.2.14",
+    "@types/react-dom": "^18.2.6",
+    "@uiw/react-codemirror": "^4.21.8",
+    "bootstrap": "^5.3.1",
+    "codemirror": "^6.0.1",
+    "eslint-plugin-react-refresh": "^0.4.1",
+    "swr": "^2.0.3"
+  }
+}

+ 2 - 0
packages/editor/src/@types/declaration.d.ts

@@ -0,0 +1,2 @@
+// prevent TS2307: Cannot find module './xxx.module.scss' or its corresponding type declarations.
+declare module '*.scss';

+ 13 - 0
packages/editor/src/components/CodeMirrorEditorContainer.module.scss

@@ -0,0 +1,13 @@
+.codemirror-editor-container :global {
+
+  display: flex;
+  align-items: stretch;
+  width: 100%;
+  height: 100%;
+
+  .cm-editor {
+    width: 100%;
+    height: 100%;
+  }
+
+}

+ 9 - 0
packages/editor/src/components/CodeMirrorEditorContainer.tsx

@@ -0,0 +1,9 @@
+import { forwardRef } from 'react';
+
+import style from './CodeMirrorEditorContainer.module.scss';
+
+export const CodeMirrorEditorContainer = forwardRef<HTMLDivElement>((props, ref) => {
+  return (
+    <div {...props} className={`${style['codemirror-editor-container']}`} ref={ref} />
+  );
+});

+ 1 - 0
packages/editor/src/components/index.ts

@@ -0,0 +1 @@
+export * from './CodeMirrorEditorContainer';

+ 34 - 0
packages/editor/src/components/playground/Playground.tsx

@@ -0,0 +1,34 @@
+import { useRef } from 'react';
+
+import { CodeMirrorEditorContainer } from '..';
+import { useCodeMirrorEditorMain } from '../../stores';
+
+import { PlaygroundController } from './PlaygroundController';
+
+export const Playground = (): JSX.Element => {
+
+  const containerRef = useRef(null);
+
+  useCodeMirrorEditorMain({
+    container: containerRef.current,
+  });
+
+  return (
+    <>
+      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center bg-dark" style={{ minHeight: '83px' }}>
+        <div className="text-white">GrowiSubNavigation</div>
+      </div>
+      <div className="flex-grow-1 d-flex overflow-y-auto">
+        <div className="flex-grow-1 d-flex flex-column" style={{ flexBasis: 0 }}>
+          <CodeMirrorEditorContainer ref={containerRef} />
+        </div>
+        <div className="flex-grow-1 mw-0 d-flex flex-column bg-light border-start border-dark-subtle p-3" style={{ flexBasis: 0 }}>
+          <PlaygroundController />
+        </div>
+      </div>
+      <div className="flex-grow-1 d-flex flex-column justify-content-center align-items-center bg-dark" style={{ minHeight: '50px' }}>
+        <div className="text-white">EditorNavbarBottom</div>
+      </div>
+    </>
+  );
+};

+ 39 - 0
packages/editor/src/components/playground/PlaygroundController.tsx

@@ -0,0 +1,39 @@
+import { useCallback } from 'react';
+
+import { useCodeMirrorEditorMain } from '../../stores';
+
+export const PlaygroundController = (): JSX.Element => {
+
+  const { data: states } = useCodeMirrorEditorMain();
+
+  const initEditorValue = useCallback(() => {
+    if (states == null) {
+      return;
+    }
+
+    states.view?.dispatch({
+      changes: {
+        from: 0,
+        to: states.view.state.doc.toString().length,
+        insert: '# Header\n\n- foo\n-bar\n',
+      },
+    });
+
+  }, [states]);
+
+  return (
+    <>
+      <div className="row">
+        <div className="column">
+          <button
+            type="button"
+            className="btn btn-outline-secondary"
+            onClick={() => initEditorValue()}
+          >
+            Initialize editor value
+          </button>
+        </div>
+      </div>
+    </>
+  );
+};

+ 1 - 0
packages/editor/src/components/playground/index.ts

@@ -0,0 +1 @@
+export * from './Playground';

+ 2 - 0
packages/editor/src/index.ts

@@ -0,0 +1,2 @@
+export * from './components';
+export * from './services';

+ 1 - 0
packages/editor/src/main.scss

@@ -0,0 +1 @@
+@import 'bootstrap';

+ 17 - 0
packages/editor/src/main.tsx

@@ -0,0 +1,17 @@
+import React from 'react';
+
+import ReactDOM from 'react-dom/client';
+
+import { Playground } from './components/playground';
+
+import './main.scss';
+
+
+const rootElem = document.getElementById('root');
+
+// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ReactDOM.createRoot(rootElem!).render(
+  <React.StrictMode>
+    <Playground />
+  </React.StrictMode>,
+);

+ 42 - 0
packages/editor/src/services/codemirror-editor.ts

@@ -0,0 +1,42 @@
+import { useEffect } from 'react';
+
+import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
+import { languages } from '@codemirror/language-data';
+import { EditorState, Extension } from '@codemirror/state';
+import { EditorView, scrollPastEnd } from '@codemirror/view';
+import { useCodeMirror, type UseCodeMirror } from '@uiw/react-codemirror';
+
+
+export type UseCodeMirrorEditor = UseCodeMirror;
+
+export type UseCodeMirrorEditorStates = {
+  state: EditorState | undefined;
+  setState: import('react').Dispatch<import('react').SetStateAction<EditorState | undefined>>;
+  view: EditorView | undefined;
+  setView: import('react').Dispatch<import('react').SetStateAction<EditorView | undefined>>;
+  container: HTMLDivElement | undefined;
+  setContainer: import('react').Dispatch<import('react').SetStateAction<HTMLDivElement | undefined>>;
+}
+
+const defaultExtensions: Extension[] = [
+  markdown({ base: markdownLanguage, codeLanguages: languages }),
+  scrollPastEnd(),
+];
+
+export const useCodeMirrorEditor = (props?: UseCodeMirrorEditor): UseCodeMirrorEditorStates => {
+
+  const codemirror = useCodeMirror({
+    extensions: defaultExtensions,
+    ...props,
+  });
+
+  const { setContainer } = codemirror;
+
+  useEffect(() => {
+    if (props?.container != null) {
+      setContainer(props.container);
+    }
+  }, [props?.container, setContainer]);
+
+  return codemirror;
+};

+ 1 - 0
packages/editor/src/services/index.ts

@@ -0,0 +1 @@
+export * from './codemirror-editor';

+ 12 - 0
packages/editor/src/stores/codemirror-editor.ts

@@ -0,0 +1,12 @@
+import type { SWRResponse } from 'swr';
+
+
+import type { UseCodeMirrorEditor, UseCodeMirrorEditorStates } from '../services';
+import { useCodeMirrorEditor } from '../services';
+
+import { useStaticSWR } from './use-static-swr';
+
+export const useCodeMirrorEditorMain = (props?: UseCodeMirrorEditor): SWRResponse<UseCodeMirrorEditorStates> => {
+  const states = useCodeMirrorEditor(props);
+  return useStaticSWR('codeMirrorEditorMain', props != null ? states : undefined);
+};

+ 1 - 0
packages/editor/src/stores/index.ts

@@ -0,0 +1 @@
+export * from './codemirror-editor';

+ 33 - 0
packages/editor/src/stores/use-static-swr.tsx

@@ -0,0 +1,33 @@
+import {
+  Key, SWRConfiguration, SWRResponse, useSWRConfig,
+} from 'swr';
+import useSWRImmutable from 'swr/immutable';
+
+
+export function useStaticSWR<Data, Error>(key: Key): SWRResponse<Data, Error>;
+export function useStaticSWR<Data, Error>(key: Key, data: Data | undefined): SWRResponse<Data, Error>;
+export function useStaticSWR<Data, Error>(key: Key, data: Data | undefined,
+  configuration: SWRConfiguration<Data, Error> | undefined): SWRResponse<Data, Error>;
+
+export function useStaticSWR<Data, Error>(
+    ...args: readonly [Key]
+    | readonly [Key, Data | undefined]
+    | readonly [Key, Data | undefined, SWRConfiguration<Data, Error> | undefined]
+): SWRResponse<Data, Error> {
+  const [key, data, configuration] = args;
+
+  // assert.notStrictEqual(configuration?.fetcher, null, 'useStaticSWR does not support \'configuration.fetcher\'');
+
+  const { cache } = useSWRConfig();
+  const swrResponse = useSWRImmutable(key, null, {
+    ...configuration,
+    fallbackData: configuration?.fallbackData ?? cache.get(key)?.data,
+  });
+
+  // write data to cache directly
+  if (data !== undefined) {
+    cache.set(key, { ...cache.get(key), data });
+  }
+
+  return swrResponse;
+}

+ 1 - 0
packages/editor/src/vite-env.d.ts

@@ -0,0 +1 @@
+/// <reference types="vite/client" />

+ 31 - 0
packages/editor/tsconfig.json

@@ -0,0 +1,31 @@
+{
+  "$schema": "http://json.schemastore.org/tsconfig",
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "module": "ESNext",
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react-jsx",
+
+    /* Linting */
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+
+    "baseUrl": ".",
+    "paths": {
+      "/*": ["./public/*"]
+    }
+  },
+  "include": ["src"],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}

+ 11 - 0
packages/editor/tsconfig.node.json

@@ -0,0 +1,11 @@
+{
+  "$schema": "http://json.schemastore.org/tsconfig",
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 55 - 0
packages/editor/vite.config.ts

@@ -0,0 +1,55 @@
+import path from 'path';
+
+import react from '@vitejs/plugin-react';
+import glob from 'glob';
+import { nodeExternals } from 'rollup-plugin-node-externals';
+import { defineConfig } from 'vite';
+import dts from 'vite-plugin-dts';
+
+
+const excludeFiles = [
+  '**/@types/*',
+  '**/components/playground/*',
+  '**/main.tsx',
+  '**/vite-env.d.ts',
+];
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    react(),
+    dts({
+      exclude: [
+        ...excludeFiles,
+      ],
+      copyDtsFiles: true,
+    }),
+    {
+      ...nodeExternals({
+        devDeps: true,
+        builtinsPrefix: 'ignore',
+      }),
+      enforce: 'pre',
+    },
+  ],
+  build: {
+    outDir: 'dist',
+    sourcemap: true,
+    lib: {
+      entry: glob.sync(path.resolve(__dirname, 'src/**/*.{ts,tsx}'), {
+        ignore: [
+          ...excludeFiles,
+          '**/*.spec.ts',
+        ],
+      }),
+      name: 'editor-libs',
+      formats: ['es'],
+    },
+    rollupOptions: {
+      output: {
+        preserveModules: true,
+        preserveModulesRoot: 'src',
+      },
+    },
+  },
+});

+ 3 - 0
packages/pluginkit/package.json

@@ -6,6 +6,9 @@
   "module": "dist/index.js",
   "module": "dist/index.js",
   "types": "dist/index.d.ts",
   "types": "dist/index.d.ts",
   "type": "module",
   "type": "module",
+  "files": [
+    "dist"
+  ],
   "scripts": {
   "scripts": {
     "build": "vite build",
     "build": "vite build",
     "clean": "shx rm -rf dist",
     "clean": "shx rm -rf dist",

+ 1 - 0
packages/pluginkit/vite.config.ts

@@ -9,6 +9,7 @@ import dts from 'vite-plugin-dts';
 export default defineConfig({
 export default defineConfig({
   plugins: [
   plugins: [
     dts({
     dts({
+      entryRoot: 'src',
       copyDtsFiles: true,
       copyDtsFiles: true,
     }),
     }),
     {
     {

+ 0 - 12
packages/preset-themes/src/styles/_variables.scss

@@ -10,12 +10,6 @@ $grw-marker-cyan: #6ff;
 $grw-marker-green: #6f6;
 $grw-marker-green: #6f6;
 
 
 //== Layout
 //== Layout
-$grw-navbar-height: 52px;
-$grw-navbar-border-width: 3.3333px;
-// slightly larger than $zindex-sticky
-// https://getbootstrap.jp/docs/4.6/layout/overview/#z-index
-$grw-navbar-z-index: 1025;
-
 $grw-subnav-min-height: 95px;
 $grw-subnav-min-height: 95px;
 $grw-subnav-min-height-md: 115px;
 $grw-subnav-min-height-md: 115px;
 $grw-subnav-height-on-edit: 95px;
 $grw-subnav-height-on-edit: 95px;
@@ -30,9 +24,3 @@ $grw-sidebar-nav-width: 64px; // !!DO NOT CHANGE!! 'margin-left' for '.css-teprs
 
 
 $grw-logo-width: $grw-sidebar-nav-width;
 $grw-logo-width: $grw-sidebar-nav-width;
 $grw-logomark-width: 36px;
 $grw-logomark-width: 36px;
-
-// fix tab width to 95 pixels
-// see also '_editor.scss'
-$grw-nav-main-left-tab-width: 95px;
-$grw-nav-main-left-tab-width-mobile: 50px;
-$grw-nav-main-tab-height: 42px;

+ 1 - 1
packages/remark-attachment-refs/package.json

@@ -52,7 +52,7 @@
     "universal-bunyan": "^0.9.2"
     "universal-bunyan": "^0.9.2"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "csstype": "^3.1.2",
+    "csstype": "^3.0.2",
     "eslint-plugin-regex": "^1.8.0",
     "eslint-plugin-regex": "^1.8.0",
     "hast-util-sanitize": "^4.1.0",
     "hast-util-sanitize": "^4.1.0",
     "hast-util-select": "^5.0.5",
     "hast-util-select": "^5.0.5",

+ 1 - 1
packages/remark-attachment-refs/vite.client.config.ts

@@ -8,7 +8,7 @@ export default defineConfig({
   plugins: [
   plugins: [
     react(),
     react(),
     dts({
     dts({
-      outputDir: 'dist',
+      entryRoot: 'src/client',
       copyDtsFiles: true,
       copyDtsFiles: true,
     }),
     }),
     {
     {

+ 1 - 1
packages/remark-attachment-refs/vite.server.config.ts

@@ -6,7 +6,7 @@ import dts from 'vite-plugin-dts';
 export default defineConfig({
 export default defineConfig({
   plugins: [
   plugins: [
     dts({
     dts({
-      outputDir: 'dist',
+      entryRoot: 'src/server',
       copyDtsFiles: true,
       copyDtsFiles: true,
     }),
     }),
     {
     {

+ 0 - 1
packages/remark-growi-directive/package.json

@@ -54,7 +54,6 @@
     "tape": "^5.0.0",
     "tape": "^5.0.0",
     "to-vfile": "^7.0.0",
     "to-vfile": "^7.0.0",
     "type-coverage": "^2.0.0",
     "type-coverage": "^2.0.0",
-    "typescript": "^4.0.0",
     "unist-util-remove-position": "^4.0.0"
     "unist-util-remove-position": "^4.0.0"
   },
   },
   "typeCoverage": {
   "typeCoverage": {

+ 1 - 0
packages/remark-lsx/src/utils/index.ts

@@ -0,0 +1 @@
+export * from './depth-utils';

+ 1 - 1
packages/remark-lsx/vite.client.config.ts

@@ -8,7 +8,7 @@ export default defineConfig({
   plugins: [
   plugins: [
     react(),
     react(),
     dts({
     dts({
-      outputDir: 'dist',
+      entryRoot: 'src/client',
       copyDtsFiles: true,
       copyDtsFiles: true,
     }),
     }),
     {
     {

+ 1 - 1
packages/remark-lsx/vite.server.config.ts

@@ -6,7 +6,7 @@ import dts from 'vite-plugin-dts';
 export default defineConfig({
 export default defineConfig({
   plugins: [
   plugins: [
     dts({
     dts({
-      outputDir: 'dist',
+      entryRoot: 'src/server',
       copyDtsFiles: true,
       copyDtsFiles: true,
     }),
     }),
     {
     {

+ 2 - 2
packages/ui/src/utils/browser-utils.ts

@@ -7,8 +7,8 @@ export const addBreakpointListener = (
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
     listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
 ): MediaQueryList => {
 ): MediaQueryList => {
-  // get the value of '--breakpoint-*'
-  const breakpointPixel = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue(`--breakpoint-${breakpoint}`), 10);
+  // get the value of '--bs-breakpoint-*'
+  const breakpointPixel = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue(`--bs-breakpoint-${breakpoint}`), 10);
 
 
   const mediaQueryList = window.matchMedia(`(min-width: ${breakpointPixel}px)`);
   const mediaQueryList = window.matchMedia(`(min-width: ${breakpointPixel}px)`);
 
 

Some files were not shown because too many files changed in this diff