浏览代码

Merge pull request #852 from transloadit/improvement/better-animation

Dashboard open/close animation
Artur Paikin 6 年之前
父节点
当前提交
51df805cbe
共有 6 个文件被更改,包括 106 次插入111 次删除
  1. 1 1
      CHANGELOG.md
  2. 3 1
      src/plugins/Dashboard/Dashboard.js
  3. 45 27
      src/plugins/Dashboard/index.js
  4. 0 23
      src/scss/_animation.scss
  5. 53 59
      src/scss/_dashboard.scss
  6. 4 0
      website/src/docs/dashboard.md

+ 1 - 1
CHANGELOG.md

@@ -125,7 +125,7 @@ To Be Released: 2018-05-31.
 - [ ] providers: select files only after “select” is pressed, don’t add them right away when they are checked (keep a list of fileIds in state?); better UI + solves issue with autoProceed uploading in background, which is weird; re-read https://github.com/transloadit/uppy/pull/419#issuecomment-345210519 (@arturi, @goto-bus-stop)
 - [x] tus: add `filename` and `filetype`, so that tus servers knows what headers to set  https://github.com/tus/tus-js-client/commit/ebc5189eac35956c9f975ead26de90c896dbe360 (#844 / @vith)
 - [ ] core: look into utilizing https://github.com/que-etc/resize-observer-polyfill for responsive components. See also https://github.com/transloadit/uppy/issues/750
-- [x] core: ⚠️ **breaking** removed .run() (to solve issues like #756), update ddocs (#793 / goto-bus-stop)
+- [x] core: ⚠️ **breaking** removed .run() (to solve issues like #756), update docs (#793 / goto-bus-stop)
 - [ ] core: use Browserslist config to share between PostCSS, Autoprefixer and Babel https://github.com/browserslist/browserslist, https://github.com/amilajack/eslint-plugin-compat (@arturi)
 - [ ] core: utilize https://github.com/jonathantneal/postcss-preset-env, maybe https://github.com/jonathantneal/postcss-normalize (@arturi)
 - [ ] core: addFile not passing restrictions shouldn’t throw when called from UI

+ 3 - 1
src/plugins/Dashboard/Dashboard.js

@@ -23,7 +23,7 @@ const PanelContent = (props) => {
 }
 
 const poweredByUppy = (props) => {
-  return <a href="https://uppy.io" rel="noreferrer noopener" target="_blank" class="uppy-Dashboard-poweredBy">Powered by <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg">
+  return <a tabindex="-1" href="https://uppy.io" rel="noreferrer noopener" target="_blank" class="uppy-Dashboard-poweredBy">Powered by <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11" xmlns="http://www.w3.org/2000/svg">
     <path d="M7.365 10.5l-.01-4.045h2.612L5.5.806l-4.467 5.65h2.604l.01 4.044h3.718z" fill-rule="evenodd" />
   </svg><span class="uppy-Dashboard-poweredByUppy">Uppy</span></a>
 }
@@ -33,6 +33,8 @@ module.exports = function Dashboard (props) {
     { 'uppy-Root': props.isTargetDOMEl },
     'uppy-Dashboard',
     { 'Uppy--isTouchDevice': isTouchDevice() },
+    { 'uppy-Dashboard--animateOpenClose': props.animateOpenClose },
+    { 'uppy-Dashboard--isClosing': props.isClosing },
     { 'uppy-Dashboard--modal': !props.inline },
     { 'uppy-Dashboard--wide': props.isWide }
   )

+ 45 - 27
src/plugins/Dashboard/index.js

@@ -13,19 +13,22 @@ const { defaultTabIcon } = require('./icons')
 // MIT licence, https://github.com/ghosh/micromodal/blob/master/LICENSE.md
 // Copyright (c) 2017 Indrashish Ghosh
 const FOCUSABLE_ELEMENTS = [
-  'a[href]',
-  'area[href]',
-  'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
-  'select:not([disabled]):not([aria-hidden])',
-  'textarea:not([disabled]):not([aria-hidden])',
-  'button:not([disabled]):not([aria-hidden])',
-  'iframe',
-  'object',
-  'embed',
-  '[contenteditable]',
-  '[tabindex]:not([tabindex^="-"])'
+  'a[href]:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  'area[href]:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  'input:not([disabled]):not([inert]):not([aria-hidden])',
+  'select:not([disabled]):not([inert]):not([aria-hidden])',
+  'textarea:not([disabled]):not([inert]):not([aria-hidden])',
+  'button:not([disabled]):not([inert]):not([aria-hidden])',
+  'iframe:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  'object:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  'embed:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  '[contenteditable]:not([tabindex^="-"]):not([inert]):not([aria-hidden])',
+  '[tabindex]:not([tabindex^="-"]):not([inert]):not([aria-hidden])'
 ]
 
+const TAB_KEY = 9
+const ESC_KEY = 27
+
 /**
  * Dashboard UI with previews, metadata editing, tabs for various services and more
  */
@@ -106,6 +109,7 @@ module.exports = class Dashboard extends Plugin {
       disableInformer: false,
       disableThumbnailGenerator: false,
       disablePageScrollWhenModalOpen: true,
+      animateOpenClose: true,
       proudlyDisplayPoweredByUppy: true,
       onRequestCloseModal: () => this.closeModal(),
       locale: defaultLocale
@@ -242,23 +246,43 @@ module.exports = class Dashboard extends Plugin {
     this.savedActiveElement = document.activeElement
 
     if (this.opts.disablePageScrollWhenModalOpen) {
-      document.body.classList.add('uppy-Dashboard-isOpen')
+      document.body.classList.add('uppy-Dashboard-isFixed')
     }
 
+    // handle ESC and TAB keys in modal dialog
+    document.addEventListener('keydown', this.onKeydown)
+
     this.rerender(this.uppy.getState())
     this.updateDashboardElWidth()
     this.setFocusToBrowse()
   }
 
   closeModal () {
-    this.setPluginState({
-      isHidden: true
-    })
-
     if (this.opts.disablePageScrollWhenModalOpen) {
-      document.body.classList.remove('uppy-Dashboard-isOpen')
+      document.body.classList.remove('uppy-Dashboard-isFixed')
+    }
+
+    if (this.opts.animateOpenClose) {
+      this.setPluginState({
+        isClosing: true
+      })
+      const handler = () => {
+        this.setPluginState({
+          isHidden: true,
+          isClosing: false
+        })
+        this.el.removeEventListener('animationend', handler, false)
+      }
+      this.el.addEventListener('animationend', handler, false)
+    } else {
+      this.setPluginState({
+        isHidden: true
+      })
     }
 
+    // handle ESC and TAB keys in modal dialog
+    document.removeEventListener('keydown', this.onKeydown)
+
     this.savedActiveElement.focus()
   }
 
@@ -268,9 +292,9 @@ module.exports = class Dashboard extends Plugin {
 
   onKeydown (event) {
     // close modal on esc key press
-    if (event.keyCode === 27) this.requestCloseModal(event)
+    if (event.keyCode === ESC_KEY) this.requestCloseModal(event)
     // maintainFocus on tab key press
-    if (event.keyCode === 9) this.maintainFocus(event)
+    if (event.keyCode === TAB_KEY) this.maintainFocus(event)
   }
 
   handleClickOutside () {
@@ -323,10 +347,6 @@ module.exports = class Dashboard extends Plugin {
       this.uppy.log('Dashboard modal trigger not found. Make sure `trigger` is set in Dashboard options unless you are planning to call openModal() method yourself')
     }
 
-    if (!this.opts.inline) {
-      document.addEventListener('keydown', this.onKeydown)
-    }
-
     // Drag Drop
     this.removeDragDropListener = dragDrop(this.el, (files) => {
       this.handleDrop(files)
@@ -342,10 +362,6 @@ module.exports = class Dashboard extends Plugin {
       showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
     }
 
-    if (!this.opts.inline) {
-      document.removeEventListener('keydown', this.onKeydown)
-    }
-
     this.removeDragDropListener()
     window.removeEventListener('resize', this.updateDashboardElWidth)
   }
@@ -456,6 +472,8 @@ module.exports = class Dashboard extends Plugin {
       totalProgress: state.totalProgress,
       acquirers: acquirers,
       activePanel: pluginState.activePanel,
+      animateOpenClose: this.opts.animateOpenClose,
+      isClosing: pluginState.isClosing,
       getPlugin: this.uppy.getPlugin,
       progressindicators: progressindicators,
       autoProceed: this.uppy.opts.autoProceed,

+ 0 - 23
src/scss/_animation.scss

@@ -1,23 +0,0 @@
-// Animation
-
-@keyframes zoomOutLeft {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
-    transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale(.1) translate3d(-2000px, 0, 0);
-    transform: scale(.1) translate3d(-2000px, 0, 0);
-    -webkit-transform-origin: left center;
-    transform-origin: left center;
-  }
-}
-
-.UppyAnimation-zoomOutLeft {
-  animation-name: zoomOutLeft;
-  animation-duration: 1s;
-  animation-fill-mode: both;
-}

+ 53 - 59
src/scss/_dashboard.scss

@@ -1,17 +1,51 @@
 .uppy-Dashboard--modal {
   z-index: $zIndex-2;
-  // transition: transform 0.2s ease-in-out;
-  // transform: none;
-  // -webkit-overflow-scrolling: touch;
 }
 
-.uppy-Dashboard--modal[aria-hidden=true] {
-  display: none;
-  // transform: translateY(-50%);
-}
+  .uppy-Dashboard--modal[aria-hidden=true] {
+    display: none;
+  }
+
+  // Modal open/close animations
+
+  @keyframes uppy-Dashboard-fadeIn {
+    from { opacity: 0;  }
+    to { opacity: 1;  }
+  }
+
+  @keyframes uppy-Dashboard-fadeOut {
+    from { opacity: 1;  }
+    to { opacity: 0;  }
+  }
+
+  @keyframes uppy-Dashboard-slideDownAndFadeIn {
+    from { transform: translate3d(-50%, -70%, 0); opacity: 0; }
+    to { transform: translate3d(-50%, -50%, 0); opacity: 1; }
+  }
+
+  @keyframes uppy-Dashboard-slideUpFadeOut {
+    from { transform: translate3d(-50%, -50%, 0); opacity: 1; }
+    to { transform: translate3d(-50%, -70%, 0); opacity: 0; }
+  }
+
+  .uppy-Dashboard--modal.uppy-Dashboard--animateOpenClose > .uppy-Dashboard-inner {
+    animation: uppy-Dashboard-slideDownAndFadeIn 0.3s cubic-bezier(0, 0, .2, 1);
+  }
+
+  .uppy-Dashboard--modal.uppy-Dashboard--animateOpenClose > .uppy-Dashboard-overlay {
+    animation: uppy-Dashboard-fadeIn 0.3s cubic-bezier(0, 0, .2, 1);
+  }
+
+  .uppy-Dashboard--modal.uppy-Dashboard--animateOpenClose.uppy-Dashboard--isClosing > .uppy-Dashboard-inner {
+    animation: uppy-Dashboard-slideUpFadeOut 0.3s cubic-bezier(0, 0, .2, 1);
+  }
+
+  .uppy-Dashboard--modal.uppy-Dashboard--animateOpenClose.uppy-Dashboard--isClosing > .uppy-Dashboard-overlay {
+    animation: uppy-Dashboard-fadeOut 0.3s cubic-bezier(0, 0, .2, 1);
+  }
 
 // Added to body to prevent the page from scrolling when Modal is open
-.uppy-Dashboard-isOpen {
+.uppy-Dashboard-isFixed {
   overflow: hidden;
   height: 100vh;
 }
@@ -289,48 +323,31 @@
     font-size: 16px;
     line-height: 50px;
     max-width: 300px;
-    // top: 15px;
   }
 }
 
-.uppy-DashboardContent-titleFile {
-  // text-decoration: underline;
-}
-
 .uppy-DashboardContent-back {
   @include reset-button;
-  // position: absolute;
-  // top: 0;
-  // left: 15px;
   font-size: 14px;
-  // line-height: 40px;
   font-weight: 500;
   cursor: pointer;
   color: $color-cornflower-blue;
 
   .uppy-Dashboard--wide & {
     font-size: 15px;
-    // line-height: 50px;
   }
 }
 
-// .uppy-DashboardContent-back .UppyIcon {
-//   position: relative;
-//   margin-right: 3px;
-// }
-
 .uppy-DashboardContent-panel {
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
-  transform: translateY(-105%);
+  transform: translate3d(0, -105%, 0);
   transition: transform 0.2s ease-in-out;
-  will-change: transform;
   background-color: darken($color-white, 4%);
   box-shadow: 0 0 10px 5px rgba($color-black, 0.15);
-  // padding: 15px;
   padding-top: 40px;
   overflow: hidden;
   z-index: $zIndex-4;
@@ -338,14 +355,10 @@
   .uppy-Dashboard--wide & {
     padding-top: 50px;
   }
-
-  // .uppy-Dashboard--modal & {
-  //   z-index: $zIndex-4;
-  // }
 }
 
 .uppy-DashboardContent-panel[aria-hidden=false] {
-  transform: none;
+  transform: translate3d(0, 0, 0);
 }
 
 // Progress bar placeholder
@@ -429,29 +442,6 @@
   border-color: darken($color-white, 20%);
 }
 
-// .uppy-Dashboard-bgIcon {
-  // width: 100%;
-  // max-width: 460px;
-  // position: absolute;
-  // top: 50%;
-  // left: 50%;
-  // transform: translate(-50%, -50%);
-  // opacity: 0.7;
-  // transition: all 0.3s;
-  // padding: 0 20px;
-// }
-
-// .uppy-Dashboard-bgIcon .UppyIcon {
-//   width: 100%;
-//   height: 80px;
-//   fill: none;
-//   stroke: $color-asphalt-gray;
-
-//   .uppy-Dashboard--wide & {
-//     height: 110px;
-//   }
-// }
-
 .uppy-Dashboard-bgIcon {
   height: 100%;
   display: flex;
@@ -1012,8 +1002,9 @@
 //
 
 .uppy-DashboardFileCard {
-  transform: translateY(0);
-  transition: all 0.25s ease-in-out;
+  transform: translate3d(0, 0, 0);
+  transition: transform 0.2s ease-in-out;
+  
   width: 100%;
   height: 100%;
   position: absolute;
@@ -1026,9 +1017,9 @@
   background-color: $color-white;
 }
 
-.uppy-DashboardFileCard[aria-hidden=true] {
-  transform: translateY(-105%);
-}
+  .uppy-DashboardFileCard[aria-hidden=true] {
+    transform: translate3d(0, -105%, 0);
+  }
 
 .uppy-DashboardFileCard-inner {
   display: flex;
@@ -1093,3 +1084,6 @@
   vertical-align: middle;
   width: 78%;
 }
+
+
+

+ 4 - 0
website/src/docs/dashboard.md

@@ -138,6 +138,10 @@ Set to true to automatically close the modal when the user clicks outside of it.
 
 By default when Dashboard modal is open, it will disable page scrolling, so when you scroll a list of files in Uppy the website in the background stays still. Set to false to override this behaviour and leave page scrolling intact.
 
+## `animateOpenClose: true`
+
+Add light animations when modal dialog is open or closed, for more satisfying user experience.
+
 ### `proudlyDisplayPoweredByUppy: true`
 
 Uppy is provided for the world for free by the [Transloadit team](https://transloadit.com). In return, we ask that you consider keeping a tiny Uppy logo at the bottom of the Dashboard, so that more people can discover and use Uppy.