Просмотр исходного кода

Merge branch 'support/apply-nextjs-2' into feat/integrate-next-show-sidebar

yohei0125 3 лет назад
Родитель
Сommit
b1c4f26a03
37 измененных файлов с 469 добавлено и 478 удалено
  1. 4 1
      packages/app/package.json
  2. 0 0
      packages/app/src/client/legacy/thirdparty-js/waves.js
  3. 3 1
      packages/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.jsx
  4. 1 0
      packages/app/src/components/Admin/Users/StatusSuspendMenuItem.tsx
  5. 2 3
      packages/app/src/components/BasicLayout.tsx
  6. 2 0
      packages/app/src/components/Common/ClosableTextInput.tsx
  7. 9 3
      packages/app/src/components/Fab.jsx
  8. 7 4
      packages/app/src/components/InAppNotification/InAppNotificationDropdown.tsx
  9. 11 2
      packages/app/src/components/Navbar/AppearanceModeDropdown.tsx
  10. 56 0
      packages/app/src/components/Navbar/GlobalSearch.scss
  11. 24 25
      packages/app/src/components/Navbar/GlobalSearch.tsx
  12. 40 3
      packages/app/src/components/Navbar/GrowiNavbar.scss
  13. 15 5
      packages/app/src/components/Navbar/GrowiNavbar.tsx
  14. 14 0
      packages/app/src/components/Navbar/GrowiNavbarBottom.scss
  15. 7 9
      packages/app/src/components/Navbar/GrowiNavbarBottom.tsx
  16. 8 3
      packages/app/src/components/Navbar/PersonalDropdown.jsx
  17. 4 4
      packages/app/src/components/PageEditorByHackmd.jsx
  18. 44 0
      packages/app/src/components/SearchTypeahead.scss
  19. 5 3
      packages/app/src/components/SearchTypeahead.tsx
  20. 5 3
      packages/app/src/components/ShortcutsModal.scss
  21. 9 6
      packages/app/src/components/ShortcutsModal.tsx
  22. 20 96
      packages/app/src/components/Sidebar.scss
  23. 2 0
      packages/app/src/components/Sidebar.tsx
  24. 69 0
      packages/app/src/components/Sidebar/SidebarNav.scss
  25. 3 1
      packages/app/src/components/Sidebar/SidebarNav.tsx
  26. 6 0
      packages/app/src/components/SystemVersion.scss
  27. 2 0
      packages/app/src/components/SystemVersion.tsx
  28. 4 1
      packages/app/src/pages/[[...path]].page.tsx
  29. 4 0
      packages/app/src/stores/context.tsx
  30. 0 7
      packages/app/src/styles/_layout.scss
  31. 0 12
      packages/app/src/styles/_navbar.scss
  32. 14 128
      packages/app/src/styles/_search.scss
  33. 0 149
      packages/app/src/styles/_waves.scss
  34. 4 0
      packages/app/src/styles/style-next.scss
  35. 1 1
      packages/app/src/styles/theme/_apply-colors.scss
  36. 2 2
      packages/app/test/cypress/integration/50-switch-sidebar-mode/switching-sidebar-mode.spec.ts
  37. 68 6
      yarn.lock

+ 4 - 1
packages/app/package.json

@@ -178,7 +178,7 @@
     "@types/jquery": "^3.5.8",
     "@types/jquery": "^3.5.8",
     "@types/multer": "^1.4.5",
     "@types/multer": "^1.4.5",
     "autoprefixer": "^9.0.0",
     "autoprefixer": "^9.0.0",
-    "bootstrap": "^4.5.0",
+    "bootstrap": "^4.6.1",
     "browser-sync": "^2.27.7",
     "browser-sync": "^2.27.7",
     "bunyan-debug": "^2.0.0",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",
     "cli": "~1.0.1",
@@ -216,6 +216,7 @@
     "markdown-it-task-checkbox": "^1.0.6",
     "markdown-it-task-checkbox": "^1.0.6",
     "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
     "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
     "markdown-table": "^1.1.1",
     "markdown-table": "^1.1.1",
+    "material-icons": "^1.11.3",
     "mini-css-extract-plugin": "^2.6.1",
     "mini-css-extract-plugin": "^2.6.1",
     "morgan": "^1.10.0",
     "morgan": "^1.10.0",
     "node-dev": "^4.0.0",
     "node-dev": "^4.0.0",
@@ -232,12 +233,14 @@
     "react-dropzone": "^11.2.4",
     "react-dropzone": "^11.2.4",
     "react-frame-component": "^4.0.0",
     "react-frame-component": "^4.0.0",
     "react-hotkeys": "^2.0.0",
     "react-hotkeys": "^2.0.0",
+    "react-use-ripple": "^1.5.2",
     "react-waypoint": "^10.1.0",
     "react-waypoint": "^10.1.0",
     "reactstrap": "^8.9.0",
     "reactstrap": "^8.9.0",
     "replacestream": "^4.0.3",
     "replacestream": "^4.0.3",
     "reveal.js": "^4.3.1",
     "reveal.js": "^4.3.1",
     "sass": "^1.43.4",
     "sass": "^1.43.4",
     "sass-loader": "^10.1.1",
     "sass-loader": "^10.1.1",
+    "simple-line-icons": "^2.5.5",
     "simple-load-script": "^1.0.2",
     "simple-load-script": "^1.0.2",
     "simplebar-react": "^2.3.6",
     "simplebar-react": "^2.3.6",
     "socket.io-client": "^4.2.0",
     "socket.io-client": "^4.2.0",

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
packages/app/src/client/legacy/thirdparty-js/waves.js


+ 3 - 1
packages/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import {
 import {
   Button, Modal, ModalHeader, ModalBody, ModalFooter,
   Button, Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
@@ -91,4 +91,6 @@ DeleteSlackBotSettingsModal.propTypes = {
   onClickDeleteButton: PropTypes.func,
   onClickDeleteButton: PropTypes.func,
 };
 };
 
 
+DeleteSlackBotSettingsModal.displayName = 'DeleteSlackBotSettingsModal';
+
 export default DeleteSlackBotSettingsModal;
 export default DeleteSlackBotSettingsModal;

+ 1 - 0
packages/app/src/components/Admin/Users/StatusSuspendMenuItem.tsx

@@ -20,6 +20,7 @@ const SuspendAlert = React.memo((): JSX.Element => {
   );
   );
 });
 });
 
 
+SuspendAlert.displayName = 'SuspendAlert';
 
 
 type Props = {
 type Props = {
   adminUsersContainer: AdminUsersContainer,
   adminUsersContainer: AdminUsersContainer,

+ 2 - 3
packages/app/src/components/BasicLayout.tsx

@@ -3,7 +3,6 @@ import React, { ReactNode } from 'react';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 
 
 import { GrowiNavbar } from './Navbar/GrowiNavbar';
 import { GrowiNavbar } from './Navbar/GrowiNavbar';
-// import GrowiNavbarBottom from './Navbar/GrowiNavbarBottom';
 import { RawLayout } from './RawLayout';
 import { RawLayout } from './RawLayout';
 import Sidebar from './Sidebar';
 import Sidebar from './Sidebar';
 
 
@@ -18,6 +17,7 @@ export const BasicLayout = ({ children, title, className }: Props): JSX.Element
 
 
   // const HotkeysManager = dynamic(() => import('../client/js/components/Hotkeys/HotkeysManager'), { ssr: false });
   // const HotkeysManager = dynamic(() => import('../client/js/components/Hotkeys/HotkeysManager'), { ssr: false });
   // const PageCreateModal = dynamic(() => import('../client/js/components/PageCreateModal'), { ssr: false });
   // const PageCreateModal = dynamic(() => import('../client/js/components/PageCreateModal'), { ssr: false });
+  const GrowiNavbarBottom = dynamic(() => import('./Navbar/GrowiNavbarBottom').then(mod => mod.GrowiNavbarBottom), { ssr: false });
   const ShortcutsModal = dynamic(() => import('./ShortcutsModal'), { ssr: false });
   const ShortcutsModal = dynamic(() => import('./ShortcutsModal'), { ssr: false });
   const SystemVersion = dynamic(() => import('./SystemVersion'), { ssr: false });
   const SystemVersion = dynamic(() => import('./SystemVersion'), { ssr: false });
 
 
@@ -36,8 +36,7 @@ export const BasicLayout = ({ children, title, className }: Props): JSX.Element
           </div>
           </div>
         </div>
         </div>
 
 
-        {/* <GrowiNavbarBottom /> */}
-        GrowiNavbarBottom
+        <GrowiNavbarBottom />
       </RawLayout>
       </RawLayout>
 
 
       {/* <PageCreateModal /> */}
       {/* <PageCreateModal /> */}

+ 2 - 0
packages/app/src/components/Common/ClosableTextInput.tsx

@@ -126,4 +126,6 @@ const ClosableTextInput: FC<ClosableTextInputProps> = memo((props: ClosableTextI
   );
   );
 });
 });
 
 
+ClosableTextInput.displayName = 'ClosableTextInput';
+
 export default ClosableTextInput;
 export default ClosableTextInput;

+ 9 - 3
packages/app/src/components/Fab.jsx

@@ -1,8 +1,10 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, {
+  useState, useCallback, useEffect, useRef,
+} from 'react';
 
 
+import { useRipple } from 'react-use-ripple';
 import StickyEvents from 'sticky-events';
 import StickyEvents from 'sticky-events';
 
 
-
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { useCurrentPagePath, useCurrentUser } from '~/stores/context';
 import { useCurrentPagePath, useCurrentUser } from '~/stores/context';
 import { usePageCreateModal } from '~/stores/modal';
 import { usePageCreateModal } from '~/stores/modal';
@@ -22,6 +24,9 @@ const Fab = () => {
   const [animateClasses, setAnimateClasses] = useState('invisible');
   const [animateClasses, setAnimateClasses] = useState('invisible');
   const [buttonClasses, setButtonClasses] = useState('');
   const [buttonClasses, setButtonClasses] = useState('');
 
 
+  // ripple
+  const createBtnRef = useRef(null);
+  useRipple(createBtnRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
 
 
   const stickyChangeHandler = useCallback((event) => {
   const stickyChangeHandler = useCallback((event) => {
     logger.debug('StickyEvents.CHANGE detected');
     logger.debug('StickyEvents.CHANGE detected');
@@ -54,7 +59,8 @@ const Fab = () => {
         <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
         <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
           <button
           <button
             type="button"
             type="button"
-            className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light ${buttonClasses}`}
+            className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 ${buttonClasses}`}
+            ref={createBtnRef}
             onClick={() => openCreateModal(currentPath)}
             onClick={() => openCreateModal(currentPath)}
           >
           >
             <CreatePageIcon />
             <CreatePageIcon />

+ 7 - 4
packages/app/src/components/InAppNotification/InAppNotificationDropdown.tsx

@@ -1,12 +1,12 @@
-import React, {
-  useState, useEffect, FC, useCallback,
-} from 'react';
+import React, { useState, useEffect, useRef } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRipple } from 'react-use-ripple';
 import {
 import {
   Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
   Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
+
 import { toastError } from '~/client/util/apiNotification';
 import { toastError } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { useSWRxInAppNotifications, useSWRxInAppNotificationStatus } from '~/stores/in-app-notification';
 import { useSWRxInAppNotifications, useSWRxInAppNotificationStatus } from '~/stores/in-app-notification';
@@ -29,6 +29,9 @@ export const InAppNotificationDropdown = (): JSX.Element => {
   const { data: inAppNotificationData, mutate: mutateInAppNotificationData } = useSWRxInAppNotifications(limit);
   const { data: inAppNotificationData, mutate: mutateInAppNotificationData } = useSWRxInAppNotifications(limit);
   const { data: inAppNotificationUnreadStatusCount, mutate: mutateInAppNotificationUnreadStatusCount } = useSWRxInAppNotificationStatus();
   const { data: inAppNotificationUnreadStatusCount, mutate: mutateInAppNotificationUnreadStatusCount } = useSWRxInAppNotificationStatus();
 
 
+  // ripple
+  const buttonRef = useRef(null);
+  useRipple(buttonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
 
 
   const updateNotificationStatus = async() => {
   const updateNotificationStatus = async() => {
     try {
     try {
@@ -77,7 +80,7 @@ export const InAppNotificationDropdown = (): JSX.Element => {
 
 
   return (
   return (
     <Dropdown className="notification-wrapper grw-notification-dropdown" isOpen={isOpen} toggle={toggleDropdownHandler}>
     <Dropdown className="notification-wrapper grw-notification-dropdown" isOpen={isOpen} toggle={toggleDropdownHandler}>
-      <DropdownToggle tag="a" className="px-3 nav-link border-0 bg-transparent waves-effect waves-light">
+      <DropdownToggle tag="a" className="px-3 nav-link border-0 bg-transparentt" innerRef={buttonRef}>
         <i className="icon-bell" /> {badge}
         <i className="icon-bell" /> {badge}
       </DropdownToggle>
       </DropdownToggle>
       <DropdownMenu right>
       <DropdownMenu right>

+ 11 - 2
packages/app/src/components/Navbar/AppearanceModeDropdown.tsx

@@ -1,6 +1,9 @@
-import React, { FC, useState, useCallback } from 'react';
+import React, {
+  FC, useState, useCallback, useRef,
+} from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRipple } from 'react-use-ripple';
 import { UncontrolledTooltip } from 'reactstrap';
 import { UncontrolledTooltip } from 'reactstrap';
 
 
 import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { useUserUISettings } from '~/client/services/user-ui-settings';
@@ -35,6 +38,10 @@ export const AppearanceModeDropdown:FC<AppearanceModeDropdownProps> = (props: Ap
   const { data: isPreferDrawerModeOnEdit, mutate: mutatePreferDrawerModeOnEdit } = usePreferDrawerModeOnEditByUser();
   const { data: isPreferDrawerModeOnEdit, mutate: mutatePreferDrawerModeOnEdit } = usePreferDrawerModeOnEditByUser();
   const { scheduleToPut } = useUserUISettings();
   const { scheduleToPut } = useUserUISettings();
 
 
+  // ripple
+  const buttonRef = useRef(null);
+  useRipple(buttonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
+
   const preferDrawerModeSwitchModifiedHandler = useCallback((preferDrawerMode: boolean, isEditMode: boolean) => {
   const preferDrawerModeSwitchModifiedHandler = useCallback((preferDrawerMode: boolean, isEditMode: boolean) => {
     if (isEditMode) {
     if (isEditMode) {
       mutatePreferDrawerModeOnEdit(preferDrawerMode);
       mutatePreferDrawerModeOnEdit(preferDrawerMode);
@@ -110,7 +117,9 @@ export const AppearanceModeDropdown:FC<AppearanceModeDropdownProps> = (props: Ap
   return (
   return (
     <>
     <>
       {/* setting button */}
       {/* setting button */}
-      <button className="bg-transparent border-0 nav-link" type="button" data-toggle="dropdown" aria-haspopup="true">
+      {/* remove .dropdown-toggle for hide caret */}
+      {/* See https://stackoverflow.com/a/44577512/13183572 */}
+      <button className="bg-transparent border-0 nav-link" type="button" data-toggle="dropdown" ref={buttonRef} aria-haspopup="true">
         <i className="icon-settings"></i>
         <i className="icon-settings"></i>
       </button>
       </button>
 
 

+ 56 - 0
packages/app/src/components/Navbar/GlobalSearch.scss

@@ -0,0 +1,56 @@
+@use '~/styles/bootstrap/init' as bs;
+
+// input styles
+.grw-global-search {
+  .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;
+    }
+  }
+
+  .form-group:not(.has-error) {
+    .rbt-input.form-control {
+      border: none;
+    }
+  }
+
+  .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;
+    }
+  }
+}

+ 24 - 25
packages/app/src/components/Navbar/GlobalSearch.tsx

@@ -1,40 +1,44 @@
-import React, {
-  FC, useState, useCallback, useRef,
-} from 'react';
-import { useTranslation } from 'next-i18next';
+import React, { useState, useCallback, useRef } from 'react';
+
 import assert from 'assert';
 import assert from 'assert';
 
 
-import AppContainer from '~/client/services/AppContainer';
+import { useTranslation } from 'next-i18next';
+
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IFocusable } from '~/client/interfaces/focusable';
-import { useGlobalSearchFormRef } from '~/stores/ui';
-import { IPageSearchMeta } from '~/interfaces/search';
 import { IPageWithMeta } from '~/interfaces/page';
 import { IPageWithMeta } from '~/interfaces/page';
-
-import { withUnstatedContainers } from '../UnstatedUtils';
+import { IPageSearchMeta } from '~/interfaces/search';
+import {
+  useCurrentPagePath, useIsSearchScopeChildrenAsDefault, useIsSearchServiceReachable,
+} from '~/stores/context';
+import { useGlobalSearchFormRef } from '~/stores/ui';
 
 
 import SearchForm from '../SearchForm';
 import SearchForm from '../SearchForm';
-import { useCurrentPagePath } from '~/stores/context';
 
 
 
 
-type Props = {
-  appContainer: AppContainer,
+import './GlobalSearch.scss';
 
 
+
+type Props = {
   dropup?: boolean,
   dropup?: boolean,
 }
 }
 
 
-const GlobalSearch: FC<Props> = (props: Props) => {
-  const { appContainer, dropup } = props;
+export const GlobalSearch = (props: Props): JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
+  const { dropup } = props;
+
   const globalSearchFormRef = useRef<IFocusable>(null);
   const globalSearchFormRef = useRef<IFocusable>(null);
 
 
   useGlobalSearchFormRef(globalSearchFormRef);
   useGlobalSearchFormRef(globalSearchFormRef);
 
 
+  const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
+  const { data: isSearchScopeChildrenAsDefault } = useIsSearchScopeChildrenAsDefault();
+  const { data: currentPagePath } = useCurrentPagePath();
+
   const [text, setText] = useState('');
   const [text, setText] = useState('');
-  const [isScopeChildren, setScopeChildren] = useState<boolean>(appContainer.getConfig().isSearchScopeChildrenAsDefault);
+  const [isScopeChildren, setScopeChildren] = useState<boolean|undefined>(isSearchScopeChildrenAsDefault);
   const [isFocused, setFocused] = useState<boolean>(false);
   const [isFocused, setFocused] = useState<boolean>(false);
 
 
-  const { data: currentPagePath } = useCurrentPagePath();
 
 
   const gotoPage = useCallback((data: IPageWithMeta<IPageSearchMeta>[]) => {
   const gotoPage = useCallback((data: IPageWithMeta<IPageSearchMeta>[]) => {
     assert(data.length > 0);
     assert(data.length > 0);
@@ -65,10 +69,12 @@ const GlobalSearch: FC<Props> = (props: Props) => {
     ? t('header_search_box.label.This tree')
     ? t('header_search_box.label.This tree')
     : t('header_search_box.label.All pages');
     : t('header_search_box.label.All pages');
 
 
-  const isSearchServiceReachable = appContainer.getConfig().isSearchServiceReachable;
-
   const isIndicatorShown = !isFocused && (text.length === 0);
   const isIndicatorShown = !isFocused && (text.length === 0);
 
 
+  if (isScopeChildren == null || isSearchServiceReachable == null) {
+    return <></>;
+  }
+
   return (
   return (
     <div className={`form-group mb-0 d-print-none ${isSearchServiceReachable ? '' : 'has-error'}`}>
     <div className={`form-group mb-0 d-print-none ${isSearchServiceReachable ? '' : 'has-error'}`}>
       <div className="input-group flex-nowrap">
       <div className="input-group flex-nowrap">
@@ -118,10 +124,3 @@ const GlobalSearch: FC<Props> = (props: Props) => {
     </div>
     </div>
   );
   );
 };
 };
-
-/**
- * Wrapper component for using unstated
- */
-const GlobalSearchWrapper = withUnstatedContainers(GlobalSearch, [AppContainer]);
-
-export default GlobalSearchWrapper;

+ 40 - 3
packages/app/src/components/Navbar/GrowiNavbar.scss

@@ -66,19 +66,20 @@
     background: rgba(0, 0, 0, 0.2);
     background: rgba(0, 0, 0, 0.2);
   }
   }
 
 
+  .grw-apperance-mode-dropdown,
   .grw-personal-dropdown {
   .grw-personal-dropdown {
     .dropdown-menu {
     .dropdown-menu {
       min-width: 15rem;
       min-width: 15rem;
 
 
-      .grw-email-sm {
-        font-size: 0.75em;
-      }
       .grw-icon-container svg {
       .grw-icon-container svg {
         width: 18px;
         width: 18px;
         height: 18px;
         height: 18px;
       }
       }
     }
     }
   }
   }
+  .grw-email-sm {
+    font-size: 0.75em;
+  }
 
 
   .grw-notification-dropdown {
   .grw-notification-dropdown {
     .dropdown-menu {
     .dropdown-menu {
@@ -87,6 +88,42 @@
   }
   }
 }
 }
 
 
+// layout for GlobalSearch
+.grw-global-search-top {
+  // 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 {
 .grw-notification-badge {
   position: absolute;
   position: absolute;
   top: 6px;
   top: 6px;

+ 15 - 5
packages/app/src/components/Navbar/GrowiNavbar.tsx

@@ -1,8 +1,11 @@
-import React, { FC, memo, useMemo } from 'react';
+import React, {
+  FC, memo, useMemo, useRef,
+} from 'react';
 
 
 import { isServer } from '@growi/core';
 import { isServer } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
+import { useRipple } from 'react-use-ripple';
 import { UncontrolledTooltip } from 'reactstrap';
 import { UncontrolledTooltip } from 'reactstrap';
 
 
 import { HasChildren } from '~/interfaces/common';
 import { HasChildren } from '~/interfaces/common';
@@ -36,6 +39,10 @@ const NavbarRight = memo((): JSX.Element => {
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isGuestUser } = useIsGuestUser();
 
 
+  // ripple
+  const newButtonRef = useRef(null);
+  useRipple(newButtonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
+
   const { open: openCreateModal } = usePageCreateModal();
   const { open: openCreateModal } = usePageCreateModal();
 
 
   const isAuthenticated = isGuestUser === false;
   const isAuthenticated = isGuestUser === false;
@@ -51,6 +58,7 @@ const NavbarRight = memo((): JSX.Element => {
           <button
           <button
             className="px-md-3 nav-link btn-create-page border-0 bg-transparent"
             className="px-md-3 nav-link btn-create-page border-0 bg-transparent"
             type="button"
             type="button"
+            ref={newButtonRef}
             data-testid="newPageBtn"
             data-testid="newPageBtn"
             onClick={() => openCreateModal(currentPagePath || '')}
             onClick={() => openCreateModal(currentPagePath || '')}
           >
           >
@@ -59,7 +67,7 @@ const NavbarRight = memo((): JSX.Element => {
           </button>
           </button>
         </li>
         </li>
 
 
-        <li className="grw-personal-dropdown nav-item dropdown">
+        <li className="grw-apperance-mode-dropdown nav-item dropdown">
           <ShowSkeltonInSSR><AppearanceModeDropdown isAuthenticated={isAuthenticated} /></ShowSkeltonInSSR>
           <ShowSkeltonInSSR><AppearanceModeDropdown isAuthenticated={isAuthenticated} /></ShowSkeltonInSSR>
         </li>
         </li>
 
 
@@ -73,14 +81,14 @@ const NavbarRight = memo((): JSX.Element => {
   const notAuthenticatedNavItem = useMemo(() => {
   const notAuthenticatedNavItem = useMemo(() => {
     return (
     return (
       <>
       <>
-        <li className="grw-personal-dropdown nav-item dropdown">
+        <li className="grw-apperance-mode-dropdown nav-item dropdown">
           <ShowSkeltonInSSR><AppearanceModeDropdown isAuthenticated={isAuthenticated} /></ShowSkeltonInSSR>
           <ShowSkeltonInSSR><AppearanceModeDropdown isAuthenticated={isAuthenticated} /></ShowSkeltonInSSR>
         </li>
         </li>
 
 
         <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>;
         <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>;
       </>
       </>
     );
     );
-  }, []);
+  }, [AppearanceModeDropdown, isAuthenticated]);
 
 
   return (
   return (
     <>
     <>
@@ -121,6 +129,8 @@ Confidential.displayName = 'Confidential';
 
 
 export const GrowiNavbar = (): JSX.Element => {
 export const GrowiNavbar = (): JSX.Element => {
 
 
+  const GlobalSearch = dynamic(() => import('./GlobalSearch').then(mod => mod.GlobalSearch), { ssr: false });
+
   const { data: appTitle } = useAppTitle();
   const { data: appTitle } = useAppTitle();
   const { data: confidential } = useConfidential();
   const { data: confidential } = useConfidential();
   const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
   const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
@@ -149,7 +159,7 @@ export const GrowiNavbar = (): JSX.Element => {
 
 
       { isSearchServiceConfigured && !isDeviceSmallerThanMd && !isSearchPage && (
       { isSearchServiceConfigured && !isDeviceSmallerThanMd && !isSearchPage && (
         <div className="grw-global-search grw-global-search-top position-absolute">
         <div className="grw-global-search grw-global-search-top position-absolute">
-          {/* <GlobalSearch /> */}
+          <GlobalSearch />
         </div>
         </div>
       ) }
       ) }
     </nav>
     </nav>

+ 14 - 0
packages/app/src/components/Navbar/GrowiNavbarBottom.scss

@@ -0,0 +1,14 @@
+@use '~/styles/variables' as var;
+@use '~/styles/mixins';
+
+.grw-navbar-bottom {
+  height: var.$grw-navbar-bottom-height;
+
+  // apply transition
+  transition-property: bottom;
+  @include mixins.apply-navigation-transition();
+
+  &.grw-navbar-bottom-drawer-opened {
+    bottom: #{-1 * var.$grw-navbar-bottom-height};
+  }
+}

+ 7 - 9
packages/app/src/components/Navbar/GrowiNavbarBottom.jsx → packages/app/src/components/Navbar/GrowiNavbarBottom.tsx

@@ -1,14 +1,15 @@
 import React from 'react';
 import React from 'react';
-import PropTypes from 'prop-types';
 
 
-
-import { useIsDeviceSmallerThanMd, useDrawerOpened } from '~/stores/ui';
-import { usePageCreateModal } from '~/stores/modal';
 import { useCurrentPagePath, useIsSearchPage } from '~/stores/context';
 import { useCurrentPagePath, useIsSearchPage } from '~/stores/context';
+import { usePageCreateModal } from '~/stores/modal';
+import { useIsDeviceSmallerThanMd, useDrawerOpened } from '~/stores/ui';
 
 
-import GlobalSearch from './GlobalSearch';
+import { GlobalSearch } from './GlobalSearch';
 
 
-const GrowiNavbarBottom = (props) => {
+import './GrowiNavbarBottom.scss';
+
+
+export const GrowiNavbarBottom = (): JSX.Element => {
 
 
   const { data: isDrawerOpened, mutate: mutateDrawerOpened } = useDrawerOpened();
   const { data: isDrawerOpened, mutate: mutateDrawerOpened } = useDrawerOpened();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
@@ -73,6 +74,3 @@ const GrowiNavbarBottom = (props) => {
     </div>
     </div>
   );
   );
 };
 };
-
-
-export default GrowiNavbarBottom;

+ 8 - 3
packages/app/src/components/Navbar/PersonalDropdown.jsx

@@ -1,7 +1,8 @@
-import React from 'react';
+import React, { useRef } from 'react';
 
 
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { useRipple } from 'react-use-ripple';
 
 
 import { toastError } from '~/client/util/apiNotification';
 import { toastError } from '~/client/util/apiNotification';
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Post } from '~/client/util/apiv3-client';
@@ -11,6 +12,10 @@ const PersonalDropdown = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();
   const { data: currentUser } = useCurrentUser();
   const { data: currentUser } = useCurrentUser();
 
 
+  // ripple
+  const buttonRef = useRef(null);
+  useRipple(buttonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
+
   const user = currentUser || {};
   const user = currentUser || {};
 
 
   const logoutHandler = async() => {
   const logoutHandler = async() => {
@@ -28,9 +33,9 @@ const PersonalDropdown = () => {
       {/* Button */}
       {/* Button */}
       {/* remove .dropdown-toggle for hide caret */}
       {/* remove .dropdown-toggle for hide caret */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
       {/* See https://stackoverflow.com/a/44577512/13183572 */}
-      <a className="px-md-3 nav-link waves-effect waves-light" data-toggle="dropdown">
+      <button className="bg-transparent border-0 nav-link" type="button" ref={buttonRef} data-toggle="dropdown">
         <UserPicture user={user} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{user.name}</span>
         <UserPicture user={user} noLink noTooltip /><span className="ml-1 d-none d-lg-inline-block">&nbsp;{user.name}</span>
-      </a>
+      </button>
 
 
       {/* Menu */}
       {/* Menu */}
       <div className="dropdown-menu dropdown-menu-right">
       <div className="dropdown-menu dropdown-menu-right">

+ 4 - 4
packages/app/src/components/PageEditorByHackmd.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 
 
-import PropTypes from 'prop-types';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 
 
 
 
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';
@@ -311,7 +311,7 @@ class PageEditorByHackmd extends React.Component {
           { !isHackmdDocumentOutdated && (
           { !isHackmdDocumentOutdated && (
             <div className="text-center hackmd-resume-button-container mb-3">
             <div className="text-center hackmd-resume-button-container mb-3">
               <button
               <button
-                className="btn btn-success btn-lg waves-effect waves-light"
+                className="btn btn-success btn-lg"
                 type="button"
                 type="button"
                 disabled={this.state.isInitializing}
                 disabled={this.state.isInitializing}
                 onClick={() => { return this.resumeToEdit() }}
                 onClick={() => { return this.resumeToEdit() }}
@@ -324,7 +324,7 @@ class PageEditorByHackmd extends React.Component {
 
 
           <div className="text-center hackmd-discard-button-container mb-3">
           <div className="text-center hackmd-discard-button-container mb-3">
             <button
             <button
-              className="btn btn-outline-secondary btn-lg waves-effect waves-light"
+              className="btn btn-outline-secondary btn-lg"
               type="button"
               type="button"
               onClick={() => { return this.discardChanges() }}
               onClick={() => { return this.discardChanges() }}
             >
             >
@@ -347,7 +347,7 @@ class PageEditorByHackmd extends React.Component {
           <p className="text-muted text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <p className="text-muted text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
           <div className="text-center hackmd-start-button-container mb-3">
           <div className="text-center hackmd-start-button-container mb-3">
             <button
             <button
-              className="btn btn-info btn-lg waves-effect waves-light"
+              className="btn btn-info btn-lg"
               type="button"
               type="button"
               disabled={isRevisionOutdated || this.state.isInitializing}
               disabled={isRevisionOutdated || this.state.isInitializing}
               onClick={() => { return this.startToEdit() }}
               onClick={() => { return this.startToEdit() }}

+ 44 - 0
packages/app/src/components/SearchTypeahead.scss

@@ -0,0 +1,44 @@
+@use '~/styles/bootstrap/init' as bs;
+
+.search-typeahead {
+  position: relative;
+  width: 100%;
+  // corner radius
+  border-top-right-radius: bs.$border-radius;
+  border-bottom-right-radius: bs.$border-radius;
+  .rbt-input-main {
+    padding-right: 36px;
+  }
+  .search-clear {
+    position: absolute;
+    top: 4px;
+    right: 4px;
+    z-index: 3;
+    width: 24px;
+    height: 24px;
+    padding: 0;
+    line-height: 0;
+  }
+
+  .rbt-menu {
+    max-height: none !important;
+    margin-top: 3px;
+
+    li a span {
+      .page-path {
+        display: inline;
+        padding: 0 4px;
+        color: inherit;
+      }
+
+      .page-list-meta {
+        font-size: 0.9em;
+        color: bs.$gray-400;
+
+        > span {
+          margin-right: 0.3rem;
+        }
+      }
+    }
+  }
+}

+ 5 - 3
packages/app/src/components/SearchTypeahead.tsx

@@ -3,17 +3,19 @@ import React, {
   KeyboardEvent, useCallback, useRef, useState, MouseEvent, useEffect,
   KeyboardEvent, useCallback, useRef, useState, MouseEvent, useEffect,
 } from 'react';
 } from 'react';
 
 
-import { AsyncTypeahead, Menu, MenuItem } from 'react-bootstrap-typeahead';
-
 import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui';
 import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui';
+import { AsyncTypeahead, Menu, MenuItem } from 'react-bootstrap-typeahead';
 
 
 import { IFocusable } from '~/client/interfaces/focusable';
 import { IFocusable } from '~/client/interfaces/focusable';
 import { TypeaheadProps } from '~/client/interfaces/react-bootstrap-typeahead';
 import { TypeaheadProps } from '~/client/interfaces/react-bootstrap-typeahead';
-import { IPageSearchMeta } from '~/interfaces/search';
 import { IPageWithMeta } from '~/interfaces/page';
 import { IPageWithMeta } from '~/interfaces/page';
+import { IPageSearchMeta } from '~/interfaces/search';
 import { useSWRxSearch } from '~/stores/search';
 import { useSWRxSearch } from '~/stores/search';
 
 
 
 
+import './SearchTypeahead.scss';
+
+
 type ResetFormButtonProps = {
 type ResetFormButtonProps = {
   input?: string,
   input?: string,
   onReset: (e: MouseEvent<HTMLButtonElement>) => void,
   onReset: (e: MouseEvent<HTMLButtonElement>) => void,

+ 5 - 3
packages/app/src/styles/_shortcuts.scss → packages/app/src/components/ShortcutsModal.scss

@@ -1,3 +1,5 @@
+@use '~/styles/bootstrap/init' as bs;
+
 #shortcuts-modal {
 #shortcuts-modal {
   h3 {
   h3 {
     margin-bottom: 1em;
     margin-bottom: 1em;
@@ -12,7 +14,7 @@
     }
     }
   }
   }
 
 
-  @include media-breakpoint-up(sm) {
+  @include bs.media-breakpoint-up(sm) {
     table {
     table {
       table-layout: fixed;
       table-layout: fixed;
       th {
       th {
@@ -30,7 +32,7 @@
     margin: 0px 4px;
     margin: 0px 4px;
     /*Text Properties*/
     /*Text Properties*/
     font: 18px/36px Helvetica, serif;
     font: 18px/36px Helvetica, serif;
-    color: $secondary;
+    color: bs.$secondary;
     text-align: center;
     text-align: center;
     text-transform: uppercase;
     text-transform: uppercase;
     background: white;
     background: white;
@@ -38,7 +40,7 @@
     box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.5);
     box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.5);
     /* SVG Properties*/
     /* SVG Properties*/
     polygon {
     polygon {
-      fill: $secondary;
+      fill: bs.$secondary;
     }
     }
 
 
     &.key-longer {
     &.key-longer {

+ 9 - 6
packages/app/src/components/ShortcutsModal.tsx

@@ -6,6 +6,9 @@ import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 import KeyboardReturnEnterIcon from '~/components/Icons/KeyboardReturnEnterIcon';
 import KeyboardReturnEnterIcon from '~/components/Icons/KeyboardReturnEnterIcon';
 import { useShortcutsModal } from '~/stores/modal';
 import { useShortcutsModal } from '~/stores/modal';
 
 
+import './ShortcutsModal.scss';
+
+
 const ShortcutsModal = (): JSX.Element => {
 const ShortcutsModal = (): JSX.Element => {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
@@ -63,33 +66,33 @@ const ShortcutsModal = (): JSX.Element => {
                           {/* eslint-disable-next-line react/no-danger */}
                           {/* eslint-disable-next-line react/no-danger */}
                           <span dangerouslySetInnerHTML={{ __html: t('modal_shortcuts.global.Show Contributors') }} />:
                           <span dangerouslySetInnerHTML={{ __html: t('modal_shortcuts.global.Show Contributors') }} />:
                         </th>
                         </th>
-                        <td>
+                        <td className='text-nowrap'>
                           <a href="{ t('modal_shortcuts.global.konami_code_url') }" target="_blank">
                           <a href="{ t('modal_shortcuts.global.konami_code_url') }" target="_blank">
                             {t('modal_shortcuts.global.Konami Code')}
                             {t('modal_shortcuts.global.Konami Code')}
                           </a>
                           </a>
                           <br />
                           <br />
                           <span className="key key-small">&uarr;</span>&nbsp;<span className="key key-small">&uarr;</span>
                           <span className="key key-small">&uarr;</span>&nbsp;<span className="key key-small">&uarr;</span>
                           <span className="key key-small">&darr;</span>&nbsp;<span className="key key-small">&darr;</span>
                           <span className="key key-small">&darr;</span>&nbsp;<span className="key key-small">&darr;</span>
-                          <span className="key key-small">&larr;</span>
                           <br />
                           <br />
-                          <span className="key key-small">&rarr;</span>
                           <span className="key key-small">&larr;</span>&nbsp;<span className="key key-small">&rarr;</span>
                           <span className="key key-small">&larr;</span>&nbsp;<span className="key key-small">&rarr;</span>
+                          <span className="key key-small">&larr;</span>&nbsp;<span className="key key-small">&rarr;</span>
+                          <br />
                           <span className="key key-small">B</span>&nbsp;<span className="key key-small">A</span>
                           <span className="key key-small">B</span>&nbsp;<span className="key key-small">A</span>
                         </td>
                         </td>
                       </tr>
                       </tr>
                       <tr>
                       <tr>
                         <th>{t('modal_shortcuts.global.MirrorMode')}:</th>
                         <th>{t('modal_shortcuts.global.MirrorMode')}:</th>
-                        <td>
+                        <td className='text-nowrap'>
                           <a href="{ t('modal_shortcuts.global.konami_code_url') }" target="_blank">
                           <a href="{ t('modal_shortcuts.global.konami_code_url') }" target="_blank">
                             {t('modal_shortcuts.global.Konami Code')}
                             {t('modal_shortcuts.global.Konami Code')}
                           </a>
                           </a>
                           <br />
                           <br />
                           <span className="key key-small">X</span>&nbsp;<span className="key key-small">X</span>
                           <span className="key key-small">X</span>&nbsp;<span className="key key-small">X</span>
                           <span className="key key-small">B</span>&nbsp;<span className="key key-small">B</span>
                           <span className="key key-small">B</span>&nbsp;<span className="key key-small">B</span>
-                          <span className="key key-small">A</span>
                           <br />
                           <br />
-                          <span className="key key-small">Y</span>
                           <span className="key key-small">A</span>&nbsp;<span className="key key-small">Y</span>
                           <span className="key key-small">A</span>&nbsp;<span className="key key-small">Y</span>
+                          <span className="key key-small">A</span>&nbsp;<span className="key key-small">Y</span>
+                          <br />
                           <span className="key key-small">&darr;</span>&nbsp;<span className="key key-small">&larr;</span>
                           <span className="key key-small">&darr;</span>&nbsp;<span className="key key-small">&larr;</span>
                         </td>
                         </td>
                       </tr>
                       </tr>

+ 20 - 96
packages/app/src/styles/_sidebar.scss → packages/app/src/components/Sidebar.scss

@@ -1,32 +1,17 @@
-.grw-sidebar {
-  $sidebar-nav-button-height: 55px;
-
-  %fukidashi-for-active {
-    position: relative;
-
-    // speech balloon
-    &:after {
-      position: absolute;
-      right: -0.1em;
-      display: block;
-      width: 0;
-      content: '';
-      border: 9px solid transparent;
-      border-right-color: white;
-      border-left-width: 0;
-      transform: translateY(-#{$sidebar-nav-button-height / 2});
-    }
-  }
+@use '~/styles/variables' as var;
+@use '~/styles/mixins';
+@use '~/styles/bootstrap/init' as bs;
 
 
+.grw-sidebar {
   // sticky
   // sticky
   position: sticky;
   position: sticky;
-  top: $grw-navbar-border-width;
+  top: var.$grw-navbar-border-width;
 
 
   // set the max value that should be taken when sticky
   // set the max value that should be taken when sticky
-  height: calc(100vh - $grw-navbar-border-width);
+  height: calc(100vh - var.$grw-navbar-border-width);
 
 
   // override @atlaskit/navigation-next styles
   // override @atlaskit/navigation-next styles
-  $navbar-total-height: $grw-navbar-height + $grw-navbar-border-width;
+  $navbar-total-height: var.$grw-navbar-height + var.$grw-navbar-border-width;
   .data-layout-container {
   .data-layout-container {
     display: flex;
     display: flex;
     flex-direction: row;
     flex-direction: row;
@@ -141,9 +126,9 @@
           }
           }
         }
         }
         .hitarea {
         .hitarea {
-          @extend .rounded-pill;
-
           position: absolute;
           position: absolute;
+          border-radius: bs.$rounded-pill;
+
           @include hitarea(30px);
           @include hitarea(30px);
         }
         }
 
 
@@ -169,55 +154,6 @@
     }
     }
   }
   }
 
 
-  .grw-sidebar-nav {
-    height: 100vh;
-
-    .btn {
-      width: $grw-sidebar-nav-width;
-      line-height: 1em;
-      border-radius: 0;
-      box-shadow: none !important;
-
-      // icon opacity
-      &:not(.active) {
-        i {
-          opacity: 0.4;
-        }
-        &:hover,
-        &:focus {
-          i {
-            opacity: 0.7;
-          }
-        }
-      }
-    }
-
-    .grw-sidebar-nav-primary-container {
-      .btn {
-        padding: 1em;
-        i {
-          font-size: 2.3em;
-        }
-
-        &.active {
-          @extend %fukidashi-for-active;
-        }
-      }
-    }
-
-    .grw-sidebar-nav-secondary-container {
-      position: fixed;
-      bottom: 1.5rem;
-
-      .btn {
-        padding: 0.9em;
-        i {
-          font-size: 1.5em;
-        }
-      }
-    }
-  }
-
   .grw-drawer-toggler {
   .grw-drawer-toggler {
     display: none; // invisible in default
     display: none; // invisible in default
   }
   }
@@ -229,14 +165,15 @@
   }
   }
 }
 }
 
 
+
 // Dock Mode
 // Dock Mode
 @mixin dock() {
 @mixin dock() {
-  z-index: $zindex-sticky;
+  z-index: bs.$zindex-sticky;
 
 
   // override @atlaskit/navigation-next styles
   // override @atlaskit/navigation-next styles
-  $navbar-total-height: $grw-navbar-height + $grw-navbar-border-width;
+  $navbar-total-height: var.$grw-navbar-height + var.$grw-navbar-border-width;
   .data-layout-container {
   .data-layout-container {
-    max-height: calc(100vh - #{$grw-navbar-border-width});
+    max-height: calc(100vh - #{var.$grw-navbar-border-width});
   }
   }
   .navigation {
   .navigation {
     position: unset;
     position: unset;
@@ -247,7 +184,7 @@
 
 
 // Drawer Mode
 // Drawer Mode
 @mixin drawer() {
 @mixin drawer() {
-  z-index: $zindex-fixed + 2;
+  z-index: bs.$zindex-fixed + 2;
 
 
   .data-layout-container {
   .data-layout-container {
     position: fixed;
     position: fixed;
@@ -259,7 +196,7 @@
 
 
     // apply transition
     // apply transition
     transition-property: transform;
     transition-property: transform;
-    @include apply-navigation-transition();
+    @include mixins.apply-navigation-transition();
   }
   }
 
 
   &:not(.open) {
   &:not(.open) {
@@ -285,13 +222,13 @@
     position: fixed;
     position: fixed;
     right: -15px;
     right: -15px;
 
 
-    @include media-breakpoint-down(sm) {
+    @include bs.media-breakpoint-down(sm) {
       bottom: 15px;
       bottom: 15px;
       width: 42px;
       width: 42px;
       height: 42px;
       height: 42px;
       font-size: 18px;
       font-size: 18px;
     }
     }
-    @include media-breakpoint-up(md) {
+    @include bs.media-breakpoint-up(md) {
       top: 72px;
       top: 72px;
       width: 50px;
       width: 50px;
       height: 50px;
       height: 50px;
@@ -303,10 +240,10 @@
 }
 }
 
 
 .grw-sidebar {
 .grw-sidebar {
-  @include media-breakpoint-down(sm) {
+  @include bs.media-breakpoint-down(sm) {
     @include drawer();
     @include drawer();
   }
   }
-  @include media-breakpoint-up(md) {
+  @include bs.media-breakpoint-up(md) {
     &.grw-sidebar-drawer {
     &.grw-sidebar-drawer {
       @include drawer();
       @include drawer();
     }
     }
@@ -317,18 +254,5 @@
 }
 }
 
 
 .grw-sidebar-backdrop.modal-backdrop {
 .grw-sidebar-backdrop.modal-backdrop {
-  z-index: $zindex-fixed + 1;
-}
-
-// style to apply when displaying search page
-.growi.on-search {
-  // set sidebar height shown in search page
-  $search-page-sidebar-height: calc(100vh - ($grw-navbar-height + $grw-navbar-border-width));
-
-  .grw-sidebar {
-    height: $search-page-sidebar-height;
-    .data-layout-container {
-      height: 100%;
-    }
-  }
+  z-index: bs.$zindex-fixed + 1;
 }
 }

+ 2 - 0
packages/app/src/components/Sidebar.tsx

@@ -18,6 +18,8 @@ import SidebarContents from './Sidebar/SidebarContents';
 import SidebarNav from './Sidebar/SidebarNav';
 import SidebarNav from './Sidebar/SidebarNav';
 import { StickyStretchableScroller } from './StickyStretchableScroller';
 import { StickyStretchableScroller } from './StickyStretchableScroller';
 
 
+import './Sidebar.scss';
+
 const sidebarMinWidth = 240;
 const sidebarMinWidth = 240;
 const sidebarMinimizeWidth = 20;
 const sidebarMinimizeWidth = 20;
 const sidebarFixedWidthInDrawerMode = 320;
 const sidebarFixedWidthInDrawerMode = 320;

+ 69 - 0
packages/app/src/components/Sidebar/SidebarNav.scss

@@ -0,0 +1,69 @@
+@use '~/styles/variables' as var;
+
+.grw-sidebar-nav {
+  $sidebar-nav-button-height: 55px;
+
+  %fukidashi-for-active {
+    position: relative;
+
+    // speech balloon
+    &:after {
+      position: absolute;
+      right: -0.1em;
+      display: block;
+      width: 0;
+      content: '';
+      border: 9px solid transparent;
+      border-right-color: white;
+      border-left-width: 0;
+      transform: translateY(-#{$sidebar-nav-button-height / 2});
+    }
+  }
+
+  height: 100vh;
+
+  .btn {
+    width: var.$grw-sidebar-nav-width;
+    line-height: 1em;
+    border-radius: 0;
+    box-shadow: none !important;
+
+    // icon opacity
+    &:not(.active) {
+      i {
+        opacity: 0.4;
+      }
+      &:hover,
+      &:focus {
+        i {
+          opacity: 0.7;
+        }
+      }
+    }
+  }
+
+  .grw-sidebar-nav-primary-container {
+    .btn {
+      padding: 1em;
+      i {
+        font-size: 2.3em;
+      }
+
+      &.active {
+        @extend %fukidashi-for-active;
+      }
+    }
+  }
+
+  .grw-sidebar-nav-secondary-container {
+    position: fixed;
+    bottom: 1.5rem;
+
+    .btn {
+      padding: 0.9em;
+      i {
+        font-size: 1.5em;
+      }
+    }
+  }
+}

+ 3 - 1
packages/app/src/components/Sidebar/SidebarNav.tsx

@@ -2,9 +2,11 @@ import React, { FC, memo, useCallback } from 'react';
 
 
 import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { useUserUISettings } from '~/client/services/user-ui-settings';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { SidebarContentsType } from '~/interfaces/ui';
-import { useCurrentUser, useIsGuestUser } from '~/stores/context';
+import { useCurrentUser } from '~/stores/context';
 import { useCurrentSidebarContents } from '~/stores/ui';
 import { useCurrentSidebarContents } from '~/stores/ui';
 
 
+import './SidebarNav.scss';
+
 
 
 type PrimaryItemProps = {
 type PrimaryItemProps = {
   contents: SidebarContentsType,
   contents: SidebarContentsType,

+ 6 - 0
packages/app/src/components/SystemVersion.scss

@@ -0,0 +1,6 @@
+.system-version {
+  position: fixed;
+  right: 0.5em;
+  bottom: 0;
+  opacity: 0.6;
+}

+ 2 - 0
packages/app/src/components/SystemVersion.tsx

@@ -3,6 +3,8 @@ import React from 'react';
 import { useGrowiVersion } from '~/stores/context';
 import { useGrowiVersion } from '~/stores/context';
 import { useShortcutsModal } from '~/stores/modal';
 import { useShortcutsModal } from '~/stores/modal';
 
 
+import './SystemVersion.scss';
+
 const SystemVersion = (): JSX.Element => {
 const SystemVersion = (): JSX.Element => {
 
 
   const { open: openShortcutsModal } = useShortcutsModal();
   const { open: openShortcutsModal } = useShortcutsModal();

+ 4 - 1
packages/app/src/pages/[[...path]].page.tsx

@@ -39,7 +39,7 @@ import {
   useIsForbidden, useIsNotFound, useIsTrashPage, useShared, useShareLinkId, useIsSharedUser, useIsAbleToDeleteCompletely,
   useIsForbidden, useIsNotFound, useIsTrashPage, useShared, useShareLinkId, useIsSharedUser, useIsAbleToDeleteCompletely,
   useAppTitle, useSiteUrl, useConfidential, useIsEnabledStaleNotification,
   useAppTitle, useSiteUrl, useConfidential, useIsEnabledStaleNotification,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsMailerSetup,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsMailerSetup,
-  useAclEnabled, useHasSlackConfig, useDrawioUri, useHackmdUri, useMathJax, useNoCdn, useEditorConfig, useCsrfToken,
+  useAclEnabled, useHasSlackConfig, useDrawioUri, useHackmdUri, useMathJax, useNoCdn, useEditorConfig, useCsrfToken, useIsSearchScopeChildrenAsDefault,
 } from '../stores/context';
 } from '../stores/context';
 
 
 import { CommonProps, getServerSideCommonProps, useCustomTitle } from './commons';
 import { CommonProps, getServerSideCommonProps, useCustomTitle } from './commons';
@@ -64,6 +64,7 @@ type Props = CommonProps & {
   // isAbleToDeleteCompletely: boolean,
   // isAbleToDeleteCompletely: boolean,
   isSearchServiceConfigured: boolean,
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchServiceReachable: boolean,
+  isSearchScopeChildrenAsDefault: boolean,
   // isMailerSetup: boolean,
   // isMailerSetup: boolean,
   // isAclEnabled: boolean,
   // isAclEnabled: boolean,
   // hasSlackConfig: boolean,
   // hasSlackConfig: boolean,
@@ -109,6 +110,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
 
 
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
+  useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
   // useIsMailerSetup(props.isMailerSetup);
   // useIsMailerSetup(props.isMailerSetup);
   // useAclEnabled(props.isAclEnabled);
   // useAclEnabled(props.isAclEnabled);
   // useHasSlackConfig(props.hasSlackConfig);
   // useHasSlackConfig(props.hasSlackConfig);
@@ -285,6 +287,7 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
 
 
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
   props.isSearchServiceReachable = searchService.isReachable;
+  props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
   // props.isMailerSetup = mailService.isMailerSetup;
   // props.isMailerSetup = mailService.isMailerSetup;
   // props.isAclEnabled = aclService.isAclEnabled();
   // props.isAclEnabled = aclService.isAclEnabled();
   // props.hasSlackConfig = slackNotificationService.hasSlackConfig();
   // props.hasSlackConfig = slackNotificationService.hasSlackConfig();

+ 4 - 0
packages/app/src/stores/context.tsx

@@ -160,6 +160,10 @@ export const useIsSearchServiceReachable = (initialData?: boolean) : SWRResponse
   return useStaticSWR<boolean, Error>('isSearchServiceReachable', initialData);
   return useStaticSWR<boolean, Error>('isSearchServiceReachable', initialData);
 };
 };
 
 
+export const useIsSearchScopeChildrenAsDefault = (initialData?: boolean) : SWRResponse<boolean, Error> => {
+  return useStaticSWR<boolean, Error>('isSearchScopeChildrenAsDefault', initialData);
+};
+
 export const useIsEnabledAttachTitleHeader = (initialData?: boolean) : SWRResponse<boolean, Error> => {
 export const useIsEnabledAttachTitleHeader = (initialData?: boolean) : SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>('isEnabledAttachTitleHeader', initialData);
   return useStaticSWR<boolean, Error>('isEnabledAttachTitleHeader', initialData);
 };
 };

+ 0 - 7
packages/app/src/styles/_layout.scss

@@ -131,10 +131,3 @@ body.growi-layout-fluid .grw-container-convertible {
     }
     }
   }
   }
 }
 }
-
-.system-version {
-  position: fixed;
-  right: 0.5em;
-  bottom: 0;
-  opacity: 0.6;
-}

+ 0 - 12
packages/app/src/styles/_navbar.scss

@@ -1,15 +1,3 @@
-.grw-navbar-bottom {
-  height: $grw-navbar-bottom-height;
-
-  // apply transition
-  transition-property: bottom;
-  @include apply-navigation-transition();
-
-  &.grw-navbar-bottom-drawer-opened {
-    bottom: -$grw-navbar-bottom-height;
-  }
-}
-
 .grw-custom-nav-tab,
 .grw-custom-nav-tab,
 .grw-custom-nav-dropdown {
 .grw-custom-nav-dropdown {
   svg {
   svg {

+ 14 - 128
packages/app/src/styles/_search.scss

@@ -1,46 +1,3 @@
-.search-typeahead {
-  position: relative;
-  width: 100%;
-  // corner radius
-  border-top-right-radius: $border-radius;
-  border-bottom-right-radius: $border-radius;
-  .rbt-input-main {
-    padding-right: 36px;
-  }
-  .search-clear {
-    position: absolute;
-    top: 4px;
-    right: 4px;
-    z-index: 3;
-    width: 24px;
-    height: 24px;
-    padding: 0;
-    line-height: 0;
-  }
-
-  .rbt-menu {
-    max-height: none !important;
-    margin-top: 3px;
-
-    li a span {
-      .page-path {
-        display: inline;
-        padding: 0 4px;
-        color: inherit;
-      }
-
-      .page-list-meta {
-        font-size: 0.9em;
-        color: $gray-400;
-
-        > span {
-          margin-right: 0.3rem;
-        }
-      }
-    }
-  }
-}
-
 // styles for admin user search
 // styles for admin user search
 .admin-user-page {
 .admin-user-page {
   .search-typeahead {
   .search-typeahead {
@@ -51,91 +8,6 @@
   }
   }
 }
 }
 
 
-// input styles
-.grw-global-search {
-  .dropdown-toggle {
-    min-width: 95px;
-    padding-left: 1.5rem;
-  }
-
-  .search-typeahead {
-    .rbt-menu {
-      @extend .dropdown-menu-right;
-      @extend .dropdown-menu-md-left;
-      @include 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;
-    }
-  }
-
-  .form-group:not(.has-error) {
-    .rbt-input.form-control {
-      border: none;
-    }
-  }
-
-  .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;
-    }
-  }
-}
-
-// layout for GlobalSearch
-.grw-global-search-top {
-  // centering on navbar
-  top: $grw-navbar-height / 2;
-  left: 50vw;
-  z-index: $zindex-fixed + 1;
-  transform: translate(-50%, -50%);
-
-  .rbt-input.form-control {
-    width: 200px;
-    transition: 0.3s ease-out;
-
-    // focus
-    &.focus {
-      width: 300px;
-    }
-
-    @include media-breakpoint-up(md) {
-      width: 300px;
-    }
-    @include media-breakpoint-up(lg) {
-      // focus
-      &.focus {
-        width: 400px;
-      }
-    }
-    @include media-breakpoint-up(xl) {
-      width: 350px;
-      // focus
-      &.focus {
-        width: 450px;
-      }
-    }
-  }
-}
-
 // layout
 // layout
 .on-search {
 .on-search {
   .page-wrapper {
   .page-wrapper {
@@ -249,3 +121,17 @@
     }
     }
   }
   }
 }
 }
+
+
+// style to apply when displaying search page
+.growi.on-search {
+  // set sidebar height shown in search page
+  $search-page-sidebar-height: calc(100vh - ($grw-navbar-height + $grw-navbar-border-width));
+
+  .grw-sidebar {
+    height: $search-page-sidebar-height;
+    .data-layout-container {
+      height: 100%;
+    }
+  }
+}

+ 0 - 149
packages/app/src/styles/_waves.scss

@@ -1,149 +0,0 @@
-/*Wave Effeects*/
-$gradient: rgba(255, 255, 255, 0.2) 0, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0) 70%;
-
-@mixin waves-transition($transition) {
-  -webkit-transition: $transition;
-  -moz-transition: $transition;
-  -o-transition: $transition;
-  transition: $transition;
-}
-
-@mixin waves-transform($string) {
-  -webkit-transform: $string;
-  -moz-transform: $string;
-  -ms-transform: $string;
-  -o-transform: $string;
-  transform: $string;
-}
-
-@mixin waves-box-shadow($shadow) {
-  -webkit-box-shadow: $shadow;
-  box-shadow: $shadow;
-}
-
-.waves-effect {
-  position: relative;
-  display: inline-block;
-  overflow: hidden;
-  cursor: pointer;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
-  -webkit-tap-highlight-color: transparent;
-
-  .waves-ripple {
-    position: absolute;
-    width: 20px;
-    height: 20px;
-    margin-top: -10px;
-    margin-left: -10px;
-    pointer-events: none;
-    background: rgba(0, 0, 0, 0.08);
-    border-radius: 50%;
-    opacity: 0;
-    -webkit-transition-property: -webkit-transform, opacity;
-    -moz-transition-property: -moz-transform, opacity;
-    -o-transition-property: -o-transform, opacity;
-    transition-property: transform, opacity;
-    -webkit-transform: scale(0);
-    -moz-transform: scale(0);
-    -ms-transform: scale(0);
-    -o-transform: scale(0);
-    transform: scale(0);
-    @include waves-transition(all 0.5s ease-out);
-    @include waves-transform(scale(0) translate(0, 0));
-  }
-
-  &.waves-light .waves-ripple {
-    background: rgba(255, 255, 255, 0.4);
-    background: -webkit-radial-gradient($gradient);
-    background: -o-radial-gradient($gradient);
-    background: -moz-radial-gradient($gradient);
-    background: radial-gradient($gradient);
-  }
-
-  &.waves-classic .waves-ripple {
-    background: rgba(0, 0, 0, 0.2);
-  }
-
-  &.waves-classic.waves-light .waves-ripple {
-    background: rgba(255, 255, 255, 0.4);
-  }
-}
-
-.waves-notransition {
-  @include waves-transition(none '!important');
-}
-
-.waves-button,
-.waves-circle {
-  @include waves-transform(translateZ(0));
-  -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
-}
-
-.waves-button,
-.waves-button:hover,
-.waves-button:visited,
-.waves-button-input {
-  z-index: 1;
-  font-size: 1em;
-  line-height: 1em;
-  color: inherit;
-  text-align: center;
-  text-decoration: none;
-  white-space: nowrap;
-  vertical-align: middle;
-  cursor: pointer;
-  background-color: rgba(0, 0, 0, 0);
-  border: none;
-  outline: none;
-}
-
-.waves-button {
-  padding: 0.85em 1.1em;
-  border-radius: 0.2em;
-}
-
-.waves-button-input {
-  padding: 0.85em 1.1em;
-  margin: 0;
-}
-
-.waves-input-wrapper {
-  vertical-align: bottom;
-  border-radius: 0.2em;
-
-  &.waves-button {
-    padding: 0;
-  }
-
-  .waves-button-input {
-    position: relative;
-    top: 0;
-    left: 0;
-    z-index: 1;
-  }
-}
-
-.waves-circle {
-  width: 2.5em;
-  height: 2.5em;
-  line-height: 2.5em;
-  text-align: center;
-  border-radius: 50%;
-}
-
-.waves-float {
-  mask-image: none;
-  @include waves-box-shadow(0px 1px 1.5px 1px rgba(0, 0, 0, 0.12));
-  @include waves-transition(all 300ms);
-
-  &:active {
-    @include waves-box-shadow(0px 8px 20px 1px rgba(0, 0, 0, 0.3));
-  }
-}
-
-.waves-block {
-  display: block;
-}

+ 4 - 0
packages/app/src/styles/style-next.scss

@@ -9,6 +9,10 @@
 // // override simplebar-react styles
 // // override simplebar-react styles
 // @import 'override-simplebar';
 // @import 'override-simplebar';
 
 
+// icons
+@import '~simple-line-icons';
+@import '~material-icons/iconfont/filled';
+
 // // atoms
 // // atoms
 // @import 'atoms/buttons';
 // @import 'atoms/buttons';
 // @import 'atoms/code';
 // @import 'atoms/code';

+ 1 - 1
packages/app/src/styles/theme/_apply-colors.scss

@@ -76,7 +76,7 @@ pre:not(.hljs):not(.CodeMirror-line) {
 // }
 // }
 
 
 // Dropdown
 // Dropdown
-.grw-personal-dropdown {
+.grw-apperance-mode-dropdown {
   .grw-sidebar-mode-icon svg {
   .grw-sidebar-mode-icon svg {
     fill: $secondary;
     fill: $secondary;
   }
   }

+ 2 - 2
packages/app/test/cypress/integration/50-switch-sidebar-mode/switching-sidebar-mode.spec.ts

@@ -21,7 +21,7 @@ context('Switch sidebar mode', () => {
 
 
   it('Switching sidebar mode', () => {
   it('Switching sidebar mode', () => {
     cy.visit('/');
     cy.visit('/');
-    cy.get('.grw-personal-dropdown').click();
+    cy.get('.grw-apperance-mode-dropdown').click();
 
 
     cy.get('[for="swSidebarModeOnEditor"]').click();
     cy.get('[for="swSidebarModeOnEditor"]').click();
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode`, { capture: 'viewport' });
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode`, { capture: 'viewport' });
@@ -30,4 +30,4 @@ context('Switch sidebar mode', () => {
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode-back`, { capture: 'viewport' });
     cy.screenshot(`${ssPrefix}-switch-sidebar-mode-back`, { capture: 'viewport' });
   });
   });
 
 
-});
+});

+ 68 - 6
yarn.lock

@@ -5549,10 +5549,10 @@ body-parser@1.19.0:
     raw-body "2.4.0"
     raw-body "2.4.0"
     type-is "~1.6.17"
     type-is "~1.6.17"
 
 
-bootstrap@^4.5.0:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.5.0.tgz#97d9dbcb5a8972f8722c9962483543b907d9b9ec"
-  integrity sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA==
+bootstrap@^4.6.1:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.1.tgz#bc25380c2c14192374e8dec07cf01b2742d222a2"
+  integrity sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==
 
 
 bootstrap@^5.0.2:
 bootstrap@^5.0.2:
   version "5.0.2"
   version "5.0.2"
@@ -7006,6 +7006,13 @@ cookie@~0.4.1:
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
   integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
   integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
 
 
+copy-anything@^2.0.1:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480"
+  integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==
+  dependencies:
+    is-what "^3.14.1"
+
 copy-descriptor@^0.1.0:
 copy-descriptor@^0.1.0:
   version "0.1.1"
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
@@ -8218,6 +8225,13 @@ err-code@^2.0.2:
   resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
   resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
   integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
   integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
 
 
+errno@^0.1.1:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
+  integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
+  dependencies:
+    prr "~1.0.1"
+
 errno@^0.1.3:
 errno@^0.1.3:
   version "0.1.6"
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.6.tgz#c386ce8a6283f14fc09563b71560908c9bf53026"
   resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.6.tgz#c386ce8a6283f14fc09563b71560908c9bf53026"
@@ -10872,6 +10886,11 @@ ignore@^5.2.0:
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
   integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
   integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
 
 
+image-size@~0.5.0:
+  version "0.5.5"
+  resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
+  integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
+
 img-diff-js@0.5.2:
 img-diff-js@0.5.2:
   version "0.5.2"
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/img-diff-js/-/img-diff-js-0.5.2.tgz#3db3c2af92d92cfc39be45ae83372d7ad2d59df2"
   resolved "https://registry.yarnpkg.com/img-diff-js/-/img-diff-js-0.5.2.tgz#3db3c2af92d92cfc39be45ae83372d7ad2d59df2"
@@ -11665,6 +11684,11 @@ is-weakref@^1.0.1, is-weakref@^1.0.2:
   dependencies:
   dependencies:
     call-bind "^1.0.2"
     call-bind "^1.0.2"
 
 
+is-what@^3.14.1:
+  version "3.14.1"
+  resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
+  integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
+
 is-whitespace-character@^1.0.0:
 is-whitespace-character@^1.0.0:
   version "1.0.3"
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac"
   resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac"
@@ -12740,6 +12764,22 @@ lerna@^4.0.0:
     import-local "^3.0.2"
     import-local "^3.0.2"
     npmlog "^4.1.2"
     npmlog "^4.1.2"
 
 
+less@^3.12.2:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/less/-/less-3.13.1.tgz#0ebc91d2a0e9c0c6735b83d496b0ab0583077909"
+  integrity sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==
+  dependencies:
+    copy-anything "^2.0.1"
+    tslib "^1.10.0"
+  optionalDependencies:
+    errno "^0.1.1"
+    graceful-fs "^4.1.2"
+    image-size "~0.5.0"
+    make-dir "^2.1.0"
+    mime "^1.4.1"
+    native-request "^1.0.5"
+    source-map "~0.6.0"
+
 leven@^2.1.0:
 leven@^2.1.0:
   version "2.1.0"
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
   resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
@@ -13388,6 +13428,11 @@ match-index@^1.0.1, match-index@^1.0.3:
   dependencies:
   dependencies:
     regexp.prototype.flags "^1.1.1"
     regexp.prototype.flags "^1.1.1"
 
 
+material-icons@^1.11.3:
+  version "1.11.3"
+  resolved "https://registry.yarnpkg.com/material-icons/-/material-icons-1.11.3.tgz#a17ae4387df09d0fc117df19aecdcd07d2266769"
+  integrity sha512-pGkZ5LJOLH/R3jlqL2HXGVi36cucTn/RxSkPtqsinxB2xoDE0fZStwAUZlMKcW0z4qqDamBwVvzM1IVQe6OQDw==
+
 math-random@^1.0.1:
 math-random@^1.0.1:
   version "1.0.4"
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
   resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c"
@@ -13887,7 +13932,7 @@ mime@1.4.1:
   version "1.4.1"
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
 
 
-mime@1.6.0:
+mime@1.6.0, mime@^1.4.1:
   version "1.6.0"
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
   integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
@@ -14407,6 +14452,11 @@ nanomatch@^1.2.9:
     snapdragon "^0.8.1"
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
     to-regex "^3.0.1"
 
 
+native-request@^1.0.5:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.1.0.tgz#acdb30fe2eefa3e1bc8c54b3a6852e9c5c0d3cb0"
+  integrity sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==
+
 natural-compare@^1.4.0:
 natural-compare@^1.4.0:
   version "1.4.0"
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -16762,6 +16812,11 @@ react-transition-group@^2.2.1, react-transition-group@^2.3.1:
     prop-types "^15.6.2"
     prop-types "^15.6.2"
     react-lifecycles-compat "^3.0.4"
     react-lifecycles-compat "^3.0.4"
 
 
+react-use-ripple@^1.5.2:
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/react-use-ripple/-/react-use-ripple-1.5.2.tgz#f42600a0c7729510c3dbba74e0c86ed6c55fd88e"
+  integrity sha512-pK7PLEaEGJ4xCM5acxW+ua7ba0lqxbhNzBHzEw+MoD0yVFT3r8SkfkG6aSpiEm4iLZO9HOeSnUz+1k7YVuYX5w==
+
 react-view-pager@^0.6.0:
 react-view-pager@^0.6.0:
   version "0.6.0"
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/react-view-pager/-/react-view-pager-0.6.0.tgz#6c6be04b0cc3b907b5ceafec7b2ab6e7228df650"
   resolved "https://registry.yarnpkg.com/react-view-pager/-/react-view-pager-0.6.0.tgz#6c6be04b0cc3b907b5ceafec7b2ab6e7228df650"
@@ -18247,6 +18302,13 @@ signal-exit@^3.0.2:
   version "3.0.2"
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
 
 
+simple-line-icons@^2.5.5:
+  version "2.5.5"
+  resolved "https://registry.yarnpkg.com/simple-line-icons/-/simple-line-icons-2.5.5.tgz#32ea1babd842a28cc1764537ba5535e0b8f42be7"
+  integrity sha512-v52iGG/qFZTSD/70yOfA1lYoN6zmjEfDjzFT6U6jNSCsh/aeVjy+8sYyTXWz1w7tLIkN2XeMmG+cLJp/0zYK4Q==
+  dependencies:
+    less "^3.12.2"
+
 simple-load-script@^1.0.2:
 simple-load-script@^1.0.2:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/simple-load-script/-/simple-load-script-1.0.2.tgz#d92951fe7b601ad90af8c9429bd4b2ee127ab8a3"
   resolved "https://registry.yarnpkg.com/simple-load-script/-/simple-load-script-1.0.2.tgz#d92951fe7b601ad90af8c9429bd4b2ee127ab8a3"
@@ -18615,7 +18677,7 @@ source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
 
 
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
   version "0.6.1"
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 
 

Некоторые файлы не были показаны из-за большого количества измененных файлов