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

fixMerge branch 'support/apply-bootstrap4' into support/replace-useless-class

# Conflicts:
#	src/server/views/me/index.html

fix conflict
akira-s 6 лет назад
Родитель
Сommit
2c230aa49d
92 измененных файлов с 757 добавлено и 1298 удалено
  1. 5 1
      CHANGES.md
  2. 1 1
      config/webpack.common.js
  3. 2 3
      package.json
  4. 1 1
      src/client/js/components/Admin/App/MailSetting.jsx
  5. 1 1
      src/client/js/components/Admin/App/PluginSetting.jsx
  6. 1 1
      src/client/js/components/Admin/Customize/CustomizeFunctionOption.jsx
  7. 1 1
      src/client/js/components/Admin/Customize/CustomizeFunctionSetting.jsx
  8. 17 29
      src/client/js/components/Admin/Customize/CustomizeLayoutOptions.jsx
  9. 4 4
      src/client/js/components/Admin/ExportArchiveData/SelectCollectionsModal.jsx
  10. 1 1
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportCollectionItem.jsx
  11. 1 1
      src/client/js/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  12. 27 31
      src/client/js/components/Admin/MarkdownSetting/LineBreakForm.jsx
  13. 1 1
      src/client/js/components/Admin/Notification/SlackAppConfiguration.jsx
  14. 1 1
      src/client/js/components/Admin/Security/BasicSecuritySetting.jsx
  15. 1 1
      src/client/js/components/Admin/Security/GitHubSecuritySetting.jsx
  16. 1 1
      src/client/js/components/Admin/Security/GoogleSecuritySetting.jsx
  17. 1 1
      src/client/js/components/Admin/Security/LdapSecuritySetting.jsx
  18. 2 2
      src/client/js/components/Admin/Security/OidcSecuritySetting.jsx
  19. 2 2
      src/client/js/components/Admin/Security/SamlSecuritySetting.jsx
  20. 2 2
      src/client/js/components/Admin/Security/SecuritySetting.jsx
  21. 1 1
      src/client/js/components/Admin/Security/TwitterSecuritySetting.jsx
  22. 13 6
      src/client/js/components/Admin/UserManagement.jsx
  23. 16 16
      src/client/js/components/Admin/Users/PasswordResetModal.jsx
  24. 4 4
      src/client/js/components/Admin/Users/UserInviteModal.jsx
  25. 2 2
      src/client/js/components/Admin/Users/UserTable.jsx
  26. 1 1
      src/client/js/components/Me/ApiSettings.jsx
  27. 21 25
      src/client/js/components/Me/ExternalAccountLinkedMe.jsx
  28. 1 1
      src/client/js/components/Me/PasswordSettings.jsx
  29. 4 4
      src/client/js/components/Me/UserSettings.jsx
  30. 57 32
      src/client/js/components/Navbar/GrowiSubNavigation.jsx
  31. 37 46
      src/client/js/components/Navbar/GrowiSubNavigationForUserPage.jsx
  32. 16 12
      src/client/js/components/Page/RevisionPath.jsx
  33. 1 1
      src/client/js/components/Page/TagEditor.jsx
  34. 1 1
      src/client/js/components/Page/TagLabels.jsx
  35. 9 6
      src/client/js/components/Sidebar/CustomSidebar.jsx
  36. 9 6
      src/client/js/components/Sidebar/History.jsx
  37. 7 6
      src/client/js/components/Sidebar/SidebarNav.jsx
  38. 4 1
      src/client/js/services/AdminUsersContainer.js
  39. 19 7
      src/client/js/services/PageContainer.js
  40. 3 0
      src/client/js/services/PersonalContainer.js
  41. 0 7
      src/client/styles/agile-admin/inverse/colors/_apply-colors-dark.scss
  42. 0 4
      src/client/styles/agile-admin/inverse/colors/antarctic.scss
  43. 0 8
      src/client/styles/agile-admin/inverse/colors/spring.scss
  44. 4 0
      src/client/styles/scss/_admin.scss
  45. 9 23
      src/client/styles/scss/_layout.scss
  46. 1 1
      src/client/styles/scss/_layout_kibela.scss
  47. 5 0
      src/client/styles/scss/_me.scss
  48. 4 4
      src/client/styles/scss/_on-edit.scss
  49. 5 0
      src/client/styles/scss/_override-bootstrap.scss
  50. 1 1
      src/client/styles/scss/_page.scss
  51. 0 41
      src/client/styles/scss/_page_growi.scss
  52. 0 10
      src/client/styles/scss/_page_header.scss
  53. 97 0
      src/client/styles/scss/_subnav.scss
  54. 12 8
      src/client/styles/scss/_tag.scss
  55. 54 29
      src/client/styles/scss/_user.scss
  56. 0 7
      src/client/styles/scss/_user_growi.scss
  57. 6 5
      src/client/styles/scss/atoms/_buttons.scss
  58. 2 2
      src/client/styles/scss/style-app.scss
  59. 18 19
      src/client/styles/scss/theme/_apply-colors-dark.scss
  60. 15 12
      src/client/styles/scss/theme/_apply-colors-light.scss
  61. 10 18
      src/client/styles/scss/theme/_apply-colors.scss
  62. 116 6
      src/client/styles/scss/theme/antarctic.scss
  63. 3 0
      src/client/styles/scss/theme/default.scss
  64. 1 3
      src/server/views/admin/Users_reserve.html
  65. 2 3
      src/server/views/admin/app.html
  66. 1 3
      src/server/views/admin/customize.html
  67. 1 3
      src/server/views/admin/export.html
  68. 1 3
      src/server/views/admin/external-accounts.html
  69. 1 3
      src/server/views/admin/global-notification-detail.html
  70. 1 3
      src/server/views/admin/importer.html
  71. 1 3
      src/server/views/admin/index.html
  72. 1 3
      src/server/views/admin/markdown.html
  73. 1 3
      src/server/views/admin/notification.html
  74. 1 3
      src/server/views/admin/search.html
  75. 1 3
      src/server/views/admin/security.html
  76. 1 3
      src/server/views/admin/user-group-detail.html
  77. 1 3
      src/server/views/admin/user-groups.html
  78. 1 3
      src/server/views/admin/users.html
  79. 1 1
      src/server/views/layout-crowi/base/layout.html
  80. 7 13
      src/server/views/layout-growi/base/layout.html
  81. 1 1
      src/server/views/layout-growi/page_list.html
  82. 8 6
      src/server/views/layout-growi/user_page.html
  83. 6 10
      src/server/views/layout-growi/widget/header.html
  84. 2 2
      src/server/views/layout-growi/widget/liker-and-seenusers.html
  85. 1 1
      src/server/views/layout-kibela/base/layout.html
  86. 1 1
      src/server/views/layout-kibela/user_page.html
  87. 0 85
      src/server/views/me/api_token.html
  88. 0 249
      src/server/views/me/external-accounts.html
  89. 4 354
      src/server/views/me/index.html
  90. 8 9
      src/server/views/tags.html
  91. 27 25
      src/server/views/widget/page_tabs.html
  92. 14 27
      yarn.lock

+ 5 - 1
CHANGES.md

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

+ 1 - 1
config/webpack.common.js

@@ -43,7 +43,7 @@ module.exports = (options) => {
       // 'styles/theme-wood':          './src/client/styles/scss/theme/wood.scss',
       // 'styles/theme-christmas':          './src/client/styles/scss/theme/christmas.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/style-hackmd':          './src/client/styles/hackmd/style.scss',
     }, options.entry || {}), // Merge with env dependent settings

+ 2 - 3
package.json

@@ -155,7 +155,6 @@
     ],
     "@alienfast/i18next-loader": "^1.0.16",
     "@atlaskit/drawer": "^5.3.5",
-    "@atlaskit/logo": "^12.3.3",
     "@atlaskit/navigation-next": "^8.0.2",
     "@babel/core": "^7.4.5",
     "@babel/plugin-proposal-class-properties": "^7.8.3",
@@ -198,8 +197,8 @@
     "load-css-file": "^1.0.0",
     "lodash-webpack-plugin": "^0.11.5",
     "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-footnote": "^3.0.1",
     "markdown-it-mathjax": "^2.0.0",

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

@@ -79,7 +79,7 @@ class MailSetting extends React.Component {
             <input
               className="form-control"
               type="text"
-              defaultValue={adminAppContainer.state.SmtpUser || ''}
+              defaultValue={adminAppContainer.state.smtpUser || ''}
               onChange={(e) => { adminAppContainer.changeSmtpUser(e.target.value) }}
             />
           </div>

+ 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="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
                 id="isEnabledPlugins"
                 className="custom-control-input"

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

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

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

@@ -150,7 +150,7 @@ class CustomizeBehaviorSetting extends React.Component {
             </div>
 
             <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
                   optionId="isAllReplyShown"
                   label={t('admin:customize_setting.function_options.show_all_reply_comments')}

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

@@ -14,8 +14,8 @@ class CustomizeLayoutOptions extends React.Component {
     const { t, adminCustomizeContainer } = this.props;
 
     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
             layoutType="crowi-plus"
             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>`}
           >
             <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>
         </div>
 
-        <div className="col-md-4">
+        <div className="col text-center">
           <CustomizeLayoutOption
             layoutType="kibela"
             isSelected={adminCustomizeContainer.state.currentLayout === 'kibela'}
@@ -39,27 +41,13 @@ class CustomizeLayoutOptions extends React.Component {
             labelHtml="Kibela Like Layout"
           >
             <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>
         </div>
       </div>

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

@@ -193,26 +193,26 @@ class SelectCollectionsModal extends React.Component {
             </div>
             <div className="row mt-4">
               <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)}
               </div>
             </div>
             <div className="row mt-4">
               <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.renderWarnForUser()}
               </div>
             </div>
             <div className="row mt-4">
               <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)}
               </div>
             </div>
             <div className="row mt-4">
               <div className="col-sm-12">
-                <h3 className="admin-setting-header">Other Collections</h3>
+                <h3 className="admin-setting-header">MongoDB Other Collections</h3>
                 {this.renderOthers()}
               </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 (
       <div className="card border-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 */}
             {this.renderCheckbox()}
             {/* 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);
 
           return (
-            <div className="col-6 my-1" key={collectionName}>
+            <div className="col-md-6 my-1" key={collectionName}>
               <ImportCollectionItem
                 isImporting={isImporting}
                 isImported={collectionProgress ? isImported : false}

+ 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') };
 
     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>
+        <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreak} />
       </div>
     );
   }
@@ -71,22 +69,20 @@ class LineBreakForm extends React.Component {
     const helpLineBreakInComment = { __html: t('admin:markdown_setting.lineBreak_options.enable_lineBreak_for_comment_desc') };
 
     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>
+        <p className="form-text text-muted" dangerouslySetInnerHTML={helpLineBreakInComment} />
       </div>
     );
   }
@@ -96,7 +92,7 @@ class LineBreakForm extends React.Component {
 
     return (
       <React.Fragment>
-        <fieldset className="col-12">
+        <fieldset className="form-group row row-cols-1 row-cols-md-2 mx-3">
           {this.renderLineBreakOption()}
           {this.renderLineBreakInCommentOption()}
         </fieldset>

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

@@ -77,7 +77,7 @@ class SlackAppConfiguration extends React.Component {
 
             <div className="row mb-3">
               <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
                     type="checkbox"
                     className="custom-control-input"

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

@@ -96,7 +96,7 @@ class BasicSecurityManagement extends React.Component {
         <React.Fragment>
           <div className="row mb-5">
             <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
                   id="bindByEmail-basic"
                   className="custom-control-input"

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

@@ -149,7 +149,7 @@ class GitHubSecurityManagement extends React.Component {
 
             <div className="row mb-5">
               <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
                     id="bindByUserNameGitHub"
                     className="custom-control-input"

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

@@ -149,7 +149,7 @@ class GoogleSecurityManagement extends React.Component {
 
             <div className="row mb-5">
               <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
                     id="bindByUserNameGoogle"
                     className="custom-control-input"

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

@@ -275,7 +275,7 @@ class LdapSecuritySetting extends React.Component {
 
             <div className="row mb-5">
               <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
                     type="checkbox"
                     className="custom-control-input"

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

@@ -262,7 +262,7 @@ class OidcSecurityManagement extends React.Component {
 
             <div className="row mb-3">
               <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
                     id="bindByUserName-oidc"
                     className="custom-control-input"
@@ -284,7 +284,7 @@ class OidcSecurityManagement extends React.Component {
 
             <div className="row mb-5">
               <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
                     id="bindByEmail-oidc"
                     className="custom-control-input"

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

@@ -422,7 +422,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
 
             <div className="row mb-5">
               <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
                     id="bindByUserName-SAML"
                     className="custom-control-input"
@@ -444,7 +444,7 @@ pWVdnzS1VCO8fKsJ7YYIr+JmHvseph3kFUOI5RqkCcMZlKUv83aUThsTHw==
 
             <div className="row mb-5">
               <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
                     id="bindByEmail-SAML"
                     className="custom-control-input"

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

@@ -104,7 +104,7 @@ class SecuritySetting extends React.Component {
         <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">
+            <div className="custom-control custom-checkbox custom-checkbox-success">
               <input
                 type="checkbox"
                 className="custom-control-input"
@@ -122,7 +122,7 @@ class SecuritySetting extends React.Component {
         <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">
+            <div className="custom-control custom-checkbox custom-checkbox-success">
               <input
                 type="checkbox"
                 className="custom-control-input"

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

@@ -149,7 +149,7 @@ class TwitterSecurityManagement extends React.Component {
 
             <div className="row mb-5">
               <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
                     id="bindByUserNameTwitter"
                     className="custom-control-input"

+ 13 - 6
src/client/js/components/Admin/UserManagement.jsx

@@ -121,7 +121,14 @@ class UserManagement extends React.Component {
 
     return (
       <Fragment>
-        {adminUsersContainer.state.userForPasswordResetModal && <PasswordResetModal />}
+        {adminUsersContainer.state.userForPasswordResetModal != null
+        && (
+        <PasswordResetModal
+          isOpen={adminUsersContainer.state.isPasswordResetModalShown}
+          onClose={adminUsersContainer.hidePasswordResetModal}
+          userForPasswordResetModal={adminUsersContainer.state.userForPasswordResetModal}
+        />
+        )}
         <p>
           <InviteUserControl />
           <a className="btn text-dark btn-outline-secondary ml-2" href="/admin/users/external-accounts" role="button">
@@ -157,7 +164,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('all') }}
                 />
                 <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>
               </div>
 
@@ -170,7 +177,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('registered') }}
                 />
                 <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>
               </div>
 
@@ -183,7 +190,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('active') }}
                 />
                 <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>
               </div>
 
@@ -196,7 +203,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('suspended') }}
                 />
                 <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-secondary d-inline-block vt mt-1">Suspended</span>
                 </label>
               </div>
 
@@ -209,7 +216,7 @@ class UserManagement extends React.Component {
                   onClick={() => { this.handleClick('invited') }}
                 />
                 <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>
               </div>
             </div>

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

@@ -8,7 +8,6 @@ import {
 import { toastError } from '../../../util/apiNotification';
 import { createSubscribedElement } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
-import AdminUsersContainer from '../../../services/AdminUsersContainer';
 
 class PasswordResetModal extends React.Component {
 
@@ -24,10 +23,9 @@ class PasswordResetModal extends React.Component {
   }
 
   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) {
       this.setState({ temporaryPassword: res.newPassword, isPasswordResetDone: true });
     }
@@ -37,14 +35,13 @@ class PasswordResetModal extends React.Component {
   }
 
   renderModalBodyBeforeReset() {
-    const { t, adminUsersContainer } = this.props;
-    const user = adminUsersContainer.state.userForPasswordResetModal;
+    const { t, userForPasswordResetModal } = this.props;
 
     return (
       <div>
         <p className="alert alert-danger">{t('admin:user_management.reset_password_modal.password_reset_message')}</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>
           {t('admin:user_management.reset_password_modal.new_password')}: <code>{this.state.temporaryPassword}</code>
@@ -54,8 +51,7 @@ class PasswordResetModal extends React.Component {
   }
 
   returnModalBodyAfterReset() {
-    const { t, adminUsersContainer } = this.props;
-    const user = adminUsersContainer.state.userForPasswordResetModal;
+    const { t, userForPasswordResetModal } = this.props;
 
     return (
       <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>
         </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>
         <button type="submit" className="btn btn-primary" onClick={this.resetPassword}>
           {t('admin:user_management.reset_password')}
@@ -76,18 +72,18 @@ class PasswordResetModal extends React.Component {
   returnModalFooter() {
     return (
       <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>
     );
   }
 
 
   render() {
-    const { t, adminUsersContainer } = this.props;
+    const { t } = this.props;
 
     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') }
         </ModalHeader>
         <ModalBody>
@@ -106,13 +102,17 @@ class PasswordResetModal extends React.Component {
  * Wrapper component for using unstated
  */
 const PasswordResetModalWrapper = (props) => {
-  return createSubscribedElement(PasswordResetModal, props, [AppContainer, AdminUsersContainer]);
+  return createSubscribedElement(PasswordResetModal, props, [AppContainer]);
 };
 
 PasswordResetModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   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);

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

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

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

@@ -56,7 +56,7 @@ class UserTable extends React.Component {
     }
 
     return (
-      <span className={`badge ${additionalClassName}`}>
+      <span className={`badge badge-pill ${additionalClassName}`}>
         {text}
       </span>
     );
@@ -71,7 +71,7 @@ class UserTable extends React.Component {
     const { t } = this.props;
 
     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/Me/ApiSettings.jsx

@@ -38,7 +38,7 @@ class ApiSettings extends React.Component {
     return (
       <React.Fragment>
 
-        <div className="mb-5 container-fluid">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">{ t('API Token Settings') }</h2>
         </div>
 

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

@@ -67,7 +67,7 @@ class ExternalAccountLinkedMe extends React.Component {
 
     return (
       <Fragment>
-        <div className="container-fluid p-0 my-4">
+        <div className="container-fluid my-4">
           <h2 className="border-bottom">
             <button type="button" className="btn btn-outline-secondary btn-sm pull-right" onClick={this.openAssociateModal}>
               <i className="icon-plus" aria-hidden="true" />
@@ -77,31 +77,27 @@ class ExternalAccountLinkedMe extends React.Component {
           </h2>
         </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
           isOpen={this.state.isAssociateModalOpen}

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

@@ -68,7 +68,7 @@ class PasswordSettings extends React.Component {
     return (
       <React.Fragment>
         {(!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)
             ? <h2 className="border-bottom">{t('personal_settings.update_password')}</h2>
           : <h2 className="border-bottom">{t('personal_settings.set_new_password')}</h2>}

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

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

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

@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
 
-import { isTrashPage } from '../../../../lib/util/path-utils';
+import { isTrashPage } from '@commons/util/path-utils';
+
 import { createSubscribedElement } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
 import RevisionPath from '../Page/RevisionPath';
@@ -15,56 +16,80 @@ import PageCreator from './PageCreator';
 import RevisionAuthor from './RevisionAuthor';
 
 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 {
-    pageId, path, createdAt, creator, updatedAt, revisionAuthor, isCompactMode,
+    pageId, path, createdAt, creator, updatedAt, revisionAuthor, isHeaderSticky, isSubnavCompact,
   } = 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);
+
+  console.log({
+    isPageForbidden, isPageNotFound, isPageInTrash, pageId, path,
+  });
+  // Display only the RevisionPath
+  if (isPageNotFound || isPageForbidden || isPageInTrash) {
     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>
     );
   }
 
+  const additionalClassNames = ['grw-subnavbar'];
+  if (isHeaderSticky) {
+    additionalClassNames.push('grw-subnavbar-sticky');
+  }
+  if (isSubnavCompact) {
+    additionalClassNames.push('grw-subnavbar-compact');
+  }
+
   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 */}
-      <div className="title-container mr-auto">
-        <h1>
+      <div>
+        <h1 className="m-0">
           <RevisionPath behaviorType={appContainer.config.behaviorType} pageId={pageId} pagePath={pageContainer.state.path} />
         </h1>
-        <TagLabels />
+        { !isPageNotFound && !isPageForbidden && (
+          <TagLabels />
+        ) }
       </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">
-              <RevisionAuthor revisionAuthor={revisionAuthor} updatedAt={updatedAt} isCompactMode={isCompactMode} />
+              <RevisionAuthor revisionAuthor={revisionAuthor} updatedAt={updatedAt} isCompactMode={isSubnavCompact} />
             </li>
-          )
-        }
-      </ul>
+          ) }
+        </ul>
+      </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 { withTranslation } from 'react-i18next';
-import { throttle } from 'throttle-debounce';
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
@@ -14,55 +13,47 @@ import UserPicture from '../User/UserPicture';
 const GrowiSubNavigationForUserPage = (props) => {
   const pageUser = JSON.parse(document.querySelector('#grw-subnav-for-user-page').getAttribute('data-page-user'));
   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 (
-    <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>
-
-          {/* Header Button */}
-          <BookmarkButton pageId={pageId} crowi={appContainer} size="lg" />
         </div>
-      </div>
-
 
+        <BookmarkButton pageId={pageId} crowi={appContainer} size="lg" />
+      </div>
     </div>
   );
 

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

@@ -16,7 +16,6 @@ class RevisionPath extends React.Component {
       pages: [],
       isListPage: false,
       isLinkToListPage: true,
-      isInTrash: false,
     };
 
     // retrieve xss library from window
@@ -60,12 +59,6 @@ class RevisionPath extends React.Component {
     const pages = [];
     const pagePaths = [];
     splitted.forEach((pageName) => {
-      // skip trash
-      if (pageName === 'trash' && splitted.length > 1) {
-        this.setState({ isInTrash: true });
-        return;
-      }
-
       pagePaths.push(encodeURIComponent(pageName));
       pages.push({
         pagePath: urljoin('/', ...pagePaths),
@@ -109,10 +102,10 @@ class RevisionPath extends React.Component {
       padding: '0 2px',
     };
 
-    const { isInTrash } = this.state;
+    const { isPageInTrash, isPageForbidden } = this.props;
     const pageLength = this.state.pages.length;
 
-    const rootElement = isInTrash
+    const rootElement = isPageInTrash
       ? (
         <>
           <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>
 
-        <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>
     );
   }
@@ -173,6 +168,15 @@ RevisionPath.propTypes = {
   behaviorType: PropTypes.string.isRequired,
   pagePath: PropTypes.string.isRequired,
   pageId: PropTypes.string,
+  isPageNotFound: PropTypes.bool,
+  isPageForbidden: PropTypes.bool,
+  isPageInTrash: PropTypes.bool,
+};
+
+RevisionPath.defaultProps = {
+  isPageNotFound: false,
+  isPageForbidden: false,
+  isPageInTrash: false,
 };
 
 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() {
     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">
           <span className="text-white">Edit Tags</span>
         </ModalHeader>

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

@@ -108,7 +108,7 @@ class TagLabels extends React.Component {
     });
 
     return (
-      <div className={`tag-viewer ${pageId ? 'existed-page' : 'new-page'}`}>
+      <div className="tag-labels">
         {tags.length === 0 && (
           <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>

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

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

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

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

+ 7 - 6
src/client/js/components/Sidebar/SidebarNav.jsx

@@ -3,9 +3,6 @@ import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
 
-import EditIcon from '@atlaskit/icon/glyph/edit';
-import TrayIcon from '@atlaskit/icon/glyph/tray';
-
 import {
   GlobalNav,
 } from '@atlaskit/navigation-next';
@@ -31,7 +28,7 @@ class SidebarNav extends React.Component {
     }
   }
 
-  generateSidebarItemObj(id, icon, label) {
+  generateSidebarItemObj(id, label, icon) {
     return {
       id,
       icon,
@@ -41,12 +38,16 @@ class SidebarNav extends React.Component {
     };
   }
 
+  generateIconFactory(classNames) {
+    return () => <i className={classNames}></i>;
+  }
+
   render() {
     return (
       <GlobalNav
         primaryItems={[
-          this.generateSidebarItemObj('custom', EditIcon, 'Custom Sidebar'),
-          this.generateSidebarItemObj('history', TrayIcon, 'History'),
+          this.generateSidebarItemObj('custom', 'Custom Sidebar', this.generateIconFactory('fa fa-2x fa-code')),
+          this.generateSidebarItemObj('history', 'History', this.generateIconFactory('icon-clock fa-2x')),
         ]}
         secondaryItems={[]}
       />

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

@@ -183,7 +183,10 @@ export default class AdminUsersContainer extends Container {
    * @memberOf AdminUsersContainer
    */
   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 toastr from 'toastr';
 
-import { throttle } from 'throttle-debounce';
-
 const logger = loggerFactory('growi:services:PageContainer');
-const scrollAmountForFixed = 50;
+const scrollThresForSticky = 50;
+const scrollThresForCompact = 100;
+const scrollThresForThrottling = 200;
 
 /**
  * Service container related to Page
@@ -58,7 +58,9 @@ export default class PageContainer extends Container {
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       isHackmdDraftUpdatingInRealtime: false,
-      isCompactMode: false,
+
+      isHeaderSticky: false,
+      isSubnavCompact: false,
     };
 
     this.initStateMarkdown();
@@ -69,9 +71,19 @@ export default class PageContainer extends Container {
     this.addWebSocketEventHandlers = this.addWebSocketEventHandlers.bind(this);
     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
    */
   getUploadedPictureSrc(user) {
+    if (user == null) {
+      return DEFAULT_IMAGE;
+    }
     if (user.image) {
       this.setState({ isUploadedPicture: true });
       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
  */

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

@@ -91,10 +91,6 @@ table,
  * Accentcolor (yellow)
  */
 
-header.affix {
-  border-bottom: 4px solid $accentcolor;
-}
-
 .modal {
   .modal-header {
     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;
 }
 
-/*
- * Accentcolor (green)
- */
-
-header.affix {
-  border-bottom: 4px solid $accentcolor;
-}
-
 .modal {
   .modal-header {
     border-bottom: 4px solid $accentcolor;

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

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

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

@@ -36,11 +36,15 @@
   }
 }
 
-/*
-  * header
-  */
-.grw-subnav {
-  overflow: unset;
+.grw-sidebar-header-container {
+  padding: 10px;
+
+  h3 {
+    margin-bottom: 0;
+  }
+}
+
+.grw-sidebar-content-container {
 }
 
 .grw-modal-head {
@@ -48,24 +52,6 @@
   border-bottom: 1px solid $grw-line-gray;
 }
 
-header#page-header {
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-
-  line-height: 1em;
-  // the container of h1
-  div.title-container {
-    padding-right: 5px;
-    padding-left: 5px;
-    margin-right: auto;
-  }
-
-  h1 {
-    @include variable-font-size(28px);
-    line-height: 1.1em;
-  }
-}
-
 .main {
   margin-top: 1rem;
 }

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

@@ -221,7 +221,7 @@ body.kibela {
 
     .card-footer {
       background: white;
-      border-top: 1px solid $border
+      border-top: 1px solid $border;
     }
   }
 

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

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

+ 4 - 4
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;
   }
 
@@ -97,7 +97,7 @@ body.on-edit {
 
     background: none;
 
-    > .grw-title-bar {
+    > .grw-subnav-container {
       width: 100%; //   for crowi layout
       padding: 0; //    for crowi layout
       pointer-events: initial; // enable pointer-events
@@ -117,7 +117,7 @@ body.on-edit {
       }
     }
 
-    .tag-viewer.new-page {
+    .tag-labels.new-page {
       display: block;
     }
   }

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

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

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

@@ -25,7 +25,7 @@
     }
   }
 
-  .tag-viewer.new-page {
+  .tag-labels.new-page {
     display: none;
   }
 }

+ 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;
-    }
-  }
-}

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

@@ -0,0 +1,97 @@
+$easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
+
+@mixin setTransitionForCompactMode() {
+  // set transition-duration (normal -> compact)
+  transition: all 300ms $easeInOutCubic;
+}
+
+/*
+ * layout for sticky
+ */
+.grw-header.sticky-top {
+  // Adjust to be on top of the growi subnavigation
+  z-index: $zindex-sticky - 100;
+  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 {
+    @include setTransitionForCompactMode();
+
+    h1 {
+      @include variable-font-size(18px);
+      @include setTransitionForCompactMode();
+    }
+  }
+}
+
+/*
+ * 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 {
+  .grw-header.sticky-top {
+    height: unset;
+  }
+
+  .list-tag-count {
+    background: rgba(0, 0, 0, 0.08);
+  }
+}
+
+.tag-labels {
   .manage-tags {
     font-size: 10px;
     cursor: pointer;
@@ -19,13 +29,7 @@
   }
 }
 
-#tags-page {
-  .list-tag-count {
-    background: rgba(0, 0, 0, 0.08);
-  }
-}
-
-#editTagModal {
+#edit-tag-modal {
   .form-control {
     height: auto;
   }

+ 54 - 29
src/client/styles/scss/_user.scss

@@ -1,4 +1,49 @@
-.user-page-header {
+$easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
+
+@mixin setTransitionForCompactMode() {
+  // 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;
+
+      @include setTransitionForCompactMode();
+    }
+    .picture {
+      width: 62px;
+      height: 62px;
+
+      @include setTransitionForCompactMode();
+    }
+    h1 {
+      font-size: 1.5em;
+      line-height: 30px;
+
+      @include setTransitionForCompactMode();
+    }
+    .users-meta {
+      margin-left: 15px;
+
+      @include setTransitionForCompactMode();
+    }
+  }
+}
+
+/*
+ * Styles
+ */
+.grw-subnavbar-user-page {
   #revision-path {
     margin-bottom: 0;
   }
@@ -14,19 +59,16 @@
   }
 
   .picture {
-    width: 64px;
-    height: 64px;
+    width: 72px;
+    height: 72px;
   }
 
-  .user-page-meta {
+  ul.user-page-meta {
+    padding-left: 0;
     color: #999;
 
-    ul {
-      padding-left: 0;
-
-      li {
-        list-style: none;
-      }
+    li {
+      list-style: none;
     }
 
     .user-page-username {
@@ -46,8 +88,8 @@
     }
   }
 
-  .btn-like,
-  .btn-bookmark {
+  .btn.btn-like,
+  .btn.btn-bookmark {
     &.btn-lg {
       padding: 8px;
       font-size: 1.5em;
@@ -55,23 +97,6 @@
   }
 }
 
-// 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 {
   .icon-container {
     .icon-copy,

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

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

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

@@ -1,4 +1,4 @@
-.btn-circle {
+.btn.btn-circle {
   width: 30px;
   height: 30px;
   padding: 6px 0;
@@ -26,12 +26,13 @@
   border-radius: 35px;
 }
 
-.btn-like,
-.btn-bookmark {
+.btn.btn-like,
+.btn.btn-bookmark {
   font-size: 1.2em;
-  line-height: 0.8em;
+  line-height: 1em;
 
-  &.active {
+  &.active,
+  &:hover {
     // header buttons are always white for active
     color: white !important;
   }

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

@@ -41,16 +41,16 @@
 @import 'layout_kibela';
 @import 'layout_variable';
 @import 'login';
+@import 'me';
 @import 'navbar';
 @import 'navbar_kibela';
 @import 'notification';
 @import 'on-edit';
 @import 'page_list';
 @import 'page';
-@import 'page_header';
-@import 'page_growi';
 @import 'search';
 @import 'shortcuts';
+@import 'subnav';
 @import 'tag';
 @import 'user';
 @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
  */
@@ -111,15 +104,6 @@ textarea.form-control {
   // border: 1px solid $border;
 }
 
-/*
- * GROWI header
- */
-header.affix {
-  .logo-mark {
-    fill: white;
-  }
-}
-
 /*
  * GROWI page list
  */
@@ -139,9 +123,24 @@ header.affix {
 /*
  * 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 {
 }
 
 /*

+ 15 - 12
src/client/styles/scss/theme/_apply-colors-light.scss

@@ -11,15 +11,6 @@
   background-color: darken($bgcolor-global, 5%);
 }
 
-/*
- * GROWI header
- */
-header.affix {
-  .logo-mark {
-    fill: theme-color('primary');
-  }
-}
-
 /*
  * GROWI search-top
  */
@@ -35,9 +26,21 @@ header.affix {
 /*
  * GROWI subnavigation
  */
-.grw-compact-subnavbar {
-  background-color: rgba(darken($bgcolor-global, 6%), 0.9);
-  box-shadow: 0 0 2px darken($bgcolor-global, 40%);
+.admin-page,
+.user-settings-page,
+.tags-page {
+  .grw-header {
+    background-color: rgba(darken($bgcolor-global, 6%), 0.9);
+  }
+}
+
+.grw-subnavbar {
+  background-color: rgba(darken($bgcolor-global, 5%), 1);
+
+  &.grw-subnavbar-sticky {
+    background-color: rgba(darken($bgcolor-global, 6%), 0.9);
+    box-shadow: 0 3px 2px -2px darken($bgcolor-global, 40%);
+  }
 }
 
 /*

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

@@ -79,21 +79,12 @@ $link-hover-color: $color-link-hover;
 }
 
 .grw-navbar {
-  // TODO: coloring
-  // background: $bgcolor-navbar;
-  // .nav-item > .nav-link {
-  //   color: white;
-  // }
-  background: darken($bgcolor-global, 8%);
+  background: $bgcolor-navbar;
   .nav-item > .nav-link {
-    color: #333;
+    color: $color-link-nabvar;
   }
 }
 
-.grw-title-bar {
-  background: darken($bgcolor-global, 2%);
-}
-
 .grw-sidebar {
   .grw-logo {
     background-color: darken($bgcolor-navbar, 10%);
@@ -215,13 +206,6 @@ $link-hover-color: $color-link-hover;
   }
 }
 
-/*
- * GROWI header
- */
-header.affix {
-  background: rgba(darken($bgcolor-global, 2%), 0.9);
-}
-
 /*
  * GROWI on-edit
  */
@@ -299,6 +283,14 @@ body.on-edit {
   }
 }
 
+/*
+ * react bootstrap typeahead
+ */
+mark.rbt-highlight-text {
+  // Temporarily the highlight color is black
+  color: black;
+}
+
 /*
  * GROWI page attachments
  */

+ 116 - 6
src/client/styles/scss/theme/antarctic.scss

@@ -1,8 +1,118 @@
-// import colors
-@import '../../agile-admin/inverse/colors/antarctic';
+@import '../variables';
+@import '../override-bootstrap-variables';
 
-// apply agile-admin theme
-@import '../../agile-admin/inverse/style';
+// == Define Bootstrap theme colors
+//
 
-// override
-@import 'override-agileadmin';
+// colors for overriding bootstrap $theme-colors
+// $secondary: #;
+// $info: #;
+// $success: #;
+// $warning: #;
+// $danger: #;
+// $light: #;
+// $dark: #;
+
+.growi:not(.login-page) {
+  // add background-image
+  #page-wrapper,
+  .page-editor-preview-container {
+    background-image: url('/images/themes/antarctic/bg.svg');
+    background-attachment: fixed;
+    background-position: center center;
+    background-size: cover;
+  }
+}
+
+.growi.login-page {
+  #page-wrapper {
+    background-image: url('/images/themes/antarctic/topimage.svg');
+    background-attachment: fixed;
+    background-position: center center;
+    background-size: cover;
+  }
+}
+
+$themecolor: #000080;
+$themelight: #f0f8ff;
+$accentcolor: #ffd700;
+
+.grw-navbar {
+  border-bottom: $accentcolor 4px solid;
+}
+
+//== Light Mode
+//
+html[light] {
+  $primary: $themecolor;
+
+  // Background colors
+  $bgcolor-global: $themelight;
+  $bgcolor-navbar: #334455;
+  $bgcolor-inline-code: #f9f2f4;
+  $bgcolor-card: #f5f5f5;
+
+  // Font colors
+  $color-global: black;
+  $color-reversal: #eeeeee;
+  // $color-header: #2b2b2b;
+  $color-link: lighten($primary, 20%);
+  $color-link-hover: lighten($color-link, 20%);
+  $color-link-wiki: lighten($primary, 20%);
+  $color-link-wiki-hover: lighten($color-link-wiki, 20%);
+  $color-link-nabvar: $color-reversal;
+  $color-inline-code: #c7254e;
+
+  // Logo colors
+  $fillcolor-logo-mark: lighten(desaturate($bgcolor-navbar, 10%), 15%);
+
+  // Border colors
+  $border-color-theme: #ccc; // former: `$navbar-border: #ccc;`
+
+  // Dropdown colors
+  $bgcolor-dropdown-link-active: $growi-blue;
+  $color-dropdown-link-active: $color-reversal;
+  $color-dropdown-link-hover: $color-global;
+
+  @import 'apply-colors';
+  @import 'apply-colors-light';
+}
+
+//== Dark Mode
+//
+html[dark] {
+  $primary: #d65a31;
+
+  $basecolor: #222831;
+
+  // Background colors
+  $bgcolor-global: $basecolor;
+  $bgcolor-navbar: #151515;
+  $bgcolor-inline-code: darken($basecolor, 5%);
+  $bgcolor-card: darken($basecolor, 5%);
+
+  // Font colors
+  $color-global: #eeeeee;
+  $color-reversal: #333333;
+  // $color-header: desaturate($primary, 20%);
+  $color-link: $primary;
+  $color-link-hover: lighten($color-link, 10%);
+  $color-link-wiki: lighten($basecolor, 50%);
+  $color-link-wiki-hover: darken($color-link-wiki, 5%);
+  $color-link-nabvar: $color-global;
+  $color-inline-code: #c7254e;
+
+  // Logo colors
+  $fillcolor-logo-mark: #444;
+
+  // Border colors
+  $border-color-theme: black; // former: `$navbar-border: #ccc;`
+
+  // Dropdown colors
+  $bgcolor-dropdown-link-active: $primary;
+  $color-dropdown-link-active: $color-global;
+  $color-dropdown-link-hover: $color-reversal;
+
+  @import 'apply-colors';
+  @import 'apply-colors-dark';
+}

+ 3 - 0
src/client/styles/scss/theme/default.scss

@@ -32,6 +32,7 @@ html[light] {
   $color-link-hover: lighten($color-link, 20%);
   $color-link-wiki: lighten($primary, 20%);
   $color-link-wiki-hover: lighten($color-link-wiki, 20%);
+  $color-link-nabvar: $color-reversal;
   $color-inline-code: #c7254e;
 
   // Logo colors
@@ -43,6 +44,7 @@ html[light] {
   // Dropdown colors
   $bgcolor-dropdown-link-active: $growi-blue;
   $color-dropdown-link-active: $color-reversal;
+  $color-dropdown-link-hover: $color-reversal;
 
   @import 'apply-colors';
   @import 'apply-colors-light';
@@ -69,6 +71,7 @@ html[dark] {
   $color-link-hover: lighten($color-link, 10%);
   $color-link-wiki: lighten($basecolor, 50%);
   $color-link-wiki-hover: darken($color-link-wiki, 5%);
+  $color-link-nabvar: $color-global;
   $color-inline-code: #c7254e;
 
   // Logo colors

+ 1 - 3
src/server/views/admin/Users_reserve.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('User_Management')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('User_Management') }}</h1>
-</header>
+<h1 class="title">{{ t('User_Management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 2 - 3
src/server/views/admin/app.html

@@ -6,11 +6,10 @@
 {% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('App settings') }}</h1>
-</header>
+<h1 class="title">{{ t('App settings') }}</h1>
 {% endblock %}
 
+
 {% block content_main %}
 <div class="content-main admin-app row">
   {% parent %}

+ 1 - 3
src/server/views/admin/customize.html

@@ -13,9 +13,7 @@
 {% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Customize') }}</h1>
-</header>
+<h1 class="title">{{ t('Customize') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/export.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Export Archive Data')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Export Archive Data') }}</h1>
-</header>
+<h1 class="title">{{ t('Export Archive Data') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/external-accounts.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('external_account_management')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('User_Management') }} / {{ t('external_account_management') }}</h1>
-</header>
+<h1 class="title">{{ t('User_Management') }} / {{ t('external_account_management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/global-notification-detail.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Notification settings')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Notification settings') }}</h1>
-</header>
+<h1 class="title">{{ t('Notification settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/importer.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Import Data')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Import Data') }}</h1>
-</header>
+<h1 class="title">{{ t('Import Data') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/index.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Management Wiki Home')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title"> {{ t('Management Wiki Home') }}</h1>
-</header>
+<h1 class="title"> {{ t('Management Wiki Home') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/markdown.html

@@ -4,9 +4,7 @@
  · {{ path }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Markdown Settings') }}</h1>
-</header>
+<h1 class="title">{{ t('Markdown Settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/notification.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Notification settings')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Notification settings') }}</h1>
-</header>
+<h1 class="title">{{ t('Notification settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/search.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('Full Text Search management')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('Full Text Search management') }}</h1>
-</header>
+<h1 class="title">{{ t('Full Text Search management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/security.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('security_settings')) }} · {% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('security_settings') }}</h1>
-</header>
+<h1 class="title">{{ t('security_settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/user-group-detail.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('UserGroup Management') + '/' + userGroup.name) | preventXss }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('UserGroup Management') + '/' + userGroup.name | preventXss }}</h1>
-</header>
+<h1 class="title">{{ t('UserGroup Management') + '/' + userGroup.name | preventXss }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/user-groups.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('UserGroup Management')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('UserGroup Management') }}</h1>
-</header>
+<h1 class="title">{{ t('UserGroup Management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 3
src/server/views/admin/users.html

@@ -3,9 +3,7 @@
 {% block html_title %}{{ customizeService.generateCustomTitle(t('User_Management')) }}{% endblock %}
 
 {% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('User_Management') }}</h1>
-</header>
+<h1 class="title">{{ t('User_Management') }}</h1>
 {% endblock %}
 
 {% block content_main %}

+ 1 - 1
src/server/views/layout-crowi/base/layout.html

@@ -31,7 +31,7 @@
   </aside>
 
   <div class="row grw-subnav">
-    <div class="col-md-9 grw-title-bar">
+    <div class="col-md-9">
       {% block content_header %}
       {% endblock %}
     </div>

+ 7 - 13
src/server/views/layout-growi/base/layout.html

@@ -7,20 +7,15 @@
 {% endblock %}
 
 {% block layout_main %}
-<div class="container-fluid">
-
-  <header class="sticky-top py-0" id="page-header">
-
-    <div id="grw-subnav" data-is-forbidden-page="{{ forbidden }}"></div>
-
-      {% if not page and not forbidden and ('/' === path or 'crowi' === getConfig('crowi', 'customize:behavior')) and not isUserPageList(path) and !isTrashPage() %}
-        {% if '/' === path.slice(-1) %}
-          {% include '../../widget/create_portal.html' %}
-        {% endif %}
-      {% endif %}
 
-  </header>
+{% block content_header_wrapper %}
+<header class="sticky-top py-0 grw-header">
+  {% block content_header %}
+  {% endblock %}
+</header>
+{% endblock %}
 
+<div class="container-fluid">
   <div class="row">
     <div id="main" class="main col-md-12 {% if page %}{{ css.grant(page) }}{% endif %} {% block main_css_class %}{% endblock %}">
       {% block content_main_before %}
@@ -33,7 +28,6 @@
       {% endblock %}
     </div><!-- /.main -->
   </div><!-- /.row -->
-
 </div><!-- /.container-fluid -->
 
 <footer class="footer">

+ 1 - 1
src/server/views/layout-growi/page_list.html

@@ -25,7 +25,7 @@
     </div>
 
     {# relocate #revision-toc #}
-    <div class="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container pr-2">
+    <div class="col-xl-2 col-lg-3 d-none d-lg-block revision-toc-container">
       {% include './widget/liker-and-seenusers.html' %}
       <div id="revision-toc" class="revision-toc mt-3 sps sps--abv" data-sps-offset="123">
         <div id="revision-toc-content" class="revision-toc-content"></div>

+ 8 - 6
src/server/views/layout-growi/user_page.html

@@ -5,12 +5,14 @@
   user-page
 {% endblock %}
 
-{% block content_header %}
-  {% if pageUser %}
-    <header id="grw-subnav-for-user-page" class="user-page-header" data-page-user="{{ pageUser|json }}"></header>
-  {% else %}
-    {% parent %}
-  {% endif %}
+{% block content_header_wrapper %}
+  <header class="sticky-top py-0 grw-header grw-header-user-page">
+    {% if pageUser %}
+      <div id="grw-subnav-for-user-page" class="grw-subnav" data-page-user="{{ pageUser|json }}"></div>
+    {% else %}
+      {% parent %}
+    {% endif %}
+  </header>
 {% endblock %}
 
 

+ 6 - 10
src/server/views/layout-growi/widget/header.html

@@ -1,11 +1,7 @@
-<header id="page-header">
+<div id="grw-subnav" class="grw-subnav" data-is-forbidden-page="{{ forbidden }}"></div>
 
-  <div id="grw-subnav" data-is-forbidden-page="{{ forbidden }}"></div>
-
-    {% if not page and not forbidden and ('/' === path or 'crowi' === getConfig('crowi', 'customize:behavior')) and not isUserPageList(path) and !isTrashPage() %}
-      {% if '/' === path.slice(-1) %}
-        {% include '../../widget/create_portal.html' %}
-      {% endif %}
-    {% endif %}
-
-</header>
+{% if not page and not forbidden and ('/' === path or 'crowi' === getConfig('crowi', 'customize:behavior')) and not isUserPageList(path) and !isTrashPage() %}
+  {% if '/' === path.slice(-1) %}
+    {% include '../../widget/create_portal.html' %}
+  {% endif %}
+{% endif %}

+ 2 - 2
src/server/views/layout-growi/widget/liker-and-seenusers.html

@@ -1,14 +1,14 @@
 <div class="liker-and-seenusers">
   <div class="text-right">
     {% if page.liker.length > 10 %}<span class="text-muted">..</span>{% endif %}
-    <span id="liker-list" class="mr-3" data-user-ids="{{ page.liker|slice(-10)|default([])|join(',') }}"></span>
+    <span id="liker-list" class="mr-3" data-user-ids="{{ page.liker|slice(-9)|default([])|join(',') }}"></span>
     <span class="text-info">
       <i class="icon-fw icon-like"></i><span class="liker-user-count">{{ page.liker.length|default(0) }}</span>
     </span>
   </div>
   <div class="text-right">
     {% if page.seenUsers.length > 10 %}<span class="text-muted">..</span>{% endif %}
-    <span id="seen-user-list" class="mr-3" data-user-ids="{{ page.seenUsers|slice(-10)|default([])|join(',') }}"></span>
+    <span id="seen-user-list" class="mr-3" data-user-ids="{{ page.seenUsers|slice(-9)|default([])|join(',') }}"></span>
     <span class="text-danger">
       <i class="icon-fw fa fa-paw"></i><span class="seen-user-count">{{ page.seenUsers.length|default(0) }}</span>
     </span>

+ 1 - 1
src/server/views/layout-kibela/base/layout.html

@@ -13,7 +13,7 @@
 
     <div id="main" class="main col-12 kibela-block bg-white round-corner {% if page %}{{ css.grant(page) }}{% endif %}{% block main_css_class %}{% endblock %}">
       <div class="row grw-subnav">
-        <div class="col-12 grw-title-bar">
+        <div class="col-12">
           {% block content_header %} {% endblock %}
         </div>
       </div>

+ 1 - 1
src/server/views/layout-kibela/user_page.html

@@ -7,7 +7,7 @@
 
 {% block content_header %}
   {% if pageUser %}
-    <header id="grw-subnav-for-user-page" class="user-page-header" data-page-user="{{ pageUser|json }}"></header>
+    <header id="grw-subnav-for-user-page" class="grw- subnav grw-subnav-user-page" data-page-user="{{ pageUser|json }}"></header>
   {% else %}
     {% parent %}
   {% endif %}

+ 0 - 85
src/server/views/me/api_token.html

@@ -1,85 +0,0 @@
-{% extends '../layout-growi/base/layout.html' %}
-
-
-{% block html_title %}{{ customizeService.generateCustomTitle(t('API Settings')) }}{% endblock %}
-
-
-{% block content_header %}
-<header id="page-header">
-  <h1 id="admin-title" class="title">{{ t('API Settings') }}</h1>
-</header>
-{% endblock %}
-
-{% block content_main %}
-<div class="content-main">
-
-  <ul class="nav nav-tabs">
-    <li><a href="/me"><i class="icon-user"></i> {{ t('User Information') }}</a></li>
-    <li><a href="/me/external-accounts"><i class="icon-share-alt"></i> {{ t('External Accounts') }}</a></li>
-    <li><a href="/me/password"><i class="icon-lock"></i> {{ t('Password Settings') }}</a></li>
-    <li class="active"><a href="/me/apiToken"><i class="icon-paper-plane"></i> {{ t('API Settings') }}</a></li>
-  </ul>
-
-  <div class="tab-content">
-
-  {% set message = req.flash('successMessage') %}
-  {% if message.length %}
-  <div class="alert alert-success grw-mt-10px">
-    {{ message }}
-  </div>
-  {% endif %}
-
-  {% if req.form.errors.length > 0 %}
-  <div class="alert alert-danger grw-mt-10px">
-    <ul>
-    {% for error in req.form.errors %}
-      <li>{{ error }}</li>
-    {% endfor %}
-    </ul>
-  </div>
-  {% endif %}
-
-  <div class="form-box m-t-20">
-
-    <form action="/me/apiToken" method="post" class="form-horizontal" role="form">
-    <fieldset>
-      <legend>{{ t('API Token Settings') }}</legend>
-      <div class="form-group {% if not user.password %}has-error{% endif %}">
-        <label for="" class="col-xs-3 control-label">{{ t('Current API Token') }}</label>
-        <div class="col-xs-6">
-          {% if user.apiToken %}
-            <input class="form-control" type="text" value="{{ user.apiToken }}">
-          {% else %}
-          <p class="form-control-static">
-            {{ t('page_me_apitoken.notice.apitoken_issued') }}
-          </p>
-          {% endif %}
-        </div>
-      </div>
-
-      <div class="form-group">
-        <div class="col-xs-offset-3 col-xs-9">
-
-          <p class="alert alert-warning">
-            {{ t('page_me_apitoken.notice.update_token1') }}<br>
-            {{ t('page_me_apitoken.notice.update_token2') }}
-          </p>
-
-          <button type="submit" value="1" name="apiTokenForm[confirm]" class="btn btn-primary">{{ t('Update API Token') }}</button>
-        </div>
-      </div>
-
-    </fieldset>
-    </form>
-  </div>
-
-
-  </div>
-</div>
-{% endblock content_main %}
-
-{% block content_footer %}
-{% endblock %}
-
-{% block layout_footer %}
-{% endblock %}

+ 0 - 249
src/server/views/me/external-accounts.html

@@ -1,249 +0,0 @@
-{% extends '../layout-growi/base/layout.html' %}
-
-{% block html_title %}{{ customizeService.generateCustomTitle(t('user_management.external_account')) }}{% endblock %}
-
-{% block content_header %}
-<header id="page-header">
-  <h1 id="mypage-title" class="title">{{ t('user_management.external_account') }}</h1>
-</header>
-{% endblock %}
-
-{% block content_main %}
-<div class="content-main">
-
-  <ul class="nav nav-tabs">
-    <li><a href="/me"><i class="icon-user"></i> {{ t('User Information') }}</a></li>
-    <li class="active"><a href="/me/external-accounts"><i class="icon-share-alt"></i> {{ t('External Accounts') }}</a></li>
-    <li><a href="/me/password"><i class="icon-lock"></i> {{ t('Password Settings') }}</a></li>
-    <li><a href="/me/apiToken"><i class="icon-paper-plane"></i> {{ t('API Settings') }}</a></li>
-  </ul>
-
-  <div class="tab-content">
-
-  {% set couldntDisassociateError = req.flash('couldntDisassociateError') %}
-  {% if couldntDisassociateError != null %}
-  <div class="alert alert-danger grw-mt-10px">
-    <b>Couldn't disassociate External Account</b><br>
-    You have not set a password and have only one External Account.
-  </div>
-  {% endif %}
-
-  {% set error = req.flash('errorMessage') %}
-  {% if error.length %}
-  {% for e in error %}
-  <div class="alert alert-danger grw-mt-10px">
-    <b>Server Error occured:</b><br>
-    {{ e }}
-  </div>
-  {% endfor %}
-  {% endif %}
-
-  {% set warn = req.flash('warningMessage') %}
-  {% if warn.length %}
-  {% for w in warn %}
-  <div class="alert alert-warning grw-mt-10px">
-    {{ w }}
-  </div>
-  {% endfor %}
-  {% endif %}
-
-  {% set message = req.flash('successMessage') %}
-  {% if message.length %}
-  <div class="alert alert-success grw-mt-10px">
-    <b>{{ message }}</b>
-  </div>
-  {% endif %}
-
-
-
-  <legend class="m-t-20" style="line-height: 1.7em;">
-    <button class="btn btn-default btn-sm float-right" data-target="#create-external-account" data-toggle="modal">
-      <i class="icon-plus" aria-hidden="true"></i>
-      Add
-    </button>
-    {{ t('External Accounts') }}
-  </legend>
-
-  <div class="row">
-    <div class="col-md-12">
-      <table class="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>
-          {% for account in externalAccounts %}
-          <tr>
-            <td>{{ account.providerType }}</td>
-            <td>
-              <strong>{{ account.accountId }}</strong>
-            </td>
-            <td>{{ account.createdAt|datetz('Y-m-d') }}</td>
-            <td class="text-center">
-              <button class="btn btn-default btn-sm btn-danger"
-                  data-toggle="modal" data-target="#diassociate-external-account" data-provider-type="{{ account.providerType }}" data-account-id="{{ account.accountId }}">
-                <i class="ti-unlink"></i>
-                {{ t('Diassociate') }}
-              </button>
-            </td>
-          </tr>
-          {% endfor %}
-        </tbody>
-      </table>
-    </div>
-  </div>
-
-  {# modal #}
-  <style>
-    .modal.create-external-account .modal-dialog {
-      width: 750px;
-    }
-  </style>
-  <div class="modal create-external-account" id="create-external-account">
-    <div class="modal-dialog">
-      <div class="modal-content">
-
-        <div class="modal-header bg-info">
-          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-          <div class="modal-title">{{ t('Create External Account') }}</div>
-        </div>
-
-        <div class="modal-body">
-
-          <ul class="nav nav-tabs passport-settings" role="tablist">
-            <li class="active">
-              <a href="#passport-ldap" data-toggle="tab" role="tab"><i class="fa fa-sitemap"></i> LDAP</a>
-            </li>
-            <li class="tbd">
-              <a href="#passport-github" data-toggle="tab" role="tab"><i class="fa fa-github"></i> (TBD) GitHub</a>
-            </li>
-            <li class="tbd">
-              <a href="#passport-google-oauth" data-toggle="tab" role="tab"><i class="fa fa-google"></i> (TBD) Google OAuth</a>
-            </li>
-            <li class="tbd">
-              <a href="#passport-facebook" data-toggle="tab" role="tab"><i class="fa fa-facebook"></i> (TBD) Facebook</a>
-            </li>
-            <li class="tbd">
-              <a href="#passport-twitter" data-toggle="tab" role="tab"><i class="fa fa-twitter"></i> (TBD) Twitter</a>
-            </li>
-          </ul>
-
-          <div class="tab-content passport-settings mt-4">
-            <div id="passport-ldap" class="tab-pane active" role="tabpanel" >
-              <div id="formLdapAssociationContainer">
-                {% include '../widget/passport/ldap-association-tester.html' %}
-                <div class="clearfix">
-                  <button type="button" class="btn btn-info float-right" onclick="associateLdap()">
-                    <i class="fa fa-plus-circle" aria-hidden="true"></i>
-                    {{ t('add') }}
-                  </button>
-                </div>
-              </div>
-            </div>
-
-            <div id="passport-google-oauth" class="tab-pane" role="tabpanel">
-              (TBD)
-            </div>
-
-            <div id="passport-github" class="tab-pane" role="tabpanel">
-              (TBD)
-            </div>
-
-            <div id="passport-facebook" class="tab-pane" role="tabpanel">
-              (TBD)
-            </div>
-
-            <div id="passport-twitter" class="tab-pane" role="tabpanel">
-              (TBD)
-            </div>
-
-          </div><!-- /.tab-content -->
-
-        </div><!-- /.modal-body -->
-
-      </div><!-- /.modal-content -->
-    </div><!-- /.modal-dialog -->
-
-    <script>
-      /**
-       * associate (submit the form)
-       */
-      function associateLdap() {
-        var $form = $('#formLdapAssociationContainer > form');
-        var $action = '/me/external-accounts/associateLdap';
-        $form.attr('action', $action);
-        $form.submit();
-      }
-    </script>
-
-  </div><!-- /.modal -->
-
-  <div class="modal diassociate-external-account" id="diassociate-external-account">
-    <div class="modal-dialog">
-      <div class="modal-content">
-
-        <div class="modal-header">
-          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-          <div class="modal-title">{{ t('Diassociate External Account') }}</div>
-        </div>
-
-        <div class="modal-body">
-          <div class="row">
-            <div class="col-md-12">
-              <p><b>
-                Are you sure to diassociate the
-                <span class="diassociate-provider-type"></span> account
-                <code class="diassociate-account-id"></code>?
-              </b></p>
-            </div>
-          </div>
-        </div>
-
-        <div class="modal-footer">
-          <form action="/me/external-accounts/disassociate" method="post">
-            <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <input type="hidden" name="providerType">
-            <input type="hidden" name="accountId">
-            <button type="button" class="btn btn-sm btn-default" data-dismiss="modal">
-              {{ t('Cancel') }}
-            </button>
-            <button type="submit" class="btn btn-sm btn-danger">
-              <i class="ti-unlink"></i>
-              {{ t('Diassociate') }}
-            </button>
-          </form>
-        </div>
-      </div><!-- /.modal-content -->
-    </div><!-- /.modal-dialog -->
-
-    <script>
-      $('#diassociate-external-account').on('show.bs.modal', function (event) {
-        var modal = $(this);
-        var button = $(event.relatedTarget); // Button that triggered the modal
-        // get data-*
-        var providerType = button.data('provider-type');
-        var accountId = button.data('account-id');
-        // set labels
-        modal.find('.diassociate-provider-type').text(providerType);
-        modal.find('.diassociate-account-id').text(accountId);
-        // set hidden inputs
-        modal.find('input:hidden[name="providerType"]').val(providerType);
-        modal.find('input:hidden[name="accountId"]').val(accountId);
-      })
-    </script>
-  </div><!-- /.modal -->
-
-</div>
-{% endblock content_main %}
-
-{% block content_footer %}
-{% endblock %}
-
-{% block layout_footer %}
-{% endblock %}

+ 4 - 354
src/server/views/me/index.html

@@ -2,364 +2,14 @@
 
 {% block html_title %}{{ customizeService.generateCustomTitle(t('User Settings')) }}{% endblock %}
 
+{% block html_base_css %}user-settings-page{% endblock %}
+
 {% block content_header %}
-<header id="page-header">
-  <h1 id="mypage-title" class="title">{{ t('User Settings') }}</h1>
-</header>
+<h1 class="title">{{ t('User Settings') }}</h1>
 {% endblock %}
 
 {% block content_main %}
-<div class="content-main" id="personal-setting">
-
-  <ul class="nav nav-tabs mb-4" role="tablist">
-    <li class="nav-item">
-      <a class="nav-link active" href="/me" role="tab" data-toggle="tab"><i class="icon-user"></i> {{ t('User Information') }}</a>
-    </li>
-    <li class="nav-item">
-      <a class="nav-link" href="/me/external-accounts" role="tab" data-toggle="tab"><i class="icon-share-alt"></i> {{ t('External Accounts') }}</a>
-    </li>
-    <li class="nav-item">
-      <a class="nav-link" href="/me/password" role="tab" data-toggle="tab"><i class="icon-lock"></i> {{ t('Password Settings') }}</a>
-    </li>
-    <li class="nav-item">
-      <a class="nav-link" href="/me/apiToken" role="tab" data-toggle="tab"><i class="icon-paper-plane"></i> {{ t('API Settings') }}</a>
-    </li>
-  </ul>
-
-  <div class="tab-content">
-
-  {% set smessage = req.flash('successMessage') %}
-  {% if smessage.length %}
-  <div class="alert alert-success grw-mt-10px">
-    {{ smessage }}
-  </div>
-  {% endif %}
-
-  {% set wmessage = req.flash('warningMessage') %}
-  {% if wmessage.length %}
-  <div class="alert alert-danger grw-mt-10px">
-    {{ wmessage }}
-  </div>
-  {% endif %}
-
-  {% if req.form.errors.length > 0 %}
-  <div class="alert alert-danger grw-mt-10px">
-    <ul>
-    {% for error in req.form.errors %}
-      <li>{{ error }}</li>
-    {% endfor %}
-    </ul>
-  </div>
-  {% endif %}
-
-
-  <div class="form-box mt-3">
-    <form action="/me" method="post" role="form">
-      <fieldset>
-        <legend class="border-bottom mb-4">{{ t('Basic Info') }}</legend>
-        <div class="form-group row">
-          <label for="userForm[name]" class="col-sm-2 col-form-label">Name</label>
-          <div class="col-sm-4">
-            <input type="text" class="form-control" name="userForm[name]" value="{{ user.name }}" required>
-          </div>
-        </div>
-        <div class="form-group">
-          <div class="row">
-            <label for="userForm[email]" class="col-sm-2 col-form-label">Email</label>
-            <div class="col-sm-4">
-              <input type="email" class="form-control" name="userForm[email]" value="{{ user.email }}" aria-describedby="userForm[email]" required>
-            </div>
-          </div>
-          <div class="offset-sm-2 col-sm-10">
-            {% if getConfig('crowi', 'security:registrationWhiteList') && getConfig('crowi', 'security:registrationWhiteList').length %}
-            <p id="userForm[email]" class="form-text text-muted">
-              {{ t('page_register.form_help.email') }}
-            <ul>
-              {% for em in getConfig('crowi', 'security:registrationWhiteList') %}
-              <li><code>{{ em }}</code></li>
-              {% endfor %}
-            </ul>
-            </p>
-            {% endif %}
-          </div>
-        </div>
-        <div class="form-group row">
-          <label for="userForm[isEmailPublished]" class="col-sm-2 col-form-label">{{ t('Disclose E-mail') }}</label>
-          <div class="col-sm-4">
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioEmailShow"
-                name="userForm[isEmailPublished]"
-                value="{{ true }}"
-                {% if user.isEmailPublished == true %}checked="checked"{% endif %}
-                class="custom-control-input"
-              > 
-              <label class="custom-control-label" for="radioEmailShow">{{ t('Show') }}</label>
-            </div>
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioEmailHide"
-                name="userForm[isEmailPublished]"
-                value="{{ false }}"
-                {% if user.isEmailPublished == false %}checked="checked"{% endif %}
-                class="custom-control-input"
-              >
-              <label class="custom-control-label" for="radioEmailHide">{{ t('Hide') }}</label>
-            </div>
-          </div>
-        </div>
-        <div class="form-group row {% if not user.lang %}has-error{% endif %}">
-          <label for="userForm[lang]" class="col-sm-2 col-form-label">Language</label>
-          <div class="col-sm-4">
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioLangEn"
-                name="userForm[lang]"
-                value="{{ consts.language.LANG_EN_US }}"
-                class="custom-control-input"
-                {% if user.lang == consts.language.LANG_EN_US %}checked="checked"{% endif %}
-              >
-              <label class="custom-control-label" for="radioLangEn">{{ t('English') }}</label>
-            </div>
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioLangJa"
-                name="userForm[lang]"
-                value="{{ consts.language.LANG_JA }}"
-                class="custom-control-input"
-                {% if user.lang == consts.language.LANG_JA %}checked="checked"{% endif %}
-              >
-              <label class="custom-control-label" for="radioLangJa">{{ t('Japanese') }}</label>
-            </div>
-          </div>
-        </div>
-
-        <div class="form-group row">
-          <div class="offset-sm-2 col-sm-10">
-            <input type="hidden" name="_csrf" value="{{ csrf() }}">
-            <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
-          </div>
-        </div>
-      </fieldset>
-    </form>
-  </div>
-
-  <div class="form-box mt-3">
-
-    <!-- separeted form tag -->
-    <form action="/me/imagetype" id="formImageType" method="post" class="form" role="form"></form>
-
-    <fieldset>
-      <legend class="border-bottom mb-5">{{ t('Set Profile Image') }}</legend>
-      <div class="form-group row">
-        <div class="col-sm-4 offset-sm-1">
-          <h4>
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioGravatar"
-                form="formImageType"
-                name="imagetypeForm[isGravatarEnabled]"
-                value="true"
-                {% if user.isGravatarEnabled %}checked="checked"{% endif %}
-                class="custom-control-input"
-              >
-              <label class="custom-control-label custom-control-inline" for="radioGravatar">
-                <img src="https://gravatar.com/avatar/00000000000000000000000000000000?s=24" />
-                <span class="pl-1">Gravatar</span>
-              </label>
-              <a href="https://gravatar.com/">
-                <small>
-                  <i class="icon-arrow-right-circle" aria-hidden="true"></i>
-                </small>
-              </a>
-            </div>
-          </h4>
-  
-          <img src="{{ user|gravatar }}" width="64">
-        </div>
-        <div class="col-sm-7">
-          <h4>
-            <div class="custom-control custom-radio custom-control-inline">
-              <input
-                type="radio"
-                id="radioUploadPicture"
-                form="formImageType"
-                name="imagetypeForm[isGravatarEnabled]"
-                value="false"
-                {% if !user.isGravatarEnabled  %}checked="checked"{% endif %}
-                class="custom-control-input"
-              >
-              <label for="radioUploadPicture" class="custom-control-label">{{ t('Upload Image') }}</label>
-            </div>
-          </h4>
-          <div class="form-group">
-            <div id="pictureUploadFormMessage" class=""></div>
-            <div class="row">
-              <label for="" class="col-sm-4">{{ t('Current Image') }}</label>
-              <div class="col-sm-8">
-                <p><img src="{{ user|uploadedpicture }}" class="picture picture-lg rounded-circle" id="settingUserPicture"></p>
-                <form
-                  id="remove-attachment"
-                  action="/_api/attachments.removeProfileImage"
-                  method="post"
-                  class="form-horizontal"
-                  style="{% if not user.imageAttachment %}display: none{% endif %}"
-                >
-                  <input type="hidden" name="_csrf" value="{{ csrf() }}">
-                  <button type="submit" class="btn btn-danger">{{ t('Delete Image') }}</button>
-                </form>
-              </div>
-            </div>
-          </div>
-        </h4>
-
-        <img src="{{ user|gravatar }}" width="64">
-      </div><!-- /.col-sm* -->
-
-      <div class="form-group col-md-4 col-sm-7">
-        <h4>
-          <div class="radio radio-primary">
-            <input type="radio" id="radioUploadPicture" form="formImageType" name="imagetypeForm[isGravatarEnabled]" value="false" {% if !user.isGravatarEnabled  %}checked="checked"{% endif %}>
-            <label for="radioUploadPicture">
-              {{ t('Upload Image') }}
-            </label>
-          </div>
-        </h4>
-        <div class="form-group">
-          <div id="pictureUploadFormMessage"></div>
-          <label for="" class="col-sm-4 control-label">
-            {{ t('Current Image') }}
-          </label>
-          <div class="col-sm-8">
-            <p>
-            <img src="{{ user|uploadedpicture }}" class="picture picture-lg img-circle" id="settingUserPicture"><br>
-            </p>
-            <p>
-            <form id="remove-attachment" action="/_api/attachments.removeProfileImage" method="post" class="form-horizontal"
-                style="{% if not user.imageAttachment %}display: none{% endif %}">
-              <input type="hidden" name="_csrf" value="{{ csrf() }}">
-              <button type="submit" class="btn btn-danger">{{ t('Delete Image') }}</button>
-            </form>
-            </p>
-          </div>
-        </div><!-- /.form-group -->
-
-        <div class="form-group">
-          <div id="profile-image-uploader"></div>
-        </div>
-      </div>
-
-      <div class="form-group">
-        <div class="offset-sm-4 col-sm-6">
-          <button type="submit" form="formImageType" class="btn btn-primary">{{ t('Update') }}</button>
-        </div>
-      </div>
-    </fieldset>
-  </div><!-- /.form-box -->
-
-  <script>
-    $("#pictureUploadForm input[name=profileImage]").on('change', function(){
-      if ($(this).val() == '') {
-        return false;
-      }
-
-      var $form = $('#pictureUploadForm');
-      var formData = new FormData();
-      formData.append('file', this.files[0]);
-      formData.append('_csrf', document.getElementsByName("_csrf")[0].value);
-
-      $('#pictureUploadFormProgress').html('<div class="speeding-wheel-sm mr-2"></div> アップロード中...');
-      $.ajax($form.attr("action"), {
-        type: 'post',
-        processData: false,
-        contentType: false,
-        data: formData
-      })
-      .then(function(data) {
-        if (data.ok) {
-          var attachment = data.attachment;
-          $('#settingUserPicture').attr('src', attachment.filePathProxied + '?time=' + (new Date()));
-          $('form#remove-attachment').show();
-          $('form#remove-attachment input[name=attachment_id]').val(attachment.id);
-          $('#pictureUploadFormMessage')
-            .addClass('alert alert-success')
-            .html('変更しました');
-        }
-        else {
-          throw new Error('statis is invalid');
-        }
-      })
-      .catch(function(err) {
-        $('#pictureUploadFormMessage')
-          .addClass('alert alert-danger')
-          .html('変更中にエラーが発生しました。');
-      })
-      // finally
-      .then(function() {
-        $('#pictureUploadFormProgress').html('');
-      });
-      return false;
-    });
-
-    $('form#remove-attachment').on('submit', function(event) {
-      // process with jQuery
-      event.preventDefault();
-
-      $.post($(this).attr('action'), $(this).serializeArray())
-      .then(function(data) {
-        if (data.ok) {
-          $('#settingUserPicture').attr('src', '/images/icons/user.svg');
-          $('form#remove-attachment').hide();
-        }
-        else {
-          throw new Error('statis is invalid');
-        }
-      })
-      .catch(function(err) {
-        $('#pictureUploadFormMessage')
-          .addClass('alert alert-danger')
-          .html('変更中にエラーが発生しました。');
-      })
-    });
-  </script>
-
-  </div> {# end of .tab-contents #}
-
-  {#
-  <div class="form-box">
-    <form action="/me/username" method="post" class="form-horizontal" role="form">
-      <fieldset>
-        <legend>ユーザーID (ユーザー名) の変更</legend>
-      <div class="form-group">
-        <label for="userNameForm[username]" class="col-sm-2 control-label">ユーザーID</label>
-        <div class="col-sm-4">
-          <input class="form-control" type="text" name="userNameForm[username]" value="{{ user.username }}" required>
-          <p class="form-text text-muted">すべてのマイページの</p>
-        </div>
-      </div>
-
-      <div class="form-group">
-        <div class="col-sm-offset-2 col-sm-10">
-          <p class="alert alert-warning">
-          ユーザーIDを変更すると、<code>/user/{{ user.username }}</code> 以下のページがすべて <code>/user/新しいユーザーID</code> の下に移動されます。<br>
-          また、これまでのページにリダイレクトは設定されず、この操作の取り消しもできません。<br>
-          実行には十分に注意をしてください。
-          </p>
-          <button type="submit" class="btn btn-warning">ユーザーIDの変更を実行する</button>
-        </div>
-      </div>
-    </fieldset>
-    </form>
-  </div>
-  #}
-
-  </div>
-</div>
+<div class="content-main" id="personal-setting"></div>
 {% endblock content_main %}
 
 {% block content_footer %}

+ 8 - 9
src/server/views/tags.html

@@ -1,16 +1,15 @@
 {% extends 'layout/layout.html' %}
 
+{% block html_title %}{{ customizeService.generateCustomTitle(t('Tags')) }}{% endblock %}
+
+{% block html_base_css %}tags-page{% endblock %}
+
 {% block layout_main %}
+<header class="sticky-top py-0 grw-header">
+  <h1 class="title">{{ t('Tags') }}</h1>
+</header>
+
 <div class="container-fluid">
-  <div class="row grw-subnav">
-    <div class="col-xs-12 grw-title-bar">
-      {% block content_header %}
-      <header id="page-header">
-        <h1 id="admin-title" class="title">{{ t('Tags') }}</h1>
-      </header>
-      {% endblock %}
-    </div>
-  </div>
   <div class="row">
     <div id="main" class="main mt-3 col-md-12 tags-page">
       <div class="" id="tags-page"></div>

+ 27 - 25
src/server/views/widget/page_tabs.html

@@ -11,31 +11,33 @@
   </li>
 
   {% if !isTrashPage() %}
-  <li class="nav-item grw-main-nav-item-left grw-nav-item-edit">
-    <a
-      {% if user %} href="#edit" data-toggle="tab" class="nav-link edit-button" {% endif %}
-      {% if not user %}
-        class="edit-button edit-button-disabled"
-        data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"
-      {% endif %}
-    >
-      <i class="icon-note"></i> {{ t('Edit') }}
-    </a>
-  </li>
-  {% if isHackmdSetup() %}
-  <li class="nav-item grw-main-nav-item-left grw-nav-tab-hackmd">
-    <a
-      {% if user %} href="#hackmd" data-toggle="tab" class="nav-link edit-button" {% endif %}
-      {% if not user %}
-        class="edit-button edit-button-disabled"
-        data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"
-      {% endif %}
-    >
-      <i class="fa fa-file-text-o"></i> {{ t('HackMD') }}
-    </a>
-  </li>
-  {% endif %}
-    <div class="grw-compact-subnav d-none ml-2">
+    <li class="nav-item grw-main-nav-item-left grw-nav-item-edit">
+      <a
+        {% if user %} href="#edit" data-toggle="tab" class="nav-link edit-button" {% endif %}
+        {% if not user %}
+          class="edit-button edit-button-disabled"
+          data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"
+        {% endif %}
+      >
+        <i class="icon-note"></i> {{ t('Edit') }}
+      </a>
+    </li>
+
+    {% if isHackmdSetup() %}
+    <li class="nav-item grw-main-nav-item-left grw-nav-tab-hackmd">
+      <a
+        {% if user %} href="#hackmd" data-toggle="tab" class="nav-link edit-button" {% endif %}
+        {% if not user %}
+          class="edit-button edit-button-disabled"
+          data-toggle="tooltip" data-placement="top" data-container="body" title="{{ t('Not available for guest') }}"
+        {% endif %}
+      >
+        <i class="fa fa-file-text-o"></i> {{ t('HackMD') }}
+      </a>
+    </li>
+    {% endif %}
+
+    <div class="grw-revision-path-for-edit d-none ml-2">
       <h4 id="revision-path" class="mb-0"></h4>
       <div id="tag-label"></div>
     </div>

+ 14 - 27
yarn.lock

@@ -90,14 +90,6 @@
     react-addons-text-content "^0.0.4"
     uuid "^3.1.0"
 
-"@atlaskit/logo@^12.3.3":
-  version "12.3.3"
-  resolved "https://registry.yarnpkg.com/@atlaskit/logo/-/logo-12.3.3.tgz#dd329f35f32e84ff9b135ffa566ca19ba31c9d7d"
-  integrity sha512-xeuseG2xDRmja+O9Wa1lib7VFkBMGBdC1N/L+lPcazL+hWEJou+garwZ2Ir6FQktqCr5/yk5AAPMn11GNLseSA==
-  dependencies:
-    react-uid "^2.2.0"
-    tslib "^1.9.3"
-
 "@atlaskit/navigation-next@^8.0.2":
   version "8.0.2"
   resolved "https://registry.yarnpkg.com/@atlaskit/navigation-next/-/navigation-next-8.0.2.tgz#045839d292d21ab01655325f6942e587c00a311d"
@@ -1656,10 +1648,10 @@
     "@types/yargs" "^15.0.0"
     chalk "^3.0.0"
 
-"@kaishuu0123/markdown-it-fence@0.1.4", "@kaishuu0123/markdown-it-fence@^0.1.4":
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/@kaishuu0123/markdown-it-fence/-/markdown-it-fence-0.1.4.tgz#759cf0dd80cca23a08e70b9cbb33c999cb23f3c3"
-  integrity sha512-u00GhVLpTeIbeflMKCozzaCAEmuwGngryomtbsYoyRwdYLfrH2nJHZa41+gwKLXsrq7Ii3N+Rei5GHaqHT3j5A==
+"@kaishuu0123/markdown-it-fence@^0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@kaishuu0123/markdown-it-fence/-/markdown-it-fence-0.2.0.tgz#f46722bfce4ab7eb3e051def5090dcae1bd6e36b"
+  integrity sha512-mdqKA+bXfJPl7gAg9tis8fGlea2oppBM068YbMDSXKWM6H18nVSZLrVKPHXpPWBgSv1ceeKkoWj8K1ntpIHlrw==
 
 "@lykmapipo/common@>=0.21.0":
   version "0.21.0"
@@ -8905,23 +8897,23 @@ markdown-escapes@^1.0.0:
   resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.3.tgz#6155e10416efaafab665d466ce598216375195f5"
   integrity sha512-XUi5HJhhV5R74k8/0H2oCbCiYf/u4cO/rX8tnGkRvrqhsr5BRNU6Mg0yt/8UIx1iIS8220BNJsDb7XnILhLepw==
 
-markdown-it-blockdiag@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/markdown-it-blockdiag/-/markdown-it-blockdiag-1.0.3.tgz#7c2be967d21a17f559da5860b6179b34f29192a3"
-  integrity sha512-2y4C5L6V3twWhDdQLl3zzYvBiumKcS7D1CD8/OTN2Y57G1gE2ot86vTPrfljaf2Q8q6J1UgcG40zTjDGcHgHrg==
+markdown-it-blockdiag@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/markdown-it-blockdiag/-/markdown-it-blockdiag-1.1.1.tgz#f89d9a56c4ef693f4cec88fc62655fce23d15115"
+  integrity sha512-e8IvRZE7hS0eQqNMyJ8l3EI89swTUVvIkQ8MlmLA0DacumMauHCAfKgzxcZ2UMSj2wQPCmUUM9L9iAOAFcAByw==
   dependencies:
-    "@kaishuu0123/markdown-it-fence" "0.1.4"
+    "@kaishuu0123/markdown-it-fence" "^0.2.0"
     pako "^1.0.6"
     paths "^0.1.1"
     url-join "^4.0.0"
     utf8-bytes "0.0.1"
 
-markdown-it-drawio-viewer@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/markdown-it-drawio-viewer/-/markdown-it-drawio-viewer-1.1.3.tgz#e5f7fa9a1d200a8711e2aeb691551add413ffe33"
-  integrity sha512-FELyH+ko9sOAC6hHQqBYi4tI1Qv8HTzVPXsYuwTFy0NF5gY1A9oriN051rHn4UtyAAXEUIMquNNYYkxC9bhMVw==
+markdown-it-drawio-viewer@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/markdown-it-drawio-viewer/-/markdown-it-drawio-viewer-1.2.0.tgz#d47648c039f12e4c5ca706ed4d0f5dc19400c9a2"
+  integrity sha512-Hu9jxqKLVfFhk2T8J4ayaVbuoW2RSugRrXIsREMW7MMWFDciBgs9C8ADKaTav7JITY5fp7q6KJU7pqP/5dMRnA==
   dependencies:
-    "@kaishuu0123/markdown-it-fence" "^0.1.4"
+    "@kaishuu0123/markdown-it-fence" "^0.2.0"
     xmldoc "^1.1.2"
 
 markdown-it-emoji@^1.4.0:
@@ -11891,11 +11883,6 @@ react-transition-group@^2.2.1, react-transition-group@^2.3.1:
     prop-types "^15.6.2"
     react-lifecycles-compat "^3.0.4"
 
-react-uid@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/react-uid/-/react-uid-2.2.0.tgz#0f77e1e0594fbf29fc4fe528cc9aa415c5bf9159"
-  integrity sha512-z+g5+hFOQ08hCfrGcJ1PNs+cmvH8Uq2CVzCmPeWBsUi5A4W4NWXR5jouledzy3oSKGMU9HOzf8zFuGi15TXJoQ==
-
 react-waypoint@^9.0.0:
   version "9.0.0"
   resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-9.0.0.tgz#a027deaafa60e3c2c998e78aa3542fa5ffc98afb"