Kaynağa Gözat

Merge branch 'imprv/profile-image-cache' into imprv/update-cache-bulk-with-comments

yusuketk 5 yıl önce
ebeveyn
işleme
154d478b86

+ 7 - 0
CHANGES.md

@@ -2,7 +2,14 @@
 
 ## v4.0.3-RC
 
+* Feature: Copy page path dropdown with Append params switch
+* Improvement: Truncate overflowed user browsing history
+* Improvement: Tabs appearance on mobile
+* Improvement: Search help appearance on mobile
+* Improvement: Accessibility of login page
+* Fix: Editor was broken by long lines
 * Fix: Editor doesn't work on mobile
+* Fix: Word break in Recent Updated contents
 * Fix: navbar is broken on Safari
 
 ## v4.0.2

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

@@ -60,8 +60,8 @@
   "No diff": "No diff",
   "Shrink versions that have no diffs": "Shrink versions that have no diffs",
   "User ID": "User ID",
-  "User's Home": "User's Home",
-  "User Settings": "User Settings",
+  "Home": "Home",
+  "Settings": "Settings",
   "User Information": "User information",
   "Basic Info": "Basic info",
   "Name": "Name",

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

@@ -60,8 +60,8 @@
   "No diff": "差分なし",
   "Shrink versions that have no diffs": "差分のないバージョンをコンパクトに表示する",
   "User ID": "ユーザーID",
-  "User's Home": "ユーザーホーム",
-  "User Settings": "ユーザー設定",
+  "Home": "ホーム",
+  "Settings": "設定",
   "User Information": "ユーザー情報",
   "Basic Info": "ユーザーの基本情報",
   "Name": "名前",

+ 0 - 2
src/client/js/app.jsx

@@ -25,7 +25,6 @@ import PageManagement from './components/Page/PageManagement';
 import TrashPageAlert from './components/Page/TrashPageAlert';
 import PageAttachment from './components/PageAttachment';
 import PageStatusAlert from './components/PageStatusAlert';
-import PagePathAutoComplete from './components/PagePathAutoComplete';
 import RecentCreated from './components/RecentCreated/RecentCreated';
 import MyDraftList from './components/MyDraftList/MyDraftList';
 import SeenUserPictureList from './components/User/SeenUserPictureList';
@@ -96,7 +95,6 @@ if (pageContainer.state.pageId != null) {
     'revision-toc': <TableOfContents />,
     'seen-user-list': <SeenUserPictureList />,
     'liker-list': <LikerPictureList />,
-    'rename-page-name-input': <PagePathAutoComplete crowi={appContainer} initializedPath={pageContainer.state.path} />,
 
     'user-created-list': <RecentCreated />,
     'user-draft-list': <MyDraftList />,

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

@@ -59,7 +59,7 @@ class CustomizeScriptSetting extends React.Component {
               Placeholders:<br />
               (Available after <code>load</code> event)
             </div>
-            <table className="table table-borderless table-sm form-text text-muted offset-1">
+            <table className="table table-borderless table-sm form-text text-muted offset-1 col-11">
               <tbody>
                 <tr>
                   <th className="text-right"><code>$</code></th>

+ 18 - 3
src/client/js/components/Navbar/PersonalDropdown.jsx

@@ -65,8 +65,23 @@ const PersonalDropdown = (props) => {
       {/* Menu */}
       <div className="dropdown-menu dropdown-menu-right">
 
-        <a className="dropdown-item" href={`/user/${user.username}`}><i className="icon-fw icon-user"></i>{ t('User\'s Home') }</a>
-        <a className="dropdown-item" href="/me"><i className="icon-fw icon-wrench"></i>{ t('User Settings') }</a>
+        <div className="px-4 pt-3 pb-2 text-center">
+          <UserPicture user={user} size="lg" noLink noTooltip />
+
+          <h5 className="mt-2">
+            {user.name}
+          </h5>
+
+          <div className="my-2">
+            <i className="icon-user icon-fw"></i>{user.username}<br />
+            <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{user.email}</span>
+          </div>
+
+          <div className="btn-group btn-block mt-2" role="group">
+            <a className="btn btn-sm btn-outline-secondary" href={`/user/${user.username}`}><i className="icon-fw icon-home"></i>{ t('Home') }</a>
+            <a className="btn btn-sm btn-outline-secondary" href="/me"><i className="icon-fw icon-wrench"></i>{ t('Settings') }</a>
+          </div>
+        </div>
 
         <div className="dropdown-divider"></div>
 
@@ -107,7 +122,7 @@ const PersonalDropdown = (props) => {
 
         <div className="dropdown-divider"></div>
 
-        <a className="dropdown-item" onClick={logoutHandler}><i className="icon-fw icon-power"></i>{ t('Sign out') }</a>
+        <button type="button" className="dropdown-item" onClick={logoutHandler}><i className="icon-fw icon-power"></i>{ t('Sign out') }</button>
       </div>
 
     </>

+ 35 - 16
src/client/js/components/Page/CopyDropdown.jsx

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 
 import {
-  Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
+  UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
   Tooltip,
 } from 'reactstrap';
 
@@ -18,6 +18,7 @@ class CopyDropdown extends React.Component {
     this.state = {
       dropdownOpen: false,
       tooltipOpen: false,
+      isParamsAppended: true,
     };
 
     this.toggle = this.toggle.bind(this);
@@ -39,13 +40,22 @@ class CopyDropdown extends React.Component {
     }, 1000);
   }
 
-  generatePagePathWithParams() {
-    const { pagePath } = this.props;
+  get uriParams() {
+    const { isParamsAppended } = this.state;
+
+    if (!isParamsAppended) {
+      return '';
+    }
+
     const {
       search, hash,
     } = window.location;
+    return `${search}${hash}`;
+  }
 
-    return `${pagePath}${search}${hash}`;
+  generatePagePathWithParams() {
+    const { pagePath } = this.props;
+    return decodeURI(`${pagePath}${this.uriParams}`);
   }
 
   generatePagePathUrl() {
@@ -55,25 +65,18 @@ class CopyDropdown extends React.Component {
 
   generatePermalink() {
     const { pageId } = this.props;
-    const { location } = window;
 
     if (pageId == null) {
       return null;
     }
 
-    const {
-      origin, search, hash,
-    } = location;
-    return `${origin}/${pageId}${search}${hash}`;
+    return decodeURI(`${origin}/${pageId}${this.uriParams}`);
   }
 
   generateMarkdownLink() {
     const { pagePath } = this.props;
-    const {
-      search, hash,
-    } = window.location;
 
-    const label = `${pagePath}${search}${hash}`;
+    const label = decodeURI(`${pagePath}${this.uriParams}`);
     const permalink = this.generatePermalink();
 
     return `[${label}](${permalink})`;
@@ -88,6 +91,7 @@ class CopyDropdown extends React.Component {
 
   render() {
     const { t, pageId } = this.props;
+    const { isParamsAppended } = this.state;
 
     const pagePathWithParams = this.generatePagePathWithParams();
     const pagePathUrl = this.generatePagePathUrl();
@@ -97,7 +101,7 @@ class CopyDropdown extends React.Component {
 
     return (
       <>
-        <Dropdown id="copyPagePathDropdown" className="grw-copy-dropdown" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
+        <UncontrolledDropdown id="copyPagePathDropdown" className="grw-copy-dropdown">
 
           <DropdownToggle
             caret
@@ -108,7 +112,22 @@ class CopyDropdown extends React.Component {
           </DropdownToggle>
 
           <DropdownMenu>
-            <DropdownItem header className="px-3">{ t('copy_to_clipboard.Copy to clipboard') }</DropdownItem>
+
+            <div className="d-flex align-items-center justify-content-between">
+              <DropdownItem header className="px-3">
+                { t('copy_to_clipboard.Copy to clipboard') }
+              </DropdownItem>
+              <div className="px-3 custom-control custom-switch custom-switch-sm">
+                <input
+                  type="checkbox"
+                  id="customSwitchForParams"
+                  className="custom-control-input"
+                  checked={isParamsAppended}
+                  onChange={e => this.setState({ isParamsAppended: !isParamsAppended })}
+                />
+                <label className="custom-control-label small" htmlFor="customSwitchForParams">Append params</label>
+              </div>
+            </div>
 
             <DropdownItem divider className="my-0"></DropdownItem>
 
@@ -162,7 +181,7 @@ class CopyDropdown extends React.Component {
             )}
           </DropdownMenu>
 
-        </Dropdown>
+        </UncontrolledDropdown>
 
         <Tooltip placement="bottom" isOpen={this.state.tooltipOpen} target="copyPagePathDropdown" fade={false}>
           copied!

+ 0 - 1
src/client/js/components/PageDuplicateModal.jsx

@@ -82,7 +82,6 @@ const PageDuplicateModal = (props) => {
                 <PagePathAutoComplete
                   crowi={appContainer}
                   initializedPath={path}
-                  addTrailingSlash
                   onSubmit={ppacSubmitHandler}
                   onInputChange={ppacInputChangeHandler}
                 />

+ 0 - 1
src/client/js/components/PagePathAutoComplete.jsx

@@ -61,7 +61,6 @@ PagePathAutoComplete.propTypes = {
 
 PagePathAutoComplete.defaultProps = {
   initializedPath: '/',
-  addTrailingSlash: true,
 };
 
 export default PagePathAutoComplete;

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

@@ -4,6 +4,10 @@
   border-bottom: $grw-navbar-border-width solid;
   border-left: 0;
 
+  .grw-app-title {
+    @include variable-font-size(24px);
+  }
+
   .grw-navbar-toggler {
     padding: 0.5rem;
     font-size: 1.5em;
@@ -23,11 +27,6 @@
     padding: 0 1rem;
   }
 
-  #personal-dropdown::after {
-    // hide caret
-    content: none;
-  }
-
   .nav-link {
     &:hover {
       background: rgba(0, 0, 0, 0.1);
@@ -40,4 +39,10 @@
   .nav-item.confidential {
     background: rgba(0, 0, 0, 0.2);
   }
+
+  .grw-personal-dropdown {
+    .grw-email-sm {
+      font-size: 0.75em;
+    }
+  }
 }

+ 3 - 3
src/client/styles/scss/_override-bootstrap.scss

@@ -77,13 +77,13 @@
   }
 
   // Dropdowns
-  .dropdown {
-    .dropdown-toggle.btn.disabled {
+  .dropdown-toggle {
+    &.btn.disabled {
       cursor: not-allowed;
     }
 
     // hide caret
-    .dropdown-toggle.dropdown-toggle-no-caret::after {
+    &.dropdown-toggle-no-caret::after {
       content: none;
     }
   }

+ 31 - 0
src/client/styles/scss/atoms/_custom_control.scss

@@ -1,3 +1,34 @@
 label.custom-control-label {
   font-weight: normal;
 }
+
+.custom-switch.custom-switch-sm {
+  $custom-control-indicator-size-sm: $custom-control-indicator-size * 0.8;
+  $custom-switch-width-sm: $custom-control-indicator-size-sm * 1.75;
+  $custom-control-gutter-sm: $custom-control-gutter * 0.8;
+  $custom-control-indicator-size-sm: $custom-control-indicator-size * 0.8;
+  $custom-switch-indicator-size-sm: subtract($custom-control-indicator-size-sm, $custom-control-indicator-border-width * 4);
+
+  padding-left: $custom-switch-width-sm + $custom-control-gutter-sm;
+
+  .custom-control-label {
+    &::before {
+      left: -($custom-switch-width-sm + $custom-control-gutter-sm);
+      width: $custom-switch-width-sm;
+      height: $custom-control-indicator-size-sm;
+    }
+
+    &::after {
+      top: add(($font-size-base * $line-height-base - $custom-control-indicator-size) / 2, $custom-control-indicator-border-width * 2);
+      left: add(-($custom-switch-width-sm + $custom-control-gutter-sm), $custom-control-indicator-border-width * 2);
+      width: $custom-switch-indicator-size-sm;
+      height: $custom-switch-indicator-size-sm;
+    }
+  }
+
+  .custom-control-input:checked ~ .custom-control-label {
+    &::after {
+      transform: translateX($custom-switch-width-sm - $custom-control-indicator-size-sm);
+    }
+  }
+}

+ 2 - 0
src/client/styles/scss/molecules/copy-dropdown.scss

@@ -1,5 +1,7 @@
 .grw-copy-dropdown {
   .dropdown-menu {
+    min-width: 310px;
+
     .dropdown-header {
       margin-bottom: 0.5em;
       font-size: 1.1em;

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

@@ -148,6 +148,10 @@ pre:not(.hljs):not(.CodeMirror-line) {
 
   border-image: $border-image-navbar;
   border-image-slice: 1;
+
+  .grw-app-title {
+    color: $fillcolor-logo-mark;
+  }
 }
 
 .search-top {

+ 7 - 5
src/server/views/layout/layout.html

@@ -75,16 +75,18 @@
 
   {% block layout_head_nav %}
   <nav class="navbar grw-navbar navbar-expand navbar-dark fixed-top mb-0 px-0">
-    <ul class="navbar-nav d-md-none mr-auto">
-      <li id="grw-navbar-toggler" class="nav-item"></li>
-    </ul>
-
     {# Brand Logo #}
     <div class="navbar-brand mr-0">
       <a class="grw-logo d-block" href="/">
         {% include '../widget/logo.html' %}
       </a>
     </div>
+    <ul class="navbar-nav d-md-none">
+      <li id="grw-navbar-toggler" class="nav-item"></li>
+    </ul>
+    <div class="grw-app-title d-none d-md-block">
+      {{ appService.getAppTitle() | preventXss }}
+    </div>
 
     {# Navbar Right #}
     <ul class="navbar-nav ml-auto">
@@ -97,7 +99,7 @@
             </a>
           </li>
         {% endif %}
-        <li id="personal-dropdown" class="nav-item dropdown dropdown-toggle"></li>
+        <li id="personal-dropdown" class="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret"></li>
       {% else %}
         <li id="login-user" class="nav-item"><a class="nav-link" href="/login">Login</a></li>
       {% endif %}