Yuki Takei 2 лет назад
Родитель
Сommit
b9027aa790
56 измененных файлов с 0 добавлено и 4721 удалено
  1. 0 1
      _obsolete/packages/.eslintignore
  2. 0 1
      _obsolete/packages/hackmd/.eslintignore
  3. 0 1
      _obsolete/packages/hackmd/.gitignore
  4. 0 25
      _obsolete/packages/hackmd/package.json
  5. 0 152
      _obsolete/packages/hackmd/src/hackmd-agent.js
  6. 0 41
      _obsolete/packages/hackmd/src/hackmd-styles.ts
  7. 0 16
      _obsolete/packages/hackmd/src/index.ts
  8. 0 21
      _obsolete/packages/hackmd/src/style.scss
  9. 0 14
      _obsolete/packages/hackmd/tsconfig.json
  10. 0 30
      _obsolete/packages/hackmd/vite.config.js
  11. 0 33
      apps/app/_obsolete/src/client/services/side-effects/hackmd-draft-updated.ts
  12. 0 51
      apps/app/_obsolete/src/client/util/codemirror/autorefresh.ext.js
  13. 0 47
      apps/app/_obsolete/src/client/util/codemirror/drawio-fold.ext.js
  14. 0 19
      apps/app/_obsolete/src/client/util/codemirror/gfm-growi.mode.js
  15. 0 41
      apps/app/_obsolete/src/client/util/codemirror/update-display-util.ext.js
  16. 0 50
      apps/app/_obsolete/src/components/Navbar/GlobalSearch.module.scss
  17. 0 144
      apps/app/_obsolete/src/components/Navbar/GlobalSearch.tsx
  18. 0 114
      apps/app/_obsolete/src/components/Navbar/GrowiNavbar.module.scss
  19. 0 141
      apps/app/_obsolete/src/components/Navbar/GrowiNavbar.tsx
  20. 0 103
      apps/app/_obsolete/src/components/Navbar/GrowiSubNavigation.module.scss
  21. 0 55
      apps/app/_obsolete/src/components/Navbar/GrowiSubNavigation.tsx
  22. 0 41
      apps/app/_obsolete/src/components/Navbar/GrowiSubNavigationSwitcher.module.scss
  23. 0 97
      apps/app/_obsolete/src/components/Navbar/GrowiSubNavigationSwitcher.tsx
  24. 0 144
      apps/app/_obsolete/src/components/PageEditor/AbstractEditor.tsx
  25. 0 50
      apps/app/_obsolete/src/components/PageEditor/Editor.module.scss
  26. 0 365
      apps/app/_obsolete/src/components/PageEditor/Editor.tsx
  27. 0 159
      apps/app/_obsolete/src/components/PageEditor/EditorIcon.jsx
  28. 0 62
      apps/app/_obsolete/src/components/PageEditor/EmojiPicker.tsx
  29. 0 116
      apps/app/_obsolete/src/components/PageEditor/EmojiPickerHelper.ts
  30. 0 46
      apps/app/_obsolete/src/components/PageEditor/MarkdownLinkUtil.js
  31. 0 95
      apps/app/_obsolete/src/components/PageEditor/MarkdownTableInterceptor.js
  32. 0 44
      apps/app/_obsolete/src/components/PageEditor/PasteHelper.js
  33. 0 44
      apps/app/_obsolete/src/components/PageEditor/PreventMarkdownListInterceptor.js
  34. 0 274
      apps/app/_obsolete/src/components/PageEditor/TextAreaEditor.jsx
  35. 0 521
      apps/app/_obsolete/src/components/PageEditorByHackmd.tsx
  36. 0 115
      apps/app/_obsolete/src/components/PageEditorByHackmd/HackmdEditor.jsx
  37. 0 101
      apps/app/_obsolete/src/components/UncontrolledCodeMirror.tsx
  38. 0 16
      apps/app/_obsolete/src/interfaces/hackmd.ts
  39. 0 346
      apps/app/_obsolete/src/server/routes/hackmd.js
  40. 0 22
      apps/app/_obsolete/src/stores/hackmd.ts
  41. 0 170
      apps/app/_obsolete/src/styles/_override.scss
  42. 0 32
      apps/app/_obsolete/src/styles/theme/_hsl-functions.scss
  43. 0 108
      apps/app/_obsolete/src/styles/theme/_hsl-reboot-bootstrap-theme-colors.scss
  44. 0 29
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-border-colors.scss
  45. 0 22
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-buttons.scss
  46. 0 60
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-colors.scss
  47. 0 38
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-dropdown.scss
  48. 0 52
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-nav.scss
  49. 0 74
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-tables.scss
  50. 0 3
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-text.scss
  51. 0 103
      apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-theme-colors.scss
  52. 0 9
      apps/app/_obsolete/src/styles/theme/mixins/_count-badge.scss
  53. 0 23
      apps/app/_obsolete/src/styles/theme/mixins/_hsl-badge.scss
  54. 0 146
      apps/app/_obsolete/src/styles/theme/mixins/_hsl-button.scss
  55. 0 72
      apps/app/_obsolete/src/styles/theme/mixins/_list-group.scss
  56. 0 22
      apps/app/_obsolete/src/styles/theme/mixins/_page-editor-mode-manager.scss

+ 0 - 1
_obsolete/packages/.eslintignore

@@ -1 +0,0 @@
-**/*

+ 0 - 1
_obsolete/packages/hackmd/.eslintignore

@@ -1 +0,0 @@
-/dist/**

+ 0 - 1
_obsolete/packages/hackmd/.gitignore

@@ -1 +0,0 @@
-/dist

+ 0 - 25
_obsolete/packages/hackmd/package.json

@@ -1,25 +0,0 @@
-{
-  "name": "@growi/hackmd",
-  "version": "7.0.0-RC.0",
-  "description": "GROWI js and css files to use hackmd",
-  "license": "MIT",
-  "type": "module",
-  "main": "dist/index.cjs",
-  "module": "dist/index.js",
-  "types": "dist/index.d.ts",
-  "scripts": {
-    "build": "vite build",
-    "clean": "shx rm -rf dist",
-    "dev": "vite build --mode dev",
-    "watch": "yarn dev -w --emptyOutDir=false",
-    "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": {},
-  "devDependencies": {
-    "penpal": "^4.0.0",
-    "throttle-debounce": "^5.0.0"
-  }
-}

+ 0 - 152
_obsolete/packages/hackmd/src/hackmd-agent.js

@@ -1,152 +0,0 @@
-/**
- * GROWI agent for HackMD
- *
- * This file will be transpiled as a single JS
- *  and should be load from HackMD head via 'routes/hackmd.js' route
- *
- * USAGE:
- *  <script src="${hostname of GROWI}/_hackmd/load-agent"></script>
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- */
-import connectToParent from 'penpal/lib/connectToParent';
-import { debounce } from 'throttle-debounce';
-
-const DEBUG_PENPAL = false;
-
-/* eslint-disable no-console  */
-
-const allowedOrigin = '<%= origin %>'; // will be replaced by ejs
-
-
-/**
- * return the value of CodeMirror
- */
-function getValueOfCodemirror() {
-  // get CodeMirror instance
-  const editor = window.editor;
-  return editor.doc.getValue();
-}
-
-/**
- * set the specified document to CodeMirror
- * @param {string} value
- */
-function setValueToCodemirror(value) {
-  // get CodeMirror instance
-  const editor = window.editor;
-  editor.doc.setValue(value);
-}
-
-/**
- * set the specified document to CodeMirror on window loaded
- * @param {string} value
- */
-function setValueToCodemirrorOnInit(newValue) {
-  if (window.cmClient != null) {
-    setValueToCodemirror(newValue);
-    return;
-  }
-
-  const intervalId = setInterval(() => {
-    if (window.cmClient != null) {
-      clearInterval(intervalId);
-      setValueToCodemirror(newValue);
-    }
-  }, 250);
-
-}
-
-/**
- * postMessage to GROWI to notify body changes
- * @param {string} body
- */
-function postParentToNotifyBodyChanges(body) {
-  window.growi.notifyBodyChanges(body);
-}
-// generate debounced function
-const debouncedPostParentToNotifyBodyChanges = debounce(800, postParentToNotifyBodyChanges);
-
-/**
- * postMessage to GROWI to save with shortcut
- * @param {string} document
- */
-function postParentToSaveWithShortcut(document) {
-  window.growi.saveWithShortcut(document);
-}
-
-function addEventListenersToCodemirror() {
-  // get CodeMirror instance
-  const codemirror = window.CodeMirror;
-  // get CodeMirror editor instance
-  const editor = window.editor;
-
-  // e.g. 404 not found
-  if (codemirror == null || editor == null) {
-    return;
-  }
-
-  // == change event
-  editor.on('change', (cm, change) => {
-    if (change.origin === 'ignoreHistory') {
-      // do nothing because this operation triggered by other user
-      return;
-    }
-    debouncedPostParentToNotifyBodyChanges(cm.doc.getValue());
-  });
-
-  // == save event
-  // Reset save commands and Cmd-S/Ctrl-S shortcuts that initialized by HackMD
-  codemirror.commands.save = function(cm) {
-    postParentToSaveWithShortcut(cm.doc.getValue());
-  };
-  delete editor.options.extraKeys['Cmd-S'];
-  delete editor.options.extraKeys['Ctrl-S'];
-}
-
-function connectToParentWithPenpal() {
-  const connection = connectToParent({
-    parentOrigin: allowedOrigin,
-    // Methods child is exposing to parent
-    methods: {
-      getValue() {
-        return getValueOfCodemirror();
-      },
-      setValue(newValue) {
-        setValueToCodemirror(newValue);
-      },
-      setValueOnInit(newValue) {
-        setValueToCodemirrorOnInit(newValue);
-      },
-    },
-    debug: DEBUG_PENPAL,
-  });
-  connection.promise
-    .then((parent) => {
-      window.growi = parent;
-    })
-    .catch((err) => {
-      console.log(err);
-    });
-}
-
-/**
- * main
- */
-(function() {
-  // check HackMD is in iframe
-  if (window === window.parent) {
-    console.log('[GROWI] Loading agent for HackMD is not processed because currently not in iframe');
-    return;
-  }
-
-  console.log('[HackMD] Loading GROWI agent for HackMD...');
-
-  window.addEventListener('load', () => {
-    addEventListenersToCodemirror();
-  });
-
-  connectToParentWithPenpal();
-
-  console.log('[HackMD] GROWI agent for HackMD has successfully loaded.');
-}());

+ 0 - 41
_obsolete/packages/hackmd/src/hackmd-styles.ts

@@ -1,41 +0,0 @@
-/**
- * GROWI styles loader for HackMD
- *
- * This file will be transpiled as a single JS
- *  and should be load from HackMD head via 'routes/hackmd.js' route
- *
- * USAGE:
- *  <script src="${hostname of GROWI}/_hackmd/load-styles"></script>
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- */
-
-/* eslint-disable no-console  */
-
-const styles = '<%= styles %>'; // will be replaced by ejs
-
-/**
- * Insert link tag to load style file
- */
-function insertStyle() {
-  const element = document.createElement('style');
-  element.appendChild(document.createTextNode(unescape(styles)));
-  document.getElementsByTagName('head')[0].appendChild(element);
-}
-
-/**
- * main
- */
-(function() {
-  // check HackMD is in iframe
-  if (window === window.parent) {
-    console.log('[GROWI] Loading styles for HackMD is not processed because currently not in iframe');
-    return;
-  }
-
-  console.log('[HackMD] Loading GROWI styles for HackMD...');
-
-  insertStyle();
-
-  console.log('[HackMD] GROWI styles for HackMD has successfully loaded.');
-}());

+ 0 - 16
_obsolete/packages/hackmd/src/index.ts

@@ -1,16 +0,0 @@
-import fs from 'node:fs';
-import path from 'node:path';
-
-const isProduction = process.env.NODE_ENV === 'production';
-const dirPath = isProduction ? '.' : '../dist';
-const stylesJSFile = fs.readFileSync(path.resolve(__dirname, `${dirPath}/hackmd-styles.js`));
-const agentJSFile = fs.readFileSync(path.resolve(__dirname, `${dirPath}/hackmd-agent.js`));
-const stylesCSSFile = fs.readFileSync(path.resolve(__dirname, `${dirPath}/style.css`));
-
-// export to app as string
-const hackmdFiles = {
-  stylesJS: stylesJSFile.toString(),
-  agentJS: agentJSFile.toString(),
-  stylesCSS: stylesCSSFile.toString().replace(/(\r\n|\n|\r)/gm, ''), // https://stackoverflow.com/questions/10805125/how-to-remove-all-line-breaks-from-a-string
-};
-export default hackmdFiles;

+ 0 - 21
_obsolete/packages/hackmd/src/style.scss

@@ -1,21 +0,0 @@
-.navbar-header {
-  .navbar-brand {
-    display: none;
-  }
-}
-
-.navbar-form {
-  margin-left: 15px;
-}
-
-.navbar-right {
-  .ui-new, .ui-publish {
-    display: none;
-  }
-}
-
-.CodeMirror pre.CodeMirror-line {
-  font-family: Osaka-Mono, 'MS Gothic', Monaco, Menlo, Consolas, 'Courier New', monospace;
-  font-size: 14px;
-  line-height: 20px;
-}

+ 0 - 14
_obsolete/packages/hackmd/tsconfig.json

@@ -1,14 +0,0 @@
-{
-  "$schema": "http://json.schemastore.org/tsconfig",
-  "extends": "../../tsconfig.base.json",
-  "compilerOptions": {
-    "isolatedModules": false,
-
-    "baseUrl": ".",
-    "paths": {
-    }
-  },
-  "include": [
-    "src"
-  ]
-}

+ 0 - 30
_obsolete/packages/hackmd/vite.config.js

@@ -1,30 +0,0 @@
-import { defineConfig } from 'vite';
-import dts from 'vite-plugin-dts';
-
-
-// https://vitejs.dev/config/
-export default defineConfig({
-  plugins: [
-    dts({ copyDtsFiles: true }),
-  ],
-  build: {
-    outDir: 'dist',
-    lib: {
-      entry: [
-        'src/index.ts',
-        'src/hackmd-styles.ts',
-        'src/hackmd-agent.js',
-        'src/style.scss',
-      ],
-      name: 'hackmd-libs',
-      formats: ['es', 'cjs'],
-    },
-    rollupOptions: {
-      external: [
-        'node:fs',
-        'node:path',
-      ],
-    },
-    sourcemap: true,
-  },
-});

+ 0 - 33
apps/app/_obsolete/src/client/services/side-effects/hackmd-draft-updated.ts

@@ -1,33 +0,0 @@
-import { useCallback, useEffect } from 'react';
-
-import { SocketEventName } from '~/interfaces/websocket';
-import { useIsHackmdDraftUpdatingInRealtime } from '~/stores/hackmd';
-import { useCurrentPageId } from '~/stores/page';
-import { useGlobalSocket } from '~/stores/websocket';
-
-export const useHackmdDraftUpdatedEffect = (): void => {
-
-  const { data: currentPageId } = useCurrentPageId();
-  const { mutate: mutateIsHackmdDraftUpdatingInRealtime } = useIsHackmdDraftUpdatingInRealtime();
-
-  const { data: socket } = useGlobalSocket();
-
-  const setIsHackmdDraftUpdatingInRealtime = useCallback((data) => {
-    const { s2cMessagePageUpdated } = data;
-    if (s2cMessagePageUpdated.pageId === currentPageId) {
-      mutateIsHackmdDraftUpdatingInRealtime(true);
-    }
-  }, [currentPageId, mutateIsHackmdDraftUpdatingInRealtime]);
-
-  // listen socket for hackmd saved
-  useEffect(() => {
-
-    if (socket == null) { return }
-
-    socket.on(SocketEventName.EditingWithHackmd, setIsHackmdDraftUpdatingInRealtime);
-
-    return () => {
-      socket.off(SocketEventName.EditingWithHackmd, setIsHackmdDraftUpdatingInRealtime);
-    };
-  }, [setIsHackmdDraftUpdatingInRealtime, socket]);
-};

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

@@ -1,51 +0,0 @@
-/**
- * extends codemirror/addon/display/autorefresh
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- * @see https://codemirror.net/addon/display/autorefresh.js
- * @see https://github.com/scniro/react-codemirror2/issues/83#issuecomment-398825212
- */
-/* eslint-disable */
-
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-(function(mod) {
-  mod(require("codemirror"));
-})(function(CodeMirror) {
-  "use strict"
-
-  CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
-    if (cm.state.autoRefresh) {
-      stopListening(cm, cm.state.autoRefresh)
-      cm.state.autoRefresh = null
-    }
-    if (val && (val.force || cm.display.wrapper.offsetHeight == 0))
-      startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
-  })
-
-  function startListening(cm, state) {
-    function check() {
-      if (cm.display.wrapper.offsetHeight) {
-        stopListening(cm, state)
-        if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
-          cm.refresh()
-      } else {
-        state.timeout = setTimeout(check, state.delay)
-      }
-    }
-    state.timeout = setTimeout(check, state.delay)
-    state.hurry = function() {
-      clearTimeout(state.timeout)
-      state.timeout = setTimeout(check, 50)
-    }
-    CodeMirror.on(window, "mouseup", state.hurry)
-    CodeMirror.on(window, "keyup", state.hurry)
-  }
-
-  function stopListening(_cm, state) {
-    clearTimeout(state.timeout)
-    CodeMirror.off(window, "mouseup", state.hurry)
-    CodeMirror.off(window, "keyup", state.hurry)
-  }
-});

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

@@ -1,47 +0,0 @@
-/* eslint-disable */
-
-import mdu from '../../../components/PageEditor/MarkdownDrawioUtil.js';
-
-(function(mod) {
-  mod(require("codemirror"));
-})(function(CodeMirror) {
-  "use strict"
-
-  CodeMirror.registerGlobalHelper('fold', 'drawio', function (mode, cm) {
-    return true;
-  }, function(cm, start) {
-    function isBeginningOfDrawio(lineNo) {
-      let line = cm.getLine(lineNo);
-      let match = mdu.lineBeginPartOfDrawioRE.exec(line);
-      if (match) {
-        return true;
-      }
-      return false;
-    }
-    function isEndOfDrawio(lineNo) {
-      let line = cm.getLine(lineNo);
-      let match = mdu.lineEndPartOfDrawioRE.exec(line);
-      if (match) {
-        return true;
-      }
-      return false;
-    }
-
-    let drawio = isBeginningOfDrawio(start.line);
-    if (drawio === false) { return; }
-
-    let lastLine = cm.lastLine();
-    let end = start.line;
-    while(end < lastLine) {
-      end += 1;
-      if (isEndOfDrawio(end)) {
-        break;
-      }
-    }
-
-    return {
-      from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
-      to: CodeMirror.Pos(end, cm.getLine(end).length)
-    };
-  });
-});

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

@@ -1,19 +0,0 @@
-// https://discuss.codemirror.net/t/cm-header-margin-padding-height/75/5
-window.CodeMirror.defineMode('gfm-growi', (cmConfig, modeCfg) => {
-  // based on Markdown (GitHub-flavour) mode
-  // https://codemirror.net/doc/manual.html#option_mode
-  // https://codemirror.net/mode/index.html
-  modeCfg.name = 'gfm';
-  modeCfg.highlightFormatting = true;
-  const mode = window.CodeMirror.getMode(cmConfig, modeCfg);
-
-  const origToken = mode.token;
-  mode.token = function(stream, state) {
-    let classes = origToken(stream, state) || '';
-    // https://regex101.com/r/Fep0w2/1
-    classes = classes.replace(/(^| )header(\S*)/g, '$1header$2 line-grw-cm-header-line');
-    return /^\s*$/.test(classes) ? null : classes;
-  };
-
-  return mode;
-});

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

@@ -1,41 +0,0 @@
-import { sawCollapsedSpans } from 'codemirror/src/line/saw_special_spans';
-import { getLine } from 'codemirror/src/line/utils_line';
-import { heightAtLine, visualLineEndNo, visualLineNo } from 'codemirror/src/line/spans';
-import { DisplayUpdate } from 'codemirror/src/display/update_display';
-import { adjustView } from 'codemirror/src/display/view_tracking';
-
-class UpdateDisplayUtil {
-
-  /**
-   * Transplant 'updateDisplayIfNeeded' method to fix weseek/growi#703
-   *
-   * @see https://github.com/weseek/growi/issues/703
-   * @see https://github.com/codemirror/CodeMirror/blob/5.42.0/src/display/update_display.js#L125
-   *
-   * @param {CodeMirror} cm
-   */
-  static forceUpdateViewOffset(cm) {
-    const doc = cm.doc;
-    const display = cm.display;
-
-    const update = new DisplayUpdate(cm, cm.getViewport());
-
-    // Compute a suitable new viewport (from & to)
-    const end = doc.first + doc.size;
-    let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
-    let to = Math.min(end, update.visible.to + cm.options.viewportMargin);
-    if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
-    if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
-    if (sawCollapsedSpans) {
-      from = visualLineNo(cm.doc, from);
-      to = visualLineEndNo(cm.doc, to);
-    }
-    adjustView(cm, from, to);
-
-    display.viewOffset = heightAtLine(getLine(doc, display.viewFrom));
-  }
-
-}
-
-
-export default UpdateDisplayUtil;

+ 0 - 50
apps/app/_obsolete/src/components/Navbar/GlobalSearch.module.scss

@@ -1,50 +0,0 @@
-@use '@growi/core/scss/bootstrap/init' as bs;
-
-// input styles
-.grw-global-search :global {
-  .dropdown-toggle {
-    min-width: 95px;
-    padding-left: 1.5rem;
-  }
-
-  .search-typeahead {
-    .rbt-menu {
-      right: 0;
-      left: auto;
-
-      @include bs.media-breakpoint-up(md) {
-        right: auto;
-        left: 0;
-      }
-
-      @include bs.media-breakpoint-down(sm) {
-        left: auto !important;
-        width: 90vw;
-      }
-    }
-  }
-
-  // using react-bootstrap-typeahead
-  // see: https://github.com/ericgio/react-bootstrap-typeahead
-  .rbt-input.form-control {
-    height: 30px;
-    .rbt-input-wrapper {
-      margin-left: 8px;
-    }
-  }
-
-  .grw-shortcut-key-indicator {
-    position: absolute;
-    top: 0;
-    right: 4px;
-
-    display: flex;
-    align-items: center;
-    height: 30px;
-
-    code {
-      padding-right: 0.4rem;
-      padding-left: 0.4rem;
-    }
-  }
-}

+ 0 - 144
apps/app/_obsolete/src/components/Navbar/GlobalSearch.tsx

@@ -1,144 +0,0 @@
-import React, {
-  useState, useCallback, useRef, useEffect,
-} from 'react';
-
-import assert from 'assert';
-
-import { pathUtils } from '@growi/core/dist/utils';
-import { useTranslation } from 'next-i18next';
-import { useRouter } from 'next/router';
-
-import { IFocusable } from '~/client/interfaces/focusable';
-import { useKeywordManager } from '~/client/services/search-operation';
-import { IPageWithSearchMeta } from '~/interfaces/search';
-import {
-  useIsSearchScopeChildrenAsDefault, useIsSearchServiceReachable,
-} from '~/stores/context';
-import { useCurrentPagePath } from '~/stores/page';
-import { useGlobalSearchFormRef } from '~/stores/ui';
-
-import SearchForm from '../../../../src/components/SearchForm';
-
-import styles from './GlobalSearch.module.scss';
-
-
-export type GlobalSearchProps = {
-  dropup?: boolean,
-}
-
-export const GlobalSearch = (props: GlobalSearchProps): JSX.Element => {
-  const { t } = useTranslation('commons');
-
-  const { dropup } = props;
-
-  const { returnPathForURL } = pathUtils;
-
-  const router = useRouter();
-
-  const globalSearchFormRef = useRef<IFocusable>(null);
-
-  useGlobalSearchFormRef(globalSearchFormRef);
-
-  const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
-  const { data: isSearchScopeChildrenAsDefault } = useIsSearchScopeChildrenAsDefault();
-  const { data: currentPagePath } = useCurrentPagePath();
-
-  const [text, setText] = useState('');
-  const [isScopeChildren, setScopeChildren] = useState<boolean|undefined>(isSearchScopeChildrenAsDefault ?? false);
-  const [isFocused, setFocused] = useState<boolean>(false);
-
-  const { pushState } = useKeywordManager();
-
-  useEffect(() => {
-    setScopeChildren(isSearchScopeChildrenAsDefault);
-  }, [isSearchScopeChildrenAsDefault]);
-
-
-  const gotoPage = useCallback((data: IPageWithSearchMeta[]) => {
-    assert(data.length > 0);
-
-    const page = data[0].data; // should be single page selected
-
-    // navigate to page
-    if (page != null) {
-      router.push(returnPathForURL(page.path, page._id));
-    }
-  }, [returnPathForURL, router]);
-
-  const search = useCallback(() => {
-    // construct search query
-    let q = text;
-    if (isScopeChildren) {
-      q += ` prefix:${currentPagePath ?? window.location.pathname}`;
-    }
-
-    pushState(q);
-  }, [currentPagePath, isScopeChildren, router, text]);
-
-  const scopeLabel = isScopeChildren
-    ? t('header_search_box.label.This tree')
-    : t('header_search_box.label.All pages');
-
-  const isIndicatorShown = !isFocused && (text.length === 0);
-
-
-  if (isScopeChildren == null || isSearchServiceReachable == null) {
-    return <></>;
-  }
-
-  return (
-    <div className={`grw-global-search ${styles['grw-global-search']} mb-0 d-print-none ${isSearchServiceReachable ? '' : 'has-error'}`}>
-      <div className="input-group flex-nowrap">
-        <div className={` ${dropup ? 'dropup' : ''}`}>
-          <button
-            className="btn btn-secondary dropdown-toggle py-0"
-            type="button"
-            data-bs-toggle="dropdown"
-            aria-haspopup="true"
-            data-testid="select-search-scope"
-          >
-            {scopeLabel}
-          </button>
-          <div className="dropdown-menu">
-            <button
-              className="dropdown-item"
-              type="button"
-              onClick={() => {
-                setScopeChildren(false);
-                globalSearchFormRef.current?.focus();
-              }}
-            >
-              { t('header_search_box.item_label.All pages') }
-            </button>
-            <button
-              data-tesid="search-current-tree"
-              className="dropdown-item"
-              type="button"
-              onClick={() => {
-                setScopeChildren(true);
-                globalSearchFormRef.current?.focus();
-              }}
-            >
-              { t('header_search_box.item_label.This tree') }
-            </button>
-          </div>
-        </div>
-        <SearchForm
-          ref={globalSearchFormRef}
-          isSearchServiceReachable={isSearchServiceReachable || false}
-          dropup={dropup}
-          onChange={gotoPage}
-          onBlur={() => setFocused(false)}
-          onFocus={() => setFocused(true)}
-          onInputChange={text => setText(text)}
-          onSubmit={search}
-        />
-        { isIndicatorShown && (
-          <span className="grw-shortcut-key-indicator">
-            <code className="bg-transparent text-muted">/</code>
-          </span>
-        ) }
-      </div>
-    </div>
-  );
-};

+ 0 - 114
apps/app/_obsolete/src/components/Navbar/GrowiNavbar.module.scss

@@ -1,114 +0,0 @@
-@use '~/styles/variables' as var;
-@use '~/styles/bootstrap/init' as bs;
-@use '~/styles/mixins';
-
-.grw-navbar :global {
-
-  .confidential {
-    font-weight: bold;
-  }
-
-}
-
-.grw-navbar :global {
-  top: #{-1 * var.$grw-navbar-height} !important;
-
-  z-index: var.$grw-navbar-z-index !important;
-  max-height: var.$grw-navbar-height + var.$grw-navbar-border-width;
-  border-top: 0;
-  border-right: 0;
-  border-bottom: var.$grw-navbar-border-width solid;
-  border-left: 0;
-
-  .grw-app-title {
-    @include mixins.variable-font-size(24px);
-  }
-
-  .grw-navbar-search {
-    position: absolute;
-    left: 50%;
-    transform: translate(-50%, 0%);
-  }
-
-  .nav-link,
-  .nav-item.confidential {
-    display: flex;
-    align-items: center;
-    min-height: var.$grw-navbar-height;
-    padding: 0 1rem;
-  }
-
-  .nav-link {
-    &:hover {
-      background: rgba(0, 0, 0, 0.1);
-    }
-
-    &:focus {
-      background: rgba(0, 0, 0, 0);
-    }
-  }
-  .nav-item.confidential {
-    :not(i) {
-      @include mixins.variable-font-size(14px);
-    }
-
-    @include bs.media-breakpoint-only(md) {
-      max-width: 100px;
-    }
-
-    max-width: 120px;
-    max-height: var.$grw-navbar-height;
-    overflow: hidden;
-    background: rgba(0, 0, 0, 0.2);
-  }
-
-  .grw-notification-dropdown {
-    .dropdown-menu {
-      max-width: 70vw;
-    }
-  }
-}
-
-// layout for GlobalSearch
-.grw-navbar :global {
-  .grw-global-search-container {
-    // centering on navbar
-    top: var.$grw-navbar-height / 2;
-    left: 50vw;
-    z-index: bs.$zindex-fixed + 1;
-    transform: translate(-50%, -50%);
-
-    .rbt-input.form-control {
-      width: 200px;
-      transition: 0.3s ease-out;
-
-      // focus
-      &.focus {
-        width: 300px;
-      }
-
-      @include bs.media-breakpoint-up(md) {
-        width: 300px;
-      }
-      @include bs.media-breakpoint-up(lg) {
-        // focus
-        &.focus {
-          width: 400px;
-        }
-      }
-      @include bs.media-breakpoint-up(xl) {
-        width: 350px;
-        // focus
-        &.focus {
-          width: 450px;
-        }
-      }
-    }
-  }
-}
-
-.grw-notification-badge {
-  position: absolute;
-  top: 6px;
-  right: 3.5px;
-}

+ 0 - 141
apps/app/_obsolete/src/components/Navbar/GrowiNavbar.tsx

@@ -1,141 +0,0 @@
-import React, {
-  FC, memo, useMemo, useRef,
-} from 'react';
-
-import { useTranslation } from 'next-i18next';
-import dynamic from 'next/dynamic';
-import { useRipple } from 'react-use-ripple';
-import { UncontrolledTooltip } from 'reactstrap';
-
-import {
-  useIsSearchPage, useIsGuestUser, useIsReadOnlyUser, useIsSearchServiceConfigured, useAppTitle, useConfidential,
-} from '~/stores/context';
-import { usePageCreateModal } from '~/stores/modal';
-import { useCurrentPagePath } from '~/stores/page';
-import { useIsDeviceSmallerThanMd } from '~/stores/ui';
-
-
-import { GlobalSearchProps } from './GlobalSearch';
-
-import styles from './GrowiNavbar.module.scss';
-
-const NavbarRight = memo((): JSX.Element => {
-  const { t } = useTranslation();
-
-  const { data: currentPagePath } = useCurrentPagePath();
-  const { data: isGuestUser } = useIsGuestUser();
-  const { data: isReadOnlyUser } = useIsReadOnlyUser();
-
-  // ripple
-  const newButtonRef = useRef(null);
-  useRipple(newButtonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
-
-  const { open: openCreateModal } = usePageCreateModal();
-
-  const isAuthenticated = isGuestUser === false;
-
-  const authenticatedNavItem = useMemo(() => {
-    return (
-      <>
-        {!isReadOnlyUser
-          && (
-            <li className="nav-item d-none d-md-block">
-              <button
-                className="px-md-3 nav-link btn-create-page border-0 bg-transparent"
-                type="button"
-                ref={newButtonRef}
-                data-testid="newPageBtn"
-                onClick={() => openCreateModal(currentPagePath || '')}
-              >
-                <span className="material-symbols-outlined">edit</span>
-                <span className="d-none d-lg-block">{ t('commons:New') }</span>
-              </button>
-            </li>
-          )
-        }
-      </>
-    );
-  }, [isReadOnlyUser, t, openCreateModal, currentPagePath]);
-
-  const notAuthenticatedNavItem = useMemo(() => {
-    return (
-      <>
-        <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>
-      </>
-    );
-  }, []);
-
-  return (
-    <>
-      {isAuthenticated ? authenticatedNavItem : notAuthenticatedNavItem}
-    </>
-  );
-});
-NavbarRight.displayName = 'NavbarRight';
-
-type ConfidentialProps = {
-  confidential?: string,
-}
-const Confidential: FC<ConfidentialProps> = memo((props: ConfidentialProps): JSX.Element => {
-  const { confidential } = props;
-
-  if (confidential == null || confidential.length === 0) {
-    return <></>;
-  }
-
-  return (
-    <li className="nav-item confidential text-light">
-      <i id="confidentialTooltip"></i><span className="material-symbols-outlined d-md-none">info</span>
-      <span className="d-none d-md-inline">
-        {confidential}
-      </span>
-      <UncontrolledTooltip
-        placement="bottom"
-        target="confidentialTooltip"
-        className="d-md-none"
-      >
-        {confidential}
-      </UncontrolledTooltip>
-    </li>
-  );
-});
-Confidential.displayName = 'Confidential';
-
-type Props = {
-  isGlobalSearchHidden?: boolean
-}
-
-export const GrowiNavbar = (props: Props): JSX.Element => {
-
-  const { isGlobalSearchHidden } = props;
-
-  const GlobalSearch = dynamic<GlobalSearchProps>(() => import('./GlobalSearch').then(mod => mod.GlobalSearch), { ssr: false });
-
-  const { data: appTitle } = useAppTitle();
-  const { data: confidential } = useConfidential();
-  const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
-  const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
-  const { data: isSearchPage } = useIsSearchPage();
-
-  return (
-    <nav id="grw-navbar" className={`navbar grw-navbar ${styles['grw-navbar']} navbar-expand navbar-dark sticky-top mb-0 px-0`}>
-
-      <div className="grw-app-title d-none d-md-block">
-        {appTitle}
-      </div>
-
-      {/* Navbar Right  */}
-      <ul className="navbar-nav ms-auto">
-        <NavbarRight />
-        <Confidential confidential={confidential} />
-      </ul>
-
-      <div className="grw-global-search-container position-absolute">
-        { !isGlobalSearchHidden && isSearchServiceConfigured && !isDeviceSmallerThanMd && !isSearchPage && (
-          <GlobalSearch />
-        ) }
-      </div>
-    </nav>
-  );
-
-};

+ 0 - 103
apps/app/_obsolete/src/components/Navbar/GrowiSubNavigation.module.scss

@@ -1,103 +0,0 @@
-@use '~/styles/variables' as var;
-@use '@growi/core/scss/bootstrap/init' as bs;
-@use '~/styles/mixins';
-
-%subnav-buttons-height {
-  height: 40px;
-}
-
-%compact-subnav-buttons-height {
-  height: 32px;
-}
-
-// https://github.com/css-modules/css-modules/issues/295#issuecomment-404873976
-// workaround to use '&' in global scope
-.grw-subnav {
-  :global {
-    min-height: var.$grw-subnav-min-height;
-    padding-top: 8px;
-    padding-bottom: 8px;
-
-    @include bs.media-breakpoint-up(md) {
-      min-height: var.$grw-subnav-min-height-md;
-    }
-
-    h1 {
-      @include mixins.variable-font-size(32px);
-      line-height: 1.4em;
-    }
-
-    .btn-copy {
-      &:not(:hover):not(:active) {
-        background-color: transparent !important;
-      }
-      opacity: 0.5;
-    }
-
-    .btn-subscribe {
-      @extend %subnav-buttons-height;
-      font-size: 20px;
-    }
-
-    .btn-like,
-    .btn-bookmark,
-    .btn-seen-user {
-      @extend %subnav-buttons-height;
-      padding-right: 6px;
-      padding-left: 8px;
-      font-size: 20px;
-      svg {
-        width: 20px;
-        height: 20px;
-      }
-    }
-    .total-likes,
-    .total-bookmarks {
-      display: flex;
-      align-items: flex-end;
-      padding-right: 8px;
-      padding-left: 6px;
-      font-size: 14px;
-      font-weight: bs.$font-weight-bold;
-    }
-    .seen-user-count {
-      padding-right: 6px;
-      padding-left: 6px;
-      font-size: 14px;
-      font-weight: bs.$font-weight-bold;
-      vertical-align: bottom;
-    }
-
-    .btn-page-item-control {
-      height: 40px;
-      font-size: 16px;
-    }
-
-    .user-list-popover {
-      max-width: 200px;
-
-      .user-list-content {
-        direction: rtl;
-
-        .liker-user-count,
-        .seen-user-count {
-          font-size: 12px;
-          font-weight: bolder;
-        }
-      }
-      .cls-1 {
-        isolation: isolate;
-      }
-    }
-  }
-
-  &:global {
-    &:hover {
-      .btn-copy,
-      .btn-edit-tags {
-        // change button opacity
-        opacity: unset;
-      }
-    }
-  }
-}

+ 0 - 55
apps/app/_obsolete/src/components/Navbar/GrowiSubNavigation.tsx

@@ -1,55 +0,0 @@
-import React from 'react';
-
-import {
-  EditorMode, useEditorMode,
-} from '~/stores/ui';
-
-import PagePathNav from '../PagePathNav';
-
-
-import styles from './GrowiSubNavigation.module.scss';
-
-
-export type GrowiSubNavigationProps = {
-  pagePath?: string,
-  pageId?: string,
-  isNotFound?: boolean,
-  isTagLabelsDisabled?: boolean,
-  tags?: string[],
-  rightComponent?: React.FunctionComponent,
-  additionalClasses?: string[],
-}
-
-export const GrowiSubNavigation = (props: GrowiSubNavigationProps): JSX.Element => {
-
-  const { data: editorMode } = useEditorMode();
-
-  const {
-    pageId, pagePath,
-    rightComponent: RightComponent,
-    additionalClasses = [],
-  } = props;
-
-  const isViewMode = editorMode === EditorMode.View;
-  const isEditorMode = !isViewMode;
-
-  return (
-    <div className={`
-      grw-subnav ${styles['grw-subnav']} d-flex align-items-center justify-content-between
-      ${additionalClasses.join(' ')}`}
-    >
-      {/* Left side */}
-      <div className="d-flex grw-subnav-start-side">
-        <div className="grw-path-nav-container">
-          { pagePath != null && (
-            <PagePathNav pageId={pageId} pagePath={pagePath} isSingleLineMode={isEditorMode} />
-          ) }
-        </div>
-      </div>
-      {/* Right side. */}
-      { RightComponent && (
-        <RightComponent />
-      ) }
-    </div>
-  );
-};

+ 0 - 41
apps/app/_obsolete/src/components/Navbar/GrowiSubNavigationSwitcher.module.scss

@@ -1,41 +0,0 @@
-@use '~/styles/variables' as var;
-@use '@growi/core/scss/bootstrap/init' as bs;
-
-/*
- * Fixed ver
- */
-$easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
-
-.grw-subnav-fixed-container {
-  top: var.$grw-navbar-border-width;
-  z-index: bs.$zindex-sticky - 5;
-}
-
-/*
- * Switching show/hide
- */
-.grw-subnav-switcher {
-  :global {
-    .grw-subnav-fixed-container {
-      transition: transform 150ms $easeInOutCubic;
-    }
-
-    /*
-    * shadow
-    */
-    .grw-subnav-append-shadow-container {
-      .grw-subnav {
-        box-shadow: 0px 0px 6px 3px rgba(black, 0.15);
-      }
-    }
-  }
-
-  &:global {
-    &.grw-subnav-switcher-hidden {
-      .grw-subnav-fixed-container {
-        transition: unset;
-        transform: translateY(-100%);
-      }
-    }
-  }
-}

+ 0 - 97
apps/app/_obsolete/src/components/Navbar/GrowiSubNavigationSwitcher.tsx

@@ -1,97 +0,0 @@
-import React, {
-  useState, useRef, useEffect, useCallback,
-} from 'react';
-
-import { debounce } from 'throttle-debounce';
-
-import { useSticky } from '~/client/services/side-effects/use-sticky';
-import { useSWRxCurrentPage } from '~/stores/page';
-import { useSidebarCollapsed } from '~/stores/ui';
-import loggerFactory from '~/utils/logger';
-
-import GrowiContextualSubNavigation from './GrowiContextualSubNavigation';
-
-import styles from './GrowiSubNavigationSwitcher.module.scss';
-
-const logger = loggerFactory('growi:cli:GrowiSubNavigationSticky');
-
-export type GrowiSubNavigationSwitcherProps = {
-  isLinkSharingDisabled: boolean,
-}
-
-/**
- * GrowiSubNavigation
- *
- * needs:
- *   #grw-subnav-fixed-container element
- *   #grw-subnav-sticky-trigger element
- */
-export const GrowiSubNavigationSwitcher = (props: GrowiSubNavigationSwitcherProps): JSX.Element => {
-  const { isLinkSharingDisabled } = props;
-
-  const { data: currentPage } = useSWRxCurrentPage();
-  const { data: isSidebarCollapsed } = useSidebarCollapsed();
-
-  const [width, setWidth] = useState<number>(0);
-
-  // use more specific type HTMLDivElement for avoid assertion error.
-  // see: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement
-  const fixedContainerRef = useRef<HTMLDivElement>(null);
-  const clientWidth = fixedContainerRef.current?.parentElement?.clientWidth;
-
-  // Get sticky status
-  const isSticky = useSticky('#grw-subnav-sticky-trigger');
-
-  // Do not use clientWidth as useCallback deps, resizing events will not work in production builds.
-  const initWidth = useCallback(() => {
-    if (fixedContainerRef.current != null && fixedContainerRef.current.parentElement != null) {
-      // get parent elements width
-      const { clientWidth } = fixedContainerRef.current.parentElement;
-      setWidth(clientWidth);
-    }
-  }, []);
-
-  // setup effect by resizing event
-  useEffect(() => {
-    const resizeHandler = debounce(100, initWidth);
-    window.addEventListener('resize', resizeHandler);
-
-    // return clean up handler
-    return () => {
-      window.removeEventListener('resize', resizeHandler);
-    };
-  }, [initWidth]);
-
-  // update width when sidebar collapsing changed
-  useEffect(() => {
-    if (isSidebarCollapsed != null) {
-      setTimeout(initWidth, 300);
-    }
-  }, [isSidebarCollapsed, initWidth]);
-
-  /*
-   * initialize width.
-   * Since width is not recalculated at production build first rendering,
-   * make initWidth execution dependent on clientWidth.
-   */
-  useEffect(() => {
-    if (clientWidth != null) initWidth();
-  }, [initWidth, clientWidth]);
-
-  if (currentPage == null) {
-    return <></>;
-  }
-
-  return (
-    <div className={`${styles['grw-subnav-switcher']} ${isSticky ? '' : 'grw-subnav-switcher-hidden'}`} data-testid="grw-subnav-switcher">
-      <div
-        id="grw-subnav-fixed-container"
-        className={`grw-subnav-fixed-container ${styles['grw-subnav-fixed-container']} position-fixed grw-subnav-append-shadow-container`}
-        ref={fixedContainerRef}
-        style={{ width }}
-      >
-        <GrowiContextualSubNavigation currentPage={currentPage} isCompactMode isLinkSharingDisabled={isLinkSharingDisabled} />
-      </div>
-    </div>
-  );
-};

+ 0 - 144
apps/app/_obsolete/src/components/PageEditor/AbstractEditor.tsx

@@ -1,144 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
-import React from 'react';
-
-import { ICodeMirror } from 'react-codemirror2';
-
-
-export interface AbstractEditorProps extends ICodeMirror {
-  value?: string;
-  isGfmMode?: boolean;
-  onScrollCursorIntoView?: (line: number) => void;
-  onSave?: () => Promise<void>;
-  onPasteFiles?: (event: Event) => void;
-  onCtrlEnter?: (event: Event) => void;
-}
-
-interface defaultProps {
-  isGfmMode: true,
-}
-
-export default class AbstractEditor<T extends AbstractEditorProps> extends React.Component<T, Record<string, unknown>> {
-
-  constructor(props: Readonly<T>) {
-    super(props);
-
-    this.forceToFocus = this.forceToFocus.bind(this);
-    this.setCaretLine = this.setCaretLine.bind(this);
-    this.setScrollTopByLine = this.setScrollTopByLine.bind(this);
-
-    this.getStrFromBol = this.getStrFromBol.bind(this);
-    this.getStrToEol = this.getStrToEol.bind(this);
-    this.insertText = this.insertText.bind(this);
-    this.insertLinebreak = this.insertLinebreak.bind(this);
-
-    this.dispatchSave = this.dispatchSave.bind(this);
-  }
-
-  public static defaultProps: defaultProps = {
-    isGfmMode: true,
-  };
-
-  forceToFocus(): void {}
-
-  /**
-   * set new value
-   */
-  setValue(_newValue: string): void {}
-
-  /**
-   * Enable/Disable GFM mode
-   * @param {bool} _bool
-   */
-  setGfmMode(_bool: boolean): void {}
-
-  /**
-   * set caret position of codemirror
-   * @param {string} number
-   */
-  setCaretLine(_line: number): void {}
-
-  /**
-   * scroll
-   * @param {number} _line
-   */
-  setScrollTopByLine(_line: number): void {}
-
-  /**
-   * return strings from BOL(beginning of line) to current position
-   */
-  getStrFromBol(): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * return strings from current position to EOL(end of line)
-   */
-  getStrToEol(): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * return strings from BOL(beginning of line) to current position
-   */
-  getStrFromBolToSelectedUpperPos(): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * replace Beggining Of Line to current position with param 'text'
-   * @param {string} _text
-   */
-  replaceBolToCurrentPos(_text: string): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * replace the current line with param 'text'
-   * @param {string} _text
-   */
-  replaceLine(_text: string): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * insert text
-   * @param {string} _text
-   */
-  insertText(_text: string): Error {
-    throw new Error('this method should be impelemented in subclass');
-  }
-
-  /**
-   * insert line break to the current position
-   */
-  insertLinebreak(): void {
-    this.insertText('\n');
-  }
-
-  /**
-   * dispatch onSave event
-   */
-  dispatchSave(): void {
-    if (this.props.onSave != null) {
-      this.props.onSave();
-    }
-  }
-
-  /**
-   * dispatch onPasteFiles event
-   * @param {object} event
-   */
-  dispatchPasteFiles(event: Event): void {
-    if (this.props.onPasteFiles != null) {
-      this.props.onPasteFiles(event);
-    }
-  }
-
-  /**
-   * returns items(an array of react elements) in navigation bar for editor
-   */
-  getNavbarItems(): null {
-    return null;
-  }
-
-}

+ 0 - 50
apps/app/_obsolete/src/components/PageEditor/Editor.module.scss

@@ -1,50 +0,0 @@
-@use '~/styles/mixins' as ms;
-@use '@growi/core/scss/bootstrap/init' as bs;
-@use './page-editor-inheritance';
-
-
-.editor-container :global {
-
-  .btn.btn-open-dropzone {
-    z-index: 2;
-    padding-top: 3px;
-    padding-bottom: 3px;
-    font-size: small;
-    border: none;
-    border-top: 1px dotted bs.$gray-300;
-    border-bottom: none;
-
-    &:hover,
-    &:focus {
-      border-bottom: none;
-    }
-  }
-
-  // for Navbar editor
-  .navbar-editor {
-    height: page-editor-inheritance.$navbar-editor-height;
-    padding: 0;
-
-    border-bottom: 1px solid transparent;
-
-    li {
-      display: inline-block;
-      i {
-        font-size: 16px;
-      }
-    }
-
-    button {
-      padding: 0px;
-      margin: 0 2px;
-      font-size: 1rem;
-      line-height: 1;
-      background-color: transparent;
-      border: none;
-    }
-
-    img {
-      vertical-align: bottom;
-    }
-  }
-}

+ 0 - 365
apps/app/_obsolete/src/components/PageEditor/Editor.tsx

@@ -1,365 +0,0 @@
-import type { ForwardRefRenderFunction } from 'react';
-import React, {
-  useState, useRef, useImperativeHandle, useCallback, forwardRef,
-  memo,
-  useEffect,
-} from 'react';
-
-import type { EditorSettings } from '@growi/editor';
-import Dropzone from 'react-dropzone';
-import { useTranslation } from 'react-i18next';
-import {
-  Modal, ModalHeader, ModalBody,
-} from 'reactstrap';
-
-import { toastError, toastSuccess } from '~/client/util/toastr';
-import { useDefaultIndentSize } from '~/stores/context';
-import { useEditorSettings } from '~/stores/editor';
-import { useIsMobile } from '~/stores/ui';
-
-import type { IEditorMethods } from '../../interfaces/editor-methods';
-
-import type AbstractEditor from './AbstractEditor';
-import { Cheatsheet } from './Cheatsheet';
-// import CodeMirrorEditor from './CodeMirrorEditor';
-import pasteHelper from './PasteHelper';
-import TextAreaEditor from './TextAreaEditor';
-
-
-import styles from './Editor.module.scss';
-
-export type EditorPropsType = {
-  value?: string,
-  isGfmMode?: boolean,
-  noCdn?: boolean,
-  isUploadable?: boolean,
-  isUploadAllFileAllowed?: boolean,
-  onChange?: (newValue: string, isClean?: boolean) => void,
-  onUpload?: (file) => void,
-  editorSettings?: EditorSettings,
-  indentSize?: number,
-  onDragEnter?: (event: any) => void,
-  onMarkdownHelpButtonClicked?: () => void,
-  onAddAttachmentButtonClicked?: () => void,
-  onScroll?: (line: { line: number }) => void,
-  onScrollCursorIntoView?: (line: number) => void,
-  onSave?: () => Promise<void>,
-  onPasteFiles?: (event: Event) => void,
-  onCtrlEnter?: (event: Event) => void,
-  isComment?: boolean,
-}
-
-type DropzoneRef = {
-  open: () => void
-}
-
-const Editor: ForwardRefRenderFunction<IEditorMethods, EditorPropsType> = (props, ref): JSX.Element => {
-  const {
-    onUpload, isUploadable, isUploadAllFileAllowed, indentSize, isGfmMode = true,
-  } = props;
-
-  const [dropzoneActive, setDropzoneActive] = useState(false);
-  const [isUploading, setIsUploading] = useState(false);
-  const [isCheatsheetModalShown, setIsCheatsheetModalShown] = useState(false);
-
-  const [navBarItems, setNavBarItems] = useState<JSX.Element[]>([]);
-
-  const { t } = useTranslation();
-  const { data: editorSettings } = useEditorSettings();
-  const { data: defaultIndentSize } = useDefaultIndentSize();
-  const { data: isMobile } = useIsMobile();
-
-  const dropzoneRef = useRef<DropzoneRef>(null);
-  // CodeMirrorEditor ref
-  const cmEditorRef = useRef<AbstractEditor<any>>(null);
-  const taEditorRef = useRef<TextAreaEditor>(null);
-
-  const editorSubstance = useCallback(() => {
-    return isMobile ? taEditorRef.current : cmEditorRef.current;
-  }, [isMobile]);
-
-  // methods for ref
-  useImperativeHandle(ref, () => ({
-    forceToFocus: () => {
-      editorSubstance()?.forceToFocus();
-    },
-    setValue: (newValue: string) => {
-      editorSubstance()?.setValue(newValue);
-    },
-    setGfmMode: (bool: boolean) => {
-      editorSubstance()?.setGfmMode(bool);
-    },
-    setCaretLine: (line: number) => {
-      editorSubstance()?.setCaretLine(line);
-    },
-    setScrollTopByLine: (line: number) => {
-      editorSubstance()?.setScrollTopByLine(line);
-    },
-    insertText: (text: string) => {
-      editorSubstance()?.insertText(text);
-    },
-    /**
-     * remove overlay and set isUploading to false
-     */
-    terminateUploadingState: () => {
-      setDropzoneActive(false);
-      setIsUploading(false);
-    },
-  }));
-
-  /**
-   * dispatch onUpload event
-   */
-  const dispatchUpload = useCallback((files) => {
-    if (onUpload != null) {
-      onUpload(files);
-    }
-  }, [onUpload]);
-
-  /**
-   * get acceptable(uploadable) file type
-   */
-  const getAcceptableType = useCallback(() => {
-    let accept = 'null'; // reject all
-    if (isUploadable) {
-      if (!isUploadAllFileAllowed) {
-        accept = 'image/*'; // image only
-      }
-      else {
-        accept = ''; // allow all
-      }
-    }
-
-    return accept;
-  }, [isUploadable, isUploadAllFileAllowed]);
-
-  const pasteFilesHandler = useCallback((event) => {
-    const items = event.clipboardData.items || event.clipboardData.files || [];
-
-    // abort if length is not 1
-    if (items.length < 1) {
-      return;
-    }
-
-    for (let i = 0; i < items.length; i++) {
-      try {
-        const file = items[i].getAsFile();
-        // check file type (the same process as Dropzone)
-        if (file != null && pasteHelper.isAcceptableType(file, getAcceptableType())) {
-          dispatchUpload(file);
-          setIsUploading(true);
-        }
-      }
-      catch (e) {
-        toastError(t('toaster.file_upload_failed'));
-      }
-    }
-  }, [dispatchUpload, getAcceptableType, t]);
-
-  const dragEnterHandler = useCallback((event) => {
-    const dataTransfer = event.dataTransfer;
-
-    // do nothing if contents is not files
-    if (!dataTransfer.types.includes('Files')) {
-      return;
-    }
-
-    setDropzoneActive(true);
-  }, []);
-
-  const dropHandler = useCallback((accepted) => {
-    // rejected
-    if (accepted.length !== 1) { // length should be 0 or 1 because `multiple={false}` is set
-      setDropzoneActive(false);
-      return;
-    }
-
-    const file = accepted[0];
-    dispatchUpload(file);
-    setIsUploading(true);
-  }, [dispatchUpload]);
-
-  const addAttachmentHandler = useCallback(() => {
-    if (dropzoneRef.current == null) { return }
-    dropzoneRef.current.open();
-  }, []);
-
-  const getDropzoneClassName = useCallback((isDragAccept: boolean, isDragReject: boolean) => {
-    let className = 'dropzone';
-    if (!isUploadable) {
-      className += ' dropzone-unuploadable';
-    }
-    else {
-      className += ' dropzone-uploadable';
-
-      if (isUploadAllFileAllowed) {
-        className += ' dropzone-uploadablefile';
-      }
-    }
-
-    // uploading
-    if (isUploading) {
-      className += ' dropzone-uploading';
-    }
-
-    if (isDragAccept) {
-      className += ' dropzone-accepted';
-    }
-
-    if (isDragReject) {
-      className += ' dropzone-rejected';
-    }
-
-    return className;
-  }, [isUploadable, isUploading, isUploadAllFileAllowed]);
-
-  const renderDropzoneOverlay = useCallback(() => {
-    return (
-      <div className="overlay overlay-dropzone-active">
-        {isUploading
-          && (
-            <span className="overlay-content">
-              <div className="speeding-wheel d-inline-block"></div>
-              <span className="visually-hidden">Uploading...</span>
-            </span>
-          )
-        }
-        {!isUploading && <span className="overlay-content"></span>}
-      </div>
-    );
-  }, [isUploading]);
-
-  const renderNavbar = () => {
-    return (
-      <div className="m-0 navbar navbar-default navbar-editor" data-testid="navbar-editor" style={{ minHeight: 'unset' }}>
-        <ul className="ps-2 nav nav-navbar">
-          { navBarItems.map((item, idx) => {
-            // eslint-disable-next-line react/no-array-index-key
-            return <li key={`navbarItem-${idx}`}>{item}</li>;
-          }) }
-        </ul>
-      </div>
-    );
-  };
-
-  const renderCheatsheetModal = useCallback(() => {
-    const hideCheatsheetModal = () => {
-      setIsCheatsheetModalShown(false);
-    };
-
-    return (
-      <Modal isOpen={isCheatsheetModalShown} toggle={hideCheatsheetModal} className={`modal-gfm-cheatsheet ${styles['modal-gfm-cheatsheet']}`} size="lg">
-        <ModalHeader tag="h4" toggle={hideCheatsheetModal} className="bg-primary text-light">
-          <span className="material-symbols-outlined me-1">help</span>Markdown help
-        </ModalHeader>
-        <ModalBody>
-          <Cheatsheet />
-        </ModalBody>
-      </Modal>
-    );
-  }, [isCheatsheetModalShown]);
-
-  const isReadyToRenderEditor = editorSettings != null;
-
-  // https://redmine.weseek.co.jp/issues/111731
-  useEffect(() => {
-    const editorRef = editorSubstance();
-    if (isReadyToRenderEditor && editorRef != null) {
-      const editorNavBarItems = editorRef.getNavbarItems() ?? [];
-      setNavBarItems(editorNavBarItems);
-    }
-  }, [editorSubstance, isReadyToRenderEditor]);
-
-  if (!isReadyToRenderEditor) {
-    return <></>;
-  }
-
-  const flexContainer: React.CSSProperties = {
-    height: '100%',
-    display: 'flex',
-    flexDirection: 'column',
-  };
-
-  return (
-    <>
-      <div style={flexContainer} className={`editor-container ${styles['editor-container']}`}>
-        <Dropzone
-          ref={dropzoneRef}
-          accept={getAcceptableType()}
-          noClick
-          noKeyboard
-          multiple={false}
-          onDragLeave={() => { setDropzoneActive(false) }}
-          onDrop={dropHandler}
-        >
-          {({
-            getRootProps,
-            getInputProps,
-            isDragAccept,
-            isDragReject,
-          }) => {
-            return (
-              <div className={getDropzoneClassName(isDragAccept, isDragReject)} {...getRootProps()}>
-                { dropzoneActive && renderDropzoneOverlay() }
-
-                { renderNavbar() }
-
-                {/* for PC */}
-                { !isMobile && (
-                  // <CodeMirrorEditor
-                  //   ref={cmEditorRef}
-                  //   indentSize={indentSize ?? defaultIndentSize}
-                  //   onPasteFiles={pasteFilesHandler}
-                  //   onDragEnter={dragEnterHandler}
-                  //   onMarkdownHelpButtonClicked={() => { setIsCheatsheetModalShown(true) }}
-                  //   onAddAttachmentButtonClicked={addAttachmentHandler}
-                  //   editorSettings={editorSettings}
-                  //   isGfmMode={isGfmMode}
-                  //   {...props}
-                  // />
-                  <></>
-                )}
-
-                {/* for mobile */}
-                { isMobile && (
-                  <TextAreaEditor
-                    ref={taEditorRef}
-                    onPasteFiles={pasteFilesHandler}
-                    onDragEnter={dragEnterHandler}
-                    {...props}
-                  />
-                )}
-
-                <input {...getInputProps()} />
-              </div>
-            );
-          }}
-        </Dropzone>
-
-        { isUploadable
-          && (
-            <button
-              type="button"
-              className="btn btn-outline-secondary btn-open-dropzone"
-              onClick={addAttachmentHandler}
-            >
-              <span className="material-symbols-outlined" aria-hidden="true">attachment</span>&nbsp;
-              Attach files
-              <span className="d-none d-sm-inline">
-              &nbsp;by dragging &amp; dropping,&nbsp;
-                <span className="btn-link">selecting them</span>,&nbsp;
-                or pasting from the clipboard.
-              </span>
-
-            </button>
-          )
-        }
-
-        { renderCheatsheetModal() }
-
-      </div>
-    </>
-  );
-};
-
-export default memo(forwardRef(Editor));

+ 0 - 159
apps/app/_obsolete/src/components/PageEditor/EditorIcon.jsx

@@ -1,159 +0,0 @@
-/* eslint-disable max-len */
-import React from 'react';
-
-import PropTypes from 'prop-types';
-
-const EditorIcon = (props) => {
-
-  switch (props.icon) {
-    case 'Bold':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M9.71,21.14V8.86A.84.84,0,0,1,10.59,8h4.46c2.41,0,4.05,1.41,4.05,3.52a3.17,3.17,0,0,1-2.44,3.08v.07a3.39,3.39,0,0,1,3.15,3.47c0,2.48-1.78,4-4.78,4H10.59A.84.84,0,0,1,9.71,21.14ZM14.11,14c2.08,0,3.21-.83,3.21-2.36s-1-2.16-2.67-2.16H11.47V14Zm.66,6.46c2.12,0,3.23-.86,3.23-2.49s-1.15-2.46-3.4-2.46H11.47v4.95Z" />
-        </svg>
-      );
-    case 'Italic':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M18.55,8a.48.48,0,0,1,.45.5.45.45,0,0,1-.39.5H16.75L14.51,21h1.62a.45.45,0,0,1,.46.5.47.47,0,0,1-.54.5h-4.6a.53.53,0,0,1-.47-.5.47.47,0,0,1,.48-.5h2L15.83,9H14a.52.52,0,0,1-.5-.5A.51.51,0,0,1,14,8Z" />
-        </svg>
-      );
-    case 'Strikethrough':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M22.5,14H7.5a.47.47,0,0,0-.5.5.46.46,0,0,0,.5.5h15a.5.5,0,0,0,0-1Z" />
-          <path d="M18,17a2.21,2.21,0,0,1,.6,1.88c-.07.51-.53,2.18-3.31,2.18a5.35,5.35,0,0,1-4.21-1.76L11,18.5c-.05-.3-.21-.5-.5-.5s-.45.17-.5.5v1A5.79,5.79,0,0,0,15,22c3.75,0,4.41-2.11,4.53-2.53A3.12,3.12,0,0,0,19.28,17Z" />
-          <path d="M12.21,13h1.91c-1.27-.44-2.37-1.52-2.1-2.5.18-.65,1-1.59,3.27-1.59a4.21,4.21,0,0,1,3.44,1.41l.07.37a.55.55,0,1,0,1.08-.19l-.09-.5-.08-.2A6.28,6.28,0,0,0,15,8c-3.11,0-3.95,1.74-4,2.33A2.32,2.32,0,0,0,12.21,13Z" />
-        </svg>
-      );
-    case 'Heading':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M10,21V9.11a.61.61,0,1,1,1.22,0v5.24h7.55V9.11a.59.59,0,0,1,.62-.64.58.58,0,0,1,.61.64V21a.58.58,0,0,1-.61.63.59.59,0,0,1-.62-.63V15.46H11.22V21A.61.61,0,1,1,10,21Z" />
-        </svg>
-      );
-    case 'InlineCode':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M11,19.57a.54.54,0,0,1-.36-.14L7.05,15.79a.49.49,0,0,1,0-.67l4.09-5a.5.5,0,0,1,.71-.07.5.5,0,0,1,.07.7L8.08,15.41l3.31,3.31a.5.5,0,0,1,0,.71A.54.54,0,0,1,11,19.57Z" /><path d="M18.5,20a.51.51,0,0,1-.32-.12.5.5,0,0,1-.07-.7l3.81-4.63-3.36-3.36a.5.5,0,0,1,0-.71.51.51,0,0,1,.71,0L23,14.21a.49.49,0,0,1,0,.67l-4.09,5A.52.52,0,0,1,18.5,20Z" /><path d="M13,21.5a.41.41,0,0,1-.16,0,.5.5,0,0,1-.32-.63l4-12a.5.5,0,0,1,.63-.31.49.49,0,0,1,.32.63l-4,12A.49.49,0,0,1,13,21.5Z" />
-        </svg>
-      );
-    case 'Quote':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M11.5,14h9a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-9a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,14Z" /><path d="M8.5,9h11a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5H8.5A.5.5,0,0,1,8,9.5H8A.5.5,0,0,1,8.5,9Z" /><path d="M11.5,19h7a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-7a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,19Z" /><path d="M8,20.5v-8a.5.5,0,0,1,.5-.5h0a.5.5,0,0,1,.5.5v8a.5.5,0,0,1-.5.5h0A.5.5,0,0,1,8,20.5Z" />
-        </svg>
-      );
-    case 'List':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <circle cx="8.5" cy="9.5" r="1" /><circle cx="8.5" cy="14.5" r="1" /><circle cx="8.5" cy="19.5" r="1" /><path d="M11.5,9h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,9Z" /><path d="M11.5,14h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,14Z" /><path d="M11.5,19h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,19Z" />
-        </svg>
-      );
-    case 'NumberedList':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M11.5,9h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,9Z" /><path d="M11.5,19h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,19Z" /><path d="M11.5,14h10a.5.5,0,0,1,.5.5h0a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5h0A.5.5,0,0,1,11.5,14Z" /><path d="M7.44,12h.85V8.62L7.58,9h-.1a.24.24,0,0,1-.29-.19.25.25,0,0,1,.19-.3l.83-.41A.77.77,0,0,1,8.5,8h.08a.29.29,0,0,1,.29.27V12H9.6a.27.27,0,1,1,.1.53H7.44a.27.27,0,0,1,0-.53Z" /><path d="M7.61,17.13a.26.26,0,0,1-.26.26h0c-.14,0-.27-.09-.26-.32v-.4a.48.48,0,0,1,.27-.42,1.93,1.93,0,0,1,1-.25,1.3,1.3,0,0,1,1.42,1.15v.22a2.54,2.54,0,0,1-1,1.74l-1,.94H9.58a.27.27,0,0,1,0,.53H7.32A.32.32,0,0,1,7,20.26v0a.47.47,0,0,1,.2-.34l1.2-1.17a2.12,2.12,0,0,0,.79-1.36.8.8,0,0,0-.75-.85H8.35a1.32,1.32,0,0,0-.7.2Z" />
-        </svg>
-      );
-    case 'CheckList':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M20.5,12.5a.57.57,0,0,1,.5.5v8a1.17,1.17,0,0,1-1,1H10a1.28,1.28,0,0,1-1-1V11a1.28,1.28,0,0,1,1-1h6a.57.57,0,0,1,.5.5.57.57,0,0,1-.5.5H10V21H20V13A.58.58,0,0,1,20.5,12.5ZM12,15.25a.41.41,0,0,0,0,.58L14,18a.78.78,0,0,0,1,0l5.9-9c.09-.16.09-.5-.16-.58a.41.41,0,0,0-.58.08L14.5,17.16l-1.91-1.91A.41.41,0,0,0,12,15.25Z" />
-        </svg>
-      );
-    case 'Link':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M12.12,18a.29.29,0,0,0,.43,0l5.3-5.3c.07-.08.3-.25.06-.51a.32.32,0,0,0-.53,0l-5.22,5.28C12.09,17.57,11.85,17.73,12.12,18Zm2.34-.9a1.74,1.74,0,0,1,0,1,3.69,3.69,0,0,1-.43.78L12.2,20.5a1.69,1.69,0,0,1-1.31.61A1.77,1.77,0,0,1,9.5,20.5a1.65,1.65,0,0,1-.61-1.31A1.74,1.74,0,0,1,9.5,17.8L11.15,16a1.74,1.74,0,0,1,1.92-.43l.69-.7a2.67,2.67,0,0,0-1.21-.26,2.89,2.89,0,0,0-2,.78L8.89,17.19a2.8,2.8,0,0,0-.09,3.92l0,0a2.62,2.62,0,0,0,2.05.83,2.79,2.79,0,0,0,2-.87l1.74-1.66A3.93,3.93,0,0,0,15.42,18a2.56,2.56,0,0,0-.26-1.56Zm6.61-8.18a2.11,2.11,0,0,0-.87-.69,2.69,2.69,0,0,0-3,.69l-1.83,1.66a2.68,2.68,0,0,0-.78,1.56,2.72,2.72,0,0,0,.26,1.66l.69-.7a2,2,0,0,1,0-1,2,2,0,0,1,.44-1l1.83-1.66L18,9.27l.35-.17.34-.18h.44a1.67,1.67,0,0,1,1.3.61,1.74,1.74,0,0,1,.61,1.4,1.62,1.62,0,0,1-.61,1.3l-1.74,1.83-.6.35a1.37,1.37,0,0,1-.79.17H17l-.69.7a8.58,8.58,0,0,0,1,.22,2.9,2.9,0,0,0,1.21-.22l.87-.61,1.74-1.83a2.6,2.6,0,0,0,.23-3.69Z" />
-        </svg>
-      );
-    case 'Image':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M22,8H8A1,1,0,0,0,7,9V21a1,1,0,0,0,1,1H22a1,1,0,0,0,1-1V9A1,1,0,0,0,22,8Zm0,13H8V18l4.07-4.06,4.07,4a.41.41,0,0,0,.33.18.4.4,0,0,0,.32-.18l1.7-1.55,3.17,3.25L22,20Zm0-2.25-3.1-3.34a.89.89,0,0,0-.33-.17.89.89,0,0,0-.28.14l-1.83,1.49-4-3.9a.49.49,0,0,0-.32-.16.5.5,0,0,0-.41.16L8,16.75V9H22ZM19.5,12.5a1,1,0,1,1-1-1A1,1,0,0,1,19.5,12.5Z" />
-        </svg>
-      );
-    case 'Grid':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
-          <rect width="30" height="30" fill="none" />
-          <g transform="translate(-375 -415)">
-            <g transform="translate(382 422)">
-              <path d="M5,7H1A.945.945,0,0,1,0,6V1A.945.945,0,0,1,1,0H5A.945.945,0,0,1,6,1V6A.945.945,0,0,1,5,7ZM1,1V6H5V1ZM1,.5V1H1Z" />
-            </g>
-            <g transform="translate(390 422)">
-              <path d="M7,7H1A.945.945,0,0,1,0,6V1A.945.945,0,0,1,1,0H7A.945.945,0,0,1,8,1V6A.945.945,0,0,1,7,7ZM1,1V6H7V1ZM1,.5V1H1Z" />
-            </g>
-            <g transform="translate(382 431)">
-              <path d="M9,7H1A.945.945,0,0,1,0,6V1A.945.945,0,0,1,1,0H9a.945.945,0,0,1,1,1V6A.945.945,0,0,1,9,7ZM1,1V6H9V1ZM1,.5V1H1Z" />
-            </g>
-            <g transform="translate(394 431)">
-              <path d="M3,7H1A.945.945,0,0,1,0,6V1A.945.945,0,0,1,1,0H3A.945.945,0,0,1,4,1V6A.945.945,0,0,1,3,7ZM1,1V6H3V1ZM1,.5V1H1Z" />
-            </g>
-          </g>
-        </svg>
-      );
-    case 'Table':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M8,22a1,1,0,0,1-1-1V9A1,1,0,0,1,8,8H22c1,0,1,.47,1,1V21a1,1,0,0,1-1,1Zm10-4v3h4V18Zm-5,0v3h4V18ZM8,18v3h4V18Zm10-4v3h4V14Zm-5,0v3h4V14ZM8,14v3h4V14Zm10-4v3h4V10Zm-5,0v3h4V10ZM8,10v3h4V10Z" />
-        </svg>
-      );
-    case 'Drawio':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M22.12,17H19.75l-3.12-4H18a1,1,0,0,0,1-1V8a1,1,0,0,0-1-1H12a1,1,0,0,0-1,1v4a1,1,0,0,0,1,1h1.38l-2.92,4H7.88A.94.94,0,0,0,7,18v4a.94.94,0,0,0,.88,1h5.24A.94.94,0,0,0,14,22V18a.94.94,0,0,0-.88-1H11.63l3.13-4h.47l3.13,4H16.88A.94.94,0,0,0,16,18v4a.94.94,0,0,0,.88,1h5.24A.94.94,0,0,0,23,22V18A.94.94,0,0,0,22.12,17ZM13,22H8V18h5ZM12,8h6v4H12ZM22,22H17V18h5Z" />
-        </svg>
-      );
-    case 'Attachment':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" height="30" viewBox="0 0 30 30">
-          <rect fillOpacity="0" width="30" height="30" />
-          <path d="M9.71,22.5a2.57,2.57,0,0,1-1.85-.79,2.79,2.79,0,0,1,0-4l9-9.23a3.21,3.21,0,0,1,1.59-.87,3.39,3.39,0,0,1,1.81.1,4.38,4.38,0,0,1,1.7,1.05,4.15,4.15,0,0,1,.46.56,3.73,3.73,0,0,1,.35.65,4.25,4.25,0,0,1,.2.72,3.91,3.91,0,0,1,.07.76,3.71,3.71,0,0,1-1.12,2.67l-6.79,7a.48.48,0,0,1-.34.16.51.51,0,0,1-.35-.13.48.48,0,0,1,0-.7l6.78-7a2.8,2.8,0,0,0,.84-2,2.58,2.58,0,0,0-.79-2,3.63,3.63,0,0,0-1.11-.75,2.41,2.41,0,0,0-1.31-.17,2.19,2.19,0,0,0-1.25.62l-9,9.22A1.8,1.8,0,0,0,8,19.69,1.78,1.78,0,0,0,8.58,21a1.81,1.81,0,0,0,.57.39,1.48,1.48,0,0,0,.66.1,2,2,0,0,0,1.28-.62l7.12-7.35.15-.16a1.15,1.15,0,0,0,.15-.2.9.9,0,0,0,.12-.24,1.17,1.17,0,0,0,.07-.25.52.52,0,0,0-.05-.27.75.75,0,0,0-.19-.26.73.73,0,0,0-.58-.27,1.29,1.29,0,0,0-.67.38l-5.36,5.53a.5.5,0,0,1-.22.13.46.46,0,0,1-.26,0,.48.48,0,0,1-.22-.12A.41.41,0,0,1,11,17.5a.5.5,0,0,1,.14-.35L16.5,11.6a2.19,2.19,0,0,1,1.29-.67,1.69,1.69,0,0,1,1.37.55,1.54,1.54,0,0,1,.53,1.31,2.26,2.26,0,0,1-.76,1.42L11.8,21.58a3.06,3.06,0,0,1-2,.91H9.71Z" />
-        </svg>
-      );
-    case 'Emoji':
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30">
-          <g transform="translate(-435 -392)">
-            <rect width="30" height="30" transform="translate(435 392)" fillOpacity="0" />
-            <path d="M8,1a7,7,0,1,0,7,7A7.008,7.008,0,0,0,8,1M8,0A8,8,0,1,1,0,8,8,8,0,0,1,8,0Z" transform="translate(442 399)" />
-            <circle cx="1" cy="1" r="1" transform="translate(446 403)" />
-            <circle cx="1" cy="1" r="1" transform="translate(452 403)" />
-            <g transform="translate(445 406.5)">
-              <path d="M5,5.5a5.006,5.006,0,0,1-5-5,.5.5,0,1,1,1,0,4,4,0,0,0,8,0,.5.5,0,0,1,1,0A5.006,5.006,0,0,1,5,5.5Z" />
-            </g>
-          </g>
-        </svg>
-      );
-    case 'Template':
-      // TODO: fix
-      return (
-        <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" className="bi bi-filetype-md" viewBox="-2 -3 28 21">
-          <path fillRule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2H9v-1h3a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM.706 13.189v2.66H0V11.85h.806l1.14 2.596h.026l1.14-2.596h.8v3.999h-.716v-2.66h-.038l-.946 2.159h-.516l-.952-2.16H.706Zm3.919 2.66V11.85h1.459c.406 0 .741.078 1.005.234.263.157.46.383.589.68.13.297.196.655.196 1.075 0 .422-.066.784-.196 1.084-.131.301-.33.53-.595.689-.264.158-.597.237-1 .237H4.626Zm1.353-3.354h-.562v2.707h.562c.186 0 .347-.028.484-.082a.8.8 0 0 0 .334-.252 1.14 1.14 0 0 0 .196-.422c.045-.168.067-.365.067-.592a2.1 2.1 0 0 0-.117-.753.89.89 0 0 0-.354-.454c-.159-.102-.362-.152-.61-.152Z" />
-        </svg>
-      );
-  }
-
-
-};
-
-EditorIcon.propTypes = {
-  icon: PropTypes.string.isRequired,
-};
-
-export default EditorIcon;

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

@@ -1,62 +0,0 @@
-import React, { FC, useCallback } from 'react';
-
-import { Picker } from 'emoji-mart';
-import { Modal } from 'reactstrap';
-
-import { useNextThemes } from '~/stores/use-next-themes';
-
-import EmojiPickerHelper, { getEmojiTranslation } from './EmojiPickerHelper';
-
-
-import 'emoji-mart/css/emoji-mart.css';
-
-
-type Props = {
-  onClose: () => void,
-  onSelected: (emoji: string) => void,
-  emojiSearchText: string,
-  emojiPickerHelper: EmojiPickerHelper,
-  isOpen: boolean
-}
-
-const EmojiPicker: FC<Props> = (props: Props) => {
-
-  const {
-    onClose, onSelected, emojiSearchText, emojiPickerHelper, isOpen,
-  } = props;
-
-  const { resolvedTheme } = useNextThemes();
-
-  // Set search emoji input and trigger search
-  const searchEmoji = useCallback(() => {
-    const input = window.document.querySelector('[id^="emoji-mart-search"]') as HTMLInputElement;
-    const valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
-    valueSetter?.call(input, emojiSearchText);
-    const event = new Event('input', { bubbles: true });
-    input.dispatchEvent(event);
-    input.focus();
-  }, [emojiSearchText]);
-
-  const selectEmoji = useCallback((emoji) => {
-    onSelected(emoji);
-    onClose();
-  }, [onClose, onSelected]);
-
-
-  const translation = getEmojiTranslation();
-
-  return (
-    <Modal isOpen={isOpen} toggle={onClose} onOpened={searchEmoji} backdropClassName="emoji-picker-modal" fade={false}>
-      <Picker
-        onSelect={selectEmoji}
-        i18n={translation}
-        title={translation.title}
-        emojiTooltip
-        style={emojiPickerHelper.setStyle()}
-        theme={resolvedTheme}
-      />
-    </Modal>
-  );
-};
-
-export default EmojiPicker;

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

@@ -1,116 +0,0 @@
-import { CSSProperties } from 'react';
-
-import { Position } from 'codemirror';
-import i18n from 'i18next';
-
-// https://regex101.com/r/x5LbOZ/1
-const EMOJI_PATTERN = new RegExp(/^:[a-z0-9-+_]+$/);
-
-export default class EmojiPickerHelper {
-
-  editor;
-
-  pattern: string;
-
-  constructor(editor) {
-    this.editor = editor;
-  }
-
-  setStyle = (): CSSProperties => {
-    const offset = 20;
-    const emojiPickerHeight = 420;
-    const cursorPos = this.editor.cursorCoords(true);
-    const editorPos = this.editor.getWrapperElement().getBoundingClientRect();
-    // Emoji Picker bottom position exceed editor's bottom position
-    if (cursorPos.bottom + emojiPickerHeight > editorPos.bottom) {
-      return {
-        top: editorPos.bottom - emojiPickerHeight,
-        left: cursorPos.left + offset,
-        position: 'fixed',
-      };
-    }
-    return {
-      top: cursorPos.top + offset,
-      left: cursorPos.left + offset,
-      position: 'fixed',
-    };
-  };
-
-  shouldModeTurnOn = (char: string): Position | null | undefined => {
-    if (char !== ':') {
-      return null;
-    }
-
-    const currentPos = this.editor.getCursor();
-    const sc = this.editor.getSearchCursor(':', currentPos, { multiline: false });
-    if (sc.findPrevious()) {
-      return sc.pos.from;
-    }
-  };
-
-  shouldOpen = (startPos: Position): boolean => {
-    const currentPos = this.editor.getCursor();
-    const rangeStr = this.editor.getRange(startPos, currentPos);
-
-    return EMOJI_PATTERN.test(rangeStr);
-  };
-
-  getInitialSearchingText = (startPos: Position): void => {
-    const currentPos = this.editor.getCursor();
-    const rangeStr = this.editor.getRange(startPos, currentPos);
-
-    return rangeStr.slice(1); // return without the heading ':'
-  };
-
-  addEmoji = (emoji: { colons: string }, startPosToReplace: Position|null): void => {
-    const currentPos = this.editor.getCursor();
-
-    const from = startPosToReplace ?? currentPos;
-    const to = currentPos;
-
-    const doc = this.editor.getDoc();
-    doc.replaceRange(`${emoji.colons} `, from, to);
-    this.editor.focus();
-    this.editor.refresh();
-  };
-
-}
-
-export const getEmojiTranslation = () => {
-
-  const categories = {};
-  [
-    'search',
-    'recent',
-    'smileys',
-    'people',
-    'nature',
-    'foods',
-    'activity',
-    'places',
-    'objects',
-    'symbols',
-    'flags',
-    'custom',
-  ].forEach((category) => {
-    categories[category] = i18n.t(`emoji.categories.${category}`);
-  });
-
-  const skintones = {};
-  (Array.from(Array(6).keys())).forEach((tone) => {
-    skintones[tone + 1] = i18n.t(`emoji.skintones.${tone + 1}`);
-  });
-
-  const translation = {
-    search: i18n.t('emoji.search'),
-    clear: i18n.t('emoji.clear'),
-    notfound: i18n.t('emoji.notfound'),
-    skintext: i18n.t('emoji.skintext'),
-    categories,
-    categorieslabel: i18n.t('emoji.categorieslabel'),
-    skintones,
-    title: i18n.t('emoji.title'),
-  };
-
-  return translation;
-};

+ 0 - 46
apps/app/_obsolete/src/components/PageEditor/MarkdownLinkUtil.js

@@ -1,46 +0,0 @@
-import Linker from '@growi/editor/src/services/link-util/Linker';
-/**
- * Utility for markdown link
- */
-class MarkdownLinkUtil {
-
-  constructor() {
-    this.getMarkdownLink = this.getMarkdownLink.bind(this);
-    this.isInLink = this.isInLink.bind(this);
-    this.replaceFocusedMarkdownLinkWithEditor = this.replaceFocusedMarkdownLinkWithEditor.bind(this);
-  }
-
-  // return an instance of Linker from cursor position or selected text.
-  getMarkdownLink(editor) {
-    if (!this.isInLink(editor)) {
-      return Linker.fromMarkdownString(editor.getDoc().getSelection());
-    }
-    const curPos = editor.getCursor();
-    return Linker.fromLineWithIndex(editor.getDoc().getLine(curPos.line), curPos.ch);
-  }
-
-  isInLink(editor) {
-    const curPos = editor.getCursor();
-    const { beginningOfLink, endOfLink } = Linker.getBeginningAndEndIndexOfLink(editor.getDoc().getLine(curPos.line), curPos.ch);
-    return beginningOfLink >= 0 && endOfLink >= 0;
-  }
-
-  // replace link(link is an instance of Linker)
-  replaceFocusedMarkdownLinkWithEditor(editor, linkText) {
-    const curPos = editor.getCursor();
-    if (!this.isInLink(editor)) {
-      editor.getDoc().replaceSelection(linkText);
-    }
-    else {
-      const line = editor.getDoc().getLine(curPos.line);
-      const { beginningOfLink, endOfLink } = Linker.getBeginningAndEndIndexOfLink(line, curPos.ch);
-      editor.getDoc().replaceRange(linkText, { line: curPos.line, ch: beginningOfLink }, { line: curPos.line, ch: endOfLink });
-    }
-  }
-
-}
-
-// singleton pattern
-const instance = new MarkdownLinkUtil();
-Object.freeze(instance);
-export default instance;

+ 0 - 95
apps/app/_obsolete/src/components/PageEditor/MarkdownTableInterceptor.js

@@ -1,95 +0,0 @@
-import { BasicInterceptor } from '@growi/core/dist/utils';
-
-import MarkdownTable from '~/client/models/MarkdownTable';
-
-import {
-  getStrFromBot, addRowToMarkdownTable, getStrToEot, isEndOfLine, mergeMarkdownTable, replaceFocusedMarkdownTableWithEditor,
-  isInTable, emptyLineOfTableRE,
-} from '../../../../src/components/PageEditor/markdown-table-util-for-editor';
-
-/**
- * Interceptor for markdown table
- */
-export default class MarkdownTableInterceptor extends BasicInterceptor {
-
-  /**
-   * @inheritdoc
-   */
-  isInterceptWhen(contextName) {
-    return (
-      contextName === 'preHandleEnter'
-    );
-  }
-
-  /**
-   * return boolean value whether processable parallel
-   */
-  isProcessableParallel() {
-    return false;
-  }
-
-  addRow(cm) {
-    // get lines all of table from current position to beginning of table
-    const strFromBot = getStrFromBot(cm);
-    let table = MarkdownTable.fromMarkdownString(strFromBot);
-
-    addRowToMarkdownTable(table);
-
-    const strToEot = getStrToEot(cm);
-    const tableBottom = MarkdownTable.fromMarkdownString(strToEot);
-    if (tableBottom.table.length > 0) {
-      table = mergeMarkdownTable([table, tableBottom]);
-    }
-
-    replaceFocusedMarkdownTableWithEditor(cm, table);
-  }
-
-  reformTable(cm) {
-    const tableStr = getStrFromBot(cm) + getStrToEot(cm);
-    const table = MarkdownTable.fromMarkdownString(tableStr);
-    replaceFocusedMarkdownTableWithEditor(cm, table);
-  }
-
-  removeRow(editor) {
-    editor.replaceLine('\n');
-  }
-
-  /**
-   * @inheritdoc
-   */
-  async process(contextName, ...args) {
-    const context = Object.assign(args[0]); // clone
-    const editor = context.editor; // AbstractEditor instance
-    // "autoFormatMarkdownTable" may be undefined, so it is compared to true and converted to bool.
-    const noIntercept = (context.autoFormatMarkdownTable === false);
-
-    // do nothing if editor is not a CodeMirrorEditor or no intercept
-    if (editor == null || editor.getCodeMirror() == null || noIntercept) {
-      return context;
-    }
-
-    const cm = editor.getCodeMirror();
-
-    const isLastRow = getStrToEot(cm) === editor.getStrToEol();
-
-    if (isInTable(cm)) {
-      // at EOL in the table
-      if (isEndOfLine(cm)) {
-        this.addRow(cm);
-      }
-      // last empty row
-      else if (isLastRow && emptyLineOfTableRE.test(editor.getStrFromBol() + editor.getStrToEol())) {
-        this.removeRow(editor);
-      }
-      else {
-        this.reformTable(cm);
-      }
-
-      // report to manager that handling was done
-      context.handlers.push(this.className);
-      return context;
-    }
-
-  }
-
-}

+ 0 - 44
apps/app/_obsolete/src/components/PageEditor/PasteHelper.js

@@ -1,44 +0,0 @@
-import accepts from 'attr-accept';
-
-import markdownListUtil from './MarkdownListUtil';
-
-class PasteHelper {
-
-  constructor() {
-    this.pasteText = this.pasteText.bind(this);
-  }
-
-  /**
-   * paste text
-   * @param {any} editor An editor instance of CodeMirror
-   * @param {any} event
-   */
-  pasteText(editor, event) {
-    // get data in clipboard
-    const text = event.clipboardData.getData('text/plain');
-
-    if (text.length === 0) {
-      return;
-    }
-
-    markdownListUtil.pasteText(editor, event, text);
-  }
-
-  // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with
-  /**
-   * transplanted from react-dropzone
-   * @see https://github.com/react-dropzone/react-dropzone/blob/master/src/utils/index.js
-   *
-   * @param {*} file
-   * @param {*} accept
-   */
-  isAcceptableType(file, accept) {
-    return file.type === 'application/x-moz-file' || accepts(file, accept);
-  }
-
-}
-
-// singleton pattern
-const instance = new PasteHelper();
-Object.freeze(instance);
-export default instance;

+ 0 - 44
apps/app/_obsolete/src/components/PageEditor/PreventMarkdownListInterceptor.js

@@ -1,44 +0,0 @@
-import { BasicInterceptor } from '@growi/core/dist/utils';
-
-import mlu from './MarkdownListUtil';
-
-export default class PreventMarkdownListInterceptor extends BasicInterceptor {
-
-  /**
-   * @inheritdoc
-   */
-  isInterceptWhen(contextName) {
-    return (
-      contextName === 'preHandleEnter'
-    );
-  }
-
-  /**
-   * return boolean value whether processable parallel
-   */
-  isProcessableParallel() {
-    return false;
-  }
-
-  /**
-   * @inheritdoc
-   */
-  process(contextName, ...args) {
-    const context = Object.assign(args[0]); // clone
-    const editor = context.editor; // AbstractEditor instance
-
-    // get strings from current position to EOL(end of line) before break the line
-    const strToEol = editor.getStrToEol();
-    if (mlu.indentAndMarkRE.test(strToEol)) {
-      // newline simply
-      editor.insertLinebreak();
-
-      // report to manager that handling was done
-      context.handlers.push(this.className);
-    }
-
-    // resolve
-    return Promise.resolve(context);
-  }
-
-}

+ 0 - 274
apps/app/_obsolete/src/components/PageEditor/TextAreaEditor.jsx

@@ -1,274 +0,0 @@
-import React from 'react';
-// import PropTypes from 'prop-types';
-
-import { Input } from 'reactstrap';
-
-import InterceptorManager from '~/services/interceptor-manager';
-import loggerFactory from '~/utils/logger';
-
-
-import AbstractEditor from './AbstractEditor';
-import mlu from './MarkdownListUtil';
-import pasteHelper from './PasteHelper';
-import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
-
-export default class TextAreaEditor extends AbstractEditor {
-
-  constructor(props) {
-    super(props);
-    this.logger = loggerFactory('growi:PageEditor:TextAreaEditor');
-
-    this.state = {
-      value: this.props.value,
-      isGfmMode: this.props.isGfmMode,
-    };
-
-    this.textarea = React.createRef();
-
-    this.init();
-
-    this.handleEnterKey = this.handleEnterKey.bind(this);
-
-    this.keyPressHandler = this.keyPressHandler.bind(this);
-    this.pasteHandler = this.pasteHandler.bind(this);
-    this.dragEnterHandler = this.dragEnterHandler.bind(this);
-  }
-
-  init() {
-    this.interceptorManager = new InterceptorManager();
-    this.interceptorManager.addInterceptors([
-      new PreventMarkdownListInterceptor(),
-    ]);
-  }
-
-  componentDidMount() {
-    // initialize caret line
-    this.setCaretLine(0);
-
-    // set event handlers
-    this.textarea.addEventListener('keypress', this.keyPressHandler);
-    this.textarea.addEventListener('paste', this.pasteHandler);
-    this.textarea.addEventListener('dragenter', this.dragEnterHandler);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  forceToFocus() {
-    setTimeout(() => {
-      this.textarea.focus();
-    }, 150);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  setValue(newValue) {
-    this.setState({ value: newValue });
-    this.textarea.value = newValue;
-  }
-
-  /**
-   * @inheritDoc
-   */
-  setGfmMode(bool) {
-    this.setState({
-      isGfmMode: bool,
-    });
-  }
-
-  /**
-   * @inheritDoc
-   */
-  setCaretLine(line) {
-    if (Number.isNaN(line)) {
-      return;
-    }
-
-    // scroll to bottom
-    this.textarea.scrollTop = this.textarea.scrollHeight;
-
-    const lines = this.textarea.value.split('\n').slice(0, line);
-    /* eslint-disable no-param-reassign, no-return-assign */
-    const pos = lines
-      .map((lineStr) => { return lineStr.length + 1 }) // correct length+1 of each lines
-      .reduce((a, x) => { return a += x }, 0) //          sum
-        - 1; //                                           -1
-    /* eslint-enable no-param-reassign, no-return-assign */
-
-    this.textarea.setSelectionRange(pos, pos);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  setScrollTopByLine(line) {
-    // do nothing
-  }
-
-  /**
-   * @inheritDoc
-   */
-  insertText(text) {
-    const startPos = this.textarea.selectionStart;
-    const endPos = this.textarea.selectionEnd;
-    this.replaceValue(text, startPos, endPos);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  getStrFromBol() {
-    const currentPos = this.textarea.selectionStart;
-    return this.textarea.value.substring(this.getBolPos(), currentPos);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  getStrToEol() {
-    const currentPos = this.textarea.selectionStart;
-    return this.textarea.value.substring(currentPos, this.getEolPos());
-  }
-
-  /**
-   * @inheritDoc
-   */
-  getStrFromBolToSelectedUpperPos() {
-    const startPos = this.textarea.selectionStart;
-    const endPos = this.textarea.selectionEnd;
-    const upperPos = (startPos < endPos) ? startPos : endPos;
-    return this.textarea.value.substring(this.getBolPos(), upperPos);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  replaceBolToCurrentPos(text) {
-    const startPos = this.textarea.selectionStart;
-    const endPos = this.textarea.selectionEnd;
-    const lowerPos = (startPos < endPos) ? endPos : startPos;
-    this.replaceValue(text, this.getBolPos(), lowerPos);
-  }
-
-  /**
-   * @inheritDoc
-   */
-  replaceLine(text) {
-    this.replaceValue(text, this.getBolPos(), this.getEolPos());
-  }
-
-  getBolPos() {
-    const currentPos = this.textarea.selectionStart;
-    return this.textarea.value.lastIndexOf('\n', currentPos - 1) + 1;
-  }
-
-  getEolPos() {
-    const currentPos = this.textarea.selectionStart;
-    const pos = this.textarea.value.indexOf('\n', currentPos);
-    if (pos < 0) { // not found but EOF
-      return this.textarea.value.length;
-    }
-    return pos;
-  }
-
-  replaceValue(text, startPos, endPos) {
-    // create new value
-    const value = this.textarea.value;
-    const newValue = value.substring(0, startPos) + text + value.substring(endPos, value.length);
-    // calculate new position
-    const newPos = startPos + text.length;
-
-    this.textarea.value = newValue;
-    this.textarea.setSelectionRange(newPos, newPos);
-  }
-
-  /**
-   * keypress event handler
-   * @param {string} event
-   */
-  keyPressHandler(event) {
-    const key = event.key.toLowerCase();
-    if (key === 'enter') {
-      if (event.ctrlKey || event.altKey || event.metaKey) {
-        return;
-      }
-
-      this.handleEnterKey(event);
-    }
-  }
-
-  /**
-   * handle ENTER key
-   * @param {string} event
-   */
-  handleEnterKey(event) {
-    if (!this.state.isGfmMode) {
-      return; // do nothing
-    }
-
-    const context = {
-      handlers: [], // list of handlers which process enter key
-      editor: this,
-    };
-
-    const interceptorManager = this.interceptorManager;
-    interceptorManager.process('preHandleEnter', context)
-      .then(() => {
-        event.preventDefault();
-        if (context.handlers.length === 0) {
-          mlu.newlineAndIndentContinueMarkdownList(this);
-        }
-      });
-  }
-
-  /**
-   * paste event handler
-   * @param {any} event
-   */
-  pasteHandler(event) {
-    const types = event.clipboardData.types;
-
-    // files
-    if (types.includes('Files')) {
-      event.preventDefault();
-      this.dispatchPasteFiles(event);
-    }
-    // text
-    else if (types.includes('text/plain')) {
-      pasteHelper.pasteText(this, event);
-    }
-  }
-
-  dragEnterHandler(event) {
-    this.dispatchDragEnter(event);
-  }
-
-  dispatchDragEnter(event) {
-    if (this.props.onDragEnter != null) {
-      this.props.onDragEnter(event);
-    }
-  }
-
-  render() {
-    return (
-      <React.Fragment>
-        <Input
-          type="textarea"
-          className="textarea-editor shadow-none"
-          innerRef={(c) => { this.textarea = c }}
-          defaultValue={this.state.value}
-          onChange={(e) => {
-            if (this.props.onChange != null) {
-              this.props.onChange(e.target.value);
-            }
-          }}
-        />
-      </React.Fragment>
-    );
-  }
-
-}
-
-TextAreaEditor.propTypes = Object.assign({
-}, AbstractEditor.propTypes);

+ 0 - 521
apps/app/_obsolete/src/components/PageEditorByHackmd.tsx

@@ -1,521 +0,0 @@
-import React, {
-  useCallback, useRef, useState, useEffect, useMemo,
-} from 'react';
-
-import EventEmitter from 'events';
-
-import { pathUtils } from '@growi/core/dist/utils';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { useTranslation } from 'react-i18next';
-import urljoin from 'url-join';
-
-import { useUpdateStateAfterSave, useSaveOrUpdate } from '~/client/services/page-operation';
-import { apiPost } from '~/client/util/apiv1-client';
-import { toastError, toastSuccess } from '~/client/util/toastr';
-import { IResHackmdIntegrated, IResHackmdDiscard } from '~/interfaces/hackmd';
-import { OptionsToSave } from '~/interfaces/page-operation';
-import {
-  useCurrentPathname, useHackmdUri,
-} from '~/stores/context';
-import {
-  useIsSlackEnabled, usePageTagsForEditors, useIsEnabledUnsavedWarning, useWaitingSaveProcessing,
-} from '~/stores/editor';
-import {
-  usePageIdOnHackmd, useHasDraftOnHackmd, useRevisionIdHackmdSynced, useIsHackmdDraftUpdatingInRealtime,
-} from '~/stores/hackmd';
-import {
-  useCurrentPagePath, useSWRMUTxCurrentPage, useSWRxCurrentPage, useSWRxTagsInfo, useCurrentPageId, useIsNotFound,
-} from '~/stores/page';
-import { mutatePageTree } from '~/stores/page-listing';
-import { useRemoteRevisionId } from '~/stores/remote-latest-page';
-import {
-  EditorMode,
-  useEditorMode, useSelectedGrant,
-} from '~/stores/ui';
-import loggerFactory from '~/utils/logger';
-
-import HackmdEditor from './PageEditorByHackmd/HackmdEditor';
-
-const logger = loggerFactory('growi:PageEditorByHackmd');
-
-
-declare global {
-  // eslint-disable-next-line vars-on-top, no-var
-  var globalEmitter: EventEmitter;
-}
-
-
-type HackEditorRef = {
-  getValue: () => Promise<string>
-};
-
-export const PageEditorByHackmd = (): JSX.Element => {
-
-  const { t } = useTranslation();
-  const router = useRouter();
-
-  const { data: isNotFound } = useIsNotFound();
-  const { mutate: mutateWaitingSaveProcessing } = useWaitingSaveProcessing();
-  const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
-  const { data: currentPagePath } = useCurrentPagePath();
-  const { data: currentPathname } = useCurrentPathname();
-  const { data: isSlackEnabled } = useIsSlackEnabled();
-  const { data: pageId } = useCurrentPageId();
-  const { data: pageTags } = usePageTagsForEditors(pageId);
-  const { mutate: mutateTagsInfo } = useSWRxTagsInfo(pageId);
-  const { data: grantData } = useSelectedGrant();
-  const { data: hackmdUri } = useHackmdUri();
-  const saveOrUpdate = useSaveOrUpdate();
-
-  const { returnPathForURL } = pathUtils;
-
-  // pageData
-  const { data: pageData } = useSWRxCurrentPage();
-  const { trigger: mutatePageData } = useSWRMUTxCurrentPage();
-  const revision = pageData?.revision;
-
-  const [isInitialized, setIsInitialized] = useState(false);
-  const [isInitializing, setIsInitializing] = useState(false);
-  // for error
-  const [hasError, setHasError] = useState(false);
-  const [errorMessage, setErrorMessage] = useState('');
-  const [errorReason, setErrorReason] = useState('');
-
-  // state from pageContainer
-  const { data: pageIdOnHackmd, mutate: mutatePageIdOnHackmd } = usePageIdOnHackmd();
-  const { data: hasDraftOnHackmd, mutate: mutateHasDraftOnHackmd } = useHasDraftOnHackmd();
-  const { data: revisionIdHackmdSynced, mutate: mutateRevisionIdHackmdSynced } = useRevisionIdHackmdSynced();
-  const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
-  const { data: isHackmdDraftUpdatingInRealtime, mutate: mutateIsHackmdDraftUpdatingInRealtime } = useIsHackmdDraftUpdatingInRealtime();
-  const { data: remoteRevisionId, mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
-
-  const updateStateAfterSave = useUpdateStateAfterSave(pageId);
-
-  const hackmdEditorRef = useRef<HackEditorRef>(null);
-
-  const optionsToSave = useMemo((): OptionsToSave | undefined => {
-    if (grantData == null) {
-      return;
-    }
-    const userRelatedGrantedGroups = grantData.userRelatedGrantedGroups?.map((group) => {
-      return { item: group.id, type: group.type };
-    });
-    const optionsToSave = {
-      isSlackEnabled: isSlackEnabled ?? false,
-      slackChannels: '', // set in save method by opts in SavePageControlls.tsx
-      grant: grantData.grant,
-      pageTags: pageTags ?? [],
-      userRelatedGrantUserGroupIds: userRelatedGrantedGroups,
-    };
-    return optionsToSave;
-  }, [grantData, isSlackEnabled, pageTags]);
-
-  const saveAndReturnToViewHandler = useCallback(async(opts?: {overwriteScopesOfDescendants: boolean}) => {
-    if (editorMode !== EditorMode.HackMD) { return }
-
-    try {
-      if (currentPathname == null || revision == null || hackmdEditorRef.current == null || revisionIdHackmdSynced == null || optionsToSave == null) {
-        throw new Error('Some materials to save are invalid');
-      }
-
-      mutateWaitingSaveProcessing(true);
-
-      const options = Object.assign(optionsToSave, opts, { isSyncRevisionToHackmd: true });
-
-      const markdown = await hackmdEditorRef.current.getValue();
-
-      const { page } = await saveOrUpdate(markdown, { pageId, path: currentPagePath || currentPathname, revisionId: revisionIdHackmdSynced }, options);
-
-      if (page == null) {
-        return;
-      }
-      if (isNotFound) {
-        await router.push(`/${page._id}`);
-      }
-      else {
-        updateStateAfterSave?.();
-        mutateIsHackmdDraftUpdatingInRealtime(false);
-
-        // to sync revision id with page tree: https://github.com/weseek/growi/pull/7227
-        mutatePageTree();
-      }
-      setIsInitialized(false);
-      mutateEditorMode(EditorMode.View);
-    }
-    catch (error) {
-      logger.error('failed to save', error);
-      toastError(error.message);
-    }
-    finally {
-      mutateWaitingSaveProcessing(false);
-    }
-
-  // eslint-disable-next-line max-len
-  }, [
-    pageId, currentPagePath, isNotFound, router,
-    editorMode, currentPathname, revision, revisionIdHackmdSynced, optionsToSave,
-    saveOrUpdate, mutateEditorMode, updateStateAfterSave, mutateIsHackmdDraftUpdatingInRealtime, mutateWaitingSaveProcessing,
-  ]);
-
-  // set handler to save and reload Page
-  useEffect(() => {
-    globalEmitter.on('saveAndReturnToView', saveAndReturnToViewHandler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('saveAndReturnToView', saveAndReturnToViewHandler);
-    };
-  }, [saveAndReturnToViewHandler]);
-
-  const resetInitializedStatusHandler = useCallback(() => {
-    setIsInitialized(false);
-  }, []);
-
-
-  // set handler to save and reload Page
-  useEffect(() => {
-    globalEmitter.on('resetInitializedHackMdStatus', resetInitializedStatusHandler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('resetInitializedHackMdStatus', resetInitializedStatusHandler);
-    };
-  }, [resetInitializedStatusHandler]);
-
-  useEffect(() => {
-    // for page translation: https://github.com/weseek/growi/pull/7100
-    setIsInitialized(false);
-  }, [pageId]);
-
-
-  const isResume = useCallback(() => {
-    const isPageExistsOnHackmd = (pageIdOnHackmd != null);
-    return (isPageExistsOnHackmd && hasDraftOnHackmd) || isHackmdDraftUpdatingInRealtime;
-  }, [hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime, pageIdOnHackmd]);
-
-  const startToEdit = useCallback(async() => {
-
-    if (hackmdUri == null) {
-      // do nothing
-      return;
-    }
-
-    setIsInitialized(false);
-    setIsInitializing(true);
-
-    try {
-      const res = await apiPost<IResHackmdIntegrated>('/hackmd.integrate', { pageId });
-
-      if (!res.ok) {
-        throw new Error(res.error);
-      }
-
-      mutatePageIdOnHackmd(res.pageIdOnHackmd);
-      mutateRevisionIdHackmdSynced(res.revisionIdHackmdSynced);
-    }
-    catch (err) {
-      toastError(err.message);
-
-      setHasError(true);
-      setErrorMessage('GROWI server failed to connect to HackMD.');
-      setErrorReason(err.toString());
-    }
-
-    setIsInitialized(true);
-    setIsInitializing(false);
-  }, [pageId, hackmdUri, mutatePageIdOnHackmd, mutateRevisionIdHackmdSynced]);
-
-  /**
-   * Start to edit w/o any api request
-   */
-  const resumeToEdit = useCallback(() => {
-    setIsInitialized(true);
-  }, []);
-
-  const discardChanges = useCallback(async() => {
-
-    if (pageId == null) { return }
-
-    try {
-      const res = await apiPost<IResHackmdDiscard>('/hackmd.discard', { pageId });
-
-      if (!res.ok) {
-        throw new Error(res.error);
-      }
-
-      mutateIsHackmdDraftUpdatingInRealtime(false);
-      mutateHasDraftOnHackmd(false);
-      mutatePageIdOnHackmd(res.pageIdOnHackmd);
-      mutateRemoteRevisionId(res.revisionIdHackmdSynced);
-      mutateRevisionIdHackmdSynced(res.revisionIdHackmdSynced);
-
-
-    }
-    catch (err) {
-      logger.error(err);
-      toastError(err.message);
-    }
-  }, [mutateIsHackmdDraftUpdatingInRealtime, mutateHasDraftOnHackmd, mutatePageIdOnHackmd, mutateRevisionIdHackmdSynced, mutateRemoteRevisionId, pageId]);
-
-  /**
-   * save and update state of containers
-   * @param {string} markdown
-   */
-  const onSaveWithShortcut = useCallback(async(markdown) => {
-    try {
-      mutateWaitingSaveProcessing(true);
-
-      const currentPagePathOrPathname = currentPagePath || currentPathname;
-      if (
-        pageId == null || revisionIdHackmdSynced == null || currentPagePathOrPathname == null || optionsToSave == null
-      ) { throw new Error('Some materials to save are invalid') }
-
-      const options = Object.assign(optionsToSave, { isSyncRevisionToHackmd: true });
-
-      const res = await saveOrUpdate(markdown, { pageId, path: currentPagePathOrPathname, revisionId: revisionIdHackmdSynced }, options);
-
-      // update pageData
-      mutatePageData(res);
-
-      // set updated data
-      updateStateAfterSave?.();
-      mutateTagsInfo();
-
-      // to sync revision id with page tree: https://github.com/weseek/growi/pull/7227
-      mutatePageTree();
-
-      mutateIsEnabledUnsavedWarning(false);
-
-      logger.debug('success to save');
-
-      toastSuccess(t('successfully_saved_the_page'));
-    }
-    catch (error) {
-      logger.error('failed to save', error);
-      toastError(error.message);
-    }
-    finally {
-      mutateWaitingSaveProcessing(false);
-    }
-
-  // eslint-disable-next-line max-len
-  }, [
-    currentPagePath, currentPathname, pageId, revisionIdHackmdSynced, optionsToSave,
-    saveOrUpdate,
-    mutateWaitingSaveProcessing, mutatePageData, updateStateAfterSave, mutateTagsInfo, mutateIsEnabledUnsavedWarning, t,
-  ]);
-
-  /**
-   * onChange event of HackmdEditor handler
-   */
-  const hackmdEditorChangeHandler = useCallback(async(body) => {
-
-    if (hackmdUri == null || pageId == null) {
-      // do nothing
-      return;
-    }
-
-    if (revision?.body === body) {
-      return;
-    }
-
-    mutateIsEnabledUnsavedWarning(true);
-
-    try {
-      await apiPost('/hackmd.saveOnHackmd', { pageId });
-    }
-    catch (err) {
-      logger.error(err);
-    }
-  }, [hackmdUri, pageId, revision?.body, mutateIsEnabledUnsavedWarning]);
-
-  const penpalErrorOccuredHandler = useCallback((error) => {
-    toastError(error.message);
-
-    setHasError(true);
-    setErrorMessage(t('hackmd.fail_to_connect'));
-    setErrorReason(error.toString());
-  }, [t]);
-
-  const renderPreInitContent = useCallback(() => {
-    const isPageNotFound = pageId == null;
-
-    let content;
-
-    /*
-     * HackMD is not setup
-     */
-    if (hackmdUri == null) {
-      content = (
-        <div>
-          <p className="text-center hackmd-status-label"><span className="material-symbols-outlined">description</span> { t('hackmd.not_set_up')}</p>
-          {/* eslint-disable-next-line react/no-danger */}
-          <p dangerouslySetInnerHTML={{ __html: t('hackmd.need_to_associate_with_growi_to_use_hackmd_refer_to_this') }} />
-        </div>
-      );
-    }
-
-    /*
-    * used HackMD from NotFound Page
-    */
-    else if (isPageNotFound) {
-      content = (
-        <div className="text-center">
-          <p className="hackmd-status-label">
-            <span className="material-symbols-outlined">description</span>
-            { t('hackmd.used_for_not_found') }
-          </p>
-          {/* eslint-disable-next-line react/no-danger */}
-          <p dangerouslySetInnerHTML={{ __html: t('hackmd.need_to_make_page') }} />
-        </div>
-      );
-    }
-    /*
-     * Resume to edit or discard changes
-     */
-    else if (isResume()) {
-      const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
-
-      content = (
-        <div>
-          <p className="text-center hackmd-status-label"><span className="material-symbols-outlined">description</span> HackMD is READY!</p>
-          <p className="text-center"><strong>{t('hackmd.unsaved_draft')}</strong></p>
-
-          { isHackmdDocumentOutdated && (
-            <div className="card border-warning">
-              <div className="card-header bg-warning text-dark"><span className="material-symbols-outlined">info</span> {t('hackmd.draft_outdated')}</div>
-              <div className="card-body text-center">
-                {t('hackmd.based_on_revision')}&nbsp;
-                { pageData != null && (
-                  <Link href={urljoin(returnPathForURL(pageData.path, pageData._id), `?revisionId=${revisionIdHackmdSynced}`)} prefetch={false}>
-                    <span className="badge bg-primary">{revisionIdHackmdSynced?.substr(-8)}</span>
-                  </Link>
-                )}
-                <div className="text-center mt-3">
-                  <button
-                    className="btn btn-link btn-view-outdated-draft p-0"
-                    type="button"
-                    disabled={isInitializing}
-                    onClick={resumeToEdit}
-                  >
-                    {t('hackmd.view_outdated_draft')}
-                  </button>
-                </div>
-              </div>
-            </div>
-          ) }
-
-          { !isHackmdDocumentOutdated && (
-            <div className="text-center hackmd-resume-button-container mb-3">
-              <button
-                className="btn btn-success btn-lg waves-effect waves-light"
-                type="button"
-                disabled={isInitializing}
-                onClick={resumeToEdit}
-              >
-                <span className="btn-label"></span><span className="material-symbols-outlined">skip_next</span>
-                <span className="btn-text">{t('hackmd.resume_to_edit')}</span>
-              </button>
-            </div>
-          ) }
-
-          <div className="text-center hackmd-discard-button-container mb-3">
-            <button
-              className="btn btn-outline-secondary btn-lg waves-effect waves-light"
-              type="button"
-              onClick={discardChanges}
-            >
-              <span className="btn-label"></span><span className="material-symbols-outlined">play_arrow</span>
-              <span className="btn-text">{t('hackmd.discard_changes')}</span>
-            </button>
-          </div>
-
-        </div>
-      );
-    }
-    /*
-     * Start to edit
-     */
-    else {
-      const isRevisionOutdated = revision?._id !== remoteRevisionId;
-
-      content = (
-        <div>
-          <p className="text-muted text-center hackmd-status-label"><span className="material-symbols-outlined">description</span> HackMD is READY!</p>
-          <div className="text-center hackmd-start-button-container mb-3">
-            <button
-              className="btn btn-info btn-lg waves-effect waves-light"
-              type="button"
-              disabled={isRevisionOutdated || isInitializing}
-              onClick={startToEdit}
-            >
-              <span className="btn-label"></span><span className="material-symbols-outlined">send</span>
-              {t('hackmd.start_to_edit')}
-            </button>
-          </div>
-          <p className="text-center">{t('hackmd.clone_page_content')}</p>
-        </div>
-      );
-    }
-
-    return (
-      <div className="hackmd-preinit d-flex justify-content-center align-items-center">
-        {content}
-      </div>
-    );
-  // eslint-disable-next-line max-len
-  }, [pageId, hackmdUri, isResume, t, revisionIdHackmdSynced, remoteRevisionId, pageData, returnPathForURL, isInitializing, resumeToEdit, discardChanges, revision?._id, startToEdit]);
-
-  if (editorMode == null || revision == null) {
-    return <></>;
-  }
-
-  let content;
-
-  // TODO: typescriptize
-  // using any because ref cann't used between FC and class conponent with type safe
-  const AnyEditor = HackmdEditor as any;
-
-  if (isInitialized && hackmdUri != null) {
-    content = (
-      <AnyEditor
-        ref={hackmdEditorRef}
-        hackmdUri={hackmdUri}
-        pageIdOnHackmd={pageIdOnHackmd}
-        initializationMarkdown={isResume() ? null : revision.body}
-        onChange={hackmdEditorChangeHandler}
-        onSaveWithShortcut={(document) => {
-          onSaveWithShortcut(document);
-        }}
-        onPenpalErrorOccured={penpalErrorOccuredHandler}
-      >
-      </AnyEditor>
-    );
-  }
-  else {
-    content = renderPreInitContent();
-  }
-
-
-  return (
-    <div className="position-relative">
-
-      {content}
-
-      { hasError && (
-        <div className="hackmd-error position-absolute d-flex flex-column justify-content-center align-items-center">
-          <div className="bg-box p-5 text-center">
-            <h2 className="text-warning"><span className="material-symbols-outlined">error</span> {t('hackmd.integration_failed')}</h2>
-            <h4>{errorMessage}</h4>
-            <p className="card custom-card text-danger">
-              {errorReason}
-            </p>
-            {/* eslint-disable-next-line react/no-danger */}
-            <p dangerouslySetInnerHTML={{ __html: t('hackmd.check_configuration') }} />
-          </div>
-        </div>
-      ) }
-
-    </div>
-  );
-
-};

+ 0 - 115
apps/app/_obsolete/src/components/PageEditorByHackmd/HackmdEditor.jsx

@@ -1,115 +0,0 @@
-import React from 'react';
-
-import connectToChild from 'penpal/lib/connectToChild';
-import PropTypes from 'prop-types';
-
-import loggerFactory from '~/utils/logger';
-
-
-const DEBUG_PENPAL = false;
-
-const logger = loggerFactory('growi:HackmdEditor');
-
-export default class HackmdEditor extends React.PureComponent {
-
-  constructor(props) {
-    super(props);
-
-    this.hackmd = null;
-
-    this.initHackmdWithPenpal = this.initHackmdWithPenpal.bind(this);
-
-    this.notifyBodyChangesHandler = this.notifyBodyChangesHandler.bind(this);
-    this.saveWithShortcutHandler = this.saveWithShortcutHandler.bind(this);
-  }
-
-  componentDidMount() {
-    // append iframe with penpal
-    this.initHackmdWithPenpal();
-  }
-
-  async initHackmdWithPenpal() {
-    const shouldInit = document.getElementById('iframe-hackmd') != null;
-    if (shouldInit) {
-      return;
-    }
-
-    // eslint-disable-next-line @typescript-eslint/no-this-alias
-    const _this = this; // for in methods scope
-    const iframe = document.createElement('iframe');
-    iframe.src = `${this.props.hackmdUri}/${this.props.pageIdOnHackmd}?both`;
-    iframe.id = 'iframe-hackmd';
-    this.iframeContainer.appendChild(iframe);
-
-    const connection = connectToChild({
-      iframe,
-      methods: { // expose methods to HackMD
-        notifyBodyChanges(document) {
-          _this.notifyBodyChangesHandler(document);
-        },
-        saveWithShortcut(document) {
-          _this.saveWithShortcutHandler(document);
-        },
-      },
-      timeout: 15000,
-      debug: DEBUG_PENPAL,
-    });
-
-    try {
-      const child = await connection.promise;
-      this.hackmd = child;
-      if (this.props.initializationMarkdown != null) {
-        child.setValueOnInit(this.props.initializationMarkdown);
-      }
-    }
-    catch (err) {
-      logger.error(err);
-
-      if (this.props.onPenpalErrorOccured != null) {
-        this.props.onPenpalErrorOccured(err);
-      }
-    }
-  }
-
-  /**
-   * return markdown document of HackMD
-   * @return {Promise<string>}
-   */
-  getValue() {
-    return this.hackmd.getValue();
-  }
-
-  setValue(newValue) {
-    this.hackmd.setValue(newValue);
-  }
-
-  notifyBodyChangesHandler(body) {
-    // dispatch onChange() when there is difference from 'initializationMarkdown' props
-    if (this.props.onChange != null && body !== this.props.initializationMarkdown) {
-      this.props.onChange(body);
-    }
-  }
-
-  saveWithShortcutHandler(document) {
-    if (this.props.onSaveWithShortcut != null) {
-      this.props.onSaveWithShortcut(document);
-    }
-  }
-
-  render() {
-    return (
-      // will be rendered in componentDidMount
-      <div id="iframe-hackmd-container" ref={(c) => { this.iframeContainer = c }}></div>
-    );
-  }
-
-}
-
-HackmdEditor.propTypes = {
-  hackmdUri: PropTypes.string.isRequired,
-  pageIdOnHackmd: PropTypes.string.isRequired,
-  initializationMarkdown: PropTypes.string,
-  onChange: PropTypes.func,
-  onSaveWithShortcut: PropTypes.func,
-  onPenpalErrorOccured: PropTypes.func,
-};

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

@@ -1,101 +0,0 @@
-import React, {
-  useCallback, useRef, MutableRefObject,
-} from 'react';
-
-import codemirror, { commands, Editor } from 'codemirror';
-import { type ICodeMirror, UnControlled as CodeMirror } from 'react-codemirror2';
-
-declare global {
-  // eslint-disable-next-line vars-on-top, no-var
-  var CodeMirror: ICodeMirror;
-}
-
-// set save handler
-// CommandActions in @types/codemirror does not include 'save' but actualy exists
-// https://codemirror.net/5/doc/manual.html#commands
-(commands as any).save = (instance) => {
-  if (instance.codeMirrorEditor != null) {
-    instance.codeMirrorEditor.dispatchSave();
-  }
-};
-
-window.CodeMirror = codemirror;
-require('codemirror/addon/display/placeholder');
-require('~/client/util/codemirror/gfm-growi.mode');
-
-export interface UncontrolledCodeMirrorProps extends ICodeMirror {
-  value: string;
-  isGfmMode?: boolean;
-  lineNumbers?: boolean;
-  onSave?: () => Promise<void>;
-  onCtrlEnter?: (event: Event) => void;
-  onPasteFiles?: (editor: any, event: Event) => void;
-  onScrollCursorIntoView?: (editor: any, event: Event) => void;
-}
-
-export const UncontrolledCodeMirror = React.forwardRef<CodeMirror|null, UncontrolledCodeMirrorProps>((props, forwardedRef): JSX.Element => {
-
-  const {
-    value, lineNumbers, options,
-    onPasteFiles, onScrollCursorIntoView,
-    ...rest
-  } = props;
-
-  const editorRef = useRef<Editor>();
-
-  const wrapperRef = useRef<CodeMirror|null>();
-
-  const editorDidMountHandler = useCallback((editor: Editor): void => {
-    editorRef.current = editor;
-    if (onPasteFiles != null) {
-      editor.on('paste', onPasteFiles);
-    }
-    if (onScrollCursorIntoView != null) {
-      editor.on('scrollCursorIntoView', onScrollCursorIntoView);
-    }
-  }, [onPasteFiles, onScrollCursorIntoView]);
-
-  const editorWillUnmountHandler = useCallback((): void => {
-    // workaround to fix editor duplicating by https://github.com/scniro/react-codemirror2/issues/284#issuecomment-1155928554
-    if (editorRef.current != null) {
-      (editorRef.current as any).display.wrapper.remove();
-    }
-    if (wrapperRef.current != null) {
-      (wrapperRef.current as any).hydrated = false;
-    }
-  }, []);
-
-  // default true
-  const isGfmMode = rest.isGfmMode ?? true;
-
-  return (
-    <CodeMirror
-      ref={(elem) => {
-        // register to wrapperRef
-        wrapperRef.current = elem;
-        // register to forwardedRef
-        if (forwardedRef != null) {
-          if (typeof forwardedRef === 'function') {
-            forwardedRef(elem);
-          }
-          else {
-            (forwardedRef as MutableRefObject<CodeMirror|null>).current = elem;
-          }
-        }
-      }}
-      value={value}
-      options={{
-        lineNumbers: lineNumbers ?? true,
-        mode: isGfmMode ? 'gfm-growi' : undefined,
-        tabSize: 4,
-        ...options,
-      }}
-      editorDidMount={editorDidMountHandler}
-      editorWillUnmount={editorWillUnmountHandler}
-      {...rest}
-    />
-  );
-
-});
-
-UncontrolledCodeMirror.displayName = 'UncontrolledCodeMirror';

+ 0 - 16
apps/app/_obsolete/src/interfaces/hackmd.ts

@@ -1,16 +0,0 @@
-
-export interface IResHackmdIntegrated {
-  ok: boolean,
-  error?: any,
-  pageIdOnHackmd?: string,
-  revisionIdHackmdSynced?: string,
-  hasDraftOnHackmd?: string
-}
-
-export interface IResHackmdDiscard {
-  ok: boolean,
-  error?: any,
-  pageIdOnHackmd?: string,
-  revisionIdHackmdSynced?: string,
-  hasDraftOnHackmd?: string
-}

+ 0 - 346
apps/app/_obsolete/src/server/routes/hackmd.js

@@ -1,346 +0,0 @@
-import * as hackmdFiles from '@growi/hackmd';
-
-import loggerFactory from '~/utils/logger';
-
-/* eslint-disable no-use-before-define */
-
-const logger = loggerFactory('growi:routes:hackmd');
-
-const axios = require('axios');
-const ejs = require('ejs');
-
-const ApiResponse = require('../util/apiResponse');
-
-/**
- * @swagger
- *
- *  components:
- *    schemas:
- *      Hackmd:
- *        description: Hackmd
- *        type: object
- *        properties:
- *          pageIdOnHackmd:
- *            type: string
- *            description: page ID on HackMD
- *            example: qLnodHLxT6C3hVEVczvbDQ
- *          revisionIdHackmdSynced:
- *            $ref: '#/components/schemas/Revision/properties/_id'
- *          hasDraftOnHackmd:
- *            type: boolean
- *            description: has draft on HackMD
- *            example: false
- */
-module.exports = function(crowi, app) {
-  const Page = crowi.models.Page;
-  const pageEvent = crowi.event('page');
-
-  /**
-   * GET /_hackmd/load-agent
-   *
-   * loadAgent action
-   * This should be access from HackMD and send agent script
-   *
-   * @param {object} req
-   * @param {object} res
-   */
-  const loadAgent = function(req, res) {
-
-    const origin = crowi.appService.getSiteUrl();
-
-    // generate definitions to replace
-    const definitions = {
-      origin,
-    };
-
-    // inject origin to script
-    const script = ejs.render(hackmdFiles.agentJS, definitions);
-
-    res.set('Content-Type', 'application/javascript');
-    res.send(script);
-  };
-
-  /**
-   * GET /_hackmd/load-styles
-   *
-   * loadStyles action
-   * This should be access from HackMD and send script to insert styles
-   *
-   * @param {object} req
-   * @param {object} res
-   */
-  const loadStyles = function(req, res) {
-
-    // generate definitions to replace
-    const definitions = {
-      styles: hackmdFiles.stylesCSS,
-    };
-    // inject styles to script
-    const script = ejs.render(hackmdFiles.stylesJS, definitions);
-
-    res.set('Content-Type', 'application/javascript');
-    res.send(script);
-  };
-
-  const validateForApi = async function(req, res, next) {
-    // validate process.env.HACKMD_URI
-    const hackmdUri = process.env.HACKMD_URI;
-    if (hackmdUri == null) {
-      return res.json(ApiResponse.error('HackMD for GROWI has not been setup'));
-    }
-    // validate pageId
-    const pageId = req.body.pageId;
-    if (pageId == null) {
-      return res.json(ApiResponse.error('pageId required'));
-    }
-    // validate page
-    const page = await Page.findOne({ _id: pageId });
-    if (page == null) {
-      return res.json(ApiResponse.error(`Page('${pageId}') does not exist`));
-    }
-
-    req.page = page;
-    next();
-  };
-
-  /**
-   * @swagger
-   *
-   *    /hackmd.integrate:
-   *      post:
-   *        tags: [Hackmd]
-   *        operationId: integrateHackmd
-   *        summary: /hackmd.integrate
-   *        description: Integrate hackmd
-   *        requestBody:
-   *          content:
-   *            application/json:
-   *              schema:
-   *                properties:
-   *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
-   *                  page:
-   *                    $ref: '#/components/schemas/Hackmd'
-   *        responses:
-   *          200:
-   *            description: Succeeded to integrate HackMD.
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  properties:
-   *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
-   *                    pageIdOnHackmd:
-   *                      $ref: '#/components/schemas/Hackmd/properties/pageIdOnHackmd'
-   *                    revisionIdHackmdSynced:
-   *                      $ref: '#/components/schemas/Hackmd/properties/revisionIdHackmdSynced'
-   *                    hasDraftOnHackmd:
-   *                      $ref: '#/components/schemas/Hackmd/properties/hasDraftOnHackmd'
-   *          403:
-   *            $ref: '#/components/responses/403'
-   *          500:
-   *            $ref: '#/components/responses/500'
-   */
-  /**
-   * POST /_api/hackmd.integrate
-   *
-   * Create page on HackMD and start to integrate
-   * @param {object} req
-   * @param {object} res
-   */
-  const integrate = async function(req, res) {
-    const hackmdUri = process.env.HACKMD_URI_FOR_SERVER || process.env.HACKMD_URI;
-    let page = req.page;
-
-    const hackmdPageUri = (page.pageIdOnHackmd != null)
-      ? `${hackmdUri}/${page.pageIdOnHackmd}`
-      : `${hackmdUri}/new`;
-
-    let hackmdResponse;
-    try {
-      // check if page is found or created in HackMD
-      hackmdResponse = await axios.get(hackmdPageUri, {
-        maxRedirects: 0,
-        // validate HTTP status is 200 or 302 or 404
-        validateStatus: (status) => {
-          return status === 200 || status === 302 || status === 404;
-        },
-      });
-    }
-    catch (err) {
-      logger.error(err);
-      return res.json(ApiResponse.error(err));
-    }
-
-    const { status, headers } = hackmdResponse;
-
-    // validate HackMD/CodiMD/HedgeDoc specific header
-    if (headers['codimd-version'] == null && headers['hackmd-version'] == null && headers['hedgedoc-version'] == null) {
-      const message = 'Connecting to a non-HackMD server.';
-      logger.error(message);
-      return res.json(ApiResponse.error(message));
-    }
-
-    try {
-      // when page is not found
-      if (status === 404) {
-        // reset registered data
-        page = await Page.registerHackmdPage(page, undefined);
-        // re-invoke
-        return integrate(req, res);
-      }
-
-      // when redirect
-      if (status === 302) {
-        // extract page id on HackMD
-        const pathnameOnHackmd = new URL(headers.location, hackmdUri).pathname; // e.g. '/NC7bSRraT1CQO1TO7wjCPw'
-        const pageIdOnHackmd = pathnameOnHackmd.substr(1); //                      strip the head '/'
-
-        page = await Page.registerHackmdPage(page, pageIdOnHackmd);
-      }
-      // when page is found
-      else {
-        page = await Page.syncRevisionToHackmd(page);
-      }
-
-      const data = {
-        pageIdOnHackmd: page.pageIdOnHackmd,
-        revisionIdHackmdSynced: page.revisionHackmdSynced,
-        hasDraftOnHackmd: page.hasDraftOnHackmd,
-      };
-      return res.json(ApiResponse.success(data));
-    }
-    catch (err) {
-      logger.error(err);
-      return res.json(ApiResponse.error('Integration with HackMD process failed'));
-    }
-  };
-
-  /**
-   * @swagger
-   *
-   *    /hackmd.discard:
-   *      post:
-   *        tags: [Hackmd]
-   *        operationId: discardHackmd
-   *        summary: /hackmd.discard
-   *        description: Discard hackmd
-   *        requestBody:
-   *          content:
-   *            application/json:
-   *              schema:
-   *                properties:
-   *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
-   *                  page:
-   *                    $ref: '#/components/schemas/Hackmd'
-   *        responses:
-   *          200:
-   *            description: Succeeded to integrate HackMD.
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  properties:
-   *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
-   *                    pageIdOnHackmd:
-   *                      $ref: '#/components/schemas/Hackmd/properties/pageIdOnHackmd'
-   *                    revisionIdHackmdSynced:
-   *                      $ref: '#/components/schemas/Hackmd/properties/revisionIdHackmdSynced'
-   *                    hasDraftOnHackmd:
-   *                      $ref: '#/components/schemas/Hackmd/properties/hasDraftOnHackmd'
-   *          403:
-   *            $ref: '#/components/responses/403'
-   *          500:
-   *            $ref: '#/components/responses/500'
-   */
-  /**
-   * POST /_api/hackmd.discard
-   *
-   * Create page on HackMD and start to integrate
-   * @param {object} req
-   * @param {object} res
-   */
-  const discard = async function(req, res) {
-    let page = req.page;
-
-    try {
-      page = await Page.syncRevisionToHackmd(page);
-
-      const data = {
-        pageIdOnHackmd: page.pageIdOnHackmd,
-        revisionIdHackmdSynced: page.revisionHackmdSynced,
-        hasDraftOnHackmd: page.hasDraftOnHackmd,
-      };
-      return res.json(ApiResponse.success(data));
-    }
-    catch (err) {
-      logger.error(err);
-      return res.json(ApiResponse.error('discard process failed'));
-    }
-  };
-
-  /**
-   * @swagger
-   *
-   *    /hackmd.saveOnHackmd:
-   *      post:
-   *        tags: [Hackmd]
-   *        operationId: saveOnHackmd
-   *        summary: /hackmd.saveOnHackmd
-   *        description: Receive when save operation triggered on HackMD
-   *        requestBody:
-   *          content:
-   *            application/json:
-   *              schema:
-   *                properties:
-   *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
-   *                  page:
-   *                    $ref: '#/components/schemas/Hackmd'
-   *        responses:
-   *          200:
-   *            description: Succeeded to receive when save operation triggered on HackMD.
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  properties:
-   *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
-   *          403:
-   *            $ref: '#/components/responses/403'
-   *          500:
-   *            $ref: '#/components/responses/500'
-   */
-  /**
-   * POST /_api/hackmd.saveOnHackmd
-   *
-   * receive when save operation triggered on HackMD
-   * !! This will be invoked many time from many people !!
-   *
-   * @param {object} req
-   * @param {object} res
-   */
-  const saveOnHackmd = async function(req, res) {
-    const { page, user } = req;
-
-    try {
-      await Page.updateHasDraftOnHackmd(page, true);
-      pageEvent.emit('saveOnHackmd', page, user);
-      return res.json(ApiResponse.success());
-    }
-    catch (err) {
-      logger.error(err);
-      return res.json(ApiResponse.error('saveOnHackmd process failed'));
-    }
-  };
-
-  return {
-    loadAgent,
-    loadStyles,
-    validateForApi,
-    integrate,
-    discard,
-    saveOnHackmd,
-  };
-};

+ 0 - 22
apps/app/_obsolete/src/stores/hackmd.ts

@@ -1,22 +0,0 @@
-import { SWRResponse } from 'swr';
-
-import { useStaticSWR } from './use-static-swr';
-
-type Nullable<T> = T | null;
-
-export const usePageIdOnHackmd = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
-  return useStaticSWR<Nullable<any>, Error>('pageIdOnHackmd', initialData);
-};
-
-
-export const useHasDraftOnHackmd = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
-  return useStaticSWR<Nullable<any>, Error>('hasDraftOnHackmd', initialData);
-};
-
-export const useRevisionIdHackmdSynced = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
-  return useStaticSWR<Nullable<any>, Error>('revisionIdHackmdSynced', initialData);
-};
-
-export const useIsHackmdDraftUpdatingInRealtime = (initialData?: Nullable<boolean>): SWRResponse<Nullable<boolean>, Error> => {
-  return useStaticSWR<Nullable<boolean>, Error>('isHackmdDraftUpdatingInRealtime', initialData);
-};

+ 0 - 170
apps/app/_obsolete/src/styles/_override.scss

@@ -1,170 +0,0 @@
-// TODO: activate (https://redmine.weseek.co.jp/issues/128307)
-
-// * {
-//   outline: none !important;
-// }
-
-// .container,
-// .container-sm,
-// .container-md,
-// .container-lg,
-// .container-xl,
-// .container-fluid {
-//   @include media-breakpoint-down(xs) {
-//     padding-right: 10px;
-//     padding-left: 10px;
-//   }
-//   @include media-breakpoint-up(md) {
-//     padding-right: 30px;
-//     padding-left: 30px;
-//   }
-// }
-
-// h1 {
-//   font-size: 36px;
-//   line-height: 48px;
-// }
-
-// h2 {
-//   font-size: 24px;
-//   line-height: 36px;
-// }
-
-// h3 {
-//   font-size: 21px;
-//   line-height: 30px;
-// }
-
-// h4 {
-//   font-size: 18px;
-//   line-height: 22px;
-// }
-
-// h5 {
-//   font-size: 16px;
-//   line-height: 18px;
-// }
-
-// h6 {
-//   font-size: 12px;
-//   line-height: 18px;
-// }
-
-// // Navs
-// .nav-tabs {
-//   .nav-item {
-//     margin-right: 0.15rem;
-//     a.active {
-//       cursor: default;
-//     }
-//   }
-// }
-
-// // Custom Control
-// .form-check {
-//   .form-check-input,
-//   .form-check-input + .form-check-label {
-//     cursor: pointer;
-//   }
-// }
-
-// // card (substitute panel of bootstrap3)
-// .card {
-//   margin-bottom: 20px;
-// }
-
-// .card-header {
-//   font-weight: 700;
-//   text-transform: none;
-// }
-
-// .card-header:first-child {
-// }
-
-// .card.custom-card {
-//   min-height: 20px;
-//   padding: $card-spacer-y $card-spacer-x;
-// }
-
-// // Dropdowns
-// .dropdown-toggle {
-//   &.btn.disabled {
-//     pointer-events: auto;
-//     cursor: not-allowed;
-//   }
-
-//   // hide caret
-//   &.dropdown-toggle-no-caret::after {
-//     content: none;
-//   }
-// }
-
-// //Modals
-// .modal-open {
-//   width: 100%;
-//   padding-right: 0 !important;
-// }
-
-// .modal-content {
-//   box-shadow: 0 0.3rem 1rem rgba(0, 0, 0, 0.1);
-// }
-
-// .modal-header {
-//   border-bottom: 1px solid #e5e5e5;
-// }
-
-// .modal-footer {
-//   border-top: 1px solid #e5e5e5;
-// }
-
-// // When fading in the modal, animate it to slide down
-// .modal.fade .modal-dialog {
-//   @include transition($modal-transition);
-//   transform: $modal-fade-transform;
-// }
-
-// .modal.show .modal-dialog {
-//   transform: $modal-show-transform;
-// }
-
-// // When trying to close, animate focus to scale
-// .modal.modal-static .modal-dialog {
-//   transform: $modal-scale-transform;
-// }
-
-// // col-form-label (substitute for control-label of bootstrap3)
-// .col-form-label {
-//   text-align: right;
-// }
-
-// // label
-// label {
-//   // add with-no-font-weight class in case you do not want to apply font-weight 700 to label
-//   :not(.with-no-font-weight) {
-//     font-weight: 700;
-//   }
-// }
-
-// // disabled button (reproduction from bootstrap3.)
-// // see https://cccabinet.jpn.org/bootstrap4/components/buttons#disabled-state
-// .btn.disabled,
-// .btn[disabled],
-// fieldset[disabled] .btn {
-//   cursor: not-allowed;
-// }
-
-// // progress bar
-// .progress {
-//   margin-bottom: 18px;
-//   overflow: hidden;
-// }
-
-// .text-break {
-//   word-break: break-word;
-//   overflow-wrap: break-word;
-// }
-
-// // prevent tooltip flickering (flashing) on hover
-// .tooltip {
-//   pointer-events: none;
-// }

+ 0 - 32
apps/app/_obsolete/src/styles/theme/_hsl-functions.scss

@@ -1,32 +0,0 @@
-@use 'bootstrap/scss/functions' as bs;
-
-@function contrast($color, $darken-degrees: 0%, $alpha-degrees: 100%) {
-  $color: bs.str-replace($color, 'var(');
-  $color: bs.str-replace($color, ')');
-  $color-hs: var(#{$color+'-hs'});
-  $color-l: var(#{$color+'-l'});
-  // @return hsl($color-hs, clamp(10%, calc((100% - $color-l - 51% ) * 1000), 95%));
-  @return hsla($color-hs, clamp(10%, calc((100% - $color-l - $darken-degrees - 51% ) * 1000), 95%), $alpha-degrees);
-}
-
-@function lighten($color, $degrees) {
-  $color: bs.str-replace($color, 'var(');
-  $color: bs.str-replace($color, ')');
-  $color-hs: var(#{$color+'-hs'});
-  $color-l: var(#{$color+'-l'});
-  @return hsl($color-hs, calc($color-l + $degrees));
-}
-@function darken($color, $degrees) {
-  $color: bs.str-replace($color, 'var(');
-  $color: bs.str-replace($color, ')');
-  $color-hs: var(#{$color+'-hs'});
-  $color-l: var(#{$color+'-l'});
-  @return hsl($color-hs, calc($color-l - $degrees));
-}
-@function alpha($color, $degrees) {
-  $color: bs.str-replace($color, 'var(');
-  $color: bs.str-replace($color, ')');
-  $color-hs: var(#{$color+'-hs'});
-  $color-l: var(#{$color+'-l'});
-  @return hsla($color-hs,$color-l,$degrees);
-}

+ 0 - 108
apps/app/_obsolete/src/styles/theme/_hsl-reboot-bootstrap-theme-colors.scss

@@ -1,108 +0,0 @@
-@use '../bootstrap/init' as *;
-@use '../atoms/mixins/buttons' as mixins-buttons;
-@use './mixins/hsl-button';
-@use './mixins/hsl-badge';
-@use './hsl-functions' as hsl;
-
-$hsl-colors: (
-  'primary': var(--primary),
-  'secondary': var(--secondary)
-);
-
-@each $color, $value in $hsl-colors {
-  .bg-#{$color} {
-    background-color: $value !important;
-  }
-}
-
-@each $color, $value in $hsl-colors {
-  .border-#{$color} {
-    border-color: $value !important;
-  }
-}
-
-// TODO: hover-focus() dropped in bootstrap v5
-// https://redmine.weseek.co.jp/issues/128307
-// @each $color, $value in $hsl-colors {
-//   .text-#{$color} {
-//     color: $value !important;
-//     @if $emphasized-link-hover-darken-percentage != 0 {
-//       a {
-//         @include hover-focus() {
-//           color: hsl.darken($value, $emphasized-link-hover-darken-percentage) !important;
-//         }
-//       }
-//     }
-//   }
-// }
-
-@each $color, $value in $hsl-colors {
-  .btn-#{$color} {
-    @include hsl-button.button-variant($value, $value);
-    @include hsl-button.button-svg-icon-variant($value, $value);
-    box-shadow: none !important;
-  }
-}
-
-@each $color, $value in $hsl-colors {
-  .btn-outline-#{$color} {
-    @include hsl-button.button-outline-variant($value, $value, hsl.alpha($value, 10%), $value);
-    @include mixins-buttons.button-outline-svg-icon-variant($value, $value);
-
-    &:not(:disabled):not(.disabled):active,
-    &:not(:disabled):not(.disabled).active {
-      color: $value;
-      background-color: hsl.alpha($value, 10%);
-      border-color: $value;
-    }
-
-    box-shadow: none !important;
-  }
-
-  // separate style: https://github.com/weseek/growi/pull/6804
-  .show > .btn-outline-#{$color}.dropdown-toggle {
-    color: $value;
-    background-color: hsl.alpha($value, 10%);
-    border-color: $value;
-  }
-}
-
-@each $color, $value in $hsl-colors {
-  .alert-#{$color} {
-    $alert-color: rgba(white,90%);
-    $alert-bg-color: hsl.darken($value, calc($alert-bg-level + 0.95) * $theme-color-interval);
-    $alert-border-color: hsl.darken($value, $alert-border-level * $theme-color-interval);
-
-    color: $alert-color;
-    @include gradient-bg($alert-bg-color);
-    border-color: $alert-border-color;
-
-    hr {
-      border-top-color: hsl.darken($value, calc($alert-border-level + 5) * $theme-color-interval);
-    }
-
-    .alert-link {
-      color: hsl.contrast($value);
-    }
-  }
-
-  // Alert link
-  :root, .wiki {
-    .alert.alert-primary {
-      a,
-      a:hover {
-        color: hsl.darken($value, calc($alert-bg-level - 7.7) * $theme-color-interval);
-      }
-    }
-  }
-}
-
-@each $color, $value in $hsl-colors {
-  .bg-#{$color} {
-    @include hsl-badge.badge-variant($value);
-  }
-
-  a.bg-#{$color}  {
-    @include hsl-badge.badge-variant($value);
-  }
-}

+ 0 - 29
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-border-colors.scss

@@ -1,29 +0,0 @@
-@use '../bootstrap/init' as *;
-
-//
-// Border
-//
-
-.border {
-  border: $border-width solid $border-color !important;
-}
-
-.border-top {
-  border-top: $border-width solid $border-color !important;
-}
-
-.border-end {
-  border-right: $border-width solid $border-color !important;
-}
-
-.border-bottom {
-  border-bottom: $border-width solid $border-color !important;
-}
-
-.border-start {
-  border-left: $border-width solid $border-color !important;
-}
-
-.border-info {
-  border-color: $info !important;
-}

+ 0 - 22
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-buttons.scss

@@ -1,22 +0,0 @@
-.btn-link {
-  color: var(--color-link);
-  svg {
-    fill: var(--color-link);
-  }
-
-  &:hover {
-    color: var(--color-link-hover);
-    svg {
-      fill: var(--color-link-hover);
-    }
-  }
-
-  &:disabled,
-  &.disabled {
-    color: var(--color-link-disabled, #{$gray-500});
-    svg {
-      fill: var(--color-link-disabled, #{$gray-500});
-    }
-  }
-}
-

+ 0 - 60
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-colors.scss

@@ -1,60 +0,0 @@
-//
-//
-// Apply partially
-//   https://github.com/twbs/bootstrap/blob/v4.5.0/scss/_reboot.scss
-//
-//
-
-// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
-
-// Body
-//
-// 1. Remove the margin in all browsers.
-// 2. As a best practice, apply a default `background-color`.
-// 3. Set an explicit initial text-align value so that we can later use
-//    the `inherit` value on things like `<th>` elements.
-
-html, body {
-  color: var(--color-global);
-  background-color: var(--bgcolor-global); // 2
-
-  svg {
-    fill: var(--color-global);
-  }
-}
-
-// Links
-
-a {
-  color: var(--color-link);
-  text-decoration: $link-decoration;
-  background-color: transparent; // Remove the gray background on active links in IE 10.
-
-  svg {
-    fill: var(--color-link);
-  }
-
-  &:hover {
-    color: var(--color-link-hover);
-    text-decoration: $link-hover-decoration;
-
-    svg {
-      fill: var(--color-link-hover);
-    }
-  }
-}
-
-// And undo these styles for placeholder links/named anchors (without href).
-// It would be more straightforward to just use a[href] in previous block, but that
-// causes specificity issues in many other styles that are too complex to fix.
-// See https://github.com/twbs/bootstrap/issues/19402
-
-// a:not([href]) {
-//   color: inherit;
-//   text-decoration: none;
-
-//   &:hover {
-//     color: inherit;
-//     text-decoration: none;
-//   }
-// }

+ 0 - 38
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-dropdown.scss

@@ -1,38 +0,0 @@
-@use '../bootstrap/init' as *;
-
-.dropdown-menu {
-  color: $color-dropdown;
-  svg {
-    fill: $color-dropdown;
-  }
-
-  background-color: $bgcolor-dropdown;
-}
-
-.dropdown-item {
-  color: $color-dropdown-link;
-  svg {
-    fill: $color-dropdown-link;
-  }
-
-  // TODO: hover-focus() dropped in bootstrap v5
-  @include hover-focus() {
-    color: $color-dropdown-link;
-    svg {
-      fill: $color-dropdown-link-hover;
-    }
-
-    @include gradient-bg($bgcolor-dropdown-link-hover);
-  }
-
-  &:active,
-  &.active,
-  &:active:hover,
-  &.active:hover {
-    color: $color-dropdown-link-active;
-    background-color:  $bgcolor-dropdown-link-active;
-    svg {
-      fill: $color-dropdown-link-active;
-    }
-  }
-}

+ 0 - 52
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-nav.scss

@@ -1,52 +0,0 @@
-//
-//
-// Apply partially
-//   https://github.com/twbs/bootstrap/blob/v4.5.0/scss/_nav.scss
-//
-//
-
-.nav-link {
-  // Disabled state lightens text
-  &.disabled {
-    color: $nav-link-disabled-color;
-    svg {
-      fill: $nav-link-disabled-color;
-    }
-  }
-}
-
-//
-// Tabs
-//
-
-.nav-tabs {
-  border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
-
-  .nav-link {
-    border: $nav-tabs-border-width solid transparent;
-    @include border-top-radius($nav-tabs-border-radius);
-
-    // TODO: hover-focus() dropped in bootstrap v5
-    @include hover-focus() {
-      border-color: $nav-tabs-link-hover-border-color;
-    }
-
-    &.disabled {
-      color: $nav-link-disabled-color;
-      background-color: transparent;
-      border-color: transparent;
-    }
-  }
-
-  .nav-link.active,
-  .nav-item.show .nav-link {
-    color: $nav-tabs-link-active-color;
-    background-color: $nav-tabs-link-active-bg;
-    border-color: $nav-tabs-link-active-border-color;
-  }
-
-  .dropdown-menu {
-    // Remove the top rounded corners here since there is a hard edge above the menu
-    @include border-top-radius(0);
-  }
-}

+ 0 - 74
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-tables.scss

@@ -1,74 +0,0 @@
-@use '../bootstrap/init' as *;
-
-//
-//
-// Apply partially
-//   https://github.com/twbs/bootstrap/blob/v4.5.0/scss/_tables.scss
-//
-//
-
-.table {
-  color: $color-table;
-  background-color: $bgcolor-table; // Reset for nesting within parents with `background-color`.
-
-  th,
-  td {
-    border-top-color: $border-color-table;
-  }
-
-  thead th {
-    border-bottom-color: $border-color-table;
-  }
-
-  tbody + tbody {
-    border-top-color: $border-color-table;
-  }
-}
-
-.table-bordered {
-  border-color: $border-color-table;
-
-  th,
-  td {
-    border-color: $border-color-table;
-  }
-}
-
-.table-hover {
-  tbody tr {
-    &:hover {
-      color: $color-table-hover;
-      background-color: $bgcolor-table-hover;
-    }
-  }
-}
-
-.table-dark {
-  color: $table-dark-color;
-  background-color: $table-dark-bg;
-
-  th,
-  td,
-  thead th {
-    border-color: $table-dark-border-color;
-  }
-
-  &.table-bordered {
-    border: 0;
-  }
-
-  &.table-striped {
-    tbody tr:nth-of-type(#{$table-striped-order}) {
-      background-color: $table-dark-accent-bg;
-    }
-  }
-
-  &.table-hover {
-    tbody tr {
-      &:hover {
-        color: $table-dark-hover-color;
-        background-color: $table-dark-hover-bg;
-      }
-    }
-  }
-}

+ 0 - 3
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-text.scss

@@ -1,3 +0,0 @@
-.text-muted {
-  color: $text-muted !important;
-}

+ 0 - 103
apps/app/_obsolete/src/styles/theme/_reboot-bootstrap-theme-colors.scss

@@ -1,103 +0,0 @@
-@use '../bootstrap/init' as *;
-@use '../atoms/mixins/buttons' as mixins-buttons;
-
-@each $color, $value in $theme-colors {
-  @include bg-variant('.bg-#{$color}', $value);
-}
-
-@each $color, $value in $theme-colors {
-  .border-#{$color} {
-    border-color: $value;
-  }
-}
-
-@each $color, $value in $theme-colors {
-  @include text-emphasis-variant('.text-#{$color}', $value, true);
-}
-
-@each $color, $value in $theme-colors {
-  .btn-#{$color} {
-    @include button-variant($value, $value);
-    @include mixins-buttons.button-svg-icon-variant($value, $value);
-    box-shadow: none !important;
-  }
-}
-
-@each $color, $value in $theme-colors {
-  .btn-outline-#{$color} {
-    @include button-outline-variant($value, $color-hover: $value, $active-background: rgba($value, 0.1), $active-border: $value);
-    @include mixins-buttons.button-outline-svg-icon-variant($value, $color-hover: $value);
-
-    &:not(:disabled):not(.disabled):active,
-    &:not(:disabled):not(.disabled).active {
-      color: $value;
-      background-color: rgba($value, 0.1);
-      border-color: $value;
-    }
-
-    box-shadow: none !important;
-  }
-
-  // separate style: https://github.com/weseek/growi/pull/6804
-  .show > .btn-outline-#{$color}.dropdown-toggle {
-    color: $value;
-    background-color: rgba($value, 0.1);
-    border-color: $value;
-  }
-}
-
-@each $theme-color, $color in $theme-colors {
-  .form-check-#{$theme-color} {
-    .form-check-label::before {
-      border-color: $input-border-color;
-      transition: 0.3s ease-in-out;
-    }
-    .form-check-input:checked + .form-check-label::before {
-      background-color: $color;
-      border-color: $color;
-    }
-    .form-check-input:checked + .form-check-label::after {
-      color: var(--bgcolor-global);
-    }
-    .form-check-input:not(:disabled):active ~ .form-check-label::before {
-      color: var(--bgcolor-global);
-      background-color: $color;
-      border-color: $color;
-    }
-    .form-check-input:focus:not(:checked) ~ .form-check-label::before {
-      color: var(--bgcolor-global);
-      background-color: var(--bgcolor-global);
-      border-color: $input-focus-border-color;
-    }
-  }
-}
-
-// TODO: activate (https://redmine.weseek.co.jp/issues/128307)
-// theme-color-level() dropped in bootstrap v5
-// @each $color, $value in $theme-colors {
-//   .alert-#{$color} {
-//     @include alert-variant(
-//       theme-color-level($color, $alert-bg-level),
-//       theme-color-level($color, $alert-border-level),
-//       theme-color-level($color, $alert-color-level)
-//     );
-//   }
-//   // Alert link
-//   :root, .wiki {
-//     .alert.alert-#{$color} {
-//       a,
-//       a:hover {
-//         color: theme-color-level($color, $alert-color-level - 2);
-//       }
-//     }
-//   }
-// }
-
-@each $color, $value in $theme-colors {
-  .bg-#{$color} {
-    @include badge-variant($value);
-  }
-  a.bg-#{$color} {
-    @include badge-variant($value);
-  }
-}

+ 0 - 9
apps/app/_obsolete/src/styles/theme/mixins/_count-badge.scss

@@ -1,9 +0,0 @@
-@mixin count-badge($color, $bg-color, $min-width: initial) {
-  min-width: $min-width;
-  padding: 0.1rem 0.5rem;
-  font-family: var(--font-family-monospace);
-  font-size: 12px;
-  font-weight: 500;
-  color: $color;
-  background-color: $bg-color;
-}

+ 0 - 23
apps/app/_obsolete/src/styles/theme/mixins/_hsl-badge.scss

@@ -1,23 +0,0 @@
-@use '../../bootstrap/init' as bs;
-@use '../hsl-functions' as hsl;
-
-// @mixin badge-variant($bg) {
-@mixin badge-variant($bg) {
-  color: hsl.contrast($bg);
-  background-color: $bg;
-
-  @at-root a#{&} {
-    // TODO: hover-focus() dropped in bootstrap v5
-    // https://redmine.weseek.co.jp/issues/128307
-    // @include bs.hover-focus() {
-    //   color: hsl.contrast($bg);
-    //   background-color: hsl.darken($bg, 10%);
-    // }
-
-    &:focus,
-    &.focus {
-      outline: 0;
-      // box-shadow: 0 0 0 $badge-focus-width hsl.alpha($bg, 50%);
-    }
-  }
-}

+ 0 - 146
apps/app/_obsolete/src/styles/theme/mixins/_hsl-button.scss

@@ -1,146 +0,0 @@
-@use '@growi/core/scss/bootstrap/init' as bs;
-@use '../hsl-functions' as hsl;
-
-// @mixin button-variant($background, $border, $hover-background: darken($background, 7.5%), $hover-border: darken($border, 10%), $active-background: darken($background, 10%), $active-border: darken($border, 12.5%)) {
-@mixin button-variant($background, $border, $hover-background-darken-degrees: 7.5%, $hover-border-darken-degrees: 10%, $active-background-darken-degrees: 10%, $active-border-darken-degrees: 12.5%) {
-  $hover-background: hsl.darken($background, $hover-background-darken-degrees);  // DO NOT ... twice
-  $hover-border: hsl.darken($border, $hover-border-darken-degrees);  // DO NOT ... twice
-
-  color: hsl.contrast($background);
-  @include bs.gradient-bg($background);
-  border-color: $border;
-  // @include box-shadow($btn-box-shadow);
-
-  &:hover {
-    color: hsl.contrast($background);
-    @include bs.gradient-bg($hover-background);
-    border-color: $hover-border;
-  }
-
-  &:focus,
-  &.focus {
-    color: hsl.contrast($background);
-    @include bs.gradient-bg($hover-background);
-    border-color: $hover-border;
-    // TODO: color-yiq() to color-contrast()
-    // https://redmine.weseek.co.jp/issues/128307
-    // @if $enable-shadows {
-    //   @include box-shadow($btn-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
-    // } @else {
-    //   // Avoid using mixin so we can pass custom focus shadow properly
-    //   box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
-    // }
-  }
-
-  // // Disabled comes first so active can properly restyle
-  &.disabled,
-  &:disabled {
-    color: hsl.contrast($background);
-    @include bs.gradient-bg($background);
-    border-color: $border;
-    // Remove CSS gradients if they're enabled
-    @if bs.$enable-gradients {
-      background-image: none;
-    }
-  }
-
-  &:not(:disabled):not(.disabled):active,
-  &:not(:disabled):not(.disabled).active,
-  .show > &.dropdown-toggle {
-    color: hsl.contrast($background);
-    background-color: $hover-background;
-    border-color: $hover-border;
-  }
-  //   @if $enable-gradients {
-  //     background-image: none; // Remove the gradient for the pressed/active state
-  //   }
-  //   border-color: $active-border;
-
-  // TODO: color-yiq() to color-contrast()
-  // https://redmine.weseek.co.jp/issues/128307
-  //   &:focus {
-  //     // @if $enable-shadows and $btn-active-box-shadow != none {
-  //     //   @include box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5));
-  //     // } @else {
-  //     //   // Avoid using mixin so we can pass custom focus shadow properly
-  //     //   box-shadow: 0 0 0 $btn-focus-width rgba(mix(color-yiq($background), $border, 15%), .5);
-  //     // }
-  //   }
-  // }
-}
-
-// @mixin button-outline-variant($color, $color-hover: color-yiq($color), $active-background: $color, $active-border: $color) {
-@mixin button-outline-variant($color, $color-hover: hsl.contrast($color), $active-background: $color, $active-border: $color) {
-  color: $color;
-  border-color: $color;
-
-  &:hover {
-    color: $color-hover;
-    background-color: $active-background;
-    border-color: $active-border;
-  }
-
-  // &:focus,
-  // &.focus {
-  //   box-shadow: 0 0 0 bs.$btn-focus-width hsl.alpha($color,50%);
-  // }
-
-  &.disabled,
-  &:disabled {
-    color: $color;
-    background-color: transparent;
-  }
-
-  // &:not(:disabled):not(.disabled):active,
-  // &:not(:disabled):not(.disabled).active,
-  // .show > &.dropdown-toggle {
-  //   color: hsl.contrast($active-background);
-  //   background-color: $active-background;
-  //   border-color: $active-border;
-
-  // &:focus {
-  //   @if $enable-shadows and $btn-active-box-shadow != none {
-  //     @include bs.box-shadow($btn-active-box-shadow, 0 0 0 $btn-focus-width hsl.alpha($color,50%));
-  //   } @else {
-  //     // Avoid using mixin so we can pass custom focus shadow properly
-  //     box-shadow: 0 0 0 $btn-focus-width hsl.alpha($color,50%);
-  //   }
-  // }
-  // }
-}
-
-// @mixin button-svg-icon-variant($background, $hover-background: darken($background, 7.5%), $active-background: darken($background, 10%)) {
-@mixin button-svg-icon-variant($background, $hover-background-darken-degrees: 7.5%, $active-background-darken-degrees: 10%) {
-  svg {
-    fill: hsl.contrast($background);
-  }
-
-  &:hover {
-    svg {
-      fill: hsl.contrast($background);
-    }
-  }
-
-  &:focus,
-  &.focus {
-    svg {
-      fill: hsl.contrast($background);
-    }
-  }
-
-  // Disabled comes first so active can properly restyle
-  &.disabled,
-  &:disabled {
-    svg {
-      fill: hsl.contrast($background);
-    }
-  }
-
-  &:not(:disabled):not(.disabled):active,
-  &:not(:disabled):not(.disabled).active,
-  .show > &.dropdown-toggle {
-    svg {
-      fill: hsl.contrast($background);
-    }
-  }
-}

+ 0 - 72
apps/app/_obsolete/src/styles/theme/mixins/_list-group.scss

@@ -1,72 +0,0 @@
-@use '@growi/core/scss/bootstrap/init' as bs;
-
-@use '../../mixins';
-@use './count-badge';
-
-@mixin override-list-group-item($color, $bgcolor, $color-hover: $color, $bgcolor-hover: $bgcolor, $color-active: $color, $bgcolor-active: $bgcolor) {
-  .list-group {
-    .list-group-item {
-      color: $color;
-      background-color: $bgcolor;
-      border-color: $border-color-global;
-
-      &.list-group-item-action {
-        &:hover {
-          background-color: $bgcolor-hover;
-        }
-        &.active {
-          color: $color-active;
-          background-color: $bgcolor-active;
-        }
-      }
-    }
-  }
-}
-
-@mixin override-list-group-item-for-pagetree($color, $bgcolor-hover, $bgcolor-active, $btn-color, $btn-color-hover, $btn-bgcolor-hover, $btn-bgcolor-active) {
-  .grw-pagetree-is-over {
-    background: $bgcolor-hover;
-  }
-  .list-group-item {
-    color: $color;
-    background-color: transparent;
-    border-color: $border-color-global;
-
-    .grw-count-badge {
-      @include count-badge.count-badge($btn-color, $bgcolor-hover, 28px);
-    }
-
-    .btn.btn-page-item-control {
-      color: $btn-color;
-      background-color: transparent;
-      &:hover {
-        color: $btn-color-hover;
-        background-color: $btn-bgcolor-hover;
-      }
-      &:not(:disabled):not(.disabled):active,
-      &:not(:disabled):not(.disabled).active {
-        color: $btn-color-hover;
-        background-color: $btn-bgcolor-active;
-      }
-    }
-
-    &.grw-pagetree-current-page-item {
-      background: $bgcolor-hover;
-    }
-
-    &.list-group-item-action {
-      &:hover {
-        background-color: $bgcolor-hover;
-      }
-      &:active {
-        background-color: $bgcolor-active;
-      }
-    }
-    .grw-pagetree-title-anchor,
-    .grw-foldertree-title-anchor {
-      .grw-sidebar-text-muted {
-        // color: rgba(desaturate($color, 50%), 0.6);
-      }
-    }
-  }
-}

+ 0 - 22
apps/app/_obsolete/src/styles/theme/mixins/_page-editor-mode-manager.scss

@@ -1,22 +0,0 @@
-@mixin btn-page-editor-mode-manager($textColor, $borderColor, $bgColorHoverAndActive, $bgColor: white) {
-  color: $textColor;
-  background-color: $bgColor;
-  border-color: $borderColor;
-
-  &:not(:first-child) {
-    &::before {
-      border-left-color: $borderColor;
-    }
-  }
-
-  &:hover,
-  &:active,
-  &.active {
-    color: $textColor;
-    background-color: $bgColorHoverAndActive;
-    border-color: $borderColor;
-    &::after {
-      border-color: $bgColorHoverAndActive;
-    }
-  }
-}