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

Merge branch 'support/apply-bootstrap4' into support/fix-label-layout

akira-s 6 лет назад
Родитель
Сommit
b602a0289b
100 измененных файлов с 1105 добавлено и 862 удалено
  1. 8 4
      .github/workflows/ci.yml
  2. 5 1
      CHANGES.md
  3. 2 2
      config/webpack.common.js
  4. 2 3
      package.json
  5. 8 0
      resource/cdn-manifests.js
  6. 1 1
      resource/locales/en-US/translation.json
  7. 2 2
      resource/locales/ja/translation.json
  8. 3 0
      src/client/js/bootstrap.jsx
  9. 9 9
      src/client/js/components/Admin/App/AppSetting.jsx
  10. 15 15
      src/client/js/components/Admin/App/AwsSetting.jsx
  11. 8 8
      src/client/js/components/Admin/App/MailSetting.jsx
  12. 1 1
      src/client/js/components/Admin/App/PluginSetting.jsx
  13. 1 1
      src/client/js/components/Admin/App/SiteUrlSetting.jsx
  14. 1 1
      src/client/js/components/Admin/Common/AdminUpdateButtonRow.jsx
  15. 1 1
      src/client/js/components/Admin/Customize/CustomizeFunctionOption.jsx
  16. 2 2
      src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx
  17. 17 29
      src/client/js/components/Admin/Customize/CustomizeLayoutOptions.jsx
  18. 3 3
      src/client/js/components/Admin/ElasticsearchManagement/ElasticsearchManagement.jsx
  19. 1 1
      src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx
  20. 1 1
      src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx
  21. 4 3
      src/client/js/components/Admin/ElasticsearchManagement/StatusTable.jsx
  22. 4 4
      src/client/js/components/Admin/ExportArchiveData/SelectCollectionsModal.jsx
  23. 1 1
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx
  24. 1 1
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  25. 2 2
      src/client/js/components/Admin/ImportDataPage.jsx
  26. 27 31
      src/client/js/components/Admin/MarkdownSetting/LineBreakForm.jsx
  27. 3 0
      src/client/js/components/Admin/Notification/NotificationSetting.jsx
  28. 7 7
      src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx
  29. 3 3
      src/client/js/components/Admin/Security/BasicSecuritySetting.jsx
  30. 5 5
      src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx
  31. 5 5
      src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx
  32. 14 14
      src/client/js/components/Admin/Security/LdapSecuritySetting.jsx
  33. 2 2
      src/client/js/components/Admin/Security/LocalSecuritySetting.jsx
  34. 13 13
      src/client/js/components/Admin/Security/OidcSecuritySetting.jsx
  35. 21 21
      src/client/js/components/Admin/Security/SamlSecuritySetting.jsx
  36. 9 0
      src/client/js/components/Admin/Security/SecurityManagement.jsx
  37. 11 11
      src/client/js/components/Admin/Security/SecuritySetting.jsx
  38. 5 5
      src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx
  39. 17 10
      src/client/js/components/Admin/UserManagement.jsx
  40. 16 16
      src/client/js/components/Admin/Users/PasswordResetModal.jsx
  41. 4 4
      src/client/js/components/Admin/Users/UserInviteModal.jsx
  42. 3 3
      src/client/js/components/Admin/Users/UserTable.jsx
  43. 1 1
      src/client/js/components/BookmarkButton.jsx
  44. 1 1
      src/client/js/components/LikeButton.jsx
  45. 5 5
      src/client/js/components/Me/ApiSettings.jsx
  46. 1 1
      src/client/js/components/Me/AssociateModal.jsx
  47. 1 1
      src/client/js/components/Me/BasicInfoSettings.jsx
  48. 21 25
      src/client/js/components/Me/ExternalAccountLinkedMe.jsx
  49. 19 17
      src/client/js/components/Me/PasswordSettings.jsx
  50. 4 4
      src/client/js/components/Me/UserSettings.jsx
  51. 54 32
      src/client/js/components/Navbar/GrowiSubNavigation.jsx
  52. 37 46
      src/client/js/components/Navbar/GrowiSubNavigationForUserPage.jsx
  53. 35 0
      src/client/js/components/Navbar/NavbarToggler.jsx
  54. 1 1
      src/client/js/components/Navbar/PersonalDropdown.jsx
  55. 16 12
      src/client/js/components/Page/RevisionPath.jsx
  56. 1 1
      src/client/js/components/Page/TagEditor.jsx
  57. 1 1
      src/client/js/components/Page/TagLabels.jsx
  58. 5 10
      src/client/js/components/PageComment/CommentEditor.jsx
  59. 4 9
      src/client/js/components/PageComment/CommentEditorLazyRenderer.jsx
  60. 1 8
      src/client/js/components/PageComment/ReplayComments.jsx
  61. 1 9
      src/client/js/components/PageComments.jsx
  62. 3 3
      src/client/js/components/PageEditor/CodeMirrorEditor.jsx
  63. 1 1
      src/client/js/components/PageEditor/DrawioModal.jsx
  64. 17 12
      src/client/js/components/PageEditor/HandsontableModal.jsx
  65. 2 2
      src/client/js/components/PageEditor/OptionsSelector.jsx
  66. 1 1
      src/client/js/components/PageHistory/RevisionDiff.jsx
  67. 1 4
      src/client/js/components/Sidebar.jsx
  68. 9 6
      src/client/js/components/Sidebar/CustomSidebar.jsx
  69. 9 6
      src/client/js/components/Sidebar/History.jsx
  70. 40 11
      src/client/js/components/Sidebar/SidebarNav.jsx
  71. 0 5
      src/client/js/components/TableOfContents.jsx
  72. 4 1
      src/client/js/services/AdminUsersContainer.js
  73. 19 7
      src/client/js/services/PageContainer.js
  74. 3 0
      src/client/js/services/PersonalContainer.js
  75. 0 7
      src/client/styles/agile-admin/inverse/colors/_apply-colors-dark.scss
  76. 0 4
      src/client/styles/agile-admin/inverse/colors/antarctic.scss
  77. 0 8
      src/client/styles/agile-admin/inverse/colors/spring.scss
  78. 4 0
      src/client/styles/scss/_admin.scss
  79. 83 23
      src/client/styles/scss/_layout.scss
  80. 3 145
      src/client/styles/scss/_layout_kibela.scss
  81. 0 22
      src/client/styles/scss/_layout_variable.scss
  82. 5 0
      src/client/styles/scss/_me.scss
  83. 1 1
      src/client/styles/scss/_mixins.scss
  84. 5 0
      src/client/styles/scss/_navbar.scss
  85. 4 8
      src/client/styles/scss/_on-edit.scss
  86. 1 1
      src/client/styles/scss/_override-bootstrap-variables.scss
  87. 5 0
      src/client/styles/scss/_override-bootstrap.scss
  88. 8 0
      src/client/styles/scss/_override-rbt.scss
  89. 2 1
      src/client/styles/scss/_page.scss
  90. 0 41
      src/client/styles/scss/_page_growi.scss
  91. 0 10
      src/client/styles/scss/_page_header.scss
  92. 100 0
      src/client/styles/scss/_subnav.scss
  93. 12 8
      src/client/styles/scss/_tag.scss
  94. 55 30
      src/client/styles/scss/_user.scss
  95. 0 7
      src/client/styles/scss/_user_growi.scss
  96. 2 2
      src/client/styles/scss/_variables.scss
  97. 6 34
      src/client/styles/scss/atoms/_buttons.scss
  98. 2 2
      src/client/styles/scss/style-app.scss
  99. 18 19
      src/client/styles/scss/theme/_apply-colors-dark.scss
  100. 203 0
      src/client/styles/scss/theme/_apply-colors-kibela.scss

+ 8 - 4
.github/workflows/ci.yml

@@ -53,12 +53,13 @@ jobs:
         yarn lint
         yarn lint
 
 
     - name: Slack Notification
     - name: Slack Notification
-      uses: homoluctus/slatify@master
+      uses: weseek/ghaction-slack-notification@master
       if: failure()
       if: failure()
       with:
       with:
         type: ${{ job.status }}
         type: ${{ job.status }}
         job_name: '*test (${{ matrix.node-version }})*'
         job_name: '*test (${{ matrix.node-version }})*'
         channel: '#ci'
         channel: '#ci'
+        isCompactMode: true
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
 
 
@@ -113,12 +114,13 @@ jobs:
         MONGO_URI: mongodb://localhost:27017/growi_test
         MONGO_URI: mongodb://localhost:27017/growi_test
 
 
     - name: Slack Notification
     - name: Slack Notification
-      uses: homoluctus/slatify@master
+      uses: weseek/ghaction-slack-notification@master
       if: failure()
       if: failure()
       with:
       with:
         type: ${{ job.status }}
         type: ${{ job.status }}
         job_name: '*test (${{ matrix.node-version }})*'
         job_name: '*test (${{ matrix.node-version }})*'
         channel: '#ci'
         channel: '#ci'
+        isCompactMode: true
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
 
 
@@ -183,12 +185,13 @@ jobs:
         yarn build:dev
         yarn build:dev
 
 
     - name: Slack Notification
     - name: Slack Notification
-      uses: homoluctus/slatify@master
+      uses: weseek/ghaction-slack-notification@master
       if: failure()
       if: failure()
       with:
       with:
         type: ${{ job.status }}
         type: ${{ job.status }}
         job_name: '*build-dev (${{ matrix.node-version }})*'
         job_name: '*build-dev (${{ matrix.node-version }})*'
         channel: '#ci'
         channel: '#ci'
+        isCompactMode: true
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
 
 
@@ -268,10 +271,11 @@ jobs:
         path: report
         path: report
 
 
     - name: Slack Notification
     - name: Slack Notification
-      uses: homoluctus/slatify@master
+      uses: weseek/ghaction-slack-notification@master
       if: failure()
       if: failure()
       with:
       with:
         type: ${{ job.status }}
         type: ${{ job.status }}
         job_name: '*build-prod (${{ matrix.node-version }})*'
         job_name: '*build-prod (${{ matrix.node-version }})*'
         channel: '#ci'
         channel: '#ci'
+        isCompactMode: true
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
         url: ${{ secrets.SLACK_WEBHOOK_URL }}

+ 5 - 1
CHANGES.md

@@ -5,10 +5,14 @@
 * Support: Upgrade libs
 * Support: Upgrade libs
     * bootstrap
     * bootstrap
 
 
-## v3.7.4-RC
+## v3.7.5
 
 
 *
 *
 
 
+## v3.7.4
+
+* Fix: Broken by displaying user image
+
 ## v3.7.3
 ## v3.7.3
 
 
 * Feature: Profile Image Cropping
 * Feature: Profile Image Cropping

+ 2 - 2
config/webpack.common.js

@@ -38,12 +38,12 @@ module.exports = (options) => {
       // 'styles/theme-mono-blue':       './src/client/styles/scss/theme/mono-blue.scss',
       // 'styles/theme-mono-blue':       './src/client/styles/scss/theme/mono-blue.scss',
       // 'styles/theme-future':          './src/client/styles/scss/theme/future.scss',
       // 'styles/theme-future':          './src/client/styles/scss/theme/future.scss',
       // 'styles/theme-blue-night':      './src/client/styles/scss/theme/blue-night.scss',
       // 'styles/theme-blue-night':      './src/client/styles/scss/theme/blue-night.scss',
-      // 'styles/theme-kibela':          './src/client/styles/scss/theme/kibela.scss',
+      'styles/theme-kibela':          './src/client/styles/scss/theme/kibela.scss',
       // 'styles/theme-halloween':       './src/client/styles/scss/theme/halloween.scss',
       // 'styles/theme-halloween':       './src/client/styles/scss/theme/halloween.scss',
       // 'styles/theme-wood':          './src/client/styles/scss/theme/wood.scss',
       // 'styles/theme-wood':          './src/client/styles/scss/theme/wood.scss',
       // 'styles/theme-christmas':          './src/client/styles/scss/theme/christmas.scss',
       // 'styles/theme-christmas':          './src/client/styles/scss/theme/christmas.scss',
       // 'styles/theme-island':      './src/client/styles/scss/theme/island.scss',
       // 'styles/theme-island':      './src/client/styles/scss/theme/island.scss',
-      // 'styles/theme-antarctic':      './src/client/styles/scss/theme/antarctic.scss',
+      'styles/theme-antarctic':      './src/client/styles/scss/theme/antarctic.scss',
       // styles for external services
       // styles for external services
       'styles/style-hackmd':          './src/client/styles/hackmd/style.scss',
       'styles/style-hackmd':          './src/client/styles/hackmd/style.scss',
     }, options.entry || {}), // Merge with env dependent settings
     }, options.entry || {}), // Merge with env dependent settings

+ 2 - 3
package.json

@@ -155,7 +155,6 @@
     ],
     ],
     "@alienfast/i18next-loader": "^1.0.16",
     "@alienfast/i18next-loader": "^1.0.16",
     "@atlaskit/drawer": "^5.3.5",
     "@atlaskit/drawer": "^5.3.5",
-    "@atlaskit/logo": "^12.3.3",
     "@atlaskit/navigation-next": "^8.0.2",
     "@atlaskit/navigation-next": "^8.0.2",
     "@babel/core": "^7.4.5",
     "@babel/core": "^7.4.5",
     "@babel/plugin-proposal-class-properties": "^7.8.3",
     "@babel/plugin-proposal-class-properties": "^7.8.3",
@@ -198,8 +197,8 @@
     "load-css-file": "^1.0.0",
     "load-css-file": "^1.0.0",
     "lodash-webpack-plugin": "^0.11.5",
     "lodash-webpack-plugin": "^0.11.5",
     "markdown-it": "^10.0.0",
     "markdown-it": "^10.0.0",
-    "markdown-it-blockdiag": "^1.0.3",
-    "markdown-it-drawio-viewer": "^1.1.3",
+    "markdown-it-blockdiag": "^1.1.1",
+    "markdown-it-drawio-viewer": "^1.2.0",
     "markdown-it-emoji": "^1.4.0",
     "markdown-it-emoji": "^1.4.0",
     "markdown-it-footnote": "^3.0.1",
     "markdown-it-footnote": "^3.0.1",
     "markdown-it-mathjax": "^2.0.0",
     "markdown-it-mathjax": "^2.0.0",

+ 8 - 0
resource/cdn-manifests.js

@@ -138,6 +138,14 @@ module.exports = {
         integrity: '',
         integrity: '',
       },
       },
     },
     },
+    {
+      name: 'animate.css',
+      url: 'https://cdn.jsdelivr.net/npm/animate.css@3.7.2/animate.min.css',
+      groups: ['basis'],
+      args: {
+        integrity: '',
+      },
+    },
     {
     {
       name: 'jquery-ui',
       name: 'jquery-ui',
       url: 'https://cdn.jsdelivr.net/jquery.ui/1.11.4/jquery-ui.min.css',
       url: 'https://cdn.jsdelivr.net/jquery.ui/1.11.4/jquery-ui.min.css',

+ 1 - 1
resource/locales/en-US/translation.json

@@ -294,7 +294,7 @@
   "modal_shortcuts": {
   "modal_shortcuts": {
     "global": {
     "global": {
       "title": "Global shortcuts",
       "title": "Global shortcuts",
-      "Open/Close shortcut help": "Open/Close shortcut help",
+      "Open/Close shortcut help": "Open/Close<br>shortcut help",
       "Edit Page": "Edit Page",
       "Edit Page": "Edit Page",
       "Create Page": "Create Page",
       "Create Page": "Create Page",
       "Show Contributors": "Show Contributors",
       "Show Contributors": "Show Contributors",

+ 2 - 2
resource/locales/ja/translation.json

@@ -292,10 +292,10 @@
   "modal_shortcuts": {
   "modal_shortcuts": {
     "global": {
     "global": {
       "title": "グローバルショートカット",
       "title": "グローバルショートカット",
-      "Open/Close shortcut help": "ショートカットヘルプの表示/非表示",
+      "Open/Close shortcut help": "ショートカットヘルプ<br>の表示/非表示",
       "Edit Page": "ページ編集",
       "Edit Page": "ページ編集",
       "Create Page": "ページ作成",
       "Create Page": "ページ作成",
-      "Show Contributors": "コントリビューターを表示",
+      "Show Contributors": "コントリビューター<br>を表示",
       "Konami Code": "コナミコマンド",
       "Konami Code": "コナミコマンド",
       "konami_code_url": "https://ja.wikipedia.org/wiki/コナミコマンド"
       "konami_code_url": "https://ja.wikipedia.org/wiki/コナミコマンド"
     },
     },

+ 3 - 0
src/client/js/bootstrap.jsx

@@ -4,6 +4,7 @@ import loggerFactory from '@alias/logger';
 import Xss from '@commons/service/xss';
 import Xss from '@commons/service/xss';
 
 
 import HeaderSearchBox from './components/HeaderSearchBox';
 import HeaderSearchBox from './components/HeaderSearchBox';
+import NavbarToggler from './components/Navbar/NavbarToggler';
 import PersonalDropdown from './components/Navbar/PersonalDropdown';
 import PersonalDropdown from './components/Navbar/PersonalDropdown';
 import Sidebar from './components/Sidebar';
 import Sidebar from './components/Sidebar';
 import StaffCredit from './components/StaffCredit/StaffCredit';
 import StaffCredit from './components/StaffCredit/StaffCredit';
@@ -37,6 +38,8 @@ appContainer.injectToWindow();
  *  value: React Element
  *  value: React Element
  */
  */
 const componentMappings = {
 const componentMappings = {
+  'grw-navbar-toggler': <NavbarToggler />,
+
   'search-top': <HeaderSearchBox />,
   'search-top': <HeaderSearchBox />,
   'search-sidebar': <HeaderSearchBox crowi={appContainer} />,
   'search-sidebar': <HeaderSearchBox crowi={appContainer} />,
   'personal-dropdown': <PersonalDropdown />,
   'personal-dropdown': <PersonalDropdown />,

+ 9 - 9
src/client/js/components/Admin/App/AppSetting.jsx

@@ -38,9 +38,9 @@ class AppSetting extends React.Component {
 
 
     return (
     return (
       <React.Fragment>
       <React.Fragment>
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.site_name')}</label>
-          <div className="col-6">
+        <div className="form-group row">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.site_name')}</label>
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -53,8 +53,8 @@ class AppSetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.confidential_name')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.confidential_name')}</label>
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -67,8 +67,8 @@ class AppSetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.default_language')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.default_language')}</label>
+          <div className="col-md-6">
             <div className="custom-control custom-radio custom-control-inline">
             <div className="custom-control custom-radio custom-control-inline">
               <input
               <input
                 type="radio"
                 type="radio"
@@ -97,8 +97,8 @@ class AppSetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.file_uploading')}</label>
-          <div className="col-6">
+          <label className="text-left text-md-right col-md-3 col-form-label">{t('admin:app_setting.file_uploading')}</label>
+          <div className="col-md-6">
             <div className="custom-control custom-checkbox custom-checkbox-info">
             <div className="custom-control custom-checkbox custom-checkbox-info">
               <input
               <input
                 type="checkbox"
                 type="checkbox"

+ 15 - 15
src/client/js/components/Admin/App/AwsSetting.jsx

@@ -50,11 +50,11 @@ class AwsSetting extends React.Component {
           </span>
           </span>
         </p>
         </p>
 
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.region')}
             {t('admin:app_setting.region')}
           </label>
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               placeholder={`${t('eg')} ap-northeast-1`}
               placeholder={`${t('eg')} ap-northeast-1`}
@@ -66,11 +66,11 @@ class AwsSetting extends React.Component {
           </div>
           </div>
         </div>
         </div>
 
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.custom_endpoint')}
             {t('admin:app_setting.custom_endpoint')}
           </label>
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -84,11 +84,11 @@ class AwsSetting extends React.Component {
           </div>
           </div>
         </div>
         </div>
 
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             {t('admin:app_setting.bucket_name')}
             {t('admin:app_setting.bucket_name')}
           </label>
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -101,11 +101,11 @@ class AwsSetting extends React.Component {
           </div>
           </div>
         </div>
         </div>
 
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             Access Key ID
             Access Key ID
           </label>
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -117,11 +117,11 @@ class AwsSetting extends React.Component {
           </div>
           </div>
         </div>
         </div>
 
 
-        <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">
+        <div className="row form-group">
+          <label className="text-left text-md-right col-md-3 col-form-label">
             Secret Access Key
             Secret Access Key
           </label>
           </label>
-          <div className="col-6">
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"

+ 8 - 8
src/client/js/components/Admin/App/MailSetting.jsx

@@ -40,8 +40,8 @@ class MailSetting extends React.Component {
       <React.Fragment>
       <React.Fragment>
         <p className="card well">{t('admin:app_setting.smtp_used')} {t('admin:app_setting.smtp_but_aws')}<br />{t('admin:app_setting.neihter_of')}</p>
         <p className="card well">{t('admin:app_setting.smtp_used')} {t('admin:app_setting.smtp_but_aws')}<br />{t('admin:app_setting.neihter_of')}</p>
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.from_e-mail_address')}</label>
-          <div className="col-6">
+          <label className="col-md-3 col-form-label text-left">{t('admin:app_setting.from_e-mail_address')}</label>
+          <div className="col-md-6">
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
@@ -53,8 +53,8 @@ class MailSetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <label className="col-3 col-form-label">{t('admin:app_setting.smtp_settings')}</label>
-          <div className="col-4">
+          <label className="col-md-3 col-form-label text-left">{t('admin:app_setting.smtp_settings')}</label>
+          <div className="col-md-4">
             <label>{t('admin:app_setting.host')}</label>
             <label>{t('admin:app_setting.host')}</label>
             <input
             <input
               className="form-control"
               className="form-control"
@@ -63,7 +63,7 @@ class MailSetting extends React.Component {
               onChange={(e) => { adminAppContainer.changeSmtpHost(e.target.value) }}
               onChange={(e) => { adminAppContainer.changeSmtpHost(e.target.value) }}
             />
             />
           </div>
           </div>
-          <div className="col-2">
+          <div className="col-md-2">
             <label>{t('admin:app_setting.port')}</label>
             <label>{t('admin:app_setting.port')}</label>
             <input
             <input
               className="form-control"
               className="form-control"
@@ -74,16 +74,16 @@ class MailSetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
-          <div className="col-3 offset-3">
+          <div className="col-md-3 offset-md-3">
             <label>{t('admin:app_setting.user')}</label>
             <label>{t('admin:app_setting.user')}</label>
             <input
             <input
               className="form-control"
               className="form-control"
               type="text"
               type="text"
-              defaultValue={adminAppContainer.state.SmtpUser || ''}
+              defaultValue={adminAppContainer.state.smtpUser || ''}
               onChange={(e) => { adminAppContainer.changeSmtpUser(e.target.value) }}
               onChange={(e) => { adminAppContainer.changeSmtpUser(e.target.value) }}
             />
             />
           </div>
           </div>
-          <div className="col-3">
+          <div className="col-md-3">
             <label>{t('Password')}</label>
             <label>{t('Password')}</label>
             <input
             <input
               className="form-control"
               className="form-control"

+ 1 - 1
src/client/js/components/Admin/App/PluginSetting.jsx

@@ -43,7 +43,7 @@ class PluginSetting extends React.Component {
 
 
         <div className="row form-group mb-5">
         <div className="row form-group mb-5">
           <div className="offset-3 col-6 text-left">
           <div className="offset-3 col-6 text-left">
-            <div className="custom-control custom-switch custom-checkbox-success">
+            <div className="custom-control custom-checkbox custom-checkbox-success">
               <input
               <input
                 id="isEnabledPlugins"
                 id="isEnabledPlugins"
                 className="custom-control-input"
                 className="custom-control-input"

+ 1 - 1
src/client/js/components/Admin/App/SiteUrlSetting.jsx

@@ -43,7 +43,7 @@ class SiteUrlSetting extends React.Component {
           && (<p className="alert alert-danger"><i className="icon-exclamation"></i> {t('admin:app_setting.site_url_warn')}</p>)}
           && (<p className="alert alert-danger"><i className="icon-exclamation"></i> {t('admin:app_setting.site_url_warn')}</p>)}
 
 
         <div className="row form-group">
         <div className="row form-group">
-          <div className="col-9 offset-3">
+          <div className="col-md-9 offset-md-3">
             <table className="table settings-table">
             <table className="table settings-table">
               <colgroup>
               <colgroup>
                 <col className="from-db" />
                 <col className="from-db" />

+ 1 - 1
src/client/js/components/Admin/Common/AdminUpdateButtonRow.jsx

@@ -7,7 +7,7 @@ const AdminUpdateButtonRow = (props) => {
 
 
   return (
   return (
     <div className="row my-3">
     <div className="row my-3">
-      <div className="offset-4 col-5">
+      <div className="mx-auto">
         <button type="button" className="btn btn-primary" onClick={props.onClick} disabled={props.disabled}>{ t('Update') }</button>
         <button type="button" className="btn btn-primary" onClick={props.onClick} disabled={props.disabled}>{ t('Update') }</button>
       </div>
       </div>
     </div>
     </div>

+ 1 - 1
src/client/js/components/Admin/Customize/CustomizeFunctionOption.jsx

@@ -7,7 +7,7 @@ class CustomizeFunctionOption extends React.PureComponent {
   render() {
   render() {
     return (
     return (
       <React.Fragment>
       <React.Fragment>
-        <div className="custom-control custom-switch custom-checkbox-success">
+        <div className="custom-control custom-checkbox custom-checkbox-success">
           <input
           <input
             className="custom-control-input"
             className="custom-control-input"
             type="checkbox"
             type="checkbox"

+ 2 - 2
src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx

@@ -150,14 +150,14 @@ class CustomizeBehaviorSetting extends React.Component {
             </div>
             </div>
 
 
             <div className="form-group row">
             <div className="form-group row">
-              <div className="col-xs-offset-3 col-xs-6 text-left">
+              <div className="offset-3 col-6 text-left">
                 <CustomizeFunctionOption
                 <CustomizeFunctionOption
                   optionId="isAllReplyShown"
                   optionId="isAllReplyShown"
                   label={t('admin:customize_setting.function_options.show_all_reply_comments')}
                   label={t('admin:customize_setting.function_options.show_all_reply_comments')}
                   isChecked={adminCustomizeContainer.state.isAllReplyShown || false}
                   isChecked={adminCustomizeContainer.state.isAllReplyShown || false}
                   onChecked={() => { adminCustomizeContainer.switchIsAllReplyShown() }}
                   onChecked={() => { adminCustomizeContainer.switchIsAllReplyShown() }}
                 >
                 >
-                  <p className="help-block">
+                  <p className="form-text text-muted">
                     {t('admin:customize_setting.function_options.show_all_reply_comments_desc')}
                     {t('admin:customize_setting.function_options.show_all_reply_comments_desc')}
                   </p>
                   </p>
                 </CustomizeFunctionOption>
                 </CustomizeFunctionOption>

+ 17 - 29
src/client/js/components/Admin/Customize/CustomizeLayoutOptions.jsx

@@ -14,8 +14,8 @@ class CustomizeLayoutOptions extends React.Component {
     const { t, adminCustomizeContainer } = this.props;
     const { t, adminCustomizeContainer } = this.props;
 
 
     return (
     return (
-      <div className="row">
-        <div className="col-md-4">
+      <div className="row row-cols-1 row-cols-md-2">
+        <div className="col text-center">
           <CustomizeLayoutOption
           <CustomizeLayoutOption
             layoutType="crowi-plus"
             layoutType="crowi-plus"
             isSelected={adminCustomizeContainer.state.currentLayout === 'growi'}
             isSelected={adminCustomizeContainer.state.currentLayout === 'growi'}
@@ -23,15 +23,17 @@ class CustomizeLayoutOptions extends React.Component {
             labelHtml={`GROWI Enhanced Layout <small class="text-success">${t('admin:customize_setting.recommended')}</small>`}
             labelHtml={`GROWI Enhanced Layout <small class="text-success">${t('admin:customize_setting.recommended')}</small>`}
           >
           >
             <h4>{t('admin:customize_setting.layout_desc.growi_title')}</h4>
             <h4>{t('admin:customize_setting.layout_desc.growi_title')}</h4>
-            <ul>
-              <li>{t('admin:customize_setting.layout_desc.growi_text1')}</li>
-              <li>{t('admin:customize_setting.layout_desc.growi_text2')}</li>
-              <li>{t('admin:customize_setting.layout_desc.growi_text3')}</li>
-            </ul>
+            <div className="text-justify d-inline-block">
+              <ul>
+                <li>{t('admin:customize_setting.layout_desc.growi_text1')}</li>
+                <li>{t('admin:customize_setting.layout_desc.growi_text2')}</li>
+                <li>{t('admin:customize_setting.layout_desc.growi_text3')}</li>
+              </ul>
+            </div>
           </CustomizeLayoutOption>
           </CustomizeLayoutOption>
         </div>
         </div>
 
 
-        <div className="col-md-4">
+        <div className="col text-center">
           <CustomizeLayoutOption
           <CustomizeLayoutOption
             layoutType="kibela"
             layoutType="kibela"
             isSelected={adminCustomizeContainer.state.currentLayout === 'kibela'}
             isSelected={adminCustomizeContainer.state.currentLayout === 'kibela'}
@@ -39,27 +41,13 @@ class CustomizeLayoutOptions extends React.Component {
             labelHtml="Kibela Like Layout"
             labelHtml="Kibela Like Layout"
           >
           >
             <h4>{t('admin:customize_setting.layout_desc.kibela_title')}</h4>
             <h4>{t('admin:customize_setting.layout_desc.kibela_title')}</h4>
-            <ul>
-              <li>{t('admin:customize_setting.layout_desc.kibela_text1')}</li>
-              <li>{t('admin:customize_setting.layout_desc.kibela_text2')}</li>
-              <li>{t('admin:customize_setting.layout_desc.kibela_text3')}</li>
-            </ul>
-          </CustomizeLayoutOption>
-        </div>
-
-        <div className="col-md-4">
-          <CustomizeLayoutOption
-            layoutType="classic"
-            isSelected={adminCustomizeContainer.state.currentLayout === 'crowi'}
-            onSelected={() => adminCustomizeContainer.switchLayoutType('crowi')}
-            labelHtml="Crowi Classic Layout"
-          >
-            <h4>{t('admin:customize_setting.layout_desc.crowi_title')}</h4>
-            <ul>
-              <li>{t('admin:customize_setting.layout_desc.crowi_text1')}</li>
-              <li>{t('admin:customize_setting.layout_desc.crowi_text2')}</li>
-              <li>{t('admin:customize_setting.layout_desc.crowi_text3')}</li>
-            </ul>
+            <div className="text-justify d-inline-block">
+              <ul>
+                <li>{t('admin:customize_setting.layout_desc.kibela_text1')}</li>
+                <li>{t('admin:customize_setting.layout_desc.kibela_text2')}</li>
+                <li>{t('admin:customize_setting.layout_desc.kibela_text3')}</li>
+              </ul>
+            </div>
           </CustomizeLayoutOption>
           </CustomizeLayoutOption>
         </div>
         </div>
       </div>
       </div>

+ 3 - 3
src/client/js/components/Admin/ElasticsearchManagement/ElasticsearchManagement.jsx

@@ -162,7 +162,7 @@ class ElasticsearchManagement extends React.Component {
 
 
         {/* Controls */}
         {/* Controls */}
         <div className="row">
         <div className="row">
-          <label className="col-md-3 col-form-label">{ t('full_text_search_management.reconnect') }</label>
+          <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.reconnect') }</label>
           <div className="col-md-6">
           <div className="col-md-6">
             <ReconnectControls
             <ReconnectControls
               isConfigured={isConfigured}
               isConfigured={isConfigured}
@@ -175,7 +175,7 @@ class ElasticsearchManagement extends React.Component {
         <hr />
         <hr />
 
 
         <div className="row">
         <div className="row">
-          <label className="col-md-3 col-form-label">{ t('full_text_search_management.normalize') }</label>
+          <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.normalize') }</label>
           <div className="col-md-6">
           <div className="col-md-6">
             <NormalizeIndicesControls
             <NormalizeIndicesControls
               isRebuildingProcessing={isRebuildingProcessing}
               isRebuildingProcessing={isRebuildingProcessing}
@@ -189,7 +189,7 @@ class ElasticsearchManagement extends React.Component {
         <hr />
         <hr />
 
 
         <div className="row">
         <div className="row">
-          <label className="col-md-3 col-form-label">{ t('full_text_search_management.rebuild') }</label>
+          <label className="col-md-3 col-form-label text-left text-md-right">{ t('full_text_search_management.rebuild') }</label>
           <div className="col-md-6">
           <div className="col-md-6">
             <RebuildIndexControls
             <RebuildIndexControls
               isRebuildingProcessing={isRebuildingProcessing}
               isRebuildingProcessing={isRebuildingProcessing}

+ 1 - 1
src/client/js/components/Admin/ElasticsearchManagement/NormalizeIndicesControls.jsx

@@ -22,7 +22,7 @@ class NormalizeIndicesControls extends React.PureComponent {
           { t('full_text_search_management.normalize_button') }
           { t('full_text_search_management.normalize_button') }
         </button>
         </button>
 
 
-        <p className="help-block">
+        <p className="form-text text-muted">
           { t('full_text_search_management.normalize_description') }<br />
           { t('full_text_search_management.normalize_description') }<br />
         </p>
         </p>
       </>
       </>

+ 1 - 1
src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx

@@ -22,7 +22,7 @@ class ReconnectControls extends React.PureComponent {
           { t('full_text_search_management.reconnect_button') }
           { t('full_text_search_management.reconnect_button') }
         </button>
         </button>
 
 
-        <p className="help-block">
+        <p className="form-text text-muted">
           { t('full_text_search_management.reconnect_description') }<br />
           { t('full_text_search_management.reconnect_description') }<br />
         </p>
         </p>
       </>
       </>

+ 4 - 3
src/client/js/components/Admin/ElasticsearchManagement/StatusTable.jsx

@@ -20,10 +20,11 @@ class StatusTable extends React.PureComponent {
     return (
     return (
       <div className="card">
       <div className="card">
         <div className="card-header">
         <div className="card-header">
-          <a role="button" data-toggle="collapse" href={`#${collapseId}`} aria-expanded="true" aria-controls={collapseId}>
+
+          <a role="button" className="text-nowrap mr-2" data-toggle="collapse" href={`#${collapseId}`} aria-expanded="true" aria-controls={collapseId}>
             <i className="fa fa-fw fa-database"></i> {indexName}
             <i className="fa fa-fw fa-database"></i> {indexName}
           </a>
           </a>
-          <span className="ml-3">{aliasLabels}</span>
+          <span className="ml-md-3">{aliasLabels}</span>
         </div>
         </div>
         <div id={collapseId} className="collapse">
         <div id={collapseId} className="collapse">
           <div className="card-body">
           <div className="card-body">
@@ -82,7 +83,7 @@ class StatusTable extends React.PureComponent {
       <div className="row">
       <div className="row">
         { Object.keys(indexNameToDataMap).map((indexName) => {
         { Object.keys(indexNameToDataMap).map((indexName) => {
           return (
           return (
-            <div key={`col-${indexName}`} className="col-6">
+            <div key={`col-${indexName}`} className="col-md-6">
               { this.renderIndexInfoPanel(indexName, indexNameToDataMap[indexName], indexNameToAliasMap[indexName]) }
               { this.renderIndexInfoPanel(indexName, indexNameToDataMap[indexName], indexNameToAliasMap[indexName]) }
             </div>
             </div>
           );
           );

+ 4 - 4
src/client/js/components/Admin/ExportArchiveData/SelectCollectionsModal.jsx

@@ -193,26 +193,26 @@ class SelectCollectionsModal extends React.Component {
             </div>
             </div>
             <div className="row mt-4">
             <div className="row mt-4">
               <div className="col-sm-12">
               <div className="col-sm-12">
-                <h3 className="admin-setting-header">Page Collections</h3>
+                <h3 className="admin-setting-header">MongoDB Page Collections</h3>
                 {this.renderGroups(GROUPS_PAGE)}
                 {this.renderGroups(GROUPS_PAGE)}
               </div>
               </div>
             </div>
             </div>
             <div className="row mt-4">
             <div className="row mt-4">
               <div className="col-sm-12">
               <div className="col-sm-12">
-                <h3 className="admin-setting-header">User Collections</h3>
+                <h3 className="admin-setting-header">MongoDB User Collections</h3>
                 {this.renderGroups(GROUPS_USER, 'danger')}
                 {this.renderGroups(GROUPS_USER, 'danger')}
                 {this.renderWarnForUser()}
                 {this.renderWarnForUser()}
               </div>
               </div>
             </div>
             </div>
             <div className="row mt-4">
             <div className="row mt-4">
               <div className="col-sm-12">
               <div className="col-sm-12">
-                <h3 className="admin-setting-header">Config Collections</h3>
+                <h3 className="admin-setting-header">MongoDB Config Collections</h3>
                 {this.renderGroups(GROUPS_CONFIG)}
                 {this.renderGroups(GROUPS_CONFIG)}
               </div>
               </div>
             </div>
             </div>
             <div className="row mt-4">
             <div className="row mt-4">
               <div className="col-sm-12">
               <div className="col-sm-12">
-                <h3 className="admin-setting-header">Other Collections</h3>
+                <h3 className="admin-setting-header">MongoDB Other Collections</h3>
                 {this.renderOthers()}
                 {this.renderOthers()}
               </div>
               </div>
             </div>
             </div>

+ 1 - 1
src/client/js/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx

@@ -203,7 +203,7 @@ export default class ImportCollectionItem extends React.Component {
     return (
     return (
       <div className="card border-light">
       <div className="card border-light">
         <div className="card-header bg-light">
         <div className="card-header bg-light">
-          <div className="d-flex justify-content-between align-items-center">
+          <div className="d-flex justify-content-between align-items-center flex-wrap">
             {/* left */}
             {/* left */}
             {this.renderCheckbox()}
             {this.renderCheckbox()}
             {/* right */}
             {/* right */}

+ 1 - 1
src/client/js/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -387,7 +387,7 @@ class ImportForm extends React.Component {
           const isConfigButtonAvailable = Object.keys(IMPORT_OPTION_CLASS_MAPPING).includes(collectionName);
           const isConfigButtonAvailable = Object.keys(IMPORT_OPTION_CLASS_MAPPING).includes(collectionName);
 
 
           return (
           return (
-            <div className="col-6 my-1" key={collectionName}>
+            <div className="col-md-6 my-1" key={collectionName}>
               <ImportCollectionItem
               <ImportCollectionItem
                 isImporting={isImporting}
                 isImporting={isImporting}
                 isImported={collectionProgress ? isImported : false}
                 isImported={collectionProgress ? isImported : false}

+ 2 - 2
src/client/js/components/Admin/ImportDataPage.jsx

@@ -216,7 +216,7 @@ class ImportDataPage extends React.Component {
                     name="Esa"
                     name="Esa"
                     type="button"
                     type="button"
                     id="importFromEsa"
                     id="importFromEsa"
-                    className="btn btn-outline-secondary btn-esa"
+                    className="btn btn-secondary btn-esa"
                     onClick={this.esaHandleSubmitTest}
                     onClick={this.esaHandleSubmitTest}
                     value={t('admin:importer_management.esa_settings.test_connection')}
                     value={t('admin:importer_management.esa_settings.test_connection')}
                   />
                   />
@@ -309,7 +309,7 @@ class ImportDataPage extends React.Component {
                     name="Qiita"
                     name="Qiita"
                     type="button"
                     type="button"
                     id="importFromQiita"
                     id="importFromQiita"
-                    className="btn btn-outline-secondary btn-qiita"
+                    className="btn btn-secondary btn-qiita"
                     onClick={this.qiitaHandleSubmitTest}
                     onClick={this.qiitaHandleSubmitTest}
                     value={t('admin:importer_management.qiita_settings.test_connection')}
                     value={t('admin:importer_management.qiita_settings.test_connection')}
                   />
                   />

+ 27 - 31
src/client/js/components/Admin/MarkdownSetting/LineBreakForm.jsx

@@ -44,22 +44,20 @@ class LineBreakForm extends React.Component {
     const helpLineBreak = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_desc') };
     const helpLineBreak = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_desc') };
 
 
     return (
     return (
-      <div className="form-group text-left my-3">
-        <div className="col-8 offset-4">
-          <div className="custom-control custom-switch custom-checkbox-success">
-            <input
-              type="checkbox"
-              className="custom-control-input"
-              id="isEnabledLinebreaks"
-              checked={isEnabledLinebreaks}
-              onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaks: !isEnabledLinebreaks }) }}
-            />
-            <label className="custom-control-label" htmlFor="isEnabledLinebreaks">
-              {t('admin:markdown_setting.lineBreak_options.enable_lineBreak') }
-            </label>
-          </div>
-          <p className="help-block" dangerouslySetInnerHTML={helpLineBreak} />
+      <div className="col">
+        <div className="custom-control custom-checkbox custom-checkbox-success">
+          <input
+            type="checkbox"
+            className="custom-control-input"
+            id="isEnabledLinebreaks"
+            checked={isEnabledLinebreaks}
+            onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaks: !isEnabledLinebreaks }) }}
+          />
+          <label className="custom-control-label" htmlFor="isEnabledLinebreaks">
+            {t('admin:markdown_setting.lineBreak_options.enable_lineBreak') }
+          </label>
         </div>
         </div>
+        <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreak} />
       </div>
       </div>
     );
     );
   }
   }
@@ -71,22 +69,20 @@ class LineBreakForm extends React.Component {
     const helpLineBreakInComment = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_for_comment_desc') };
     const helpLineBreakInComment = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_for_comment_desc') };
 
 
     return (
     return (
-      <div className="form-group text-left my-3">
-        <div className="col-8 offset-4">
-          <div className="custom-control custom-switch custom-checkbox-success">
-            <input
-              type="checkbox"
-              className="custom-control-input"
-              id="isEnabledLinebreaksInComments"
-              checked={isEnabledLinebreaksInComments}
-              onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaksInComments: !isEnabledLinebreaksInComments }) }}
-            />
-            <label className="custom-control-label" htmlFor="isEnabledLinebreaksInComments">
-              {t('admin:markdown_setting.lineBreak_options.enable_lineBreak') }
-            </label>
-          </div>
-          <p className="help-block" dangerouslySetInnerHTML={helpLineBreakInComment} />
+      <div className="col">
+        <div className="custom-control custom-checkbox custom-checkbox-success">
+          <input
+            type="checkbox"
+            className="custom-control-input"
+            id="isEnabledLinebreaksInComments"
+            checked={isEnabledLinebreaksInComments}
+            onChange={() => { adminMarkDownContainer.setState({ isEnabledLinebreaksInComments: !isEnabledLinebreaksInComments }) }}
+          />
+          <label className="custom-control-label" htmlFor="isEnabledLinebreaksInComments">
+            {t('admin:markdown_setting.lineBreak_options.enable_lineBreak') }
+          </label>
         </div>
         </div>
+        <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreakInComment} />
       </div>
       </div>
     );
     );
   }
   }
@@ -96,7 +92,7 @@ class LineBreakForm extends React.Component {
 
 
     return (
     return (
       <React.Fragment>
       <React.Fragment>
-        <fieldset className="col-12">
+        <fieldset className="form-group row row-cols-1 row-cols-md-2 mx-3">
           {this.renderLineBreakOption()}
           {this.renderLineBreakOption()}
           {this.renderLineBreakInCommentOption()}
           {this.renderLineBreakInCommentOption()}
         </fieldset>
         </fieldset>

+ 3 - 0
src/client/js/components/Admin/Notification/NotificationSetting.jsx

@@ -63,6 +63,7 @@ class NotificationSetting extends React.Component {
             <NavLink
             <NavLink
               className={`${activeTab === 'slack-configuration' && 'active'} `}
               className={`${activeTab === 'slack-configuration' && 'active'} `}
               onClick={() => { this.toggleActiveTab('slack-configuration') }}
               onClick={() => { this.toggleActiveTab('slack-configuration') }}
+              href="#slack-configuration"
             >
             >
               <i className="icon-settings"></i> Slack Configuration
               <i className="icon-settings"></i> Slack Configuration
             </NavLink>
             </NavLink>
@@ -71,6 +72,7 @@ class NotificationSetting extends React.Component {
             <NavLink
             <NavLink
               className={`${activeTab === 'user-trigger-notification' && 'active'} `}
               className={`${activeTab === 'user-trigger-notification' && 'active'} `}
               onClick={() => { this.toggleActiveTab('user-trigger-notification') }}
               onClick={() => { this.toggleActiveTab('user-trigger-notification') }}
+              href="#user-trigger-notification"
             >
             >
               <i className="icon-settings"></i> User Trigger Notification
               <i className="icon-settings"></i> User Trigger Notification
             </NavLink>
             </NavLink>
@@ -79,6 +81,7 @@ class NotificationSetting extends React.Component {
             <NavLink
             <NavLink
               className={`${activeTab === 'global-notification' && 'active'} `}
               className={`${activeTab === 'global-notification' && 'active'} `}
               onClick={() => { this.toggleActiveTab('global-notification') }}
               onClick={() => { this.toggleActiveTab('global-notification') }}
+              href="#global-notification"
             >
             >
               <i className="icon-settings"></i> Global Notification
               <i className="icon-settings"></i> Global Notification
             </NavLink>
             </NavLink>

+ 7 - 7
src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx

@@ -64,8 +64,8 @@ class SlackAppConfiguration extends React.Component {
             <h2 className="border-bottom mb-5">{t('notification_setting.slack_incoming_configuration')}</h2>
             <h2 className="border-bottom mb-5">{t('notification_setting.slack_incoming_configuration')}</h2>
 
 
             <div className="row mb-3">
             <div className="row mb-3">
-              <label className="col-3 text-right">Webhook URL</label>
-              <div className="col-6">
+              <label className="col-md-3 text-left text-md-right">Webhook URL</label>
+              <div className="col-md-6">
                 <input
                 <input
                   className="form-control"
                   className="form-control"
                   type="text"
                   type="text"
@@ -76,8 +76,8 @@ class SlackAppConfiguration extends React.Component {
             </div>
             </div>
 
 
             <div className="row mb-3">
             <div className="row mb-3">
-              <div className="offset-3 col-6 text-left">
-                <div className="custom-control custom-switch custom-checkbox-success">
+              <div className="offset-md-3 col-md-6 text-left">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     type="checkbox"
                     type="checkbox"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -89,7 +89,7 @@ class SlackAppConfiguration extends React.Component {
                     {t('notification_setting.prioritize_webhook')}
                     {t('notification_setting.prioritize_webhook')}
                   </label>
                   </label>
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {t('notification_setting.prioritize_webhook_desc')}
                   {t('notification_setting.prioritize_webhook_desc')}
                 </p>
                 </p>
               </div>
               </div>
@@ -116,8 +116,8 @@ class SlackAppConfiguration extends React.Component {
               </div>
               </div>
 
 
               <div className="row mb-5">
               <div className="row mb-5">
-                <label className="col-3 text-right">OAuth Access Token</label>
-                <div className="col-6">
+                <label className="col-md-3 text-left text-md-right">OAuth Access Token</label>
+                <div className="col-md-6">
                   <input
                   <input
                     className="form-control"
                     className="form-control"
                     type="text"
                     type="text"

+ 3 - 3
src/client/js/components/Admin/Security/BasicSecuritySetting.jsx

@@ -81,7 +81,7 @@ class BasicSecurityManagement extends React.Component {
                 { t('security_setting.Basic.enable_basic') }
                 { t('security_setting.Basic.enable_basic') }
               </label>
               </label>
             </div>
             </div>
-            <p className="help-block">
+            <p className="form-text text-muted">
               <small>
               <small>
                 <span dangerouslySetInnerHTML={{ __html: t('security_setting.Basic.desc_1') }} /><br />
                 <span dangerouslySetInnerHTML={{ __html: t('security_setting.Basic.desc_1') }} /><br />
                 { t('security_setting.Basic.desc_2')}
                 { t('security_setting.Basic.desc_2')}
@@ -96,7 +96,7 @@ class BasicSecurityManagement extends React.Component {
         <React.Fragment>
         <React.Fragment>
           <div className="row mb-5">
           <div className="row mb-5">
             <div className="offset-3 col-6">
             <div className="offset-3 col-6">
-              <div className="custom-control custom-switch custom-checkbox-success">
+              <div className="custom-control custom-checkbox custom-checkbox-success">
                 <input
                 <input
                   id="bindByEmail-basic"
                   id="bindByEmail-basic"
                   className="custom-control-input"
                   className="custom-control-input"
@@ -110,7 +110,7 @@ class BasicSecurityManagement extends React.Component {
                   dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical', 'username') }}
                   dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical', 'username') }}
                 />
                 />
               </div>
               </div>
-              <p className="help-block">
+              <p className="form-text text-muted">
                 <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn', 'username') }} />
                 <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn', 'username') }} />
               </p>
               </p>
             </div>
             </div>

+ 5 - 5
src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx

@@ -96,7 +96,7 @@ class GitHubSecurityManagement extends React.Component {
               value={adminGitHubSecurityContainer.state.appSiteUrl}
               value={adminGitHubSecurityContainer.state.appSiteUrl}
               readOnly
               readOnly
             />
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
               <div className="alert alert-danger">
                 <i
                 <i
@@ -125,7 +125,7 @@ class GitHubSecurityManagement extends React.Component {
                   value={adminGitHubSecurityContainer.state.githubClientId || ''}
                   value={adminGitHubSecurityContainer.state.githubClientId || ''}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientId(e.target.value)}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientId(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_ID' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_ID' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -141,7 +141,7 @@ class GitHubSecurityManagement extends React.Component {
                   defaultValue={adminGitHubSecurityContainer.state.githubClientSecret || ''}
                   defaultValue={adminGitHubSecurityContainer.state.githubClientSecret || ''}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientSecret(e.target.value)}
                   onChange={e => adminGitHubSecurityContainer.changeGitHubClientSecret(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_SECRET' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GITHUB_CLIENT_SECRET' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -149,7 +149,7 @@ class GitHubSecurityManagement extends React.Component {
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6 text-left">
               <div className="offset-3 col-6 text-left">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByUserNameGitHub"
                     id="bindByUserNameGitHub"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -163,7 +163,7 @@ class GitHubSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>

+ 5 - 5
src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx

@@ -96,7 +96,7 @@ class GoogleSecurityManagement extends React.Component {
               value={adminGoogleSecurityContainer.state.callbackUrl}
               value={adminGoogleSecurityContainer.state.callbackUrl}
               readOnly
               readOnly
             />
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
               <div className="alert alert-danger">
                 <i
                 <i
@@ -125,7 +125,7 @@ class GoogleSecurityManagement extends React.Component {
                   defaultValue={adminGoogleSecurityContainer.state.googleClientId || ''}
                   defaultValue={adminGoogleSecurityContainer.state.googleClientId || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientId(e.target.value)}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientId(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_ID' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_ID' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -141,7 +141,7 @@ class GoogleSecurityManagement extends React.Component {
                   defaultValue={adminGoogleSecurityContainer.state.googleClientSecret || ''}
                   defaultValue={adminGoogleSecurityContainer.state.googleClientSecret || ''}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientSecret(e.target.value)}
                   onChange={e => adminGoogleSecurityContainer.changeGoogleClientSecret(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_SECRET' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_GOOGLE_CLIENT_SECRET' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -149,7 +149,7 @@ class GoogleSecurityManagement extends React.Component {
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6">
               <div className="offset-3 col-6">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByUserNameGoogle"
                     id="bindByUserNameGoogle"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -163,7 +163,7 @@ class GoogleSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>

+ 14 - 14
src/client/js/components/Admin/Security/LdapSecuritySetting.jsx

@@ -112,7 +112,7 @@ class LdapSecuritySetting extends React.Component {
                 />
                 />
                 <small>
                 <small>
                   <p
                   <p
-                    className="help-block"
+                    className="form-text text-muted"
                     // eslint-disable-next-line react/no-danger
                     // eslint-disable-next-line react/no-danger
                     dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.server_url_detail') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.server_url_detail') }}
                   />
                   />
@@ -164,7 +164,7 @@ class LdapSecuritySetting extends React.Component {
                   onChange={e => adminLdapSecurityContainer.changeBindDN(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeBindDN(e.target.value)}
                 />
                 />
                 {(adminLdapSecurityContainer.state.isUserBind === true) ? (
                 {(adminLdapSecurityContainer.state.isUserBind === true) ? (
-                  <p className="help-block passport-ldap-userbind">
+                  <p className="form-text text-muted passport-ldap-userbind">
                     <small>
                     <small>
                       {t('security_setting.ldap.bind_DN_user_detail1')}<br />
                       {t('security_setting.ldap.bind_DN_user_detail1')}<br />
                       {/* eslint-disable-next-line react/no-danger */}
                       {/* eslint-disable-next-line react/no-danger */}
@@ -175,7 +175,7 @@ class LdapSecuritySetting extends React.Component {
                   </p>
                   </p>
                 )
                 )
                   : (
                   : (
-                    <p className="help-block passport-ldap-managerbind">
+                    <p className="form-text text-muted passport-ldap-managerbind">
                       <small>
                       <small>
                         {t('security_setting.ldap.bind_DN_manager_detail')}<br />
                         {t('security_setting.ldap.bind_DN_manager_detail')}<br />
                         {t('security_setting.example')}1: <code>uid=admin,dc=domain,dc=com</code><br />
                         {t('security_setting.example')}1: <code>uid=admin,dc=domain,dc=com</code><br />
@@ -229,7 +229,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapSearchFilter || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeSearchFilter(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeSearchFilter(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {t('security_setting.ldap.search_filter_detail1')}<br />
                     {t('security_setting.ldap.search_filter_detail1')}<br />
                     {/* eslint-disable-next-line react/no-danger */}
                     {/* eslint-disable-next-line react/no-danger */}
@@ -238,7 +238,7 @@ class LdapSecuritySetting extends React.Component {
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.search_filter_detail3') }} />
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.search_filter_detail3') }} />
                   </small>
                   </small>
                 </p>
                 </p>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {t('security_setting.example')}1 - {t('security_setting.ldap.search_filter_example1')}:
                     {t('security_setting.example')}1 - {t('security_setting.ldap.search_filter_example1')}:
                     <code>(|(uid={'{{ username }}'})(mail={'{{ username }}'}))</code><br />
                     <code>(|(uid={'{{ username }}'})(mail={'{{ username }}'}))</code><br />
@@ -266,7 +266,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapUsername || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapUsername || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapUsername(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapUsername(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.username_detail') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.username_detail') }} />
                 </p>
                 </p>
@@ -275,7 +275,7 @@ class LdapSecuritySetting extends React.Component {
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6">
               <div className="offset-3 col-6">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     type="checkbox"
                     type="checkbox"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -290,7 +290,7 @@ class LdapSecuritySetting extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
                 </p>
@@ -310,7 +310,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapMail || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapMail || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapMail(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapMail(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {t('security_setting.ldap.mail_detail')}
                     {t('security_setting.ldap.mail_detail')}
                   </small>
                   </small>
@@ -330,7 +330,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapName || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapAttrMapName || ''}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapName(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeAttrMapName(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {t('security_setting.ldap.name_detail')}
                     {t('security_setting.ldap.name_detail')}
                   </small>
                   </small>
@@ -355,7 +355,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchBase || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchBase || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchBase(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchBase(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {/* eslint-disable-next-line react/no-danger */}
                     {/* eslint-disable-next-line react/no-danger */}
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_base_DN_detail') }} /><br />
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_base_DN_detail') }} /><br />
@@ -377,7 +377,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchFilter || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupSearchFilter || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchFilter(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeGroupSearchFilter(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {/* eslint-disable react/no-danger */}
                     {/* eslint-disable react/no-danger */}
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_filter_detail1') }} /><br />
                     <span dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_filter_detail1') }} /><br />
@@ -386,7 +386,7 @@ class LdapSecuritySetting extends React.Component {
                     {/* eslint-enable react/no-danger */}
                     {/* eslint-enable react/no-danger */}
                   </small>
                   </small>
                 </p>
                 </p>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small>
                   <small>
                     {t('security_setting.example')}:
                     {t('security_setting.example')}:
                     {/* eslint-disable-next-line react/no-danger */}
                     {/* eslint-disable-next-line react/no-danger */}
@@ -409,7 +409,7 @@ class LdapSecuritySetting extends React.Component {
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupDnProperty || ''}
                   defaultValue={adminLdapSecurityContainer.state.ldapGroupDnProperty || ''}
                   onChange={e => adminLdapSecurityContainer.changeGroupDnProperty(e.target.value)}
                   onChange={e => adminLdapSecurityContainer.changeGroupDnProperty(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   {/* eslint-disable-next-line react/no-danger */}
                   {/* eslint-disable-next-line react/no-danger */}
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_user_DN_property_detail') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.ldap.group_search_user_DN_property_detail') }} />
                 </p>
                 </p>

+ 2 - 2
src/client/js/components/Admin/Security/LocalSecuritySetting.jsx

@@ -130,7 +130,7 @@ class LocalSecuritySetting extends React.Component {
                   </div>
                   </div>
                 </div>
                 </div>
 
 
-                <p className="help-block small">
+                <p className="form-text text-muted small">
                   {t('security_setting.Register limitation desc')}
                   {t('security_setting.Register limitation desc')}
                 </p>
                 </p>
               </div>
               </div>
@@ -147,7 +147,7 @@ class LocalSecuritySetting extends React.Component {
                   defaultValue={adminLocalSecurityContainer.state.registrationWhiteList.join('\n')}
                   defaultValue={adminLocalSecurityContainer.state.registrationWhiteList.join('\n')}
                   onChange={e => adminLocalSecurityContainer.changeRegistrationWhiteList(e.target.value)}
                   onChange={e => adminLocalSecurityContainer.changeRegistrationWhiteList(e.target.value)}
                 />
                 />
-                <p className="help-block small">{t('security_setting.restrict_emails')}<br />{t('security_setting.for_instance')}
+                <p className="form-text text-muted small">{t('security_setting.restrict_emails')}<br />{t('security_setting.for_instance')}
                   <code>@growi.org</code>{t('security_setting.only_those')}<br />
                   <code>@growi.org</code>{t('security_setting.only_those')}<br />
                   {t('security_setting.insert_single')}
                   {t('security_setting.insert_single')}
                 </p>
                 </p>

+ 13 - 13
src/client/js/components/Admin/Security/OidcSecuritySetting.jsx

@@ -91,7 +91,7 @@ class OidcSecurityManagement extends React.Component {
               value={adminOidcSecurityContainer.state.callbackUrl}
               value={adminOidcSecurityContainer.state.callbackUrl}
               readOnly
               readOnly
             />
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
               <div className="alert alert-danger">
                 <i
                 <i
@@ -132,7 +132,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcIssuerHost || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcIssuerHost(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_ISSUER_HOST' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_ISSUER_HOST' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -148,7 +148,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcClientId || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcClientId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientId(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_ID' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_ID' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -164,7 +164,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcClientSecret || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcClientSecret || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcClientSecret(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_SECRET' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_OIDC_CLIENT_SECRET' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -184,7 +184,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapId || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapId(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.id_detail') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.id_detail') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -200,7 +200,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapUserName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapUserName(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.username_detail') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.username_detail') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -216,7 +216,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapName || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapName(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.name_detail') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.name_detail') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -232,7 +232,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
                   defaultValue={adminOidcSecurityContainer.state.oidcAttrMapEmail || ''}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                   onChange={e => adminOidcSecurityContainer.changeOidcAttrMapEmail(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.mapping_detail', { target: t('Email') }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.OAuth.OIDC.mapping_detail', { target: t('Email') }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -247,7 +247,7 @@ class OidcSecurityManagement extends React.Component {
                   defaultValue={adminOidcSecurityContainer.state.callbackUrl || ''}
                   defaultValue={adminOidcSecurityContainer.state.callbackUrl || ''}
                   readOnly
                   readOnly
                 />
                 />
-                <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+                <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
                 {!adminGeneralSecurityContainer.state.appSiteUrl && (
                 {!adminGeneralSecurityContainer.state.appSiteUrl && (
                   <div className="alert alert-danger">
                   <div className="alert alert-danger">
                     <i
                     <i
@@ -262,7 +262,7 @@ class OidcSecurityManagement extends React.Component {
 
 
             <div className="row mb-3">
             <div className="row mb-3">
               <div className="offset-3 col-6">
               <div className="offset-3 col-6">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByUserName-oidc"
                     id="bindByUserName-oidc"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -276,7 +276,7 @@ class OidcSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -284,7 +284,7 @@ class OidcSecurityManagement extends React.Component {
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6">
               <div className="offset-3 col-6">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByEmail-oidc"
                     id="bindByEmail-oidc"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -298,7 +298,7 @@ class OidcSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>

+ 21 - 21
src/client/js/components/Admin/Security/SamlSecuritySetting.jsx

@@ -118,7 +118,7 @@ class SamlSecurityManagement extends React.Component {
               defaultValue={adminSamlSecurityContainer.state.callbackUrl}
               defaultValue={adminSamlSecurityContainer.state.callbackUrl}
               readOnly
               readOnly
             />
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'SAML Identity' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'SAML Identity' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
               <div className="alert alert-danger">
                 <i
                 <i
@@ -180,7 +180,7 @@ class SamlSecurityManagement extends React.Component {
                       value={this.state.envEntryPoint || ''}
                       value={this.state.envEntryPoint || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ENTRY_POINT' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ENTRY_POINT' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -204,7 +204,7 @@ class SamlSecurityManagement extends React.Component {
                       value={this.state.envIssuer || ''}
                       value={this.state.envIssuer || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ISSUER' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ISSUER' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -249,7 +249,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       readOnly
                       readOnly
                       value={this.state.envCert || ''}
                       value={this.state.envCert || ''}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_CERT' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_CERT' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -281,7 +281,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapId}
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapId}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapId(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapId(e.target.value)}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                       <small>
                         {t('security_setting.SAML.id_detail')}
                         {t('security_setting.SAML.id_detail')}
                       </small>
                       </small>
@@ -294,7 +294,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapId || ''}
                       value={this.state.envAttrMapId || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_ID' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_ID' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -309,7 +309,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapUsername}
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapUsername}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapUserName(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapUserName(e.target.value)}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.username_detail') }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.username_detail') }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -320,7 +320,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapUsername || ''}
                       value={this.state.envAttrMapUsername || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_USERNAME' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_USERNAME' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -335,7 +335,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapMail}
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapMail}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapMail(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapMail(e.target.value)}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: 'Email' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: 'Email' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -346,7 +346,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapMail || ''}
                       value={this.state.envAttrMapMail || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_MAIL' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_MAIL' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>
@@ -361,7 +361,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapFirstName}
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapFirstName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapFirstName(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapFirstName(e.target.value)}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       {/* eslint-disable-next-line max-len */}
                       {/* eslint-disable-next-line max-len */}
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapFirstName') }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapFirstName') }) }} />
                     </p>
                     </p>
@@ -373,7 +373,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapFirstName || ''}
                       value={this.state.envAttrMapFirstName || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_FIRST_NAME' }) }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_FIRST_NAME' }) }} />
                         <br />
                         <br />
@@ -392,7 +392,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapLastName}
                       defaultValue={adminSamlSecurityContainer.state.samlAttrMapLastName}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapLastName(e.target.value)}
                       onChange={e => adminSamlSecurityContainer.changeSamlAttrMapLastName(e.target.value)}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       {/* eslint-disable-next-line max-len */}
                       {/* eslint-disable-next-line max-len */}
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapLastName') }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.mapping_detail', { target: t('security_setting.form_item_name.attrMapLastName') }) }} />
                     </p>
                     </p>
@@ -404,7 +404,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envAttrMapLastName || ''}
                       value={this.state.envAttrMapLastName || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_LAST_NAME' }) }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ATTR_MAPPING_LAST_NAME' }) }} />
                         <br />
                         <br />
@@ -422,7 +422,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6 text-left">
               <div className="offset-3 col-6 text-left">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByUserName-SAML"
                     id="bindByUserName-SAML"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -436,7 +436,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -444,7 +444,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6 text-left">
               <div className="offset-3 col-6 text-left">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByEmail-SAML"
                     id="bindByEmail-SAML"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -458,7 +458,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -468,7 +468,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
               Attribute-based Login Control
               Attribute-based Login Control
             </h3>
             </h3>
 
 
-            <p className="help-block">
+            <p className="form-text text-muted">
               <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_detail') }} />
               <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_detail') }} />
             </p>
             </p>
 
 
@@ -494,7 +494,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       onChange={(e) => { adminSamlSecurityContainer.changeSamlABLCRule(e.target.value) }}
                       onChange={(e) => { adminSamlSecurityContainer.changeSamlABLCRule(e.target.value) }}
                       readOnly={useOnlyEnvVars}
                       readOnly={useOnlyEnvVars}
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small>
                       <small>
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_detail') }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_detail') }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_example') }} />
                         <span dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.attr_based_login_control_rule_example') }} />
@@ -508,7 +508,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
                       value={this.state.envABLCRule || ''}
                       value={this.state.envABLCRule || ''}
                       readOnly
                       readOnly
                     />
                     />
-                    <p className="help-block">
+                    <p className="form-text text-muted">
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ABLC_RULE' }) }} />
                       <small dangerouslySetInnerHTML={{ __html: t('security_setting.SAML.Use env var if empty', { env: 'SAML_ABLC_RULE' }) }} />
                     </p>
                     </p>
                   </td>
                   </td>

+ 9 - 0
src/client/js/components/Admin/Security/SecurityManagement.jsx

@@ -65,6 +65,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-local' && 'active'} `}
                 className={`${activeTab === 'passport-local' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-local') }}
                 onClick={() => { this.toggleActiveTab('passport-local') }}
+                href="#passport-local"
               >
               >
                 <i className="fa fa-users" /> ID/Pass
                 <i className="fa fa-users" /> ID/Pass
               </NavLink>
               </NavLink>
@@ -73,6 +74,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-ldap' && 'active'} `}
                 className={`${activeTab === 'passport-ldap' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-ldap') }}
                 onClick={() => { this.toggleActiveTab('passport-ldap') }}
+                href="#passport-ldap"
               >
               >
                 <i className="fa fa-sitemap" /> LDAP
                 <i className="fa fa-sitemap" /> LDAP
               </NavLink>
               </NavLink>
@@ -81,6 +83,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-saml' && 'active'} `}
                 className={`${activeTab === 'passport-saml' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-saml') }}
                 onClick={() => { this.toggleActiveTab('passport-saml') }}
+                href="#passport-saml"
               >
               >
                 <i className="fa fa-key" /> SAML
                 <i className="fa fa-key" /> SAML
               </NavLink>
               </NavLink>
@@ -89,6 +92,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-oidc' && 'active'} `}
                 className={`${activeTab === 'passport-oidc' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-oidc') }}
                 onClick={() => { this.toggleActiveTab('passport-oidc') }}
+                href="#passport-oidc"
               >
               >
                 <i className="fa fa-openid" /> OIDC
                 <i className="fa fa-openid" /> OIDC
               </NavLink>
               </NavLink>
@@ -97,6 +101,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-basic' && 'active'} `}
                 className={`${activeTab === 'passport-basic' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-basic') }}
                 onClick={() => { this.toggleActiveTab('passport-basic') }}
+                href="#passport-basic"
               >
               >
                 <i className="fa fa-lock" /> BASIC
                 <i className="fa fa-lock" /> BASIC
               </NavLink>
               </NavLink>
@@ -105,6 +110,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-google' && 'active'} `}
                 className={`${activeTab === 'passport-google' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-google') }}
                 onClick={() => { this.toggleActiveTab('passport-google') }}
+                href="#passport-google"
               >
               >
                 <i className="fa fa-google" /> Google
                 <i className="fa fa-google" /> Google
               </NavLink>
               </NavLink>
@@ -113,6 +119,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-github' && 'active'} `}
                 className={`${activeTab === 'passport-github' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-github') }}
                 onClick={() => { this.toggleActiveTab('passport-github') }}
+                href="#passport-github"
               >
               >
                 <i className="fa fa-github" /> GitHub
                 <i className="fa fa-github" /> GitHub
               </NavLink>
               </NavLink>
@@ -121,6 +128,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-twitter' && 'active'} `}
                 className={`${activeTab === 'passport-twitter' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-twitter') }}
                 onClick={() => { this.toggleActiveTab('passport-twitter') }}
+                href="#passport-twitter"
               >
               >
                 <i className="fa fa-twitter" /> Twitter
                 <i className="fa fa-twitter" /> Twitter
               </NavLink>
               </NavLink>
@@ -129,6 +137,7 @@ class SecurityManagement extends React.Component {
               <NavLink
               <NavLink
                 className={`${activeTab === 'passport-facebook' && 'active'} `}
                 className={`${activeTab === 'passport-facebook' && 'active'} `}
                 onClick={() => { this.toggleActiveTab('passport-facebook') }}
                 onClick={() => { this.toggleActiveTab('passport-facebook') }}
+                href="#passport-facebook"
               >
               >
                 <i className="fa fa-facebook" /> (TBD) Facebook
                 <i className="fa fa-facebook" /> (TBD) Facebook
               </NavLink>
               </NavLink>

+ 11 - 11
src/client/js/components/Admin/Security/SecuritySetting.jsx

@@ -58,10 +58,10 @@ class SecuritySetting extends React.Component {
         </div>
         </div>
           )}
           )}
         <div className="row mb-5">
         <div className="row mb-5">
-          <div className="col-3 text-right py-2">
+          <div className="col-md-3 text-md-right text-nowrap py-2 mr-md-5">
             <strong>{t('security_setting.Guest Users Access')}</strong>
             <strong>{t('security_setting.Guest Users Access')}</strong>
           </div>
           </div>
-          <div className="col-6">
+          <div className="col-md-6 ml-md-5">
             <div className="dropdown">
             <div className="dropdown">
               <button
               <button
                 className={`btn btn-outline-secondary dropdown-toggle ${adminGeneralSecurityContainer.isWikiModeForced && 'disabled'}`}
                 className={`btn btn-outline-secondary dropdown-toggle ${adminGeneralSecurityContainer.isWikiModeForced && 'disabled'}`}
@@ -102,9 +102,9 @@ class SecuritySetting extends React.Component {
         </div>
         </div>
           )}
           )}
         <div className="row mb-5">
         <div className="row mb-5">
-          <strong className="col-3 text-right" dangerouslySetInnerHTML={{ __html: t('security_setting.page_listing_1') }} />
-          <div className="col-6">
-            <div className="custom-control custom-switch custom-checkbox-success">
+          <strong className="col-md-3 text-md-right text-nowrap mb-2 mr-md-5" dangerouslySetInnerHTML={{ __html: t('security_setting.page_listing_1') }} />
+          <div className="col-md-6">
+            <div className="custom-control custom-checkbox custom-checkbox-success ml-md-5">
               <input
               <input
                 type="checkbox"
                 type="checkbox"
                 className="custom-control-input"
                 className="custom-control-input"
@@ -120,9 +120,9 @@ class SecuritySetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row mb-5">
         <div className="row mb-5">
-          <strong className="col-3 text-right" dangerouslySetInnerHTML={{ __html: t('security_setting.page_listing_2') }} />
-          <div className="col-6">
-            <div className="custom-control custom-switch custom-checkbox-success">
+          <strong className="col-md-3 text-md-right text-nowrap mr-md-5 mb-2" dangerouslySetInnerHTML={{ __html: t('security_setting.page_listing_2') }} />
+          <div className="col-md-6 ml-md-5">
+            <div className="custom-control custom-checkbox custom-checkbox-success">
               <input
               <input
                 type="checkbox"
                 type="checkbox"
                 className="custom-control-input"
                 className="custom-control-input"
@@ -138,10 +138,10 @@ class SecuritySetting extends React.Component {
         </div>
         </div>
 
 
         <div className="row mb-5">
         <div className="row mb-5">
-          <div className="col-3 text-right">
+          <div className="col-md-3 text-md-right mr-md-5 mb-2">
             <strong>{t('security_setting.complete_deletion')}</strong>
             <strong>{t('security_setting.complete_deletion')}</strong>
           </div>
           </div>
-          <div className="col-9">
+          <div className="col-md-6 ml-md-5">
             <div className="dropdown">
             <div className="dropdown">
               <button
               <button
                 className="btn btn-outline-secondary dropdown-toggle"
                 className="btn btn-outline-secondary dropdown-toggle"
@@ -167,7 +167,7 @@ class SecuritySetting extends React.Component {
                   {t('security_setting.admin_and_author')}
                   {t('security_setting.admin_and_author')}
                 </a>
                 </a>
               </div>
               </div>
-              <p className="help-block small">
+              <p className="form-text text-muted small">
                 {t('security_setting.complete_deletion_explain')}
                 {t('security_setting.complete_deletion_explain')}
               </p>
               </p>
             </div>
             </div>

+ 5 - 5
src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx

@@ -96,7 +96,7 @@ class TwitterSecurityManagement extends React.Component {
               value={adminTwitterSecurityContainer.state.callbackUrl}
               value={adminTwitterSecurityContainer.state.callbackUrl}
               readOnly
               readOnly
             />
             />
-            <p className="help-block small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
+            <p className="form-text text-muted small">{t('security_setting.desc_of_callback_URL', { AuthName: 'OAuth' })}</p>
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
             {!adminGeneralSecurityContainer.state.appSiteUrl && (
               <div className="alert alert-danger">
               <div className="alert alert-danger">
                 <i
                 <i
@@ -125,7 +125,7 @@ class TwitterSecurityManagement extends React.Component {
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerKey || ''}
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerKey || ''}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerKey(e.target.value)}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerKey(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_KEY' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_KEY' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -141,7 +141,7 @@ class TwitterSecurityManagement extends React.Component {
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerSecret || ''}
                   defaultValue={adminTwitterSecurityContainer.state.twitterConsumerSecret || ''}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerSecret(e.target.value)}
                   onChange={e => adminTwitterSecurityContainer.changeTwitterConsumerSecret(e.target.value)}
                 />
                 />
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_SECRET' }) }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Use env var if empty', { env: 'OAUTH_TWITTER_CONSUMER_SECRET' }) }} />
                 </p>
                 </p>
               </div>
               </div>
@@ -149,7 +149,7 @@ class TwitterSecurityManagement extends React.Component {
 
 
             <div className="row mb-5">
             <div className="row mb-5">
               <div className="offset-3 col-6">
               <div className="offset-3 col-6">
-                <div className="custom-control custom-switch custom-checkbox-success">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
                   <input
                   <input
                     id="bindByUserNameTwitter"
                     id="bindByUserNameTwitter"
                     className="custom-control-input"
                     className="custom-control-input"
@@ -163,7 +163,7 @@ class TwitterSecurityManagement extends React.Component {
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                     dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical') }}
                   />
                   />
                 </div>
                 </div>
-                <p className="help-block">
+                <p className="form-text text-muted">
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                   <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat email matching as identical_warn') }} />
                 </p>
                 </p>
               </div>
               </div>

+ 17 - 10
src/client/js/components/Admin/UserManagement.jsx

@@ -121,10 +121,17 @@ class UserManagement extends React.Component {
 
 
     return (
     return (
       <Fragment>
       <Fragment>
-        {adminUsersContainer.state.userForPasswordResetModal && <PasswordResetModal />}
+        {adminUsersContainer.state.userForPasswordResetModal != null
+        && (
+        <PasswordResetModal
+          isOpen={adminUsersContainer.state.isPasswordResetModalShown}
+          onClose={adminUsersContainer.hidePasswordResetModal}
+          userForPasswordResetModal={adminUsersContainer.state.userForPasswordResetModal}
+        />
+        )}
         <p>
         <p>
           <InviteUserControl />
           <InviteUserControl />
-          <a className="btn text-dark btn-outline-secondary ml-2" href="/admin/users/external-accounts" role="button">
+          <a className="btn btn-outline-secondary ml-2" href="/admin/users/external-accounts" role="button">
             <i className="icon-user-follow" aria-hidden="true"></i>
             <i className="icon-user-follow" aria-hidden="true"></i>
             {t('admin:user_management.external_account')}
             {t('admin:user_management.external_account')}
           </a>
           </a>
@@ -157,7 +164,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('all') }}
                   onClick={() => { this.handleClick('all') }}
                 />
                 />
                 <label className="custom-control-label" htmlFor="c1">
                 <label className="custom-control-label" htmlFor="c1">
-                  <span className="badge badge-primary d-inline-block vt mt-1">All</span>
+                  <span className="badge badge-pill badge-primary d-inline-block vt mt-1">All</span>
                 </label>
                 </label>
               </div>
               </div>
 
 
@@ -170,7 +177,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('registered') }}
                   onClick={() => { this.handleClick('registered') }}
                 />
                 />
                 <label className="custom-control-label" htmlFor="c2">
                 <label className="custom-control-label" htmlFor="c2">
-                  <span className="badge badge-info d-inline-block vt mt-1">Approval Pending</span>
+                  <span className="badge badge-pill badge-info d-inline-block vt mt-1">Approval Pending</span>
                 </label>
                 </label>
               </div>
               </div>
 
 
@@ -183,11 +190,11 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('active') }}
                   onClick={() => { this.handleClick('active') }}
                 />
                 />
                 <label className="custom-control-label" htmlFor="c3">
                 <label className="custom-control-label" htmlFor="c3">
-                  <span className="badge badge-success d-inline-block vt mt-1">Active</span>
+                  <span className="badge badge-pill badge-success d-inline-block vt mt-1">Active</span>
                 </label>
                 </label>
               </div>
               </div>
 
 
-              <div className="custom-control custom-checkbox custom-checkbox-secondary mr-2">
+              <div className="custom-control custom-checkbox custom-checkbox-warning mr-2">
                 <input
                 <input
                   className="custom-control-input"
                   className="custom-control-input"
                   type="checkbox"
                   type="checkbox"
@@ -196,7 +203,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('suspended') }}
                   onClick={() => { this.handleClick('suspended') }}
                 />
                 />
                 <label className="custom-control-label" htmlFor="c4">
                 <label className="custom-control-label" htmlFor="c4">
-                  <span className="badge badge-secondary d-inline-block vt mt-1">Suspended</span>
+                  <span className="badge badge-pill badge-warning d-inline-block vt mt-1">Suspended</span>
                 </label>
                 </label>
               </div>
               </div>
 
 
@@ -209,7 +216,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('invited') }}
                   onClick={() => { this.handleClick('invited') }}
                 />
                 />
                 <label className="custom-control-label" htmlFor="c5">
                 <label className="custom-control-label" htmlFor="c5">
-                  <span className="badge badge-info d-inline-block vt mt-1">Invited</span>
+                  <span className="badge badge-pill badge-info d-inline-block vt mt-1">Invited</span>
                 </label>
                 </label>
               </div>
               </div>
             </div>
             </div>
@@ -228,8 +235,8 @@ class UserManagement extends React.Component {
               </button>
               </button>
             </div>
             </div>
 
 
-            <div className="ml-5">
-              {this.state.isNotifyCommentShow && <span className="text-warning small">{t('admin:user_management.click_twice_same_checkbox')}</span>}
+            <div className="ml-4">
+              {this.state.isNotifyCommentShow && <span className="text-warning">{t('admin:user_management.click_twice_same_checkbox')}</span>}
             </div>
             </div>
 
 
           </div>
           </div>

+ 16 - 16
src/client/js/components/Admin/Users/PasswordResetModal.jsx

@@ -8,7 +8,6 @@ import {
 import { toastError } from '../../../util/apiNotification';
 import { toastError } from '../../../util/apiNotification';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 import AppContainer from '../../../services/AppContainer';
-import AdminUsersContainer from '../../../services/AdminUsersContainer';
 
 
 class PasswordResetModal extends React.Component {
 class PasswordResetModal extends React.Component {
 
 
@@ -24,10 +23,9 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
   async resetPassword() {
   async resetPassword() {
-    const { appContainer, adminUsersContainer } = this.props;
-    const user = adminUsersContainer.state.userForPasswordResetModal;
+    const { appContainer, userForPasswordResetModal } = this.props;
 
 
-    const res = await appContainer.apiPost('/admin/users.resetPassword', { user_id: user._id });
+    const res = await appContainer.apiPost('/admin/users.resetPassword', { user_id: userForPasswordResetModal._id });
     if (res.ok) {
     if (res.ok) {
       this.setState({ temporaryPassword: res.newPassword, isPasswordResetDone: true });
       this.setState({ temporaryPassword: res.newPassword, isPasswordResetDone: true });
     }
     }
@@ -37,14 +35,13 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
   renderModalBodyBeforeReset() {
   renderModalBodyBeforeReset() {
-    const { t, adminUsersContainer } = this.props;
-    const user = adminUsersContainer.state.userForPasswordResetModal;
+    const { t, userForPasswordResetModal } = this.props;
 
 
     return (
     return (
       <div>
       <div>
         <p className="alert alert-danger">{t('admin:user_management.reset_password_modal.password_reset_message')}</p>
         <p className="alert alert-danger">{t('admin:user_management.reset_password_modal.password_reset_message')}</p>
         <p>
         <p>
-          {t('admin:user_management.reset_password_modal.target_user')}: <code>{user.email}</code>
+          {t('admin:user_management.reset_password_modal.target_user')}: <code>{userForPasswordResetModal.email}</code>
         </p>
         </p>
         <p>
         <p>
           {t('admin:user_management.reset_password_modal.new_password')}: <code>{this.state.temporaryPassword}</code>
           {t('admin:user_management.reset_password_modal.new_password')}: <code>{this.state.temporaryPassword}</code>
@@ -54,8 +51,7 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
   returnModalBodyAfterReset() {
   returnModalBodyAfterReset() {
-    const { t, adminUsersContainer } = this.props;
-    const user = adminUsersContainer.state.userForPasswordResetModal;
+    const { t, userForPasswordResetModal } = this.props;
 
 
     return (
     return (
       <div>
       <div>
@@ -64,7 +60,7 @@ class PasswordResetModal extends React.Component {
           <span className="text-danger">{t('admin:user_management.reset_password_modal.send_new_password')}</span>
           <span className="text-danger">{t('admin:user_management.reset_password_modal.send_new_password')}</span>
         </p>
         </p>
         <p>
         <p>
-          {t('admin:user_management.reset_password_modal.target_user')}: <code>{user.email}</code>
+          {t('admin:user_management.reset_password_modal.target_user')}: <code>{userForPasswordResetModal.email}</code>
         </p>
         </p>
         <button type="submit" className="btn btn-primary" onClick={this.resetPassword}>
         <button type="submit" className="btn btn-primary" onClick={this.resetPassword}>
           {t('admin:user_management.reset_password')}
           {t('admin:user_management.reset_password')}
@@ -76,18 +72,18 @@ class PasswordResetModal extends React.Component {
   returnModalFooter() {
   returnModalFooter() {
     return (
     return (
       <div>
       <div>
-        <button type="submit" className="btn btn-primary" onClick={this.props.adminUsersContainer.hidePasswordResetModal}>OK</button>
+        <button type="submit" className="btn btn-primary" onClick={this.props.onClose}>OK</button>
       </div>
       </div>
     );
     );
   }
   }
 
 
 
 
   render() {
   render() {
-    const { t, adminUsersContainer } = this.props;
+    const { t } = this.props;
 
 
     return (
     return (
-      <Modal isOpen={adminUsersContainer.state.isPasswordResetModalShown} toggle={adminUsersContainer.hidePasswordResetModal}>
-        <ModalHeader tag="h4" toggle={adminUsersContainer.hidePasswordResetModal} className="modal-header">
+      <Modal isOpen={this.props.isOpen} toggle={this.props.onClose}>
+        <ModalHeader tag="h4" toggle={this.props.onClose} className="modal-header">
           {t('admin:user_management.reset_password') }
           {t('admin:user_management.reset_password') }
         </ModalHeader>
         </ModalHeader>
         <ModalBody>
         <ModalBody>
@@ -106,13 +102,17 @@ class PasswordResetModal extends React.Component {
  * Wrapper component for using unstated
  * Wrapper component for using unstated
  */
  */
 const PasswordResetModalWrapper = (props) => {
 const PasswordResetModalWrapper = (props) => {
-  return createSubscribedElement(PasswordResetModal, props, [AppContainer, AdminUsersContainer]);
+  return createSubscribedElement(PasswordResetModal, props, [AppContainer]);
 };
 };
 
 
 PasswordResetModal.propTypes = {
 PasswordResetModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
+
+  isOpen: PropTypes.bool.isRequired,
+  onClose: PropTypes.func.isRequired,
+  userForPasswordResetModal: PropTypes.object,
+
 };
 };
 
 
 export default withTranslation()(PasswordResetModalWrapper);
 export default withTranslation()(PasswordResetModalWrapper);

+ 4 - 4
src/client/js/components/Admin/Users/UserInviteModal.jsx

@@ -87,7 +87,7 @@ class UserInviteModal extends React.Component {
         <div>
         <div>
           <button
           <button
             type="button"
             type="button"
-            className="btn btn-outline-danger rounded-pill mr-2"
+            className="btn btn-outline-secondary mr-2"
             onClick={this.onToggleModal}
             onClick={this.onToggleModal}
           >
           >
             Cancel
             Cancel
@@ -95,7 +95,7 @@ class UserInviteModal extends React.Component {
 
 
           <button
           <button
             type="button"
             type="button"
-            className="btn btn-outline-primary rounded-pill"
+            className="btn btn-primary"
             onClick={this.handleSubmit}
             onClick={this.handleSubmit}
             disabled={!this.validEmail()}
             disabled={!this.validEmail()}
           >
           >
@@ -116,7 +116,7 @@ class UserInviteModal extends React.Component {
         </label>
         </label>
         <button
         <button
           type="button"
           type="button"
-          className="btn btn-outline-primary"
+          className="btn btn-outline-secondary"
           onClick={this.onToggleModal}
           onClick={this.onToggleModal}
         >
         >
           Close
           Close
@@ -193,7 +193,7 @@ class UserInviteModal extends React.Component {
 
 
 
 
     return (
     return (
-      <Modal isOpen={adminUsersContainer.state.isUserInviteModalShown} toggle={this.onToggleModal}>
+      <Modal isOpen={adminUsersContainer.state.isUserInviteModalShown}>
         <ModalHeader tag="h4" toggle={this.onToggleModal} className="modal-header">
         <ModalHeader tag="h4" toggle={this.onToggleModal} className="modal-header">
           {t('admin:user_management.invite_users') }
           {t('admin:user_management.invite_users') }
         </ModalHeader>
         </ModalHeader>

+ 3 - 3
src/client/js/components/Admin/Users/UserTable.jsx

@@ -42,7 +42,7 @@ class UserTable extends React.Component {
         text = 'Active';
         text = 'Active';
         break;
         break;
       case 3:
       case 3:
-        additionalClassName = 'badge-secondary';
+        additionalClassName = 'badge-warning';
         text = 'Suspended';
         text = 'Suspended';
         break;
         break;
       case 4:
       case 4:
@@ -56,7 +56,7 @@ class UserTable extends React.Component {
     }
     }
 
 
     return (
     return (
-      <span className={`badge ${additionalClassName}`}>
+      <span className={`badge badge-pill ${additionalClassName}`}>
         {text}
         {text}
       </span>
       </span>
     );
     );
@@ -71,7 +71,7 @@ class UserTable extends React.Component {
     const { t } = this.props;
     const { t } = this.props;
 
 
     if (isAdmin) {
     if (isAdmin) {
-      return <span className="badge badge-primary ml-2">{t('admin:user_management.user_table.administrator')}</span>;
+      return <span className="badge badge-primary badge-pill ml-2">{t('admin:user_management.user_table.administrator')}</span>;
     }
     }
   }
   }
 
 

+ 1 - 1
src/client/js/components/BookmarkButton.jsx

@@ -64,7 +64,7 @@ class BookmarkButton extends React.Component {
         href="#"
         href="#"
         title="Bookmark"
         title="Bookmark"
         onClick={this.handleClick}
         onClick={this.handleClick}
-        className={`btn btn-circle btn-outline-warning btn-bookmark border-0 ${`btn-${this.props.size}`} ${this.state.isBookmarked && 'active'}`}
+        className={`btn rounded-circle btn-outline-warning btn-bookmark border-0 ${`btn-${this.props.size}`} ${this.state.isBookmarked && 'active'}`}
       >
       >
         <i className="icon-star"></i>
         <i className="icon-star"></i>
       </button>
       </button>

+ 1 - 1
src/client/js/components/LikeButton.jsx

@@ -43,7 +43,7 @@ class LikeButton extends React.Component {
       <button
       <button
         type="button"
         type="button"
         onClick={this.handleClick}
         onClick={this.handleClick}
-        className={`btn btn-circle btn-outline-info btn-like border-0 ${this.state.isLiked ? 'active' : ''}`}
+        className={`btn rounded-circle btn-outline-info btn-like border-0 ${this.state.isLiked ? 'active' : ''}`}
       >
       >
         <i className="icon-like"></i>
         <i className="icon-like"></i>
       </button>
       </button>

+ 5 - 5
src/client/js/components/Me/ApiSettings.jsx

@@ -38,13 +38,13 @@ class ApiSettings extends React.Component {
     return (
     return (
       <React.Fragment>
       <React.Fragment>
 
 
-        <div className="mb-5 container-fluid">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">{ t('API Token Settings') }</h2>
           <h2 className="border-bottom">{ t('API Token Settings') }</h2>
         </div>
         </div>
 
 
         <div className="row mb-3">
         <div className="row mb-3">
-          <label htmlFor="apiToken" className="col-3 text-right">{t('Current API Token')}</label>
-          <div className="col-6">
+          <label htmlFor="apiToken" className="col-md-3 text-md-right">{t('Current API Token')}</label>
+          <div className="col-md-6">
             {personalContainer.state.apiToken != null
             {personalContainer.state.apiToken != null
             ? (
             ? (
               <input
               <input
@@ -65,7 +65,7 @@ class ApiSettings extends React.Component {
 
 
 
 
         <div className="row">
         <div className="row">
-          <div className="offset-3 col-6">
+          <div className="offset-lg-1 col-lg-7">
 
 
             <p className="alert alert-warning">
             <p className="alert alert-warning">
               { t('page_me_apitoken.notice.update_token1') }<br />
               { t('page_me_apitoken.notice.update_token1') }<br />
@@ -79,7 +79,7 @@ class ApiSettings extends React.Component {
           <div className="offset-4 col-5">
           <div className="offset-4 col-5">
             <button
             <button
               type="button"
               type="button"
-              className="btn btn-primary"
+              className="btn btn-primary text-nowrap"
               onClick={this.onClickSubmit}
               onClick={this.onClickSubmit}
             >
             >
               {t('Update API Token')}
               {t('Update API Token')}

+ 1 - 1
src/client/js/components/Me/AssociateModal.jsx

@@ -70,7 +70,7 @@ class AssociateModal extends React.Component {
     const { t } = this.props;
     const { t } = this.props;
 
 
     return (
     return (
-      <Modal isOpen={this.props.isOpen} toggle={this.props.onClose} className="mw-100 m-4">
+      <Modal isOpen={this.props.isOpen} toggle={this.props.onClose} size="lg">
         <ModalHeader className="bg-info" toggle={this.props.onClose}>
         <ModalHeader className="bg-info" toggle={this.props.onClose}>
           { t('Create External Account') }
           { t('Create External Account') }
         </ModalHeader>
         </ModalHeader>

+ 1 - 1
src/client/js/components/Me/BasicInfoSettings.jsx

@@ -71,7 +71,7 @@ class BasicInfoSettings extends React.Component {
           </div>
           </div>
           {registrationWhiteList.length !== 0 && (
           {registrationWhiteList.length !== 0 && (
             <div className="col-sm-offset-2 col-sm-10">
             <div className="col-sm-offset-2 col-sm-10">
-              <div className="help-block">
+              <div className="form-text text-muted">
                 {t('page_register.form_help.email')}
                 {t('page_register.form_help.email')}
                 <ul>
                 <ul>
                   {registrationWhiteList.map(data => <li key={data}><code>{data}</code></li>)}
                   {registrationWhiteList.map(data => <li key={data}><code>{data}</code></li>)}

+ 21 - 25
src/client/js/components/Me/ExternalAccountLinkedMe.jsx

@@ -67,7 +67,7 @@ class ExternalAccountLinkedMe extends React.Component {
 
 
     return (
     return (
       <Fragment>
       <Fragment>
-        <div className="container-fluid p-0 my-4">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">
           <h2 className="border-bottom">
             <button type="button" className="btn btn-outline-secondary btn-sm pull-right" onClick={this.openAssociateModal}>
             <button type="button" className="btn btn-outline-secondary btn-sm pull-right" onClick={this.openAssociateModal}>
               <i className="icon-plus" aria-hidden="true" />
               <i className="icon-plus" aria-hidden="true" />
@@ -77,31 +77,27 @@ class ExternalAccountLinkedMe extends React.Component {
           </h2>
           </h2>
         </div>
         </div>
 
 
-        <div className="row">
-          <div className="col-md-12">
-            <table className="table table-bordered table-user-list">
-              <thead>
-                <tr>
-                  <th width="120px">Authentication Provider</th>
-                  <th>
-                    <code>accountId</code>
-                  </th>
-                  <th width="200px">{ t('Created') }</th>
-                  <th width="150px">{ t('Admin') }</th>
-                </tr>
-              </thead>
-              <tbody>
-                {externalAccounts !== 0 && externalAccounts.map(account => (
-                  <ExternalAccountRow
-                    account={account}
-                    key={account._id}
-                    openDisassociateModal={this.openDisassociateModal}
-                  />
+        <table className="table table-bordered table-user-list">
+          <thead>
+            <tr>
+              <th width="120px">Authentication Provider</th>
+              <th>
+                <code>accountId</code>
+              </th>
+              <th width="200px">{ t('Created') }</th>
+              <th width="150px">{ t('Admin') }</th>
+            </tr>
+          </thead>
+          <tbody>
+            {externalAccounts !== 0 && externalAccounts.map(account => (
+              <ExternalAccountRow
+                account={account}
+                key={account._id}
+                openDisassociateModal={this.openDisassociateModal}
+              />
                 ))}
                 ))}
-              </tbody>
-            </table>
-          </div>
-        </div>
+          </tbody>
+        </table>
 
 
         <AssociateModal
         <AssociateModal
           isOpen={this.state.isAssociateModalOpen}
           isOpen={this.state.isAssociateModalOpen}

+ 19 - 17
src/client/js/components/Me/PasswordSettings.jsx

@@ -68,7 +68,7 @@ class PasswordSettings extends React.Component {
     return (
     return (
       <React.Fragment>
       <React.Fragment>
         {(!personalContainer.state.isPasswordSet) && <div className="alert alert-warning m-t-10">{ t('Password is not set') }</div>}
         {(!personalContainer.state.isPasswordSet) && <div className="alert alert-warning m-t-10">{ t('Password is not set') }</div>}
-        <div className="mb-5 container-fluid">
+        <div className="container-fluid my-4">
           {(personalContainer.state.isPasswordSet)
           {(personalContainer.state.isPasswordSet)
             ? <h2 className="border-bottom">{t('personal_settings.update_password')}</h2>
             ? <h2 className="border-bottom">{t('personal_settings.update_password')}</h2>
           : <h2 className="border-bottom">{t('personal_settings.set_new_password')}</h2>}
           : <h2 className="border-bottom">{t('personal_settings.set_new_password')}</h2>}
@@ -76,8 +76,8 @@ class PasswordSettings extends React.Component {
         {(personalContainer.state.isPasswordSet)
         {(personalContainer.state.isPasswordSet)
         && (
         && (
           <div className="row mb-3">
           <div className="row mb-3">
-            <label htmlFor="oldPassword" className="col-3 text-right">{ t('personal_settings.current_password') }</label>
-            <div className="col-6">
+            <label htmlFor="oldPassword" className="col-md-3 text-md-right">{ t('personal_settings.current_password') }</label>
+            <div className="col-md-5">
               <input
               <input
                 className="form-control"
                 className="form-control"
                 type="password"
                 type="password"
@@ -89,8 +89,8 @@ class PasswordSettings extends React.Component {
           </div>
           </div>
         )}
         )}
         <div className="row mb-3">
         <div className="row mb-3">
-          <label htmlFor="newPassword" className="col-3 text-right">{t('personal_settings.new_password') }</label>
-          <div className="col-6">
+          <label htmlFor="newPassword" className="col-md-3 text-md-right">{t('personal_settings.new_password') }</label>
+          <div className="col-md-5">
             <input
             <input
               className="form-control"
               className="form-control"
               type="password"
               type="password"
@@ -101,8 +101,8 @@ class PasswordSettings extends React.Component {
           </div>
           </div>
         </div>
         </div>
         <div className={`row mb-3 ${isIncorrectConfirmPassword && 'has-error'}`}>
         <div className={`row mb-3 ${isIncorrectConfirmPassword && 'has-error'}`}>
-          <label htmlFor="newPasswordConfirm" className="col-3 text-right">{t('personal_settings.new_password_confirm') }</label>
-          <div className="col-6">
+          <label htmlFor="newPasswordConfirm" className="col-md-3 text-md-right">{t('personal_settings.new_password_confirm') }</label>
+          <div className="col-md-5">
             <input
             <input
               className="form-control"
               className="form-control"
               type="password"
               type="password"
@@ -111,19 +111,21 @@ class PasswordSettings extends React.Component {
               onChange={(e) => { this.onChangeNewPasswordConfirm(e.target.value) }}
               onChange={(e) => { this.onChangeNewPasswordConfirm(e.target.value) }}
             />
             />
 
 
-            <p className="help-block">{t('page_register.form_help.password') }</p>
+            <p className="form-text text-muted">{t('page_register.form_help.password') }</p>
           </div>
           </div>
         </div>
         </div>
 
 
-        <div className="my-3 text-center">
-          <button
-            type="button"
-            className="btn btn-primary"
-            onClick={this.onClickSubmit}
-            disabled={this.state.retrieveError != null || isIncorrectConfirmPassword}
-          >
-            {t('Update')}
-          </button>
+        <div className="row my-3">
+          <div className="offset-5">
+            <button
+              type="button"
+              className="btn btn-primary"
+              onClick={this.onClickSubmit}
+              disabled={this.state.retrieveError != null || isIncorrectConfirmPassword}
+            >
+              {t('Update')}
+            </button>
+          </div>
         </div>
         </div>
       </React.Fragment>
       </React.Fragment>
     );
     );

+ 4 - 4
src/client/js/components/Me/UserSettings.jsx

@@ -13,15 +13,15 @@ class UserSettings extends React.Component {
 
 
     return (
     return (
       <Fragment>
       <Fragment>
-        <div className="mb-5 container-fluid">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">{t('Basic Info')}</h2>
           <h2 className="border-bottom">{t('Basic Info')}</h2>
-          <BasicInfoSettings />
         </div>
         </div>
+        <BasicInfoSettings />
 
 
-        <div className="mb-5 container-fluid">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">{t('Set Profile Image')}</h2>
           <h2 className="border-bottom">{t('Set Profile Image')}</h2>
-          <ProfileImageSettings />
         </div>
         </div>
+        <ProfileImageSettings />
 
 
       </Fragment>
       </Fragment>
     );
     );

+ 54 - 32
src/client/js/components/Navbar/GrowiSubNavigation.jsx

@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
-import { isTrashPage } from '../../../../lib/util/path-utils';
+import { isTrashPage } from '@commons/util/path-utils';
+
 import { createSubscribedElement } from '../UnstatedUtils';
 import { createSubscribedElement } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 import AppContainer from '../../services/AppContainer';
 import RevisionPath from '../Page/RevisionPath';
 import RevisionPath from '../Page/RevisionPath';
@@ -15,56 +16,77 @@ import PageCreator from './PageCreator';
 import RevisionAuthor from './RevisionAuthor';
 import RevisionAuthor from './RevisionAuthor';
 
 
 const GrowiSubNavigation = (props) => {
 const GrowiSubNavigation = (props) => {
-  const isPageForbidden = document.querySelector('#grw-subnav').getAttribute('data-is-forbidden-page');
+  const isPageForbidden = document.querySelector('#grw-subnav').getAttribute('data-is-forbidden-page') === 'true';
   const { appContainer, pageContainer } = props;
   const { appContainer, pageContainer } = props;
   const {
   const {
-    pageId, path, createdAt, creator, updatedAt, revisionAuthor, isCompactMode,
+    pageId, path, createdAt, creator, updatedAt, revisionAuthor, isHeaderSticky, isSubnavCompact,
   } = pageContainer.state;
   } = pageContainer.state;
-  const compactClassName = isCompactMode ? 'grw-compact-subnavbar' : null;
 
 
-  // Display only the RevisionPath if the page is trash or forbidden
-  if (isTrashPage(path) || isPageForbidden) {
+  const isPageNotFound = pageId == null;
+  const isPageInTrash = isTrashPage(path);
+
+  // Display only the RevisionPath
+  if (isPageNotFound || isPageForbidden || isPageInTrash) {
     return (
     return (
-      <div className="d-flex align-items-center">
-        <div className="title-container mr-auto">
-          <h1>
-            <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
-          </h1>
-        </div>
+      <div className="d-flex align-items-center px-3 py-3 grw-subnavbar">
+        <h1 className="m-0">
+          <RevisionPath
+            behaviorType={appContainer.config.behaviorType}
+            pageId={pageId}
+            pagePath={pageContainer.state.path}
+            isPageNotFound={isPageNotFound}
+            isPageForbidden={isPageForbidden}
+            isPageInTrash={isPageInTrash}
+          />
+        </h1>
       </div>
       </div>
     );
     );
   }
   }
 
 
+  const additionalClassNames = ['grw-subnavbar'];
+  if (isHeaderSticky) {
+    additionalClassNames.push('grw-subnavbar-sticky');
+  }
+  if (isSubnavCompact) {
+    additionalClassNames.push('grw-subnavbar-compact');
+  }
+
   return (
   return (
-    <div className={`row px-3 py-1 align-items-center ${compactClassName}`}>
+    <div className={`d-flex align-items-center justify-content-between px-3 py-1 ${additionalClassNames.join(' ')}`}>
 
 
       {/* Page Path */}
       {/* Page Path */}
-      <div className="title-container mr-auto">
-        <h1>
+      <div>
+        <h1 className="m-0">
           <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
           <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
         </h1>
         </h1>
-        <TagLabels />
+        { !isPageNotFound && !isPageForbidden && (
+          <TagLabels />
+        ) }
       </div>
       </div>
 
 
-      {/* Header Button */}
-      <div className="mr-2">
-        <LikeButton pageId={pageId} isLiked={pageContainer.state.isLiked} />
-      </div>
-      <div>
-        <BookmarkButton pageId={pageId} crowi={appContainer} />
-      </div>
+      <div className="d-flex align-items-center">
+        {/* Header Button */}
+        <div className="mr-2">
+          <LikeButton pageId={pageId} isLiked={pageContainer.state.isLiked} />
+        </div>
+        <div>
+          <BookmarkButton pageId={pageId} crowi={appContainer} />
+        </div>
 
 
-      {/* Page Authors */}
-      <ul className="authors text-nowrap d-none d-lg-block">
-        {creator != null && <li><PageCreator creator={creator} createdAt={createdAt} isCompactMode={isCompactMode} /></li>}
-        { revisionAuthor != null
-          && (
+        {/* Page Authors */}
+        <ul className="authors text-nowrap d-none d-lg-block">
+          { creator != null && (
+            <li>
+              <PageCreator creator={creator} createdAt={createdAt} isCompactMode={isSubnavCompact} />
+            </li>
+          ) }
+          { revisionAuthor != null && (
             <li className="mt-1">
             <li className="mt-1">
-              <RevisionAuthor revisionAuthor={revisionAuthor} updatedAt={updatedAt} isCompactMode={isCompactMode} />
+              <RevisionAuthor revisionAuthor={revisionAuthor} updatedAt={updatedAt} isCompactMode={isSubnavCompact} />
             </li>
             </li>
-          )
-        }
-      </ul>
+          ) }
+        </ul>
+      </div>
 
 
     </div>
     </div>
   );
   );

+ 37 - 46
src/client/js/components/Navbar/GrowiSubNavigationForUserPage.jsx

@@ -1,8 +1,7 @@
-import React, { useState, useEffect } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
-import { throttle } from 'throttle-debounce';
 
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import { createSubscribedElement } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 import AppContainer from '../../services/AppContainer';
@@ -14,55 +13,47 @@ import UserPicture from '../User/UserPicture';
 const GrowiSubNavigationForUserPage = (props) => {
 const GrowiSubNavigationForUserPage = (props) => {
   const pageUser = JSON.parse(document.querySelector('#grw-subnav-for-user-page').getAttribute('data-page-user'));
   const pageUser = JSON.parse(document.querySelector('#grw-subnav-for-user-page').getAttribute('data-page-user'));
   const { appContainer, pageContainer } = props;
   const { appContainer, pageContainer } = props;
-  const { pageId } = pageContainer.state;
-  const [isCompactMode, setIsCompactMode] = useState(false);
-  const scrollAmountForFixed = 50;
-  const layoutType = appContainer.getConfig().layoutType;
-
-  useEffect(() => {
-    window.addEventListener('scroll', throttle(300, () => {
-      setIsCompactMode(window.pageYOffset > scrollAmountForFixed);
-    }));
-  }, []);
+  const { pageId, isHeaderSticky, isSubnavCompact } = pageContainer.state;
+
+  const additionalClassNames = ['grw-subnavbar', 'grw-subnavbar-user-page'];
+  if (isHeaderSticky) {
+    additionalClassNames.push('grw-subnavbar-sticky');
+  }
+  if (isSubnavCompact) {
+    additionalClassNames.push('py-2 grw-subnavbar-compact');
+  }
+  else {
+    additionalClassNames.push('py-3');
+  }
 
 
   return (
   return (
-    <div className={`row px-3 py-1 ${(isCompactMode && layoutType === 'growi') && 'grw-compact-subnavbar'}`}>
-
-      <div className="col-12">
-        {/* Page Path */}
-        <h4>
-          <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
-        </h4>
-
-        <div className="d-flex">
-          <div className="users-info d-flex align-items-center mr-auto">
-            <UserPicture user={pageUser} />
-
-            <div className="users-meta">
-              <div className="d-flex align-items-center">
-                <h1>
-                  {pageUser.name}
-                </h1>
-              </div>
-              <div className="user-page-meta">
-                <ul>
-                  <li className="user-page-username"><i className="icon-user mr-1"></i>{pageUser.username}</li>
-                  <li className="user-page-email">
-                    <i className="icon-envelope mr-1"></i>
-                    {pageUser.isEmailPublished ? pageUser.email : '*****'}
-                  </li>
-                  {pageUser.introduction && <li className="user-page-introduction"><p>{pageUser.introduction}</p></li>}
-                </ul>
-              </div>
-            </div>
+    <div className={`px-3 ${additionalClassNames.join(' ')}`}>
+      <h4 className="grw-user-page-path">
+        <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
+      </h4>
+
+      <div className="d-flex align-items-center justify-content-between">
+
+        <div className="users-info d-flex align-items-center">
+          <UserPicture user={pageUser} />
+
+          <div className="users-meta">
+            <h1>
+              {pageUser.name}
+            </h1>
+            <ul className="user-page-meta mt-1 mb-0">
+              <li className="user-page-username"><i className="icon-user mr-1"></i>{pageUser.username}</li>
+              <li className="user-page-email">
+                <i className="icon-envelope mr-1"></i>
+                {pageUser.isEmailPublished ? pageUser.email : '*****'}
+              </li>
+              {pageUser.introduction && <li className="user-page-introduction"><p>{pageUser.introduction}</p></li>}
+            </ul>
           </div>
           </div>
-
-          {/* Header Button */}
-          <BookmarkButton pageId={pageId} crowi={appContainer} size="lg" />
         </div>
         </div>
-      </div>
-
 
 
+        <BookmarkButton pageId={pageId} crowi={appContainer} size="lg" />
+      </div>
     </div>
     </div>
   );
   );
 
 

+ 35 - 0
src/client/js/components/Navbar/NavbarToggler.jsx

@@ -0,0 +1,35 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../UnstatedUtils';
+import AppContainer from '../../services/AppContainer';
+
+const NavbarToggler = (props) => {
+
+  // eslint-disable-next-line no-unused-vars
+  const { appContainer } = props;
+
+  return (
+    <button className="navbar-toggler grw-navbar-toggler border-0" type="button" aria-expanded="false" aria-label="Toggle navigation">
+      <i className="icon-menu"></i>
+    </button>
+  );
+
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const NavbarTogglerWrapper = (props) => {
+  return createSubscribedElement(NavbarToggler, props, [AppContainer]);
+};
+
+
+NavbarToggler.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+};
+
+export default withTranslation()(NavbarTogglerWrapper);

+ 1 - 1
src/client/js/components/Navbar/PersonalDropdown.jsx

@@ -59,7 +59,7 @@ const PersonalDropdown = (props) => {
       {/* 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="nav-link waves-effect waves-light" data-toggle="dropdown">
       <a className="nav-link waves-effect waves-light" data-toggle="dropdown">
-        <UserPicture user={user} withoutLink />&nbsp;{user.name}
+        <UserPicture user={user} withoutLink /><span className="d-none d-sm-inline-block">&nbsp;{user.name}</span>
       </a>
       </a>
 
 
       {/* Menu */}
       {/* Menu */}

+ 16 - 12
src/client/js/components/Page/RevisionPath.jsx

@@ -16,7 +16,6 @@ class RevisionPath extends React.Component {
       pages: [],
       pages: [],
       isListPage: false,
       isListPage: false,
       isLinkToListPage: true,
       isLinkToListPage: true,
-      isInTrash: false,
     };
     };
 
 
     // retrieve xss library from window
     // retrieve xss library from window
@@ -60,12 +59,6 @@ class RevisionPath extends React.Component {
     const pages = [];
     const pages = [];
     const pagePaths = [];
     const pagePaths = [];
     splitted.forEach((pageName) => {
     splitted.forEach((pageName) => {
-      // skip trash
-      if (pageName === 'trash' && splitted.length > 1) {
-        this.setState({ isInTrash: true });
-        return;
-      }
-
       pagePaths.push(encodeURIComponent(pageName));
       pagePaths.push(encodeURIComponent(pageName));
       pages.push({
       pages.push({
         pagePath: urljoin('/', ...pagePaths),
         pagePath: urljoin('/', ...pagePaths),
@@ -109,10 +102,10 @@ class RevisionPath extends React.Component {
       padding: '0 2px',
       padding: '0 2px',
     };
     };
 
 
-    const { isInTrash } = this.state;
+    const { isPageInTrash, isPageForbidden } = this.props;
     const pageLength = this.state.pages.length;
     const pageLength = this.state.pages.length;
 
 
-    const rootElement = isInTrash
+    const rootElement = isPageInTrash
       ? (
       ? (
         <>
         <>
           <span className="path-segment">
           <span className="path-segment">
@@ -159,9 +152,11 @@ class RevisionPath extends React.Component {
 
 
         <CopyDropdown t={this.props.t} pagePath={this.props.pagePath} pageId={this.props.pageId} buttonStyle={buttonStyle}></CopyDropdown>
         <CopyDropdown t={this.props.t} pagePath={this.props.pagePath} pageId={this.props.pageId} buttonStyle={buttonStyle}></CopyDropdown>
 
 
-        <a href="#edit" className="d-block btn btn-outline-secondary btn-edit text-muted" style={buttonStyle}>
-          <i className="icon-note" />
-        </a>
+        { !isPageInTrash && !isPageForbidden && (
+          <a href="#edit" className="d-block text-muted btn btn-secondary bg-transparent btn-edit border-0" style={buttonStyle}>
+            <i className="icon-note" />
+          </a>
+        ) }
       </span>
       </span>
     );
     );
   }
   }
@@ -173,6 +168,15 @@ RevisionPath.propTypes = {
   behaviorType: PropTypes.string.isRequired,
   behaviorType: PropTypes.string.isRequired,
   pagePath: PropTypes.string.isRequired,
   pagePath: PropTypes.string.isRequired,
   pageId: PropTypes.string,
   pageId: PropTypes.string,
+  isPageNotFound: PropTypes.bool,
+  isPageForbidden: PropTypes.bool,
+  isPageInTrash: PropTypes.bool,
+};
+
+RevisionPath.defaultProps = {
+  isPageNotFound: false,
+  isPageForbidden: false,
+  isPageInTrash: false,
 };
 };
 
 
 export default withTranslation()(RevisionPath);
 export default withTranslation()(RevisionPath);

+ 1 - 1
src/client/js/components/Page/TagEditor.jsx

@@ -46,7 +46,7 @@ export default class TagEditor extends React.Component {
 
 
   render() {
   render() {
     return (
     return (
-      <Modal isOpen={this.state.isOpenModal} toggle={this.closeModalHandler} id="editTagModal">
+      <Modal isOpen={this.state.isOpenModal} toggle={this.closeModalHandler} id="edit-tag-modal">
         <ModalHeader tag="h4" toggle={this.closeModalHandler} className="bg-primary">
         <ModalHeader tag="h4" toggle={this.closeModalHandler} className="bg-primary">
           <span className="text-white">Edit Tags</span>
           <span className="text-white">Edit Tags</span>
         </ModalHeader>
         </ModalHeader>

+ 1 - 1
src/client/js/components/Page/TagLabels.jsx

@@ -108,7 +108,7 @@ class TagLabels extends React.Component {
     });
     });
 
 
     return (
     return (
-      <div className={`tag-viewer ${pageId ? 'existed-page' : 'new-page'}`}>
+      <div className="tag-labels">
         {tags.length === 0 && (
         {tags.length === 0 && (
           <a className="btn btn-link btn-edit-tags no-tags p-0 text-muted" onClick={this.showEditor}>
           <a className="btn btn-link btn-edit-tags no-tags p-0 text-muted" onClick={this.showEditor}>
             { t('Add tags for this page') } <i className="manage-tags ml-2 icon-plus"></i>
             { t('Add tags for this page') } <i className="manage-tags ml-2 icon-plus"></i>

+ 5 - 10
src/client/js/components/PageComment/CommentEditor.jsx

@@ -219,9 +219,6 @@ class CommentEditor extends React.Component {
     const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : null;
     const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : null;
     const emojiStrategy = appContainer.getEmojiStrategy();
     const emojiStrategy = appContainer.getEmojiStrategy();
 
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     const errorMessage = <span className="text-danger text-right mr-2">{this.state.errorMessage}</span>;
     const errorMessage = <span className="text-danger text-right mr-2">{this.state.errorMessage}</span>;
     const cancelButton = (
     const cancelButton = (
       <Button outline color="danger" size="xs" className="btn btn-outline-danger rounded-pill" onClick={this.toggleEditor}>
       <Button outline color="danger" size="xs" className="btn btn-outline-danger rounded-pill" onClick={this.toggleEditor}>
@@ -242,11 +239,9 @@ class CommentEditor extends React.Component {
     return (
     return (
       <div className="form page-comment-form">
       <div className="form page-comment-form">
         <div className="comment-form">
         <div className="comment-form">
-          { isBaloonStyle && (
-            <div className="comment-form-user">
-              <UserPicture user={appContainer.currentUser} />
-            </div>
-          ) }
+          <div className="comment-form-user">
+            <UserPicture user={appContainer.currentUser} />
+          </div>
           <div className="comment-form-main">
           <div className="comment-form-main">
             <div className="comment-write">
             <div className="comment-write">
               <Nav tabs>
               <Nav tabs>
@@ -271,7 +266,7 @@ class CommentEditor extends React.Component {
                     isGfmMode={this.state.isMarkdown}
                     isGfmMode={this.state.isMarkdown}
                     lineNumbers={false}
                     lineNumbers={false}
                     isMobile={appContainer.isMobile}
                     isMobile={appContainer.isMobile}
-                    isUploadable={this.state.isUploadable && layoutType !== 'crowi'} // disabled upload with crowi layout
+                    isUploadable={this.state.isUploadable}
                     isUploadableFile={this.state.isUploadableFile}
                     isUploadableFile={this.state.isUploadableFile}
                     emojiStrategy={emojiStrategy}
                     emojiStrategy={emojiStrategy}
                     onChange={this.updateState}
                     onChange={this.updateState}
@@ -289,7 +284,7 @@ class CommentEditor extends React.Component {
             <div className="comment-submit">
             <div className="comment-submit">
               <div className="d-flex">
               <div className="d-flex">
                 <label className="mr-2">
                 <label className="mr-2">
-                  { isBaloonStyle && activeTab === 1 && (
+                  {activeTab === 1 && (
                     <span className="custom-control custom-checkbox">
                     <span className="custom-control custom-checkbox">
                       <input
                       <input
                         type="checkbox"
                         type="checkbox"

+ 4 - 9
src/client/js/components/PageComment/CommentEditorLazyRenderer.jsx

@@ -30,9 +30,6 @@ class CommentEditorLazyRenderer extends React.Component {
     const user = appContainer.currentUser;
     const user = appContainer.currentUser;
     const isLoggedIn = user != null;
     const isLoggedIn = user != null;
 
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     if (!isLoggedIn) {
     if (!isLoggedIn) {
       return <React.Fragment></React.Fragment>;
       return <React.Fragment></React.Fragment>;
     }
     }
@@ -43,16 +40,14 @@ class CommentEditorLazyRenderer extends React.Component {
         { !this.state.isEditorShown && (
         { !this.state.isEditorShown && (
           <div className="form page-comment-form">
           <div className="form page-comment-form">
             <div className="comment-form">
             <div className="comment-form">
-              { isBaloonStyle && (
-                <div className="comment-form-user">
-                  <UserPicture user={user} />
-                </div>
-              ) }
+              <div className="comment-form-user">
+                <UserPicture user={user} />
+              </div>
               <div className="comment-form-main">
               <div className="comment-form-main">
                 { !this.state.isEditorShown && (
                 { !this.state.isEditorShown && (
                   <button
                   <button
                     type="button"
                     type="button"
-                    className={`btn btn-lg ${isBaloonStyle ? 'btn-link' : 'btn-primary'} center-block`}
+                    className="btn btn-lg btn-link center-block"
                     onClick={this.showCommentFormBtnClickHandler}
                     onClick={this.showCommentFormBtnClickHandler}
                   >
                   >
                     <i className="icon-bubble"></i> Add Comment
                     <i className="icon-bubble"></i> Add Comment

+ 1 - 8
src/client/js/components/PageComment/ReplayComments.jsx

@@ -40,15 +40,8 @@ class ReplayComments extends React.PureComponent {
 
 
   render() {
   render() {
 
 
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
     const isAllReplyShown = this.props.appContainer.getConfig().isAllReplyShown || false;
     const isAllReplyShown = this.props.appContainer.getConfig().isAllReplyShown || false;
-
-    let replyList = this.props.replyList;
-    if (!isBaloonStyle) {
-      replyList = replyList.slice().reverse();
-    }
+    const replyList = this.props.replyList;
 
 
     if (isAllReplyShown) {
     if (isAllReplyShown) {
       return (
       return (

+ 1 - 9
src/client/js/components/PageComments.jsx

@@ -180,15 +180,7 @@ class PageComments extends React.Component {
   render() {
   render() {
     const topLevelComments = [];
     const topLevelComments = [];
     const allReplies = [];
     const allReplies = [];
-
-    const layoutType = this.props.appContainer.getConfig().layoutType;
-    const isBaloonStyle = layoutType.match(/crowi-plus|growi|kibela/);
-
-    let comments = this.props.commentContainer.state.comments;
-    if (isBaloonStyle) {
-      // replace with asc order array
-      comments = comments.slice().reverse(); // non-destructive reverse
-    }
+    const comments = this.props.commentContainer.state.comments;
 
 
     comments.forEach((comment) => {
     comments.forEach((comment) => {
       if (comment.replyTo === undefined) {
       if (comment.replyTo === undefined) {

+ 3 - 3
src/client/js/components/PageEditor/CodeMirrorEditor.jsx

@@ -552,7 +552,7 @@ export default class CodeMirrorEditor extends AbstractEditor {
 
 
   renderCheatsheetModalButton() {
   renderCheatsheetModalButton() {
     return (
     return (
-      <button type="button" className="btn-link gfm-cheatsheet-modal-link small p-0" onClick={() => { this.markdownHelpButtonClickedHandler() }}>
+      <button type="button" className="btn-link gfm-cheatsheet-modal-link small" onClick={() => { this.markdownHelpButtonClickedHandler() }}>
         <i className="icon-question" /> Markdown
         <i className="icon-question" /> Markdown
       </button>
       </button>
     );
     );
@@ -567,13 +567,13 @@ export default class CodeMirrorEditor extends AbstractEditor {
           ? (
           ? (
             <div className="text-right">
             <div className="text-right">
               {cheatsheetModalButton}
               {cheatsheetModalButton}
-              <div className="mt-2">
+              <div className="mb-2">
                 <SimpleCheatsheet />
                 <SimpleCheatsheet />
               </div>
               </div>
             </div>
             </div>
           )
           )
           : (
           : (
-            <div className="mr-4">
+            <div className="mr-4 mb-2">
               {cheatsheetModalButton}
               {cheatsheetModalButton}
             </div>
             </div>
           )
           )

+ 1 - 1
src/client/js/components/PageEditor/DrawioModal.jsx

@@ -134,7 +134,7 @@ class DrawioModal extends React.PureComponent {
 
 
   render() {
   render() {
     return (
     return (
-      <Modal isOpen={this.state.show} toggle={this.cancel} className="drawio-modal" bssize="large" keyboard={false}>
+      <Modal isOpen={this.state.show} toggle={this.cancel} className="drawio-modal" size="xl" keyboard={false}>
         <ModalBody className="p-0">
         <ModalBody className="p-0">
           {/* Loading spinner */}
           {/* Loading spinner */}
           <div className="w-100 h-100 position-absolute d-flex">
           <div className="w-100 h-100 position-absolute d-flex">

+ 17 - 12
src/client/js/components/PageEditor/HandsontableModal.jsx

@@ -2,7 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import {
 import {
-  Button, ButtonGroup,
   Collapse,
   Collapse,
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
@@ -437,15 +436,21 @@ export default class HandsontableModal extends React.PureComponent {
         <ModalHeader tag="h4" toggle={this.cancel} close={buttons}>Edit Table</ModalHeader>
         <ModalHeader tag="h4" toggle={this.cancel} close={buttons}>Edit Table</ModalHeader>
         <ModalBody className="p-0 d-flex flex-column">
         <ModalBody className="p-0 d-flex flex-column">
           <div className="px-4 py-3 modal-navbar bg-light">
           <div className="px-4 py-3 modal-navbar bg-light">
-            <Button className="mr-4 data-import-button bg-light" onClick={this.toggleDataImportArea}>
+            <button
+              type="button"
+              className="mr-4 data-import-button btn btn-secondary"
+              data-toggle="collapse"
+              data-target="#collapseDataImport"
+              aria-expanded={this.state.isDataImportAreaExpanded}
+              onClick={this.toggleDataImportArea}
+            >
               <span className="mr-3">Data Import</span><i className={this.state.isDataImportAreaExpanded ? 'fa fa-angle-up' : 'fa fa-angle-down'}></i>
               <span className="mr-3">Data Import</span><i className={this.state.isDataImportAreaExpanded ? 'fa fa-angle-up' : 'fa fa-angle-down'}></i>
-
-            </Button>
-            <ButtonGroup>
-              <Button onClick={() => { this.alignButtonHandler('l') }}><i className="ti-align-left"></i></Button>
-              <Button onClick={() => { this.alignButtonHandler('c') }}><i className="ti-align-center"></i></Button>
-              <Button onClick={() => { this.alignButtonHandler('r') }}><i className="ti-align-right"></i></Button>
-            </ButtonGroup>
+            </button>
+            <div role="group" className="btn-group">
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('l') }}><i className="ti-align-left"></i></button>
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('c') }}><i className="ti-align-center"></i></button>
+              <button type="button" className="btn btn-secondary" onClick={() => { this.alignButtonHandler('r') }}><i className="ti-align-right"></i></button>
+            </div>
             <Collapse isOpen={this.state.isDataImportAreaExpanded}>
             <Collapse isOpen={this.state.isDataImportAreaExpanded}>
               <div className="mt-4">
               <div className="mt-4">
                 <MarkdownTableDataImportForm onCancel={this.toggleDataImportArea} onImport={this.importData} />
                 <MarkdownTableDataImportForm onCancel={this.toggleDataImportArea} onImport={this.importData} />
@@ -468,10 +473,10 @@ export default class HandsontableModal extends React.PureComponent {
           </div>
           </div>
         </ModalBody>
         </ModalBody>
         <ModalFooter className="grw-modal-footer">
         <ModalFooter className="grw-modal-footer">
-          <Button color="danger" onClick={this.reset}>Reset</Button>
+          <button type="button" className="btn btn-danger" onClick={this.reset}>Reset</button>
           <div className="ml-auto">
           <div className="ml-auto">
-            <Button className="mr-2" color="secondary" onClick={this.cancel}>Cancel</Button>
-            <Button color="primary" onClick={this.save}>Done</Button>
+            <button type="button" className="mr-2 btn btn-secondary" onClick={this.cancel}>Cancel</button>
+            <button type="button" className="btn btn-primary" onClick={this.save}>Done</button>
           </div>
           </div>
         </ModalFooter>
         </ModalFooter>
       </Modal>
       </Modal>

+ 2 - 2
src/client/js/components/PageEditor/OptionsSelector.jsx

@@ -110,7 +110,7 @@ class OptionsSelector extends React.Component {
 
 
     return (
     return (
       <div className="my-0 form-group">
       <div className="my-0 form-group">
-        <label>Theme:</label>
+        <label className="mr-2">Theme:</label>
         <div className="btn-group btn-group-sm dropup">
         <div className="btn-group btn-group-sm dropup">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
             {selectedTheme}
             {selectedTheme}
@@ -137,7 +137,7 @@ class OptionsSelector extends React.Component {
 
 
     return (
     return (
       <div className="my-0 form-group">
       <div className="my-0 form-group">
-        <label>Keymap:</label>
+        <label className="mr-2">Keymap:</label>
         <div className="btn-group btn-group-sm dropup">
         <div className="btn-group btn-group-sm dropup">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
           <button className="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
             {selectedKeymapMode}
             {selectedKeymapMode}

+ 1 - 1
src/client/js/components/PageHistory/RevisionDiff.jsx

@@ -39,7 +39,7 @@ export default class RevisionDiff extends React.Component {
 
 
     const diffView = { __html: diffViewHTML };
     const diffView = { __html: diffViewHTML };
     // eslint-disable-next-line react/no-danger
     // eslint-disable-next-line react/no-danger
-    return <div className="revision-history-diff" dangerouslySetInnerHTML={diffView} />;
+    return <div className="revision-history-diff d-table w-100" dangerouslySetInnerHTML={diffView} />;
   }
   }
 
 
 }
 }

+ 1 - 4
src/client/js/components/Sidebar.jsx

@@ -15,7 +15,6 @@ import Drawer from '@atlaskit/drawer';
 import { createSubscribedElement } from './UnstatedUtils';
 import { createSubscribedElement } from './UnstatedUtils';
 import AppContainer from '../services/AppContainer';
 import AppContainer from '../services/AppContainer';
 
 
-import GrowiLogo from './GrowiLogo';
 import SidebarNav from './Sidebar/SidebarNav';
 import SidebarNav from './Sidebar/SidebarNav';
 import History from './Sidebar/History';
 import History from './Sidebar/History';
 import CustomSidebar from './Sidebar/CustomSidebar';
 import CustomSidebar from './Sidebar/CustomSidebar';
@@ -56,9 +55,6 @@ class Sidebar extends React.Component {
 
 
   renderGlobalNavigation = () => (
   renderGlobalNavigation = () => (
     <>
     <>
-      <div className="grw-logo">
-        <a href="/"><GrowiLogo /></a>
-      </div>
       <SidebarNav currentContentsId={this.state.currentContentsId} onItemSelected={this.itemSelectedHandler} />
       <SidebarNav currentContentsId={this.state.currentContentsId} onItemSelected={this.itemSelectedHandler} />
       <Drawer onClose={this.closeDrawer} isOpen={this.state.isDrawerOpen} width="wide">
       <Drawer onClose={this.closeDrawer} isOpen={this.state.isDrawerOpen} width="wide">
         <code>Drawer contents</code>
         <code>Drawer contents</code>
@@ -99,6 +95,7 @@ class Sidebar extends React.Component {
           // experimental_fullWidthFlyout
           // experimental_fullWidthFlyout
           shouldHideGlobalNavShadow
           shouldHideGlobalNavShadow
           showContextualNavigation
           showContextualNavigation
+          topOffset={50}
         >
         >
         </LayoutManager>
         </LayoutManager>
       </ThemeProvider>
       </ThemeProvider>

+ 9 - 6
src/client/js/components/Sidebar/CustomSidebar.jsx

@@ -3,12 +3,9 @@ import React from 'react';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
-import { JiraWordmark } from '@atlaskit/logo';
-
 import {
 import {
   HeaderSection,
   HeaderSection,
   MenuSection,
   MenuSection,
-  Wordmark,
 } from '@atlaskit/navigation-next';
 } from '@atlaskit/navigation-next';
 
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import { createSubscribedElement } from '../UnstatedUtils';
@@ -22,19 +19,25 @@ class CustomSidebar extends React.Component {
   state = {
   state = {
   };
   };
 
 
+  renderHeaderWordmark() {
+    return <h3>Custom Sidebar</h3>;
+  }
+
   render() {
   render() {
     return (
     return (
       <>
       <>
         <HeaderSection>
         <HeaderSection>
           { () => (
           { () => (
-            <div className="grw-product-nav-header">
-              <Wordmark wordmark={JiraWordmark} />
+            <div className="grw-sidebar-header-container">
+              {this.renderHeaderWordmark()}
             </div>
             </div>
           ) }
           ) }
         </HeaderSection>
         </HeaderSection>
         <MenuSection>
         <MenuSection>
           { () => (
           { () => (
-            <span>(TBD) CustomSidebar Contents</span>
+            <div className="grw-sidebar-content-container">
+              <span>(TBD) CustomSidebar Contents</span>
+            </div>
           ) }
           ) }
         </MenuSection>
         </MenuSection>
       </>
       </>

+ 9 - 6
src/client/js/components/Sidebar/History.jsx

@@ -3,12 +3,9 @@ import React from 'react';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
-import { JiraWordmark } from '@atlaskit/logo';
-
 import {
 import {
   HeaderSection,
   HeaderSection,
   MenuSection,
   MenuSection,
-  Wordmark,
 } from '@atlaskit/navigation-next';
 } from '@atlaskit/navigation-next';
 
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import { createSubscribedElement } from '../UnstatedUtils';
@@ -22,19 +19,25 @@ class History extends React.Component {
   state = {
   state = {
   };
   };
 
 
+  renderHeaderWordmark() {
+    return <h3>History</h3>;
+  }
+
   render() {
   render() {
     return (
     return (
       <>
       <>
         <HeaderSection>
         <HeaderSection>
           { () => (
           { () => (
-            <div className="grw-product-nav-header">
-              <Wordmark wordmark={JiraWordmark} />
+            <div className="grw-sidebar-header-container">
+              {this.renderHeaderWordmark()}
             </div>
             </div>
           ) }
           ) }
         </HeaderSection>
         </HeaderSection>
         <MenuSection>
         <MenuSection>
           { () => (
           { () => (
-            <span>(TBD) History Contents</span>
+            <div className="grw-sidebar-content-container">
+              <span>(TBD) History Contents</span>
+            </div>
           ) }
           ) }
         </MenuSection>
         </MenuSection>
       </>
       </>

+ 40 - 11
src/client/js/components/Sidebar/SidebarNav.jsx

@@ -3,11 +3,9 @@ import PropTypes from 'prop-types';
 
 
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 
 
-import EditIcon from '@atlaskit/icon/glyph/edit';
-import TrayIcon from '@atlaskit/icon/glyph/tray';
-
 import {
 import {
   GlobalNav,
   GlobalNav,
+  GlobalItem,
 } from '@atlaskit/navigation-next';
 } from '@atlaskit/navigation-next';
 
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import { createSubscribedElement } from '../UnstatedUtils';
@@ -31,24 +29,55 @@ class SidebarNav extends React.Component {
     }
     }
   }
   }
 
 
-  generateSidebarItemObj(id, icon, label) {
+  generatePrimaryItemObj(id, label, icon) {
+    const isSelected = this.props.currentContentsId === id;
+
+    return {
+      id,
+      component: ({ className }) => (
+        <div className={`${className} grw-global-item-container ${isSelected ? 'active' : ''}`}>
+          <GlobalItem
+            icon={icon}
+            label={label}
+            isSelected={isSelected}
+            onClick={() => this.itemSelectedHandler(id)}
+          />
+        </div>
+      ),
+    };
+  }
+
+  generateSecondaryItemObj(id, label, icon, href) {
     return {
     return {
       id,
       id,
-      icon,
-      label,
-      isSelected: this.props.currentContentsId === id,
-      onClick: () => this.itemSelectedHandler(id),
+      component: ({ className }) => (
+        <div className={`${className} grw-global-item-container d-block d-md-none`}>
+          <a href={href}>
+            <GlobalItem
+              icon={icon}
+              label={label}
+            />
+          </a>
+        </div>
+      ),
     };
     };
   }
   }
 
 
+  generateIconFactory(classNames) {
+    return () => <i className={classNames}></i>;
+  }
+
   render() {
   render() {
     return (
     return (
       <GlobalNav
       <GlobalNav
         primaryItems={[
         primaryItems={[
-          this.generateSidebarItemObj('custom', EditIcon, 'Custom Sidebar'),
-          this.generateSidebarItemObj('history', TrayIcon, 'History'),
+          this.generatePrimaryItemObj('custom', 'Custom Sidebar', this.generateIconFactory('fa fa-code')),
+          this.generatePrimaryItemObj('history', 'History', this.generateIconFactory('icon-clock')),
+        ]}
+        secondaryItems={[
+          this.generateSecondaryItemObj('admin', 'Admin', this.generateIconFactory('icon-settings'), '/admin'),
+          this.generateSecondaryItemObj('help', 'Help', this.generateIconFactory('icon-question'), 'https://docs.growi.org'),
         ]}
         ]}
-        secondaryItems={[]}
       />
       />
     );
     );
   }
   }

+ 0 - 5
src/client/js/components/TableOfContents.jsx

@@ -60,11 +60,6 @@ class TableOfContents extends React.Component {
   }
   }
 
 
   init() {
   init() {
-    const { layoutType } = this.props.appContainer.config;
-    if (layoutType === 'crowi') {
-      return;
-    }
-
     /*
     /*
      * set event listener
      * set event listener
      */
      */

+ 4 - 1
src/client/js/services/AdminUsersContainer.js

@@ -183,7 +183,10 @@ export default class AdminUsersContainer extends Container {
    * @memberOf AdminUsersContainer
    * @memberOf AdminUsersContainer
    */
    */
   async hidePasswordResetModal() {
   async hidePasswordResetModal() {
-    await this.setState({ isPasswordResetModalShown: false });
+    await this.setState({
+      isPasswordResetModalShown: false,
+      userForPasswordResetModal: null,
+    });
   }
   }
 
 
   /**
   /**

+ 19 - 7
src/client/js/services/PageContainer.js

@@ -5,10 +5,10 @@ import loggerFactory from '@alias/logger';
 import * as entities from 'entities';
 import * as entities from 'entities';
 import * as toastr from 'toastr';
 import * as toastr from 'toastr';
 
 
-import { throttle } from 'throttle-debounce';
-
 const logger = loggerFactory('growi:services:PageContainer');
 const logger = loggerFactory('growi:services:PageContainer');
-const scrollAmountForFixed = 50;
+const scrollThresForSticky = 0;
+const scrollThresForCompact = 30;
+const scrollThresForThrottling = 100;
 
 
 /**
 /**
  * Service container related to Page
  * Service container related to Page
@@ -58,7 +58,9 @@ export default class PageContainer extends Container {
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       isHackmdDraftUpdatingInRealtime: false,
       isHackmdDraftUpdatingInRealtime: false,
-      isCompactMode: false,
+
+      isHeaderSticky: false,
+      isSubnavCompact: false,
     };
     };
 
 
     this.initStateMarkdown();
     this.initStateMarkdown();
@@ -69,9 +71,19 @@ export default class PageContainer extends Container {
     this.addWebSocketEventHandlers = this.addWebSocketEventHandlers.bind(this);
     this.addWebSocketEventHandlers = this.addWebSocketEventHandlers.bind(this);
     this.addWebSocketEventHandlers();
     this.addWebSocketEventHandlers();
 
 
-    window.addEventListener('scroll', throttle(300, () => {
-      this.setState({ isCompactMode: window.pageYOffset > scrollAmountForFixed });
-    }));
+    window.addEventListener('scroll', () => {
+      const currentYOffset = window.pageYOffset;
+
+      // original throttling
+      if (this.state.isSubnavCompact && scrollThresForThrottling < currentYOffset) {
+        return;
+      }
+
+      this.setState({
+        isHeaderSticky: scrollThresForSticky < currentYOffset,
+        isSubnavCompact: scrollThresForCompact < currentYOffset,
+      });
+    });
   }
   }
 
 
   /**
   /**

+ 3 - 0
src/client/js/services/PersonalContainer.js

@@ -70,6 +70,9 @@ export default class PersonalContainer extends Container {
    * define a function for uploaded picture
    * define a function for uploaded picture
    */
    */
   getUploadedPictureSrc(user) {
   getUploadedPictureSrc(user) {
+    if (user == null) {
+      return DEFAULT_IMAGE;
+    }
     if (user.image) {
     if (user.image) {
       this.setState({ isUploadedPicture: true });
       this.setState({ isUploadedPicture: true });
       return user.image;
       return user.image;

+ 0 - 7
src/client/styles/agile-admin/inverse/colors/_apply-colors-dark.scss

@@ -1,10 +1,3 @@
-.top-left-part {
-  .logo-mark,
-  .logo-text {
-    fill: white;
-  }
-}
-
 /*
 /*
  * Button
  * Button
  */
  */

+ 0 - 4
src/client/styles/agile-admin/inverse/colors/antarctic.scss

@@ -91,10 +91,6 @@ table,
  * Accentcolor (yellow)
  * Accentcolor (yellow)
  */
  */
 
 
-header.affix {
-  border-bottom: 4px solid $accentcolor;
-}
-
 .modal {
 .modal {
   .modal-header {
   .modal-header {
     border-bottom: 4px solid $accentcolor;
     border-bottom: 4px solid $accentcolor;

+ 0 - 8
src/client/styles/agile-admin/inverse/colors/spring.scss

@@ -49,14 +49,6 @@ $wikilinktext-hover: gba(171, 224, 174, 0.9);
   border-top-color: $third-main-color;
   border-top-color: $third-main-color;
 }
 }
 
 
-/*
- * Accentcolor (green)
- */
-
-header.affix {
-  border-bottom: 4px solid $accentcolor;
-}
-
 .modal {
 .modal {
   .modal-header {
   .modal-header {
     border-bottom: 4px solid $accentcolor;
     border-bottom: 4px solid $accentcolor;

+ 4 - 0
src/client/styles/scss/_admin.scss

@@ -1,4 +1,8 @@
 .admin-page {
 .admin-page {
+  header.grw-header {
+    height: unset;
+  }
+
   .admin-user-menu {
   .admin-user-menu {
     .dropdown-menu {
     .dropdown-menu {
       right: 0;
       right: 0;

+ 83 - 23
src/client/styles/scss/_layout.scss

@@ -1,5 +1,24 @@
 @import 'layout_variable';
 @import 'layout_variable';
 
 
+%fukidashi-for-active {
+  position: relative;
+
+  // speech balloon
+  &:after {
+    position: absolute;
+    top: 0.5em;
+    right: -1em;
+    display: block;
+    width: 0;
+    content: '';
+    border: 1em solid transparent;
+    border-left-width: 0;
+
+    // @include media-breakpoint-down(xs) {
+    // }
+  }
+}
+
 // FIXME: replace with mt-2 or mt-3
 // FIXME: replace with mt-2 or mt-3
 .grw-mt-10px {
 .grw-mt-10px {
   margin-top: 10px !important;
   margin-top: 10px !important;
@@ -32,38 +51,67 @@
 
 
 .grw-sidebar {
 .grw-sidebar {
   .ak-navigation-resize-button {
   .ak-navigation-resize-button {
-    top: 110px;
+    top: calc(50vh - 20px);
   }
   }
-}
 
 
-/*
-  * header
-  */
-.grw-subnav {
-  overflow: unset;
+  // override @atlaskit/navigation-next styles
+  div[class$='-NavigationContainer'] {
+    // Adjust to be on top of the growi subnavigation
+    z-index: $zindex-sticky + 5;
+  }
+
+  // override @atlaskit/navigation-next styles
+  div[class$='-Outer'] {
+    div[class$='-Shadow'] {
+      background: unset;
+      border-right: 1px solid $border;
+    }
+  }
+
+  .grw-global-item-container {
+    i {
+      font-size: 1.5em;
+    }
+
+    // icon opacity
+    &:not(.active) {
+      i {
+        opacity: 0.4;
+      }
+      &:hover,
+      &:focus {
+        i {
+          opacity: 0.7;
+        }
+      }
+    }
+
+    &.active {
+      button {
+        @extend %fukidashi-for-active;
+      }
+    }
+  }
 }
 }
 
 
-.grw-modal-head {
-  font-size: 1em;
-  border-bottom: 1px solid $grw-line-gray;
+#page-wrapper {
+  margin-top: $grw-navbar-height;
 }
 }
 
 
-header#page-header {
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
+.grw-sidebar-header-container {
+  padding: 10px;
 
 
-  line-height: 1em;
-  // the container of h1
-  div.title-container {
-    padding-right: 5px;
-    padding-left: 5px;
-    margin-right: auto;
+  h3 {
+    margin-bottom: 0;
   }
   }
+}
 
 
-  h1 {
-    @include variable-font-size(28px);
-    line-height: 1.1em;
-  }
+.grw-sidebar-content-container {
+}
+
+.grw-modal-head {
+  font-size: 1em;
+  border-bottom: 1px solid $grw-line-gray;
 }
 }
 
 
 .main {
 .main {
@@ -114,6 +162,18 @@ header#page-header {
   }
   }
 }
 }
 
 
+.grw-fixed-controls-container {
+  position: fixed;
+  right: 1em;
+  bottom: 3em;
+
+  transition: all 200ms linear;
+
+  .grw-fixed-controls-button-container {
+    box-shadow: 0 3px 4px rgba($black, 0.3);
+  }
+}
+
 // printable style
 // printable style
 @media print {
 @media print {
   padding: 30px;
   padding: 30px;

+ 3 - 145
src/client/styles/scss/_layout_kibela.scss

@@ -1,39 +1,8 @@
-@import '../scss/theme/layout_kibela_variable';
-
 body.kibela {
 body.kibela {
-  .icon-link,
-  .CodeMirror-hint-active,
-  .grw-nav-main-left-tab,
-  .tav-pane,
-  .active {
-    color: #5882fa;
-  }
-
-  .bg-white {
-    background: #fefffe !important;
-  }
-
-  .bg-primary {
-    background-color: $primary !important;
-  }
-
-  /* h element */
-  h1,
-  h2,
-  h3,
-  h4,
-  h5,
-  h6 {
-    color: $color-header;
-    margin: 10px 0;
-  }
-
+  /* Logo */
   .logo {
   .logo {
-    background: transparent;
-
     .logo-mark {
     .logo-mark {
       height: 50px;
       height: 50px;
-      background-color: white;
       box-shadow: none;
       box-shadow: none;
 
 
       svg {
       svg {
@@ -43,10 +12,6 @@ body.kibela {
   }
   }
 
 
   /* header */
   /* header */
-  .background-t {
-    background-color: transparent;
-  }
-
   .authors {
   .authors {
     padding-top: 10px;
     padding-top: 10px;
 
 
@@ -55,28 +20,12 @@ body.kibela {
     }
     }
   }
   }
 
 
-  .search-input-group,
-  .search-typeahead {
-    .btn {
-      background-color: transparent;
-    }
-  }
-
   .panel-heading {
   .panel-heading {
     border-radius: 0 !important;
     border-radius: 0 !important;
   }
   }
 
 
-  .btn-open-dropzone {
-    background: rgb(243, 245, 247);
-  }
-
   /* page list */
   /* page list */
-  .page-list {
-    background: white;
-  }
-
   .page-attachments-row {
   .page-attachments-row {
-    background-color: #e5ecf1 !important;
     border: 0px;
     border: 0px;
   }
   }
 
 
@@ -86,7 +35,6 @@ body.kibela {
 
 
   .round-corner-top {
   .round-corner-top {
     z-index: absolute;
     z-index: absolute;
-    border-top: solid 0.4em #5584e1;
     border-radius: 0.35em;
     border-radius: 0.35em;
   }
   }
 
 
@@ -97,16 +45,14 @@ body.kibela {
     bottom: 0px;
     bottom: 0px;
     left: 0px;
     left: 0px;
     z-index: absolute;
     z-index: absolute;
-    height: 11em;
     max-width: 840px;
     max-width: 840px;
+    height: 11em;
     margin: auto;
     margin: auto;
-    border-top: solid 0.4em #5584e1;
     border-radius: 0.35em;
     border-radius: 0.35em;
   }
   }
 
 
   .grw-subnav {
   .grw-subnav {
     position: relative;
     position: relative;
-    background: transparent;
     border: none;
     border: none;
 
 
     svg {
     svg {
@@ -142,108 +88,23 @@ body.kibela {
     .list-group-item + .list-group-item.active {
     .list-group-item + .list-group-item.active {
       margin-top: 2px;
       margin-top: 2px;
     }
     }
-
-    .list-group-item.active {
-      color: #fff;
-      background: #1256a3;
-    }
-
-    .list-group-item {
-      &:hover {
-        background: #eee;
-      }
-    }
-  }
-
-  /* search page */
-  .search-result-list,
-  .page-list-li {
-    background: #f4f5f6;
   }
   }
 
 
   /* Tabs */
   /* Tabs */
   .nav.nav-tabs {
   .nav.nav-tabs {
     > .nav-item {
     > .nav-item {
-      color: #5882fa;
       cursor: pointer;
       cursor: pointer;
-      background: transparent;
-
-      &:hover,
-      &:focus {
-        > .nav-link {
-          color: #7a94d9;
-        }
-      }
 
 
       > .nav-link {
       > .nav-link {
-        color: #5882fa;
         border: none;
         border: none;
         border-radius: 3px;
         border-radius: 3px;
       }
       }
-
-      > .nav-link.active {
-        background: transparent !important;
-        border-bottom: solid 2.7px #5584e1;
-      }
-    }
-
-    .wiki {
-      h1 {
-        border-bottom: solid 2px #5584e1 !important;
-      }
-
-      h2 {
-        border-color: solid 1px #5584e1;
-      }
-    }
-  }
-
-  /* Modal */
-  .modal-content {
-    background-color: $themelight;
-
-    .modal-header.bg-primary {
-      color: white;
-
-      .close {
-        color: white;
-      }
-    }
-  }
-
-  /* Inline Code */
-  :not(.hljs) > code:not(.hljs) {
-    background-color: $bgcolor-inline-code;
-    color: $color-inline-code;
-  }
-
-  /* Card */
-  .card {
-    border: 1px solid $border;
-
-    .card-header {
-      background-color: $lightthemecolor;
-      border-bottom: 1px solid $border;
-    }
-
-    .card-body {
-      background-color: $themelight;
-    }
-
-    .card-footer {
-      background: white;
-      border-top: 1px solid $border
     }
     }
   }
   }
 
 
   /* button */
   /* button */
   .btn {
   .btn {
-    border-radius: $radius;
-  }
-
-  .btn-primary {
-    background: $primary;
-    border: 1px solid $primary;
+    // border-radius: $radius;
   }
   }
 
 
   /* Custom Control */
   /* Custom Control */
@@ -262,8 +123,6 @@ body.kibela {
 
 
   /* edit */
   /* edit */
   .CodeMirror {
   .CodeMirror {
-    border: solid 1.2px #d8d8d8;
-    border-top: solid 0.3em #5584e1 !important;
     border-radius: 0.35em;
     border-radius: 0.35em;
   }
   }
 
 
@@ -304,7 +163,6 @@ body.kibela {
     .page-editor-preview-container {
     .page-editor-preview-container {
       padding-right: 0px !important;
       padding-right: 0px !important;
       padding-left: 2em;
       padding-left: 2em;
-      background: white !important;
     }
     }
 
 
     .page-editor-footer {
     .page-editor-footer {

+ 0 - 22
src/client/styles/scss/_layout_variable.scss

@@ -1,30 +1,8 @@
 /* color variables */
 /* color variables */
 
 
-/* green */
-$grw-base-green: rgb(7, 146, 72);
-$grw-mos-green: rgb(5, 70, 35);
-$grw-light-green: rgb(204, 238, 220);
-$grw-white-green: #f8fffb;
-$grw-sea-green: seagreen;
-
-.bg-grw-green {
-  background: $grw-base-green;
-}
-
-.bg-grw-mos-green {
-  background: $grw-mos-green;
-}
-
-.bg-grw-light-green {
-  background: $grw-light-green;
-}
-
 /* blue */
 /* blue */
 $grw-alice-blue: aliceblue;
 $grw-alice-blue: aliceblue;
 
 
 /* gray */
 /* gray */
 $grw-line-gray: #dee2e6;
 $grw-line-gray: #dee2e6;
 $grw-line-light-gray: #ddd;
 $grw-line-light-gray: #ddd;
-
-/* white */
-$grw-floral-white: floralwhite;

+ 5 - 0
src/client/styles/scss/_me.scss

@@ -0,0 +1,5 @@
+.user-settings-page {
+  header.grw-header {
+    height: unset;
+  }
+}

+ 1 - 1
src/client/styles/scss/_mixins.scss

@@ -72,7 +72,7 @@
         .hackmd-preinit,
         .hackmd-preinit,
         .hackmd-error,
         .hackmd-error,
         #iframe-hackmd-container > iframe {
         #iframe-hackmd-container > iframe {
-          width: 100vw;
+          width: 100%;
           height: calc(100vh - #{$header-plus-footer});
           height: calc(100vh - #{$header-plus-footer});
           min-height: calc(100vh - #{$header-plus-footer}); // for IE11
           min-height: calc(100vh - #{$header-plus-footer}); // for IE11
         }
         }

+ 5 - 0
src/client/styles/scss/_navbar.scss

@@ -1,4 +1,9 @@
 .grw-navbar {
 .grw-navbar {
+  .grw-navbar-toggler {
+    padding: 0.5rem;
+    font-size: 1.5em;
+  }
+
   .nav-link,
   .nav-link,
   .nav-item.confidential {
   .nav-item.confidential {
     display: flex;
     display: flex;

+ 4 - 8
src/client/styles/scss/_on-edit.scss

@@ -72,8 +72,8 @@ body.on-edit {
     }
     }
   }
   }
 
 
-  // show compact subnav
-  .grw-compact-subnav {
+  // show revision path
+  .grw-revision-path-for-edit {
     display: block !important;
     display: block !important;
   }
   }
 
 
@@ -97,7 +97,7 @@ body.on-edit {
 
 
     background: none;
     background: none;
 
 
-    > .grw-title-bar {
+    > .grw-subnav-container {
       width: 100%; //   for crowi layout
       width: 100%; //   for crowi layout
       padding: 0; //    for crowi layout
       padding: 0; //    for crowi layout
       pointer-events: initial; // enable pointer-events
       pointer-events: initial; // enable pointer-events
@@ -117,7 +117,7 @@ body.on-edit {
       }
       }
     }
     }
 
 
-    .tag-viewer.new-page {
+    .tag-labels.new-page {
       display: block;
       display: block;
     }
     }
   }
   }
@@ -197,10 +197,6 @@ body.on-edit {
     }
     }
 
 
     #page-editor-options-selector {
     #page-editor-options-selector {
-      label {
-        margin-right: 0.5em;
-      }
-
       .grw-editor-configuration-dropdown {
       .grw-editor-configuration-dropdown {
         .icon-container {
         .icon-container {
           display: inline-block;
           display: inline-block;

+ 1 - 1
src/client/styles/scss/_override-bootstrap-variables.scss

@@ -18,7 +18,7 @@ $dark: #3e4d6c !default;
 //## Font, line-height, and color for body text, headings, and more.
 //## Font, line-height, and color for body text, headings, and more.
 $font-family-sans-serif:  Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
 $font-family-sans-serif:  Lato, -apple-system, BlinkMacSystemFont, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
 $font-family-serif:       Georgia, "Times New Roman", Times, serif;
 $font-family-serif:       Georgia, "Times New Roman", Times, serif;
-$font-family-monospace: Osaka-Mono, 'MS Gothic', Monaco, Menlo, Consolas, 'Courier New', monospace;
+$font-family-monospace: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
 $font-family-base:        $font-family-sans-serif;
 $font-family-base:        $font-family-sans-serif;
 
 
 $font-size-root: 14px;
 $font-size-root: 14px;

+ 5 - 0
src/client/styles/scss/_override-bootstrap.scss

@@ -44,6 +44,11 @@
     line-height: 10px;
     line-height: 10px;
   }
   }
 
 
+  code {
+    padding: 2px 4px;
+    font-size: 90%;
+  }
+
   // Navs
   // Navs
   .nav-tabs {
   .nav-tabs {
     .nav-item {
     .nav-item {

+ 8 - 0
src/client/styles/scss/_override-rbt.scss

@@ -9,6 +9,14 @@
     }
     }
   }
   }
 }
 }
+
+.rbt-input-wrapper {
+  .close.rbt-close {
+    // default bootstrap .close has padding 0
+    padding: 3px 7px;
+  }
+}
+
 // hide loading icon
 // hide loading icon
 .rbt-aux {
 .rbt-aux {
   display: none;
   display: none;

+ 2 - 1
src/client/styles/scss/_page.scss

@@ -25,7 +25,7 @@
     }
     }
   }
   }
 
 
-  .tag-viewer.new-page {
+  .tag-labels.new-page {
     display: none;
     display: none;
   }
   }
 }
 }
@@ -70,6 +70,7 @@
       .revision-history-diff {
       .revision-history-diff {
         padding-left: 40px;
         padding-left: 40px;
         color: #333;
         color: #333;
+        table-layout: fixed;
       }
       }
     }
     }
 
 

+ 0 - 41
src/client/styles/scss/_page_growi.scss

@@ -1,41 +0,0 @@
-.growi {
-  header {
-    // Adjust to be on top of the growi subnavigation
-    z-index: $zindex-sticky - 100;
-    ul.authors {
-      padding-left: 1.5em;
-      margin: 0;
-
-      li {
-        font-size: 12px;
-        list-style: none;
-      }
-
-      .picture {
-        width: 22px;
-        height: 22px;
-        border: 1px solid #ccc;
-
-        &.picture-xs {
-          width: 14px;
-          height: 14px;
-        }
-      }
-    }
-    .grw-compact-subnavbar {
-      h2 {
-        font-size: 20px;
-        line-height: 1.1em;
-        @include media-breakpoint-down(md) {
-          font-size: 18px;
-        }
-        @include media-breakpoint-down(sm) {
-          font-size: 14px;
-        }
-        @include media-breakpoint-down(xs) {
-          font-size: 12px;
-        }
-      }
-    }
-  }
-}

+ 0 - 10
src/client/styles/scss/_page_header.scss

@@ -1,10 +0,0 @@
-#page-header {
-  &:hover {
-    .btn-copy,
-    .btn-edit,
-    .btn-edit-tags {
-      // change button opacity
-      opacity: unset;
-    }
-  }
-}

+ 100 - 0
src/client/styles/scss/_subnav.scss

@@ -0,0 +1,100 @@
+$easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
+
+%transitionForCompactMode {
+  // set transition-duration (normal -> compact)
+  transition: all 300ms $easeInOutCubic;
+}
+
+/*
+ * layout for sticky
+ */
+header.grw-header {
+  top: $grw-navbar-height;
+
+  // Adjust to be on top of the growi subnavigation
+  z-index: $zindex-sticky;
+
+  height: 80px;
+  pointer-events: none; // disable pointer events for sticky
+
+  .grw-subnav {
+    overflow: unset;
+    pointer-events: all; // enable pointer events
+  }
+}
+
+/*
+ * Compact Mode Switching
+ */
+.grw-subnavbar {
+  &.grw-subnavbar-compact {
+    @extend %transitionForCompactMode;
+
+    h1 {
+      @include variable-font-size(18px);
+      @extend %transitionForCompactMode;
+    }
+  }
+}
+
+/*
+ * Sticky Mode Switching
+ */
+.grw-subnavbar {
+  &.grw-subnavbar-sticky {
+    // set transition-duration (init -> sticky)
+    transition: all 400ms linear !important;
+  }
+}
+
+/*
+ * Styles
+ */
+
+.grw-header {
+  .title {
+    padding: 0.5rem 15px;
+
+    line-height: 1em;
+
+    @include variable-font-size(28px);
+    line-height: 1.1em;
+  }
+}
+
+.grw-subnavbar {
+  &:hover {
+    .btn-copy,
+    .btn-edit,
+    .btn-edit-tags {
+      // change button opacity
+      opacity: unset;
+    }
+  }
+
+  h1 {
+    @include variable-font-size(28px);
+    line-height: 1.1em;
+  }
+
+  ul.authors {
+    padding-left: 1.5em;
+    margin: 0;
+
+    li {
+      font-size: 12px;
+      list-style: none;
+    }
+
+    .picture {
+      width: 22px;
+      height: 22px;
+      border: 1px solid #ccc;
+
+      &.picture-xs {
+        width: 14px;
+        height: 14px;
+      }
+    }
+  }
+}

+ 12 - 8
src/client/styles/scss/_tag.scss

@@ -1,4 +1,14 @@
-.tag-viewer {
+.tags-page {
+  header.grw-header {
+    height: unset;
+  }
+
+  .list-tag-count {
+    background: rgba(0, 0, 0, 0.08);
+  }
+}
+
+.tag-labels {
   .manage-tags {
   .manage-tags {
     font-size: 10px;
     font-size: 10px;
     cursor: pointer;
     cursor: pointer;
@@ -19,13 +29,7 @@
   }
   }
 }
 }
 
 
-#tags-page {
-  .list-tag-count {
-    background: rgba(0, 0, 0, 0.08);
-  }
-}
-
-#editTagModal {
+#edit-tag-modal {
   .form-control {
   .form-control {
     height: auto;
     height: auto;
   }
   }

+ 55 - 30
src/client/styles/scss/_user.scss

@@ -1,4 +1,49 @@
-.user-page-header {
+$easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
+
+%transitionForCompactMode {
+  // set transition-duration (normal -> compact)
+  transition: all 300ms $easeInOutCubic;
+}
+
+.grw-header.grw-header-user-page {
+  height: 150px;
+}
+
+/*
+ * Compact Mode Switching
+ */
+.grw-subnavbar.grw-subnavbar-user-page {
+  &.grw-subnavbar-compact {
+    .grw-user-page-path {
+      margin-bottom: 0;
+      font-size: 14px;
+
+      @extend %transitionForCompactMode;
+    }
+    .picture {
+      width: 62px;
+      height: 62px;
+
+      @extend %transitionForCompactMode;
+    }
+    h1 {
+      font-size: 1.5em;
+      line-height: 30px;
+
+      @extend %transitionForCompactMode;
+    }
+    .users-meta {
+      margin-left: 15px;
+
+      @extend %transitionForCompactMode;
+    }
+  }
+}
+
+/*
+ * Styles
+ */
+.grw-subnavbar-user-page {
   #revision-path {
   #revision-path {
     margin-bottom: 0;
     margin-bottom: 0;
   }
   }
@@ -14,19 +59,16 @@
   }
   }
 
 
   .picture {
   .picture {
-    width: 64px;
-    height: 64px;
+    width: 72px;
+    height: 72px;
   }
   }
 
 
-  .user-page-meta {
+  ul.user-page-meta {
+    padding-left: 0;
     color: #999;
     color: #999;
 
 
-    ul {
-      padding-left: 0;
-
-      li {
-        list-style: none;
-      }
+    li {
+      list-style: none;
     }
     }
 
 
     .user-page-username {
     .user-page-username {
@@ -46,32 +88,15 @@
     }
     }
   }
   }
 
 
-  .btn-like,
-  .btn-bookmark {
+  .btn.btn-bookmark {
     &.btn-lg {
     &.btn-lg {
-      padding: 8px;
+      width: 50px;
+      height: 50px;
       font-size: 1.5em;
       font-size: 1.5em;
     }
     }
   }
   }
 }
 }
 
 
-// affix
-.user-page-header.affix {
-  .users-meta {
-    margin-left: 15px;
-  }
-
-  h1 {
-    font-size: 1.5em;
-    line-height: 30px;
-  }
-
-  .picture {
-    width: 48px;
-    height: 48px;
-  }
-}
-
 .draft-list-item {
 .draft-list-item {
   .icon-container {
   .icon-container {
     .icon-copy,
     .icon-copy,

+ 0 - 7
src/client/styles/scss/_user_growi.scss

@@ -1,11 +1,4 @@
 .growi .user-page {
 .growi .user-page {
-  // affix
-  .user-page-header.affix {
-    #revision-path {
-      display: none;
-    }
-  }
-
   .revision-toc {
   .revision-toc {
     position: sticky;
     position: sticky;
     top: 105px;
     top: 105px;

+ 2 - 2
src/client/styles/scss/_variables.scss

@@ -6,8 +6,8 @@ $font-family-monospace-not-strictly: Monaco, Menlo, Consolas, 'Courier New', Mei
 
 
 //== Layout
 //== Layout
 $grw-navbar-height: 50px;
 $grw-navbar-height: 50px;
-$grw-logo-width: 60px;
-$grw-logomark-width: 32px;
+$grw-logo-width: 64px;
+$grw-logomark-width: 40px;
 
 
 // fix tab width to 95 pixels
 // fix tab width to 95 pixels
 // see also '_on-edit.scss'
 // see also '_on-edit.scss'

+ 6 - 34
src/client/styles/scss/atoms/_buttons.scss

@@ -1,37 +1,7 @@
-.btn-circle {
-  width: 30px;
-  height: 30px;
-  padding: 6px 0;
-  font-size: 12px;
-  line-height: 1.428571429;
-  text-align: center;
-  border-radius: 15px;
-}
-
-.btn-circle.btn-lg {
-  width: 50px;
-  height: 50px;
-  padding: 10px 16px;
-  font-size: 18px;
-  line-height: 1.33;
-  border-radius: 25px;
-}
-
-.btn-circle.btn-xl {
-  width: 70px;
-  height: 70px;
-  padding: 10px 16px;
-  font-size: 24px;
-  line-height: 1.33;
-  border-radius: 35px;
-}
-
-.btn-like,
-.btn-bookmark {
-  font-size: 1.2em;
-  line-height: 0.8em;
-
-  &.active {
+.btn.btn-like,
+.btn.btn-bookmark {
+  &.active,
+  &:hover {
     // header buttons are always white for active
     // header buttons are always white for active
     color: white !important;
     color: white !important;
   }
   }
@@ -39,6 +9,8 @@
   &:not(:hover):not(.active) {
   &:not(:hover):not(.active) {
     background-color: transparent;
     background-color: transparent;
   }
   }
+  width: 35px;
+  height: 35px;
 }
 }
 
 
 .btn-copy,
 .btn-copy,

+ 2 - 2
src/client/styles/scss/style-app.scss

@@ -41,16 +41,16 @@
 @import 'layout_kibela';
 @import 'layout_kibela';
 @import 'layout_variable';
 @import 'layout_variable';
 @import 'login';
 @import 'login';
+@import 'me';
 @import 'navbar';
 @import 'navbar';
 @import 'navbar_kibela';
 @import 'navbar_kibela';
 @import 'notification';
 @import 'notification';
 @import 'on-edit';
 @import 'on-edit';
 @import 'page_list';
 @import 'page_list';
 @import 'page';
 @import 'page';
-@import 'page_header';
-@import 'page_growi';
 @import 'search';
 @import 'search';
 @import 'shortcuts';
 @import 'shortcuts';
+@import 'subnav';
 @import 'tag';
 @import 'tag';
 @import 'user';
 @import 'user';
 @import 'user_growi';
 @import 'user_growi';

+ 18 - 19
src/client/styles/scss/theme/_apply-colors-dark.scss

@@ -1,10 +1,3 @@
-.logo {
-  .logo-mark,
-  .logo-text {
-    fill: white;
-  }
-}
-
 /*
 /*
  * Button
  * Button
  */
  */
@@ -111,15 +104,6 @@ textarea.form-control {
   // border: 1px solid $border;
   // border: 1px solid $border;
 }
 }
 
 
-/*
- * GROWI header
- */
-header.affix {
-  .logo-mark {
-    fill: white;
-  }
-}
-
 /*
 /*
  * GROWI page list
  * GROWI page list
  */
  */
@@ -139,9 +123,24 @@ header.affix {
 /*
 /*
  * GROWI subnavigation
  * GROWI subnavigation
  */
  */
-.grw-compact-subnavbar {
-  background-color: rgba(darken($bgcolor-global, 90%), 0.9);
-  box-shadow: 0 0 2px darken($bgcolor-global, 5%);
+.admin-page,
+.user-settings-page,
+.tags-page {
+  .grw-header {
+    background-color: rgba(darken($bgcolor-global, 90%), 0.9);
+  }
+}
+
+.grw-subnavbar {
+  background-color: rgba(darken($bgcolor-global, 90%), 1);
+
+  &.grw-subnavbar-sticky {
+    background-color: rgba(darken($bgcolor-global, 90%), 0.9);
+    box-shadow: 0 3px 2px -2px darken($bgcolor-global, 5%);
+  }
+}
+
+.grw-subnavbar-sticky {
 }
 }
 
 
 /*
 /*

+ 203 - 0
src/client/styles/scss/theme/_apply-colors-kibela.scss

@@ -0,0 +1,203 @@
+body.kibela {
+  .icon-link,
+  .CodeMirror-hint-active,
+  .grw-nav-main-left-tab,
+  .tav-pane,
+  .active {
+    color: $subthemecolor;
+  }
+
+  .bg-white {
+    background: #fefffe !important;
+  }
+
+  .bg-primary {
+    background-color: $primary !important;
+  }
+
+  /* kibela block */
+  .kibela-block {
+    border-top: solid 0.4em $thickborder;
+  }
+
+  /* page wrapper */
+  #page-wrapper {
+    background-color: $bgcolor-global;
+  }
+
+  /* header*/
+  .background-t {
+    background-color: transparent;
+  }
+
+  .search-input-group,
+  .search-typeahead {
+    .btn {
+      background-color: transparent;
+    }
+  }
+
+  .btn-open-dropzone {
+    background: $themelight;
+  }
+
+  /* page list */
+  .page-list {
+    background: white;
+  }
+
+  .page-attachments-row {
+    background-color: #e5ecf1;
+  }
+
+  /* round */
+  .round-corner-top {
+    border-top: solid 0.4em $thickborder;
+  }
+
+  /* admin navigation */
+  .admin-navigation {
+    .list-group-item {
+      background-color: transparent;
+
+      &:hover {
+        background: #eee;
+      }
+    }
+
+    .list-group-item.active {
+      color: white;
+      background: $bgcolor-navbar-active;
+    }
+  }
+
+  /* search page */
+  .search-result-list,
+  .page-list-li {
+    background: $themelight;
+  }
+
+  /* Tabs */
+  .nav.nav-tabs {
+    > .nav-item {
+      color: $color-link;
+      background: transparent;
+
+      &:hover,
+      &:focus {
+        > .nav-link {
+          color: $color-link-hover;
+        }
+      }
+
+      > .nav-link {
+        color: $color-link;
+      }
+
+      > .nav-link.active {
+        background: transparent !important;
+        border-bottom: solid 2.7px $thickborder;
+      }
+    }
+  }
+
+  /* wiki */
+  .wiki {
+    h1 {
+      border-bottom: solid 2px $thickborder !important;
+    }
+
+    h2 {
+      border-color: solid 1px $thickborder;
+    }
+
+    // change color of highlighted header in wiki (default: orange)
+    .code-line.revision-head.highlighted {
+      color: $themelight;
+      background-color: lighten($bgcolor-theme, 20%);
+
+      .icon-note,
+      .icon-link {
+        color: $themelight;
+      }
+    }
+  }
+
+  /* Modal */
+  .modal-content {
+    background-color: $themelight;
+
+    .modal-header.bg-primary {
+      color: white;
+
+      .close {
+        color: white;
+      }
+    }
+  }
+
+  /* Inline Code */
+  :not(.hljs) > code:not(.hljs) {
+    color: $color-inline-code;
+    background-color: $bgcolor-inline-code;
+  }
+
+  /* Card */
+  .card {
+    border: 1px solid $border-color-theme;
+
+    .card-header {
+      background-color: $lightthemecolor;
+      border-bottom: 1px solid $border-color-theme;
+    }
+
+    .card-body {
+      background-color: $themelight;
+    }
+
+    .card-footer {
+      background: white;
+      border-top: 1px solid $border-color-theme;
+    }
+  }
+
+  /* button */
+  .btn-primary {
+    background: $primary;
+    border: 1px solid $primary;
+  }
+
+  /* edit */
+  .CodeMirror {
+    border: solid 1.2px #d8d8d8;
+    border-top: solid 0.3em $thickborder !important;
+  }
+
+  &.on-edit {
+    .page-editor-preview-container {
+      background: white !important;
+    }
+  }
+
+  /* subnav */
+  .grw-subnav {
+    background: transparent;
+  }
+
+  /* navbar */
+  .grw-navbar {
+    .nav-item > .nav-link:hover {
+      color: $color-link-nabvar-hover;
+    }
+  }
+
+  /* h */
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    color: $color-header;
+  }
+}

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